All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.ir.persistence.IRReader Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jruby.ir.persistence;

import org.jruby.EvalType;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubySymbol;
import org.jruby.ir.*;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.ClosureLocalVariable;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.parser.StaticScope;
import org.jruby.parser.StaticScopeFactory;
import org.jruby.runtime.Signature;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import org.jruby.util.ByteList;
import org.jruby.util.KeyValuePair;

/**
 *
 * @author enebo
 */
public class IRReader implements IRPersistenceValues {
    public static IRScope load(IRManager manager, final IRReaderDecoder file) throws IOException {
        int version = file.decodeIntRaw();

        if (version != VERSION) {
            throw new IOException("Trying to read incompatible persistence format (version found: " +
                    version + ", version expected: " + VERSION);
        }
        int headersOffset = file.decodeIntRaw();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("header_offset = " + headersOffset);
        int poolOffset = file.decodeIntRaw();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("pool_offset = " + headersOffset);

        file.seek(headersOffset);
        int scopesToRead  = file.decodeInt();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("scopes to read = " + scopesToRead);

        KeyValuePair[] scopes = new KeyValuePair[scopesToRead];
        for (int i = 0; i < scopesToRead; i++) {
            scopes[i] = decodeScopeHeader(manager, file);
        }

        // Lifecycle woes.  All IRScopes need to exist before we can decodeInstrs.
        for (KeyValuePair pair: scopes) {
            final IRScope scope = pair.getKey();
            final int instructionsOffset = pair.getValue();

            scope.allocateInterpreterContext(new Callable>() {
                public List call() {
                    return file.decodeInstructionsAt(scope, instructionsOffset);
                }
            });
        }

        // Run through all scopes again and ensure they've calculated flags.
        // This also forces lazy instrs from above to eagerly decode.
        for (KeyValuePair pair: scopes) {
            final IRScope scope = pair.getKey();
            scope.computeScopeFlags();
        }

        return scopes[0].getKey(); // topmost scope;
    }

    private static KeyValuePair decodeScopeHeader(IRManager manager, IRReaderDecoder decoder) {
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("DECODING SCOPE HEADER");
        IRScopeType type = decoder.decodeIRScopeType();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("IRScopeType = " + type);
        // Wackiness we decode as bytelist when we encoded as symbol because currentScope is not defined yet on first
        // name of first scope.  We will use manager in this method to finish the job in constructing our symbol.
        ByteList name = decoder.decodeByteList();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("NAME = " + name);
        int line = decoder.decodeInt();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("LINE = " + line);
        int tempVarsCount = decoder.decodeInt();
        if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("# of Temp Vars = " + tempVarsCount);
        Map indices = decodeScopeLabelIndices(decoder);

        IRScope parent = type != IRScopeType.SCRIPT_BODY ? decoder.decodeScope() : null;
        Signature signature;

        if (type == IRScopeType.CLOSURE || type == IRScopeType.FOR) {
            signature = Signature.decode(decoder.decodeLong());
        } else {
            signature = Signature.OPTIONAL;
        }
        StaticScope parentScope = parent == null ? null : parent.getStaticScope();
        // FIXME: It seems wrong we have static scope + local vars both being persisted.  They must have the same values
        // and offsets?
        StaticScope staticScope = decodeStaticScope(decoder, parentScope);
        IRScope scope = createScope(manager, type, manager.runtime.newSymbol(name), line, parent, signature, staticScope);

        scope.setTemporaryVariableCount(tempVarsCount);
        // FIXME: Replace since we are defining this...perhaps even make a persistence constructor
        scope.setLabelIndices(indices);

        // FIXME: This is odd, but ClosureLocalVariable wants it's defining closure...feels wrong.
        // But because of this we have to push decoding lvars to the end of the scope info.
        scope.setLocalVariables(decodeScopeLocalVariables(decoder, scope));

        decoder.addScope(scope);

        int instructionsOffset = decoder.decodeInt();

        return new KeyValuePair<>(scope, instructionsOffset);
    }

    private static Map decodeScopeLocalVariables(IRReaderDecoder decoder, IRScope scope) {
        int size = decoder.decodeInt();
        Map localVariables = new HashMap(size);
        for (int i = 0; i < size; i++) {
            RubySymbol name = scope.getManager().getRuntime().newSymbol(decoder.decodeByteList());
            int offset = decoder.decodeInt();

            localVariables.put(name, scope instanceof IRClosure ?
                    // SSS FIXME: do we need to read back locallyDefined boolean?
                    new ClosureLocalVariable(name, 0, offset) : new LocalVariable(name, 0, offset));
        }

        return localVariables;
    }

    private static Map decodeScopeLabelIndices(IRReaderDecoder decoder) {
        int labelIndicesSize = decoder.decodeInt();
        Map indices = new HashMap(labelIndicesSize);
        for (int i = 0; i < labelIndicesSize; i++) {
            indices.put(decoder.decodeString(), decoder.decodeInt());
        }
        return indices;
    }

    private static StaticScope decodeStaticScope(IRReaderDecoder decoder, StaticScope parentScope) {
        StaticScope scope = StaticScopeFactory.newStaticScope(parentScope, decoder.decodeStaticScopeType(), decoder.decodeStringArray(), decoder.decodeInt());

        scope.setSignature(decoder.decodeSignature());

        return scope;
    }

    public static IRScope createScope(IRManager manager, IRScopeType type, RubySymbol name, int line,
                                      IRScope lexicalParent, Signature signature, StaticScope staticScope) {

        switch (type) {
        case CLASS_BODY:
            return new IRClassBody(manager, lexicalParent, name, line, staticScope);
        case METACLASS_BODY:
            return new IRMetaClassBody(manager, lexicalParent, manager.getMetaClassName(), line, staticScope);
        case INSTANCE_METHOD:
            return new IRMethod(manager, lexicalParent, null, name, true, line, staticScope, false);
        case CLASS_METHOD:
            return new IRMethod(manager, lexicalParent, null, name, false, line, staticScope, false);
        case MODULE_BODY:
            return new IRModuleBody(manager, lexicalParent, name, line, staticScope);
        case SCRIPT_BODY:
            return new IRScriptBody(manager, name, staticScope);
        case FOR:
            return new IRFor(manager, lexicalParent, line, staticScope, signature);
        case CLOSURE:
            return new IRClosure(manager, lexicalParent, line, staticScope, signature);
        case EVAL_SCRIPT:
            // SSS FIXME: This is broken right now -- the isModuleEval arg has to be persisted and then read back.
            return new IREvalScript(manager, lexicalParent, lexicalParent.getFileName(), line, staticScope, EvalType.NONE);
        }

        throw new RuntimeException("No such scope type: " + type);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy