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

org.jruby.truffle.language.objects.WriteObjectFieldNode Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014, 2016 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.language.objects;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.FinalLocationException;
import com.oracle.truffle.api.object.IncompatibleLocationException;
import com.oracle.truffle.api.object.Location;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import org.jruby.truffle.language.RubyBaseNode;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.objects.shared.SharedObjects;
import org.jruby.truffle.language.objects.shared.WriteBarrierNode;

@ImportStatic({ RubyGuards.class, ShapeCachingGuards.class })
public abstract class WriteObjectFieldNode extends RubyBaseNode {

    private final Object name;

    public WriteObjectFieldNode(Object name) {
        this.name = name;
    }

    public Object getName() {
        return name;
    }

    public abstract void execute(DynamicObject object, Object value);

    @Specialization(
            guards = {
                    "location != null",
                    "object.getShape() == cachedShape"
            },
            assumptions = { "cachedShape.getValidAssumption()", "validLocation" },
            limit = "getCacheLimit()")
    public void writeExistingField(DynamicObject object, Object value,
            @Cached("getLocation(object, value)") Location location,
            @Cached("object.getShape()") Shape cachedShape,
            @Cached("createAssumption()") Assumption validLocation,
            @Cached("isShared(cachedShape)") boolean shared,
            @Cached("createWriteBarrierNode(shared)") WriteBarrierNode writeBarrierNode) {
        try {
            if (shared) {
                writeBarrierNode.executeWriteBarrier(value);
                synchronized (object) {
                    // Re-check the shape under the monitor as another thread might have changed it
                    // by adding a field (fine) or upgrading an existing field to Object storage
                    // (need to use the new storage)
                    if (object.getShape() != cachedShape) {
                        CompilerDirectives.transferToInterpreter();
                        execute(object, value);
                        return;
                    }
                    location.set(object, value, cachedShape);
                }
            } else {
                location.set(object, value, cachedShape);
            }
        } catch (IncompatibleLocationException | FinalLocationException e) {
            // remove this entry
            validLocation.invalidate();
            execute(object, value);
        }
    }

    @Specialization(
            guards = {
                    "location == null",
                    "object.getShape() == oldShape" },
            assumptions = { "oldShape.getValidAssumption()", "newShape.getValidAssumption()", "validLocation" },
            limit = "getCacheLimit()")
    public void writeNewField(DynamicObject object, Object value,
            @Cached("getLocation(object, value)") Location location,
            @Cached("object.getShape()") Shape oldShape,
            @Cached("defineProperty(oldShape, value)") Shape newShape,
            @Cached("getNewLocation(newShape)") Location newLocation,
            @Cached("createAssumption()") Assumption validLocation,
            @Cached("isShared(oldShape)") boolean shared,
            @Cached("createWriteBarrierNode(shared)") WriteBarrierNode writeBarrierNode) {
        try {
            if (shared) {
                writeBarrierNode.executeWriteBarrier(value);
                synchronized (object) {
                    // Re-check the shape under the monitor as another thread might have changed it
                    // by adding a field or upgrading an existing field to Object storage
                    // (we need to make sure to have the right shape to add the new field)
                    if (object.getShape() != oldShape) {
                        CompilerDirectives.transferToInterpreter();
                        execute(object, value);
                        return;
                    }
                    newLocation.set(object, value, oldShape, newShape);
                }
            } else {
                newLocation.set(object, value, oldShape, newShape);
            }
        } catch (IncompatibleLocationException e) {
            // remove this entry
            validLocation.invalidate();
            newShape.getLastProperty().setGeneric(object, value, oldShape, newShape);
        }
    }

    @Specialization(guards = "updateShape(object)")
    public void updateShapeAndWrite(DynamicObject object, Object value) {
        execute(object, value);
    }

    @TruffleBoundary
    @Specialization(contains = { "writeExistingField", "writeNewField", "updateShapeAndWrite" })
    public void writeUncached(DynamicObject object, Object value) {
        final boolean shared = SharedObjects.isShared(object);
        if (shared) {
            SharedObjects.writeBarrier(value);
            synchronized (object) {
                Shape shape = object.getShape();
                Shape newShape = defineProperty(shape, value);
                newShape.getProperty(name).setSafe(object, value, shape, newShape);
            }
        } else {
            object.define(name, value, 0);
        }
    }

    protected Location getLocation(DynamicObject object, Object value) {
        final Shape oldShape = object.getShape();
        final Property property = oldShape.getProperty(name);

        if (PropertyFlags.isDefined(property) && property.getLocation().canSet(object, value)) {
            return property.getLocation();
        } else {
            return null;
        }
    }

    protected Shape defineProperty(Shape oldShape, Object value) {
        Property property = oldShape.getProperty(name);
        if (property != null && PropertyFlags.isRemoved(property)) {
            // Do not reuse location of removed properties
            Location location = oldShape.allocator().locationForValue(value);
            return oldShape.replaceProperty(property, property.relocate(location).copyWithFlags(0));
        } else {
            return oldShape.defineProperty(name, value, 0);
        }
    }

    protected Location getNewLocation(Shape newShape) {
        return newShape.getProperty(name).getLocation();
    }

    protected Assumption createAssumption() {
        return Truffle.getRuntime().createAssumption("object location is valid");
    }

    protected int getCacheLimit() {
        return getContext().getOptions().INSTANCE_VARIABLE_CACHE;
    }

    protected boolean isShared(Shape shape) {
        return SharedObjects.isShared(shape);
    }

    protected WriteBarrierNode createWriteBarrierNode(boolean shared) {
        if (shared) {
            return WriteBarrierNode.create();
        } else {
            return null;
        }
    }

    @Override
    public String toString() {
        return name + " =";
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy