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

org.truffleruby.debug.RubyScope Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2019, 2024 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 2.0, or
 * GNU General Public License version 2, or
 * GNU Lesser General Public License version 2.1.
 */
package org.truffleruby.debug;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import org.truffleruby.RubyContext;
import org.truffleruby.RubyLanguage;
import org.truffleruby.core.binding.BindingNodes;
import org.truffleruby.core.binding.RubyBinding;
import org.truffleruby.core.string.StringUtils;
import org.truffleruby.language.RubyNode;
import org.truffleruby.language.arguments.RubyArguments;
import org.truffleruby.language.control.RaiseException;

import java.util.List;

@ExportLibrary(InteropLibrary.class)
public final class RubyScope implements TruffleObject {

    public static final String RECEIVER_MEMBER = "self";

    public final MaterializedFrame frame;
    public final RubyBinding binding;
    public final RubyNode node;

    public RubyScope(RubyContext context, RubyLanguage language, MaterializedFrame frame, RubyNode node) {
        assert frame != null;
        this.frame = frame;
        this.binding = BindingNodes.createBinding(context, language, frame);
        this.node = node;
    }

    @ExportMessage
    Object toDisplayString(boolean allowSideEffects) {
        return hasScopeParent() ? "block" : "method";
    }

    @ExportMessage
    boolean isScope() {
        return true;
    }

    @ExportMessage
    boolean hasScopeParent() {
        return RubyArguments.getDeclarationFrame(frame) != null;
    }

    @ExportMessage
    Object getScopeParent(
            @CachedLibrary("this") InteropLibrary node) throws UnsupportedMessageException {
        final MaterializedFrame parentFrame = RubyArguments.getDeclarationFrame(frame);
        if (parentFrame != null) {
            // Do no set the node for parent scopes, as we don't know it and
            // to recognize them as Local/Closure and not Block scopes.
            final RubyLanguage language = RubyLanguage.get(node);
            final RubyContext context = RubyContext.get(node);
            return new RubyScope(context, language, parentFrame, null);
        }
        throw UnsupportedMessageException.create();
    }

    @TruffleBoundary
    @ExportMessage
    public boolean hasSourceLocation() {
        RootNode rootNode = node == null ? null : node.getRootNode();
        return rootNode != null && rootNode.getSourceSection() != null;
    }

    @TruffleBoundary
    @ExportMessage
    public SourceSection getSourceLocation() throws UnsupportedMessageException {
        RootNode rootNode = node == null ? null : node.getRootNode();
        if (rootNode == null) {
            throw UnsupportedMessageException.create();
        }
        final SourceSection sourceSection = rootNode.getSourceSection();
        if (sourceSection == null) {
            throw UnsupportedMessageException.create();
        }
        return sourceSection;
    }

    @ExportMessage
    boolean hasLanguage() {
        return true;
    }

    @ExportMessage
    Class> getLanguage() {
        return RubyLanguage.class;
    }

    @ExportMessage
    protected boolean hasMembers() {
        return true;
    }

    @ExportMessage
    static final class ReadMember {
        @Specialization(guards = "RECEIVER_MEMBER.equals(member)")
        static Object readSelf(RubyScope scope, String member) {
            return RubyArguments.getSelf(scope.frame);
        }

        @Specialization(guards = "!RECEIVER_MEMBER.equals(member)")
        static Object read(RubyScope scope, String member,
                @Cached @Exclusive BindingNodes.LocalVariableGetNode localVariableGetNode,
                @Bind("this") Node node)
                throws UnknownIdentifierException {
            try {
                return localVariableGetNode.execute(node, scope.binding, member);
            } catch (RaiseException e) {
                throw UnknownIdentifierException.create(member);
            }
        }
    }

    @ExportMessage
    @TruffleBoundary
    protected Object getMembers(boolean includeInternal) {
        List members = BindingNodes.LocalVariablesNode.listLocalVariablesWithDuplicates(frame, RECEIVER_MEMBER);
        return new VariableNamesObject(members.toArray(StringUtils.EMPTY_STRING_ARRAY));
    }

    @ExportMessage
    static final class IsMemberReadable {
        @Specialization(guards = "RECEIVER_MEMBER.equals(member)")
        static boolean readSelf(RubyScope scope, String member) {
            return true;
        }

        @Specialization(guards = "!RECEIVER_MEMBER.equals(member)")
        static boolean isMemberReadable(RubyScope scope, String member,
                @Cached @Exclusive BindingNodes.HasLocalVariableNode hasLocalVariableNode,
                @Bind("this") Node node) {
            return hasLocalVariableNode.execute(node, scope.binding, member);
        }
    }

    @ExportMessage
    static final class IsMemberModifiable {
        @Specialization(guards = "RECEIVER_MEMBER.equals(member)")
        static boolean readSelf(RubyScope scope, String member) {
            return false;
        }

        @Specialization(guards = "!RECEIVER_MEMBER.equals(member)")
        static boolean isMemberModifiable(RubyScope scope, String member,
                @Cached @Exclusive BindingNodes.HasLocalVariableNode hasLocalVariableNode,
                @Bind("this") Node node) {
            return hasLocalVariableNode.execute(node, scope.binding, member);
        }
    }


    @ExportMessage
    static final class WriteMember {
        @Specialization
        static void writeMember(RubyScope scope, String member, Object value,
                @CachedLibrary("scope") InteropLibrary interopLibrary,
                @Cached BindingNodes.LocalVariableSetNode localVariableSetNode,
                @Bind("this") Node node) throws UnknownIdentifierException {
            if (interopLibrary.isMemberModifiable(scope, member)) {
                localVariableSetNode.execute(node, scope.binding, member, value);
            } else {
                throw UnknownIdentifierException.create(member);
            }
        }
    }

    @ExportMessage
    protected boolean isMemberInsertable(String member) {
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy