/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* RTResultFunction.java
* Created: Jan 22, 2002 at 2:16:12 PM
* By: Raymond Cypher
*/
package org.openquark.cal.internal.runtime.lecc;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalValue;
/**
* @author Raymond Cypher
*
* This class is the root class for functional nodes that need
* a pointer for an assigned result.
*/
public abstract class RTResultFunction extends RTFunction {
/** The result, once evaluated (otherwise this can be an application chain for functions) */
protected RTValue result;
/** Back pointer used to avoid indirection chains. */
private RTResultFunction parent;
/** true if runtime stats are being generated, which results in a slower evaluate method. */
private static final boolean HAS_RUNTIME_STATS =
LECCMachineConfiguration.generateStatistics();
public final void setResult (RTValue res) {
res = res.getValue();
if (res != this) {
result = res;
if (parent == null) {
if (res instanceof RTResultFunction) {
((RTResultFunction)res).setParent(this);
}
} else {
parent.setResult(res);
}
}
}
/**
* Get the value without forcing evaluation.
* Try to follow any indirection chains to
* their conclusion.
* @return RTValue the value
*/
@Override
public RTValue getValue() {
if (result == null) {
return this;
}
return (result = result.getValue());
}
/**
* Return the evaluated RTValue. This may cause the value to be evaluated
* if this hasn't already been done. The memoised result is returned.
* @param ec
* @return RTValue the memoised result of evaluating this RTValue
* @throws CALExecutorException
*/
@Override
public RTValue evaluate(RTExecutionContext ec) throws CALExecutorException {
if (LECCMachineConfiguration.concurrentRuntime()) {
return synchronizedEvaluate(ec);
}
return unsynchronizedEvaluate(ec);
}
//WARNING: the implementation of this method must be kept compatible with synchronizedEvaluate
private final RTValue unsynchronizedEvaluate(RTExecutionContext ec) throws CALExecutorException {
if (!LECCMachineConfiguration.nonInterruptibleRuntime() && ec.isQuitRequested()) {
throw RTValue.INTERRUPT_EXCEPTION;
}
RTValue newResult = result == null ? this : result;
RTValue lastResult;
//do not repeatedly poll the SourceGenerationConfiguration settings in the most performance important common case
//where we are not generating any runtime statistics
if (RTResultFunction.HAS_RUNTIME_STATS) {
// Attempt to reduce this result
do {
if (LECCMachineConfiguration.generateStatistics()) {
ec.incrementNReductions();
}
lastResult = newResult;
newResult = newResult.reduce(ec);
} while (lastResult != newResult);
} else {
// Attempt to reduce this result
do {
lastResult = newResult;
newResult = newResult.reduce(ec);
} while (lastResult != newResult);
}
if (newResult != this) {
setResult (newResult);
}
return newResult;
}
//WARNING: the implementation of this method must be kept compatible with unsynchronizedEvaluate
synchronized private final RTValue synchronizedEvaluate(RTExecutionContext ec) throws CALExecutorException {
if (!LECCMachineConfiguration.nonInterruptibleRuntime() && ec.isQuitRequested()) {
throw RTValue.INTERRUPT_EXCEPTION;
}
RTValue newResult = result == null ? this : result;
RTValue lastResult;
//do not repeatedly poll the SourceGenerationConfiguration settings in the most performance important common case
//where we are not generating any runtime statistics
if (RTResultFunction.HAS_RUNTIME_STATS) {
// Attempt to reduce this result
do {
if (LECCMachineConfiguration.generateStatistics()) {
ec.incrementNReductions();
}
lastResult = newResult;
newResult = newResult.synchronizedReduce(ec);
} while (lastResult != newResult);
} else {
// Attempt to reduce this result
do {
lastResult = newResult;
newResult = newResult.synchronizedReduce(ec);
} while (lastResult != newResult);
}
if (newResult != this) {
setResult (newResult);
}
return newResult;
}
@Override
synchronized final protected RTValue synchronizedReduce(RTExecutionContext ec) throws CALExecutorException {
return reduce(ec);
}
/**
* Set the parent reference. This allows the root of the graph to be
* updated as each intermediate result is further reduced. This avoids
* building up long indirection chains.
* @param parent
*/
private final void setParent (final RTResultFunction parent) {
this.parent = parent;
}
/**
* Release any held graph nodes.
*/
public abstract void clearMembers();
/**
* The method returns the argument value i.e. right hand branch of
* a general application node.
* @return the right hand branch as appropriate
*/
@Override
public RTValue getArgValue () {
return result.getArgValue();
}
/**
* Return the previous argument (until we reach the root)
* @return the previous argument
*/
@Override
public RTValue prevArg() {
// Note: In the normal course of graph reduction this method will not be
// called before result is set. An explicit check for a null result value
// is omitted for performance reasons.
return result.prevArg();
}
/**
* Function used for generalized traversal to the
* left hand side of a program graph.
* @return the next graph node to the left, null if there is nothing to the left.
* {@inheritDoc}
*/
@Override
RTValue lhs () {
if(result != null) {
return result;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public int debug_getNChildren() {
if (result == null) {
throw new IllegalStateException("should call the overidden version if result == null");
}
return 1;
}
/**
* {@inheritDoc}
*/
@Override
public CalValue debug_getChild(int childN) {
if (result == null) {
throw new IllegalStateException("should call the overidden version if result == null");
}
if (childN == 0) {
return result;
}
throw new IndexOutOfBoundsException();
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeStartText() {
if (result == null) {
throw new IllegalStateException("should call the overidden version if result == null");
}
return "";
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeEndText() {
if (result == null) {
throw new IllegalStateException("should call the overidden version if result == null");
}
return "";
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getChildPrefixText(int childN) {
if (result == null) {
throw new IllegalStateException("should call the overidden version if result == null");
}
if (childN == 0) {
return "";
}
throw new IndexOutOfBoundsException();
}
/**
* {@inheritDoc}
*/
@Override
public final boolean debug_isIndirectionNode() {
return result != null;
}
/**
* {@inheritDoc}
*/
@Override
public final DataType getDataType() {
if (result != null) {
return result.getDataType();
} else {
return DataType.OTHER;
}
}
}