ReadInstanceVariableNode.java

/*
 * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 1.0
 * GNU General Public License version 2
 * GNU Lesser General Public License version 2.1
 */
package org.jruby.truffle.nodes.objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jruby.truffle.nodes.ReadNode;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;

public class ReadInstanceVariableNode extends RubyNode implements ReadNode {

    @Child protected RubyNode receiver;
    @Child protected ReadHeadObjectFieldNode readNode;
    private final boolean isGlobal;

    private final BranchProfile nullProfile = BranchProfile.create();
    private final BranchProfile primitiveProfile = BranchProfile.create();

    public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, Object name, RubyNode receiver, boolean isGlobal) {
        super(context, sourceSection);
        this.receiver = receiver;
        readNode = new ReadHeadObjectFieldNode(name);
        this.isGlobal = isGlobal;
    }

    @Override
    public int executeIntegerFixnum(VirtualFrame frame) throws UnexpectedResultException {
        final Object receiverObject = receiver.execute(frame);

        if (receiverObject instanceof RubyBasicObject) {
            return readNode.executeInteger((RubyBasicObject) receiverObject);
        } else {
            // TODO(CS): need to put this onto the fast path?

            CompilerDirectives.transferToInterpreter();
            throw new UnexpectedResultException(getContext().getCoreLibrary().getNilObject());
        }
    }

    @Override
    public long executeLongFixnum(VirtualFrame frame) throws UnexpectedResultException {
        final Object receiverObject = receiver.execute(frame);

        if (receiverObject instanceof RubyBasicObject) {
            return readNode.executeLong((RubyBasicObject) receiverObject);
        } else {
            // TODO(CS): need to put this onto the fast path?

            CompilerDirectives.transferToInterpreter();
            throw new UnexpectedResultException(getContext().getCoreLibrary().getNilObject());
        }
    }

    @Override
    public double executeFloat(VirtualFrame frame) throws UnexpectedResultException {
        final Object receiverObject = receiver.execute(frame);

        if (receiverObject instanceof RubyBasicObject) {
            return readNode.executeDouble((RubyBasicObject) receiverObject);
        } else {
            // TODO(CS): need to put this onto the fast path?

            CompilerDirectives.transferToInterpreter();
            throw new UnexpectedResultException(getContext().getCoreLibrary().getNilObject());
        }
    }

    @Override
    public Object execute(VirtualFrame frame) {
        final Object receiverObject = receiver.execute(frame);

        if (receiverObject instanceof RubyBasicObject) {
            Object value = readNode.execute((RubyBasicObject) receiverObject);

            if (value == null) {
                nullProfile.enter();
                value = getContext().getCoreLibrary().getNilObject();
            }

            return value;
        } else {
            primitiveProfile.enter();
            return getContext().getCoreLibrary().getNilObject();
        }
    }

    @Override
    public Object isDefined(VirtualFrame frame) {
        notDesignedForCompilation();

        if (isGlobal) {
            final RubyBasicObject recieverValue = (RubyBasicObject) receiver.execute(frame);

            if (readNode.getName().equals("$~") || readNode.getName().equals("$!")) {
                return getContext().makeString("global-variable");
            } else if (readNode.isSet(recieverValue)) {
                if (readNode.execute(recieverValue) == getContext().getCoreLibrary().getNilObject()) {
                    return getContext().getCoreLibrary().getNilObject();
                } else {
                    return getContext().makeString("global-variable");
                }
            } else {
                return getContext().getCoreLibrary().getNilObject();
            }
        }

        final RubyContext context = getContext();

        final Object receiverObject = receiver.execute(frame);

        if (receiverObject instanceof RubyBasicObject) {
            final RubyBasicObject receiverRubyObject = (RubyBasicObject) receiverObject;

            final Shape layout = receiverRubyObject.getDynamicObject().getShape();
            final Property storageLocation = layout.getProperty(readNode.getName());

            if (storageLocation != null) {
                return context.makeString("instance-variable");
            } else {
                return getContext().getCoreLibrary().getNilObject();
            }
        } else {
            return false;
        }
    }

    @Override
    public RubyNode makeWriteNode(RubyNode rhs) {
        return new WriteInstanceVariableNode(getContext(), getSourceSection(), readNode.getName(), receiver, rhs, isGlobal);
    }
}