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

com.oracle.truffle.api.frame.FrameDescriptor Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.truffle.api.frame;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.impl.TVMCI;

/**
 * Descriptor of the slots of frame objects. Multiple frame instances are associated with one such
 * descriptor.
 *
 * @since 0.8 or earlier
 */
public final class FrameDescriptor implements Cloneable {

    private final Object defaultValue;
    private final ArrayList slots;
    private final HashMap identifierToSlotMap;
    private Assumption version;
    private HashMap identifierToNotInFrameAssumptionMap;

    /**
     * Flag that can be used by the runtime to track that {@link Frame#materialize()} was called on
     * a frame that has this descriptor. Since the flag is not public API, access is encapsulated
     * via {@link TVMCI}.
     *
     * @since 0.14
     */
    boolean materializeCalled;

    private static final String NEVER_PART_OF_COMPILATION_MESSAGE = "interpreter-only. includes hashmap operations.";

    /**
     * Constructs empty descriptor. The {@link #getDefaultValue()} is null.
     *
     * @since 0.8 or earlier
     */
    public FrameDescriptor() {
        this(null);
    }

    /**
     * Constructs new descriptor with specified {@link #getDefaultValue()}.
     *
     * @param defaultValue to be returned from {@link #getDefaultValue()}
     * @since 0.8 or earlier
     */
    public FrameDescriptor(Object defaultValue) {
        CompilerAsserts.neverPartOfCompilation("do not create a FrameDescriptor from compiled code");
        this.defaultValue = defaultValue;
        slots = new ArrayList<>();
        identifierToSlotMap = new HashMap<>();
        version = createVersion();
    }

    /**
     * Use {@link #FrameDescriptor()}.
     *
     * @return new instance of the descriptor
     * @deprecated
     * @since 0.8 or earlier
     */
    @Deprecated
    public static FrameDescriptor create() {
        return new FrameDescriptor();
    }

    /**
     * Use {@link #FrameDescriptor(java.lang.Object) }.
     *
     * @return new instance of the descriptor
     * @deprecated
     * @since 0.8 or earlier
     */
    @Deprecated
    public static FrameDescriptor create(Object defaultValue) {
        return new FrameDescriptor(defaultValue);
    }

    /**
     * Adds frame slot. Delegates to
     * {@link #addFrameSlot(java.lang.Object, java.lang.Object, com.oracle.truffle.api.frame.FrameSlotKind)
     * addFrameSlot}(identifier, null, {@link FrameSlotKind#Illegal}). This is a slow
     * operation that switches to interpreter mode.
     *
     * @param identifier key for the slot
     * @return the newly created slot
     * @since 0.8 or earlier
     */
    public FrameSlot addFrameSlot(Object identifier) {
        return addFrameSlot(identifier, null, FrameSlotKind.Illegal);
    }

    /**
     * Adds frame slot. Delegates to
     * {@link #addFrameSlot(java.lang.Object, java.lang.Object, com.oracle.truffle.api.frame.FrameSlotKind)
     * addFrameSlot}(identifier, null, kind). This is a slow operation
     * that switches to interpreter mode.
     *
     * @param identifier key for the slot
     * @param kind the kind of the new slot
     * @return the newly created slot
     * @since 0.8 or earlier
     */
    public FrameSlot addFrameSlot(Object identifier, FrameSlotKind kind) {
        return addFrameSlot(identifier, null, kind);
    }

    /**
     * Adds new frame slot to {@link #getSlots()} list. This is a slow operation that switches to
     * interpreter mode.
     *
     * @param identifier key for the slot - it needs proper {@link #equals(java.lang.Object)} and
     *            {@link Object#hashCode()} implementations
     * @param info additional {@link FrameSlot#getInfo() information for the slot}
     * @param kind the kind of the new slot
     * @return the newly created slot
     * @throws IllegalArgumentException if a frame slot with the same identifier exists
     * @since 0.8 or earlier
     */
    public FrameSlot addFrameSlot(Object identifier, Object info, FrameSlotKind kind) {
        CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
        if (identifierToSlotMap.containsKey(identifier)) {
            throw new IllegalArgumentException("duplicate frame slot: " + identifier);
        }
        FrameSlot slot = new FrameSlot(this, identifier, info, kind, slots.size());
        slots.add(slot);
        identifierToSlotMap.put(identifier, slot);
        updateVersion();
        invalidateNotInFrameAssumption(identifier);
        return slot;
    }

