Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Ruby 1.9 marshalling compatibility.

IR now writes string encodings, self-referential objects, arrays and hashes, and exceptions using the same format as MRI.
Likewise it can now read all the above successfully. Also added a few more specs to cover things that were missed.
  • Loading branch information...
commit 630995eca293b5b0531e821b76bab7e15c4506f6 1 parent e495db1
@gglresearchanddevelopment authored
View
13 Languages/Ruby/Libraries.Yaml/RubyConstructor.cs
@@ -265,15 +265,20 @@ private class ExternalConstructor {
throw new ConstructorException("Cannot construct module");
}
Hash values = ctor.ConstructMapping(mapping);
+ // TODO: call allocate here:
+ object result = RubyUtils.CreateObject((RubyClass)module);
+
RubyMethodInfo method = module.GetMethod("yaml_initialize") as RubyMethodInfo;
if (method != null) {
- // TODO: call allocate here:
- object result = RubyUtils.CreateObject((RubyClass)module);
ctor._yamlInitializeSite.Target(ctor._yamlInitializeSite, result, className, values);
- return result;
} else {
- return RubyUtils.CreateObject((RubyClass)module, EnumerateAttributes(globalScope.Context, values));
+ var special = result as IRubySpecialMarshalling;
+ foreach (var kvp in EnumerateAttributes(globalScope.Context, values)) {
+ if (special == null || !special.TrySpecialUnmarshal(kvp.Key, kvp.Value))
+ module.Context.SetInstanceVariable(result, kvp.Key, kvp.Value);
+ }
}
+ return result;
} else {
//TODO: YAML::Object
throw new NotImplementedError("YAML::Object is not implemented yet");
View
17 Languages/Ruby/Libraries/Builtins/ExceptionOps.cs
@@ -171,6 +171,23 @@ public static class ExceptionOps {
return RubyExceptionData.GetInstance(self).Message;
}
+ [RubyMethod("eql?")]
+ [RubyMethod("==")]
+ [RubyMethod("===")]
+ public static bool Equal(RubyContext context, Exception/*!*/ self, [NotNull]Exception/*!*/ other)
+ {
+ var selfData = RubyExceptionData.GetInstance(self);
+ var otherData = RubyExceptionData.GetInstance(other);
+
+ return context.GetImmediateClassOf(self) == context.GetImmediateClassOf(other) &&
+ Object.Equals(selfData.Message, otherData.Message) &&
+ Object.Equals(context.Inspect(selfData.Backtrace).ToString(), context.Inspect(otherData.Backtrace).ToString()); // TODO: Comparing backtraces using tostring is not nice, come up with a nicer way
+ }
+
+ [RubyMethod("==")]
+ public static bool Equal(Exception/*!*/ self, object other)
+ { return false; }
+
[RubyMethod("inspect", RubyMethodAttributes.PublicInstance)]
public static MutableString/*!*/ Inspect(UnaryOpStorage/*!*/ inspectStorage, ConversionStorage<MutableString>/*!*/ tosConversion, Exception/*!*/ self) {
object message = RubyExceptionData.GetInstance(self).Message;
View
175 Languages/Ruby/Libraries/Builtins/Marshal.cs
@@ -28,6 +28,7 @@
using IronRuby.Runtime.Calls;
using System.Globalization;
using System.Text;
+using System.Reflection;
namespace IronRuby.Builtins {
@@ -86,6 +87,7 @@ internal class MarshalWriter {
private readonly WriterSites/*!*/ _sites;
private int _recursionLimit;
private readonly Dictionary<string/*!*/, int>/*!*/ _symbols;
+ private readonly Dictionary<RubyEncoding/*!*/, MutableString/*!*/>/*!*/ _encodingNames;
private readonly Dictionary<object, int>/*!*/ _objects;
internal MarshalWriter(WriterSites/*!*/ sites, BinaryWriter/*!*/ writer, RubyContext/*!*/ context, int? limit) {
@@ -96,6 +98,7 @@ internal class MarshalWriter {
_context = context;
_recursionLimit = (limit.HasValue ? limit.Value : -1);
_symbols = new Dictionary<string, int>();
+ _encodingNames = new Dictionary<RubyEncoding, MutableString>();
_objects = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
}
@@ -272,6 +275,49 @@ internal class MarshalWriter {
}
}
+ private void WriteEncoding(RubyEncoding/*!*/ value) {
+ if (RubyEncoding.Binary == value) // don't write encoding for binary
+ return;
+
+ if (value == RubyEncoding.Ascii) {
+ WriteSymbol("E", RubyEncoding.Binary);
+ _writer.Write((byte)'F');
+ } else if (value == RubyEncoding.UTF8) {
+ WriteSymbol("E", RubyEncoding.Binary);
+ _writer.Write((byte)'T');
+ } else {
+ WriteSymbol("encoding", RubyEncoding.Binary);
+ MutableString encodingName;
+ if(!_encodingNames.TryGetValue(value, out encodingName)) { // we can't pass the encoding's name directly to WriteAnObject as it's a CLR string
+ encodingName = MutableString.Create(value.Name, RubyEncoding.Binary);
+ _encodingNames.Add(value, encodingName);
+ }
+ WriteAnObject(encodingName);
+ }
+ }
+
+ private List<KeyValuePair<string, object>> GetExceptionData(Exception ex) {
+ if (ex == null)
+ return null;
+
+ var result = new List<KeyValuePair<string, object>>();
+ result.Add(new KeyValuePair<string,object>("mesg", MutableString.Create(ex.Message, RubyEncoding.Ascii)));
+
+ var bt = ExceptionOps.GetBacktrace(ex);
+ if (bt != null)
+ result.Add(new KeyValuePair<string,object>("bt", bt));
+
+ if (ex is MemberAccessException) { // NameError and Descendents have a name and args attribute in MRI 1.9. IronRuby doesn't currently support them, but we marshal dump for compatibility
+ result.Add(new KeyValuePair<string, object>("name", null));
+
+ if (ex is MissingMethodException) {
+ result.Add(new KeyValuePair<string, object>("args", null));
+ }
+ }
+
+ return result;
+ }
+
private void TestForAnonymous(RubyModule/*!*/ theModule) {
if (theModule.Name == null) {
throw RubyExceptions.CreateTypeError("can't dump anonymous {0} {1}",
@@ -390,15 +436,27 @@ internal class MarshalWriter {
bool implementsMarshalDump = _context.ResolveMethod(obj, "marshal_dump", VisibilityContext.AllVisible).Found;
bool writeInstanceData = false;
+ RubyEncoding objEncoding = null;
string[] instanceNames = null;
+ ICollection<KeyValuePair<string, object>> exceptionData = null;
if (!implementsDump && !implementsMarshalDump) {
// Neither "_dump" nor "marshal_dump" writes instance vars separately
instanceNames = _context.GetInstanceVariableNames(obj);
- if (instanceNames.Length > 0) {
+
+ if(obj is MutableString) // TODO maybe add an IHasEncoding interface instead of checking for string/regex explicitly
+ objEncoding = ((MutableString)obj).Encoding;
+ else if(obj is RubyRegex)
+ objEncoding = ((RubyRegex)obj).Encoding;
+
+ if (objEncoding == RubyEncoding.Binary)
+ objEncoding = null;
+
+ exceptionData = GetExceptionData(obj as Exception);
+
+ writeInstanceData = instanceNames.Length > 0 || objEncoding != null || exceptionData != null; // ruby 1.9: A string/regex with encoding acts as if it contains an extra unnamed ivar for the encoding
+ if (writeInstanceData)
_writer.Write((byte)'I');
- writeInstanceData = true;
- }
}
if (!implementsDump || implementsMarshalDump) {
@@ -438,6 +496,16 @@ internal class MarshalWriter {
WriteStruct((RubyStruct)obj);
} else if (obj is Range) {
WriteRange((Range)obj);
+ } else if(obj is RubyMethod) {
+ throw RubyExceptions.CreateTypeError("no marshal_dump is defined for class Method");
+ } else if(obj is Proc) {
+ throw RubyExceptions.CreateTypeError("no marshal_dump is defined for class Proc");
+ } else if(obj is RubyFile) {
+ throw RubyExceptions.CreateTypeError("can't dump File");
+ } else if(obj is RubyIO) {
+ throw RubyExceptions.CreateTypeError("can't dump IO");
+ } else if(obj is MatchData) {
+ throw RubyExceptions.CreateTypeError("can't dump MatchData");
} else {
if (writeInstanceData) {
// Overwrite the "I"; we always have instance data
@@ -446,11 +514,28 @@ internal class MarshalWriter {
writeInstanceData = true;
}
WriteObject(obj);
- }
+ }
if (writeInstanceData) {
- WriteInt32(instanceNames.Length);
+ var ivarCount = instanceNames.Length;
+ if (exceptionData != null)
+ ivarCount += exceptionData.Count;
+
+ if (objEncoding != null) {
+ WriteInt32(ivarCount + 1);
+ WriteEncoding(objEncoding);
+ } else {
+ WriteInt32(ivarCount);
+ }
+
var encoding = _context.GetIdentifierEncoding();
+ if(exceptionData != null){
+ foreach (var kv in exceptionData) {
+ WriteSymbol(kv.Key, encoding);
+ WriteAnObject(kv.Value);
+ }
+ }
+
foreach (string name in instanceNames) {
object value;
if (!_context.TryGetInstanceVariable(obj, name, out value)) {
@@ -626,18 +711,25 @@ private sealed class Symbol {
return new RubyRegex(pattern, (RubyRegexOptions)flags);
}
- private RubyArray/*!*/ ReadArray() {
+ private RubyArray/*!*/ ReadArray(int objectRef = -1) {
int count = ReadInt32();
RubyArray result = new RubyArray(count);
+ if (objectRef != -1) { // put the array in our _objects cache before loading children so we can deal with arrays that contain themselves
+ _objects[objectRef] = result;
+ }
+
for (int i = 0; i < count; i++) {
result.Add(ReadAnObject(false));
}
return result;
}
- private Hash/*!*/ ReadHash(int typeFlag) {
+ private Hash/*!*/ ReadHash(int typeFlag, int objectRef = -1) {
int count = ReadInt32();
Hash result = new Hash(Context);
+ if (objectRef != -1) { // put the Hash in our _objects cache before loading children so we can deal with arrays that contain themselves
+ _objects[objectRef] = result;
+ }
for (int i = 0; i < count; i++) {
object key = ReadAnObject(false);
result[key] = ReadAnObject(false);
@@ -685,17 +777,27 @@ private sealed class Symbol {
return RubyUtils.CreateObject(ReadType());
}
- private object/*!*/ ReadObject() {
+ private object/*!*/ ReadObject(int objectRef = -1) {
RubyClass theClass = ReadType();
+
+ var result = RubyUtils.CreateObject(theClass);
+ if (objectRef != -1) { // put the object in our _objects cache before loading children so we can deal with arrays that contain themselves
+ _objects[objectRef] = result;
+ }
+
+ var special = GetCustomUnmarshaller(result);
+
int count = ReadInt32();
- var attributes = new Dictionary<string, object>();
for (int i = 0; i < count; i++) {
- string name = ReadIdentifier();
- attributes[name] = ReadAnObject(false);
+ var attrName = ReadIdentifier();
+ var attrValue = ReadAnObject(false);
+
+ if(special == null || !special.TrySpecialUnmarshal(attrName, attrValue))
+ theClass.Context.SetInstanceVariable(result, attrName, attrValue);
}
- return RubyUtils.CreateObject(theClass, attributes);
+ return result;
}
-
+
private object/*!*/ ReadUsingLoad() {
RubyClass theClass = ReadType();
return _sites.Load.Target(_sites.Load, theClass, ReadString());
@@ -850,18 +952,17 @@ private sealed class Symbol {
case ';':
obj = ReadSymbolOrIdentifier(typeFlag, true).GetSymbol(Context);
- runProc = false;
break;
case '@':
obj = _objects[ReadInt32()];
- runProc = false;
break;
default:
// Reserve a reference
- int objectRef = _objects.Count;
+ int objectRef = -1;
if (!noCache) {
+ objectRef = _objects.Count;
_objects[objectRef] = null;
}
@@ -879,14 +980,14 @@ private sealed class Symbol {
obj = ReadRegex();
break;
case '[':
- obj = ReadArray();
+ obj = ReadArray(objectRef);
break;
case '{':
case '}':
- obj = ReadHash(typeFlag);
+ obj = ReadHash(typeFlag, objectRef);
break;
case 'o':
- obj = ReadObject();
+ obj = ReadObject(objectRef);
break;
case 'u':
obj = ReadUsingLoad();
@@ -919,7 +1020,7 @@ private sealed class Symbol {
break;
}
if (runProc) {
- _sites.ProcCall.Target(_sites.ProcCall, _proc, obj);
+ obj = _sites.ProcCall.Target(_sites.ProcCall, _proc, obj); // ruby 1.9: return the value from the proc
}
return obj;
}
@@ -929,7 +1030,39 @@ private sealed class Symbol {
CheckPreamble();
return ReadAnObject(false);
} catch (IOException e) {
- throw RubyExceptions.CreateArgumentError("marshal data too short", e);
+ throw new IronRuby.Builtins.EOFError("marshal data too short", e);
+ }
+ }
+
+ private IRubySpecialMarshalling GetCustomUnmarshaller(object o) {
+ var self = o as IRubySpecialMarshalling;
+ if (self != null)
+ return self;
+
+ var exception = o as Exception;
+ if (exception != null)
+ return new ExceptionUnmarshaller(exception);
+
+ return null;
+ }
+
+ class ExceptionUnmarshaller : IRubySpecialMarshalling {
+ readonly Exception _exception;
+
+ public ExceptionUnmarshaller(Exception/*!*/ exception)
+ { _exception = exception; }
+
+ public bool TrySpecialUnmarshal(string attrName, object attrValue) {
+ switch(attrName) {
+ case "mesg":
+ typeof(Exception).InvokeMember("_message", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, null, _exception, new object[] { attrValue.ToString() });
+ return true;
+ case "bt":
+ ExceptionOps.SetBacktrace(_exception, attrValue as RubyArray);
+ return true;
+ default:
+ return false;
+ }
}
}
}
View
54 Languages/Ruby/Libraries/Initializers.Generated.cs
@@ -1525,11 +1525,27 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
}
private static void LoadException_Instance(IronRuby.Builtins.RubyModule/*!*/ module) {
+ DefineLibraryMethod(module, "==", 0x51,
+ 0x00000004U, 0x00000000U,
+ new Func<IronRuby.Runtime.RubyContext, System.Exception, System.Exception, System.Boolean>(IronRuby.Builtins.ExceptionOps.Equal),
+ new Func<System.Exception, System.Object, System.Boolean>(IronRuby.Builtins.ExceptionOps.Equal)
+ );
+
+ DefineLibraryMethod(module, "===", 0x51,
+ 0x00000004U,
+ new Func<IronRuby.Runtime.RubyContext, System.Exception, System.Exception, System.Boolean>(IronRuby.Builtins.ExceptionOps.Equal)
+ );
+
DefineLibraryMethod(module, "backtrace", 0x51,
0x00000000U,
new Func<System.Exception, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.ExceptionOps.GetBacktrace)
);
+ DefineLibraryMethod(module, "eql?", 0x51,
+ 0x00000004U,
+ new Func<IronRuby.Runtime.RubyContext, System.Exception, System.Exception, System.Boolean>(IronRuby.Builtins.ExceptionOps.Equal)
+ );
+
DefineRuleGenerator(module, "exception", 0x51, IronRuby.Builtins.ExceptionOps.GetException());
DefineLibraryMethod(module, "initialize", 0x52,
@@ -2931,11 +2947,11 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
);
DefineLibraryMethod(module, "readlines", 0x51,
- 0x00020004U, 0x00060000U, 0x00000000U, 0x00000000U,
- new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, IronRuby.Runtime.Union<IronRuby.Builtins.MutableString, System.Int32>, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines),
- new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, IronRuby.Builtins.MutableString, System.Int32, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines),
+ 0x00000000U, 0x00000000U, 0x00020004U, 0x00060000U,
new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines),
- new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, Microsoft.Scripting.Runtime.DynamicNull, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines)
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, Microsoft.Scripting.Runtime.DynamicNull, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines),
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, IronRuby.Runtime.Union<IronRuby.Builtins.MutableString, System.Int32>, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines),
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.RubyIO, IronRuby.Builtins.MutableString, System.Int32, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.RubyIOOps.ReadLines)
);
DefineLibraryMethod(module, "reopen", 0x51,
@@ -4152,15 +4168,15 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
);
DefineLibraryMethod(module, "define_singleton_method", 0x51,
- new[] { 0x0000000cU, 0x0002000cU, 0x0000000cU, 0x0002000cU, 0x0000000cU, 0x0004000aU, 0x0000000aU, 0x0002000cU},
- new Func<IronRuby.Runtime.RubyScope, System.Object, IronRuby.Runtime.ClrName, IronRuby.Builtins.Proc, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
+ new[] { 0x0002000cU, 0x0000000cU, 0x0002000cU, 0x0000000cU, 0x0004000aU, 0x0000000aU, 0x0002000cU, 0x0000000cU},
new Func<IronRuby.Runtime.RubyScope, System.Object, System.String, IronRuby.Builtins.RubyMethod, IronRuby.Builtins.RubyMethod>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
new Func<IronRuby.Runtime.RubyScope, System.Object, IronRuby.Runtime.ClrName, IronRuby.Builtins.RubyMethod, IronRuby.Builtins.RubyMethod>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
new Func<IronRuby.Runtime.RubyScope, System.Object, System.String, IronRuby.Builtins.UnboundMethod, IronRuby.Builtins.UnboundMethod>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
new Func<IronRuby.Runtime.RubyScope, System.Object, IronRuby.Runtime.ClrName, IronRuby.Builtins.UnboundMethod, IronRuby.Builtins.UnboundMethod>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
new Func<IronRuby.Runtime.RubyScope, IronRuby.Runtime.BlockParam, System.Object, System.String, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
new Func<IronRuby.Runtime.RubyScope, IronRuby.Runtime.BlockParam, System.Object, IronRuby.Runtime.ClrName, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
- new Func<IronRuby.Runtime.RubyScope, System.Object, System.String, IronRuby.Builtins.Proc, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod)
+ new Func<IronRuby.Runtime.RubyScope, System.Object, System.String, IronRuby.Builtins.Proc, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod),
+ new Func<IronRuby.Runtime.RubyScope, System.Object, IronRuby.Runtime.ClrName, IronRuby.Builtins.Proc, IronRuby.Builtins.Proc>(IronRuby.Builtins.KernelOps.DefineSingletonMethod)
);
DefineLibraryMethod(module, "display", 0x51,
@@ -6740,11 +6756,11 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
);
DefineLibraryMethod(module, "each_line", 0x51,
- 0x00000002U, 0x00010000U, 0x00020001U, 0x00000000U,
+ 0x00000000U, 0x00000002U, 0x00010000U, 0x00020001U,
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine),
new Func<IronRuby.Runtime.RubyContext, IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine),
new Func<IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine),
- new Func<IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine),
- new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine)
+ new Func<IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine)
);
DefineLibraryMethod(module, "empty?", 0x51,
@@ -6855,11 +6871,11 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
);
DefineLibraryMethod(module, "lines", 0x51,
- 0x00000002U, 0x00010000U, 0x00020001U, 0x00000000U,
+ 0x00000000U, 0x00000002U, 0x00010000U, 0x00020001U,
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine),
new Func<IronRuby.Runtime.RubyContext, IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine),
new Func<IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine),
- new Func<IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine),
- new Func<IronRuby.Runtime.RubyContext, IronRuby.Builtins.MutableString, IronRuby.Builtins.Enumerator>(IronRuby.Builtins.MutableStringOps.EachLine)
+ new Func<IronRuby.Runtime.BlockParam, IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.EachLine)
);
DefineLibraryMethod(module, "ljust", 0x51,
@@ -6920,10 +6936,10 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitia
);
DefineLibraryMethod(module, "rindex", 0x51,
- 0x00010002U, 0x00030002U, 0x00040004U,
+ 0x00040004U, 0x00010002U, 0x00030002U,
+ new Func<IronRuby.Runtime.RubyScope, IronRuby.Builtins.MutableString, IronRuby.Builtins.RubyRegex, System.Int32, System.Object>(IronRuby.Builtins.MutableStringOps.LastIndexOf),
new Func<IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Object>(IronRuby.Builtins.MutableStringOps.LastIndexOf),
- new Func<IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Int32, System.Object>(IronRuby.Builtins.MutableStringOps.LastIndexOf),
- new Func<IronRuby.Runtime.RubyScope, IronRuby.Builtins.MutableString, IronRuby.Builtins.RubyRegex, System.Int32, System.Object>(IronRuby.Builtins.MutableStringOps.LastIndexOf)
+ new Func<IronRuby.Builtins.MutableString, IronRuby.Builtins.MutableString, System.Int32, System.Object>(IronRuby.Builtins.MutableStringOps.LastIndexOf)
);
DefineLibraryMethod(module, "rjust", 0x51,
@@ -11285,12 +11301,12 @@ public sealed class BigDecimalLibraryInitializer : IronRuby.Builtins.LibraryInit
);
DefineLibraryMethod(module, "<=>", 0x11,
- new[] { 0x00000000U, 0x00000002U, 0x00000004U, 0x00000000U, 0x00000000U},
- new Func<IronRuby.Runtime.BinaryOpStorage, IronRuby.Runtime.BinaryOpStorage, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Object, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare),
+ new[] { 0x00000002U, 0x00000004U, 0x00000000U, 0x00000000U, 0x00000000U},
new Func<IronRuby.StandardLibrary.BigDecimal.BigDecimal, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare),
new Func<IronRuby.Runtime.RubyContext, IronRuby.StandardLibrary.BigDecimal.BigDecimal, Microsoft.Scripting.Math.BigInteger, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare),
new Func<IronRuby.Runtime.RubyContext, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Int32, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare),
- new Func<IronRuby.Runtime.RubyContext, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Double, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare)
+ new Func<IronRuby.Runtime.RubyContext, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Double, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare),
+ new Func<IronRuby.Runtime.BinaryOpStorage, IronRuby.Runtime.BinaryOpStorage, IronRuby.StandardLibrary.BigDecimal.BigDecimal, System.Object, System.Object>(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare)
);
DefineLibraryMethod(module, "==", 0x11,
View
36 Languages/Ruby/Ruby/Builtins/Range.cs
@@ -22,10 +22,11 @@
using System.Runtime.CompilerServices;
using IronRuby.Runtime.Calls;
using System.Text;
+using System.Collections.Generic;
namespace IronRuby.Builtins {
-
- public partial class Range : IDuplicable, ISerializable {
+
+ public partial class Range : IDuplicable, IRubySpecialMarshalling {
private object _begin;
private object _end;
private bool _excludeEnd;
@@ -35,21 +36,6 @@ public partial class Range : IDuplicable, ISerializable {
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;
- }
-
- 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;
@@ -107,6 +93,22 @@ public partial class Range : IDuplicable, ISerializable {
_initialized = true;
}
+ public bool TrySpecialUnmarshal(string attrName, object attrValue) {
+ switch(attrName) {
+ case "begin":
+ _begin = attrValue;
+ return true;
+ case "end":
+ _end = attrValue;
+ return true;
+ case "excl":
+ _excludeEnd = true.Equals(attrValue);
+ return true;
+ default:
+ return false;
+ }
+ }
+
protected virtual Range/*!*/ Copy() {
return new Range(this);
}
View
1  Languages/Ruby/Ruby/Ruby.csproj
@@ -292,6 +292,7 @@
<Compile Include="Runtime\Globals\SpecialGlobalVariableInfo.cs" />
<Compile Include="Runtime\IDuplicable.cs" />
<Compile Include="Runtime\IRubyObject.cs" />
+ <Compile Include="Runtime\IRubySpecialMarshalling.cs" />
<Compile Include="Runtime\RubyInstanceData.cs" />
<Compile Include="Runtime\InstanceDataWeakTable.cs" />
<Compile Include="Builtins\Exceptions.cs" />
View
11 Languages/Ruby/Ruby/Runtime/IRubySpecialMarshalling.cs
@@ -0,0 +1,11 @@
+
+namespace IronRuby.Runtime {
+
+ /// <summary>Ruby classes which are backed by .NET and aren't primitives can implement this interface to
+ /// do special marshalling without relying on the .NET serialization stuff.</summary>
+ public interface IRubySpecialMarshalling {
+
+ /// <summary>If TrySpecialUnmarshal returns true, it means that attrName was handled specially and should not be set as an instance variable or passed to ruby.</summary>
+ bool TrySpecialUnmarshal(string attrName, object attrValue);
+ }
+}
View
2  Languages/Ruby/Ruby/Runtime/RubyOps.cs
@@ -2481,6 +2481,7 @@ private sealed class _NeedsUpdate {
}
#if !SILVERLIGHT // serialization
+ // TODO: Remove this, it isn't used by Marshal anymore
[Emitted(UseReflection = true)] //RubyTypeBuilder
public static void DeserializeObject(out RubyInstanceData/*!*/ instanceData, out RubyClass/*!*/ immediateClass, SerializationInfo/*!*/ info) {
immediateClass = (RubyClass)info.GetValue(RubyUtils.SerializationInfoClassKey, typeof(RubyClass));
@@ -2496,6 +2497,7 @@ private sealed class _NeedsUpdate {
instanceData = newInstanceData;
}
+ // TODO: Remove this, it isn't used by Marshal anymore
[Emitted(UseReflection = true)] //RubyTypeBuilder
public static void SerializeObject(RubyInstanceData instanceData, RubyClass/*!*/ immediateClass, SerializationInfo/*!*/ info) {
info.AddValue(RubyUtils.SerializationInfoClassKey, immediateClass, typeof(RubyClass));
View
38 Languages/Ruby/Ruby/Runtime/RubyUtils.cs
@@ -967,45 +967,9 @@ private class RecursionHandle : IDisposable {
private static readonly Type[] _ccTypes1 = new Type[] { typeof(RubyClass) };
private static readonly Type[] _ccTypes2 = new Type[] { typeof(RubyContext) };
-#if !SILVERLIGHT // serialization
- private static readonly Type[] _serializableTypeSignature = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) };
-#endif
public static readonly string SerializationInfoClassKey = "#immediateClass";
-
- public static object/*!*/ CreateObject(RubyClass/*!*/ theclass, IEnumerable<KeyValuePair<string, object>>/*!*/ attributes) {
- Assert.NotNull(theclass, attributes);
-
- Type baseType = theclass.GetUnderlyingSystemType();
- object obj;
-#if SILVERLIGHT // serialization
- if (typeof(ISerializable).IsAssignableFrom(baseType) && !typeof(RubyObject).IsAssignableFrom(baseType)) {
-#else
- if (typeof(ISerializable).IsAssignableFrom(baseType)) {
- 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 // serialization
- }
- SerializationInfo info = new SerializationInfo(baseType, new FormatterConverter());
- info.AddValue(SerializationInfoClassKey, theclass);
- foreach (var pair in attributes) {
- info.AddValue(pair.Key, pair.Value);
- }
- obj = ci.Invoke(new object[2] { info, new StreamingContext(StreamingContextStates.Other, theclass) });
-#endif
- } else {
- obj = CreateObject(theclass);
- foreach (var pair in attributes) {
- theclass.Context.SetInstanceVariable(obj, pair.Key, pair.Value);
- }
- }
- return obj;
- }
-
+
private static bool IsAvailable(MethodBase method) {
return method != null && !method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly;
}
View
46 Languages/Ruby/Tests/mspec/rubyspec/core/marshal/dump_spec.rb
@@ -87,6 +87,52 @@ def _dump(depth); 10; end
Marshal.dump(obj).should ==
"#{mv+nv}o:\vObject\x06:\t@str[\t:\aso;\aI\"\ahi\x06:\x06EF@\a"
end
+
+ it "dumps an array of 2 strings both with ascii-bit encoding" do # This tests the behaviour where we write an index for the encoding and use EF for ascii
+ s, s2 = "hi", "bye"
+ s.force_encoding 'ascii-8bit'
+ s2.force_encoding 'ascii-8bit'
+ Marshal.dump([s, s2]).should ==
+ "#{mv+nv}[\a\"\ahi\"\bbye"
+ end
+
+ it "dumps an array of 2 strings both with ascii encoding" do # This tests the behaviour where we write an index for the encoding and use EF for ascii
+ s, s2 = "hi", "bye"
+ s.force_encoding 'ascii'
+ s2.force_encoding 'ascii'
+ Marshal.dump([s, s2]).should ==
+ "#{mv+nv}[\aI\"\ahi\x06:\x06EFI\"\bbye\x06;\x00F"
+ end
+
+ it "dumps an array of strings with non-ascii encoding" do # This tests the behaviour where we write an index for the encoding and also write an index for the custom encoding object
+ s, s2 = "hi", "bye"
+ s.force_encoding 'cp850'
+ s2.force_encoding 'cp850'
+
+ enc_str = s.encoding.to_s # CRuby will have CP850 and IronRuby will have IBM850. Neither is "wrong" as they're aliases for the same thing
+ enc_l = (enc_str.length + 5).chr
+ enc_l_str = "#{enc_l}#{enc_str}"
+
+ Marshal.dump([s, s2]).should ==
+ "#{mv+nv}[\aI\"\ahi\x06:\rencoding\"#{enc_l_str}I\"\bbye\x06;\x00@\a"
+ end
+
+ it "dumps an array of mixed strings with duplicates" do
+ a1, a2, c1, c2, b1, b2 = *%w(asc1 asc2 cp1 cp2 bin1 bin2)
+ a1.force_encoding 'ascii'
+ a2.force_encoding 'ascii'
+ c1.force_encoding 'cp850'
+ c2.force_encoding 'cp850'
+ b1.force_encoding 'ascii-8bit'
+ b2.force_encoding 'ascii-8bit'
+
+ enc_str = c1.encoding.to_s # CRuby will have CP850 and IronRuby will have IBM850. Neither is "wrong" as they're aliases for the same thing
+ enc_l = (enc_str.length + 5).chr
+ enc_l_str = "#{enc_l}#{enc_str}"
+
+ Marshal.dump([a1, c1, b1, a2, c2, b2]).should ==
+ "\x04\b[\vI\"\tasc1\x06:\x06EFI\"\bcp1\x06:\rencoding\"#{enc_l_str}\"\tbin1I\"\tasc2\x06;\x00FI\"\bcp2\x06;\x06@\b\"\tbin2"
+ end
end
ruby_version_is ""..."1.9" do
View
22 Languages/Ruby/Tests/mspec/rubyspec/core/marshal/fixtures/marshal_data.rb
@@ -122,6 +122,9 @@ class UserRegexp < Regexp
class UserString < String
end
+class UserRange < Range
+end
+
module Meths
def meths_method() end
end
@@ -133,6 +136,12 @@ def meths_more_method() end
Struct.new "Pyramid"
Struct.new "Useful", :a, :b
+def make_error(klass, *args)
+ error = klass.new(*args)
+ error.set_backtrace ["caller1", "caller2"] # we need to use a fake caller otherwise the caller will contain whatever random path the developer has at the time
+ return error
+end
+
module MarshalSpec
DATA = {
"nil" => [nil, "\004\b0"],
@@ -336,6 +345,19 @@ module MarshalSpec
"\004\bC:\016UserArray[\000"],
"Struct" => [Struct::Pyramid.new,
"\004\bS:\024Struct::Pyramid\000"],
+
+ "Exception" => [make_error(Exception, 'foo'),
+ "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF"],
+ "NotImplementedError" => [make_error(NotImplementedError, 'message'),
+ "\x04\bo:\x18NotImplementedError\a:\tmesgI\"\fmessage\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF"],
+ "StandardError" => [make_error(StandardError, 'message'),
+ "\x04\bo:\x12StandardError\a:\tmesgI\"\fmessage\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF"],
+ "RuntimeError" => [make_error(RuntimeError, 'message'),
+ "\x04\bo:\x11RuntimeError\a:\tmesgI\"\fmessage\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF"],
+ "NameError" => [make_error(NameError, 'message'),
+ "\x04\bo:\x0ENameError\b:\tmesgI\"\fmessage\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF:\tname0"],
+ "NoMethodError" => [make_error(NoMethodError, 'message'),
+ "\x04\bo:\x12NoMethodError\t:\tmesgI\"\fmessage\x06:\x06EF:\abt[\aI\"\fcaller1\x06;\aFI\"\fcaller2\x06;\aF:\tname0:\targs0"],
}
end
View
42 Languages/Ruby/Tests/mspec/rubyspec/core/marshal/load_spec.rb
@@ -111,6 +111,30 @@
Marshal.load("\004\bf\0361.1867344999999999e+22\000\344@").should ==
obj
end
+
+ it "loads a recursive array" do
+ a = Marshal.load("\x04\b[\ai\x06@\x00")
+ a[0].should == 1
+ a[1].should == a
+ end
+
+ it "loads a recursive hash" do
+ a = Marshal.load("\x04\b{\x06i\x06@\x00")
+ a.keys.should == [1]
+ a.values.should == [a]
+ end
+
+ it "loads a recursive object" do
+ obj = Marshal.load "\x04\bo:\vObject\x06:\t@obj@\x00"
+ r = obj.instance_variable_get :@obj
+ r.should == obj
+ end
+
+ it "loads a user range" do
+ obj = Marshal.load "\x04\bo:\x0EUserRange\b:\texclF:\nbegini\x06:\bendi\b"
+ obj.class.should == UserRange
+ obj.should == UserRange.new(1,3)
+ end
ruby_version_is "1.9" do
it "returns the value of the proc when called with a proc" do
@@ -310,10 +334,20 @@
File.unlink(temp_file)
end
end
-
- MarshalSpec::DATA.each do |description, (object, marshal, attributes)|
- it "loads a #{description}" do
- Marshal.load(marshal).should == object
+
+ ruby_version_is ""..."1.9" do
+ MarshalSpec::DATA.each do |description, (object, marshal, attributes)|
+ it "loads a #{description}" do
+ Marshal.load(marshal).should == object
+ end
+ end
+ end
+
+ ruby_version_is "1.9" do
+ MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)|
+ it "loads a #{description}" do
+ Marshal.load(marshal).should == object
+ end
end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.