Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: EPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Eclipse 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/epl-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) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
  * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
  * 
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the EPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the EPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 package org.jruby.runtime.marshal;
 
 import java.util.List;
 import org.jruby.Ruby;
Marshals objects into Ruby's binary marshal format.

Author(s):
Anders
 
 public class MarshalStream extends FilterOutputStream {
     private final Ruby runtime;
     private final MarshalCache cache;
     private final int depthLimit;
     private boolean tainted = false;
     private boolean untrusted = false;
     
     private int depth = 0;
 
     private final static char TYPE_IVAR = 'I';
     private final static char TYPE_USRMARSHAL = 'U';
     private final static char TYPE_USERDEF = 'u';
     private final static char TYPE_UCLASS = 'C';
     public final static String SYMBOL_ENCODING_SPECIAL = "E";
     private final static String SYMBOL_ENCODING = "encoding";
 
     public MarshalStream(Ruby runtimeOutputStream outint depthLimitthrows IOException {
         super(out);
 
         this. = runtime;
         this. = depthLimit >= 0 ? depthLimit : .;
         this. = new MarshalCache();
 
         out.write(.);
         out.write(.);
     }
 
     public void dumpObject(IRubyObject valuethrows IOException {
         ++;
        
        if ( > ) {
            throw .newArgumentError("exceed depth limit");
        }
         |= value.isTaint();
         |= value.isUntrusted();
        writeAndRegister(value);
        --;
        if ( == 0) {
            .flush(); // flush afer whole dump is complete
        }
    }
    public void registerLinkTarget(IRubyObject newObject) {
        if (shouldBeRegistered(newObject)) {
            .register(newObject);
        }
    }
    public void registerSymbol(String sym) {
        .registerSymbol(sym);
    }
    static boolean shouldBeRegistered(IRubyObject value) {
        if (value.isNil()) {
            return false;
        } else if (value instanceof RubyBoolean) {
            return false;
        } else if (value instanceof RubyFixnum) {
            return ! isMarshalFixnum((RubyFixnum)value);
        }
        return true;
    }
    private static boolean isMarshalFixnum(RubyFixnum fixnum) {
        return fixnum.getLongValue() <= . && fixnum.getLongValue() >= .;
    }
    private void writeAndRegisterSymbol(String symthrows IOException {
        if (.isSymbolRegistered(sym)) {
            .writeSymbolLink(thissym);
        } else {
            registerSymbol(sym);
            dumpSymbol(sym);
        }
    }
    private void writeAndRegister(IRubyObject valuethrows IOException {
        if (.isRegistered(value)) {
            .writeLink(thisvalue);
        } else {
            value.getMetaClass().smartDump(thisvalue);
        }
    }
    private List<Variable<Object>> getVariables(IRubyObject valuethrows IOException {
        List<Variable<Object>> variables = null;
        if (value instanceof CoreObjectType) {
            int nativeTypeIndex = ((CoreObjectType)value).getNativeTypeIndex();
            
            if (nativeTypeIndex != . && nativeTypeIndex != .) {
                if (shouldMarshalEncoding(value) || (
                        !value.isImmediate()
                        && value.hasVariables()
                        && nativeTypeIndex != .
                        && nativeTypeIndex != .
                        )) {
                    // object has instance vars and isn't a class, get a snapshot to be marshalled
                    // and output the ivar header here
                    variables = value.getVariableList();
                    // write `I' instance var signet if class is NOT a direct subclass of Object
                    write();
                }
                RubyClass type = value.getMetaClass();
                switch(nativeTypeIndex) {
                case .:
                case .:
                case .:
                case .:
                    type = dumpExtended(type);
                    break;
                }
                if (nativeTypeIndex != value.getMetaClass(). && nativeTypeIndex != .) {
                    // object is a custom class that extended one of the native types other than Object
                    writeUserClass(valuetype);
                }
            }
        }
        return variables;
    }
    private boolean shouldMarshalEncoding(IRubyObject value) {
        if (!.is1_9()) return false;
        if (!(value instanceof MarshalEncoding)) return false;
        return ((MarshalEncodingvalue).shouldMarshalEncoding();
    }
    public void writeDirectly(IRubyObject valuethrows IOException {
        List<Variable<Object>> variables = getVariables(value);
        writeObjectData(value);
        if (variables != null) {
            if (.is1_9()) {
                dumpVariablesWithEncoding(variablesvalue);
            } else {
                dumpVariables(variables);
            }
        }
    }
    public static String getPathFromClass(RubyModule clazz) {
        String path = clazz.getName();
        
        if (path.charAt(0) == '#') {
            String classOrModule = clazz.isClass() ? "class" : "module";
            throw clazz.getRuntime().newTypeError("can't dump anonymous " + classOrModule + " " + path);
        }
        
        RubyModule real = clazz.isModule() ? clazz : ((RubyClass)clazz).getRealClass();
        if (clazz.getRuntime().getClassFromPath(path) != real) {
            throw clazz.getRuntime().newTypeError(path + " can't be referred");
        }
        return path;
    }
    
    private void writeObjectData(IRubyObject valuethrows IOException {
        // switch on the object's *native type*. This allows use-defined
        // classes that have extended core native types to piggyback on their
        // marshalling logic.
        if (value instanceof CoreObjectType) {
            if (value instanceof DataType) {
                throw value.getRuntime().newTypeError("no marshal_dump is defined for class " + value.getMetaClass().getName());
            }
            int nativeTypeIndex = ((CoreObjectType)value).getNativeTypeIndex();
            switch (nativeTypeIndex) {
            case .:
                write('[');
                RubyArray.marshalTo((RubyArray)valuethis);
                return;
            case .:
                write('F');
                return;
            case .: {
                RubyFixnum fixnum = (RubyFixnum)value;
                if (isMarshalFixnum(fixnum)) {
                    write('i');
                    writeInt((intfixnum.getLongValue());
                    return;
                }
                // FIXME: inefficient; constructing a bignum just for dumping?
                value = RubyBignum.newBignum(value.getRuntime(), fixnum.getLongValue());
                // fall through
            }
            case .:
                write('l');
                RubyBignum.marshalTo((RubyBignum)valuethis);
                return;
            case .:
                if (((RubyClass)value).isSingleton()) throw .newTypeError("singleton class can't be dumped");
                write('c');
                RubyClass.marshalTo((RubyClass)valuethis);
                return;
            case .:
                write('f');
                RubyFloat.marshalTo((RubyFloat)valuethis);
                return;
            case .: {
                RubyHash hash = (RubyHash)value;
                if(hash.getIfNone().isNil()){
                    write('{');
                }else if (hash.hasDefaultProc()) {
                    throw hash.getRuntime().newTypeError("can't dump hash with default proc");
                } else {
                    write('}');
                }
                RubyHash.marshalTo(hashthis);
                return;
            }
            case .:
                write('m');
                RubyModule.marshalTo((RubyModule)valuethis);
                return;
            case .:
                write('0');
                return;
            case .:
            case .:
                dumpDefaultObjectHeader(value.getMetaClass());
                value.getMetaClass().getRealClass().marshal(valuethis);
                return;
            case .:
                write('/');
                RubyRegexp.marshalTo((RubyRegexp)valuethis);
                return;
            case .:
                registerLinkTarget(value);
                write('"');
                writeString(value.convertToString().getByteList());
                return;
            case .:
                RubyStruct.marshalTo((RubyStruct)valuethis);
                return;
            case .:
                writeAndRegisterSymbol(((RubySymbol)value).asJavaString());
                return;
            case .:
                write('T');
                return;
            default:
                throw .newTypeError("can't dump " + value.getMetaClass().getName());
            }
        } else {
            dumpDefaultObjectHeader(value.getMetaClass());
            value.getMetaClass().getRealClass().marshal(valuethis);
        }
    }
    public void userNewMarshal(IRubyObject valueDynamicMethod methodthrows IOException {
        userNewCommon(valuemethod);
    }
    public void userNewMarshal(IRubyObject valuethrows IOException {
        userNewCommon(valuenull);
    }
    private void userNewCommon(IRubyObject valueDynamicMethod methodthrows IOException {
        registerLinkTarget(value);
        write();
        RubyClass metaclass = value.getMetaClass().getRealClass();
        writeAndRegisterSymbol(metaclass.getName());
        IRubyObject marshaled;
        if (method != null) {
            marshaled = method.call(.getCurrentContext(), valuevalue.getMetaClass(), "marshal_dump");
        } else {
            marshaled = value.callMethod(.getCurrentContext(), "marshal_dump");
        }
        dumpObject(marshaled);
    }
    public void userMarshal(IRubyObject valueDynamicMethod methodthrows IOException {
        userCommon(valuemethod);
    }
    public void userMarshal(IRubyObject valuethrows IOException {
        userCommon(valuenull);
    }
    private void userCommon(IRubyObject valueDynamicMethod methodthrows IOException {
        RubyFixnum depthLimitFixnum = .newFixnum();
        IRubyObject dumpResult;
        if (method != null) {
            dumpResult = method.call(.getCurrentContext(), valuevalue.getMetaClass(), "_dump"depthLimitFixnum);
        } else {
            dumpResult = value.callMethod(.getCurrentContext(), "_dump"depthLimitFixnum);
        }
        
        if (!(dumpResult instanceof RubyString)) {
            throw .newTypeError(dumpResult.getString());
        }
        RubyString marshaled = (RubyString)dumpResult;
        boolean hasVars;
        if (hasVars = marshaled.hasVariables()) {
            write();
        }
        write();
        RubyClass metaclass = value.getMetaClass().getRealClass();
        writeAndRegisterSymbol(metaclass.getName());
        writeString(marshaled.getByteList());
        if (hasVars) {
            dumpVariables(marshaled.getVariableList());
        }
        registerLinkTarget(value);
    }
    
    public void writeUserClass(IRubyObject objRubyClass typethrows IOException {
        write();
        
        // w_unique
        if (type.getName().charAt(0) == '#') {
            throw obj.getRuntime().newTypeError("can't dump anonymous class " + type.getName());
        }
        
        // w_symbol
        writeAndRegisterSymbol(type.getName());
    }
    
    public void dumpVariablesWithEncoding(List<Variable<Object>> varsIRubyObject objthrows IOException {
        if (shouldMarshalEncoding(obj)) {
            writeInt(vars.size() + 1); // vars preceded by encoding
            writeEncoding(((MarshalEncoding)obj).getMarshalEncoding());
        } else {
            writeInt(vars.size());
        }
        
        dumpVariablesShared(vars);
    }
    public void dumpVariables(List<Variable<Object>> varsthrows IOException {
        writeInt(vars.size());
        dumpVariablesShared(vars);
    }
    private void dumpVariablesShared(List<Variable<Object>> varsthrows IOException {
        for (Variable<Objectvar : vars) {
            if (var.getValue() instanceof IRubyObject) {
                writeAndRegisterSymbol(var.getName());
                dumpObject((IRubyObject)var.getValue());
            }
        }
    }
    public void writeEncoding(Encoding encodingthrows IOException {
        if (encoding == null || encoding == .) {
            writeObjectData(.getFalse());
        } else if (encoding == .) {
            writeObjectData(.getTrue());
        } else {
            writeAndRegisterSymbol();
            RubyString encodingString = new RubyString(.getString(), encoding.getName());
            writeObjectData(encodingString);
        }
    }
    
    private boolean hasSingletonMethods(RubyClass type) {
        for(DynamicMethod method : type.getMethods().values()) {
            // We do not want to capture cached methods
            if(method.getImplementationClass() == type) {
                return true;
            }
        }
        return false;
    }

    
w_extended
    private RubyClass dumpExtended(RubyClass typethrows IOException {
        if(type.isSingleton()) {
            if (hasSingletonMethods(type) || type.hasVariables()) { // any ivars, since we don't have __attached__ ivar now
                throw type.getRuntime().newTypeError("singleton can't be dumped");
            }
            type = type.getSuperClass();
        }
        while(type.isIncluded()) {
            write('e');
            type = type.getSuperClass();
        }
        return type;
    }
    public void dumpDefaultObjectHeader(RubyClass typethrows IOException {
        dumpDefaultObjectHeader('o',type);
    }
    public void dumpDefaultObjectHeader(char tpRubyClass typethrows IOException {
        dumpExtended(type);
        write(tp);
    }
    public void writeString(String valuethrows IOException {
        writeInt(value.length());
        // FIXME: should preserve unicode?
        .write(RubyString.stringToBytes(value));
    }
    public void writeString(ByteList valuethrows IOException {
        int len = value.length();
        writeInt(len);
        .write(value.getUnsafeBytes(), value.begin(), len);
    }
    public void dumpSymbol(String valuethrows IOException {
        write(':');
        writeString(value);
    }
    public void writeInt(int valuethrows IOException {
        if (value == 0) {
            .write(0);
        } else if (0 < value && value < 123) {
            .write(value + 5);
        } else if (-124 < value && value < 0) {
            .write((value - 5) & 0xff);
        } else {
            byte[] buf = new byte[4];
            int i = 0;
            for (; i < buf.lengthi++) {
                buf[i] = (byte)(value & 0xff);
                
                value = value >> 8;
                if (value == 0 || value == -1) {
                    break;
                }
            }
            int len = i + 1;
            .write(value < 0 ? -len : len);
            .write(buf, 0, i + 1);
        }
    }
    public void writeByte(int valuethrows IOException {
        .write(value);
    }
    public boolean isTainted() {
        return ;
    }
    public boolean isUntrusted() {
        return ;
    }
New to GrepCode? Check out our FAQ X