    /**
     * Finds an existing slot. This is a slow operation.
     *
     * @param identifier the key of the slot to search for
     * @return the slot or null
     * @since 0.8 or earlier
     */
    public FrameSlot findFrameSlot(Object identifier) {
        CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
        return identifierToSlotMap.get(identifier);
    }

    /**
     * Finds an existing slot or creates new one. This is a slow operation.
     *
     * @param identifier the key of the slot to search for
     * @return the slot
     * @since 0.8 or earlier
     */
    public FrameSlot findOrAddFrameSlot(Object identifier) {
        FrameSlot result = findFrameSlot(identifier);
        if (result != null) {
            return result;
        }
        return addFrameSlot(identifier);
    }

    /**
     * Finds an existing slot or creates new one. This is a slow operation.
     *
     * @param identifier the key of the slot to search for
     * @param kind the kind for the newly created slot
     * @return the found or newly created slot
     * @since 0.8 or earlier
     */
    public FrameSlot findOrAddFrameSlot(Object identifier, FrameSlotKind kind) {
        FrameSlot result = findFrameSlot(identifier);
        if (result != null) {
            return result;
        }
        return addFrameSlot(identifier, kind);
    }

    /**
     * Finds an existing slot or creates new one. This is a slow operation.
     *
     * @param identifier the key of the slot to search for
     * @param info info for the newly created slot
     * @param kind the kind for the newly created slot
     * @return the found or newly created slot
     * @since 0.8 or earlier
     */
    public FrameSlot findOrAddFrameSlot(Object identifier, Object info, FrameSlotKind kind) {
        FrameSlot result = findFrameSlot(identifier);
        if (result != null) {
            return result;
        }
        return addFrameSlot(identifier, info, kind);
    }

    /**
     * Removes a slot. If the identifier is found, its slot is removed from this descriptor. This is
     * a slow operation.
     *
     * @param identifier identifies the slot to remove
     * @throws IllegalArgumentException if no such frame slot exists
     * @since 0.8 or earlier
     */
    public void removeFrameSlot(Object identifier) {
        CompilerAsserts.neverPartOfCompilation(NEVER_PART_OF_COMPILATION_MESSAGE);
        if (!identifierToSlotMap.containsKey(identifier)) {
            throw new IllegalArgumentException("no such frame slot: " + identifier);
        }
        slots.remove(identifierToSlotMap.get(identifier));
        identifierToSlotMap.remove(identifier);
        updateVersion();
        getNotInFrameAssumption(identifier);
    }

    /**
     * Returns number of slots in the descriptor.
     *
     * @return the same value as {@link #getSlots()}.{@link List#size()} would return
     * @since 0.8 or earlier
     */
    public int getSize() {
        return slots.size();
    }

    /**
     * Current set of slots in the descriptor.
     *
     * @return unmodifiable list of {@link FrameSlot}
     * @since 0.8 or earlier
     */
    public List getSlots() {
        return Collections.unmodifiableList(slots);
    }

    /**
     * Retrieve the list of all the identifiers associated with this frame descriptor.
     *
     * @return the list of all the identifiers in this frame descriptor
     * @since 0.8 or earlier
     */
    public Set getIdentifiers() {
        return Collections.unmodifiableSet(identifierToSlotMap.keySet());
    }

    /**
     * Deeper copy of the descriptor. Copies all slots in the descriptor, but only their
     * {@linkplain FrameSlot#getIdentifier() identifier} and {@linkplain FrameSlot#getInfo() info}
     * but not their {@linkplain FrameSlot#getKind() kind}!
     *
     * @return new instance of a descriptor with copies of values from this one
     * @since 0.8 or earlier
     */
    public FrameDescriptor copy() {
        FrameDescriptor clonedFrameDescriptor = new FrameDescriptor(this.defaultValue);
        for (int i = 0; i < slots.size(); i++) {
            FrameSlot slot = slots.get(i);
            clonedFrameDescriptor.addFrameSlot(slot.getIdentifier(), slot.getInfo(), FrameSlotKind.Illegal);
        }
        return clonedFrameDescriptor;
    }

