package som.interpreter.nodes;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
import com.oracle.truffle.api.source.SourceSection;

import som.VM;
import som.interpreter.TruffleCompiler;
import som.interpreter.nodes.nary.UnaryExpressionNode;
import som.primitives.ObjectPrims.IsValue;
import som.vm.constants.KernelObj;
import som.vmobjects.SObject.SImmutableObject;


/**
 * This node is used for the instantiation of objects to check whether all
 * fields have been initialized to values, in case, the object is declared
 * as a Value.
 */
public abstract class IsValueCheckNode extends UnaryExpressionNode {

  public static IsValueCheckNode create(final SourceSection source, final ExpressionNode self) {
    return new UninitializedNode(source, self);
  }

  @Child protected ExpressionNode self;

  protected IsValueCheckNode(final SourceSection source, final ExpressionNode self) {
    super(false, source);
    this.self = self;
  }

  private static final class UninitializedNode extends IsValueCheckNode {
    UninitializedNode(final SourceSection source, final ExpressionNode self) {
      super(source, self);
    }

    @Override
    public Object executeGeneric(final VirtualFrame frame) {
      TruffleCompiler.transferToInterpreterAndInvalidate("Need to specialize node");
      return super.executeGeneric(frame);
    }

    @Override
    public Object executeEvaluated(final VirtualFrame frame, final Object receiver) {
      return specialize(frame, receiver);
    }

    private Object specialize(final VirtualFrame frame, final Object receiver) {
      if (!(receiver instanceof SImmutableObject)) {
        // can remove ourselves, this node is only used in initializers,
        // which are by definition monomorphic
        replace(self);
        return receiver;
      }

      SImmutableObject rcvr = (SImmutableObject) receiver;

      if (rcvr.isValue()) {
        return replace(new ValueCheckNode(sourceSection, self)).
            executeEvaluated(frame, receiver);
      } else {
        // neither transfer object nor value, so nothing to check
        replace(self);
        return receiver;
      }
    }
  }

  private static final class ValueCheckNode extends IsValueCheckNode {
    ValueCheckNode(final SourceSection source, final ExpressionNode self) {
      super(source, self);
    }

    @Override
    public Object executeEvaluated(final VirtualFrame frame, final Object receiver) {
      SImmutableObject rcvr = (SImmutableObject) receiver;

      boolean allFieldsContainValues = allFieldsContainValues(rcvr);
      if (allFieldsContainValues) {
        return rcvr;
      }
      return KernelObj.signalException("signalNotAValueWith:", receiver);
    }

    private boolean allFieldsContainValues(final SImmutableObject rcvr) {
      VM.thisMethodNeedsToBeOptimized("Should be optimized or on slowpath");

      if (rcvr.field1 == null) {
        return true;
      }

      boolean result = IsValue.isObjectValue(rcvr.field1);
      if (rcvr.field2 == null || !result) {
        return result;
      }

      result = result && IsValue.isObjectValue(rcvr.field2);
      if (rcvr.field3 == null || !result) {
        return result;
      }

      result = result && IsValue.isObjectValue(rcvr.field3);
      if (rcvr.field4 == null || !result) {
        return result;
      }

      result = result && IsValue.isObjectValue(rcvr.field4);
      if (rcvr.field5 == null || !result) {
        return result;
      }

      result = result && IsValue.isObjectValue(rcvr.field5);
      if (rcvr.getExtensionObjFields() == null || !result) {
        return result;
      }

      Object[] ext = rcvr.getExtensionObjFields();
      for (Object o : ext) {
        if (!IsValue.isObjectValue(o)) {
          return false;
        }
      }
      return true;
    }
  }

  @Override
  public Object executeGeneric(final VirtualFrame frame) {
    return executeEvaluated(frame, self.executeGeneric(frame));
  }

  @Override
  protected final boolean isTaggedWithIgnoringEagerness(final Class<?> tag) {
    if (tag == StatementTag.class) {
      return false;
    }
    return super.isTaggedWithIgnoringEagerness(tag);
  }
}

@@ -148,16 +148,139 @@
 entTag;%0A
+import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode;%0Aimport com.oracle.truffle.api.nodes.Node;%0A
 import c
@@ -1937,36 +1937,38 @@
 hic%0A        
-replace(self
+dropCheckNode(
 );%0A        r
@@ -2285,46 +2285,357 @@
     
-replace(self);%0A        return receiver
+dropCheckNode();%0A        return receiver;%0A      %7D%0A    %7D%0A%0A    private void dropCheckNode() %7B%0A      if (getParent() instanceof WrapperNode) %7B%0A        // in case there is a wrapper, get rid of it first%0A        Node wrapper = getParent();%0A        wrapper.replace(self);%0A        VM.insertInstrumentationWrapper(self);%0A      %7D else %7B%0A        replace(self)
 ;%0A  

Fix issue with instrumentation and dropping of IsValueCheckNode