1 33 package org.jruby.runtime.marshal; 34 35 import java.io.BufferedInputStream ; 36 import java.io.IOException ; 37 import java.io.InputStream ; 38 import org.jruby.Ruby; 39 import org.jruby.RubyArray; 40 import org.jruby.RubyBignum; 41 import org.jruby.RubyClass; 42 import org.jruby.RubyFixnum; 43 import org.jruby.RubyFloat; 44 import org.jruby.RubyHash; 45 import org.jruby.RubyModule; 46 import org.jruby.RubyRegexp; 47 import org.jruby.RubyString; 48 import org.jruby.RubyStruct; 49 import org.jruby.RubySymbol; 50 import org.jruby.exceptions.RaiseException; 51 import org.jruby.runtime.Block; 52 import org.jruby.runtime.builtin.IRubyObject; 53 import org.jruby.util.ByteList; 54 55 60 public class UnmarshalStream extends BufferedInputStream { 61 protected final Ruby runtime; 62 private UnmarshalCache cache; 63 private IRubyObject proc; 64 65 public UnmarshalStream(Ruby runtime, InputStream in, IRubyObject proc) throws IOException { 66 super(in); 67 this.runtime = runtime; 68 this.cache = new UnmarshalCache(runtime); 69 this.proc = proc; 70 71 in.read(); in.read(); } 74 75 public IRubyObject unmarshalObject() throws IOException { 76 try { 77 int type = readUnsignedByte(); 78 IRubyObject result; 79 if (cache.isLinkType(type)) { 80 result = cache.readLink(this, type); 81 } else { 82 result = unmarshalObjectDirectly(type); 83 } 84 return result; 85 } catch (IOException ioe) { 86 throw ioe; 87 } 88 } 89 90 public void registerLinkTarget(IRubyObject newObject) { 91 cache.register(newObject); 92 } 93 94 private IRubyObject unmarshalObjectDirectly(int type) throws IOException { 95 IRubyObject rubyObj = null; 96 switch (type) { 97 case 'I': 98 rubyObj = unmarshalObject(); 99 defaultInstanceVarsUnmarshal(rubyObj); 100 break; 101 case '0' : 102 rubyObj = runtime.getNil(); 103 break; 104 case 'T' : 105 rubyObj = runtime.getTrue(); 106 break; 107 case 'F' : 108 rubyObj = runtime.getFalse(); 109 break; 110 case '"' : 111 rubyObj = RubyString.unmarshalFrom(this); 112 break; 113 case 'i' : 114 rubyObj = RubyFixnum.unmarshalFrom(this); 115 break; 116 case 'f' : 117 rubyObj = RubyFloat.unmarshalFrom(this); 118 break; 119 case '/' : 120 rubyObj = RubyRegexp.unmarshalFrom(this); 121 break; 122 case ':' : 123 rubyObj = RubySymbol.unmarshalFrom(this); 124 break; 125 case '[' : 126 rubyObj = RubyArray.unmarshalFrom(this); 127 break; 128 case '{' : 129 rubyObj = RubyHash.unmarshalFrom(this, false); 130 break; 131 case '}' : 132 rubyObj = RubyHash.unmarshalFrom(this, true); 134 break; 135 case 'c' : 136 rubyObj = RubyClass.unmarshalFrom(this); 137 break; 138 case 'm' : 139 rubyObj = RubyModule.unmarshalFrom(this); 140 break; 141 case 'l' : 142 rubyObj = RubyBignum.unmarshalFrom(this); 143 break; 144 case 'S' : 145 rubyObj = RubyStruct.unmarshalFrom(this); 146 break; 147 case 'o' : 148 rubyObj = defaultObjectUnmarshal(); 149 break; 150 case 'u' : 151 rubyObj = userUnmarshal(); 152 break; 153 case 'U' : 154 rubyObj = userNewUnmarshal(); 155 break; 156 case 'C' : 157 rubyObj = uclassUnmarshall(); 158 break; 159 default : 160 throw getRuntime().newArgumentError("dump format error(" + (char)type + ")"); 161 } 162 163 if (proc != null && type != ':') { 164 proc.callMethod(getRuntime().getCurrentContext(), "call", new IRubyObject[] {rubyObj}); 166 } 167 return rubyObj; 168 } 169 170 171 public Ruby getRuntime() { 172 return runtime; 173 } 174 175 public int readUnsignedByte() throws IOException { 176 int result = read(); 177 if (result == -1) { 178 throw new IOException ("Unexpected end of stream"); 179 } 180 return result; 181 } 182 183 public byte readSignedByte() throws IOException { 184 int b = readUnsignedByte(); 185 if (b > 127) { 186 return (byte) (b - 256); 187 } 188 return (byte) b; 189 } 190 191 public ByteList unmarshalString() throws IOException { 192 int length = unmarshalInt(); 193 byte[] buffer = new byte[length]; 194 195 int b = 0; 197 int i = 0; 198 while (i < length && (b = read()) != -1) { 199 buffer[i++] = (byte)b; 200 } 201 if (i < length) { 202 throw new IOException ("Unexpected end of stream"); 203 } 204 return new ByteList(buffer,false); 205 } 206 207 public int unmarshalInt() throws IOException { 208 int c = readSignedByte(); 209 if (c == 0) { 210 return 0; 211 } else if (5 < c && c < 128) { 212 return c - 5; 213 } else if (-129 < c && c < -5) { 214 return c + 5; 215 } 216 long result; 217 if (c > 0) { 218 result = 0; 219 for (int i = 0; i < c; i++) { 220 result |= (long) readUnsignedByte() << (8 * i); 221 } 222 } else { 223 c = -c; 224 result = -1; 225 for (int i = 0; i < c; i++) { 226 result &= ~((long) 0xff << (8 * i)); 227 result |= (long) readUnsignedByte() << (8 * i); 228 } 229 } 230 return (int) result; 231 } 232 233 private IRubyObject defaultObjectUnmarshal() throws IOException { 234 RubySymbol className = (RubySymbol) unmarshalObject(); 235 236 RubyClass type = null; 237 try { 238 type = (RubyClass)runtime.getClassFromPath(className.asSymbol()); 239 } catch (RaiseException e) { 240 if (e.getException().isKindOf(runtime.getModule("NameError"))) { 241 throw runtime.newArgumentError("undefined class/module " + className.asSymbol()); 242 } 243 244 throw e; 245 } 246 247 assert type != null : "type shouldn't be null."; 248 249 IRubyObject result = (IRubyObject)type.unmarshal(this); 250 251 return result; 252 } 253 254 public void defaultInstanceVarsUnmarshal(IRubyObject object) throws IOException { 255 int count = unmarshalInt(); 256 257 for (int i = 0; i < count; i++) { 258 String name = unmarshalObject().asSymbol(); 259 IRubyObject value = unmarshalObject(); 260 object.setInstanceVariable(name, value); 261 } 262 } 263 264 private IRubyObject uclassUnmarshall() throws IOException { 265 RubySymbol className = (RubySymbol)unmarshalObject(); 266 267 RubyClass type = (RubyClass)runtime.getClassFromPath(className.asSymbol()); 268 269 IRubyObject result = unmarshalObject(); 270 271 result.setMetaClass(type); 272 273 return result; 274 } 275 276 private IRubyObject userUnmarshal() throws IOException { 277 String className = unmarshalObject().asSymbol(); 278 ByteList marshaled = unmarshalString(); 279 RubyModule classInstance; 280 try { 281 classInstance = runtime.getClassFromPath(className); 282 } catch (RaiseException e) { 283 if (e.getException().isKindOf(runtime.getModule("NameError"))) { 284 throw runtime.newArgumentError("undefined class/module " + className); 285 } 286 287 throw e; 288 } 289 if (!classInstance.respondsTo("_load")) { 290 throw runtime.newTypeError("class " + classInstance.getName() + " needs to have method `_load'"); 291 } 292 IRubyObject result = classInstance.callMethod(getRuntime().getCurrentContext(), 293 "_load", RubyString.newString(getRuntime(), marshaled)); 294 registerLinkTarget(result); 295 return result; 296 } 297 298 private IRubyObject userNewUnmarshal() throws IOException { 299 String className = unmarshalObject().asSymbol(); 300 IRubyObject marshaled = unmarshalObject(); 301 RubyClass classInstance = runtime.getClass(className); 302 IRubyObject result = classInstance.newInstance(new IRubyObject[0], Block.NULL_BLOCK); 303 result.callMethod(getRuntime().getCurrentContext(),"marshal_load", marshaled); 304 registerLinkTarget(result); 305 return result; 306 } 307 } 308 | Popular Tags |