    /**
     * Shallow copy of the descriptor. Re-uses the existing slots in new descriptor. As a result, if
     * you {@link FrameSlot#setKind(com.oracle.truffle.api.frame.FrameSlotKind) change kind} of one
     * of the slots it is changed in the original as well as in the shallow copy.
     *
     * @return new instance of a descriptor with copies of values from this one
     * @since 0.8 or earlier
     */
    public FrameDescriptor shallowCopy() {
        FrameDescriptor clonedFrameDescriptor = new FrameDescriptor(this.defaultValue);
        clonedFrameDescriptor.slots.addAll(slots);
        clonedFrameDescriptor.identifierToSlotMap.putAll(identifierToSlotMap);
        return clonedFrameDescriptor;
    }

    /**
     * Invalidates the current, and create a new version assumption.
     */
    void updateVersion() {
        version.invalidate();
        version = createVersion();
    }

    /**
     * Returns an assumption reflecting the frame's current version, which is updated every time a
     * slot is added or removed, or an existing slot's kind is changed. This assumption is
     * associated with compiled code that depends on the internal frame layout.
     *
     * @return an assumption invalidated when a slot is added or removed, or a slot kind changed.
     * @since 0.8 or earlier
     */
    public Assumption getVersion() {
        return version;
    }

    private static Assumption createVersion() {
        return Truffle.getRuntime().createAssumption("frame version");
    }

    /**
     * Default value for the created slots.
     *
     * @return value provided to {@link #FrameDescriptor(java.lang.Object)}
     * @since 0.8 or earlier
     */
    public Object getDefaultValue() {
        return defaultValue;
    }

    /**
     * Make an assumption that no slot with the specified identifier is present in this frame
     * descriptor. Invalidated when a frame slot with the identifier is added.
     *
     * @param identifier frame slot identifier
     * @return an assumption that this frame descriptor does not contain a slot with the identifier
     * @throws IllegalArgumentException if the frame descriptor contains a slot with the identifier
     * @since 0.8 or earlier
     */
    public Assumption getNotInFrameAssumption(Object identifier) {
        if (identifierToSlotMap.containsKey(identifier)) {
            throw new IllegalArgumentException("Cannot get not-in-frame assumption for existing frame slot!");
        }

        if (identifierToNotInFrameAssumptionMap == null) {
            identifierToNotInFrameAssumptionMap = new HashMap<>();
        } else {
            Assumption assumption = identifierToNotInFrameAssumptionMap.get(identifier);
            if (assumption != null) {
                return assumption;
            }
        }
        Assumption assumption = Truffle.getRuntime().createAssumption("identifier not in frame");
        identifierToNotInFrameAssumptionMap.put(identifier, assumption);
        return assumption;
    }

    private void invalidateNotInFrameAssumption(Object identifier) {
        if (identifierToNotInFrameAssumptionMap != null) {
            Assumption assumption = identifierToNotInFrameAssumptionMap.get(identifier);
            if (assumption != null) {
                assumption.invalidate();
                identifierToNotInFrameAssumptionMap.remove(identifier);
            }
        }
    }

    /** @since 0.8 or earlier */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("FrameDescriptor@").append(Integer.toHexString(hashCode()));
        sb.append("{");
        boolean comma = false;
        for (FrameSlot slot : slots) {
            if (comma) {
                sb.append(", ");
            } else {
                comma = true;
            }
            sb.append(slot.getIndex()).append(":").append(slot.getIdentifier());
        }
        sb.append("}");
        return sb.toString();
    }

    /** @since 0.14 */
    static final class AccessorFrames extends Accessor {
        @Override
        protected Frames framesSupport() {
            return new FramesImpl();
        }

        static final class FramesImpl extends Frames {
            @Override
            protected void markMaterializeCalled(FrameDescriptor descriptor) {
                descriptor.materializeCalled = true;
            }

            @Override
            protected boolean getMaterializeCalled(FrameDescriptor descriptor) {
                return descriptor.materializeCalled;
            }
        }
    }

    static {
        // registers into Accessor.FRAMES
        @SuppressWarnings("unused")
        AccessorFrames unused = new AccessorFrames();
    }
}