/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#include "platform.h"
#include
#include "jliball.hpp"
#include "jlib.hpp"
#include "jexcept.hpp"
#include "jmisc.hpp"
#include "javahash.hpp"
#include "jmd5.hpp"
#include "jfile.hpp"
#include "eclhelper.hpp"
#include "hql.hpp"
#include "hqlfunc.hpp"
#include "hqlattr.hpp"
#include "hqlcpp.ipp"
#include "hqlwcpp.hpp"
#include "hqlcpputil.hpp"
#include "hqlres.hpp"
#include "hqlerror.hpp"
#include "hqlcerrors.hpp"
#include "hqlcatom.hpp"
#include "hqlpmap.hpp"
#include "hqlthql.hpp"
#include "hqlfold.hpp"
#include "eclrtl.hpp"
#include "hqllib.ipp"
#include "hqlnlp.ipp"
#include "hqlutil.hpp"
#include "hqltcppc.ipp"
#include "hqlttcpp.ipp"
#include "hqlccommon.hpp"
#include "hqlopt.hpp"
#include "hqlpopt.hpp"
#include "hqlcse.ipp"
#include "thorplugin.hpp"
#ifdef _DEBUG
//#define ADD_ASSIGNMENT_COMMENTS
//#define ADD_RESOURCE_AS_CPP_COMMENT
#endif
//Defaults for various options.
#define COMPLEXITY_TO_HOIST 2
#define INLINE_COMPARE_THRESHOLD 2 //7 // above this, a loop is generated
#define MAX_NESTED_CASES 8
#define MAX_SIMPLE_VAR_SIZE 99999
#define MAX_STATIC_ROW_SIZE 10000
#define MAX_LOCAL_ROW_SIZE 32
#define DEFAULT_NLP_DETAIL 1
#ifdef _WIN32
#define DEFAULT_ACTIVITIES_PER_CPP 800 // windows compiler is fast, linker is slow, but compiler also has quite a small compile limit
#else
#define DEFAULT_ACTIVITIES_PER_CPP 500 // gcc assembler is v.slow
#endif
//MORE: Simple vars don't work if they are made class members...
//#define SEARCH_VARIABLE "v211"
//===========================================================================
static CriticalSection * systemCS;
static IHqlScope * cppSystemScope;
//===========================================================================
MODULE_INIT(INIT_PRIORITY_STANDARD)
{
systemCS = new CriticalSection;
return true;
}
MODULE_EXIT()
{
::Release(cppSystemScope);
cppSystemScope = NULL;
delete systemCS;
}
//---------------------------------------------------------------------------
class SubStringInfo : public SubStringHelper
{
public:
SubStringInfo(IHqlExpression * _expr) : SubStringHelper(_expr) { expr = _expr; }
void bindToFrom(HqlCppTranslator & translator, BuildCtx & ctx);
public:
IHqlExpression * expr;
CHqlBoundExpr boundFrom;
CHqlBoundExpr boundTo;
};
void SubStringInfo::bindToFrom(HqlCppTranslator & translator, BuildCtx & ctx)
{
if (to && to->isAttribute())
throwError(HQLERR_StarRangeOnlyInJoinCondition);
if (from == to)
{
if (from)
{
translator.buildSimpleExpr(ctx, from, boundFrom);
boundTo.expr.set(boundFrom.expr);
}
}
else
{
if (from)
translator.buildCachedExpr(ctx, from, boundFrom);
if (to)
translator.buildCachedExpr(ctx, to, boundTo);
}
}
//---------------------------------------------------------------------------
IHqlExpression * DatasetReference::querySelector() const
{
if (side == no_none)
return ds->queryNormalizedSelector();
return selector;
}
IHqlExpression * DatasetReference::querySelSeq() const
{
if (side == no_none)
return NULL;
return selector->queryChild(1);
}
IHqlExpression * DatasetReference::mapCompound(IHqlExpression * expr, IHqlExpression * to) const
{
return replaceSelector(expr, querySelector(), to);
}
IHqlExpression * DatasetReference::mapScalar(IHqlExpression * expr, IHqlExpression * to) const
{
return replaceSelector(expr, querySelector(), to);
}
//---------------------------------------------------------------------------
extern ClusterType getClusterType(const char * platform, ClusterType dft)
{
if (stricmp(platform, "thor") == 0)
return ThorCluster;
if (stricmp(platform, "thorlcr") == 0)
return ThorLCRCluster;
if (stricmp(platform, "hthor") == 0)
return HThorCluster;
if (stricmp(platform, "roxie") == 0)
return RoxieCluster;
return dft;
}
IHqlExpression * createVariable(ITypeInfo * type)
{
StringBuffer tempName;
getUniqueId(tempName.append('v'));
#ifdef _DEBUG
#ifdef SEARCH_VARIABLE
if (stricmp(tempName.str(), SEARCH_VARIABLE)==0)
type = type;
#endif
#endif
return ::createVariable(tempName.str(), type);
}
IHqlExpression * convertWrapperToPointer(IHqlExpression * expr)
{
ITypeInfo * type = expr->queryType();
if (hasWrapperModifier(type))
return createValue(no_implicitcast, makeReferenceModifier(removeModifier(type, typemod_wrapper)), LINK(expr));
return LINK(expr);
}
IHqlExpression * ensureIndexable(IHqlExpression * expr)
{
ITypeInfo * type = expr->queryType();
if (type->getTypeCode() == type_data)
{
IHqlExpression * base = queryStripCasts(expr);
return createValue(no_implicitcast, makeReferenceModifier(makeStringType(type->getSize(), NULL, NULL)), LINK(base));
}
return convertWrapperToPointer(expr);
}
void extendConjunctionOwn(HqlExprAttr & cond, IHqlExpression * next)
{
if (cond)
next = createBoolExpr(no_and, cond.getClear(), next);
cond.setown(next);
}
inline bool isPushed(const IHqlExpression * expr)
{
return (expr->getOperator() == no_decimalstack);
}
inline bool isPushed(const CHqlBoundExpr & bound)
{
return isPushed(bound.expr);
}
bool isSimpleTranslatedStringExpr(IHqlExpression * expr)
{
loop
{
node_operator op = expr->getOperator();
switch (op)
{
case no_constant:
case no_variable:
case no_callback:
return true;
case no_cast:
case no_implicitcast:
case no_typetransfer:
case no_deref:
case no_address:
expr = expr->queryChild(0);
break;
case no_add:
case no_sub:
if (!isSimpleTranslatedStringExpr(expr->queryChild(1)))
return false;
expr = expr->queryChild(0);
break;
default:
return false;
}
}
}
bool isSimpleTranslatedExpr(IHqlExpression * expr)
{
switch (expr->queryType()->getTypeCode())
{
case type_data:
case type_string:
case type_qstring:
case type_varstring:
case type_decimal:
//Less strict rules for strings (and decimal), because string temporaries are more expensive.
return isSimpleTranslatedStringExpr(expr);
case type_set:
//for the moment assume set expressions are always simple once translated.
return true;
}
loop
{
node_operator op = expr->getOperator();
switch (op)
{
case no_constant:
case no_variable:
case no_callback:
case no_nullptr:
return true;
case no_typetransfer:
expr = expr->queryChild(0);
break;
default:
return false;
}
}
}
bool isFixedLengthList(IHqlExpression * expr)
{
switch (expr->getOperator())
{
case no_list:
case no_datasetlist:
case no_sortlist:
return true;
}
return false;
}
bool needVarStringCompare(ITypeInfo * leftType, ITypeInfo * rightType)
{
unsigned lSize = leftType->getSize();
unsigned rSize = rightType->getSize();
return (lSize != rSize) || (lSize == UNKNOWN_LENGTH);
}
_ATOM queryStrCompareFunc(ITypeInfo * realType)
{
switch (realType->getTypeCode())
{
case type_data:
return compareDataDataAtom;
case type_qstring:
return compareQStrQStrAtom;
}
ICharsetInfo * charset = realType->queryCharset();
_ATOM charsetName = charset->queryName();
if (charsetName == dataAtom)
return compareDataDataAtom;
if (charsetName == asciiAtom)
return compareStrStrAtom;
if (charsetName == ebcdicAtom)
return compareEStrEStrAtom;
assertex(!"Unknown string comparison");
return compareStrStrAtom;
}
IHqlExpression * getAddress(IHqlExpression * expr)
{
if (expr->getOperator() == no_deref)
{
IHqlExpression * address = expr->queryChild(0);
return LINK(address);
}
return createValue(no_address, makePointerType(expr->getType()), LINK(expr));
}
IHqlExpression * getRawAddress(IHqlExpression * expr)
{
OwnedHqlExpr raw = getAddress(expr);
loop
{
switch (raw->getOperator())
{
case no_cast:
case no_implicitcast:
break;
default:
return raw.getClear();
}
raw.set(raw->queryChild(0));
}
}
IHqlExpression * getPointer(IHqlExpression * source)
{
if (source->getOperator() == no_constant)
return LINK(source);
ITypeInfo * type = source->queryType();
Owned newType;
switch (type->getTypeCode())
{
case type_set:
if (type->isReference())
return LINK(source);
newType.setown(makeReferenceModifier(LINK(queryUnqualifiedType(type))));
if (hasWrapperModifier(type))
{
OwnedHqlExpr cast = createValue(no_implicitcast, LINK(newType), LINK(source));
return createValue(no_typetransfer, LINK(newType), LINK(cast));
}
break;
case type_table:
case type_groupedtable:
case type_row:
case type_decimal:
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_utf8:
if (isTypePassedByAddress(type))
{
if (type->isReference())
return LINK(source);
newType.setown(makeReferenceModifier(LINK(queryUnqualifiedType(type))));
if (hasLinkCountedModifier(type))
newType.setown(makeAttributeModifier(newType.getClear(), getLinkCountedAttr()));
if (hasWrapperModifier(type))
return createValue(no_implicitcast, LINK(newType), LINK(source));
//An array of X is implicitly converted to pointer to X so no need to do &a[0]
return createValue(no_typetransfer, LINK(newType), LINK(source));
}
else
{
newType.setown(removeModifier(type, typemod_wrapper));
if (hasWrapperModifier(type))
return createValue(no_implicitcast, LINK(newType), LINK(source));
return LINK(source);
}
break;
case type_pointer:
return LINK(source);
default:
newType.setown(makePointerType(LINK(type)));
break;
}
IHqlExpression * cur = source;
while (cur->getOperator() == no_typetransfer)
cur = cur->queryChild(0);
if (cur->getOperator() == no_deref)
{
IHqlExpression * address = cur->queryChild(0);
if (address->queryType() == newType)
return LINK(address);
return createValue(no_implicitcast, newType.getClear(), LINK(address));
}
else
return createValue(no_address, newType.getClear(), LINK(source));
}
bool isChildOf(IHqlExpression * parent, IHqlExpression * child)
{
unsigned max = parent->numChildren();
unsigned idx;
for (idx = 0; idx < max; idx++)
{
IHqlExpression * cur = parent->queryChild(idx);
if (cur == child)
return true;
}
return false;
}
bool canRemoveStringCast(ITypeInfo * to, ITypeInfo * from)
{
unsigned fromSize = from->getSize();
unsigned toSize = to->getSize();
//Special case string conversions that don't require us to copy any data.
if ((toSize == UNKNOWN_LENGTH) || ((fromSize != UNKNOWN_LENGTH) && (toSize <= fromSize)))
{
switch (from->getTypeCode())
{
case type_varstring:
if (toSize != UNKNOWN_LENGTH)
break;
//fall through
case type_data:
case type_string:
{
ICharsetInfo * srcset = from->queryCharset();
ICharsetInfo * tgtset = to->queryCharset();
//Data never calls a conversion function...
if ((srcset == tgtset) || (to->getTypeCode() == type_data) || (from->getTypeCode() == type_data))
return true;
}
case type_qstring:
return false;
}
}
return false;
}
bool isProjectedInRecord(IHqlExpression * record, IHqlExpression * expr)
{
unsigned max = record->numChildren();
unsigned idx;
for (idx = 0; idx < max; idx++)
{
IHqlExpression * cur = record->queryChild(idx);
if (cur->queryChild(0) == expr)
return true;
}
return false;
}
IHqlExpression * queryStripCasts(IHqlExpression * expr)
{
while ((expr->getOperator() == no_cast) || (expr->getOperator() == no_implicitcast))
expr = expr->queryChild(0);
return expr;
}
// Format the list is stored in doesn't matter, so allow constant strings to be stored by reference
IHqlExpression * getOptimialListFormat(IHqlExpression * table)
{
if (table->isConstant() && table->getOperator() == no_list)
{
ITypeInfo * elemType = table->queryType()->queryChildType();
if (!elemType->isReference())
{
switch (elemType->getTypeCode())
{
case type_string:
case type_data:
{
HqlExprArray args;
table->unwindList(args, no_list);
return createValue(no_list, makeSetType(makeReferenceModifier(LINK(elemType))), args);
}
}
}
}
return LINK(table);
}
bool canOptimizeAdjust(IHqlExpression * value)
{
ITypeInfo * type = value->queryType();
switch (value->getOperator())
{
case no_constant:
return true;
case no_add:
case no_sub:
return value->queryChild(1)->queryValue() != NULL;
}
return false;
}
IHqlExpression * adjustValue(IHqlExpression * value, __int64 delta)
{
if (delta == 0)
return LINK(value);
ITypeInfo * type = value->queryType();
switch (value->getOperator())
{
case no_constant:
{
__int64 newValue = value->queryValue()->getIntValue()+delta;
if (type == sizetType)
return getSizetConstant((size32_t)newValue);
return createConstant(type->castFrom(true, newValue));
}
case no_add:
{
IHqlExpression * lhs = value->queryChild(0);
IHqlExpression * rhs = value->queryChild(1);
IValue * rhsValue = rhs->queryValue();
IValue * lhsValue = lhs->queryValue();
if (rhsValue)
{
delta += rhsValue->getIntValue();
if (delta == 0)
return LINK(lhs);
value = lhs;
}
else if (lhsValue)
{
delta += lhsValue->getIntValue();
if (delta == 0)
return LINK(rhs);
value = rhs;
}
else if (canOptimizeAdjust(rhs))
return createValue(no_add, value->getType(), LINK(lhs), adjustValue(rhs, delta));
break;
}
case no_sub:
{
IValue * rhsValue = value->queryChild(1)->queryValue();
if (rhsValue)
{
IHqlExpression * lhs = value->queryChild(0);
delta -= rhsValue->getIntValue();
if (delta == 0)
return LINK(lhs);
value = lhs;
}
break;
}
case no_translated:
{
IHqlExpression * arg = value->queryChild(0);
if (arg->queryValue())
{
OwnedHqlExpr newValue = adjustValue(arg, delta);
return createTranslated(newValue);
}
break;
}
}
IHqlExpression * deltaExpr;
node_operator op = no_add;
if (delta < 0)
{
op = no_sub;
delta = -delta;
}
if (type == sizetType || !type->isInteger())
deltaExpr = getSizetConstant((size32_t)delta);
else
deltaExpr = createConstant(type->castFrom(true, delta));
return createValue(op, LINK(type), LINK(value), deltaExpr);
}
IHqlExpression * adjustIndexBaseToZero(IHqlExpression * index)
{
return adjustValue(index, -1);
}
IHqlExpression * adjustIndexBaseToOne(IHqlExpression * index)
{
return adjustValue(index, +1);
}
IHqlExpression * adjustBoundIntegerValues(IHqlExpression * left, IHqlExpression * right, bool subtract)
{
assertex(queryUnqualifiedType(left->queryType()) == queryUnqualifiedType(right->queryType()));
if (canOptimizeAdjust(left))
{
node_operator op = right->getOperator();
switch (op)
{
case no_constant:
{
__int64 rhsValue = right->queryValue()->getIntValue();
if (subtract)
rhsValue = -rhsValue;
return adjustValue(left, rhsValue);
}
case no_add:
case no_sub:
{
IHqlExpression * rl = right->queryChild(0);
IHqlExpression * rr = right->queryChild(1);
if (rr->getOperator() == no_constant)
{
ITypeInfo * rlt = rl->queryType();
ITypeInfo * rrt = rr->queryType();
if (queryUnqualifiedType(rl->queryType()) == queryUnqualifiedType(rr->queryType()))
{
__int64 delta = rr->queryValue()->getIntValue();
if (op == no_sub)
delta = -delta;
if (subtract)
delta = -delta;
OwnedHqlExpr newLeft = adjustValue(left, delta);
return adjustBoundIntegerValues(newLeft, rl, subtract);
}
}
break;
}
}
}
switch (left->getOperator())
{
case no_constant:
if (!subtract)
return adjustBoundIntegerValues(right, left, false);
break;
case no_add:
{
IHqlExpression * lr = left->queryChild(1);
if (lr->getOperator() == no_constant)
{
OwnedHqlExpr newLeft = adjustBoundIntegerValues(left->queryChild(0), right, subtract);
return adjustBoundIntegerValues(newLeft, lr, false);
}
break;
}
case no_variable:
if (!subtract && (right->getOperator() == no_add) && (right->queryChild(1)->getOperator() == no_constant))
{
OwnedHqlExpr temp = adjustBoundIntegerValues(left, right->queryChild(0), false);
return adjustBoundIntegerValues(temp, right->queryChild(1), false);
}
break;
}
return createValue(subtract ? no_sub : no_add, left->getType(), LINK(left), LINK(right));
}
IHqlExpression * multiplyValue(IHqlExpression * expr, unsigned __int64 value)
{
if (isZero(expr))
return LINK(expr);
ITypeInfo * type = expr->queryType();
IValue * exprValue = expr->queryValue();
if (exprValue && type->isInteger())
return createConstant(type->castFrom(false, exprValue->getIntValue() * value));
if (expr->getOperator() == no_translated)
{
IHqlExpression * translated = expr->queryChild(0);
if (translated->queryValue())
{
OwnedHqlExpr newValue = multiplyValue(translated, value);
return createTranslated(newValue);
}
}
return createValue(no_mul, LINK(type), LINK(expr), createConstant(type->castFrom(false, value)));
}
bool matchesConstValue(IHqlExpression * expr, __int64 matchValue)
{
IValue * value = expr->queryValue();
if (value)
return value->getIntValue() == matchValue;
if (expr->getOperator() == no_translated)
return matchesConstValue(expr->queryChild(0), matchValue);
return false;
}
IHqlExpression * createTranslated(IHqlExpression * expr)
{
return createValue(no_translated, expr->getType(), LINK(expr));
}
IHqlExpression * createTranslatedOwned(IHqlExpression * expr)
{
return createValue(no_translated, expr->getType(), expr);
}
IHqlExpression * createTranslated(IHqlExpression * expr, IHqlExpression * length)
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_table:
case type_groupedtable:
return createDataset(no_translated, LINK(expr), LINK(length));
}
return createValue(no_translated, expr->getType(), LINK(expr), LINK(length));
}
static IHqlExpression * querySimplifyCompareArgCast(IHqlExpression * expr)
{
if (expr->isConstant())
return expr;
while ((expr->getOperator() == no_implicitcast) || (expr->getOperator() == no_cast))
{
ITypeInfo * type = expr->queryType()->queryPromotedType();
switch (type->getTypeCode())
{
case type_string:
case type_data:
case type_unicode:
case type_qstring:
case type_utf8:
break;
default:
return expr;
}
IHqlExpression * child = expr->queryChild(0);
ITypeInfo * childType = child->queryType()->queryPromotedType();
if (type->getStringLen() < childType->getStringLen())
break;
type_t tc = type->getTypeCode();
if (tc != childType->getTypeCode())
{
if (tc == type_string)
{
if (childType->getTypeCode() != type_varstring)
break;
if (type->queryCharset() != childType->queryCharset())
break;
}
else if (tc == type_unicode)
{
if (childType->getTypeCode() != type_varunicode)
break;
if (type->queryLocale() != childType->queryLocale())
break;
}
else
break;
}
else
{
Owned stretched = getStretchedType(type->getStringLen(), childType);
if (stretched != type)
break;
}
expr = child;
}
return expr;
}
IHqlExpression * getSimplifyCompareArg(IHqlExpression * expr)
{
IHqlExpression * cast = querySimplifyCompareArgCast(expr);
if (cast->getOperator() != no_substring)
return LINK(cast);
if (cast->queryChild(0)->queryType()->getTypeCode() == type_qstring)
return LINK(cast);
HqlExprArray args;
unwindChildren(args, cast);
args.append(*createAttribute(quickAtom));
return cast->clone(args);
}
bool isNullAssign(const CHqlBoundTarget & target, IHqlExpression * expr)
{
ITypeInfo * targetType = target.expr->queryType();
//if an assignment to a local variable size temporary object, then it is ok to omit an assignment of null
//since it won't change its value, and it isn't going to be assigned more than once.
if ((targetType->getSize() == UNKNOWN_LENGTH) && target.length && hasWrapperModifier(targetType) && !hasModifier(targetType, typemod_member))
{
ITypeInfo * exprType = expr->queryType();
switch (exprType->getTypeCode())
{
case type_data:
case type_string:
case type_qstring:
return exprType->getSize() == 0;
case type_table:
return expr->getOperator() == no_null;
}
}
return false;
}
ExpressionFormat queryNaturalFormat(ITypeInfo * type)
{
if (hasOutOfLineModifier(type))
return FormatArrayDataset;
if (hasLinkCountedModifier(type))
return FormatLinkedDataset;
return FormatBlockedDataset;
}
//===========================================================================
SubGraphInfo::SubGraphInfo(IPropertyTree * _tree, unsigned _id, IHqlExpression * _graphTag, SubGraphType _type)
: HqlExprAssociation(subGraphMarker), tree(_tree)
{
id = _id;
type = _type;
graphTag.set(_graphTag);
}
//===========================================================================
IHqlExpression * CHqlBoundExpr::getTranslatedExpr() const
{
HqlExprArray args;
args.append(*LINK(expr));
if (length) args.append(*LINK(length));
if (count) args.append(*createAttribute(countAtom, LINK(count)));
if (isAll) args.append(*createAttribute(allAtom, LINK(isAll)));
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_table:
case type_groupedtable:
return createDataset(no_translated, args);
}
return createValue(no_translated, LINK(type), args);
}
IHqlExpression * CHqlBoundExpr::getIsAll() const
{
if (isAll)
return LINK(isAll);
return LINK(queryBoolExpr(false));
}
void CHqlBoundExpr::setFromTarget(const CHqlBoundTarget & target)
{
isAll.set(target.isAll);
count.set(target.count);
length.set(target.length);
expr.setown(convertWrapperToPointer(target.expr));
}
void CHqlBoundExpr::setFromTranslated(IHqlExpression * translatedExpr)
{
expr.set(translatedExpr->queryChild(0));
IHqlExpression * arg = translatedExpr->queryChild(1);
if (arg)
{
unsigned i = 2;
if (arg->getOperator() != no_attr)
{
length.set(arg);
arg = translatedExpr->queryChild(i++);
}
while (arg)
{
_ATOM name = arg->queryName();
if (name == countAtom)
count.set(arg->queryChild(0));
else if (name == allAtom)
isAll.set(arg->queryChild(0));
else
UNIMPLEMENTED;
arg = translatedExpr->queryChild(i++);
}
}
}
bool CHqlBoundTarget::extractFrom(const CHqlBoundExpr & bound)
{
ITypeInfo * boundType = bound.queryType();
if (bound.count)
{
if (bound.count->getOperator() != no_variable)
return false;
if (!hasLinkCountedModifier(boundType))
return false;
}
if (bound.isAll)
{
assertex(bound.isAll->getOperator() == no_variable);
}
else if (boundType->getTypeCode() == type_set)
return false;
if (bound.length)
{
if (bound.length->getOperator() != no_variable)
return false;
}
else if (boundType->getSize() == UNKNOWN_LENGTH)
{
type_t btc = boundType->getTypeCode();
if ((btc != type_varstring) && (btc != type_varunicode) && !hasLinkCountedModifier(boundType))
return false;
}
IHqlExpression * boundExpr = bound.expr;
if (boundExpr->getOperator() == no_implicitcast)
{
IHqlExpression * uncast = boundExpr->queryChild(0);
if (hasModifier(uncast->queryType(), typemod_member) &&
(queryUnqualifiedType(boundExpr->queryType()) == queryUnqualifiedType(uncast->queryType())))
boundExpr = uncast;
}
expr.set(boundExpr);
isAll.set(bound.isAll);
length.set(bound.length);
count.set(bound.count);
return true;
}
bool CHqlBoundTarget::isFixedSize() const
{
validate();
return queryType()->getSize() != UNKNOWN_LENGTH;
}
void CHqlBoundTarget::validate() const
{
if (expr)
{
if (queryType()->getSize() != UNKNOWN_LENGTH)
{
assertex(!length);
}
else if (isArrayRowset(queryType()))
assertex(count);
else
{
assertex(length || queryType()->getTypeCode() == type_varstring || queryType()->getTypeCode() == type_varunicode);
}
}
}
ITypeInfo * CHqlBoundTarget::queryType() const
{
return expr->queryType();
}
IHqlExpression * CHqlBoundTarget::getTranslatedExpr() const
{
CHqlBoundExpr temp;
temp.setFromTarget(*this);
return temp.getTranslatedExpr();
}
//===========================================================================
CompoundBuilder::CompoundBuilder(node_operator _op)
{
op = _op;
}
void CompoundBuilder::addOperand(IHqlExpression * arg)
{
if (first.get())
{
compound.setown(createOpenValue(op, makeBoolType()));
compound->addOperand(first.getClear());
}
if (compound)
compound->addOperand(arg);
else
first.setown(arg);
}
IHqlExpression * CompoundBuilder::getCompound()
{
if (compound)
return compound.getClear()->closeExpr();
return first.getClear();
}
//===========================================================================
void buildClearPointer(BuildCtx & ctx, IHqlExpression * expr, CompilerType compiler)
{
StringBuffer s;
generateExprCpp(s, expr, compiler).append("=NULL;");
ctx.addQuoted(s);
}
void insertUniqueString(StringAttrArray & array, const char * text)
{
ForEachItemIn(idx, array)
{
StringAttrItem & cur = array.item(idx);
if (stricmp(cur.text, text) == 0)
return;
}
array.append(* new StringAttrItem(text));
}
HqlCppInstance::HqlCppInstance(IWorkUnit *_wu, const char * _wupathname)
{
workunit.set(_wu);
wupathname.set(_wupathname);
}
HqlStmts * HqlCppInstance::ensureSection(_ATOM section)
{
HqlStmts * match = querySection(section);
if (match)
return match;
HqlCppSection * cur = new HqlCppSection;
cur->section = section;
sections.append(*cur);
return &cur->stmts;
}
void HqlCppInstance::processIncludes()
{
BuildCtx ctx(*this, includeAtom);
StringBuffer s;
ForEachItemIn(idx, includes)
{
s.clear().append("#include \"").append(includes.item(idx).text).append("\"");
ctx.addQuoted(s);
}
}
const char * HqlCppInstance::queryLibrary(unsigned idx)
{
if (modules.isItem(idx))
return modules.item(idx).text;
return NULL;
}
HqlStmts * HqlCppInstance::querySection(_ATOM section)
{
ForEachItemIn(idx, sections)
{
HqlCppSection & cur = (HqlCppSection &)sections.item(idx);
if (cur.section == section)
return &cur.stmts;
}
return NULL;
}
void HqlCppInstance::addPlugin(const char *plugin, const char *version, bool inThor)
{
if (!plugin || !*plugin)
return;
StringBuffer dllname(plugin);
getFileNameOnly(dllname, false); // MORE - shouldn't really need to do this here....
if (workunit)
{
Owned p = workunit->updatePluginByName(dllname.str());
p->setPluginVersion(version);
if (inThor)
p->setPluginThor(true);
else
p->setPluginHole(true);
}
if (!plugins)
plugins.setown(createPTree("Plugins"));
StringBuffer xpath;
xpath.append("Plugin[@dll='").append(dllname).append("']");
if (!plugins->hasProp(xpath.str()))
{
IPropertyTree * pluginNode = createPTree("Plugin");
pluginNode->setProp("@dll", dllname.str());
pluginNode->setProp("@version", version);
plugins->addPropTree("Plugin", pluginNode);
}
}
void HqlCppInstance::addPluginsAsResource()
{
if (!plugins)
return;
StringBuffer pluginXML;
toXML(plugins, pluginXML);
addResource("PLUGINS", 1, pluginXML.length(), pluginXML.str());
}
bool HqlCppInstance::useFunction(IHqlExpression * func)
{
assertex(func);
func = func->queryBody();
if (helpers.contains(*func))
return false;
helpers.append(*LINK(func));
IHqlExpression * funcDef = func->queryChild(0);
StringBuffer libname, init, include;
getProperty(funcDef, libraryAtom, libname);
getProperty(funcDef, initfunctionAtom, init);
getProperty(funcDef, includeAtom, include);
if (init.length())
{
BuildCtx ctx(*this, initAtom);
ctx.addQuoted(init.append("(wuid);"));
}
IHqlExpression *pluginAttr = funcDef->queryProperty(pluginAtom);
if (pluginAttr)
{
StringBuffer plugin, version;
pluginAttr->queryChild(0)->queryValue()->getStringValue(plugin);
pluginAttr->queryChild(1)->queryValue()->getStringValue(version);
addPlugin(plugin.str(), version.str(), false);
if (!libname.length())
{
pluginAttr->queryChild(0)->queryValue()->getStringValue(libname);
getFullFileName(libname, true);
}
}
if (!funcDef->hasProperty(ctxmethodAtom) && !funcDef->hasProperty(gctxmethodAtom) && !funcDef->hasProperty(methodAtom))
{
if (libname.length())
useLibrary(libname.str());
}
if (include.length())
useInclude(include.str());
return true;
}
void HqlCppInstance::useInclude(const char * include)
{
insertUniqueString(includes, include);
}
void HqlCppInstance::useLibrary(const char * libname)
{
insertUniqueString(modules, libname);
}
void HqlCppInstance::addHint(const char * hintXml, ICodegenContextCallback * ctxCallback)
{
if (!hintFile)
{
StringBuffer hintFilename;
if (wupathname)
hintFilename.append(wupathname);
else
hintFilename.append("wu");
hintFilename.append("_hints.xml");
Owned file = createIFile(hintFilename);
Owned io = file->open(IFOcreate);
if (!io)
return;
hintFile.setown(createIOStream(io));
appendHintText("\n");
Owned query = workunit->updateQuery();
associateLocalFile(query, FileTypeCpp, hintFilename.str(), "Hints", 0);
ctxCallback->registerFile(hintFilename.str(), "Hints");
}
appendHintText(hintXml);
}
void HqlCppInstance::appendHintText(const char * xml)
{
hintFile->write(strlen(xml), xml);
}
unsigned HqlCppInstance::addStringResource(unsigned len, const char * body)
{
return resources.addString(len, body);
}
void HqlCppInstance::addResource(const char * type, unsigned id, unsigned len, const void * body)
{
resources.addNamed(type, id, len, body);
}
void HqlCppInstance::addCompressResource(const char * type, unsigned id, unsigned len, const void * data)
{
#ifdef ADD_RESOURCE_AS_CPP_COMMENT
BuildCtx ctx(*this, includeAtom);
StringBuffer s;
s.append("/* ").append(type).append(".").append(id).append(":\n").append(len,(const char *)data).newline().append("*/");
ctx.addQuoted(s);
#endif
MemoryBuffer compressed;
compressResource(compressed, len, data);
addResource(type, id, compressed.length(), compressed.toByteArray());
}
void HqlCppInstance::flushHints()
{
if (hintFile)
{
appendHintText("\n");
hintFile.clear();
}
}
IPropertyTree * HqlCppInstance::ensureWebServiceInfo()
{
if (!webServiceInfo)
webServiceInfo.setown(createPTree("WebServicesInfo"));
return webServiceInfo;
}
void HqlCppInstance::addWebServicesResource()
{
if (webServiceInfo)
{
StringBuffer webXML;
toXML(webServiceInfo, webXML);
addCompressResource("SOAPINFO", 1000, webXML.length(), webXML.str());
}
}
void HqlCppInstance::addWebServices(IPropertyTree * info)
{
if (info)
mergePTree(ensureWebServiceInfo(), info);
}
void HqlCppInstance::flushResources(const char *filename, ICodegenContextCallback * ctxCallback)
{
addPluginsAsResource();
addWebServicesResource();
if (resources.count())
{
bool flushText = workunit->getDebugValueBool("flushResourceAsText", false);
StringBuffer path, trailing;
splitFilename(filename, &path, &path, &trailing, &trailing);
StringBuffer ln;
ln.append(path).append(SharedObjectPrefix).append(trailing).append(LibraryExtension);
#ifdef __64BIT__
bool target64bit = workunit->getDebugValueBool("target64bit", true);
#else
bool target64bit = workunit->getDebugValueBool("target64bit", false);
#endif
resources.flush(ln.str(), flushText, target64bit);
StringBuffer resTextName;
if (flushText && resources.queryWriteText(resTextName, ln))
{
Owned query = workunit->updateQuery();
associateLocalFile(query, FileTypeHintXml, resTextName, "Workunit resource text", 0);
ctxCallback->registerFile(resTextName, "Workunit Res.txt");
}
useLibrary(filename);
}
}
IHqlCppInstance * createCppInstance(IWorkUnit *wu, const char * wupathname)
{
return new HqlCppInstance(wu, wupathname);
}
//===========================================================================
#include "hqlcppsys.ecl"
HqlCppTranslator::HqlCppTranslator(IErrorReceiver * _errors, const char * _soName, IHqlCppInstance * _code, ClusterType _targetClusterType, ICodegenContextCallback *_ctxCallback) : ctxCallback(_ctxCallback)
{
targetClusterType = _targetClusterType;
{
CriticalBlock block(*systemCS);
if (!cppSystemScope)
{
StringBuffer systemText;
unsigned size = 0;
for (unsigned i1=0; cppSystemText[i1]; i1++)
size += strlen(cppSystemText[i1]) + 2;
systemText.ensureCapacity(size);
for (unsigned i2=0; cppSystemText[i2]; i2++)
systemText.append(cppSystemText[i2]).newline();
MultiErrorReceiver errs;
HqlDummyLookupContext ctx(&errs);
cppSystemScope = createScope();
Owned sysPath = createSourcePath("");
Owned systemContents = createFileContentsFromText(systemText.str(), sysPath);
OwnedHqlExpr query = parseQuery(cppSystemScope, systemContents, ctx, NULL, false);
if (errs.errCount())
{
StringBuffer errtext;
IECLError *first = errs.firstError();
first->toString(errtext);
throw MakeStringException(HQLERR_FailedToLoadSystemModule, "%s @ %d:%d", errtext.str(), first->getColumn(), first->getLine());
}
#if 0
else if (errs.warnCount())
{
StringBuffer s;
errs.toString(s);
PrintLog("Parsing system scope: ");
PrintLog(s.str());
}
#endif
}
}
errors = _errors;
hints = 0;
litno = 0;
soName.set(_soName);
HqlDummyLookupContext dummyctx(NULL);
OwnedHqlExpr internalScopeLookup = cppSystemScope->lookupSymbol(createIdentifierAtom("InternalCppService"), LSFsharedOK, dummyctx);
internalScope = internalScopeLookup->queryScope();
_clear(options); // init options is called later, but depends on the workunit.
startCursorSet = 0;
requireTable = true;
activeGraphCtx = NULL;
maxSequence = 0;
contextAvailable = true;
graphSeqNumber = 0;
nlpParse = NULL;
outputLibrary = NULL;
checkedEmbeddedCpp = false;
cachedAllowEmbeddedCpp = true;
checkedPipeAllowed = false;
activitiesThisCpp = 0;
curCppFile = 0;
timeReporter.setown(createStdTimeReporter());
curActivityId = 0;
holeUniqueSequence = 0;
nextUid = 0;
nextTypeId = 0;
nextFieldId = 0;
code = (HqlCppInstance*)_code;
}
HqlCppTranslator::~HqlCppTranslator()
{
::Release(nlpParse);
::Release(outputLibrary);
}
void HqlCppTranslator::setTargetClusterType(ClusterType clusterType)
{
targetClusterType = clusterType;
}
void HqlCppTranslator::checkAbort()
{
if (wu() && wu()->aborting())
throw MakeStringException(HQLERR_ErrorAlreadyReported, "Aborting");
}
// Option: (Name, value, ?overridden, default())
// problems:
// default value can depend on another option (e.g., cluster type/supports lcr).
// don't want code in multiple places - e.g., the values initialized and defaulted, and dependencies calculations duplicated separately.
// don't want lots of start up costs each time the translator is created -> lightweight classes if any.
// don't really want two structures, one for the definitions, and another for the values.
//RESOLVED? want to walk the debug options provided, instead of checking for each possibility in turn.
// Without this restriction it becomes much easier.
void HqlCppTranslator::cacheOptions()
{
SCMStringBuffer targetText;
wu()->getDebugValue("targetClusterType", targetText);
ClusterType clusterType = getClusterType(targetText.s.str());
if (clusterType != NoCluster)
setTargetClusterType(clusterType);
//Some compound flags, which provide defaults for various other options.
bool paranoid = getDebugFlag("paranoid", false);
bool releaseMode = getDebugFlag("release", true);
struct DebugOption
{
typedef enum { typeByte, typeUnsigned, typeBool } OptionType;
DebugOption (bool & _option, const char * name, bool defaultValue) : option(&_option), optName(name)
{
_option = defaultValue;
type = typeBool;
}
DebugOption (byte & _option, const char * name, byte defaultValue) : option(&_option), optName(name)
{
_option = defaultValue;
type = typeByte;
}
DebugOption (unsigned & _option, const char * name, unsigned defaultValue) : option(&_option), optName(name)
{
_option = defaultValue;
type = typeUnsigned;
};
void setValue(const char * val)
{
switch (type)
{
case typeBool:
{
bool * b = (bool*)option;
*b = strToBool(val);
break;
}
case typeUnsigned:
{
unsigned * u = (unsigned*)option;
*u = (unsigned)atoi(val);
break;
}
case typeByte:
{
byte * b = (byte*)option;
*b = (byte)atoi(val);
break;
}
}
}
void * option;
const char * optName;
OptionType type;
};
//Note this list cannot have any initial values which are dependent on other options.
DebugOption debugOptions[] =
{
DebugOption(options.peephole,"peephole", true),
DebugOption(options.foldConstantCast,"foldConstantCast", true),
DebugOption(options.optimizeBoolReturn,"optimizeBoolReturn", true),
DebugOption(options.checkRowOverflow,"checkRowOverflow", true),
DebugOption(options.freezePersists,"freezePersists", false),
DebugOption(options.maxRecordSize, "defaultMaxLengthRecord", MAX_RECORD_SIZE),
DebugOption(options.maxInlineDepth, "maxInlineDepth", 999), // a debugging setting...
DebugOption(options.checkRoxieRestrictions,"checkRoxieRestrictions", true), // a debug aid for running regression suite
DebugOption(options.checkThorRestrictions,"checkThorRestrictions", true), // a debug aid for running regression suite
DebugOption(options.allowCsvWorkunitRead,"allowStoredCsvFormat", false),
DebugOption(options.evaluateCoLocalRowInvariantInExtract,"evaluateCoLocalRowInvariantInExtract", false),
DebugOption(options.spanMultipleCpp,"spanMultipleCpp", false),
DebugOption(options.activitiesPerCpp, "", 0x7fffffff),
DebugOption(options.allowInlineSpill,"allowInlineSpill", true),
DebugOption(options.optimizeGlobalProjects,"optimizeGlobalProjects", false),
DebugOption(options.optimizeResourcedProjects,"optimizeResourcedProjects", false),
DebugOption(options.reduceNetworkTraffic,"aggressiveOptimizeProjects", false),
DebugOption(options.notifyOptimizedProjects, "notifyOptimizedProjects", 0),
DebugOption(options.optimizeProjectsPreservePersists,"optimizeProjectsPreservePersists", false),
DebugOption(options.checkAsserts,"checkAsserts", true),
DebugOption(options.optimizeLoopInvariant,"optimizeLoopInvariant", false), // doesn't fully work yet! and has little effect, and messes up the alias dependencies
DebugOption(options.defaultImplicitKeyedJoinLimit, "defaultImplicitKeyedJoinLimit", 10000),
DebugOption(options.defaultImplicitIndexReadLimit, "defaultImplicitIndexReadLimit", 0),
DebugOption(options.commonUpChildGraphs,"commonUpChildGraphs", true),
DebugOption(options.detectAmbiguousSelector,"detectAmbiguousSelector", false),
DebugOption(options.allowAmbiguousSelector,"allowAmbiguousSelector", false),
DebugOption(options.preserveUniqueSelector,"preserveUniqueSelector", true),
#ifdef _DEBUG
DebugOption(options.regressionTest,"regressionTest", true),
#else
DebugOption(options.regressionTest,"regressionTest", false),
#endif
DebugOption(options.addTimingToWorkunit, "addTimingToWorkunit", true),
//recreating case can cause duplicate branches in weird situations.
DebugOption(options.recreateMapFromIf,"recreateMapFromIf", !targetThor()),
DebugOption(options.showMetaText,"debugShowMetaText", false),
DebugOption(options.resourceSequential,"resourceSequential", false),
DebugOption(options.workunitTemporaries,"workunitTemporaries", true),
DebugOption(options.resourceConditionalActions,"resourceConditionalActions", false), //targetRoxie() ??
DebugOption(options.minimizeWorkunitTemporaries, "", false),
DebugOption(options.pickBestEngine,"pickBestEngine", true),
DebugOption(options.groupedChildIterators,"groupedChildIterators", false),
DebugOption(options.noAllToLookupConversion,"noAllToLookupConversion", false),
DebugOption(options.notifyWorkflowCse,"notifyWorkflowCse", true),
DebugOption(options.performWorkflowCse,"performWorkflowCse", false),
DebugOption(options.warnOnImplicitJoinLimit,"warnOnImplicitJoinLimit", targetRoxie()),
DebugOption(options.warnOnImplicitReadLimit,"warnOnImplicitReadLimit", targetRoxie()),
DebugOption(options.convertJoinToLookup,"convertJoinToLookup", true),
DebugOption(options.convertJoinToLookupIfSorted,"convertJoinToLookupIfSorted", false), // should change to false once tested
DebugOption(options.spotCSE,"spotCSE", true),
DebugOption(options.optimizeNonEmpty,"optimizeNonEmpty", !targetThor()), // not sure that it will be conditional resourced correctly for thor
DebugOption(options.allowVariableRoxieFilenames,"allowVariableRoxieFilenames", false),
DebugOption(options.foldConstantDatasets,"foldConstantDatasets", true),
DebugOption(options.hoistSimpleGlobal,"hoistSimpleGlobal", true),
DebugOption(options.percolateConstants,"percolateConstants", true),
DebugOption(options.percolateFilters,"percolateFilters", false),
DebugOption(options.usePrefetchForAllProjects,"usePrefetchForAllProjects", false),
DebugOption(options.allFilenamesDynamic,"allFilenamesDynamic", false),
DebugOption(options.optimizeSteppingPostfilter,"optimizeSteppingPostfilter", true),
DebugOption(options.moveUnconditionalActions,"moveUnconditionalActions", false),
DebugOption(options.paranoidCheckNormalized, "paranoidCheckNormalized", paranoid),
DebugOption(options.paranoidCheckDependencies, "paranoidCheckDependencies", paranoid),
DebugOption(options.preventKeyedSplit,"preventKeyedSplit", true),
DebugOption(options.preventSteppedSplit,"preventSteppedSplit", true),
DebugOption(options.canGenerateSimpleAction,"canGenerateSimpleAction", true),
DebugOption(options.minimizeActivityClasses,"minimizeActivityClasses", true),
DebugOption(options.maxRootMaybeThorActions, "maxRootMaybeThorActions", 0),
DebugOption(options.includeHelperInGraph,"includeHelperInGraph", false),
DebugOption(options.supportsLinkedChildRows,"supportsLinkedChildRows", (targetClusterType != ThorCluster)),
DebugOption(options.minimizeSkewBeforeSpill,"minimizeSkewBeforeSpill", false),
DebugOption(options.createSerializeForUnknownSize,"createSerializeForUnknownSize", false),
DebugOption(options.implicitLinkedChildRows,"implicitLinkedChildRows", false),
DebugOption(options.tempDatasetsUseLinkedRows,"tempDatasetsUseLinkedRows", false),
// DebugOption(options.mainRowsAreLinkCounted,"mainRowsAreLinkCounted", options.supportsLinkedChildRows),
DebugOption(options.allowSections,"allowSections", true),
DebugOption(options.autoPackRecords,"autoPackRecords", false),
DebugOption(options.commonUniqueNameAttributes,"commonUniqueNameAttributes", true),
DebugOption(options.sortIndexPayload,"sortIndexPayload", true),
DebugOption(options.foldFilter,"foldFilter", true),
DebugOption(options.finalizeAllRows, "finalizeAllRows", false),
DebugOption(options.finalizeAllVariableRows, "finalizeAllVariableRows", true),
DebugOption(options.maxStaticRowSize , "maxStaticRowSize", MAX_STATIC_ROW_SIZE),
DebugOption(options.maxLocalRowSize , "maxLocalRowSize", MAX_LOCAL_ROW_SIZE),
DebugOption(options.optimizeGraph,"optimizeGraph", true),
DebugOption(options.optimizeChildGraph,"optimizeChildGraph", false),
DebugOption(options.orderDiskFunnel,"orderDiskFunnel", true),
DebugOption(options.alwaysAllowAllNodes,"alwaysAllowAllNodes", false),
DebugOption(options.slidingJoins,"slidingJoins", false),
DebugOption(options.foldOptimized,"foldOptimized", false),
DebugOption(options.globalFold,"globalFold", true),
DebugOption(options.globalOptimize,"globalOptimize", false),
DebugOption(options.optimizeThorCounts,"optimizeThorCounts", true),
DebugOption(options.applyInstantEclTransformations,"applyInstantEclTransformations", false), // testing option
DebugOption(options.calculateComplexity,"calculateComplexity", false),
DebugOption(options.generateLogicalGraph,"generateLogicalGraph", false),
DebugOption(options.generateLogicalGraphOnly,"generateLogicalGraphOnly", false),
DebugOption(options.globalAutoHoist,"globalAutoHoist", true),
DebugOption(options.useLinkedRawIterator,"useLinkedRawIterator", false),
DebugOption(options.useLinkedNormalize,"useLinkedNormalize", false),
DebugOption(options.applyInstantEclTransformationsLimit, "applyInstantEclTransformationsLimit", 100),
DebugOption(options.insertProjectCostLevel, "insertProjectCostLevel", (unsigned)-1),
DebugOption(options.dfaRepeatMax, "dfaRepeatMax", 10),
DebugOption(options.dfaRepeatMaxScore, "dfaRepeatMaxScore", 100),
DebugOption(options.debugNlp, "debugNlp", DEFAULT_NLP_DETAIL),
DebugOption(options.regexVersion, "regexVersion",2),
DebugOption(options.parseDfaComplexity, "parseDfaComplexity", (unsigned)-1),
DebugOption(options.expandRepeatAnyAsDfa,"expandRepeatAnyAsDfa", true),
DebugOption(options.resourceMaxMemory, "resourceMaxMemory", 0),
DebugOption(options.resourceMaxSockets, "resourceMaxSockets", 0),
DebugOption(options.resourceMaxActivities, "resourceMaxActivities", 0),
DebugOption(options.resourceMaxHeavy, "resourceMaxHeavy", 1),
DebugOption(options.resourceMaxDistribute, "resourceMaxDistribute", 2),
DebugOption(options.unlimitedResources,"unlimitedResources", false),
DebugOption(options.resourceUseMpForDistribute,"resourceUseMpForDistribute", false),
DebugOption(options.filteredReadSpillThreshold, "filteredReadSpillThreshold", 999),
DebugOption(options.allowThroughSpill,"allowThroughSpill", true),
DebugOption(options.minimiseSpills,"minimiseSpills", false),
DebugOption(options.spillMultiCondition,"spillMultiCondition", false),
DebugOption(options.spotThroughAggregate,"spotThroughAggregate", true),
DebugOption(options.hoistResourced,"hoistResourced", true),
DebugOption(options.minimizeSpillSize, "minimizeSpillSize", 0),
DebugOption(options.maximizeLexer,"maximizeLexer", false),
DebugOption(options.foldStored,"foldStored", false),
DebugOption(options.spotTopN,"spotTopN", true),
DebugOption(options.topnLimit, "topnLimit", 10000),
DebugOption(options.groupAllDistribute,"groupAllDistribute", false),
DebugOption(options.spotLocalMerge,"spotLocalMerge", true),
DebugOption(options.spotPotentialKeyedJoins,"spotPotentialKeyedJoins", false),
DebugOption(options.combineTrivialStored,"combineTrivialStored", true),
DebugOption(options.combineAllStored,"combineAllStored", false),
DebugOption(options.allowStoredDuplicate,"allowStoredDuplicate", false), // only here as a temporary workaround
DebugOption(options.specifiedClusterSize, "clusterSize", 0),
DebugOption(options.globalFoldOptions, "globalFoldOptions", (unsigned)-1),
DebugOption(options.allowScopeMigrate,"allowScopeMigrate", true),
DebugOption(options.supportFilterProject,"supportFilterProject", true),
DebugOption(options.normalizeExplicitCasts,"normalizeExplicitCasts", true),
DebugOption(options.optimizeInlineSource,"optimizeInlineSource", false),
DebugOption(options.optimizeDiskSource,"optimizeDiskSource", true),
DebugOption(options.optimizeIndexSource,"optimizeIndexSource", true),
DebugOption(options.optimizeChildSource,"optimizeChildSource", false),
DebugOption(options.createCountFile,"countFile", true),
DebugOption(options.createCountIndex,"countIndex", false),
DebugOption(options.reportLocations,"reportLocations", true),
DebugOption(options.debugGeneratedCpp,"debugGeneratedCpp", false),
DebugOption(options.addFilesnamesToGraph,"addFilesnamesToGraph", true),
DebugOption(options.normalizeLocations,"normalizeLocations", true),
DebugOption(options.ensureRecordsHaveSymbols,"ensureRecordsHaveSymbols", true),
DebugOption(options.constantFoldNormalize,"constantFoldNormalize", true),
DebugOption(options.constantFoldPostNormalize,"constantFoldPostNormalize", false),
DebugOption(options.optimizeGrouping,"optimizeGrouping", true),
DebugOption(options.showMetaInGraph,"showMetaInGraph", false),
DebugOption(options.spotComplexClasses,"spotComplexClasses", true),
DebugOption(options.complexClassesThreshold,"complexClassesThreshold", 5000),
DebugOption(options.complexClassesActivityFilter,"complexClassesActivityFilter", 0),
DebugOption(options.optimizeString1Compare,"optimizeString1Compare", true),
DebugOption(options.optimizeSpillProject,"optimizeSpillProject", true),
DebugOption(options.expressionPeephole,"expressionPeephole", false),
DebugOption(options.optimizeIncrement,"optimizeIncrement", true),
DebugOption(options.supportsMergeDistribute,"supportsMergeDistribute", true),
DebugOption(options.debugNlpAsHint,"debugNlpAsHint", false),
DebugOption(options.forceVariableWuid,"forceVariableWuid", false),
DebugOption(options.okToDeclareAndAssign,"okToDeclareAndAssign", false),
DebugOption(options.noteRecordSizeInGraph,"noteRecordSizeInGraph", true),
DebugOption(options.convertRealAssignToMemcpy,"convertRealAssignToMemcpy", false),
DebugOption(options.allowActivityForKeyedJoin,"allowActivityForKeyedJoin", false),
DebugOption(options.forceActivityForKeyedJoin,"forceActivityForKeyedJoin", false),
DebugOption(options.addLibraryInputsToGraph,"addLibraryInputsToGraph", false),
DebugOption(options.showRecordCountInGraph,"showRecordCountInGraph", true),
DebugOption(options.serializeRowsetInExtract,"serializeRowsetInExtract", false),
DebugOption(options.optimizeInSegmentMonitor,"optimizeInSegmentMonitor", true),
DebugOption(options.supportDynamicRows,"supportDynamicRows", true),
DebugOption(options.testIgnoreMaxLength,"testIgnoreMaxLength", false),
DebugOption(options.limitMaxLength,"limitMaxLength", false),
DebugOption(options.trackDuplicateActivities,"trackDuplicateActivities", false),
DebugOption(options.showActivitySizeInGraph,"showActivitySizeInGraph", false),
DebugOption(options.addLocationToCpp,"addLocationToCpp", false),
DebugOption(options.alwaysCreateRowBuilder,"alwaysCreateRowBuilder", false),
DebugOption(options.precalculateFieldOffsets,"precalculateFieldOffsets", false),
DebugOption(options.generateStaticInlineTables,"generateStaticInlineTables", true),
DebugOption(options.staticRowsUseStringInitializer,"staticRowsUseStringInitializer", true),
DebugOption(options.convertWhenExecutedToCompound,"convertWhenExecutedToCompound", queryLegacyEclSemantics()),
DebugOption(options.standAloneExe,"standAloneExe", false),
DebugOption(options.enableCompoundCsvRead,"enableCompoundCsvRead", true),
};
//get options values from workunit
const unsigned numDebugOptions = _elements_in(debugOptions);
Owned debugs(&wu()->getDebugValues());
SCMStringBuffer name, val;
ForEach(*debugs)
{
debugs->str(name);
wu()->getDebugValue(name.str(),val);
unsigned x = 0;
for (; x < numDebugOptions; x++)
{
if (0 == stricmp(name.str(), debugOptions[x].optName))
{
debugOptions[x].setValue(val.str());
break;
}
}
}
//The following cases handle options whose default values are dependent on other options.
//Or where one debug options sets more than one option
options.hasResourceUseMpForDistribute = wu()->hasDebugValue("resourceUseMpForDistribute");
if (!options.supportsLinkedChildRows)
options.implicitLinkedChildRows = false;
if (options.spanMultipleCpp)
{
options.activitiesPerCpp = wu()->getDebugValueInt("activitiesPerCpp", DEFAULT_ACTIVITIES_PER_CPP);
curCppFile = 1;
}
options.targetCompiler = DEFAULT_COMPILER;
if (wu()->hasDebugValue("targetGcc"))
options.targetCompiler = wu()->getDebugValueBool("targetGcc", false) ? GccCppCompiler : Vs6CppCompiler;
SCMStringBuffer compilerText;
wu()->getDebugValue("targetCompiler", compilerText);
for (CompilerType iComp = (CompilerType)0; iComp < MaxCompiler; iComp = (CompilerType)(iComp+1))
{
if (stricmp(compilerText.s.str(), compilerTypeText[iComp]) == 0)
options.targetCompiler = iComp;
}
if (getDebugFlag("optimizeProjects", true))
{
options.optimizeGlobalProjects = true;
options.optimizeResourcedProjects = true;
}
options.mainRowsAreLinkCounted = options.supportsLinkedChildRows && getDebugFlag("mainRowsAreLinkCounted", true);
options.minimizeWorkunitTemporaries = !options.workunitTemporaries || getDebugFlag("minimizeWorkunitTemporaries", false);//options.resourceConditionalActions);
options.tempDatasetsUseLinkedRows = getDebugFlag("tempDatasetsUseLinkedRows", options.implicitLinkedChildRows);
options.inlineStringThreshold = wu()->getDebugValueInt("inlineStringThreshold", (options.targetCompiler != Vs6CppCompiler) ? 0 : 10000);
if (!options.globalFold)
{
options.constantFoldNormalize = false;
options.constantFoldPostNormalize = false;
}
//A meta flag for enabling link counted child rows.
bool useLCR = getDebugFlag("linkCountedRows", getDebugFlag("testLCR", true));
if (options.supportsLinkedChildRows && useLCR)
{
options.implicitLinkedChildRows = true;
options.tempDatasetsUseLinkedRows = true;
options.useLinkedRawIterator = true;
options.useLinkedNormalize = true;
options.finalizeAllRows = true; // inline temporary rows should actually be ok.
options.finalizeAllVariableRows = true;
}
postProcessOptions();
}
void HqlCppTranslator::postProcessOptions()
{
//Any post processing - e.g., dependent flags goes here...
if (targetClusterType == ThorCluster)
options.supportDynamicRows = false;
if (options.supportDynamicRows)
{
options.finalizeAllVariableRows = true;
}
else
{
options.testIgnoreMaxLength = false;
options.limitMaxLength = false;
}
if (options.supportsLinkedChildRows)
{
options.maxStaticRowSize = 0;
}
else
{
options.maxStaticRowSize = (unsigned)-1;
options.implicitLinkedChildRows = false;
options.tempDatasetsUseLinkedRows = false;
options.useLinkedRawIterator = false;
options.useLinkedNormalize = false;
options.finalizeAllRows = false;
options.finalizeAllVariableRows = false;
}
options.optimizeDiskFlag = 0;
if (options.optimizeInlineSource)
options.optimizeDiskFlag |= CSFnewinline;
if (options.optimizeDiskSource)
options.optimizeDiskFlag |= CSFnewdisk;
if (options.optimizeIndexSource)
options.optimizeDiskFlag |= CSFnewindex;
if (options.optimizeChildSource)
options.optimizeDiskFlag |= CSFnewchild;
if (options.optimizeIndexSource && (targetClusterType != ThorCluster)) // thor doesn't support child queries - so still need following
options.createCountIndex = false;
if (targetThor())
{
//options.resourceConditionalActions = false;
//options.resourceSequential = false;
}
if (!targetThor())
{
//Roxie doesn't gain from additional projects, hthor doesn't support split
options.optimizeSpillProject = false;
}
if (options.resourceSequential)
options.resourceConditionalActions = true;
//Probably best to ignore this warning. - possibly configure it based on some other option
queryWarningProcessor().addGlobalOnWarning(HQLWRN_FoldRemoveKeyed, ignoreAtom);
if (options.forceVariableWuid)
wu()->setCloneable(true);
}
unsigned HqlCppTranslator::getOptimizeFlags() const
{
unsigned optFlags = HOOfold;
switch (targetClusterType)
{
case RoxieCluster:
optFlags |= HOOnoclonelimit;
break;
case HThorCluster:
optFlags |= HOOnocloneindexlimit;
break;
case ThorCluster:
case ThorLCRCluster:
break;
default:
UNIMPLEMENTED;
}
if ((options.optimizeDiskFlag & CSFnewdisk) && (options.optimizeDiskFlag & CSFnewindex))
optFlags |= HOOhascompoundaggregate;
if (options.foldConstantDatasets)
optFlags |= HOOfoldconstantdatasets;
return optFlags;
}
void HqlCppTranslator::overrideOptionsForLibrary()
{
options.workunitTemporaries = false;
options.pickBestEngine = false;
options.minimizeWorkunitTemporaries = true;
}
void HqlCppTranslator::overrideOptionsForQuery()
{
options.workunitTemporaries = getDebugFlag("workunitTemporaries", true);
options.pickBestEngine = getDebugFlag("pickBestEngine", true);
options.minimizeWorkunitTemporaries = !options.workunitTemporaries || getDebugFlag("minimizeWorkunitTemporaries", false);//options.resourceConditionalActions);
}
IHqlExpression *HqlCppTranslator::addBigLiteral(const char *lit, unsigned litLen)
{
unsigned resid = code->addStringResource(litLen, lit);
HqlExprArray args;
args.append(*getSizetConstant(resid));
return bindTranslatedFunctionCall(loadResourceAtom, args);
}
IHqlExpression * HqlCppTranslator::addLiteral(const char * text)
{
return createConstant(text);
}
IHqlExpression *HqlCppTranslator::addDataLiteral(const char *lit, unsigned litLen)
{
if (!canGenerateStringInline(litLen))
return addBigLiteral(lit, litLen);
else
return createConstant(createStringValue(lit, litLen));
}
IHqlExpression *HqlCppTranslator::addStringLiteral(const char *lit)
{
unsigned litLen = strlen(lit);
if (!canGenerateStringInline(litLen))
return addBigLiteral(lit, litLen+1);
else
return createConstant(createStringValue(lit, litLen));
}
bool HqlCppTranslator::allowEmbeddedCpp()
{
if (!checkedEmbeddedCpp)
{
cachedAllowEmbeddedCpp = ctxCallback->allowAccess("cpp");
checkedEmbeddedCpp = true;
}
return cachedAllowEmbeddedCpp;
}
void HqlCppTranslator::checkPipeAllowed()
{
if (!checkedPipeAllowed)
{
if (!ctxCallback->allowAccess("pipe"))
throwError(HQLERR_PipeNotAllowed);
checkedPipeAllowed = true;
}
}
IHqlExpression * HqlCppTranslator::bindFunctionCall(_ATOM name, HqlExprArray & args)
{
OwnedHqlExpr function = needFunction(name);
assertex(function);
return bindFunctionCall(function, args);
}
IHqlExpression * HqlCppTranslator::bindFunctionCall(_ATOM name, IHqlExpression * arg1)
{
HqlExprArray args;
args.append(*arg1);
return bindFunctionCall(name, args);
}
IHqlExpression * HqlCppTranslator::bindFunctionCall(IHqlExpression * function, HqlExprArray & args)
{
useFunction(function);
IHqlExpression * ret = createBoundFunction(NULL, function, args, NULL, false);
assertex(ret->queryExternalDefinition());
args.kill();
return ret;
}
IHqlExpression * HqlCppTranslator::bindFunctionCall(_ATOM name, HqlExprArray & args, ITypeInfo * newType)
{
if (!newType)
return bindFunctionCall(name, args);
OwnedHqlExpr function = needFunction(name);
useFunction(function);
assertex(function->getOperator() == no_funcdef);
IHqlExpression * body = function->queryChild(0);
HqlExprArray bodyArgs;
unwindChildren(bodyArgs, body);
HqlExprArray funcArgs;
funcArgs.append(*createValue(body->getOperator(), LINK(newType), bodyArgs));
unwindChildren(funcArgs, function, 1);
ITypeInfo * funcType = makeFunctionType(LINK(newType), LINK(function->queryChild(1)), LINK(function->queryChild(2)));
OwnedHqlExpr newFunction = createValue(function->getOperator(), funcType, funcArgs);
return bindFunctionCall(newFunction, args);
}
IHqlExpression * HqlCppTranslator::bindTranslatedFunctionCall(IHqlExpression * function, HqlExprArray & args)
{
useFunction(function);
IHqlExpression * ret = createTranslatedExternalCall(NULL, function, args);
assertex(ret->queryExternalDefinition());
args.kill();
return ret;
}
IHqlExpression * HqlCppTranslator::bindTranslatedFunctionCall(_ATOM name, HqlExprArray & args)
{
OwnedHqlExpr function = needFunction(name);
return bindTranslatedFunctionCall(function, args);
}
void HqlCppTranslator::buildTranslatedFunctionCall(BuildCtx & ctx, _ATOM name, HqlExprArray & args)
{
OwnedHqlExpr call = bindTranslatedFunctionCall(name, args);
ctx.addExpr(call);
}
void HqlCppTranslator::buildFunctionCall(BuildCtx & ctx, _ATOM name, HqlExprArray & args)
{
OwnedHqlExpr call = bindFunctionCall(name, args);
buildStmt(ctx, call);
}
/* Args: all elements in it are LINKED */
void HqlCppTranslator::callProcedure(BuildCtx & ctx, _ATOM name, HqlExprArray & args)
{
OwnedHqlExpr call = bindTranslatedFunctionCall(name, args);
assertex(call->queryExternalDefinition());
ctx.addExpr(call);
}
void HqlCppTranslator::buildServicePrototypes(IHqlScope * scope)
{
BuildCtx ctx(*code, prototypeAtom);
StringBuffer s;
HqlExprArray exprs;
scope->getSymbols(exprs);
ForEachItemIn(idx, exprs)
{
IHqlExpression & cur = exprs.item(idx);
node_operator op = cur.getOperator();
if (op == no_scope)
{
s.clear().append("/* SERVICE ").append(cur.queryName()).append(" */");
ctx.addQuoted(s);
IHqlScope * serviceScope = cur.queryScope();
HqlExprArray funcs;
serviceScope->getSymbols(funcs);
ForEachItemIn(idx2, funcs)
{
IHqlExpression & curFunc = funcs.item(idx2);
IHqlExpression * def = curFunc.queryExternalDefinition();
if (def)
{
s.clear().append("//").append(cur.queryName()).append(".").append(curFunc.queryName());
ctx.addQuoted(s);
expandFunctionPrototype(ctx, def, true);
}
}
}
}
}
bool HqlCppTranslator::getDebugFlag(const char * name, bool defValue)
{
return wu()->getDebugValueBool(name, defValue);
}
void HqlCppTranslator::doReportWarning(IHqlExpression * location, unsigned id, const char * msg)
{
Owned warnError;
if (!location)
location = queryActiveActivityLocation();
if (location)
warnError.setown(createECLError(false, id, msg, location->querySourcePath()->str(), location->getStartLine(), location->getStartColumn(), 0));
else
warnError.setown(createECLError(false, id, msg, NULL, 0, 0, 0));
warningProcessor.report(NULL, errors, warnError);
}
void HqlCppTranslator::reportWarning(IHqlExpression * location, unsigned id, const char * msg, ...)
{
StringBuffer s;
va_list args;
va_start(args, msg);
s.valist_appendf(msg, args);
va_end(args);
doReportWarning(location, id, s.str());
}
void HqlCppTranslator::reportWarning(unsigned id, const char * msg, ...)
{
StringBuffer s;
va_list args;
va_start(args, msg);
s.valist_appendf(msg, args);
va_end(args);
doReportWarning(NULL, id, s.str());
}
void HqlCppTranslator::addWorkunitException(WUExceptionSeverity severity, unsigned code, const char * text, IHqlExpression * location)
{
Owned msg = wu()->createException();
msg->setExceptionSource("Code Generator");
if (code)
msg->setExceptionCode(code);
msg->setExceptionMessage(text);
msg->setSeverity(severity);
msg->setTimeStamp(NULL);
if (!location)
location = queryActiveActivityLocation();
if (location)
{
msg->setExceptionFileName(location->querySourcePath()->str());
msg->setExceptionLineNo(location->getStartLine());
msg->setExceptionColumn(location->getStartColumn());
}
}
IHqlExpression * HqlCppTranslator::queryActiveNamedActivity()
{
ForEachItemInRev(i, activityExprStack)
{
IHqlExpression & cur = activityExprStack.item(i);
IHqlExpression * symbol = queryNamedSymbol(&cur);
if (symbol && symbol->querySourcePath())
return symbol;
if (isCompoundSource(&cur))
{
IHqlExpression * child = cur.queryChild(0);
if (hasNamedSymbol(child))
return child;
}
}
return NULL;
}
IHqlExpression * HqlCppTranslator::queryActiveActivityLocation() const
{
ForEachItemInRev(i, activityExprStack)
{
IHqlExpression & cur = activityExprStack.item(i);
IHqlExpression * location = queryLocation(&cur);
if (location)
return location;
if (isCompoundSource(&cur))
{
location = queryLocation(cur.queryChild(0));
if (location)
return location;
}
}
return NULL;
}
void HqlCppTranslator::ThrowStringException(int code,const char *format, ...)
{
IHqlExpression * location = queryActiveActivityLocation();
if (errors && location)
{
StringBuffer errorMsg;
va_list args;
va_start(args, format);
errorMsg.valist_appendf(format, args);
va_end(args);
throw createECLError(code, errorMsg.str(), location->querySourcePath()->str(), location->getStartLine(), location->getStartColumn(), 0);
}
va_list args;
va_start(args, format);
IException *ret = MakeStringExceptionVA(code, format, args);
va_end(args);
throw ret;
}
void HqlCppTranslator::reportErrorDirect(IHqlExpression * location, int code,const char *msg, bool alwaysAbort)
{
if (location)
{
ECLlocation loc;
loc.extractLocationAttr(location);
if (alwaysAbort)
throw createECLError(code, msg, loc.sourcePath->str(), loc.lineno, loc.column, loc.position);
errors->reportError(code, msg, loc.sourcePath->str(), loc.lineno, loc.column, loc.position);
}
else
throw MakeStringExceptionDirect(code, msg);
}
void HqlCppTranslator::reportError(IHqlExpression * location, int code,const char *format, ...)
{
StringBuffer errorMsg;
va_list args;
va_start(args, format);
errorMsg.valist_appendf(format, args);
va_end(args);
reportErrorDirect(location, code, errorMsg.str(), true);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildStmtAssign(BuildCtx & ctx, IHqlExpression * target, IHqlExpression * expr)
{
buildAssign(ctx, target, expr);
}
void HqlCppTranslator::buildAddress(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_address:
buildExpr(ctx, expr->queryChild(0), tgt);
break;
case no_typetransfer:
buildAddress(ctx, expr->queryChild(0), tgt);
break;
default:
{
Owned selector = buildReference(ctx, expr);
selector->buildAddress(ctx, tgt);
break;
}
}
}
bool HqlCppTranslator::hasAddress(BuildCtx & ctx, IHqlExpression * expr)
{
switch (expr->getOperator())
{
case no_deref:
case no_variable:
return true;
case no_field:
case no_select:
{
Owned selector = buildReference(ctx, expr);
return !selector->isConditional();
}
case no_typetransfer:
return hasAddress(ctx, expr->queryChild(0));
default:
return false;
}
}
void HqlCppTranslator::buildAssign(BuildCtx & ctx, IHqlExpression * target, IHqlExpression * expr)
{
#ifdef ADD_ASSIGNMENT_COMMENTS
if (target->getOperator() == no_select)
{
StringBuffer s;
ctx.addQuoted(s.append("//Assign to field ").append(target->queryChild(1)->queryName()));
}
#endif
Owned selector = buildReference(ctx, target);
if (expr->getOperator() == no_null)
selector->buildClear(ctx, 0);
else if (target->isDatarow() && (!hasReferenceModifier(target->queryType()) || !recordTypesMatch(target->queryType(), expr->queryType())))
buildRowAssign(ctx, selector, expr);
else
selector->set(ctx, expr);
}
void HqlCppTranslator::doBuildStmtAssignModify(BuildCtx & ctx, IHqlExpression * target, IHqlExpression * expr, node_operator assignOp)
{
Owned selector = buildReference(ctx, target);
selector->modifyOp(ctx, expr, assignOp);
}
void HqlCppTranslator::buildExprAssign(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
/*
switch (target.queryType().getTypeCode())
{
case type_table:
case type_groupedtable:
buildDatasetAssign(ctx, target, expr);
break;
case type_row:
buildAssignRow(ctx, target, expr);
break;
}
*/
node_operator op = expr->getOperator();
switch (op)
{
case no_constant:
if (!isNullAssign(target, expr))
doBuildExprAssign(ctx, target, expr);
else
ctx.addAssign(target.length, queryZero());
break;
case no_regex_find:
case no_regex_replace:
doBuildAssignRegexFindReplace(ctx, target, expr);
break;
case no_matched:
case no_matchtext:
case no_matchlength:
case no_matchposition:
case no_matchunicode:
case no_matchutf8:
doBuildMatched(ctx, &target, expr, NULL);
break;
case no_matchattr:
doBuildMatchAttr(ctx, &target, expr, NULL);
break;
case no_loopcounter:
doBuildAssignLoopCounter(ctx, target, expr);
break;
case no_evalonce:
doBuildEvalOnce(ctx, &target, expr, NULL);
break;
case no_alias_scope:
{
expandAliasScope(ctx, expr);
buildExprAssign(ctx, target, expr->queryChild(0));
break;
}
case no_case:
case no_map:
{
HqlCppCaseInfo info(*this);
doBuildCaseInfo(expr, info);
info.buildAssign(ctx, target);
break;
}
case no_which:
case no_rejected:
doBuildAssignWhich(ctx, target, expr);
break;
case no_call:
case no_externalcall:
doBuildAssignCall(ctx, target, expr);
break;
case no_cast:
case no_implicitcast:
doBuildAssignCast(ctx, target, expr);
break;
case no_choose:
doBuildAssignChoose(ctx, target, expr);
break;
case no_comma:
case no_compound:
buildStmt(ctx, expr->queryChild(0));
buildExprAssign(ctx, target, expr->queryChild(1));
break;
case no_concat:
doBuildAssignConcat(ctx, target, expr);
break;
case no_div:
case no_modulus:
doBuildAssignDivide(ctx, target, expr);
break;
case no_crc:
case no_hash:
case no_hash32:
case no_hash64:
doBuildAssignHashCrc(ctx, target, expr);
break;
case no_hashmd5:
doBuildAssignHashMd5(ctx, target, expr);
break;
case no_if:
doBuildAssignIf(ctx, target, expr);
break;
case no_index:
doBuildAssignIndex(ctx, target, expr);
break;
case no_in:
case no_notin:
{
OwnedHqlExpr optimized = querySimplifyInExpr(expr);
if (options.globalFold && optimized)
{
OwnedHqlExpr folded = foldHqlExpression(optimized);
buildExprAssign(ctx, target, folded);
}
else
doBuildAssignIn(ctx, target, expr);
break;
}
case no_intformat:
doBuildAssignFormat(intFormatAtom, ctx, target, expr);
break;
case no_nofold:
case no_nohoist:
case no_section:
case no_sectioninput:
buildExprAssign(ctx, target, expr->queryChild(0));
break;
case no_realformat:
doBuildAssignFormat(realFormatAtom, ctx, target, expr);
break;
case no_order:
doBuildAssignOrder(ctx, target, expr);
break;
case no_unicodeorder:
doBuildAssignUnicodeOrder(ctx, target, expr);
break;
case no_substring:
doBuildAssignSubString(ctx, target, expr);
break;
case no_trim:
doBuildAssignTrim(ctx, target, expr);
break;
case no_field:
throwUnexpected();
case no_select:
{
OwnedHqlExpr aggregate = convertToSimpleAggregate(expr);
if (aggregate && canProcessInline(&ctx, aggregate->queryChild(0)))
{
buildExprAssign(ctx, target, aggregate);
return;
}
Owned selector = buildReference(ctx, expr);
selector->assignTo(ctx, target);
return;
}
break;
case no_not:
{
IHqlExpression * child = expr->queryChild(0);
node_operator childOp = child->getOperator();
if (((childOp == no_and) || (childOp == no_or)) && requiresTempAfterFirst(ctx, child))
{
if (childOp == no_and)
doBuildAssignAnd(ctx, target, child, true);
else
{
OwnedHqlExpr inverted = convertOrToAnd(expr);
buildExprAssign(ctx, target, inverted);
}
}
else
doBuildExprAssign(ctx, target, expr);
break;
}
case no_or:
{
IHqlExpression * left = expr->queryChild(0);
//in always goes via an assign, so do this first, and then filter on result.
if (left->getOperator() == no_in)
{
BuildCtx subctx(ctx);
buildExprAssign(subctx, target, left);
OwnedHqlExpr inverse = getInverse(target.expr);
subctx.addFilter(inverse);
buildExprAssign(subctx, target, expr->queryChild(1));
}
else if (requiresTempAfterFirst(ctx, expr))
{
OwnedHqlExpr inverted = convertOrToAnd(expr);
buildExprAssign(ctx, target, inverted);
}
else
doBuildExprAssign(ctx, target, expr);
break;
}
case no_and:
if (requiresTempAfterFirst(ctx, expr))
doBuildAssignAnd(ctx, target, expr, false);
else
doBuildExprAssign(ctx, target, expr);
break;
case no_fromunicode:
case no_tounicode:
doBuildAssignToFromUnicode(ctx, target, expr);
break;
case no_toxml:
doBuildAssignToXml(ctx, target, expr);
break;
case no_wuid:
doBuildAssignWuid(ctx, target, expr);
break;
case no_xmldecode:
case no_xmlencode:
doBuildXmlEncode(ctx, &target, expr, NULL);
break;
case no_all:
doBuildAssignAll(ctx, target, expr);
return;
case no_list:
doBuildAssignList(ctx, target, expr);
return;
case no_addsets:
doBuildAssignAddSets(ctx, target, expr);
return;
case no_createset:
buildSetAssignViaBuilder(ctx, target, expr);
return;
case no_failmessage:
doBuildAssignFailMessage(ctx, target, expr);
return;
case no_eventname:
doBuildAssignEventName(ctx, target, expr);
return;
case no_eventextra:
doBuildAssignEventExtra(ctx, target, expr);
return;
case no_catch:
doBuildAssignCatch(ctx, target, expr);
break;
case no_id2blob:
doBuildAssignIdToBlob(ctx, target, expr);
break;
case no_getresult:
case no_workunit_dataset:
if (isSameFullyUnqualifiedType(expr->queryType(), target.queryType()))
doBuildAssignGetResult(ctx, target, expr);
else
doBuildExprAssign(ctx, target, expr);
break;
case no_getgraphresult:
doBuildAssignGetGraphResult(ctx, target, expr);
break;
case no_existslist:
doBuildAggregateList(ctx, &target, expr, NULL);
break;
case no_countlist:
doBuildAggregateList(ctx, &target, expr, NULL);
break;
case no_sumlist:
doBuildAggregateList(ctx, &target, expr, NULL);
break;
case no_minlist:
doBuildAggregateList(ctx, &target, expr, NULL);
break;
case no_maxlist:
doBuildAggregateList(ctx, &target, expr, NULL);
break;
case no_skip:
{
bool canReachFollowing = false;
doBuildStmtSkip(ctx, expr, &canReachFollowing);
if (canReachFollowing)
{
OwnedHqlExpr null = createNullExpr(expr);
doBuildExprAssign(ctx, target, null);
}
break;
}
case no_count:
case no_max:
case no_min:
case no_sum:
case no_exists:
case no_notexists:
doBuildAssignAggregate(ctx, target, expr);
break;
case no_getenv:
{
OwnedHqlExpr mapped = cvtGetEnvToCall(expr);
buildExprAssign(ctx, target, mapped);
break;
}
default:
doBuildExprAssign(ctx, target, expr);
break;
}
}
void HqlCppTranslator::buildExprAssignViaType(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, ITypeInfo * type)
{
OwnedHqlExpr temp = createValue(no_implicitcast, LINK(type), LINK(expr));
buildExprAssign(ctx, target, temp);
}
void HqlCppTranslator::buildExprAssignViaString(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, unsigned len)
{
OwnedITypeInfo type = makeStringType(len, NULL, NULL);
buildExprAssignViaType(ctx, target, expr, type);
}
void HqlCppTranslator::buildAssignToTemp(BuildCtx & ctx, IHqlExpression * variable, IHqlExpression * expr)
{
CHqlBoundTarget boundTarget;
boundTarget.expr.set(variable);
buildExprAssign(ctx, boundTarget, expr);
}
void HqlCppTranslator::buildAssignViaTemp(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
CHqlBoundExpr temp;
buildTempExpr(ctx, expr, temp);
buildExprAssign(ctx, target, temp.expr);
}
static bool canOptimizeIncrementAssign(ITypeInfo * type)
{
switch (type->getTypeCode())
{
case type_real:
return true;
case type_int:
switch (type->getSize())
{
case 1:
case 2:
case 4:
case 8:
return true;
}
}
return false;
}
IHqlExpression * HqlCppTranslator::optimizeIncrementAssign(BuildCtx & ctx, IHqlExpression * value)
{
//MORE: Could spot x += if(cond, y, 0) and convert to if (cond) x+= y; (especially if y is 1)
if (value->getOperator() == no_if)
{
IHqlExpression * left = value->queryChild(1);
IHqlExpression * right = value->queryChild(2);
if (isZero(right))
{
buildFilter(ctx, value->queryChild(0));
return optimizeIncrementAssign(ctx, left);
}
if (isZero(left))
{
OwnedHqlExpr filter = getInverse(value->queryChild(0));
buildFilter(ctx, filter);
return optimizeIncrementAssign(ctx, right);
}
}
if (isCast(value))
{
IHqlExpression * uncast = value->queryChild(0);
OwnedHqlExpr optimizedValue = optimizeIncrementAssign(ctx, uncast);
if (optimizedValue != uncast)
return ensureExprType(optimizedValue, value->queryType());
}
return LINK(value);
}
void HqlCppTranslator::buildIncrementAssign(BuildCtx & ctx, IHqlExpression * target, IHqlExpression * value)
{
// CHqlBoundExpr bound;
// buildExpr(ctx, target, bound);
ITypeInfo * type = target->queryType();
OwnedHqlExpr castValue = ensureExprType(value, type);
BuildCtx condctx(ctx);
if (options.optimizeIncrement)
{
castValue.setown(optimizeIncrementAssign(condctx, castValue));
if (isZero(castValue))
return;
if (canOptimizeIncrementAssign(type))
{
CHqlBoundExpr boundTarget;
buildExpr(condctx, target, boundTarget); // Not very clean!
CHqlBoundExpr boundValue;
buildExpr(condctx, castValue, boundValue);
condctx.addAssignIncrement(boundTarget.expr, boundValue.expr);
return;
}
}
OwnedHqlExpr plus = createValue(no_add, LINK(target), castValue.getClear());
buildAssign(condctx, target, plus);
}
void HqlCppTranslator::buildIncrementAssign(BuildCtx & ctx, IReferenceSelector * target, IHqlExpression * value)
{
buildIncrementAssign(ctx, target->queryExpr(), value);
// OwnedHqlExpr plus = createValue(no_add, LINK(type), ensureExprType(target->queryExpr(), type), ensureExprType(value, type));
// target->set(condctx, plus);
}
void HqlCppTranslator::buildIncrementAssign(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * value)
{
ITypeInfo * type = target.queryType();
OwnedHqlExpr castValue = ensureExprType(value, type);
BuildCtx condctx(ctx);
if (options.optimizeIncrement)
{
castValue.setown(optimizeIncrementAssign(condctx, castValue));
if (isZero(castValue))
return;
if (canOptimizeIncrementAssign(type))
{
CHqlBoundExpr boundValue;
buildExpr(ctx, castValue, boundValue);
ctx.addAssignIncrement(target.expr, boundValue.expr);
return;
}
}
OwnedHqlExpr plus = createValue(no_add, target.getTranslatedExpr(), castValue.getClear());
buildExprAssign(ctx, target, plus);
}
void HqlCppTranslator::buildClear(BuildCtx & ctx, IHqlExpression * expr)
{
OwnedHqlExpr null = createNullExpr(expr);
buildAssign(ctx, expr, null);
}
void HqlCppTranslator::buildClear(BuildCtx & ctx, const CHqlBoundTarget & target)
{
if (target.length)
{
buildAssignToTemp(ctx, target.length, queryZero());
//NB: Don't need to clear target.pointer/target.variable if a length is defined......
return;
}
if (target.isFixedSize())
{
OwnedHqlExpr null = createNullExpr(target.expr);
buildExprAssign(ctx, target, null);
}
else
{
StringBuffer code;
if (hasWrapperModifier(target.queryType()))
generateExprCpp(code, target.expr).append(".clear();");
else
generateExprCpp(code, target.expr).append(" = 0;");
ctx.addQuoted(code);
}
}
void HqlCppTranslator::buildFilter(BuildCtx & ctx, IHqlExpression * expr)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_attr:
case no_attr_expr:
case no_attr_link:
return;
case no_and:
doBuildFilterAnd(ctx, expr);
return;
case no_not:
{
IHqlExpression * child = expr->queryChild(0);
if ((child->getOperator() == no_or) && requiresTempAfterFirst(ctx, child))
{
OwnedHqlExpr inverted = convertOrToAnd(expr);
buildFilter(ctx,inverted);
return;
}
}
break;
case no_or:
if (!(hints & HintSize) && requiresTempAfterFirst(ctx, expr))
{
OwnedHqlExpr inverted = convertOrToAnd(expr);
buildFilter(ctx, inverted);
return;
}
break;
case no_between:
case no_notbetween:
{
OwnedHqlExpr between = expandBetween(expr);
buildFilter(ctx, between);
return;
}
case no_alias_scope:
{
expandAliasScope(ctx, expr);
buildFilter(ctx, expr->queryChild(0));
return;
}
}
buildFilterViaExpr(ctx, expr);
}
IHqlStmt * HqlCppTranslator::buildFilterViaExpr(BuildCtx & ctx, IHqlExpression * expr)
{
//default action...
CHqlBoundExpr pure;
buildExpr(ctx, expr, pure);
if (pure.length) // check length is non zero
return ctx.addFilter(pure.length);
else
{
IHqlStmt * stmt = ctx.addFilter(pure.expr);
ctx.associateExpr(expr, queryBoolExpr(true));
return stmt;
}
}
void HqlCppTranslator::tidyupExpr(BuildCtx & ctx, CHqlBoundExpr & bound)
{
if (isPushed(bound))
{
HqlExprArray args;
callProcedure(ctx, DecPopLongAtom, args);
bound.expr.set(NULL);
}
}
void HqlCppTranslator::expandTranslated(IHqlExpression * expr, CHqlBoundExpr & tgt)
{
tgt.setFromTranslated(expr);
}
void HqlCppTranslator::buildCachedExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
buildExpr(ctx, expr, tgt);
}
void HqlCppTranslator::buildAnyExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (expr->isDataset())
buildDataset(ctx, expr, tgt, FormatNatural);
else if (expr->isDatarow())
{
Owned selector = buildNewRow(ctx, expr);
selector->buildAddress(ctx, tgt);
}
else
buildExpr(ctx, expr, tgt);
}
void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_counter:
doBuildExprCounter(ctx, expr, tgt);
return;
case no_countfile:
doBuildExprCountFile(ctx, expr, tgt);
return;
case no_countindex:
doBuildExprCountIndex(ctx, expr, tgt);
return;
case no_evaluate:
doBuildExprEvaluate(ctx, expr, tgt);
return;
case no_thor:
throwUnexpected();
// assertex(expr->queryType()->isScalar());
// buildExpr(ctx, expr->queryChild(0), tgt);
return;
case no_count:
if (!(expr->isPure() && ctx.getMatchExpr(expr, tgt)))
doBuildExprCount(ctx, expr, tgt);
return;
case no_max:
case no_min:
case no_sum:
if (!(expr->isPure() && ctx.getMatchExpr(expr, tgt)))
doBuildExprAggregate(ctx, expr, tgt);
return;
case no_exists:
case no_notexists:
if (!(expr->isPure() && ctx.getMatchExpr(expr, tgt)))
doBuildExprExists(ctx, expr, tgt);
return;
case no_existslist:
doBuildAggregateList(ctx, NULL, expr, &tgt);
return;
case no_countlist:
doBuildAggregateList(ctx, NULL, expr, &tgt);
return;
case no_sumlist:
doBuildAggregateList(ctx, NULL, expr, &tgt);
return;
case no_minlist:
doBuildAggregateList(ctx, NULL, expr, &tgt);
return;
case no_maxlist:
doBuildAggregateList(ctx, NULL, expr, &tgt);
return;
case no_sizeof:
doBuildExprSizeof(ctx, expr, tgt);
return;
case no_filepos:
doBuildExprFilepos(ctx, expr, tgt);
return;
case no_file_logicalname:
doBuildExprFileLogicalName(ctx, expr, tgt);
return;
case no_getresult:
case no_workunit_dataset:
doBuildExprGetResult(ctx, expr, tgt);
return;
case no_getgraphresult:
doBuildExprGetGraphResult(ctx, expr, tgt, FormatNatural);
return;
case no_regex_find:
case no_regex_replace:
doBuildExprRegexFindReplace(ctx, expr, tgt);
return;
case no_skip:
case no_assert:
{
buildStmt(ctx, expr);
OwnedHqlExpr null = createNullExpr(expr);
buildExpr(ctx, null, tgt);
return;
}
case no_matched:
case no_matchtext:
case no_matchlength:
case no_matchposition:
case no_matchunicode:
case no_matchutf8:
doBuildMatched(ctx, NULL, expr, &tgt);
return;
case no_matchattr:
doBuildMatchAttr(ctx, NULL, expr, &tgt);
return;
case no_rowdiff:
doBuildExprRowDiff(ctx, expr, tgt);
return;
case no_xmltext:
doBuildExprXmlText(ctx, expr, tgt);
return;
case no_xmlunicode:
doBuildExprXmlUnicode(ctx, expr, tgt);
return;
case no_evalonce:
doBuildEvalOnce(ctx, NULL, expr, &tgt);
return;
case no_alias:
doBuildExprAlias(ctx, expr, &tgt);
return;
case no_alias_scope:
{
expandAliasScope(ctx, expr);
buildExpr(ctx, expr->queryChild(0), tgt);
return;
}
case no_between:
case no_notbetween:
{
OwnedHqlExpr between = expandBetween(expr);
buildExpr(ctx, between, tgt);
return;
}
case no_libraryinput:
doBuildAliasValue(ctx, expr, tgt);
return;
case no_externalcall:
case no_call:
doBuildExprCall(ctx, expr, tgt);
return;
case no_comma:
case no_compound:
buildStmt(ctx, expr->queryChild(0));
buildExpr(ctx, expr->queryChild(1), tgt);
return;
case no_cast:
case no_implicitcast:
doBuildExprCast(ctx, expr, tgt);
return;
case no_charlen:
tgt.expr.setown(doBuildCharLength(ctx, expr->queryChild(0)));
return;
case no_add:
doBuildExprAdd(ctx, expr, tgt);
return;
case no_mul:
case no_sub:
doBuildExprArith(ctx, expr, tgt);
return;
case no_abs:
doBuildExprAbs(ctx, expr, tgt);
return;
case no_negate:
doBuildExprNegate(ctx, expr, tgt);
return;
case no_div:
case no_modulus:
doBuildExprDivide(ctx, expr, tgt);
return;
case no_if:
doBuildExprIf(ctx, expr, tgt);
return;
case no_index:
doBuildExprIndex(ctx, expr, tgt);
return;
case no_list:
doBuildExprList(ctx, expr, tgt);
return;
case no_all:
doBuildExprAll(ctx, expr, tgt);
return;
case no_trim:
doBuildExprTrim(ctx, expr, tgt);
return;
case no_intformat:
doBuildExprFormat(intFormatAtom, ctx, expr, tgt);
return;
case no_realformat:
doBuildExprFormat(realFormatAtom, ctx, expr, tgt);
return;
case no_exp:
doBuildExprSysFunc(ctx, expr, tgt, clibExpIdAtom);
return;
case no_ln:
doBuildExprSysFunc(ctx, expr, tgt, lnAtom);
return;
case no_sin:
doBuildExprSysFunc(ctx, expr, tgt, sinAtom);
return;
case no_cos:
doBuildExprSysFunc(ctx, expr, tgt, cosAtom);
return;
case no_tan:
doBuildExprSysFunc(ctx, expr, tgt, tanAtom);
return;
case no_asin:
doBuildExprSysFunc(ctx, expr, tgt, asinAtom);
return;
case no_acos:
doBuildExprSysFunc(ctx, expr, tgt, acosAtom);
return;
case no_atan:
doBuildExprSysFunc(ctx, expr, tgt, atanAtom);
return;
case no_atan2:
doBuildExprSysFunc(ctx, expr, tgt, atan2Atom);
return;
case no_sinh:
doBuildExprSysFunc(ctx, expr, tgt, sinhAtom);
return;
case no_cosh:
doBuildExprSysFunc(ctx, expr, tgt, coshAtom);
return;
case no_tanh:
doBuildExprSysFunc(ctx, expr, tgt, tanhAtom);
return;
case no_log10:
doBuildExprSysFunc(ctx, expr, tgt, log10Atom);
return;
case no_power:
doBuildExprSysFunc(ctx, expr, tgt, powerAtom);
return;
case no_fail:
doBuildStmtFail(ctx, expr);
tgt.expr.setown(createConstant(0));
return;
case no_failcode:
doBuildExprFailCode(ctx, expr, tgt);
return;
case no_ordered:
doBuildExprOrdered(ctx, expr, tgt);
return;
case no_random:
doBuildExprSysFunc(ctx, expr, tgt, rtlRandomAtom);
return;
case no_recovering:
doBuildExprSysFunc(ctx, expr, tgt, getRecoveringCountAtom);
return;
case no_rank:
doBuildExprRank(ctx, expr, tgt);
return;
case no_ranked:
doBuildExprRanked(ctx, expr, tgt);
return;
case no_round:
case no_roundup:
doBuildExprRound(ctx, expr, tgt);
return;
case no_sqrt:
doBuildExprSysFunc(ctx, expr, tgt, sqrtAtom);
return;
case no_truncate:
doBuildExprSysFunc(ctx, expr, tgt, truncateAtom);
return;
case no_offsetof:
doBuildExprOffsetOf(ctx, expr, tgt);
return;
case no_substring:
doBuildExprSubString(ctx, expr, tgt);
return;
case no_in:
case no_notin:
{
if (expr->queryChild(1)->getOperator() == no_all)
tgt.expr.setown(createConstant(op == no_in));
else
{
OwnedHqlExpr optimized = querySimplifyInExpr(expr);
if (options.globalFold && optimized)
{
OwnedHqlExpr folded = foldHqlExpression(optimized);
buildExpr(ctx, folded, tgt);
}
else
buildTempExpr(ctx, expr, tgt);
}
return;
}
case no_case:
case no_choose:
case no_concat:
case no_crc:
case no_hash:
case no_hash32:
case no_hash64:
case no_hashmd5:
case no_map:
case no_order:
case no_unicodeorder:
case no_rejected:
case no_which:
case no_addsets:
case no_createset:
case no_catch:
case no_failmessage:
case no_eventname:
case no_eventextra:
case no_loopcounter:
case no_toxml:
buildTempExpr(ctx, expr, tgt);
return;
case no_asstring:
case no_typetransfer:
doBuildExprTransfer(ctx, expr, tgt);
return;
case no_translated:
{
expandTranslated(expr, tgt);
return;
}
case no_eq:
case no_ne:
case no_le:
case no_lt:
case no_ge:
case no_gt:
if (options.expressionPeephole)
{
OwnedHqlExpr optimized = peepholeOptimize(ctx, expr);
if (optimized != expr)
{
buildExpr(ctx, optimized, tgt);
return;
}
}
doBuildExprCompare(ctx, expr, tgt);
return;
case no_wuid:
doBuildExprWuid(ctx, expr, tgt);
return;
case no_getenv:
{
OwnedHqlExpr mapped = cvtGetEnvToCall(expr);
buildExpr(ctx, mapped, tgt);
return;
}
case no_notnot:
{
OwnedHqlExpr castChild = ensureExprType(expr->queryChild(0), queryBoolType());
buildExpr(ctx, castChild, tgt);
}
return;
case no_not:
{
IHqlExpression * child = expr->queryChild(0);
node_operator childOp = child->getOperator();
if (((childOp == no_and) || (childOp == no_or)) && requiresTempAfterFirst(ctx, child))
buildTempExpr(ctx, expr, tgt);
else
doBuildExprNot(ctx, expr, tgt);
return;
}
case no_constant:
{
ITypeInfo * type = expr->queryType();
if ((options.inlineStringThreshold > 0) && (type->getSize() > options.inlineStringThreshold) && (type->getSize() != UNKNOWN_LENGTH))
{
IHqlExpression * literal = addBigLiteral((const char *)expr->queryValue()->queryValue(), type->getSize());
Owned retType = makeReferenceModifier(LINK(type));
switch (type->getTypeCode())
{
case type_unicode:
case type_varunicode:
case type_utf8:
literal = createValue(no_implicitcast, LINK(retType), literal);
break;
}
if (literal->queryType() != retType)
literal = createValue(no_typetransfer, LINK(retType), literal);
tgt.expr.setown(literal);
}
else
tgt.expr.set(expr);
return;
}
case no_quoted:
case no_variable:
tgt.expr.set(expr);
return;
case no_globalscope:
if (options.regressionTest)
throwUnexpected();
buildExpr(ctx, expr->queryChild(0), tgt);
return;
case no_nofold:
case no_nohoist:
case no_section:
case no_sectioninput:
case no_pure:
buildExpr(ctx, expr->queryChild(0), tgt);
return;
case no_band:
case no_bor:
case no_bnot:
case no_bxor:
case no_lshift:
case no_rshift:
doBuildPureSubExpr(ctx, expr, tgt);
return;
//MORE: Shouldn't these be special cased?
case no_xor:
doBuildPureSubExpr(ctx, expr, tgt);
return;
case no_select:
{
OwnedHqlExpr aggregate = convertToSimpleAggregate(expr);
if (aggregate && canProcessInline(&ctx, aggregate->queryChild(0)))
{
buildExpr(ctx, aggregate, tgt);
return;
}
Owned selector = buildReference(ctx, expr);
selector->get(ctx, tgt);
return;
}
case no_field:
throwUnexpected();
case no_is_null:
{
//Until we have something better in place isNull is the inverse of isValid().
IHqlExpression * child = expr->queryChild(0);
OwnedHqlExpr null = createValue(no_not, makeBoolType(), createValue(no_is_valid, makeBoolType(), LINK(child)));
buildExpr(ctx, null, tgt);
}
return;
case no_is_valid:
doBuildExprIsValid(ctx, expr, tgt);
return;
case no_fromunicode:
case no_tounicode:
doBuildExprToFromUnicode(ctx, expr, tgt);
return;
case no_keyunicode:
doBuildExprKeyUnicode(ctx, expr, tgt);
return;
case no_xmldecode:
case no_xmlencode:
buildTempExpr(ctx, expr, tgt);
return;
case no_and:
case no_or:
if (requiresTempAfterFirst(ctx, expr))
buildTempExpr(ctx, expr, tgt);
else
doBuildPureSubExpr(ctx, expr, tgt);
return;
case no_assertkeyed:
case no_assertwild:
{
StringBuffer s;
throwError1(HQLERR_KeyedWildNoIndex, getExprECL(expr, s).str());
}
case no_assertstepped:
{
StringBuffer s;
throwError1(HQLERR_SteppedNoJoin, getExprECL(expr, s).str());
}
case no_id2blob:
doBuildExprIdToBlob(ctx, expr, tgt);
return;
case no_blob2id:
doBuildExprBlobToId(ctx, expr, tgt);
return;
case no_cppbody:
doBuildExprCppBody(ctx, expr, &tgt);
return;
case no_null:
tgt.length.setown(getSizetConstant(0));
tgt.expr.setown(createValue(no_nullptr, makeReferenceModifier(expr->getType())));
return;
case no_clustersize:
doBuildExprSysFunc(ctx, expr, tgt, getClusterSizeAtom);
return;
case no_deref:
//Untested
buildExpr(ctx, expr->queryChild(0), tgt);
if (tgt.expr->getOperator() == no_address)
tgt.expr.setown(createValue(no_typetransfer, expr->getType(), LINK(tgt.expr->queryChild(0))));
else
tgt.expr.setown(createValue(no_deref, expr->getType(), LINK(tgt.expr)));
return;
case no_funcdef:
tgt.expr.setown(doBuildInternalFunction(expr));
useFunction(tgt.expr);
return;
case no_purevirtual:
case no_internalvirtual:
{
//This shouldn't happen we should have an no_checkconcrete wrapper inserted into the tree like checkconstant,
//but it currently can in obscure library contexts (e.g., library3ie2.xhql)
_ATOM name = expr->queryName();
throwError1(HQLERR_ConcreteMemberRequired, name ? name->str() : "");
}
case NO_AGGREGATEGROUP:
throwError1(HQLERR_OutsideGroupAggregate, getOpString(op));
default:
break;
}
StringBuffer msg;
msg.append("Unexpected operator '").append(getOpString(op)).append("' in: HqlCppTranslator::buildExpr(");
toECL(expr, msg, true);
// expr->toString(msg);
msg.append(")");
throw MakeStringException(HQLERR_UnexpectedOperator, "%s", msg.str());
doBuildPureSubExpr(ctx, expr, tgt);
}
void HqlCppTranslator::buildExprOrAssign(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * expr, CHqlBoundExpr * tgt)
{
if (target)
buildExprAssign(ctx, *target, expr);
else if (tgt)
buildExpr(ctx, expr, *tgt);
else
buildStmt(ctx, expr);
}
bool HqlCppTranslator::specialCaseBoolReturn(BuildCtx & ctx, IHqlExpression * expr)
{
if (!options.optimizeBoolReturn)
return false;
if ((expr->getOperator() == no_and) && (unwoundCount(expr, no_and) <= MAX_NESTED_CASES))
return true;
if (expr->getOperator() == no_not)
expr = expr->queryChild(0);
if (!requiresTemp(ctx, expr, true))
return false;
if (expr->getOperator() == no_alias_scope)
expr = expr->queryChild(0);
if ((expr->getOperator() == no_and) || (expr->getOperator() == no_or))
return true;
return false;
}
void HqlCppTranslator::buildReturn(BuildCtx & ctx, IHqlExpression * expr, ITypeInfo * retType)
{
ITypeInfo * exprType = expr->queryType();
if (!retType) retType = exprType;
expr = queryExpandAliasScope(ctx, expr);
node_operator op = expr->getOperator();
if ((retType->getSize() == UNKNOWN_LENGTH) && (retType->getTypeCode() == type_varstring))
{
if (hasConstModifier(retType) && (hasConstModifier(exprType) || expr->queryValue()))
{
OwnedHqlExpr cast = ensureExprType(expr, retType);
CHqlBoundExpr ret;
buildCachedExpr(ctx, cast, ret);
ctx.setNextDestructor();
ctx.addReturn(ret.expr);
}
else
{
if (hasConstModifier(retType))
{
BuildCtx * declareCtx = NULL;
if (getInvariantMemberContext(ctx, &declareCtx, NULL, false, false))
{
CHqlBoundTarget tempTarget;
createTempFor(*declareCtx, retType, tempTarget, typemod_member, FormatNatural);
buildExprAssign(ctx, tempTarget, expr);
OwnedHqlExpr result = getElementPointer(tempTarget.expr);
ctx.addReturn(result);
return;
}
//we are going to have a memory leak......
PrintLog("Runtime memory leak returning allocated string from constant function");
}
CHqlBoundTarget retVar;
retVar.expr.setown(createWrapperTemp(ctx, retType, typemod_none));
buildExprAssign(ctx, retVar, expr);
ctx.setNextDestructor();
StringBuffer s;
retVar.expr->toString(s);
switch (retType->getTypeCode())
{
case type_varstring:
s.append(".detachstr()");
break;
case type_varunicode:
s.append(".detachustr()");
break;
default:
UNIMPLEMENTED;
}
OwnedHqlExpr temp = createQuoted(s.str(), LINK(exprType));
ctx.addReturn(temp);
}
}
else if ((retType->getTypeCode() == type_boolean) && specialCaseBoolReturn(ctx, expr))
{
bool successValue = true;
if (op == no_not)
{
//!(a and b) is converted into !a || !b. Otherwise just invert the test condition.
IHqlExpression * child = expr->queryChild(0);
if (child->getOperator() == no_alias_scope)
child = child->queryChild(0);
if (child->getOperator() != no_and)
{
successValue = false;
expr = expr->queryChild(0);
}
}
BuildCtx condctx(ctx);
buildFilteredReturn(condctx, expr, queryBoolExpr(successValue));
buildReturn(ctx, queryBoolExpr(!successValue));
}
else if (op == no_if)
{
OwnedHqlExpr castTrue = ensureExprType(expr->queryChild(1), retType);
OwnedHqlExpr castFalse = ensureExprType(expr->queryChild(2), retType);
BuildCtx condctx(ctx);
buildFilter(condctx, expr->queryChild(0));
buildReturn(condctx, castTrue);
buildReturn(ctx, castFalse);
}
else if (op == no_map || op == no_case)
{
HqlCppCaseInfo info(*this);
doBuildCaseInfo(expr, info);
info.buildReturn(ctx);
}
else
{
CHqlBoundExpr ret;
// A very temporary work around for potential gpf using variable length strings
OwnedHqlExpr castExpr = ensureExprType(expr, retType);
buildSimpleExpr(ctx, castExpr, ret);
ctx.setNextDestructor();
ctx.addReturn(ret.expr);
}
}
//Assumes that the value being returned is simple.
//for (a || b || c) gen if (a) return x; if (b) return x; if (c) return x;
//and !(a && b && c) -> !a || !b || !c
void HqlCppTranslator::buildFilteredReturn(BuildCtx & ctx, IHqlExpression * filter, IHqlExpression * value)
{
filter = queryExpandAliasScope(ctx, filter);
HqlExprArray conds;
node_operator op = filter->getOperator();
if (op == no_or)
{
buildFilteredReturn(ctx, filter->queryChild(0), value);
buildFilteredReturn(ctx, filter->queryChild(1), value);
return;
}
if (op == no_not)
{
IHqlExpression * child = filter->queryChild(0);
node_operator childOp = child->getOperator();
if (childOp == no_and)
{
child->unwindList(conds, no_and);
ForEachItemIn(i, conds)
{
IHqlExpression & cur = conds.item(i);
OwnedHqlExpr inverse = getInverse(&cur);
buildFilteredReturn(ctx, inverse, value);
}
return;
}
if (childOp == no_alias_scope)
{
expandAliasScope(ctx, child);
OwnedHqlExpr inverse = getInverse(child->queryChild(0));
buildFilteredReturn(ctx, inverse, value);
return;
}
}
BuildCtx condctx(ctx);
buildFilter(condctx, filter);
if (value)
buildReturn(condctx, value);
else
condctx.addReturn(NULL);
}
void HqlCppTranslator::buildStmt(BuildCtx & _ctx, IHqlExpression * expr)
{
BuildCtx ctx(_ctx);
switch (expr->getOperator())
{
case no_assign:
doBuildStmtAssign(ctx, expr->queryChild(0), expr->queryChild(1));
return;
case no_assign_addfiles:
doBuildStmtAssignModify(ctx, expr->queryChild(0), expr->queryChild(1), expr->getOperator());
return;
case no_alias:
doBuildExprAlias(ctx, expr, NULL);
return;
case no_alias_scope:
{
expandAliasScope(ctx, expr);
buildStmt(ctx, expr->queryChild(0));
return;
}
case no_assignall:
{
unsigned idx;
unsigned kids = expr->numChildren();
for (idx = 0; idx < kids; idx++)
buildStmt(ctx, expr->queryChild(idx));
return;
}
case no_comma:
case no_compound:
buildStmt(ctx, expr->queryChild(0));
buildStmt(ctx, expr->queryChild(1));
return;
case no_if:
doBuildStmtIf(ctx, expr);
return;
case no_call:
case no_externalcall:
doBuildStmtCall(ctx, expr);
return;
case no_nofold:
case no_nohoist:
case no_nothor:
case no_section:
case no_sectioninput:
buildStmt(ctx, expr->queryChild(0));
return;
case no_null:
return;
case no_fail:
doBuildStmtFail(ctx, expr);
return;
case no_setmeta:
return;
case no_update:
doBuildStmtUpdate(ctx, expr);
return;
case no_output:
if (queryRealChild(expr, 1))
throwError1(HQLERR_NotSupportedInsideNoThor, "OUTPUT to file");
doBuildStmtOutput(ctx, expr);
return;
case no_subgraph:
doBuildThorChildSubGraph(ctx, expr, SubGraphRoot);
return;
case no_thor:
doBuildThorGraph(ctx, expr);
return;
case no_workflow_action:
return;
case no_ensureresult:
doBuildStmtEnsureResult(ctx, expr);
return;
case no_extractresult:
case no_setresult:
doBuildStmtSetResult(ctx, expr);
return;
case no_parallel:
case no_sequential:
case no_actionlist:
{
ForEachChild(idx, expr)
buildStmt(ctx, expr->queryChild(idx));
return;
}
case no_wait:
doBuildStmtWait(ctx, expr);
return;
case no_notify:
doBuildStmtNotify(ctx, expr);
return;
case no_skip:
doBuildStmtSkip(ctx, expr, NULL);
return;
case no_assert:
doBuildStmtAssert(ctx, expr);
return;
case no_cppbody:
doBuildExprCppBody(ctx, expr, NULL);
return;
case no_setworkflow_cond:
{
HqlExprArray args;
args.append(*LINK(expr->queryChild(0)));
buildFunctionCall(ctx, setWorkflowConditionAtom, args);
return;
}
case no_apply:
doBuildStmtApply(ctx, expr);
return;
case no_cluster:
doBuildStmtCluster(ctx, expr);
return;
case no_choose:
doBuildChoose(ctx, NULL, expr);
return;
case no_persist_check:
buildWorkflowPersistCheck(ctx, expr);
return;
case no_evaluate_stmt:
expr = expr->queryChild(0);
if (expr->queryValue())
return;
// fall through to default behaviour.
}
CHqlBoundExpr tgt;
buildAnyExpr(ctx, expr, tgt);
ctx.addExpr(tgt.expr);
tidyupExpr(ctx, tgt);
}
class AliasExpansionInfo
{
public:
void pushCondition(IHqlExpression * expr, unsigned branch) { conditions.append(*createAttribute(branchAtom, LINK(expr), getSizetConstant(branch))); }
void popCondition() { conditions.pop(); }
void popConditions(unsigned num) { conditions.popn(num); }
bool isConditional() { return conditions.ordinality() != 0; }
IHqlExpression * createConditionIntersection(IHqlExpression * prev)
{
if (conditions.ordinality() == 0)
return NULL;
if (!prev)
return createValueSafe(no_sortlist, makeSortListType(NULL), conditions);
unsigned maxPrev = prev->numChildren();
unsigned max = maxPrev;
if (max > conditions.ordinality())
max = conditions.ordinality();
for (unsigned i=0; i < max; i++)
{
if (&conditions.item(i) != prev->queryChild(i))
{
if (i == 0)
return NULL;
return createValueSafe(no_sortlist, makeSortListType(NULL), conditions, 0, i);
}
}
if (max == maxPrev)
return LINK(prev);
return createValueSafe(no_sortlist, makeSortListType(NULL), conditions);
}
HqlExprArray conditions;
};
void HqlCppTranslator::doExpandAliases(BuildCtx & ctx, IHqlExpression * expr, AliasExpansionInfo & info)
{
IHqlExpression * prev = static_cast(expr->queryTransformExtra());
if (prev == expr)
return;
#ifdef USE_NEW_ALIAS_CODE
OwnedHqlExpr commonPath = info.createConditionIntersection(prev);
if (prev == commonPath)
return;
if (commonPath)
expr->setTransformExtra(commonPath);
else
expr->setTransformExtraUnlinked(expr);
node_operator op = expr->getOperator();
switch (op)
{
//MORE: Anything that creates a child query shouldn't be included here...
case no_select:
case NO_AGGREGATE:
case no_alias_scope:
break;
case no_alias:
{
IHqlExpression * value = expr->queryChild(0);
if ((commonPath == NULL) && !ctx.queryMatchExpr(value))
{
if (containsAliasLocally(value) && !expr->hasProperty(globalAtom))
doExpandAliases(ctx, value, info);
doBuildExprAlias(ctx, expr, NULL);
}
break;
}
case no_and:
case no_or:
{
HqlExprArray args;
expr->unwindList(args, op);
doExpandAliases(ctx, &args.item(0), info);
unsigned max = args.ordinality();
for (unsigned i=1; i < max; i++)
{
info.pushCondition(expr, i);
doExpandAliases(ctx, &args.item(i), info);
}
info.popConditions(max-1);
break;
}
case no_if:
{
doExpandAliases(ctx, expr->queryChild(0), info);
info.pushCondition(expr, 1);
doExpandAliases(ctx, expr->queryChild(1), info);
info.popCondition();
info.pushCondition(expr, 2);
doExpandAliases(ctx, expr->queryChild(2), info);
info.popCondition();
break;
}
case no_case:
{
doExpandAliases(ctx, expr->queryChild(0), info);
unsigned max = expr->numChildren();
for (unsigned i=1; i < max-1; i++)
{
info.pushCondition(expr, i*2);
doExpandAliases(ctx, expr->queryChild(i)->queryChild(0), info);
info.popCondition();
info.pushCondition(expr, i*2+1);
doExpandAliases(ctx, expr->queryChild(i)->queryChild(1), info);
info.popCondition();
}
info.pushCondition(expr, (max-1)*2);
doExpandAliases(ctx, expr->queryChild(max-1), info);
info.popCondition();
break;
}
case no_map:
{
//The following is equivalent to old, code; I'm not sure it is the best implementation
unsigned max = expr->numChildren();
for (unsigned i=0; i < max-1; i++)
{
info.pushCondition(expr, i*2);
doExpandAliases(ctx, expr->queryChild(i)->queryChild(0), info);
info.popCondition();
info.pushCondition(expr, i*2+1);
doExpandAliases(ctx, expr->queryChild(i)->queryChild(1), info);
info.popCondition();
}
info.pushCondition(expr, (max-1)*2);
doExpandAliases(ctx, expr->queryChild(max-1), info);
info.popCondition();
break;
}
default:
if (containsAliasLocally(expr))
{
ForEachChild(i, expr)
doExpandAliases(ctx, expr->queryChild(i), info);
}
break;
}
#else
expr->setTransformExtraUnlinked(expr);
node_operator op = expr->getOperator();
switch (op)
{
//MORE: Anything that creates a child query shouldn't be included here...
case no_select:
case NO_AGGREGATE:
case no_alias_scope:
break;
case no_alias:
{
doBuildExprAlias(ctx, expr, NULL);
break;
}
default:
if (containsAliasLocally(expr))
{
ForEachChild(i, expr)
doExpandAliases(ctx, expr->queryChild(i), info);
}
break;
}
#endif
}
void HqlCppTranslator::expandAliases(BuildCtx & ctx, IHqlExpression * expr)
{
if (containsAliasLocally(expr))
{
TransformMutexBlock block;
AliasExpansionInfo info;
doExpandAliases(ctx, expr, info);
}
}
void HqlCppTranslator::expandAliasScope(BuildCtx & ctx, IHqlExpression * expr)
{
TransformMutexBlock block;
AliasExpansionInfo info;
unsigned max = expr->numChildren();
for (unsigned idx = 1; idx < max; idx++)
{
IHqlExpression * child = expr->queryChild(idx);
if (containsAliasLocally(child))
doExpandAliases(ctx, child, info);
}
}
//------------------------------------------------------------------------------
void HqlCppTranslator::gatherActiveCursors(BuildCtx & ctx, HqlExprCopyArray & activeRows)
{
AssociationIterator iter(ctx);
ForEach(iter)
{
HqlExprAssociation & cur = iter.get();
if (cur.isRowAssociation())
{
BoundRow & curRow = static_cast(cur);
if ((curRow.querySide() != no_self) && !curRow.isBuilder())
activeRows.append(*curRow.querySelector());
}
else if (cur.represents->getOperator() == no_counter)
activeRows.append(*cur.represents);
}
}
bool HqlCppTranslator::canEvaluateInContext(BuildCtx & ctx, IHqlExpression * expr)
{
HqlExprCopyArray cursors;
gatherActiveCursors(ctx, cursors);
return ::canEvaluateInScope(cursors, expr);
}
bool mustEvaluateInContext(BuildCtx & ctx, IHqlExpression * expr)
{
HqlExprCopyArray required;
expr->gatherTablesUsed(NULL, &required);
if (required.ordinality() == 0)
return false;
HqlExprCopyArray activeRows;
HqlExprCopyArray inheritedRows;
RowAssociationIterator iter(ctx);
ForEach(iter)
{
BoundRow & cur = iter.get();
if ((cur.querySide() != no_self) && !cur.isBuilder())
{
IHqlExpression * selector = cur.querySelector();
if (cur.isInherited())
inheritedRows.append(*selector);
else
activeRows.append(*selector);
}
}
//Ensure all instances of activeRows which match the inherited rows are removed
ForEachItemInRev(i, activeRows)
{
if (inheritedRows.find(activeRows.item(i)))
activeRows.remove(i);
}
return canEvaluateInScope(activeRows, required);
}
bool filterIsTableInvariant(IHqlExpression * expr)
{
IHqlExpression * dsSelector = expr->queryChild(0)->queryNormalizedSelector();
ForEachChildFrom(i, expr, 1)
{
IHqlExpression * cur = expr->queryChild(i);
if (containsSelector(cur, dsSelector))
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool LoopInvariantHelper::getBestContext(BuildCtx & ctx, IHqlExpression * expr)
{
finished();
active = ctx.selectBestContext(expr);
return (active != NULL);
}
void LoopInvariantHelper::finished()
{
if (active)
{
active->mergeScopeWithContainer();
active = NULL;
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::buildBlockCopy(BuildCtx & ctx, IHqlExpression * tgt, CHqlBoundExpr & src)
{
OwnedHqlExpr size = getBoundSize(src);
if (!size->queryValue() || size->queryValue()->getIntValue() != 0)
{
HqlExprArray args;
args.append(*getPointer(tgt));
args.append(*getPointer(src.expr));
args.append(*size.getClear());
callProcedure(ctx, memcpyAtom, args);
}
}
void HqlCppTranslator::buildSimpleExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
node_operator op = expr->getOperator();
bool simple = false;
switch (op)
{
case no_alias_scope:
{
expandAliasScope(ctx, expr);
buildSimpleExpr(ctx, expr->queryChild(0), tgt);
return;
}
case no_preservemeta:
buildSimpleExpr(ctx, expr->queryChild(0), tgt);
return;
case no_constant:
case no_variable:
case no_getresult: // gets forced into a variable.
case no_workunit_dataset: // gets forced into a variable.
case no_getgraphresult: // gets forced into a variable.
case no_alias:
case no_list:
case no_all:
case no_null:
case no_id2blob:
case no_rows:
case no_libraryinput:
simple = true;
break;
case no_compound:
buildStmt(ctx, expr->queryChild(0));
buildSimpleExpr(ctx, expr->queryChild(1), tgt);
return;
case no_field:
throwUnexpected();
case no_select:
{
CHqlBoundExpr bound;
buildCachedExpr(ctx, expr, bound);
if (isSimpleTranslatedExpr(bound.expr))
tgt.set(bound);
else
{
OwnedHqlExpr trans = bound.getTranslatedExpr();
buildTempExpr(ctx, trans, tgt);
}
return;
}
break; // should depend on whether conditional etc....
case no_translated:
simple = isSimpleTranslatedExpr(expr->queryChild(0));
break;
case no_substring:
{
SubStringInfo info(expr);
if (info.canGenerateInline() || expr->hasProperty(quickAtom))
simple = true;
break;
}
case no_cast:
case no_implicitcast:
{
//special case casting a string to (string) - saves lots of temporaries.
ITypeInfo * exprType = expr->queryType();
IHqlExpression * child = expr->queryChild(0);
ITypeInfo * childType = child->queryType();
if ((exprType->getTypeCode() == type_string) && (exprType->getSize() == UNKNOWN_LENGTH))
{
if ((childType->getTypeCode() == type_string) && (exprType->queryCharset() == childType->queryCharset()))
{
buildSimpleExpr(ctx, child, tgt);
return;
}
}
if (options.foldConstantCast && (child->getOperator() == no_constant))
simple = true;
break;
}
case no_sizeof:
case no_offsetof:
simple = true;
break;
case no_regex_find:
simple = expr->isBoolean();
break;
case no_call:
case no_externalcall:
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_set:
simple = true;
break;
case type_string:
case type_data:
case type_qstring:
if (type->getSize() == UNKNOWN_LENGTH)
simple = true;
break;
}
}
}
if (simple)
buildCachedExpr(ctx, expr, tgt);
else
buildTempExpr(ctx, expr, tgt);
}
IHqlExpression * HqlCppTranslator::buildSimplifyExpr(BuildCtx & ctx, IHqlExpression * expr)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_constant:
case no_all:
case no_null:
return LINK(expr);
case no_list:
if (expr->isConstant())
return LINK(expr);
break;
}
CHqlBoundExpr bound;
buildSimpleExpr(ctx, expr, bound);
return bound.getTranslatedExpr();
}
/* In type: not linked. Return: linked */
IHqlExpression * HqlCppTranslator::createWrapperTemp(BuildCtx & ctx, ITypeInfo * type, typemod_t modifier)
{
Linked rawType = queryUnqualifiedType(type);
if (hasLinkCountedModifier(type))
rawType.setown(makeAttributeModifier(rawType.getClear(), getLinkCountedAttr()));
Owned declType = makeWrapperModifier(rawType.getClear());
declType.setown(makeModifier(declType.getClear(), modifier));
return ctx.getTempDeclare(declType, NULL);
}
void HqlCppTranslator::createTempFor(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundTarget & target)
{
createTempFor(ctx, expr->queryType(), target, typemod_none, FormatNatural);
}
void HqlCppTranslator::createTempFor(BuildCtx & ctx, ITypeInfo * _exprType, CHqlBoundTarget & target, typemod_t modifier, ExpressionFormat format)
{
Owned exprType = makeModifier(LINK(_exprType->queryPromotedType()), modifier);
type_t tc = exprType->getTypeCode();
switch (tc)
{
case type_array:
case type_table:
case type_groupedtable:
{
if (recordRequiresSerialization(::queryRecord(exprType)) || hasLinkCountedModifier(_exprType))
{
assertex(format != FormatBlockedDataset);
format = FormatLinkedDataset;
}
else if (options.tempDatasetsUseLinkedRows && (format == FormatNatural))
format = FormatLinkedDataset;
break;
}
}
if (hasLinkCountedModifier(exprType))
{
if (format == FormatNatural)
format = FormatLinkedDataset;
else if (format == FormatBlockedDataset)
exprType.setown(setLinkCountedAttr(exprType, false));
}
else
{
if (format == FormatNatural)
format = FormatBlockedDataset;
else if ((format == FormatLinkedDataset) || (format == FormatArrayDataset))
exprType.setown(setLinkCountedAttr(exprType, true));
}
size32_t size = exprType->getSize();
if (size == UNKNOWN_LENGTH)
{
switch (tc)
{
case type_string:
case type_data:
case type_qstring:
case type_unicode:
case type_utf8:
{
OwnedITypeInfo lenType = makeModifier(LINK(sizetType), modifier);
target.expr.setown(createWrapperTemp(ctx, exprType, modifier));
target.length.setown(ctx.getTempDeclare(lenType, NULL));
break;
}
case type_varstring:
case type_varunicode:
{
target.expr.setown(createWrapperTemp(ctx, exprType, modifier));
break;
}
case type_set:
case type_array:
case type_table:
case type_groupedtable:
break;
default:
UNIMPLEMENTED;
}
}
else if (size > MAX_SIMPLE_VAR_SIZE)
{
switch (tc)
{
case type_string:
case type_data:
case type_qstring:
case type_unicode:
case type_varstring:
case type_varunicode:
case type_utf8:
{
target.expr.setown(createWrapperTemp(ctx, exprType, modifier));
break;
}
}
}
switch (tc)
{
case type_set:
{
OwnedITypeInfo isAllType = makeModifier(LINK(boolType), modifier);
target.isAll.setown(ctx.getTempDeclare(isAllType, NULL));
}
//fall through
case type_array:
case type_table:
case type_groupedtable:
{
OwnedITypeInfo lenType = makeModifier(LINK(sizetType), modifier);
if ((modifier != typemod_member) && ctx.queryMatchExpr(queryConditionalRowMarker()))
ctx.setNextPriority(BuildCtx::OutermostScopePrio);
target.expr.setown(createWrapperTemp(ctx, exprType, modifier));
if (isArrayRowset(exprType))
{
//A bit of a hack, but the cleanest I could come up with... really access to the count member should be wrapped in
//member functions, but getting them created needs a whole new level of complication (probably moving out out of hqlwcpp)
StringBuffer name;
target.expr->toString(name).append(".count");
target.count.setown(createVariable(name, LINK(lenType)));
}
else
target.length.setown(ctx.getTempDeclare(lenType, NULL));
break;
}
}
if (!target.expr)
{
target.expr.setown(ctx.getTempDeclare(exprType, NULL));
}
}
void HqlCppTranslator::buildTempExpr(BuildCtx & ctx, BuildCtx & declareCtx, CHqlBoundTarget & tempTarget, IHqlExpression * expr, ExpressionFormat format, bool ignoreSetAll)
{
if (options.addLocationToCpp)
{
IHqlExpression * location = queryLocation(expr);
if (location)
{
StringBuffer s;
s.append("// ").append(location->querySourcePath()->str()).append("(").append(location->getStartLine()).append(",").append(location->getStartColumn()).append(") ").append(expr->queryName());
ctx.addQuoted(s);
}
else if (expr->queryName())
{
StringBuffer s;
s.append("// ").append(expr->queryName());
ctx.addQuoted(s);
}
}
typemod_t modifier = (&ctx != &declareCtx) ? typemod_member : typemod_none;
OwnedITypeInfo type = makeModifier(expr->getType(), modifier);
BuildCtx subctx(ctx);
switch (type->getTypeCode())
{
case type_row:
{
Owned tempRow = declareTempRow(declareCtx, subctx, expr);
IHqlStmt * stmt = subctx.addGroup();
stmt->setIncomplete(true);
Owned rowBuilder = createRowBuilder(subctx, tempRow);
Owned createdRef = createReferenceSelector(rowBuilder);
buildRowAssign(subctx, createdRef, expr);
finalizeTempRow(subctx, tempRow, rowBuilder);
stmt->setIncomplete(false);
stmt->mergeScopeWithContainer();
tempTarget.expr.set(tempRow->queryBound());
ctx.associate(*tempRow);
break;
}
break;
case type_table:
case type_groupedtable:
{
createTempFor(declareCtx, type, tempTarget, modifier, format);
IHqlStmt * stmt = subctx.addGroup();
stmt->setIncomplete(true);
buildDatasetAssign(subctx, tempTarget, expr);
stmt->setIncomplete(false);
stmt->mergeScopeWithContainer();
break;
}
default:
{
createTempFor(declareCtx, type, tempTarget, modifier, format);
if (ignoreSetAll)
tempTarget.isAll.clear();
IHqlStmt * stmt = subctx.addGroup();
stmt->setIncomplete(true);
buildExprAssign(subctx, tempTarget, expr);
stmt->setIncomplete(false);
stmt->mergeScopeWithContainer();
break;
}
}
}
void HqlCppTranslator::buildTempExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ExpressionFormat format)
{
node_operator op = expr->getOperator();
if (op == no_alias)
{
doBuildExprAlias(ctx, expr, &tgt);
return;
}
if (isCast(expr))
{
ITypeInfo * exprType = expr->queryType();
if (exprType->getStringLen() == UNKNOWN_LENGTH)
{
unsigned bestLen = getBestLengthEstimate(expr);
if (bestLen != UNKNOWN_LENGTH)
{
IHqlExpression * uncast = expr->queryChild(0);
Owned stretchedType = getStretchedType(bestLen, exprType);
OwnedHqlExpr castExpr = ensureExprType(uncast, stretchedType);
buildTempExpr(ctx, castExpr, tgt, format);
ctx.associateExpr(expr, tgt);
return;
}
}
}
BuildCtx bestctx(ctx);
if (expr->isPure() && ctx.getMatchExpr(expr, tgt))
return;
switch (expr->getOperator())
{
case no_variable:
tgt.expr.set(expr);
return;
case no_translated:
{
if (!expr->queryChild(1))
{
IHqlExpression * value = expr->queryChild(0);
if (value->getOperator() == no_variable)
{
tgt.expr.set(value);
return;
}
}
break;
}
case no_id2blob:
buildExpr(ctx, expr, tgt);
return;
case no_externalcall:
if (format == FormatNatural && expr->isDataset())
format = hasLinkCountedModifier(expr->queryType()) ? FormatLinkedDataset : FormatBlockedDataset;
break;
}
LoopInvariantHelper helper;
if (options.optimizeLoopInvariant)
helper.getBestContext(bestctx, expr);
bool canBeAll = canSetBeAll(expr);
CHqlBoundTarget tempTarget;
buildTempExpr(bestctx, bestctx, tempTarget, expr, format, !canBeAll);
tgt.setFromTarget(tempTarget);
bestctx.associateExpr(expr, tgt);
}
void HqlCppTranslator::buildExprViaTypedTemp(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ITypeInfo * type)
{
OwnedHqlExpr cast = createValue(no_implicitcast, LINK(type), LINK(expr));
if (cast->isPure() && ctx.getMatchExpr(cast, tgt))
return;
LoopInvariantHelper helper;
BuildCtx bestctx(ctx);
if (options.optimizeLoopInvariant)
helper.getBestContext(bestctx, expr);
CHqlBoundTarget tempTarget;
createTempFor(bestctx, type, tempTarget, typemod_none, FormatNatural);
buildExprAssign(bestctx, tempTarget, expr);
tgt.setFromTarget(tempTarget);
if (cast->isPure())
bestctx.associateExpr(cast, tgt);
}
void HqlCppTranslator::buildExprEnsureType(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, ITypeInfo * type)
{
if (queryUnqualifiedType(expr->queryType()) != queryUnqualifiedType(type))
buildExprViaTypedTemp(ctx, expr, tgt, type);
else
buildExpr(ctx, expr, tgt);
}
AliasKind HqlCppTranslator::doBuildAliasValue(BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt)
{
//can happen when this is called for non no_alias arguments
if (value->getOperator() == no_alias)
value = value->queryChild(0);
EvalContext * instance = queryEvalContext(ctx);
if (instance)
return instance->evaluateExpression(ctx, value, tgt, true);
expandAliases(ctx, value);
buildTempExpr(ctx, value, tgt);
return RuntimeAlias;
}
void HqlCppTranslator::doBuildExprAlias(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt)
{
//MORE These will be declared in a different context later.
IHqlExpression * value = expr->queryChild(0);
assertex(value->getOperator() != no_alias);
//The second half of this test could cause aliases to be duplicated, but has the significant effect of reducing the amount of data that is serialised.
//so far on my examples it does the latter, but doesn't seem to cause the former
if (expr->hasProperty(localAtom) || (insideOnCreate(ctx) && !expr->hasProperty(globalAtom)))
{
expandAliases(ctx, value);
if (tgt)
buildTempExpr(ctx, value, *tgt);
else
{
CHqlBoundExpr bound;
buildTempExpr(ctx, value, bound);
}
}
else
{
if (tgt)
doBuildAliasValue(ctx, value, *tgt);
else
{
CHqlBoundExpr bound;
doBuildAliasValue(ctx, value, bound);
}
}
}
void HqlCppTranslator::doBuildBoolAssign(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
if (requiresTemp(ctx, expr, true))
{
BuildCtx subctx(ctx);
assignBound(subctx, target, queryBoolExpr(false));
buildFilter(subctx, expr);
assignBound(subctx, target, queryBoolExpr(true));
}
else
{
CHqlBoundExpr temp;
buildCachedExpr(ctx, expr, temp);
assign(ctx, target, temp);
}
}
void HqlCppTranslator::doBuildExprAssign(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
CHqlBoundExpr temp;
buildExpr(ctx, expr, temp);
assign(ctx, target, temp);
}
void HqlCppTranslator::ensureSimpleExpr(BuildCtx & ctx, CHqlBoundExpr & tgt)
{
if (!isSimpleTranslatedExpr(tgt.expr))
{
OwnedHqlExpr bound = tgt.getTranslatedExpr();
buildTempExpr(ctx, bound, tgt);
}
}
IHqlExpression * HqlCppTranslator::ensureSimpleTranslatedExpr(BuildCtx & ctx, IHqlExpression * expr)
{
if (isSimpleTranslatedExpr(expr))
return LINK(expr);
OwnedHqlExpr translated = createTranslated(expr);
CHqlBoundExpr bound;
buildTempExpr(ctx, translated, bound);
return LINK(bound.expr);
}
void HqlCppTranslator::ensureHasAddress(BuildCtx & ctx, CHqlBoundExpr & tgt)
{
IHqlExpression * expr = tgt.expr;
node_operator op = expr->getOperator();
switch (op)
{
case no_deref:
case no_variable:
break;
default:
if (!isTypePassedByAddress(expr->queryType()))
{
OwnedHqlExpr bound = tgt.getTranslatedExpr();
buildTempExpr(ctx, bound, tgt);
}
break;
}
}
//---------------------------------------------------------------------------
bool optimizeVarStringCompare(node_operator op, const CHqlBoundExpr & lhs, const CHqlBoundExpr & rhs, CHqlBoundExpr & tgt)
{
IHqlExpression * rhsExpr = rhs.expr;
if ((rhsExpr->getOperator() == no_constant) && (rhsExpr->queryType()->getStringLen() == 0))
{
if ((op == no_eq) || (op == no_ne))
{
tgt.expr.setown(createValue(op, LINK(boolType), createValue(no_deref, makeCharType(), lhs.expr.getLink()), getZero()));
return true;
}
}
return false;
}
void HqlCppTranslator::doBuildExprSetCompareAll(BuildCtx & ctx, IHqlExpression * set, CHqlBoundExpr & tgt, bool invert)
{
Owned cursor = createSetSelector(ctx, set);
cursor->buildIsAll(ctx, tgt);
if (invert)
tgt.expr.setown(getInverse(tgt.expr));
}
void HqlCppTranslator::doBuildExprSetCompareNone(BuildCtx & ctx, IHqlExpression * set, CHqlBoundExpr & tgt, bool invert)
{
Owned cursor = createSetSelector(ctx, set);
cursor->buildExists(ctx, tgt);
if (!invert)
tgt.expr.setown(getInverse(tgt.expr));
}
bool HqlCppTranslator::doBuildExprSetCompare(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
//Special case comparison against all and null set. All other work goes through a the order code.
node_operator exprOp = expr->getOperator();
if ((exprOp == no_eq) || (exprOp == no_ne))
{
OwnedHqlExpr left = normalizeListCasts(expr->queryChild(0));
OwnedHqlExpr right = normalizeListCasts(expr->queryChild(1));
if (right->getOperator() == no_all)
doBuildExprSetCompareAll(ctx, left, tgt, exprOp==no_ne);
else if (left->getOperator() == no_all)
doBuildExprSetCompareAll(ctx, right, tgt, exprOp==no_ne);
else if (isNullList(right))
doBuildExprSetCompareNone(ctx, left, tgt, exprOp==no_ne);
else if (isNullList(left))
doBuildExprSetCompareNone(ctx, right, tgt, exprOp==no_ne);
else
return false;
return true;
}
return false;
}
IHqlExpression * HqlCppTranslator::convertBoundStringToChar(const CHqlBoundExpr & bound)
{
OwnedHqlExpr element = getElementPointer(bound.expr);
Owned charType = makeCharType(true);
switch (element->getOperator())
{
case no_constant:
{
IValue * value = element->queryValue();
return createConstant(value->castTo(charType));
}
case no_address:
return LINK(element->queryChild(0));
}
return createValue(no_deref, charType.getClear(), element.getClear());
}
void HqlCppTranslator::doBuildExprCompare(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * left = expr->queryChild(0);
IHqlExpression * right = expr->queryChild(1);
ITypeInfo * leftType = left->queryType()->queryPromotedType();
ITypeInfo * rightType = right->queryType()->queryPromotedType();
assertex(areTypesComparable(leftType,rightType));
OwnedHqlExpr orderExpr;
CHqlBoundExpr lhs, rhs;
node_operator compareOp = expr->getOperator();
type_t tc = leftType->getTypeCode();
switch (tc)
{
case type_string:
case type_data:
case type_qstring:
{
OwnedHqlExpr simpleLeft = getSimplifyCompareArg(left);
OwnedHqlExpr simpleRight = getSimplifyCompareArg(right);
HqlExprArray args;
buildCachedExpr(ctx, simpleLeft, lhs);
buildCachedExpr(ctx, simpleRight, rhs);
//update types - lengths may be constant by now..
leftType = lhs.queryType();
rightType = rhs.queryType();
_ATOM func = queryStrCompareFunc(leftType);
//MORE: Move blank string compare here?
if (lhs.length || rhs.length || needVarStringCompare(leftType, rightType))
{
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
if (func == compareStrStrAtom && isBlankString(rhs.expr))
{
func = compareStrBlankAtom;
}
else
{
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
}
orderExpr.setown(bindTranslatedFunctionCall(func, args));
}
else if (options.optimizeString1Compare &&
(tc == type_string || tc == type_data) && (leftType->getSize() == 1) &&
((compareOp == no_eq) || (compareOp == no_ne)))
{
//Optimize equality/non equality of a single character string.
//Not done for > etc because of potential issues with signed/unsigned chars
args.append(*convertBoundStringToChar(lhs));
args.append(*convertBoundStringToChar(rhs));
tgt.expr.setown(createValue(compareOp, makeBoolType(), args));
return;
}
else
{
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
args.append(*getSizetConstant(leftType->getSize()));
orderExpr.setown(bindTranslatedFunctionCall(memcmpAtom, args));
}
break;
}
case type_unicode:
{
OwnedHqlExpr simpleLeft = LINK(left);//getSimplifyCompareArg(left);
OwnedHqlExpr simpleRight = LINK(right);//getSimplifyCompareArg(right);
HqlExprArray args;
buildCachedExpr(ctx, simpleLeft, lhs);
buildCachedExpr(ctx, simpleRight, rhs);
assertex(haveCommonLocale(leftType, rightType));
char const * locale = getCommonLocale(leftType, rightType)->str();
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
orderExpr.setown(bindTranslatedFunctionCall(compareUnicodeUnicodeAtom, args));
break;
}
case type_varunicode:
{
HqlExprArray args;
buildCachedExpr(ctx, left, lhs);
buildCachedExpr(ctx, right, rhs);
assertex(haveCommonLocale(leftType, rightType));
char const * locale = getCommonLocale(leftType, rightType)->str();
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
orderExpr.setown(bindTranslatedFunctionCall(compareVUnicodeVUnicodeAtom, args));
break;
}
case type_utf8:
{
OwnedHqlExpr simpleLeft = LINK(left);//getSimplifyCompareArg(left);
OwnedHqlExpr simpleRight = LINK(right);//getSimplifyCompareArg(right);
HqlExprArray args;
buildCachedExpr(ctx, simpleLeft, lhs);
buildCachedExpr(ctx, simpleRight, rhs);
assertex(haveCommonLocale(leftType, rightType));
char const * locale = getCommonLocale(leftType, rightType)->str();
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
orderExpr.setown(bindTranslatedFunctionCall(compareUtf8Utf8Atom, args));
break;
}
case type_varstring:
{
HqlExprArray args;
buildCachedExpr(ctx, left, lhs);
buildCachedExpr(ctx, right, rhs);
//optimize comparison against null string.
if (optimizeVarStringCompare(compareOp, lhs, rhs, tgt) || optimizeVarStringCompare(compareOp, rhs, lhs, tgt))
return;
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
orderExpr.setown(bindTranslatedFunctionCall(compareVStrVStrAtom, args));
break;
}
case type_decimal:
{
HqlExprArray args;
buildCachedExpr(ctx, left, lhs);
buildCachedExpr(ctx, right, rhs);
if (!isPushed(lhs) && !isPushed(rhs) && isSameBasicType(leftType, rightType))
{
args.append(*getSizetConstant(leftType->getSize()));
args.append(*getPointer(lhs.expr));
args.append(*getPointer(rhs.expr));
orderExpr.setown(bindTranslatedFunctionCall(leftType->isSigned() ? DecCompareDecimalAtom : DecCompareUDecimalAtom, args));
}
else
{
bool pushedLhs = ensurePushed(ctx, lhs);
bool pushedRhs = ensurePushed(ctx, rhs);
//NB: Arguments could be pushed in opposite order 1 <=> x *2
if (pushedLhs && !pushedRhs)
orderExpr.setown(bindTranslatedFunctionCall(DecDistinctRAtom, args));
else
orderExpr.setown(bindTranslatedFunctionCall(DecDistinctAtom, args));
}
break;
}
case type_set:
case type_array:
{
if (doBuildExprSetCompare(ctx, expr, tgt))
return;
//fallthrough....
}
case type_table:
case type_groupedtable:
case type_row:
case type_record:
{
IHqlExpression * orderExpr = createValue(no_order, LINK(signedType), LINK(left), LINK(right));
OwnedHqlExpr cmpExpr = createBoolExpr(compareOp, orderExpr, createConstant(signedType->castFrom(true, 0)));
buildExpr(ctx, cmpExpr, tgt);
return;
}
case type_swapint:
case type_packedint:
{
Owned type = makeIntType(leftType->getSize(), leftType->isSigned());
IHqlExpression * intLeft = createValue(no_implicitcast, type.getLink(), LINK(left));
IHqlExpression * intRight = createValue(no_implicitcast, type.getLink(), LINK(right));
OwnedHqlExpr transformed = createValue(compareOp, makeBoolType(), intLeft, intRight);
doBuildPureSubExpr(ctx, transformed, tgt);
return;
}
default:
doBuildPureSubExpr(ctx, expr, tgt);
return;
}
tgt.expr.setown(createValue(compareOp, LINK(boolType), LINK(orderExpr), createConstant(orderExpr->queryType()->castFrom(true, 0))));
}
//---------------------------------------------------------------------------
//-- no_and --
void HqlCppTranslator::doBuildFilterToTarget(BuildCtx & ctx, const CHqlBoundTarget & isOk, HqlExprArray & conds, bool invert)
{
LinkedHqlExpr test = isOk.expr;
if (invert)
test.setown(createValue(no_not, makeBoolType(), LINK(test)));
unsigned max = conds.ordinality();
unsigned curIndex = 0;
for (unsigned outer =0; curIndex < max; outer += MAX_NESTED_CASES)
{
BuildCtx subctx(ctx);
if (outer != 0)
subctx.addFilter(test);
buildExprAssign(subctx, isOk, queryBoolExpr(false ^ invert));
doBuildFilterNextAndRange(subctx, curIndex, MAX_NESTED_CASES, conds);
buildExprAssign(subctx, isOk, queryBoolExpr(true ^ invert));
}
}
void HqlCppTranslator::doBuildFilterAnd(BuildCtx & ctx, IHqlExpression * expr)
{
HqlExprArray conds;
expr->unwindList(conds, no_and);
//Estimate the depth generated by the conditions - it may be wrong because
//aliases generated in outer levels can stop temporaries being required later.
unsigned numConds = conds.ordinality();
unsigned depthEstimate = 1;
for (unsigned i=1; i < numConds; i++)
if (requiresTemp(ctx, &conds.item(i), true))
depthEstimate++;
if (depthEstimate < MAX_NESTED_CASES)
{
unsigned curIndex = 0;
doBuildFilterNextAndRange(ctx, curIndex, numConds, conds);
}
else
{
CHqlBoundTarget isOk;
createTempFor(ctx, expr, isOk);
doBuildFilterToTarget(ctx, isOk, conds, false);
ctx.addFilter(isOk.expr);
}
}
void HqlCppTranslator::doBuildFilterNextAndRange(BuildCtx & ctx, unsigned & curIndex, unsigned maxIterations, HqlExprArray & conds)
{
unsigned max = conds.ordinality();
for (unsigned i=0; (i < maxIterations) && (curIndex != max); i++)
{
unsigned last;
expandAliases(ctx, &conds.item(curIndex));
for (last = curIndex+1; last < max; last++)
if (requiresTemp(ctx, &conds.item(last), true))
break;
doBuildFilterAndRange(ctx, curIndex, last, conds);
curIndex = last;
}
}
void HqlCppTranslator::doBuildFilterAndRange(BuildCtx & ctx, unsigned first, unsigned last, HqlExprArray & conds)
{
if (first+1 == last)
buildFilter(ctx, &conds.item(first));
else
{
HqlExprArray args;
for (unsigned k = first; k < last; k++)
args.append(OLINK(conds.item(k)));
OwnedHqlExpr test = createValue(no_and, makeBoolType(), args);
buildFilterViaExpr(ctx, test);
}
}
void HqlCppTranslator::doBuildAssignAnd(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, bool invert)
{
HqlExprArray conds;
expr->unwindList(conds, no_and);
doBuildFilterToTarget(ctx, target, conds, invert);
}
void HqlCppTranslator::doBuildAssignOr(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
BuildCtx subctx(ctx);
HqlExprArray conds;
expr->unwindList(conds, no_or);
unsigned first = 0;
unsigned max = conds.ordinality();
while (first < max)
{
unsigned last = first+1;
//special case no_in because it always creates a temporary, so may as well assign on its own
if (conds.item(first).getOperator() != no_in)
{
for (; last < max; last++)
{
IHqlExpression & cur = conds.item(last);
if ((cur.getOperator() == no_in) || requiresTemp(subctx, &cur, true))
break;
}
}
if (first != 0 || last != max)
{
if (last != first+1)
{
OwnedHqlExpr left = createBalanced(no_or, queryBoolType(), conds, first, last);
doBuildExprAssign(subctx, target, left);
}
else
buildExprAssign(subctx, target, &conds.item(first));
if (last != max)
{
OwnedHqlExpr inverse = getInverse(target.expr);
subctx.addFilter(inverse);
}
}
else
{
doBuildExprAssign(subctx, target, expr);
}
first = last;
}
}
//---------------------------------------------------------------------------
//-- no_case --
void HqlCppTranslator::doBuildCaseInfo(IHqlExpression * expr, HqlCppCaseInfo & info)
{
unsigned maxMaps = expr->numChildren()-1;
unsigned index = 0;
if (expr->getOperator() == no_case)
{
info.setCond(expr->queryChild(0));
index++;
}
for (;indexqueryChild(index));
info.setDefault(expr->queryChild(maxMaps));
}
void HqlCppTranslator::doBuildInCaseInfo(IHqlExpression * expr, HqlCppCaseInfo & info, IHqlExpression * normalized)
{
bool valueIfMatch = (expr->getOperator() == no_in);
HqlExprArray args;
LinkedHqlExpr values = normalized;
if (!normalized)
values.setown(normalizeListCasts(expr->queryChild(1)));
info.setCond(expr->queryChild(0));
cvtInListToPairs(args, values, valueIfMatch);
info.addPairs(args);
info.setDefault(queryBoolExpr(!valueIfMatch));
}
void HqlCppTranslator::doBuildAssignInStored(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr values = normalizeListCasts(expr->queryChild(1));
CHqlBoundExpr bound;
if (values->isPure())
ctx.getMatchExpr(values, bound);
if (options.optimizeLoopInvariant && !bound.expr)
{
if (values->getOperator() == no_createset)
{
IHqlExpression * ds = values->queryChild(0);
//MORE: This is a special case - it should check if the dataset can be iterated without any projection
switch (ds->getOperator())
{
case no_select:
case no_rows:
break;
default:
{
//Evaluate the dataset into a temporary - the iterator will match it up later
//Evaluating the dataset instead of the set since this is likely to avoid a memcpy with link counted rows enabled.
LoopInvariantHelper helper;
BuildCtx bestctx(ctx);
if (helper.getBestContext(bestctx, ds))
{
CHqlBoundExpr temp;
buildTempExpr(ctx, ds, temp);
}
break;
}
}
}
else
{
LoopInvariantHelper helper;
BuildCtx bestctx(ctx);
if (helper.getBestContext(bestctx, values))
{
//Unfortunately this will do strength reduction again, but shouldn't take too long.
buildTempExpr(bestctx, values, bound);
}
}
}
if (bound.expr)
values.setown(bound.getTranslatedExpr());
bool valueIfMatch = (expr->getOperator() == no_in);
ITypeInfo * elementType = values->queryType()->queryChildType();
Owned compareType = getPromotedECLCompareType(expr->queryChild(0)->queryType(), elementType);
CHqlBoundExpr boundSearch;
OwnedHqlExpr castSearch = ensureExprType(expr->queryChild(0), compareType);
BuildCtx subctx(ctx);
Owned cursor = createSetSelector(ctx, values);
CHqlBoundExpr isAll;
cursor->buildIsAll(ctx, isAll);
if (isAll.expr->queryValue())
{
if (isAll.expr->queryValue()->getBoolValue())
{
buildExprAssign(subctx, target, queryBoolExpr(valueIfMatch));
//If this inverted the test we would need to do more.
return;
}
}
else
{
IHqlStmt * stmt = subctx.addFilter(isAll.expr);
buildExprAssign(subctx, target, queryBoolExpr(valueIfMatch));
subctx.selectElse(stmt);
}
//result = false/true
buildSimpleExpr(subctx, castSearch, boundSearch);
buildExprAssign(subctx, target, queryBoolExpr(!valueIfMatch));
//iterate through the set
bool needToBreak = !cursor->isSingleValued();
CHqlBoundExpr boundCurElement;
cursor->buildIterateLoop(subctx, boundCurElement, needToBreak);
OwnedHqlExpr curElement = boundCurElement.getTranslatedExpr();
OwnedHqlExpr castCurElement = ensureExprType(curElement, compareType);
//if match then
OwnedHqlExpr matchTest = createValue(no_eq, makeBoolType(), boundSearch.getTranslatedExpr(), LINK(castCurElement));
buildFilter(subctx, matchTest);
//result = true/false + break loop.
buildExprAssign(subctx, target, queryBoolExpr(valueIfMatch));
if (needToBreak)
subctx.addBreak();
}
void HqlCppTranslator::doBuildAssignInCreateSet(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * setExpr = expr->queryChild(1);
IHqlExpression * dataset = setExpr->queryChild(0);
IHqlExpression * selected = setExpr->queryChild(1);
bool valueIfMatch = (expr->getOperator() == no_in);
ITypeInfo * elementType = setExpr->queryType()->queryChildType();
Owned compareType = getPromotedECLCompareType(expr->queryChild(0)->queryType(), elementType);
CHqlBoundExpr boundSearch;
OwnedHqlExpr castSearch = ensureExprType(expr->queryChild(0), compareType);
//result = false/true
buildSimpleExpr(ctx, castSearch, boundSearch);
buildExprAssign(ctx, target, queryBoolExpr(!valueIfMatch));
//iterate through the set
bool needToBreak = !hasNoMoreRowsThan(dataset, 1);
BuildCtx loopctx(ctx);
buildDatasetIterate(loopctx, dataset, needToBreak);
//if match then
OwnedHqlExpr matchTest = createValue(no_eq, makeBoolType(), boundSearch.getTranslatedExpr(), ensureExprType(selected, compareType));
buildFilter(loopctx, matchTest);
//result = true/false + break loop.
buildExprAssign(loopctx, target, queryBoolExpr(valueIfMatch));
if (needToBreak)
loopctx.addBreak();
}
void HqlCppTranslator::doBuildAssignIn(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr values = normalizeListCasts(expr->queryChild(1));
node_operator op = expr->getOperator();
bool valueIfMatch = (op == no_in);
switch (values->getOperator())
{
case no_all:
buildExprAssign(ctx, target, queryBoolExpr(valueIfMatch));
break;
case no_null:
buildExprAssign(ctx, target, queryBoolExpr(!valueIfMatch));
break;
case no_list:
{
HqlCppCaseInfo info(*this);
doBuildInCaseInfo(expr, info, values);
info.buildAssign(ctx, target);
break;
}
case no_createset:
{
//Look further at bug #52745.eclxml and what causes the poor code.
//if (canIterateInline(&ctx, values->queryChild(0)))
// doBuildAssignInCreateSet(ctx, target, expr);
//else
doBuildAssignInStored(ctx, target, expr);
break;
}
#if 0
//Possible optimizations, but I don't have any examples that would trigger them, so don't enable
case no_if:
{
BuildCtx subctx(ctx);
OwnedHqlExpr inLhs = createValue(expr->getOperator(), LINK(expr->queryChild(0)), LINK(values->queryChild(1)));
OwnedHqlExpr inRhs = createValue(expr->getOperator(), LINK(expr->queryChild(0)), LINK(values->queryChild(2)));
IHqlStmt * stmt = buildFilterViaExpr(subctx, values->queryChild(0));
buildExprAssign(subctx, target, inLhs);
subctx.selectElse(stmt);
buildExprAssign(subctx, target, inRhs);
break;
}
case no_addsets:
if (op == no_in)
{
BuildCtx subctx(ctx);
OwnedHqlExpr inLhs = createValue(expr->getOperator(), LINK(expr->queryChild(0)), LINK(values->queryChild(0)));
OwnedHqlExpr inRhs = createValue(expr->getOperator(), LINK(expr->queryChild(0)), LINK(values->queryChild(1)));
buildExprAssign(ctx, target, inLhs);
OwnedHqlExpr test = getInverse(target.expr);
subctx.addFilter(test);
buildExprAssign(subctx, target, inRhs);
break;
}
#endif
//fall through
default:
doBuildAssignInStored(ctx, target, expr);
break;
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprArith(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_decimal:
{
bindAndPush(ctx, expr->queryChild(0));
bindAndPush(ctx, expr->queryChild(1));
HqlExprArray args;
_ATOM func;
switch (expr->getOperator())
{
case no_add: func = DecAddAtom; break;
case no_div: func = DecDivideAtom; break;
case no_modulus: func = DecModulusAtom; break;
case no_mul: func = DecMulAtom; break;
case no_sub: func = DecSubAtom; break;
default: UNIMPLEMENTED;
}
callProcedure(ctx, func, args);
tgt.expr.setown(createValue(no_decimalstack, LINK(type)));
}
break;
case type_swapint:
case type_packedint:
{
//someone is being deliberately perverse....
ITypeInfo * type = expr->queryType();
ITypeInfo * intType = makeIntType(type->getSize(), type->isSigned());
IHqlExpression * lhs = expr->queryChild(0);
IHqlExpression * rhs = expr->queryChild(1);
assertex(isSameBasicType(type, lhs->queryType()));
assertex(isSameBasicType(type, rhs->queryType()));
lhs = createValue(no_implicitcast, LINK(intType), LINK(lhs));
rhs = createValue(no_implicitcast, LINK(intType), LINK(rhs));
IHqlExpression * newExpr = createValue(expr->getOperator(), intType, lhs, rhs);
OwnedHqlExpr castNewExpr = createValue(no_implicitcast, LINK(type), newExpr);
buildExpr(ctx, castNewExpr, tgt);
break;
}
default:
doBuildPureSubExpr(ctx, expr, tgt);
break;
}
}
void HqlCppTranslator::doBuildExprAdd(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (expr->queryType()->getTypeCode() != type_int)
{
doBuildExprArith(ctx, expr, tgt);
return;
}
CHqlBoundExpr boundL, boundR;
buildExpr(ctx, expr->queryChild(0), boundL);
bool WORK_AROUND_GCC_CONDITION_BUG = (options.targetCompiler == GccCppCompiler);
if (WORK_AROUND_GCC_CONDITION_BUG && expr->queryChild(1)->getOperator() == no_if)
buildTempExpr(ctx, expr->queryChild(1), boundR);
else
buildExpr(ctx, expr->queryChild(1), boundR);
tgt.expr.setown(adjustBoundIntegerValues(boundL.expr, boundR.expr, false));
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprNegate(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_decimal:
{
bindAndPush(ctx, expr->queryChild(0));
HqlExprArray args;
callProcedure(ctx, DecNegateAtom, args);
tgt.expr.setown(createValue(no_decimalstack, LINK(type)));
}
break;
case type_data:
case type_qstring:
case type_string:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_utf8:
throwError(HQLERR_MinusOnString);
default:
doBuildPureSubExpr(ctx, expr, tgt);
break;
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprRound(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * arg = expr->queryChild(0);
node_operator op = expr->getOperator();
switch (arg->queryType()->getTypeCode())
{
case type_decimal:
{
bindAndPush(ctx, arg);
HqlExprArray args;
if (op == no_round)
{
IHqlExpression * places = queryRealChild(expr, 1);
if (places)
{
args.append(*LINK(places));
callProcedure(ctx, DecRoundToAtom, args);
}
else
callProcedure(ctx, DecRoundAtom, args);
}
else
callProcedure(ctx, DecRoundUpAtom, args);
assertex(expr->queryType()->getTypeCode() == type_decimal);
tgt.expr.setown(createValue(no_decimalstack, expr->getType()));
}
break;
default:
{
if (op == no_round)
{
if (queryRealChild(expr, 1))
doBuildExprSysFunc(ctx, expr, tgt, roundToAtom);
else
doBuildExprSysFunc(ctx, expr, tgt, roundAtom);
}
else
doBuildExprSysFunc(ctx, expr, tgt, roundupAtom);
break;
}
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprAbs(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_decimal:
{
bindAndPush(ctx, expr->queryChild(0));
HqlExprArray args;
_ATOM func = DecAbsAtom;
callProcedure(ctx, func, args);
tgt.expr.setown(createValue(no_decimalstack, LINK(type)));
}
break;
case type_int:
case type_swapint:
case type_packedint:
case type_real:
{
CHqlBoundExpr temp;
buildTempExpr(ctx, expr->queryChild(0), temp);
ITypeInfo * type = expr->getType();
IHqlExpression * cond = createValue(no_ge, temp.expr.getLink(), createConstant(type->castFrom(true, 0)));
tgt.expr.setown(createValue(no_if, type, cond, temp.expr.getLink(), createValue(no_negate, LINK(type), temp.expr.getLink())));
}
break;
default:
buildExpr(ctx, expr->queryChild(0), tgt);
break;
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildAssignCatch(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
BuildCtx subctx(ctx);
subctx.addGroup();
BuildCtx tryctx(subctx);
tryctx.addQuotedCompound("try");
buildExprAssign(tryctx, target, expr->queryChild(0));
BuildCtx catchctx(subctx);
catchctx.addQuotedCompound("catch (IException * e)");
IHqlExpression * exceptVar = associateLocalFailure(catchctx, "e");
buildExprAssign(catchctx, target, expr->queryChild(1));
HqlExprArray args;
args.append(*LINK(exceptVar));
callProcedure(catchctx, freeExceptionAtom, args);
}
//---------------------------------------------------------------------------
//-- no_externalcall --
IHqlExpression * getCastParameter(IHqlExpression * curParam, ITypeInfo * argType)
{
type_t atc = argType->getTypeCode();
//Remove a few unnecessary casts which clutter up the code/or make it less efficient
if (isCast(curParam) && (curParam->queryType() == argType))
{
//If the following code is incorrect, the casts should get added back by the code that follows
IHqlExpression * uncast = curParam->queryChild(0);
//casts to larger size integers
if (atc == type_int)
curParam = uncast;
else if ((atc == type_unicode) && (uncast->queryType()->getTypeCode() == type_varunicode))
curParam = uncast;
else if ((atc == type_string) && (uncast->queryType()->getTypeCode() == type_varstring))
curParam = uncast;
}
ITypeInfo * paramType = curParam->queryType();
type_t ptc = paramType->getTypeCode();
if ((atc != ptc) && !(atc == type_row && ptc == type_table))
{
switch (atc)
{
case type_unicode:
if ((argType->getSize() == UNKNOWN_LENGTH) && (ptc == type_varunicode))
return LINK(curParam);
break;
case type_string:
if ((argType->getSize() == UNKNOWN_LENGTH) &&
((ptc == type_varstring) && (argType->queryCharset() == paramType->queryCharset())))
return LINK(curParam);
break;
case type_row:
case type_table:
case type_groupedtable:
{
IHqlExpression * record = queryRecord(argType);
if (record && (record->numChildren() != 0))
{
//really need to project instead.
//return ensureExprType(curParam, argType);
}
return LINK(curParam);
}
}
return ensureExprType(curParam, argType);
}
ITypeInfo * childType = argType->queryChildType();
if (paramType->queryTypeBase() != argType)
{
size32_t argSize = argType->getSize();
switch (atc)
{
case type_int:
case type_real:
if (argSize <= paramType->getSize())
return ensureExprType(curParam, argType);
break;
case type_decimal:
//Don't need explicit cast to cast between different sizes of these types...
//Will be done automatically by the compiler.
break;
case type_unicode:
//Don't need cast between different locales
if ((argSize != paramType->getSize()) && (argSize != UNKNOWN_LENGTH))
{
Owned modArgType = makeUnicodeType(argType->getStringLen(), curParam->queryType()->queryLocale());
return ensureExprType(curParam, modArgType);
}
break;
case type_varunicode:
//Don't need cast between different locales
if(argSize != paramType->getSize())
{
Owned modArgType = makeVarUnicodeType(argType->getStringLen(), curParam->queryType()->queryLocale());
return ensureExprType(curParam, modArgType);
}
break;
case type_utf8:
//Don't need cast between different locales
if(argSize != paramType->getSize())
{
Owned modArgType = makeUtf8Type(argType->getStringLen(), curParam->queryType()->queryLocale());
return ensureExprType(curParam, modArgType);
}
break;
case type_set:
if (childType)
return ensureExprType(curParam, argType);
break;
case type_table:
case type_groupedtable:
case type_row:
{
IHqlExpression * record = queryRecord(argType);
if (record && (record->numChildren() != 0))
{
//really need to project instead.
//return ensureExprType(curParam, argType);
}
break;
}
default:
return ensureExprType(curParam, argType);
}
}
// if (argType->queryCharset() != paramType->queryCharset())
// return ensureExprType(curParam, argType);
return LINK(curParam);
}
void HqlCppTranslator::normalizeBoundExpr(BuildCtx & ctx, CHqlBoundExpr & bound)
{
bound.expr.setown(convertWrapperToPointer(bound.expr));
}
IHqlExpression * HqlCppTranslator::doBuildInternalFunction(IHqlExpression * funcdef)
{
unsigned match = internalFunctions.find(*funcdef);
if (match != NotFound)
return LINK(&internalFunctionExternals.item(match));
StringBuffer funcname;
funcname.append("user").append(internalFunctions.ordinality()+1);
if (options.debugGeneratedCpp)
funcname.append("_").append(funcdef->queryName()).toLowerCase();
HqlExprArray attrs;
attrs.append(*createLocalAttribute());
attrs.append(*createExprAttribute(entrypointAtom, createConstant(funcname)));
HqlExprArray funcdefArgs;
ITypeInfo * returnType = funcdef->queryType()->queryChildType();
funcdefArgs.append(*createExternalReference(funcdef->queryName(), LINK(returnType), attrs));
funcdefArgs.append(*LINK(queryFunctionParameters(funcdef)));
unwindChildren(funcdefArgs, funcdef, 1);
OwnedHqlExpr externalFuncdef = funcdef->clone(funcdefArgs);
internalFunctions.append(*LINK(funcdef));
internalFunctionExternals.append(*LINK(externalFuncdef));
buildFunctionDefinition(externalFuncdef, funcdef);
return LINK(externalFuncdef);
}
void HqlCppTranslator::doBuildCall(BuildCtx & ctx, const CHqlBoundTarget * tgt, IHqlExpression * expr, CHqlBoundExpr * result)
{
if (result && expr->isPure() && ctx.getMatchExpr(expr, *result))
return;
LinkedHqlExpr funcdef;
if (expr->getOperator() == no_externalcall)
{
funcdef.set(expr->queryExternalDefinition());
assertex(funcdef);
useFunction(funcdef);
}
else
{
IHqlExpression * def = expr->queryBody()->queryFunctionDefinition();
assertex(def);
funcdef.setown(doBuildInternalFunction(def));
}
IHqlExpression * external = funcdef->queryChild(0);
IHqlExpression * formals = funcdef->queryChild(1);
if (external->hasProperty(ctxmethodAtom))
ensureContextAvailable(ctx);
if (external->hasProperty(gctxmethodAtom) || external->hasProperty(globalContextAtom))
{
if (!ctx.queryMatchExpr(globalContextMarkerExpr))
throwError1(HQLERR_FuncNotInGlobalContext, external->queryName()->str());
}
unsigned maxArg = formals->numChildren();
unsigned maxParam = expr->numChildren();
bool returnByReference = false;
bool returnMustAssign = false;
HqlExprArray args;
unsigned arg = 0;
unsigned param;
unsigned firstParam = 0;
bool isMethod = external->hasProperty(methodAtom) || external->hasProperty(omethodAtom) ;
bool newFormatSet = !external->hasProperty(oldSetFormatAtom);
bool translateSetReturn = false;
if (isMethod)
{
CHqlBoundExpr bound;
buildExpr(ctx, expr->queryChild(firstParam++), bound);
args.append(*bound.expr.getClear());
}
if (external->hasProperty(userMatchFunctionAtom))
{
//MORE: Test valid in this location...
args.append(*createVariable("walker", makeBoolType()));
}
bool doneAssign = false;
CHqlBoundExpr localBound;
CHqlBoundTarget localTarget;
Linked resultRow;
Linked resultRowBuilder;
Owned targetType = tgt ? LINK(tgt->queryType()) : makeVoidType();
ITypeInfo * retType = funcdef->queryType()->queryChildType();
BoundRow * resultSelfCursor = NULL;
switch (retType->getTypeCode())
{
case type_varstring:
case type_varunicode:
if (retType->getSize() == UNKNOWN_LENGTH)
{
if (hasConstModifier(retType))
break;
returnMustAssign = true;
if (tgt && !tgt->isFixedSize())
{
doneAssign = true;
localBound.expr.set(tgt->expr);
}
else
localBound.expr.setown(createWrapperTemp(ctx, retType, typemod_none));
break;
}
else
{
//fixed size strings => just pass pointer
IHqlExpression * resultVar = ctx.getTempDeclare(retType, NULL);
args.append(*getElementPointer(resultVar));
localBound.expr.setown(resultVar);
}
returnByReference = true;
break;
case type_string:
case type_data:
case type_qstring:
case type_unicode:
case type_utf8:
if (retType->getSize() == UNKNOWN_LENGTH)
{
OwnedHqlExpr lenVar;
OwnedHqlExpr strVar;
if (tgt && !tgt->isFixedSize())
{
doneAssign = true;
strVar.set(tgt->expr);
lenVar.set(tgt->length);
}
else
{
strVar.setown(createWrapperTemp(ctx, retType, typemod_none));
lenVar.setown(ctx.getTempDeclare(sizetType, NULL));
}
args.append(*LINK(lenVar));
args.append(*createValue(no_reference, strVar->getType(), LINK(strVar)));
localBound.length.set(lenVar);
localBound.expr.set(strVar);
}
else
{
//fixed size strings => just pass pointer
if (tgt && tgt->isFixedSize() && targetType->queryPromotedType() == retType)
{
doneAssign = true;
args.append(*getElementPointer(tgt->expr));
localBound.expr.set(tgt->expr);
}
else
{
IHqlExpression * resultVar = ctx.getTempDeclare(retType, NULL);
args.append(*getElementPointer(resultVar));
localBound.expr.setown(resultVar);
}
}
returnByReference = true;
break;
case type_set:
{
translateSetReturn = !newFormatSet;
OwnedHqlExpr lenVar;
OwnedHqlExpr strVar;
OwnedHqlExpr isAll;
if (tgt && !tgt->isFixedSize())
{
doneAssign = true;
strVar.set(tgt->expr);
if (translateSetReturn)
lenVar.setown(ctx.getTempDeclare(unsignedType, NULL));
else
lenVar.set(tgt->length);
assertex(tgt->isAll);
isAll.set(tgt->isAll);
}
else
{
Owned dataType = makeDataType(UNKNOWN_LENGTH);
strVar.setown(createWrapperTemp(ctx, dataType, typemod_none));
if (translateSetReturn)
{
lenVar.setown(ctx.getTempDeclare(unsignedType, NULL));
}
else
{
lenVar.setown(ctx.getTempDeclare(sizetType, NULL));
isAll.setown(ctx.getTempDeclare(boolType, NULL));
}
}
if (newFormatSet)
args.append(*LINK(isAll));
args.append(*LINK(lenVar));
args.append(*createValue(no_reference, strVar->getType(), LINK(strVar)));
if (newFormatSet)
{
localBound.length.set(lenVar);
}
else
{
localBound.count.set(lenVar);
if (isAll && !isAll->queryValue())
ctx.addAssign(isAll, queryBoolExpr(false));
}
localBound.isAll.set(isAll);
localBound.expr.setown(createValue(no_implicitcast, makeReferenceModifier(LINK(retType)), LINK(strVar)));
returnByReference = true;
break;
}
case type_table:
case type_groupedtable:
{
if (hasStreamedModifier(retType))
{
args.append(*createRowAllocator(ctx, ::queryRecord(retType)));
break;
}
const CHqlBoundTarget * curTarget;
if (tgt && !tgt->isFixedSize() &&
(hasLinkCountedModifier(targetType) == hasLinkCountedModifier(retType)))
{
doneAssign = true;
curTarget = tgt;
}
else
{
curTarget = &localTarget;
ExpressionFormat format = (hasLinkCountedModifier(retType) ? FormatLinkedDataset : FormatBlockedDataset);
createTempFor(ctx, retType, localTarget, typemod_none, format);
}
if (curTarget->count)
args.append(*LINK(curTarget->count));
if (curTarget->length)
args.append(*LINK(curTarget->length));
args.append(*createValue(no_reference, curTarget->expr->getType(), LINK(curTarget->expr)));
if (hasLinkCountedModifier(retType) && hasNonNullRecord(retType) && getBoolProperty(external, allocatorAtom, true))
args.append(*createRowAllocator(ctx, ::queryRecord(retType)));
localBound.setFromTarget(*curTarget);
// localBound.expr.setown(createValue(no_implicitcast, makeReferenceModifier(LINK(retType)), LINK(strVar)));
returnByReference = true;
break;
}
case type_row:
{
if (hasLinkCountedModifier(retType))
{
//Always assign link counted rows to a temporary (or the target) to ensure the are not leaked.
returnMustAssign = true;
if (tgt && hasLinkCountedModifier(targetType) && recordTypesMatch(targetType, retType))
{
doneAssign = true;
localBound.expr.set(tgt->expr);
}
else
localBound.expr.setown(createWrapperTemp(ctx, retType, typemod_none));
}
else
{
//row, just pass pointer
if (tgt && recordTypesMatch(targetType, retType) && !hasLinkCountedModifier(targetType))
{
doneAssign = true;
args.append(*getPointer(tgt->expr));
localBound.expr.set(tgt->expr);
}
else
{
resultRow.setown(declareTempRow(ctx, ctx, expr));
resultRowBuilder.setown(createRowBuilder(ctx, resultRow));
IHqlExpression * bound = resultRowBuilder->queryBound();
args.append(*getPointer(bound));
localBound.expr.set(bound);
}
returnByReference = true;
}
break;
}
case type_transform:
{
//Ugly, but target selector is passed in as the target
assertex(tgt && tgt->expr);
resultSelfCursor = resolveSelectorDataset(ctx, tgt->expr);
assertex(resultSelfCursor);
if (resultSelfCursor->queryBuilder())
args.append(*LINK(resultSelfCursor->queryBuilder()));
else
{
//Legacy support....
assertex(!options.supportDynamicRows);
OwnedHqlExpr rowExpr = getPointer(resultSelfCursor->queryBound());
StringBuffer builderName;
getUniqueId(builderName.append("b"));
StringBuffer s;
s.append("RtlStaticRowBuilder ").append(builderName).append("(");
generateExprCpp(s, rowExpr).append(",").append(getMaxRecordSize(funcdef->queryRecord())).append(");");
ctx.addQuoted(s);
args.append(*createVariable(builderName, makeBoolType()));
}
returnByReference = true;
doneAssign = true;
break;
}
case type_array:
UNIMPLEMENTED;
}
for (param = firstParam; param < maxParam; param++)
{
IHqlExpression * curParam = expr->queryChild(param);
if (curParam->isAttribute())
continue;
if (arg >= maxArg)
{
PrintLog("Too many parameters passed to function '%s'", expr->queryName()->str());
throwError1(HQLERR_TooManyParameters, expr->queryName()->str());
}
CHqlBoundExpr bound;
IHqlExpression * curArg = formals->queryChild(arg);
ITypeInfo * argType = curArg->queryType();
OwnedHqlExpr castParam = getCastParameter(curParam, argType);
type_t atc = argType->getTypeCode();
switch (atc)
{
case type_table:
case type_groupedtable:
{
ExpressionFormat format = queryNaturalFormat(argType);
buildDataset(ctx, castParam, bound, format);
break;
}
case type_row:
{
Owned selector = buildNewRow(ctx, castParam);
selector->buildAddress(ctx, bound);
// buildExpr(ctx, castParam, bound); // more this needs more work I think
break;
}
case type_set:
{
ITypeInfo * elemType = argType->queryChildType();
if (newFormatSet && elemType && (elemType->getTypeCode() != type_any) && (elemType != castParam->queryType()->queryChildType()))
buildExprEnsureType(ctx, castParam, bound, argType);
else
buildExpr(ctx, castParam, bound);
normalizeBoundExpr(ctx, bound);
break;
}
case type_decimal:
{
buildSimpleExpr(ctx, castParam, bound);
normalizeBoundExpr(ctx, bound);
break;
}
default:
{
buildExpr(ctx, castParam, bound);
normalizeBoundExpr(ctx, bound);
break;
}
}
bool done = false;
switch (atc)
{
case type_string:
case type_data:
case type_qstring:
case type_unicode:
case type_utf8:
if (argType->getSize() == UNKNOWN_LENGTH)
args.append(*getBoundLength(bound));
/*
Ensure parameter is passed as non-const if the argument does not have const.
if (!curArg->hasProperty(constAtom))//!argType->isConstantType())// && bound.queryType()->isConstantType())
bound.expr.setown(createValue(no_cast, LINK(argType), LINK(bound.expr)));
*/
break;
case type_varstring:
case type_varunicode:
//MORE: pass length only if an out parameter
break;
case type_set:
{
if (newFormatSet)
{
args.append(*bound.getIsAll());
args.append(*getBoundSize(bound));
}
else
{
if (castParam->getOperator() == no_all)
throwError(HQLERR_AllPassedExternal);
args.append(*getBoundCount(bound));
}
break;
}
case type_table:
case type_groupedtable:
{
if (isArrayRowset(argType))
args.append(*getBoundCount(bound));
else
args.append(*getBoundSize(bound));
bound.expr.setown(getPointer(bound.expr));
break;
}
case type_array:
UNIMPLEMENTED;
}
if (!done)
args.append(*bound.expr.getClear());
arg++;
}
if (arg < maxArg)
{
//MORE: Process default parameters...
PrintLog("Not enough parameters passed to function '%s'", expr->queryName()->str());
throwError1(HQLERR_TooFewParameters, expr->queryName()->str());
}
OwnedHqlExpr call = bindTranslatedFunctionCall(funcdef, args);
//either copy the integral value across, or a var string to fixed string
if (returnMustAssign)
{
ctx.addAssign(localBound.expr, call);
}
else if (resultSelfCursor)
{
OwnedHqlExpr sizeVar = ctx.getTempDeclare(unsignedType, call);
OwnedHqlExpr sizeOfExpr = createSizeof(resultSelfCursor->querySelector());
ctx.associateExpr(sizeOfExpr, sizeVar);
}
else if (returnByReference || (!tgt && !result))
{
ctx.addExpr(call);
if (translateSetReturn && tgt)
{
CHqlBoundTarget targetLength;
CHqlBoundExpr boundLength;
boundLength.expr.setown(getBoundLength(localBound));
targetLength.expr.set(tgt->length);
assign(ctx, targetLength, boundLength);
}
}
else
localBound.expr.set(call);
if (localBound.expr)
localBound.expr.setown(convertWrapperToPointer(localBound.expr));
if (tgt && !doneAssign)
assign(ctx, *tgt, localBound);
else if (result)
result->set(localBound);
//Old style row target where the row is passed in as a parameter
if (resultRow)
finalizeTempRow(ctx, resultRow, resultRowBuilder);
if (returnByReference)
ctx.associateExpr(expr, localBound);
}
void HqlCppTranslator::doBuildExprCall(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
doBuildCall(ctx, NULL, expr, &tgt);
}
void HqlCppTranslator::doBuildAssignCall(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
ITypeInfo * exprType = expr->queryType()->queryPromotedType();
if ((exprType->getSize() == UNKNOWN_LENGTH) && target.isFixedSize())
{
doBuildExprAssign(ctx, target, expr);
return;
}
ITypeInfo * targetType = target.queryType()->queryPromotedType();
if ((isStringType(exprType) || isUnicodeType(exprType) || isStringType(targetType) || isUnicodeType(targetType)) && exprType->getTypeCode() != targetType->getTypeCode())
{
doBuildExprAssign(ctx, target, expr);
return;
}
if ((exprType->getTypeCode() == type_set) && (queryUnqualifiedType(targetType) != queryUnqualifiedType(exprType)))
{
if (exprType->queryChildType()) // allow direct assignment of generic set functions. (e.g., internal)
{
doBuildExprAssign(ctx, target, expr);
return;
}
}
doBuildCall(ctx, &target, expr, NULL);
}
void HqlCppTranslator::doBuildStmtCall(BuildCtx & ctx, IHqlExpression * expr)
{
doBuildCall(ctx, NULL, expr, NULL);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildXmlEncode(BuildCtx & ctx, const CHqlBoundTarget * tgt, IHqlExpression * expr, CHqlBoundExpr * result)
{
node_operator op = expr->getOperator();
bool isUnicode = isUnicodeType(expr->queryType());
HqlExprArray args;
_ATOM func;
args.append(*LINK(expr->queryChild(0)));
if (op == no_xmldecode)
func = isUnicode ? xmlDecodeUStrAtom : xmlDecodeStrAtom;
else
{
func = isUnicode ? xmlEncodeUStrAtom : xmlEncodeStrAtom;
__int64 flags = 0;
if (expr->hasProperty(allAtom))
flags = ENCODE_WHITESPACE;
args.append(*createConstant(flags));
}
OwnedHqlExpr call = bindFunctionCall(func, args);
if (tgt)
buildExprAssign(ctx, *tgt, call);
else
buildExpr(ctx, call, *result);
}
//---------------------------------------------------------------------------
//-- no_cast --
//-- no_implicitcast --
IValue * getCastValue(ITypeInfo * cast, IHqlExpression * arg)
{
Owned value;
switch (arg->getOperator())
{
case no_constant:
value.set(arg->queryValue());
break;
case no_cast: case no_implicitcast:
value.setown(getCastValue(arg->queryType(), arg->queryChild(0)));
break;
default:
return NULL;
}
if (!value)
return NULL;
return value->castTo(cast);
}
inline IHqlExpression * getCastExpr(ITypeInfo * cast, IHqlExpression * arg)
{
IValue * value = getCastValue(cast, arg);
if (value)
return createConstant(value);
return NULL;
}
void HqlCppTranslator::doBuildAssignCast(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * left = expr->queryChild(0);
if (options.foldConstantCast)
{
//remove when we have constant folding installed...
OwnedHqlExpr cast = getCastExpr(expr->queryType(), left);
if (cast)
{
buildExprAssign(ctx, target, cast);
return;
}
}
#if 0
StringBuffer s;
s.append("assign cast from=");
left->queryType()->getECLType(s);
target.queryType()->getECLType(s.append(" target="));
expr->queryType()->getECLType(s.append(" expr="));
PrintLog(s.str());
#endif
ITypeInfo * targetType = target.queryType();
ITypeInfo * exprType = expr->queryType();
if ((targetType->queryPromotedType() == exprType->queryPromotedType()))
{
CHqlBoundExpr bound;
if (ctx.getMatchExpr(left, bound))
assignAndCast(ctx, target, bound);
else
{
OwnedHqlExpr values = normalizeListCasts(expr);
if (values != expr)
{
buildExprAssign(ctx, target, values);
return;
}
bool useTemp = requiresTemp(ctx, left, false) && (left->getOperator() != no_concat) && (left->getOperator() != no_createset);
if (useTemp && isStringType(targetType) && (left->getOperator() == no_substring) && target.isFixedSize())
{
//don't do this if the target type is unicode at the moment
Owned stretchedType = getStretchedType(targetType->getStringLen(), exprType);
if (isSameBasicType(stretchedType, targetType->queryPromotedType()))
useTemp = false;
}
if (useTemp)
{
buildExpr(ctx, left, bound);
assignAndCast(ctx, target, bound);
}
else
{
buildExprAssign(ctx, target, left);
}
}
return;
}
ITypeInfo * leftType = left->queryType();
if ((targetType->queryPromotedType() == left->queryType()->queryPromotedType()))
{
if (preservesValue(exprType, leftType))
{
buildExprAssign(ctx, target, left);
return;
}
}
CHqlBoundExpr pure;
bool assignDirect = false;
if ((exprType->getSize() == UNKNOWN_LENGTH) && (targetType->getTypeCode() == exprType->getTypeCode()) &&
(isStringType(exprType) || isUnicodeType(exprType)))
{
OwnedITypeInfo stretched = getStretchedType(UNKNOWN_LENGTH, targetType->queryPromotedType());
if (stretched == exprType->queryPromotedType())
assignDirect = true;
}
if (assignDirect)
buildExpr(ctx, left, pure);
else
buildExpr(ctx, expr, pure);
assignAndCast(ctx, target, pure);
}
void HqlCppTranslator::doBuildExprCast(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * arg = expr->queryChild(0);
ITypeInfo * exprType = expr->queryType();
if (exprType->getTypeCode() == type_set)
{
OwnedHqlExpr castArg = ensureExprType(arg, exprType);
if ((castArg->getOperator() != no_cast) && (castArg->getOperator() != no_implicitcast))
{
buildExpr(ctx, castArg, tgt);
return;
}
//The case is almost certainly going to go via a temporary anyway, better code is generated if we can avoid an extra assign
buildTempExpr(ctx, expr, tgt);
return;
}
if (options.foldConstantCast)
{
//remove when we have constant folding installed...
IHqlExpression * cast = getCastExpr(exprType, arg);
if (cast)
{
tgt.expr.setown(cast);
return;
}
}
// weird special case.... (int4)(swapint4)int4_x - remove both casts...
switch (arg->getOperator())
{
case no_cast:
case no_implicitcast:
{
IHqlExpression * child = arg->queryChild(0);
if (child->queryType() == exprType)
{
if ((exprType->getTypeCode() == type_int) || (exprType->getTypeCode() == type_swapint) || (exprType->getTypeCode() == type_packedint))
{
ITypeInfo * argType = arg->queryType();
if ((argType->getTypeCode() == type_int) || (argType->getTypeCode() == type_swapint) || (argType->getTypeCode() == type_packedint))
{
if (argType->getSize() == exprType->getSize())
{
buildExpr(ctx, child, tgt);
return;
}
}
}
}
break;
}
#if 0
case no_if:
{
//optimize cast of an if where one of the arguments is already the correct type
//this doesn't really improve things
IHqlExpression * lhs = arg->queryChild(1);
IHqlExpression * rhs = arg->queryChild(2);
if (lhs->queryType() == exprType || lhs->getOperator() == no_constant ||
rhs->queryType() == exprType || rhs->getOperator() == no_constant)
{
HqlExprArray args;
args.append(*LINK(arg->queryChild(0)));
args.append(*ensureExprType(lhs, exprType));
args.append(*ensureExprType(rhs, exprType));
unwindChildren(args, arg, 3);
OwnedHqlExpr next = createValue(no_if, LINK(exprType), args);
buildExpr(ctx, next, tgt);
return;
}
break;
}
#endif
case no_substring:
{
ITypeInfo * argType = arg->queryType();
if ((exprType->getSize() != UNKNOWN_LENGTH) && (argType->getSize() == UNKNOWN_LENGTH) && (exprType->getTypeCode() == argType->getTypeCode()))
{
OwnedITypeInfo stretched = getStretchedType(exprType->getStringLen(), argType);
if (stretched == exprType)
{
buildTempExpr(ctx, expr, tgt);
return;
}
}
break;
}
}
CHqlBoundExpr pure;
buildExpr(ctx, arg, pure);
doBuildExprCast(ctx, exprType, pure, tgt);
if ((arg->queryType()->getTypeCode() == type_decimal) && !isTypePassedByAddress(exprType))
{
OwnedHqlExpr translated = tgt.getTranslatedExpr();
buildTempExpr(ctx, translated, tgt);
}
}
void HqlCppTranslator::doBuildCastViaTemp(BuildCtx & ctx, ITypeInfo * to, CHqlBoundExpr & pure, CHqlBoundExpr & tgt)
{
CHqlBoundTarget boundTarget;
Linked targetType = to;
//If the temporary size can be deduced, then use a fixed length temporary to save a heap operation.
ITypeInfo * fromType = pure.expr->queryType();
if (isStringType(to) && to->getSize() == UNKNOWN_LENGTH && isStringType(fromType) && !pure.length)
{
assertex(fromType->getSize() != UNKNOWN_LENGTH);
targetType.setown(getStretchedType(fromType->getStringLen(), to));
}
OwnedHqlExpr translated = pure.getTranslatedExpr();
OwnedHqlExpr cast = ensureExprType(translated, targetType);
buildTempExpr(ctx, cast, tgt);
}
void HqlCppTranslator::doBuildCastViaString(BuildCtx & ctx, ITypeInfo * to, const CHqlBoundExpr & pure, CHqlBoundExpr & tgt)
{
ITypeInfo * from = pure.expr->queryType();
Owned tempType = makeStringType(from->getStringLen(), NULL, NULL);
OwnedHqlExpr temp = createValue(no_implicitcast, LINK(to),
createValue(no_implicitcast, tempType.getLink(), pure.getTranslatedExpr()));
buildExpr(ctx, temp, tgt);
}
void HqlCppTranslator::doBuildExprCast(BuildCtx & ctx, ITypeInfo * to, CHqlBoundExpr & pure, CHqlBoundExpr & tgt)
{
ITypeInfo * from = pure.expr->queryType();
HqlExprArray args;
_ATOM funcName = NULL;
OwnedHqlExpr op;
switch (to->getTypeCode())
{
case type_boolean:
{
switch (from->getTypeCode())
{
case type_string:
funcName = an2bAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_data:
funcName = data2BoolAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_qstring:
funcName = qstr2BoolAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_varstring:
funcName = vn2bAtom;
args.append(*getElementPointer(pure.expr));
break;
case type_decimal:
ensurePushed(ctx, pure);
funcName = DecCompareNullAtom;
break;
case type_unicode:
case type_varunicode:
case type_utf8:
doBuildCastViaString(ctx, to, pure, tgt);
return;
case type_real:
default:
//default action
break;
}
break;
}
case type_packedint:
{
ITypeInfo * logicalType = to->queryPromotedType();
size32_t toSize = logicalType->getSize();
if ((from->getTypeCode() != type_int) || (toSize != from->getSize()))
{
OwnedHqlExpr translated = pure.getTranslatedExpr();
OwnedHqlExpr castTranslated = createValue(no_implicitcast, LINK(logicalType), LINK(translated));
buildExpr(ctx, castTranslated, tgt);
return;
}
tgt.set(pure);
return;
}
case type_swapint:
{
if ((from->getTypeCode() == type_swapint) && (to->getSize() == from->getSize()))
{
break; // default behaviour
//MORE: Could special case cast between diff size swapints, but a bit too complicated.
}
if (from->getTypeCode() != type_int)
{
ITypeInfo * tempType = makeIntType(to->getSize(), to->isSigned());
IHqlExpression * translated = pure.getTranslatedExpr();
translated = createValue(no_implicitcast, tempType, translated);
OwnedHqlExpr castTranslated = createValue(no_implicitcast, LINK(to), translated);
buildExpr(ctx, castTranslated, tgt);
return;
}
unsigned toSize = to->getSize();
unsigned fromSize = from->getSize();
if ((toSize == 1) && (fromSize == 1))
{
if (to->isSigned() != from->isSigned())
break;
tgt.expr.setown(createValue(no_typetransfer, LINK(to), LINK(pure.expr)));
return;
}
if (toSize != fromSize)
{
Owned tempType = makeIntType(toSize, from->isSigned());
CHqlBoundExpr tempInt;
tempInt.expr.setown(ensureExprType(pure.expr, tempType));
doBuildCastViaTemp(ctx, to, tempInt, tgt);
}
else
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
}
case type_int:
{
switch (from->getTypeCode())
{
case type_qstring:
{
//Need to go via a temporary string.
Owned tempType = makeStringType(from->getStringLen(), NULL, NULL);
OwnedHqlExpr temp = createValue(no_implicitcast, LINK(to),
createValue(no_implicitcast, tempType.getLink(), pure.getTranslatedExpr()));
buildExpr(ctx, temp, tgt);
return;
}
case type_string:
case type_data:
{
_ATOM charset = from->queryCharset()->queryName();
if (charset == ebcdicAtom)
{
if (to->isSigned())
funcName = (to->getSize() > 4 ? en2ls8Atom : en2ls4Atom);
else
funcName = (to->getSize() > 4 ? en2l8Atom : en2l4Atom);
}
else if ((charset == asciiAtom) || (charset == dataAtom))
{
if (to->isSigned())
funcName = (to->getSize() > 4 ? an2ls8Atom : an2ls4Atom);
else
funcName = (to->getSize() > 4 ? an2l8Atom : an2l4Atom);
}
else
assertex(!"Unknown character set");
//MORE: This should really cast the result to the real width to remove extra bytes.
//e.g. (unsigned3)-1 should be 0xffffff, not 0xffffffff
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
}
case type_varstring:
if (to->isSigned())
funcName = (to->getSize() > 4 ? vn2ls8Atom : vn2ls4Atom);
else
funcName = (to->getSize() > 4 ? vn2l8Atom : vn2l4Atom);
args.append(*getElementPointer(pure.expr));
break;
case type_decimal:
ensurePushed(ctx, pure);
if (to->getSize() > 4)
funcName = DecPopInt64Atom;
else
funcName = DecPopLongAtom;
break;
case type_packedint:
{
if (to->getSize() < from->getSize())
{
funcName = castIntAtom[to->getSize()][to->isSigned()];
args.append(*LINK(pure.expr));
}
else
tgt.set(pure);
break;
}
case type_swapint:
{
unsigned toSize = to->getSize();
unsigned fromSize = from->getSize();
if ((toSize == 1) && (fromSize == 1))
{
if (to->isSigned() != from->isSigned())
break;
tgt.expr.setown(createValue(no_typetransfer, LINK(to), LINK(pure.expr)));
return;
}
if (toSize != fromSize)
{
Owned tempType = makeIntType(fromSize, from->isSigned());
CHqlBoundExpr tempInt;
doBuildCastViaTemp(ctx, tempType, pure, tempInt);
funcName = castIntAtom[to->getSize()][to->isSigned()];
if (funcName && toSize < fromSize)
{
args.append(*LINK(tempInt.expr));
tgt.expr.setown(bindTranslatedFunctionCall(funcName, args));
}
else
tgt.expr.setown(ensureExprType(tempInt.expr, to));
}
else
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
}
case type_int:
if (to->getSize() < from->getSize())
{
_ATOM name = castIntAtom[to->getSize()][to->isSigned()];
if (name)
{
args.append(*LINK(pure.expr));
IHqlExpression * call = bindTranslatedFunctionCall(name, args);
op.setown(createValue(no_typetransfer, LINK(to), call));
}
}
break;
case type_unicode:
case type_varunicode:
case type_utf8:
doBuildCastViaString(ctx, to, pure, tgt);
return;
case type_real:
default:
//default action
break;
}
break;
}
case type_real:
switch (from->getTypeCode())
{
case type_qstring:
case type_unicode:
case type_varunicode:
case type_utf8:
doBuildCastViaString(ctx, to, pure, tgt);
return;
case type_data:
case type_string:
funcName = from->queryCharset()->queryName() != ebcdicAtom ? an2fAtom : en2fAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_varstring:
funcName = from->queryCharset()->queryName() != ebcdicAtom ? vn2fAtom : ex2fAtom;
args.append(*getElementPointer(pure.expr));
break;
case type_decimal:
ensurePushed(ctx, pure);
funcName = DecPopRealAtom;
break;
case type_swapint:
{
//cast via intermediate int.
ITypeInfo * type = makeIntType(from->getSize(), from->isSigned());
IHqlExpression * translated = pure.getTranslatedExpr();
OwnedHqlExpr castTranslated = createValue(no_implicitcast, type, translated);
pure.clear();
buildExpr(ctx, castTranslated, pure);
from = type;
}
//fallthrough
case type_int:
case type_boolean:
case type_packedint:
default:
//default action
break;
}
break;
case type_decimal:
{
ensurePushed(ctx, pure);
bool needToSetPrecision = true;
switch (from->getTypeCode())
{
case type_int:
case type_swapint:
if (to->getDigits() >= from->getDigits())
needToSetPrecision = false;
break;
case type_decimal:
if (((to->getDigits() - to->getPrecision()) >= (from->getDigits() - from->getPrecision())) &&
(to->getPrecision() >= from->getPrecision()))
needToSetPrecision = false;
break;
}
if (needToSetPrecision)
{
args.append(*createConstant(createIntValue(to->getDigits(), 1, false)));
args.append(*createConstant(createIntValue(to->getPrecision(), 1, false)));
callProcedure(ctx, DecSetPrecisionAtom, args);
}
op.setown(createValue(no_decimalstack, LINK(to)));
}
break;
case type_set:
{
//MORE: Shouldn't have to create this node...
OwnedHqlExpr cast = createValue(no_implicitcast, LINK(to), pure.getTranslatedExpr());
buildTempExpr(ctx, cast, tgt);
return;
}
case type_pointer:
case type_row:
break;
case type_varstring:
if ((to->getSize() == UNKNOWN_LENGTH) && (from->getTypeCode() == type_varstring))
tgt.set(pure);
else
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
case type_string:
case type_data:
{
if (canRemoveStringCast(to, from))
{
ICharsetInfo * srcset = from->queryCharset();
ICharsetInfo * tgtset = to->queryCharset();
//Data never calls a conversion function... but does add a cast
if ((srcset == tgtset) || (to->getTypeCode() == type_data) || (from->getTypeCode() == type_data))
{
if (from->getTypeCode() == type_varstring)
tgt.length.setown(getBoundLength(pure));
else
tgt.length.set(pure.length);
Owned newType;
if (to->getSize() == UNKNOWN_LENGTH)
newType.setown(getStretchedType(from->getSize(), to));
else
newType.set(to);
if (from->getTypeCode() != type_data)
{
newType.setown(cloneModifiers(from, newType));
tgt.expr.setown(createValue(no_typetransfer, newType.getClear(), pure.expr.getLink()));
}
else
{
IHqlExpression * base = queryStripCasts(pure.expr);
newType.setown(makeReferenceModifier(newType.getClear()));
tgt.expr.setown(createValue(no_cast, newType.getClear(), LINK(base)));
}
return;
}
}
}
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
case type_unicode:
case type_varunicode:
if ((from->getTypeCode() == to->getTypeCode()) && (to->getSize() == UNKNOWN_LENGTH))
{
tgt.set(pure);
return;
}
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
default:
doBuildCastViaTemp(ctx, to, pure, tgt);
return;
}
if (funcName)
op.setown(bindTranslatedFunctionCall(funcName, args));
if (!op)
op.setown(ensureExprType(pure.expr, to));
if (queryUnqualifiedType(op->queryType()) != queryUnqualifiedType(to))
{
OwnedHqlExpr translated = createTranslated(op);
OwnedHqlExpr cast = ensureExprType(translated, to);
buildExpr(ctx, cast, tgt);
}
else
tgt.expr.setown(op.getClear());
}
//---------------------------------------------------------------------------
//-- no_char_length --
//NB: parameter is expression to take length of - not the length node.
IHqlExpression * HqlCppTranslator::doBuildCharLength(BuildCtx & ctx, IHqlExpression * expr)
{
CHqlBoundExpr bound;
buildCachedExpr(ctx, expr, bound);
return getBoundLength(bound);
}
//---------------------------------------------------------------------------
//-- no_choose
void HqlCppTranslator::doBuildChoose(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * expr)
{
CHqlBoundExpr test;
BuildCtx subctx(ctx);
buildExpr(subctx, expr->queryChild(0), test);
IHqlStmt * stmt = subctx.addSwitch(test.expr);
unsigned max = expr->numChildren()-1;
unsigned idx;
for (idx = 1; idx < max; idx++)
{
OwnedHqlExpr branch = getSizetConstant(idx);
subctx.addCase(stmt, branch);
buildExprOrAssign(subctx, target, expr->queryChild(idx), NULL);
}
subctx.addDefault(stmt);
buildExprOrAssign(subctx, target, expr->queryChild(max), NULL);
}
void HqlCppTranslator::doBuildAssignChoose(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
unsigned max = expr->numChildren()-1;
unsigned idx;
bool allConstant = true;
for (idx = 1; idx < max; idx++)
{
if (!expr->queryChild(idx)->queryValue())
allConstant = false;
}
if (allConstant)
{
//MORE: Need to calculate the correct type.
HqlExprArray args;
args.append(*LINK(expr->queryChild(0)));
for (idx = 1; idx < max; idx++)
{
IHqlExpression * v1 = createConstant(createIntValue(idx, LINK(unsignedType)));
IHqlExpression * v2 = expr->queryChild(idx);
ITypeInfo * type = v2->queryType();
args.append(* createValue(no_mapto, LINK(type), v1, LINK(v2)));
}
args.append(*LINK(expr->queryChild(max)));
OwnedHqlExpr caseExpr = createValue(no_case, expr->getType(), args);
buildExprAssign(ctx, target, caseExpr);
}
else
{
doBuildChoose(ctx, &target, expr);
}
}
//---------------------------------------------------------------------------
//-- compare (no_eq,no_ne,no_lt,no_gt,no_le,no_ge) --
//Are the arguments scalar? If so return the scalar arguments.
static bool getIsScalarCompare(IHqlExpression * expr, OwnedHqlExpr & left, OwnedHqlExpr & right)
{
left.set(expr->queryChild(0));
right.set(expr->queryChild(1));
ITypeInfo * lType = left->queryType();
ITypeInfo * rType = right->queryType();
type_t leftTypeCode = lType ? lType->getTypeCode() : type_row;
type_t rightTypeCode = rType ? rType->getTypeCode() : type_row;
assertex(leftTypeCode == rightTypeCode);
switch (leftTypeCode)
{
case type_row:
case type_set:
return false;
default:
return true;
}
}
void HqlCppTranslator::doBuildAssignCompare(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr left;
OwnedHqlExpr right;
if (getIsScalarCompare(expr, left, right))
{
doBuildExprAssign(ctx, target, expr);
return;
}
//Comparing a row - calculate the ordering and then compare against zero...
IHqlExpression * compare = createValue(no_order, LINK(signedType), LINK(left), LINK(right));
OwnedHqlExpr temp = createBoolExpr(expr->getOperator(), compare, getZero());
buildExprAssign(ctx, target, temp);
}
//---------------------------------------------------------------------------
//-- no_concat --
void HqlCppTranslator::buildConcatFArgs(HqlExprArray & args, BuildCtx & ctx, const HqlExprArray & values, ITypeInfo * argType)
{
ForEachItemIn(idx, values)
{
IHqlExpression * cur = &values.item(idx);
OwnedHqlExpr value = getCastParameter(cur, argType);
CHqlBoundExpr bound;
buildCachedExpr(ctx, value, bound);
//The function takes ... so the length must be 4 bytes or the stack
//gets out of sync...
OwnedHqlExpr length = getBoundLength(bound);
args.append(*ensureExprType(length, unsignedType));
args.append(*getElementPointer(bound.expr));
}
args.append(*createConstant(signedType->castFrom(true, -1))); // correct size terminator
}
void HqlCppTranslator::doBuildVarLengthConcatF(BuildCtx & ctx, const CHqlBoundTarget & target, const HqlExprArray & values)
{
ITypeInfo * targetType = target.queryType();
Linked argType = targetType;
type_t ttc = targetType->getTypeCode();
HqlExprArray args;
_ATOM func;
if (ttc == type_varstring)
{
func = concatVStrAtom;
argType.setown(makeStringType(UNKNOWN_LENGTH, LINK(targetType->queryCharset()), LINK(targetType->queryCollation())));
}
else if (ttc == type_unicode)
{
args.append(*target.length.getLink());
func = concatUnicodeAtom;
}
else if (ttc == type_utf8)
{
args.append(*target.length.getLink());
func = concatUtf8Atom;
}
else if (ttc == type_varunicode)
{
func = concatVUnicodeAtom;
argType.setown(makeUnicodeType(UNKNOWN_LENGTH, targetType->queryLocale()));
}
else
{
args.append(*target.length.getLink());
func = concatAtom;
}
IHqlExpression * tgt = createValue(no_address, makeVoidType(), LINK(target.expr));
if (ttc == type_data)
tgt = createValue(no_implicitcast, makePointerType(makeStringType(UNKNOWN_LENGTH, NULL, NULL)), tgt);
args.append(*tgt);
buildConcatFArgs(args, ctx, values, argType);
callProcedure(ctx, func, args);
}
bool HqlCppTranslator::doBuildFixedLengthConcatF(BuildCtx & ctx, const CHqlBoundTarget & target, const HqlExprArray & values)
{
ITypeInfo * targetType = target.queryType();
Owned argType = getStretchedType(UNKNOWN_LENGTH, targetType);
type_t ttc = targetType->getTypeCode();
HqlExprArray args;
_ATOM func = NULL;
OwnedHqlExpr fill;
switch (ttc)
{
case type_varstring:
func = concatVStrFAtom;
argType.setown(makeStringType(UNKNOWN_LENGTH, LINK(targetType->queryCharset()), LINK(targetType->queryCollation())));
break;
case type_varunicode:
func = concatVUnicodeFAtom;
argType.setown(makeUnicodeType(UNKNOWN_LENGTH, targetType->queryLocale()));
break;
case type_unicode:
func = concatUnicodeFAtom;
break;
case type_string:
func = concatStrFAtom;
if (targetType->queryCharset()->queryName() == ebcdicAtom)
fill.setown(getSizetConstant('@'));
else
fill.setown(getSizetConstant(' '));
break;
case type_data:
func = concatStrFAtom;
fill.setown(getSizetConstant(0));
break;
}
if (func)
{
args.append(*getSizetConstant(targetType->getStringLen()));
args.append(*getPointer(target.expr));
if (fill)
args.append(*LINK(fill));
buildConcatFArgs(args, ctx, values, argType);
callProcedure(ctx, func, args);
return true;
}
return false;
}
void HqlCppTranslator::doBuildAssignConcat(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
ITypeInfo * targetType = target.queryType();
ICharsetInfo * targetCharset = targetType->queryCharset();
type_t ttc = targetType->getTypeCode();
switch (ttc)
{
case type_unicode:
case type_varunicode:
case type_utf8:
break;
case type_string:
case type_varstring:
case type_data:
if (!queryDefaultTranslation(targetCharset, expr->queryType()->queryCharset()))
break;
//fallthrough
default:
doBuildExprAssign(ctx, target, expr);
return;
}
HqlExprArray values;
expr->unwindList(values, no_concat);
//Combine adjacent constants (not folded previously because of the tree structure)
//Not optimized in the folder since it can mess up cse evaluation
for (unsigned i=0; i < values.ordinality()-1; i++)
{
IValue * firstValue = values.item(i).queryValue();
if (firstValue)
{
Linked combinedValue = firstValue;
while (i+1 < values.ordinality())
{
IValue * nextValue = values.item(i+1).queryValue();
if (!nextValue)
break;
combinedValue.setown(concatValues(combinedValue, nextValue));
values.remove(i+1);
}
if (combinedValue->queryType()->getStringLen() == 0)
{
values.remove(i);
i--; // not nice, but will be incremented before comparison so safe
}
else if (combinedValue != firstValue)
values.replace(*createConstant(combinedValue.getClear()), i);
}
}
if (!target.isFixedSize())
{
doBuildVarLengthConcatF(ctx, target, values);
}
else if (!doBuildFixedLengthConcatF(ctx, target, values))
{
Owned varType = getStretchedType(UNKNOWN_LENGTH, targetType);
OwnedHqlExpr castValue = createValue(no_concat, LINK(varType), values);
doBuildExprAssign(ctx, target, castValue);
}
}
//---------------------------------------------------------------------------
//-- no_div --
// also used for no_modulus
void HqlCppTranslator::doBuildExprDivide(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (expr->queryType()->getTypeCode() == type_decimal)
{
doBuildExprArith(ctx, expr, tgt);
return;
}
IHqlExpression * left = expr->queryChild(0);
IHqlExpression * right = expr->queryChild(1);
assertex(left->queryType() == right->queryType());
IValue * value = right->queryValue();
if (value)
{
Owned zero = right->queryType()->castFrom(false, 0);
int cmp = value->compare(zero);
if (cmp == 0)
tgt.expr.setown(createConstant(zero.getClear()));
else
doBuildPureSubExpr(ctx, expr, tgt);
}
else
{
buildTempExpr(ctx, expr, tgt);
}
}
void HqlCppTranslator::doBuildAssignDivide(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
if (expr->queryType()->getTypeCode() == type_decimal)
{
doBuildExprAssign(ctx, target, expr);
return;
}
IHqlExpression * left = expr->queryChild(0);
IHqlExpression * right = expr->queryChild(1);
assertex(left->queryType() == right->queryType());
CHqlBoundExpr lhs,rhs;
buildExpr(ctx, left, lhs);
buildSimpleExpr(ctx, right, rhs);
IHqlExpression * divisor = rhs.expr.get();
OwnedHqlExpr pureExpr = createValue(expr->getOperator(), left->getType(), lhs.expr.getClear(), LINK(divisor));
IValue * zero = pureExpr->queryType()->castFrom(false, 0);
OwnedHqlExpr eZero = createConstant(zero);
IValue * value = rhs.expr->queryValue();
if (value)
{
int cmp = value->compare(eZero->queryValue());
if (cmp == 0)
assignBound(ctx, target, eZero);
else
assignBound(ctx, target, pureExpr);
}
else
{
BuildCtx subctx(ctx);
IHqlStmt * stmt = subctx.addFilter(divisor);
assignBound(subctx, target, pureExpr);
subctx.selectElse(stmt);
assignBound(subctx, target, eZero);
}
}
//---------------------------------------------------------------------------
//-- no_if --
bool HqlCppTranslator::ifRequiresAssignment(BuildCtx & ctx, IHqlExpression * expr)
{
IHqlExpression * trueExpr = expr->queryChild(1);
IHqlExpression * falseExpr = expr->queryChild(2);
if (requiresTemp(ctx, trueExpr, true) || requiresTemp(ctx, falseExpr, true) || expr->queryType()->getSize() == UNKNOWN_LENGTH)
return true;
if (trueExpr->queryType() != falseExpr->queryType() && isStringType(expr->queryType()))
return true;
if (expr->queryType()->getTypeCode() == type_decimal)
return true;
return false;
}
void HqlCppTranslator::doBuildAssignIf(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
if (!ifRequiresAssignment(ctx, expr))
{
doBuildExprAssign(ctx, target, expr);
return;
}
IHqlExpression * trueExpr = expr->queryChild(1);
IHqlExpression * falseExpr = expr->queryChild(2);
BuildCtx subctx(ctx);
CHqlBoundExpr cond;
buildCachedExpr(subctx, expr->queryChild(0), cond);
IHqlStmt * test = subctx.addFilter(cond.expr);
buildExprAssign(subctx, target, trueExpr);
subctx.selectElse(test);
buildExprAssign(subctx, target, falseExpr);
}
void HqlCppTranslator::doBuildExprIf(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (ifRequiresAssignment(ctx, expr))
{
buildTempExpr(ctx, expr, tgt);
return;
}
IHqlExpression * trueExpr = expr->queryChild(1);
IHqlExpression * falseExpr = expr->queryChild(2);
//Length should not be conditional...
CHqlBoundExpr cond;
CHqlBoundExpr boundTrue;
CHqlBoundExpr boundFalse;
buildCachedExpr(ctx, expr->queryChild(0), cond);
buildCachedExpr(ctx, trueExpr, boundTrue);
buildCachedExpr(ctx, falseExpr, boundFalse);
//true and false should have same type...
tgt.expr.setown(createValue(no_if, expr->getType(), cond.expr.getClear(), boundTrue.expr.getClear(), boundFalse.expr.getClear()));
}
void HqlCppTranslator::doBuildStmtIf(BuildCtx & ctx, IHqlExpression * expr)
{
BuildCtx subctx(ctx);
CHqlBoundExpr cond;
buildCachedExpr(subctx, expr->queryChild(0), cond);
IHqlStmt * test = subctx.addFilter(cond.expr);
buildStmt(subctx, expr->queryChild(1));
IHqlExpression * elseExpr = queryRealChild(expr, 2);
if (elseExpr && elseExpr->getOperator() != no_null)
{
subctx.selectElse(test);
buildStmt(subctx, elseExpr);
}
}
//---------------------------------------------------------------------------
//-- no_intformat --
IHqlExpression * HqlCppTranslator::createFormatCall(_ATOM func, IHqlExpression * expr)
{
HqlExprArray args;
unsigned max = expr->numChildren();
unsigned idx;
for (idx=0; idx < max; idx++)
{
IHqlExpression * cur = expr->queryChild(idx);;
args.append(*LINK(cur));
}
return bindFunctionCall(func, args);
}
void HqlCppTranslator::doBuildExprFormat(_ATOM func, BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
OwnedHqlExpr call = createFormatCall(func, expr);
buildExpr(ctx, call, tgt);
}
void HqlCppTranslator::doBuildAssignFormat(_ATOM func, BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr call = createFormatCall(func, expr);
buildExprAssign(ctx, target, call);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildAssignToXml(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * row = expr->queryChild(0);
HqlExprArray args;
args.append(*buildMetaParameter(row));
args.append(*LINK(row));
args.append(*getSizetConstant(XWFtrim|XWFopt|XWFnoindent));
OwnedHqlExpr call = bindFunctionCall(ctxGetRowXmlAtom, args);
buildExprAssign(ctx, target, call);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprCppBody(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt)
{
if (!allowEmbeddedCpp())
throwError(HQLERR_EmbeddedCppNotAllowed);
StringBuffer text;
expr->queryChild(0)->queryValue()->getStringValue(text);
OwnedHqlExpr quoted = createQuoted(text.str(), expr->getType());
if (tgt)
{
ITypeInfo * type = expr->queryType();
assertex(type->getTypeCode() == type_varstring || type->getSize() != UNKNOWN_LENGTH);
tgt->expr.set(quoted);
}
else
ctx.addExpr(quoted);
}
//---------------------------------------------------------------------------
//-- no_index --
IHqlExpression * getSimpleListIndex(BuildCtx & ctx, IHqlExpression * expr)
{
IHqlExpression * index = expr->queryChild(1);
if (!index->isConstant())
return NULL;
OwnedHqlExpr set = normalizeListCasts(expr->queryChild(0));
switch (set->getOperator())
{
case no_null:
case no_list:
break;
default:
return NULL;
}
OwnedHqlExpr folded = foldHqlExpression(index);
__int64 which = folded->queryValue()->getIntValue();
if ((which > 0) && (which <= set->numChildren()))
return LINK(set->queryChild((unsigned)which-1));
return createNullExpr(expr);
}
void HqlCppTranslator::doBuildExprIndex(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
OwnedHqlExpr simple = getSimpleListIndex(ctx, expr);
if (simple)
buildExpr(ctx, simple, tgt);
else
{
OwnedHqlExpr simpleList = simplifyFixedLengthList(expr->queryChild(0));
Owned cursor = createSetSelector(ctx, simpleList);
cursor->buildExprSelect(ctx, expr, tgt);
}
}
void HqlCppTranslator::doBuildAssignIndex(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr simple = getSimpleListIndex(ctx, expr);
if (simple)
buildExprAssign(ctx, target, simple);
else
{
OwnedHqlExpr simpleList = simplifyFixedLengthList(expr->queryChild(0));
Owned cursor = createSetSelector(ctx, simpleList);
cursor->buildAssignSelect(ctx, target, expr);
}
}
//---------------------------------------------------------------------------
//-- no_list --
bool isComplexSet(ITypeInfo * type)
{
ITypeInfo * childType = type->queryChildType();
if (!childType)
return false;
switch (childType->getTypeCode())
{
case type_alien:
return true;
case type_string:
case type_qstring:
case type_data:
case type_unicode:
case type_varstring:
case type_varunicode:
return (childType->getSize() == UNKNOWN_LENGTH);
case type_utf8:
case type_swapint:
case type_packedint:
return true;
case type_int:
switch (childType->getSize())
{
case 3: case 5: case 6: case 7:
return true;
}
return false;
}
return false;
}
bool isComplexSet(IHqlExpression * expr)
{
return isComplexSet(expr->queryType());
}
bool isConstantSet(IHqlExpression * expr)
{
unsigned max = expr->numChildren();
unsigned idx;
for (idx = 0; idx < max; idx++)
{
IHqlExpression * child = expr->queryChild(idx);
if (!child->queryValue())
return false;
}
return true;
}
void HqlCppTranslator::doBuildExprConstList(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
BuildCtx declareCtx(*code, literalAtom);
if (!declareCtx.getMatchExpr(expr, tgt))
{
ITypeInfo * type = expr->queryType();
Linked elementType = type->queryChildType();
if (!elementType)
throwError(HQLERR_NullSetCannotGenerate);
Owned transferType;
LinkedHqlExpr values = expr;
if ((isTypePassedByAddress(elementType) && (elementType->getTypeCode() != type_varstring)))
{
if (elementType->isReference())
{
// use a var string type to get better C++ generated...
transferType.set(elementType);
elementType.setown(makeVarStringType(UNKNOWN_LENGTH));
}
else
{
// for string, data and qstring we need to initialize the array with a list of characters instead of
// a cstring e.g., char[][2] = { { 'a','b' }, { 'c', 'd' } };
HqlExprArray newValues;
ForEachChild(idx, expr)
{
IHqlExpression * next = expr->queryChild(idx);
newValues.append(*createValue(no_create_initializer, next->getType(), LINK(next)));
}
values.setown(createValue(no_list, makeSetType(LINK(elementType)), newValues));
}
}
unsigned numElements = expr->numChildren();
Owned t = makeConstantModifier(makeArrayType(LINK(elementType), numElements));
IHqlExpression * table = declareCtx.getTempDeclare(t, values);
if (transferType)
{
ITypeInfo * arrayType = makeArrayType(LINK(transferType), numElements);
table = createValue(no_typetransfer, arrayType, table);
}
tgt.count.setown(getSizetConstant(numElements));
tgt.expr.setown(table);
//make sure tables get added before any global functions
declareCtx.associateExpr(expr, tgt);
if (options.spanMultipleCpp)
{
BuildCtx protoctx(*code, mainprototypesAtom);
protoctx.addDeclareExternal(table);
}
}
}
void HqlCppTranslator::doBuildExprDynList(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (!ctx.getMatchExpr(expr, tgt))
{
ITypeInfo * type = expr->queryType();
ITypeInfo * elementType = type->queryChildType();
unsigned max = expr->numChildren();
//MORE: What if this is an array of variable length strings?
Owned t = makeArrayType(LINK(elementType), max);
IHqlExpression * table = ctx.getTempDeclare(t, NULL);
// new code - should really use a selector...
unsigned idx;
CHqlBoundTarget boundTarget;
for (idx = 0; idx < max; idx++)
{
IHqlExpression * child = expr->queryChild(idx);
boundTarget.expr.setown(createValue(no_index, LINK(elementType), LINK(table), createConstant((int)idx)));
buildExprAssign(ctx, boundTarget, child);
}
tgt.count.setown(getSizetConstant(max));
tgt.expr.setown(table);
ctx.associateExpr(expr, tgt);
}
}
void HqlCppTranslator::doBuildExprList(BuildCtx & ctx, IHqlExpression * _expr, CHqlBoundExpr & tgt)
{
OwnedHqlExpr expr = simplifyFixedLengthList(_expr);
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_set:
case type_array:
{
LinkedHqlExpr values = expr;
ITypeInfo * childType = type->queryChildType();
//MORE: Also alien data types and other weird things...
//if (childType->getSize() == UNKNOWN_LENGTH)
if (expr->numChildren() == 0)
{
tgt.length.setown(getSizetConstant(0));
tgt.expr.setown(createValue(no_nullptr, makeReferenceModifier(LINK(type))));
return;
}
else if (isComplexSet(expr))
{
buildTempExpr(ctx, expr, tgt);
return;
}
else if (childType->getSize() == 0)
{
//codes := [''] convert it to a set of a single char string for the moment.
Owned newType = makeSetType(getStretchedType(1, childType));
values.setown(ensureExprType(expr, newType));
}
if (isConstantSet(expr))
doBuildExprConstList(ctx, values, tgt);
else
doBuildExprDynList(ctx, values, tgt);
tgt.isAll.set(queryBoolExpr(false));
}
break;
default:
throw(!"This type of list not supported yet");
}
}
void HqlCppTranslator::doBuildAssignList(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * _expr)
{
OwnedHqlExpr expr = simplifyFixedLengthList(_expr);
node_operator op = expr->getOperator();
assertex(op == no_list);
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_set:
case type_array:
break;
default:
throw(!"This type of list not supported yet");
}
//This is an assignment, a non-constant set would end up creating two temporaries.
unsigned numItems = expr->numChildren();
if (((numItems > 0) && (numItems < 3)) || isComplexSet(expr) || !isConstantSet(expr))
{
Owned builder = createTempSetBuilder(target.queryType()->queryChildType(), target.isAll);
builder->buildDeclare(ctx);
buildSetAssign(ctx, builder, expr);
builder->buildFinish(ctx, target);
}
else if (numItems == 0)
{
CHqlBoundExpr temp;
buildExpr(ctx, expr, temp);
if (target.isAll)
{
if (temp.isAll)
ctx.addAssign(target.isAll, temp.isAll);
else
ctx.addAssign(target.isAll, queryBoolExpr(false));
}
ctx.addAssign(target.length, temp.length);
ctx.addAssign(target.expr, temp.expr);
}
else
{
OwnedHqlExpr cast = ensureExprType(expr, target.queryType());
// can do a direct assignment without any casts
doBuildExprAssign(ctx, target, cast);
}
}
void HqlCppTranslator::doBuildExprAll(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
tgt.isAll.set(queryBoolExpr(true));
tgt.length.setown(getSizetConstant(0));
tgt.expr.setown(createQuoted("0", makeSetType(NULL)));
}
void HqlCppTranslator::doBuildAssignAll(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
CHqlBoundExpr temp;
buildExpr(ctx, expr, temp);
ctx.addAssign(target.isAll, temp.isAll);
ctx.addAssign(target.length, temp.length);
ctx.addAssign(target.expr, temp.expr);
}
//---------------------------------------------------------------------------
//-- no_not --
void HqlCppTranslator::doBuildExprNot(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
ITypeInfo * type = expr->queryChild(0)->queryType();
switch (type->getTypeCode())
{
case type_decimal:
{
//MORE: Could leak decimal stack.... Is the last line correct?
HqlExprArray args;
bindAndPush(ctx, expr->queryChild(0));
IHqlExpression * op = bindTranslatedFunctionCall(DecCompareNullAtom, args);
tgt.expr.setown(createValue(expr->getOperator(), LINK(boolType), op, getZero()));
}
break;
default:
doBuildPureSubExpr(ctx, expr, tgt);
break;
}
}
//---------------------------------------------------------------------------
//-- no_or --
IHqlExpression * HqlCppTranslator::convertOrToAnd(IHqlExpression * expr)
{
bool invert = true;
if (expr->getOperator() == no_not)
{
invert = false;
expr = expr->queryChild(0);
}
assertex(expr->getOperator() == no_or);
HqlExprArray original, inverted;
expr->unwindList(original, no_or);
ForEachItemIn(idx, original)
inverted.append(*getInverse(&original.item(idx)));
IHqlExpression * ret = createValue(no_and, makeBoolType(), inverted);
if (invert)
ret = createValue(no_not, makeBoolType(), ret);
return ret;
}
//---------------------------------------------------------------------------
// no_unicodeorder
void HqlCppTranslator::doBuildAssignUnicodeOrder(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
CHqlBoundExpr lhs, rhs, locale, strength;
buildCachedExpr(ctx, expr->queryChild(0), lhs);
buildCachedExpr(ctx, expr->queryChild(1), rhs);
buildCachedExpr(ctx, expr->queryChild(2), locale);
buildCachedExpr(ctx, expr->queryChild(3), strength);
Owned op;
HqlExprArray args;
ITypeInfo * realType = lhs.expr->queryType()->queryPromotedType();
switch(realType->getTypeCode())
{
case type_unicode:
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*getElementPointer(locale.expr));
args.append(*strength.expr.getLink());
op.setown(bindTranslatedFunctionCall(compareUnicodeUnicodeStrengthAtom, args));
break;
case type_varunicode:
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
args.append(*getElementPointer(locale.expr));
args.append(*strength.expr.getLink());
op.setown(bindTranslatedFunctionCall(compareVUnicodeVUnicodeStrengthAtom, args));
break;
case type_utf8:
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*getElementPointer(locale.expr));
args.append(*strength.expr.getLink());
op.setown(bindTranslatedFunctionCall(compareUtf8Utf8StrengthAtom, args));
break;
default:
UNIMPLEMENTED;
}
assignBound(ctx, target, op);
}
//---------------------------------------------------------------------------
//-- no_order --
static void buildIteratorFirst(HqlCppTranslator & translator, BuildCtx & ctx, IHqlExpression * iter, IHqlExpression * row)
{
StringBuffer s;
translator.generateExprCpp(s, row).append(" = (byte*)");
translator.generateExprCpp(s, iter).append(".first();");
ctx.addQuoted(s);
}
static void buildIteratorNext(HqlCppTranslator & translator, BuildCtx & ctx, IHqlExpression * iter, IHqlExpression * row)
{
StringBuffer s;
translator.generateExprCpp(s, row).append(" = (byte*)");
translator.generateExprCpp(s, iter).append(".next();");
ctx.addQuoted(s);
}
static void buildIteratorIsValid(BuildCtx & ctx, IHqlExpression * iter, IHqlExpression * row, CHqlBoundExpr & bound)
{
bound.expr.set(row);
}
void HqlCppTranslator::doBuildAssignCompareRow(BuildCtx & ctx, EvaluateCompareInfo & info, IHqlExpression * left, IHqlExpression * right)
{
HqlExprArray leftValues, rightValues;
IHqlExpression * record = left->queryRecord();
expandRowOrder(left->queryNormalizedSelector(), record, leftValues, false);
expandRowOrder(right->queryNormalizedSelector(), record, rightValues, false);
optimizeOrderValues(leftValues, rightValues, false);
doBuildAssignCompare(ctx, info, leftValues, rightValues, true, false); //MORE: ,no_break,true
}
void HqlCppTranslator::doBuildAssignCompareTable(BuildCtx & ctx, EvaluateCompareInfo & info, IHqlExpression * left, IHqlExpression * right)
{
ITypeInfo * targetType = info.target.queryType();
OwnedHqlExpr zeroTarget = createConstant(targetType->castFrom(true, 0));
OwnedHqlExpr plusOne = createConstant(targetType->castFrom(true, +1));
OwnedHqlExpr minusOne = createConstant(targetType->castFrom(true, -1));
// cmp = 0;
assignBound(ctx, info.target, zeroTarget);
BuildCtx subctx(ctx);
subctx.addGroup(); // stop bound cursors leaking outside the testing block.
// i1 iter1; i1.first();
HqlExprAttr leftIter, leftRow;
Owned cursor = createDatasetSelector(subctx, left);
cursor->buildIterateClass(subctx, leftIter, leftRow);
buildIteratorFirst(*this, subctx, leftIter, leftRow);
// i2; forEachIn(i2); {
CHqlBoundExpr isValid;
BuildCtx loopctx(subctx);
buildDatasetIterate(loopctx, right, true);
bindTableCursor(loopctx, left, leftRow);
// if (!i1.isValid()) { cmp = -1; break; }
buildIteratorIsValid(loopctx, leftIter, leftRow, isValid);
OwnedHqlExpr test = createValue(no_not, makeBoolType(), isValid.expr.getClear());
BuildCtx moreRightCtx(loopctx);
moreRightCtx.addFilter(test);
if (info.actionIfDiffer == return_stmt)
{
if (info.isEqualityCompare())
{
OwnedHqlExpr returnValue = info.getEqualityReturnValue();
moreRightCtx.addReturn(returnValue);
}
else
moreRightCtx.addReturn(minusOne);
}
else
{
buildExprAssign(moreRightCtx, info.target, minusOne);
moreRightCtx.addBreak();
}
//Now do the comparison....
{
EvaluateCompareInfo childInfo(info);
if (childInfo.actionIfDiffer == break_stmt)
childInfo.actionIfDiffer = null_stmt;
//***childInfo??
doBuildAssignCompareRow(loopctx, info, left, right);
}
if (info.actionIfDiffer != return_stmt)
{
// if (cmp != 0) break;
BuildCtx donectx(loopctx);
donectx.addFilter(info.target.expr);
donectx.addQuoted("break;");
}
// i1.next();
buildIteratorNext(*this, loopctx, leftIter, leftRow);
buildIteratorIsValid(subctx, leftIter, leftRow, isValid);
if (info.actionIfDiffer == return_stmt)
{
//if (i1.isValid) return +1;
BuildCtx moreLeftCtx(subctx);
moreLeftCtx.addFilter(isValid.expr);
if (info.isEqualityCompare())
{
OwnedHqlExpr returnValue = info.getEqualityReturnValue();
moreLeftCtx.addReturn(returnValue);
}
else
moreLeftCtx.addReturn(plusOne);
}
else
{
//if (cmp == 0 && i1.isValid) cmp = +1;
OwnedHqlExpr cmp = createBoolExpr(no_and, createBoolExpr(no_eq, LINK(info.target.expr), LINK(zeroTarget)), LINK(isValid.expr));
BuildCtx moreLeftCtx(subctx);
moreLeftCtx.addFilter(cmp);
buildExprAssign(moreLeftCtx, info.target, plusOne);
BuildCtx tailctx(ctx);
if (info.actionIfDiffer == break_stmt)
{
tailctx.addFilter(info.target.expr);
tailctx.addBreak();
}
}
}
void HqlCppTranslator::expandRowOrder(IHqlExpression * selector, IHqlExpression * record, HqlExprArray & values, bool isRow)
{
ForEachChild(idx, record)
{
IHqlExpression * field = record->queryChild(idx);
switch (field->getOperator())
{
case no_ifblock:
expandRowOrder(selector, field->queryChild(1), values, isRow);
break;
case no_record:
expandRowOrder(selector, field, values, isRow);
break;
case no_field:
{
OwnedHqlExpr selected;
if (isRow)
selected.setown(createNewSelectExpr(LINK(selector), LINK(field)));
else
selected.setown(createSelectExpr(LINK(selector), LINK(field)));
if (field->isDatarow())
expandRowOrder(selected, field->queryRecord(), values, false);
else
values.append(*LINK(selected));
break;
}
}
}
}
void HqlCppTranslator::expandSimpleOrder(IHqlExpression * left, IHqlExpression * right, HqlExprArray & leftValues, HqlExprArray & rightValues)
{
while ((left->getOperator() == no_negate) && (right->getOperator() == no_negate))
{
IHqlExpression * temp = right->queryChild(0);
right = left->queryChild(0);
left = temp;
}
if (left == right)
{
//Weird code is here just so I can force some strange exceptions in the regression suite.
IHqlExpression * cur = left;
if (cur->getOperator() == no_alias)
cur = cur->queryChild(0);
if (cur->getOperator() != no_nofold)
return;
}
if (left->isDatarow())
{
IHqlExpression * record = left->queryRecord();
assertex(right->isDatarow() && (record == right->queryRecord()));
expandRowOrder(left, record, leftValues, !isActiveRow(left));
expandRowOrder(right, record, rightValues, !isActiveRow(right));
}
else
{
leftValues.append(*LINK(left));
rightValues.append(*LINK(right));
}
}
void HqlCppTranslator::expandOrder(IHqlExpression * expr, HqlExprArray & leftValues, HqlExprArray & rightValues, OwnedHqlExpr & defaultValue)
{
OwnedHqlExpr left = normalizeListCasts(expr->queryChild(0));
OwnedHqlExpr right = normalizeListCasts(expr->queryChild(1));
if ((isFixedLengthList(left) || isNullList(left)) && (isFixedLengthList(right) || isNullList(right)))
{
unsigned maxLeft = left->numChildren();
unsigned maxRight = right->numChildren();
unsigned max = std::min(maxLeft, maxRight);
for (unsigned i=0; i < max; i++)
expandSimpleOrder(left->queryChild(i), right->queryChild(i), leftValues, rightValues);
if (maxLeft != maxRight)
defaultValue.setown(createConstant(signedType->castFrom(true, ((maxLeft > maxRight) ? +1 : -1))));
}
else
expandSimpleOrder(left, right, leftValues, rightValues);
}
IHqlExpression * HqlCppTranslator::querySimpleOrderSelector(IHqlExpression * expr)
{
if (expr->getOperator() != no_select)
return NULL;
bool isNew;
IHqlExpression * ds = querySelectorDataset(expr, isNew);
if (isNew)
return NULL;
return ds;
}
static unsigned getMemcmpSize(IHqlExpression * left, IHqlExpression * right, bool isEqualityCompare)
{
ITypeInfo * leftType = left->queryType();
if (!leftType)
return 0;
if (!isSameBasicType(leftType, right->queryType()))
return 0;
unsigned size = leftType->getSize();
switch (leftType->getTypeCode())
{
case type_bigendianint:
case type_boolean:
return size;
case type_littleendianint:
if ((size == 1) && !leftType->isSigned())
return 1;
if (isEqualityCompare)
return size;
break;
case type_string:
case type_data:
case type_qstring:
if (size != UNKNOWN_LENGTH)
return size;
break;
}
return 0;
}
void HqlCppTranslator::optimizeOrderValues(HqlExprArray & leftValues, HqlExprArray & rightValues, bool isEqualityCompare)
{
unsigned max = leftValues.ordinality();
if (max <= 1)
return;
for (unsigned i=0; i < max-1; i++)
{
IHqlExpression * curFirstLeft = &leftValues.item(i);
IHqlExpression * curFirstRight = &rightValues.item(i);
IHqlExpression * leftSel = querySimpleOrderSelector(curFirstLeft);
IHqlExpression * rightSel = querySimpleOrderSelector(curFirstRight);
if (!leftSel || !rightSel)
continue;
unsigned compareSize = getMemcmpSize(curFirstLeft, curFirstRight, isEqualityCompare);
if (!compareSize)
continue;
IHqlExpression * nextLeft = &leftValues.item(i+1);
IHqlExpression * nextRight = &rightValues.item(i+1);
if (querySimpleOrderSelector(nextLeft) != leftSel || querySimpleOrderSelector(nextRight) != rightSel ||
(getMemcmpSize(nextLeft, nextRight, isEqualityCompare) == 0))
continue;
//Worth iterating the selectors...
RecordSelectIterator leftIter(leftSel->queryRecord(), leftSel);
ForEach(leftIter)
if (leftIter.query() == curFirstLeft)
break;
if (!leftIter.isValid() || leftIter.isInsideIfBlock())
continue;
RecordSelectIterator rightIter(rightSel->queryRecord(), rightSel);
ForEach(rightIter)
if (rightIter.query() == curFirstRight)
break;
if (!rightIter.isValid() || rightIter.isInsideIfBlock())
continue;
unsigned j; // linux wants it declared outside of 'for'
for (j=i+1; j < max; j++)
{
if (!leftIter.next() || leftIter.isInsideIfBlock())
break;
if (!rightIter.next() || rightIter.isInsideIfBlock())
break;
IHqlExpression * nextLeft = &leftValues.item(j);
IHqlExpression * nextRight = &rightValues.item(j);
if (leftIter.query() != nextLeft || rightIter.query() != nextRight)
break;
unsigned thisSize = getMemcmpSize(nextLeft, nextRight, isEqualityCompare);
if (!thisSize)
break;
compareSize += thisSize;
}
if (j != i+1)
{
IHqlExpression * newLeft = createValue(no_typetransfer, makeStringType(compareSize), LINK(curFirstLeft));
IHqlExpression * newRight = createValue(no_typetransfer, makeStringType(compareSize), LINK(curFirstRight));
leftValues.replace(*newLeft, i);
rightValues.replace(*newRight, i);
leftValues.removen(i+1, j-(i+1));
rightValues.removen(i+1, j-(i+1));
max -= (j - (i+1));
}
}
}
inline IHqlExpression * createSignedConstant(__int64 value)
{
return createConstant(signedType->castFrom(true, value));
}
static IHqlExpression * convertAllToInteger(IHqlExpression * allExpr)
{
IValue * allValue = allExpr->queryValue();
if (allValue)
return createSignedConstant(allValue->getBoolValue() ? 1 : 0);
return createValue(no_if, LINK(signedType), LINK(allExpr), createSignedConstant(1), createSignedConstant(0));
}
void HqlCppTranslator::doBuildAssignCompareElement(BuildCtx & ctx, EvaluateCompareInfo & info, IHqlExpression * left, IHqlExpression * right, bool isFirst, bool isLast)
{
if (left->getOperator() == no_if && right->getOperator() == no_if && left->queryChild(0) == right->queryChild(0))
{
BuildCtx subctx(ctx);
IHqlStmt * filter = buildFilterViaExpr(subctx, left->queryChild(0));
doBuildAssignCompareElement(subctx, info, left->queryChild(1), right->queryChild(1), isFirst, false);
if ((isFirst && (info.actionIfDiffer != return_stmt)) || left->queryChild(2) != right->queryChild(2))
{
subctx.selectElse(filter);
doBuildAssignCompareElement(subctx, info, left->queryChild(2), right->queryChild(2), isFirst, false);
}
return;
}
if (left == right)
{
//Can happen from conditions expanded above
if (isFirst)
{
if (info.actionIfDiffer != return_stmt)
buildExprAssign(ctx, info.target, queryZero());
}
return;
}
ITypeInfo * leftType = left->queryType();
type_t tc;
if (leftType)
tc = leftType->getTypeCode();
else
tc = type_set;
CHqlBoundExpr lhs,rhs;
bool useMemCmp = false;
switch (tc)
{
case type_table:
case type_groupedtable:
doBuildAssignCompareTable(ctx, info, left, right);
return;
case type_row:
doBuildAssignCompareRow(ctx, info, left, right);
return;
case type_bigendianint:
{
//MORE: Compare big endian integers with a memcmp
if (hasAddress(ctx, left) && hasAddress(ctx, right) && isSameBasicType(leftType, right->queryType()))
{
buildAddress(ctx, left, lhs);
buildAddress(ctx, right, rhs);
useMemCmp = true;
break;
}
Owned type = makeIntType(leftType->getSize(), leftType->isSigned());
OwnedHqlExpr intLeft = createValue(no_implicitcast, type.getLink(), LINK(left));
OwnedHqlExpr intRight = createValue(no_implicitcast, type.getLink(), LINK(right));
buildCachedExpr(ctx, intLeft, lhs);
buildCachedExpr(ctx, intRight, rhs);
break;
}
case type_string:
case type_data:
case type_qstring:
{
OwnedHqlExpr simpleLeft = getSimplifyCompareArg(left);
OwnedHqlExpr simpleRight = getSimplifyCompareArg(right);
buildCachedExpr(ctx, simpleLeft, lhs);
buildCachedExpr(ctx, simpleRight, rhs);
break;
}
default:
buildCachedExpr(ctx, left, lhs);
buildCachedExpr(ctx, right, rhs);
break;
}
ITypeInfo * realType = realType = lhs.queryType()->queryPromotedType();
tc = realType->getTypeCode();
IHqlExpression * op = NULL;
switch (tc)
{
//MORE: Should common up with comparison code...
case type_string:
case type_data:
case type_qstring:
{
HqlExprArray args;
_ATOM func;
if (lhs.length || rhs.length || needVarStringCompare(realType, rhs.queryType()->queryPromotedType()))
{
//MORE: This does not cope with different padding characters...
func = queryStrCompareFunc(realType);
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
}
else
{
func = memcmpAtom;
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
args.append(*getSizetConstant(realType->getSize()));
}
op = bindTranslatedFunctionCall(func, args);
break;
}
case type_unicode:
{
HqlExprArray args;
assertex(haveCommonLocale(leftType, right->queryType()));
char const * locale = getCommonLocale(leftType, right->queryType())->str();
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
op = bindTranslatedFunctionCall(compareUnicodeUnicodeAtom, args);
break;
}
case type_varunicode:
{
HqlExprArray args;
assertex(haveCommonLocale(leftType, right->queryType()));
char const * locale = getCommonLocale(leftType, right->queryType())->str();
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
op = bindTranslatedFunctionCall(compareVUnicodeVUnicodeAtom, args);
break;
}
case type_utf8:
{
HqlExprArray args;
assertex(haveCommonLocale(leftType, right->queryType()));
char const * locale = getCommonLocale(leftType, right->queryType())->str();
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
args.append(*createConstant(locale));
op = bindTranslatedFunctionCall(compareUtf8Utf8Atom, args);
break;
}
case type_varstring:
{
HqlExprArray args;
args.append(*getElementPointer(lhs.expr));
args.append(*getElementPointer(rhs.expr));
op = bindTranslatedFunctionCall(compareVStrVStrAtom, args);
break;
}
case type_decimal:
{
HqlExprArray args;
if (!isPushed(lhs) && !isPushed(rhs) && (leftType->queryPromotedType() == right->queryType()->queryPromotedType()))
{
args.append(*getSizetConstant(leftType->queryPromotedType()->getSize()));
args.append(*getPointer(lhs.expr));
args.append(*getPointer(rhs.expr));
op = bindTranslatedFunctionCall(leftType->isSigned() ? DecCompareDecimalAtom : DecCompareUDecimalAtom, args);
}
else
{
bool pushedLhs = ensurePushed(ctx, lhs);
bool pushedRhs = ensurePushed(ctx, rhs);
//NB: Arguments could be pushed in opposite order 1 <=> x *2
if (pushedLhs && !pushedRhs)
op = bindTranslatedFunctionCall(DecDistinctRAtom, args);
else
op = bindTranslatedFunctionCall(DecDistinctAtom, args);
}
break;
}
case type_set:
case type_array:
{
//compare all
OwnedHqlExpr leftAll = lhs.getIsAll();
OwnedHqlExpr rightAll = rhs.getIsAll();
assertex(leftAll && rightAll);
if (leftAll != rightAll)
{
if (leftAll->queryValue() && rightAll->queryValue())
{
op = createConstant(leftAll->queryValue()->getIntValue() - rightAll->queryValue()->getIntValue());
break;
}
if (getIntValue(leftAll, false) || getIntValue(rightAll, false))
{
op = createValue(no_sub, LINK(signedType), convertAllToInteger(leftAll), convertAllToInteger(rightAll));
break;
}
}
if (lhs.expr != rhs.expr)
{
HqlExprArray args;
args.append(*getBoundLength(lhs));
args.append(*getElementPointer(lhs.expr));
args.append(*getBoundLength(rhs));
args.append(*getElementPointer(rhs.expr));
op = bindTranslatedFunctionCall(compareDataDataAtom, args);
}
if (leftAll != rightAll)
{
IHqlExpression * orderAll = createValue(no_sub, LINK(signedType), convertAllToInteger(leftAll), convertAllToInteger(rightAll));
if (op)
{
IHqlExpression * cond = NULL;
if (!getIntValue(leftAll, true))
{
if (!getIntValue(rightAll, true))
cond = NULL;
else
cond = LINK(rightAll);
}
else
{
if (!getIntValue(rightAll, true))
cond = LINK(leftAll);
else
cond = createBoolExpr(no_or, LINK(leftAll), LINK(rightAll));
}
if (cond)
op = createValue(no_if, LINK(signedType), cond, orderAll, op);
else
orderAll->Release();
}
else
op = orderAll;
}
if (!op)
op = getZero();
break;
}
case type_boolean:
case type_swapint:
case type_packedint:
case type_int:
if (!useMemCmp && !info.isEqualityCompare() && (realType->getSize() < signedType->getSize()))
{
op = createValue(no_sub, LINK(signedType),
createValue(no_implicitcast, LINK(signedType), lhs.expr.getLink()),
createValue(no_implicitcast, LINK(signedType), rhs.expr.getLink()));
break;
}
//fall through
default:
if (useMemCmp)
{
HqlExprArray args;
args.append(*lhs.expr.getLink());
args.append(*rhs.expr.getLink());
args.append(*getSizetConstant(leftType->getSize()));
op = bindTranslatedFunctionCall(memcmpAtom, args);
}
else
{
if (info.isEqualityCompare())
{
op = createBoolExpr(no_ne, LINK(lhs.expr), LINK(rhs.expr));
}
else
{
ensureSimpleExpr(ctx, lhs);
ensureSimpleExpr(ctx, rhs);
OwnedHqlExpr testlt = createBoolExpr(no_lt, lhs.expr.getLink(), rhs.expr.getLink());
OwnedHqlExpr retlt = createIntConstant(-1);
OwnedHqlExpr testgt = createBoolExpr(no_gt, lhs.expr.getLink(), rhs.expr.getLink());
OwnedHqlExpr retgt = createIntConstant(+1);
if (info.actionIfDiffer == return_stmt)
{
BuildCtx subctx1(ctx);
subctx1.addFilter(testlt);
subctx1.addReturn(retlt);
BuildCtx subctx2(ctx);
subctx2.addFilter(testgt);
subctx2.addReturn(retgt);
return;
}
else
{
// generate (a < b ? -1 : (a > b ? +1 : 0))
op = createValue(no_if, LINK(signedType),
LINK(testlt), LINK(retlt),
createValue(no_if, LINK(signedType), LINK(testgt), LINK(retgt), getZero()));
}
}
}
break;
}
OwnedHqlExpr safeReleaseOp = op;
BuildCtx subctx(ctx);
if (info.isEqualityCompare() && (info.actionIfDiffer == return_stmt))
{
subctx.addFilter(op);
OwnedHqlExpr returnValue = info.getEqualityReturnValue();
subctx.addReturn(returnValue);
}
else
{
assignBound(subctx, info.target, op);
switch (info.actionIfDiffer)
{
case break_stmt:
subctx.addFilter(info.target.expr);
subctx.addBreak();
break;
case return_stmt:
if (!isLast || info.hasDefault)
subctx.addFilter(info.target.expr);
else
info.alwaysReturns = true;
subctx.addReturn(info.target.expr);
break;
}
}
}
void HqlCppTranslator::doBuildAssignCompare(BuildCtx & ctx, EvaluateCompareInfo & info, HqlExprArray & leftValues, HqlExprArray & rightValues, bool isFirst, bool isOuter)
{
assertex(leftValues.ordinality() == rightValues.ordinality());
Owned subctx = new BuildCtx(ctx);
OwnedHqlExpr compare = createBoolExpr(no_not, LINK(info.target.expr));
unsigned idx;
unsigned max = leftValues.ordinality();
for (idx = 0; idx < max; idx++)
{
if ((idx & 7) == 0)
subctx.setown(new BuildCtx(ctx));
if (!info.actionIfDiffer && !isFirst)
subctx->addFilter(compare);
doBuildAssignCompareElement(*subctx, info, &leftValues.item(idx), &rightValues.item(idx), isFirst, isOuter && (idx == max-1));
isFirst = false;
}
}
void HqlCppTranslator::doBuildAssignOrder(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
HqlExprArray leftValues, rightValues;
OwnedHqlExpr defaultValue;
expandOrder(expr, leftValues, rightValues, defaultValue);
optimizeOrderValues(leftValues, rightValues, false);
EvaluateCompareInfo info(no_order);
info.target.set(target);
doBuildAssignCompare(ctx, info, leftValues, rightValues, true, false);
unsigned maxLeft = leftValues.ordinality();
if (defaultValue)
{
if (maxLeft != 0)
{
OwnedHqlExpr compare = createBoolExpr(no_not, LINK(target.expr));
ctx.addFilter(compare);
}
buildExprAssign(ctx, target, defaultValue);
}
else if (maxLeft == 0)
buildExprAssign(ctx, target, queryZero());
}
void HqlCppTranslator::doBuildReturnCompare(BuildCtx & ctx, IHqlExpression * expr, node_operator op, bool isBoolEquality)
{
HqlExprArray leftValues, rightValues;
OwnedHqlExpr defaultValue;
expandOrder(expr, leftValues, rightValues, defaultValue);
optimizeOrderValues(leftValues, rightValues, (op == no_eq));
EvaluateCompareInfo info(op);
info.actionIfDiffer = return_stmt;
info.isBoolEquality = isBoolEquality;
info.hasDefault = (defaultValue != NULL);
createTempFor(ctx, expr, info.target);
doBuildAssignCompare(ctx, info, leftValues, rightValues, true, true);
if (!info.alwaysReturns)
{
if (info.isBoolEquality)
{
OwnedHqlExpr returnValue = createConstant(defaultValue == NULL);
buildReturn(ctx, returnValue);
}
else
{
if (defaultValue)
buildReturn(ctx, defaultValue);
else
buildReturn(ctx, queryZero());
}
}
}
//---------------------------------------------------------------------------
//-- no_hash --
class HashCodeCreator
{
public:
HashCodeCreator(HqlCppTranslator & _translator, const CHqlBoundTarget & _target, node_operator _hashKind, bool _optimizeInternal)
: translator(_translator), target(_target), hashKind(_hashKind), optimizeInternal(_optimizeInternal)
{
prevFunc = NULL;
}
//Combine calls to the hash function on adjacent memory to minimise the number of calls
//and the generated code size.
void buildHash(BuildCtx & ctx, _ATOM func, IHqlExpression * length, IHqlExpression * ptr)
{
if ((func == hash32DataAtom) || (func == hash64DataAtom))
{
ptr = stripTranslatedCasts(ptr);
if (prevFunc)
{
if ((prevFunc == func) && rightFollowsLeft(prevPtr, prevLength, ptr))
{
prevLength.setown(peepholeAddExpr(prevLength, length));
return;
}
flush(ctx);
}
prevFunc = func;
prevLength.set(length);
prevPtr.set(ptr);
return;
}
flush(ctx);
buildCall(ctx, func, length, ptr);
}
void beginCondition(BuildCtx & ctx)
{
ensureInitialAssigned(ctx);
flush(ctx);
}
void endCondition(BuildCtx & ctx)
{
flush(ctx);
}
void finish(BuildCtx & ctx)
{
flush(ctx);
ensureInitialAssigned(ctx);
}
void setInitialValue(IHqlExpression * expr)
{
initialValue.set(expr);
}
inline node_operator kind() const { return hashKind; }
inline bool optimize() const { return optimizeInternal; }
protected:
void buildCall(BuildCtx & ctx, _ATOM func, IHqlExpression * length, IHqlExpression * ptr)
{
if (func == hash32DataAtom)
{
unsigned fixedSize = (unsigned)getIntValue(length, 0);
switch (fixedSize)
{
case 1: func = hash32Data1Atom; break;
case 2: func = hash32Data2Atom; break;
case 3: func = hash32Data3Atom; break;
case 4: func = hash32Data4Atom; break;
case 5: func = hash32Data5Atom; break;
case 6: func = hash32Data6Atom; break;
case 7: func = hash32Data7Atom; break;
case 8: func = hash32Data8Atom; break;
}
if (func != hash32DataAtom)
length = NULL;
}
HqlExprArray args;
if (length)
args.append(*LINK(length));
args.append(*LINK(ptr));
if (initialValue)
args.append(*initialValue.getClear());
else
args.append(*LINK(target.expr));
CHqlBoundExpr boundHash;
boundHash.expr.setown(translator.bindTranslatedFunctionCall(func, args));
translator.assign(ctx, target, boundHash);
}
void ensureInitialAssigned(BuildCtx & ctx)
{
if (initialValue)
{
translator.assignBound(ctx, target, initialValue);
initialValue.clear();
}
}
void flush(BuildCtx & ctx)
{
if (prevFunc)
{
buildCall(ctx, prevFunc, prevLength, prevPtr);
prevFunc = NULL;
}
}
protected:
HqlCppTranslator & translator;
const CHqlBoundTarget & target;
LinkedHqlExpr initialValue;
node_operator hashKind;
bool optimizeInternal;
_ATOM prevFunc;
OwnedHqlExpr prevLength;
OwnedHqlExpr prevPtr;
};
void HqlCppTranslator::doBuildAssignHashCrc(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * child = expr->queryChild(0);
ITypeInfo * childType = child->queryType();
LinkedHqlExpr initialValue = queryZero();
node_operator op = expr->getOperator();
if (op == no_hash32)
initialValue.setown(createConstant(createIntValue(0x811C9DC5, 4, false)));
else if (op == no_hash64)
initialValue.setown(createConstant(createIntValue(I64C(0xcbf29ce484222325), 8, false)));
HashCodeCreator creator(*this, target, op, expr->hasProperty(internalAtom));
creator.setInitialValue(initialValue);
if (child->getOperator() != no_sortlist)
doBuildAssignHashElement(ctx, creator, child);
else
{
unsigned max = child->numChildren();
unsigned idx;
for (idx = 0; idx < max; idx++)
doBuildAssignHashElement(ctx, creator, child->queryChild(idx));
}
creator.finish(ctx);
}
void HqlCppTranslator::doBuildAssignHashElement(BuildCtx & ctx, HashCodeCreator & creator, IHqlExpression * elem, IHqlExpression * record)
{
bool useNewSelector = elem->isDatarow() && ((elem->getOperator() != no_select) || isNewSelector(elem));
ForEachChild(i, record)
{
IHqlExpression * cur = record->queryChild(i);
switch (cur->getOperator())
{
case no_field:
{
OwnedHqlExpr selected = useNewSelector ? createNewSelectExpr(LINK(elem), LINK(cur)) : createSelectExpr(LINK(elem), LINK(cur));
doBuildAssignHashElement(ctx, creator, selected);
break;
}
case no_record:
doBuildAssignHashElement(ctx, creator, elem, cur);
break;
case no_ifblock:
doBuildAssignHashElement(ctx, creator, elem, cur->queryChild(1));
break;
}
}
}
void HqlCppTranslator::doBuildAssignHashElement(BuildCtx & ctx, HashCodeCreator & creator, IHqlExpression * elem)
{
if (creator.optimize())
{
switch (elem->getOperator())
{
case no_if:
{
BuildCtx subctx(ctx);
creator.beginCondition(subctx);
IHqlStmt * cond = buildFilterViaExpr(subctx, elem->queryChild(0));
doBuildAssignHashElement(subctx, creator, elem->queryChild(1));
creator.endCondition(subctx);
IHqlExpression * elseValue = elem->queryChild(2);
if (elseValue && elseValue->getOperator() != no_constant)
{
subctx.selectElse(cond);
creator.beginCondition(subctx);
doBuildAssignHashElement(subctx, creator, elseValue);
creator.endCondition(subctx);
}
return;
}
case no_constant:
return;
}
}
Linked type = elem->queryType()->queryPromotedType(); // skip alien data types, to logical type.
if (type->getTypeCode() == type_row)
{
doBuildAssignHashElement(ctx, creator, elem, elem->queryRecord());
return;
}
_ATOM func=NULL;
switch (creator.kind())
{
case no_hash: func = hashDataAtom; break;
case no_hash32: func = hash32DataAtom; break;
case no_hash64: func = hash64DataAtom; break;
case no_crc: func = crcDataAtom; break;
}
CHqlBoundExpr bound;
OwnedHqlExpr length;
OwnedHqlExpr ptr;
bool alreadyTrimmedRight = (elem->getOperator() == no_trim) && (elem->hasProperty(rightAtom) || !elem->hasProperty(leftAtom));
//If this hash is generated internally (e.g., for a dedup) and fixed length, then can simplify the hash calculation
bool canOptimizeHash = (creator.optimize() && isFixedSize(type));
bool optimizeTrim = alreadyTrimmedRight || canOptimizeHash;
switch (type->getTypeCode())
{
case type_string:
{
if (!optimizeTrim)
{
OwnedHqlExpr trimmed = createValue(no_trim, getStretchedType(UNKNOWN_LENGTH, type), LINK(elem));
buildCachedExpr(ctx, trimmed, bound);
}
else
buildCachedExpr(ctx, elem, bound);
length.setown(getBoundLength(bound));
ptr.setown(getElementPointer(bound.expr));
}
break;
case type_unicode:
{
if (!optimizeTrim)
{
OwnedHqlExpr trimmed = createValue(no_trim, getStretchedType(UNKNOWN_LENGTH, type), LINK(elem));
buildCachedExpr(ctx, trimmed, bound);
}
else
buildCachedExpr(ctx, elem, bound);
length.setown(getBoundLength(bound));
ptr.setown(getElementPointer(bound.expr));
switch (creator.kind())
{
case no_hash: func = hashUnicodeAtom; break;
case no_hash32: func = hash32UnicodeAtom; break;
case no_hash64: func = hash64UnicodeAtom; break;
case no_crc: func = crcUnicodeAtom; break;
}
}
break;
case type_utf8:
{
if (!optimizeTrim)
{
OwnedHqlExpr trimmed = createValue(no_trim, getStretchedType(UNKNOWN_LENGTH, type), LINK(elem));
buildCachedExpr(ctx, trimmed, bound);
}
else
buildCachedExpr(ctx, elem, bound);
length.setown(getBoundLength(bound));
ptr.setown(getElementPointer(bound.expr));
switch (creator.kind())
{
case no_hash: func = hashUtf8Atom; break;
case no_hash32: func = hash32Utf8Atom; break;
case no_hash64: func = hash64Utf8Atom; break;
case no_crc: func = crcUtf8Atom; break;
}
}
break;
case type_data:
{
buildCachedExpr(ctx, elem, bound);
length.setown(getBoundLength(bound));
ptr.setown(getElementPointer(bound.expr));
break;
}
case type_qstring:
{
LinkedHqlExpr exprToHash = elem;
if (!canOptimizeHash)
{
//Always convert to a string so the hash is compatible with a string.
OwnedHqlExpr cast = ensureExprType(elem, unknownStringType);
if (alreadyTrimmedRight)
{
exprToHash.set(cast);
}
else
{
OwnedHqlExpr trimmed = createValue(no_trim, LINK(unknownStringType), LINK(cast));
exprToHash.setown(foldHqlExpression(trimmed));
}
}
buildCachedExpr(ctx, exprToHash, bound);
length.setown(getBoundSize(bound));
ptr.setown(getElementPointer(bound.expr));
break;
}
case type_varstring:
buildCachedExpr(ctx, elem, bound);
ptr.setown(getElementPointer(bound.expr));
switch (creator.kind())
{
case no_hash: func = hashVStrAtom; break;
case no_hash32: func = hash32VStrAtom; break;
case no_hash64: func = hash64VStrAtom; break;
case no_crc: func = crcVStrAtom; break;
}
break;
case type_varunicode:
buildCachedExpr(ctx, elem, bound);
ptr.setown(getElementPointer(bound.expr));
switch (creator.kind())
{
case no_hash: func = hashVUnicodeAtom; break;
case no_hash32: func = hash32VUnicodeAtom; break;
case no_hash64: func = hash64VUnicodeAtom; break;
case no_crc: func = crcVUnicodeAtom; break;
}
break;
case type_boolean:
case type_int:
case type_swapint:
case type_real:
if (creator.optimize() && hasAddress(ctx, elem))
{
buildAddress(ctx, elem, bound);
length.setown(getSizetConstant(type->getSize()));
ptr.setown(LINK(bound.expr));
}
else
{
if (!creator.optimize())
type.setown(makeIntType(8, true));
OwnedHqlExpr castElem = ensureExprType(elem, type);
buildTempExpr(ctx, castElem, bound);
length.setown(getSizetConstant(type->getSize()));
ptr.setown(getPointer(bound.expr));
}
break;
case type_row:
throwUnexpected();
break;
case type_groupedtable:
case type_table:
//MORE: Should be handle this differently, with an iterator for the link counted rows case?
//Not sure if that is a good idea - we need to be certain we get the same values with
//LCR rows enabled and disabled. But this won't be very efficient with lcr rows.
//fallthrough
if (creator.optimize() && hasOutOfLineRows(elem->queryType()))
{
BuildCtx iterctx(ctx);
BoundRow * row = buildDatasetIterate(iterctx, elem, false);
doBuildAssignHashElement(iterctx, creator, elem->queryNormalizedSelector(), elem->queryRecord());
return;
}
else
{
OwnedHqlExpr serialized = ::ensureSerialized(elem);
buildDataset(ctx, serialized, bound, FormatBlockedDataset);
length.setown(getBoundSize(bound));
ptr.setown(getPointer(bound.expr));
}
break;
default:
buildTempExpr(ctx, elem, bound, FormatBlockedDataset);
length.setown(getBoundSize(bound));
ptr.setown(getPointer(bound.expr));
break;
}
creator.buildHash(ctx, func, length, ptr);
}
//---------------------------------------------------------------------------
//-- no_hash --
void HqlCppTranslator::doBuildAssignHashMd5(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * child = expr->queryChild(0);
ITypeInfo * childType = child->queryType();
Owned stateType = makeDataType(sizeof(md5_state_s));
//initialize the state object
CHqlBoundTarget stateTemp;
CHqlBoundExpr state;
createTempFor(ctx, stateType, stateTemp, typemod_none, FormatNatural);
state.setFromTarget(stateTemp);
OwnedHqlExpr stateExpr = state.getTranslatedExpr();
HqlExprArray args;
args.append(*LINK(stateExpr));
OwnedHqlExpr callInit = bindFunctionCall(hashMd5InitAtom, args);
buildStmt(ctx, callInit);
//Now hash each of the elements in turn.
if (child->getOperator() != no_sortlist)
doBuildHashMd5Element(ctx, child, state);
else
{
unsigned max = child->numChildren();
for (unsigned idx = 0; idx < max; idx++)
doBuildHashMd5Element(ctx, child->queryChild(idx), state);
}
//finalise the md5, and get the result.
args.append(*LINK(stateExpr));
OwnedHqlExpr callFinish = bindFunctionCall(hashMd5FinishAtom, args);
buildExprAssign(ctx, target, callFinish);
}
void HqlCppTranslator::doBuildHashMd5Element(BuildCtx & ctx, IHqlExpression * elem, CHqlBoundExpr & state)
{
CHqlBoundExpr bound;
Linked type = elem->queryType()->queryPromotedType(); // skip alien data types, to logical type.
HqlExprArray args;
_ATOM func=NULL;
switch (type->getTypeCode())
{
case type_string:
case type_unicode:
case type_data:
case type_qstring:
case type_varstring:
case type_varunicode:
case type_utf8:
buildExpr(ctx, elem, bound);
args.append(*getBoundSize(bound));
args.append(*getElementPointer(bound.expr));
break;
case type_int:
case type_swapint:
case type_packedint:
{
type.setown(makeIntType(8, true));
OwnedHqlExpr castElem = ensureExprType(elem, type);
buildTempExpr(ctx, castElem, bound);
args.append(*getSizetConstant(type->getSize()));
args.append(*getPointer(bound.expr));
break;
}
default:
buildTempExpr(ctx, elem, bound);
args.append(*getSizetConstant(type->getSize()));
args.append(*getPointer(bound.expr));
break;
}
args.append(*getBoundSize(state));
args.append(*LINK(state.expr));
OwnedHqlExpr call = bindTranslatedFunctionCall(hashMd5DataAtom, args);
ctx.addExpr(call);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprTransfer(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
CHqlBoundExpr bound;
//Ensure the bound result has an address
IHqlExpression * src = expr->queryChild(0);
bool gotAddress = false;
if (src->isDataset())
{
buildDataset(ctx, src, bound, FormatBlockedDataset);
bound.expr.setown(getPointer(bound.expr));
}
else if (src->isDatarow())
{
Owned ref = buildNewRow(ctx, src);
ref->get(ctx, bound);
bound.expr.setown(getPointer(bound.expr));
}
else if (isTypePassedByAddress(src->queryType()))
buildCachedExpr(ctx, src, bound);
else if (hasAddress(ctx, src))
{
buildAddress(ctx, src, bound);
gotAddress = true;
}
else
buildTempExpr(ctx, src, bound);
OwnedITypeInfo from = bound.expr->getType();
ITypeInfo * to = expr->queryType();
//Must calculate the size of the bound value before we start messing about with stripping casts etc.
OwnedHqlExpr size;
if (to->getSize() == UNKNOWN_LENGTH)
{
if (from->getSize() == UNKNOWN_LENGTH)
size.setown(getBoundSize(bound));
else
size.setown(getSizetConstant(from->getSize()));
}
if (!isTypePassedByAddress(from) && !gotAddress)
bound.expr.setown(getAddress(bound.expr));
//strip unnecessary casts...
while (bound.expr->getOperator() == no_implicitcast)
bound.expr.set(bound.expr->queryChild(0));
if (isTypePassedByAddress(to))
{
to->Link();
if (!to->isReference())
to = makeReferenceModifier(to);
tgt.expr.setown(createValue(no_implicitcast, to, LINK(bound.expr)));
if (to->getSize() == UNKNOWN_LENGTH)
{
switch (to->getTypeCode())
{
case type_unicode:
if (size->isConstant())
tgt.length.setown(getSizetConstant((size32_t)getIntValue(size)/sizeof(UChar)));
else
tgt.length.setown(createValue(no_div, LINK(sizetType), LINK(size), getSizetConstant(2)));
break;
case type_qstring:
if (size->isConstant())
tgt.length.setown(getSizetConstant(rtlQStrLength((size32_t)getIntValue(size))));
else
tgt.length.setown(createValue(no_div, LINK(sizetType), multiplyValue(size, 4), getSizetConstant(3)));
break;
case type_varstring:
case type_varunicode:
break;
default:
tgt.length.set(size);
break;
}
}
}
else
{
tgt.length.clear();
tgt.expr.set(bound.expr);
if (hasWrapperModifier(tgt.expr->queryType()))
tgt.expr.setown(createValue(no_implicitcast, makeReferenceModifier(LINK(queryUnqualifiedType(from))), LINK(tgt.expr)));
tgt.expr.setown(createValue(no_implicitcast, makePointerType(LINK(to)), tgt.expr.getClear()));
tgt.expr.setown(createValue(no_deref, LINK(to), tgt.expr.getClear()));
}
}
//---------------------------------------------------------------------------
//-- no_ordered
void HqlCppTranslator::doBuildExprOrdered(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (ctx.getMatchExpr(expr, tgt))
return;
bool ascending = true;
IHqlExpression * list = expr->queryChild(0);
IHqlExpression * attr = expr->queryChild(1);
if (attr && attr->isAttribute() && (attr->queryName() == descAtom))
ascending = false;
if (list->numChildren() == 0)
throwError(HQLERR_RankOnNull);
//create the list that is going to be sorted
CHqlBoundExpr boundList;
OwnedHqlExpr optimalList = getOptimialListFormat(list);
buildExpr(ctx, optimalList, boundList);
//create a compare function....
ITypeInfo * elementType = boundList.expr->queryType()->queryChildType();
unsigned elementSize = elementType->getSize();
if (elementSize == UNKNOWN_LENGTH)
throwError(HQLERR_OrderOnVarlengthStrings);
StringBuffer tempName;
getUniqueId(tempName.append('v'));
IHqlExpression * tempCompare = createVariable(tempName.str(), makeVoidType());
StringBuffer s;
s.clear().append("extern int ").append(tempName).append("(const void * left, const void * right);");
if (options.spanMultipleCpp)
{
BuildCtx protoctx(*code, mainprototypesAtom);
protoctx.addQuoted(s);
}
else
{
BuildCtx protoctx(*code, prototypeAtom);
protoctx.addQuoted(s);
}
BuildCtx declareCtx(*code, declareAtom);
s.clear().append("int ").append(tempName).append("(const void * left, const void * right)");
declareCtx.addQuotedCompound(s);
Owned argType;
if (isTypePassedByAddress(elementType) && !hasReferenceModifier(elementType))
argType.setown(makeReferenceModifier(LINK(elementType)));
else
argType.setown(makePointerType(LINK(elementType)));
OwnedHqlExpr leftAddr = createVariable("left", LINK(argType));
OwnedHqlExpr rightAddr = createVariable("right", LINK(argType));
IHqlExpression * left = convertAddressToValue(leftAddr, elementType);
IHqlExpression * right = convertAddressToValue(rightAddr, elementType);
if (elementType->isReference())
elementSize = sizeof(char * *);
left = createTranslatedOwned(left);
right = createTranslatedOwned(right);
OwnedHqlExpr compare;
if (ascending)
compare.setown(createValue(no_order, LINK(signedType), left, right));
else
compare.setown(createValue(no_order, LINK(signedType), right, left));
CHqlBoundExpr boundCompare;
buildExpr(declareCtx, compare, boundCompare);
declareCtx.setNextDestructor();
declareCtx.addReturn(boundCompare.expr);
//Allocate an array to store the orders
unsigned max = list->numChildren();
Owned t = makeArrayType(LINK(unsignedType), max);
IHqlExpression * table = ctx.getTempDeclare(t, NULL);
ctx.associateExpr(expr, table);
//Generate the call to the function that calculates the orders
IHqlExpression * castCompare = createValue(no_implicitcast, makePointerType(makeVoidType()), tempCompare);
HqlExprArray args;
args.append(*getPointer(table));
args.append(*getPointer(boundList.expr));
args.append(*createConstant(unsignedType->castFrom(false, max)));
args.append(*getSizetConstant(elementSize));
args.append(*castCompare);
callProcedure(ctx, createOrderAtom, args);
tgt.expr.setown(table);
}
//---------------------------------------------------------------------------
//-- no_rank
void checkRankRange(IHqlExpression * index, IHqlExpression * list)
{
IValue * indexValue = index->queryValue();
if (indexValue)
{
unsigned max = list->numChildren();
unsigned idx = (unsigned)indexValue->getIntValue();
//MORE: Should be a warning.....
if ((idx < 1) || (idx > max))
throwError(HQLERR_RankOutOfRange);
}
//MORE: Could dynamically allocate the array indexes...
if (list->getOperator() == no_getresult)
{
StringBuffer s;
IHqlExpression * sequence = queryPropertyChild(list, sequenceAtom, 0);
IHqlExpression * name = queryPropertyChild(list, namedAtom, 0);
getStoredDescription(s, sequence, name, true);
throwError1(HQLERR_RankOnStored, s.str());
}
}
void HqlCppTranslator::createOrderList(BuildCtx & ctx, IHqlExpression * expr, IHqlExpression * ascdesc, CHqlBoundExpr & tgt)
{
ITypeInfo * orderedType = makeArrayType(LINK(unsignedType), expr->numChildren());
OwnedHqlExpr ordered = createValue(no_ordered, orderedType, LINK(expr), LINK(ascdesc));
buildExpr(ctx, ordered, tgt);
}
void HqlCppTranslator::doBuildExprRank(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * index = expr->queryChild(0);
IHqlExpression * list = expr->queryChild(1);
checkRankRange(index, list);
CHqlBoundExpr bound, boundIndex;
createOrderList(ctx, list, expr->queryChild(2), bound);
buildExpr(ctx, index, boundIndex);
HqlExprArray args;
args.append(*boundIndex.expr.getClear());
args.append(*createConstant(unsignedType->castFrom(false, list->numChildren())));
args.append(*getPointer(bound.expr));
tgt.expr.setown(bindTranslatedFunctionCall(rankFromOrderAtom, args));
}
//---------------------------------------------------------------------------
//-- no_ranked
void HqlCppTranslator::doBuildExprRanked(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * index = expr->queryChild(0);
IHqlExpression * list = expr->queryChild(1);
checkRankRange(index, list);
CHqlBoundExpr bound, boundIndex;
createOrderList(ctx, list, expr->queryChild(2), bound);
buildExpr(ctx, index, boundIndex);
HqlExprArray args;
args.append(*boundIndex.expr.getClear());
args.append(*createConstant(unsignedType->castFrom(false, list->numChildren())));
args.append(*getPointer(bound.expr));
tgt.expr.setown(bindTranslatedFunctionCall(rankedFromOrderAtom, args));
}
//---------------------------------------------------------------------------
//-- no_fail
void HqlCppTranslator::doBuildStmtFail(BuildCtx & ctx, IHqlExpression * expr)
{
HqlExprArray args;
args.append(*getFailCode(expr));
args.append(*getFailMessage(expr, false));
_ATOM func = expr->hasProperty(defaultAtom) ? sysFailAtom : _failIdAtom;
OwnedHqlExpr call = bindFunctionCall(func, args);
buildStmt(ctx, call);
}
void HqlCppTranslator::doBuildExprFailCode(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
OwnedHqlExpr activeFailMarker = createAttribute(activeFailureAtom);
HqlExprAssociation * matchedMarker = ctx.queryMatchExpr(activeFailMarker);
if (!matchedMarker && !ctx.queryMatchExpr(globalContextMarkerExpr))
{
if (!buildExprInCorrectContext(ctx, expr, tgt, false))
throwError1(HQLERR_FailXUsedOutsideFailContext, getOpString(expr->getOperator()));
return;
}
HqlExprArray args;
if (matchedMarker)
{
args.append(*LINK(matchedMarker->queryExpr()));
tgt.expr.setown(bindTranslatedFunctionCall(queryLocalFailCodeAtom, args));
}
else
{
tgt.expr.setown(bindTranslatedFunctionCall(queryFailCodeAtom, args));
}
}
void HqlCppTranslator::doBuildAssignFailMessage(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
OwnedHqlExpr activeFailMarker = createAttribute(activeFailureAtom);
HqlExprAssociation * matchedMarker = ctx.queryMatchExpr(activeFailMarker);
if (!matchedMarker && !ctx.queryMatchExpr(globalContextMarkerExpr))
{
CHqlBoundExpr match;
if (!buildExprInCorrectContext(ctx, expr, match, false))
throwError1(HQLERR_FailXUsedOutsideFailContext, getOpString(expr->getOperator()));
assign(ctx, target, match);
return;
}
_ATOM func = getFailMessageAtom;
HqlExprArray args;
if (matchedMarker)
{
func = getLocalFailMessageAtom;
args.append(*createTranslated(matchedMarker->queryExpr()));
}
LinkedHqlExpr tag = expr->queryChild(0);
if (!tag)
tag.setown(createQuoted("0", makeConstantModifier(makeReferenceModifier(makeVarStringType(UNKNOWN_LENGTH, 0, 0)))));
args.append(*LINK(tag));
OwnedHqlExpr call = bindFunctionCall(func, args);
buildExprAssign(ctx, target, call);
}
void HqlCppTranslator::doBuildAssignEventName(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
HqlExprArray args;
OwnedHqlExpr call = bindFunctionCall(getEventNameAtom, args);
buildExprAssign(ctx, target, call);
}
void HqlCppTranslator::doBuildAssignEventExtra(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
LinkedHqlExpr tag = expr->queryChild(0);
if (!tag)
tag.setown(createQuoted("0", makeConstantModifier(makeReferenceModifier(makeVarStringType(UNKNOWN_LENGTH, 0, 0)))));
HqlExprArray args;
args.append(*LINK(tag));
OwnedHqlExpr call = bindFunctionCall(getEventExtraAtom, args);
buildExprAssign(ctx, target, call);
}
//---------------------------------------------------------------------------
//-- system call e.g. EXP(), LOG()...
void HqlCppTranslator::doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, IAtom * funcName)
{
HqlExprArray args;
ForEachChild(i, expr)
{
IHqlExpression * cur = expr->queryChild(i);
if (!cur->isAttribute())
args.append(*LINK(cur));
}
OwnedHqlExpr call = bindFunctionCall(funcName, args);
buildExpr(ctx, call, tgt);
}
void HqlCppTranslator::doBuildExprOffsetOf(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (ctx.getMatchExpr(expr, tgt))
return;
IHqlExpression * arg = expr->queryChild(0);
Owned selector = buildActiveReference(ctx, arg);
selector->getOffset(ctx, tgt);
//cache non-constant values in a temporary variable...
if (!isSimpleLength(tgt.expr))
{
IHqlExpression * temp = ctx.getTempDeclare(expr->queryType(), tgt.expr);
tgt.expr.setown(temp);
ctx.associateExpr(expr, tgt);
}
}
//---------------------------------------------------------------------------
//-- no_subselect --
void HqlCppTranslator::doBuildAssignSubString(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
if (expr->queryChild(1)->getOperator() == no_rangecommon)
throwError(HQLERR_StarRangeOnlyInJoinCondition);
ITypeInfo * resultType = expr->queryType();
ITypeInfo * targetType = target.queryType();
type_t rtc = resultType->getTypeCode();
type_t ttc = targetType->getTypeCode();
SubStringInfo info(expr);
CHqlBoundExpr newBound;
bool doneAssign = false;
if (info.special)
doBuildExprSpecialSubString(ctx, info, newBound);
else if (info.infiniteString)
doBuildExprInfiniteSubString(ctx, info, newBound);
if (!newBound.expr)
{
_ATOM func = NULL;
type_t stc = info.src->queryType()->getTypeCode();
if (target.isFixedSize())
{
switch (ttc)
{
case type_qstring:
if (stc == type_qstring)
func = subQStrFTAtom;
break;
case type_data:
switch (stc)
{
case type_data:
case type_string:
case type_varstring:
func = subDataFTAtom;
break;
}
break;
case type_string:
switch (stc)
{
case type_data:
func = subDataFTAtom;
break;
case type_string:
case type_varstring:
if (resultType->queryCharset() == info.src->queryType()->queryCharset())
func = subStrFTAtom;
break;
}
break;
}
if (!func && (queryUnqualifiedType(resultType) != queryUnqualifiedType(targetType)))
{
CHqlBoundExpr bound;
buildTempExpr(ctx, expr, bound);
assign(ctx, target, bound);
return;
}
}
CHqlBoundExpr boundSrc;
buildCachedExpr(ctx, info.src, boundSrc);
info.bindToFrom(*this, ctx);
ITypeInfo * sourceType = boundSrc.queryType();
if (!info.boundFrom.expr)
info.boundFrom.expr.setown(getSizetConstant(1));
//Some hacks to force the parameters/return values to the same type. It could be solved more cleanly,
//but with more functions by calling different functions instead.
CHqlBoundTarget tempTarget;
tempTarget.set(target);
switch (rtc)
{
case type_string:
if (resultType->queryCollation()->queryName() != asciiAtom)
{
unsigned sourceLen = boundSrc.queryType()->getStringLen();
boundSrc.expr.setown(createValue(no_typetransfer, makeStringType(sourceLen, NULL, NULL), LINK(boundSrc.expr)));
OwnedITypeInfo newTargetType = makeStringType(targetType->getStringLen(), NULL, NULL);
tempTarget.expr.setown(createValue(no_typetransfer, cloneModifiers(targetType, newTargetType), LINK(tempTarget.expr)));
}
break;
}
HqlExprArray args;
args.append(*boundSrc.getTranslatedExpr());
args.append(*info.boundFrom.getTranslatedExpr());
if (func)
{
args.add(*createTranslated(tempTarget.expr), 0);
if (info.boundTo.expr)
args.append(*info.boundTo.getTranslatedExpr());
else
args.append(*createConstant(unsignedType->castFrom(false, 0x7fffffff)));
OwnedHqlExpr call = bindFunctionCall(func, args);
buildStmt(ctx, call);
}
else
{
if (info.boundTo.expr)
{
args.append(*info.boundTo.getTranslatedExpr());
switch (rtc)
{
case type_qstring:
func = subQStrFTXAtom;
break;
case type_data:
func = subDataFTXAtom;
break;
case type_unicode:
case type_varunicode:
func = unicodeSubStrFTXAtom;
break;
case type_utf8:
func = utf8SubStrFTXAtom;
break;
default:
func = subStrFTXAtom;
break;
}
}
else
{
switch (rtc)
{
case type_qstring:
func = subQStrFXAtom;
break;
case type_data:
func = subDataFXAtom;
break;
case type_unicode:
case type_varunicode:
func = unicodeSubStrFXAtom;
break;
case type_utf8:
func = utf8SubStrFXAtom;
break;
default:
func = subStrFXAtom;
break;
}
}
OwnedHqlExpr call = bindFunctionCall(func, args);
buildExprAssign(ctx, tempTarget, call);
}
doneAssign = true;
}
if (!doneAssign)
assign(ctx, target, newBound);
}
bool HqlCppTranslator::doBuildExprSpecialSubString(BuildCtx & ctx, SubStringInfo & info, CHqlBoundExpr & tgt)
{
unsigned size = info.srcType->getStringLen();
unsigned fromIndex = info.fixedStart;
unsigned toIndex = info.fixedEnd;
//If substring is larger than the source use the default processing.
if (toIndex <= size)
{
CHqlBoundExpr boundSrc;
buildCachedExpr(ctx, info.src, boundSrc);
boundSrc.expr.setown(getIndexedElementPointer(boundSrc.expr, fromIndex-1));
unsigned newLength = fromIndex <= toIndex ? toIndex-(fromIndex-1) : 0;
ITypeInfo * newType = makeReferenceModifier(getStretchedType(newLength, info.srcType));
tgt.expr.setown(createValue(no_typetransfer, newType, boundSrc.expr.getClear()));
if (info.expr->queryType()->getStringLen() != newLength)
tgt.length.setown(getSizetConstant(newLength));
return true;
}
return false;
}
bool HqlCppTranslator::doBuildExprInfiniteSubString(BuildCtx & ctx, SubStringInfo & info, CHqlBoundExpr & tgt)
{
CHqlBoundExpr boundSrc;
info.bindToFrom(*this, ctx);
buildCachedExpr(ctx, info.src, boundSrc);
IHqlExpression * from = info.from;
if (info.fixedStart == 1)
from = NULL;
IHqlExpression * start = from ? adjustValue(info.boundFrom.expr, -1) : NULL;
tgt.expr.setown(getIndexedElementPointer(boundSrc.expr, start));
//ensure type is no longer infinite length, so same optimization does not happen again...
ITypeInfo * newType = makeReferenceModifier(getStretchedType(UNKNOWN_LENGTH, tgt.expr->queryType()));
tgt.expr.setown(createValue(no_typetransfer, newType, tgt.expr.getLink()));
OwnedHqlExpr length;
if (start && !isZero(start))
length.setown(createValue(no_sub, info.boundTo.expr.getLink(), LINK(start)));
else
length.setown(info.boundTo.expr.getLink());
tgt.length.setown(ensureExprType(length, sizetType));
::Release(start);
return true;
}
void HqlCppTranslator::doBuildExprAnySubString(BuildCtx & ctx, SubStringInfo & info, CHqlBoundExpr & tgt)
{
CHqlBoundExpr boundSource;
buildCachedExpr(ctx, info.src, boundSource);
info.bindToFrom(*this, ctx);
OwnedHqlExpr from;
if (info.from)
{
OwnedHqlExpr start = adjustValue(info.boundFrom.expr, -1);
if (!isZero(start))
{
HqlExprArray args;
args.append(*LINK(start));
args.append(*getBoundLength(boundSource));
OwnedHqlExpr call = bindTranslatedFunctionCall(rtlMinAtom, args);
call.setown(createTranslated(call));
CHqlBoundExpr fromVar;
buildTempExpr(ctx, call, fromVar);
from.set(fromVar.expr);
}
}
OwnedHqlExpr to;
if (info.to)
{
OwnedHqlExpr toExpr = LINK(info.boundTo.expr);
if (from)
{
HqlExprArray args;
args.append(*LINK(toExpr));
args.append(*LINK(from));
toExpr.setown(bindTranslatedFunctionCall(rtlMaxAtom, args));
}
HqlExprArray args;
args.append(*LINK(toExpr));
args.append(*getBoundLength(boundSource));
to.setown(bindTranslatedFunctionCall(rtlMinAtom, args));
}
else
to.setown(getBoundLength(boundSource));
boundSource.expr.setown(getIndexedElementPointer(boundSource.expr, from));
ITypeInfo * newType = makeReferenceModifier(info.expr->getType());
tgt.expr.setown(createValue(no_typetransfer, newType, boundSource.expr.getClear()));
if (from && !isZero(from))
tgt.length.setown(createValue(no_sub, LINK(sizetType), LINK(to), LINK(from)));
else
tgt.length.set(to);
}
void HqlCppTranslator::doBuildExprSubString(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
if (expr->queryChild(1)->getOperator() == no_rangecommon)
throwError(HQLERR_StarRangeOnlyInJoinCondition);
/* Optimize string[start..end] into a type transfer where appropriate */
SubStringInfo info(expr);
if (info.special)
if (doBuildExprSpecialSubString(ctx, info, tgt))
return;
if (info.infiniteString)
if (doBuildExprInfiniteSubString(ctx, info, tgt))
return;
if (expr->hasProperty(quickAtom))
{
doBuildExprAnySubString(ctx, info, tgt);
return;
}
buildTempExpr(ctx, expr, tgt);
}
//---------------------------------------------------------------------------
//-- no_trim --
void HqlCppTranslator::doBuildAssignTrim(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
IHqlExpression * str = expr->queryChild(0);
_ATOM func;
bool hasAll = expr->hasProperty(allAtom);
bool hasLeft = expr->hasProperty(leftAtom);
bool hasRight = expr->hasProperty(rightAtom);
if (str->queryType()->getTypeCode() == type_varstring)
{
if(hasAll)
func = trimVAllAtom;
else if(hasLeft && hasRight)
func = trimVBothAtom;
else if(hasLeft)
func = trimVLeftAtom;
else
func = trimVRightAtom;
}
else if(str->queryType()->getTypeCode() == type_unicode)
{
if(hasAll)
func = trimUnicodeAllAtom;
else if(hasLeft && hasRight)
func = trimUnicodeBothAtom;
else if(hasLeft)
func = trimUnicodeLeftAtom;
else
func = trimUnicodeRightAtom;
}
else if(str->queryType()->getTypeCode() == type_varunicode)
{
if(hasAll)
func = trimVUnicodeAllAtom;
else if(hasLeft && hasRight)
func = trimVUnicodeBothAtom;
else if(hasLeft)
func = trimVUnicodeLeftAtom;
else
func = trimVUnicodeRightAtom;
}
else if(str->queryType()->getTypeCode() == type_utf8)
{
if(hasAll)
func = trimUtf8AllAtom;
else if(hasLeft && hasRight)
func = trimUtf8BothAtom;
else if(hasLeft)
func = trimUtf8LeftAtom;
else
func = trimUtf8RightAtom;
}
else
{
if(hasAll)
func = trimAllAtom;
else if(hasLeft && hasRight)
func = trimBothAtom;
else if(hasLeft)
func = trimLeftAtom;
else
func = trimRightAtom;
}
HqlExprArray args;
args.append(*LINK(str));
OwnedHqlExpr call = bindFunctionCall(func, args);
buildExprAssign(ctx, target, call);
}
void HqlCppTranslator::doBuildExprTrim(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
// MORE - support LEFT,RIGHT,ALL attributes
CHqlBoundExpr bound;
buildSimpleExpr(ctx, expr->queryChild(0), bound);
HqlExprArray args;
_ATOM func;
OwnedHqlExpr str = getElementPointer(bound.expr);
bool hasAll = expr->hasProperty(allAtom);
bool hasLeft = expr->hasProperty(leftAtom);
bool hasRight = expr->hasProperty(rightAtom);
type_t btc = bound.expr->queryType()->getTypeCode();
if(hasAll || hasLeft)
{
if (btc == type_varstring)
{
if(hasAll) {
func = trimVAllAtom;
}
else if(hasLeft && hasRight) {
func = trimVBothAtom;
}
else {
func = trimVLeftAtom;
}
}
else if (btc == type_unicode)
{
if(hasAll) {
func = trimUnicodeAllAtom;
}
else if(hasLeft && hasRight) {
func = trimUnicodeBothAtom;
}
else {
func = trimUnicodeLeftAtom;
}
}
else if (btc == type_varunicode)
{
if(hasAll) {
func = trimVUnicodeAllAtom;
}
else if(hasLeft && hasRight) {
func = trimVUnicodeBothAtom;
}
else {
func = trimVUnicodeLeftAtom;
}
}
else if (btc == type_utf8)
{
if(hasAll) {
func = trimUtf8AllAtom;
}
else if(hasLeft && hasRight) {
func = trimUtf8BothAtom;
}
else {
func = trimUtf8LeftAtom;
}
}
else
{
if(hasAll) {
func = trimAllAtom;
}
else if(hasLeft && hasRight) {
func = trimBothAtom;
}
else {
func = trimLeftAtom;
}
}
args.append(*bound.getTranslatedExpr());
OwnedHqlExpr call = bindFunctionCall(func, args);
buildExpr(ctx, call, tgt);
}
else {
if (btc == type_varstring)
{
args.append(*LINK(str));
func = trimVStrLenAtom;
}
else if (btc == type_unicode)
{
args.append(*getBoundLength(bound));
args.append(*LINK(str));
func = trimUnicodeStrLenAtom;
}
else if (btc == type_varunicode)
{
args.append(*LINK(str));
func = trimVUnicodeStrLenAtom;
}
else if (btc == type_utf8)
{
args.append(*getBoundLength(bound));
args.append(*LINK(str));
func = trimUtf8StrLenAtom;
}
else
{
args.append(*getBoundLength(bound));
args.append(*LINK(str));
func = trimStrLenAtom;
}
tgt.length.setown(bindTranslatedFunctionCall(func, args));
tgt.expr.set(str);
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprIsValid(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
IHqlExpression * const value = expr->queryChild(0);
HqlExprArray args;
ITypeInfo * type = value->queryType();
assertex(type);
if (type->getTypeCode() == type_alien)
{
IHqlAlienTypeInfo * alien = queryAlienType(type);
IHqlExpression * isValidFunction = alien->queryFunction(getIsValidAtom);
if (isValidFunction)
{
CHqlBoundExpr bound;
buildAddress(ctx, value, bound);
OwnedITypeInfo physicalType = alien->getPhysicalType();
if (!isTypePassedByAddress(physicalType))
bound.expr.setown(createValue(no_deref, makeReferenceModifier(LINK(physicalType)), LINK(bound.expr)));
HqlExprArray args;
args.append(*bound.getTranslatedExpr());
OwnedHqlExpr test = createBoundFunction(NULL, isValidFunction, args, NULL, true);
buildExpr(ctx, test, tgt);
return;
}
else
type = alien->queryLogicalType();
}
CHqlBoundExpr bound;
buildExpr(ctx, value, bound);
ensureHasAddress(ctx, bound);
OwnedHqlExpr address = getPointer(bound.expr);
switch (type->getTypeCode())
{
case type_decimal:
args.append(*createConstant(type->isSigned()));
args.append(*getSizetConstant(type->getDigits()));
args.append(*address.getLink());
tgt.expr.setown(bindTranslatedFunctionCall(DecValidAtom, args));
break;
case type_real:
args.append(*getSizetConstant(type->getSize()));
args.append(*address.getLink());
tgt.expr.setown(bindTranslatedFunctionCall(validRealAtom, args));
break;
default:
tgt.expr.set(queryBoolExpr(true));
break;
}
}
IHqlExpression * HqlCppTranslator::getConstWuid(IHqlExpression * expr)
{
SCMStringBuffer out;
wu()->getWuid(out);
OwnedHqlExpr wuid = createConstant(out.str());
return ensureExprType(wuid, expr->queryType());
}
void HqlCppTranslator::doBuildAssignWuid(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
HqlExprArray args;
OwnedHqlExpr call = bindFunctionCall(getWuidAtom, args);
buildExprAssign(ctx, target, call);
}
void HqlCppTranslator::doBuildExprWuid(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
HqlExprArray args;
OwnedHqlExpr call = bindFunctionCall(getWuidAtom, args);
buildTempExpr(ctx, call, tgt);
}
IHqlExpression * HqlCppTranslator::cvtGetEnvToCall(IHqlExpression * expr)
{
IHqlExpression * dft = queryRealChild(expr, 1);
HqlExprArray args;
args.append(*LINK(expr->queryChild(0)));
if (dft)
args.append(*LINK(dft));
else
args.append(*createConstant(createStringValue((const char *)NULL, 0U)));
return bindFunctionCall(getEnvAtom, args);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildAssignToFromUnicode(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
HqlExprArray args;
if(!target.isFixedSize())
{
args.append(*LINK(expr->queryChild(0)));
args.append(*foldHqlExpression(expr->queryChild(1)));
OwnedHqlExpr call = bindFunctionCall((expr->getOperator() == no_fromunicode) ? unicode2CodepageXAtom : codepage2UnicodeXAtom, args);
buildExprAssign(ctx, target, call);
}
else
{
args.append(*createTranslated(target.expr));
args.append(*LINK(expr->queryChild(0)));
args.append(*foldHqlExpression(expr->queryChild(1)));
OwnedHqlExpr call = bindFunctionCall((expr->getOperator() == no_fromunicode) ? unicode2CodepageAtom : codepage2UnicodeAtom, args);
buildStmt(ctx, call);
}
}
void HqlCppTranslator::doBuildExprToFromUnicode(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
HqlExprArray args;
args.append(*LINK(expr->queryChild(0)));
args.append(*foldHqlExpression(expr->queryChild(1)));
OwnedHqlExpr call = bindFunctionCall((expr->getOperator() == no_fromunicode) ? unicode2CodepageXAtom : codepage2UnicodeXAtom, args);
buildExpr(ctx, call, tgt);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildExprKeyUnicode(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
HqlExprArray args;
args.append(*LINK(expr->queryChild(0)));
args.append(*LINK(expr->queryChild(1)));
args.append(*LINK(expr->queryChild(2)));
OwnedHqlExpr call = bindFunctionCall(keyUnicodeStrengthXAtom, args);
buildExpr(ctx, call, tgt);
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildAssignWhich(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
{
BuildCtx whichCtx(ctx);
unsigned max = expr->numChildren();
unsigned idx;
bool invert = (expr->getOperator() == no_rejected);
for (idx = 0; idx < max; idx++)
{
IHqlExpression * cur = expr->queryChild(idx);
CHqlBoundExpr bound;
if (invert)
{
OwnedHqlExpr inverse = getInverse(cur);
buildExpr(whichCtx, inverse, bound);
}
else
buildExpr(whichCtx, cur, bound);
IHqlStmt * stmt = whichCtx.addFilter(bound.expr);
OwnedHqlExpr value = createConstant(target.queryType()->castFrom(false, idx+1));
assignBound(whichCtx, target, value);
whichCtx.selectElse(stmt);
}
assignBound(whichCtx, target, queryZero());
}
//---------------------------------------------------------------------------
void HqlCppTranslator::assignBound(BuildCtx & ctx, const CHqlBoundTarget & lhs, IHqlExpression * rhs)
{
CHqlBoundExpr bound;
bound.expr.set(rhs);
assign(ctx, lhs, bound);
}
void HqlCppTranslator::assignBoundToTemp(BuildCtx & ctx, IHqlExpression * lhs, IHqlExpression * rhs)
{
CHqlBoundExpr bound;
CHqlBoundTarget target;
bound.expr.set(rhs);
target.expr.set(lhs);
assign(ctx, target, bound);
}
void HqlCppTranslator::assign(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & rhs)
{
if (!target.isFixedSize())
{
assignCastUnknownLength(ctx, target, rhs);
return;
}
IHqlExpression * lhs = target.expr;
ITypeInfo * lType = lhs->queryType()->queryPromotedType();
if (!isSameBasicType(lType, rhs.expr->queryType()->queryPromotedType()))
assignAndCast(ctx, target, rhs);
else
{
switch (lType->getTypeCode())
{
case type_decimal:
if (isPushed(rhs))
{
_ATOM funcName = lType->isSigned() ? DecPopDecimalAtom : DecPopUDecimalAtom;
HqlExprArray args;
args.append(*getPointer(lhs));
args.append(*getSizetConstant(lType->getSize()));
args.append(*getSizetConstant(lType->getPrecision()));
callProcedure(ctx, funcName, args);
return;
}
buildBlockCopy(ctx, lhs, rhs);
return;
case type_string:
{
if (lType->getSize() == 1 && !options.peephole)
{
OwnedHqlExpr l1 = getFirstCharacter(lhs);
OwnedHqlExpr r1 = getFirstCharacter(rhs.expr);
ctx.addAssign(l1, r1);
}
else
buildBlockCopy(ctx, lhs, rhs);
break;
}
//fall through...
case type_unicode:
case type_data:
case type_qstring:
case type_utf8:
{
buildBlockCopy(ctx, lhs, rhs);
break;
}
case type_varstring:
{
HqlExprArray args;
args.append(*getElementPointer(lhs));
args.append(*getElementPointer(rhs.expr));
callProcedure(ctx, strcpyAtom, args);
break;
}
case type_varunicode:
{
HqlExprArray args;
args.append(*getElementPointer(lhs));
args.append(*getElementPointer(rhs.expr));
callProcedure(ctx, unicodeStrcpyAtom, args);
break;
}
case type_row:
{
if (hasWrapperModifier(target.queryType()))
{
//I can't think of any situation where this isn't true....
assertex(hasLinkCountedModifier(rhs.expr));
StringBuffer assignText;
generateExprCpp(assignText, lhs).append(".set(");
generateExprCpp(assignText, rhs.expr).append(");");
ctx.addQuoted(assignText);
//Could generate the following instead
//ctx.addAssign(lhs, no_link(rhs.expr));
//And post-optimize to the above.
}
else
ctx.addAssign(lhs, rhs.expr);
break;
}
default:
ctx.addAssign(lhs, rhs.expr);
break;
}
}
}
void HqlCppTranslator::doStringTranslation(BuildCtx & ctx, ICharsetInfo * tgtset, ICharsetInfo * srcset, unsigned tgtlen, IHqlExpression * srclen, IHqlExpression * target, IHqlExpression * src)
{
HqlExprArray args;
ITranslationInfo * translator = queryDefaultTranslation(tgtset, srcset);
if (translator)
{
_ATOM func = createIdentifierAtom(translator->queryRtlFunction());
args.append(*getSizetConstant(tgtlen));
args.append(*getElementPointer(target));
args.append(*LINK(srclen));
args.append(*getElementPointer(src));
callProcedure(ctx, func, args);
}
}
void HqlCppTranslator::assignSwapInt(BuildCtx & ctx, ITypeInfo * to, const CHqlBoundTarget & target, CHqlBoundExpr & pure)
{
switch (pure.expr->getOperator())
{
case no_deref:
case no_variable:
break;
default:
{
OwnedHqlExpr translated = pure.getTranslatedExpr();
pure.clear();
buildTempExpr(ctx, translated, pure);
break;
}
}
ITypeInfo * from = pure.expr->queryType();
unsigned copySize = to->getSize();
assertex(copySize == from->getSize());
IHqlExpression * address = getRawAddress(pure.expr);
switch (copySize)
{
case 1:
ctx.addAssign(target.expr, pure.expr);
break;
default:
{
HqlExprArray args;
args.append(*address);
OwnedHqlExpr call = bindTranslatedFunctionCall(reverseIntAtom[copySize][to->isSigned()], args);
ctx.addAssign(target.expr, call);
break;
}
}
}
void HqlCppTranslator::throwCannotCast(ITypeInfo * from, ITypeInfo * to)
{
StringBuffer fromText, toText;
getFriendlyTypeStr(from, fromText);
getFriendlyTypeStr(to, toText);
throwError2(HQLERR_CastXNotImplemented, fromText.str(), toText.str());
}
void HqlCppTranslator::assignAndCast(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & pure)
{
if (!target.isFixedSize())
{
assignCastUnknownLength(ctx, target, pure);
return;
}
ITypeInfo * to = target.queryType()->queryPromotedType();
if ((pure.expr->getOperator() == no_constant) && options.foldConstantCast &&
((options.inlineStringThreshold == 0) || (to->getSize() <= options.inlineStringThreshold)))
{
OwnedHqlExpr cast = getCastExpr(to, pure.expr);
if (cast)
{
assignBound(ctx, target, cast);
return;
}
}
//NB: Does not include variable length return types....
ITypeInfo * from = (pure.expr->queryType()->queryPromotedType());
type_t toType = to->getTypeCode();
type_t fromType = from->getTypeCode();
unsigned toSize = to->getSize();
IHqlExpression * targetVar = target.expr;
HqlExprArray args;
assertex(targetVar);
assertex(toSize != UNKNOWN_LENGTH);
switch(toType)
{
case type_qstring:
switch (fromType)
{
case type_qstring:
{
unsigned srcsize = from->getSize();
if (!pure.length && (srcsize == toSize))
{
//memcpy(tgt, src, srclen)
args.append(*getElementPointer(targetVar));
args.append(*getElementPointer(pure.expr));
args.append(*getSizetConstant(toSize));
callProcedure(ctx, memcpyAtom, args);
}
else
{
args.append(*getSizetConstant(to->getStringLen()));
args.append(*getElementPointer(targetVar));
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, qstrToQStrAtom, args);
}
break;
}
case type_data:
case type_varstring:
case type_string:
{
if(!queryDefaultTranslation(to->queryCharset(), from->queryCharset()))
{
args.append(*getSizetConstant(to->getStringLen()));
args.append(*getElementPointer(targetVar));
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, strToQStrAtom, args);
break;
}
//fall through
}
default:
//Need to go via a temporary string.
OwnedHqlExpr temp = pure.getTranslatedExpr();
buildExprAssignViaString(ctx, target, temp, to->getStringLen());
return;
}
break;
case type_data:
case type_string:
case type_varstring:
{
unsigned srclen = from->getSize();
ICharsetInfo * srcset = NULL;
ICharsetInfo * tgtset = to->queryCharset();
switch (fromType)
{
case type_data:
case type_string:
case type_varstring:
{
srcset = from->queryCharset();
OwnedHqlExpr boundLen = getBoundLength(pure);
if ((srcset == tgtset) || (toType == type_data) || (fromType == type_data))
{
bool doDefault = true;
if (boundLen->queryValue())
{
unsigned srclen = (unsigned)boundLen->queryValue()->getIntValue();
if (srclen >= toSize && toType != type_varstring)
{
if (srclen > toSize)
srclen = toSize;
//memcpy(tgt, src, srclen)
args.append(*getElementPointer(targetVar));
args.append(*getElementPointer(pure.expr));
args.append(*getSizetConstant(srclen));
callProcedure(ctx, memcpyAtom, args);
doDefault = false;
}
}
if (doDefault)
{
if (fromType == type_varstring)
{
_ATOM func;
switch (toType)
{
case type_varstring: func = vstr2VStrAtom; break;
case type_string: func = vstr2StrAtom; break;
case type_data: func = vstr2DataAtom; break;
default: UNIMPLEMENTED;
}
if ((toSize < srclen) || (srclen==UNKNOWN_LENGTH) || (toType != type_varstring))
{
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, func, args);
}
else
{
//strcpy(tgt, src);
args.append(*getElementPointer(targetVar));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, strcpyAtom, args);
}
}
else
{
_ATOM func;
switch (toType)
{
case type_data:
func = str2DataAtom;
break;
case type_varstring:
func = str2VStrAtom;
break;
case type_string:
func = (srcset->queryName() == ebcdicAtom) ? estr2EStrAtom : str2StrAtom;
break;
}
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
args.append(*LINK(boundLen));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, func, args);
}
}
}
else
{
if ((from->getSize() == INFINITE_LENGTH) && !pure.length)
throwError(HQLERR_CastInfiniteString);
IHqlExpression * srclen;
if (toType == type_varstring)
{
srclen = getSizetConstant(toSize);
args.append(*srclen);
args.append(*getElementPointer(targetVar));
args.append(*LINK(boundLen));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, estr2VStrAtom, args);
}
else
doStringTranslation(ctx, tgtset, srcset, toSize, boundLen, targetVar, pure.expr);
}
}
break;
case type_qstring:
if (queryDefaultTranslation(tgtset, from->queryCharset()))
{
OwnedHqlExpr temp = pure.getTranslatedExpr();
buildExprAssignViaString(ctx, target, temp, to->getStringLen());
}
else
{
_ATOM func;
switch (toType)
{
case type_varstring: func = qstr2VStrAtom; break;
case type_string: func = qstr2StrAtom; break;
case type_data: func = qstr2DataAtom; break;
}
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
callProcedure(ctx, func, args);
}
break;
case type_unicode:
case type_varunicode:
{
_ATOM func;
switch(toType)
{
case type_data:
func = (fromType == type_varunicode) ? vunicode2DataAtom : unicode2DataAtom;
break;
case type_string:
func = (fromType == type_varunicode) ? vunicode2CodepageAtom : unicode2CodepageAtom;
break;
case type_varstring:
func = (fromType == type_varunicode) ? vunicode2VCodepageAtom : unicode2VCodepageAtom;
break;
}
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
if(fromType != type_varunicode)
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
if(toType != type_data)
args.append(*createConstant(to->queryCharset()->queryCodepageName()));
callProcedure(ctx, func, args);
}
break;
case type_utf8:
{
_ATOM func;
switch(toType)
{
case type_data:
func = utf82DataAtom;
break;
case type_string:
func = utf82CodepageAtom;
break;
case type_varstring:
OwnedHqlExpr temp = pure.getTranslatedExpr();
buildExprAssignViaString(ctx, target, temp, to->getStringLen());
return;
}
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
if(toType != type_data)
args.append(*createConstant(to->queryCharset()->queryCodepageName()));
callProcedure(ctx, func, args);
}
break;
case type_swapint:
{
CHqlBoundExpr recast;
ITypeInfo * tempType = makeIntType(srclen, from->isSigned());
OwnedHqlExpr translated = createValue(no_implicitcast, tempType, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignAndCast(ctx, target, recast);
return;
}
case type_int:
case type_packedint:
{
//l2an4(toSize, tgt, expr);
_ATOM funcName;
if (from->isSigned())
{
if (toType != type_varstring)
funcName = (srclen > 4 ? ls82anAtom : ls42anAtom);
else
funcName = (srclen > 4 ? ls82vnAtom : ls42vnAtom);
}
else
{
if (toType != type_varstring)
funcName = (srclen > 4 ? l82anAtom : l42anAtom);
else
funcName = (srclen > 4 ? l82vnAtom : l42vnAtom);
}
IHqlExpression * strlen = getSizetConstant(toSize);
args.append(*strlen);
args.append(*getElementPointer(targetVar));
args.append(*LINK(pure.expr));
callProcedure(ctx, funcName, args);
if (toType != type_data)
{
Owned charset = getCharset(asciiAtom);
doStringTranslation(ctx, tgtset, charset, toSize, strlen, targetVar, targetVar);
}
break;
}
case type_void:
if (pure.expr->getOperator() != no_decimalstack)
{
throwCannotCast(from, to);
break;
}
//fall through
case type_decimal:
{
ensurePushed(ctx, pure);
args.append(*getSizetConstant(toSize));
OwnedHqlExpr sp = getElementPointer(targetVar);
args.append(*ensureIndexable(sp));
_ATOM func;
switch (toType)
{
case type_string: func = DecPopStringAtom; break;
case type_data: func = DecPopStringAtom; break;
case type_varstring: func = DecPopVStringAtom; break;
}
callProcedure(ctx, func, args);
break;
}
case type_enumerated:
throwCannotCast(from, to);
break;
case type_boolean:
{
_ATOM func = (toType == type_varstring) ? bool2VStrAtom : (toType == type_data) ? bool2DataAtom : bool2StrAtom;
args.append(*getSizetConstant(toSize));
args.append(*getElementPointer(targetVar));
args.append(*pure.expr.getLink());
callProcedure(ctx, func, args);
break;
}
case type_real:
{
IHqlExpression * strlen = getSizetConstant(toSize);
args.append(*strlen);
args.append(*getElementPointer(targetVar));
args.append(*pure.expr.getLink());
_ATOM func = (toType == type_varstring) ? f2vnAtom : f2anAtom;
callProcedure(ctx, func, args);
if (toType != type_data)
{
Owned charset = getCharset(asciiAtom);
doStringTranslation(ctx, tgtset, charset, toSize, strlen, targetVar, targetVar);
}
}
break;
default:
throwCannotCast(from, to);
break;
}
}
break;
case type_unicode:
case type_varunicode:
switch (fromType)
{
case type_unicode:
case type_varunicode:
case type_data:
case type_string:
case type_varstring:
case type_utf8:
{
_ATOM func;
switch(fromType)
{
case type_unicode:
func = (toType == type_varunicode) ? unicode2VUnicodeAtom : unicode2UnicodeAtom;
break;
case type_varunicode:
func = (toType == type_varunicode) ? vunicode2VUnicodeAtom : vunicode2UnicodeAtom;
break;
case type_data:
pure.expr.setown(createValue(no_implicitcast, makeReferenceModifier(makeStringType(from->getStringLen(), NULL)), LINK(pure.expr)));
func = (toType == type_varunicode) ? codepage2VUnicodeAtom : codepage2UnicodeAtom;
break;
case type_string:
func = (toType == type_varunicode) ? codepage2VUnicodeAtom : codepage2UnicodeAtom;
break;
case type_varstring:
func = (toType == type_varunicode) ? vcodepage2VUnicodeAtom : vcodepage2UnicodeAtom;
break;
case type_utf8:
if (toType == type_varunicode)
{
OwnedHqlExpr temp = pure.getTranslatedExpr();
OwnedITypeInfo type = makeUnicodeType(to->getStringLen(), NULL);
buildExprAssignViaType(ctx, target, temp, type);
return;
}
func = utf82UnicodeAtom;
break;
}
args.append(*getSizetConstant(toSize/2));
args.append(*getElementPointer(targetVar));
if((fromType != type_varunicode) && (fromType != type_varstring))
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
if((fromType == type_data) || (fromType == type_string) || (fromType == type_varstring))
args.append(*createConstant(from->queryCharset()->queryCodepageName()));
callProcedure(ctx, func, args);
break;
}
default:
OwnedHqlExpr temp = pure.getTranslatedExpr();
buildExprAssignViaString(ctx, target, temp, to->getStringLen());
return;
}
break;
case type_utf8:
switch (fromType)
{
case type_unicode:
case type_varunicode:
case type_data:
case type_string:
case type_varstring:
case type_utf8:
{
_ATOM func;
switch(fromType)
{
case type_unicode:
case type_varunicode:
func = unicodeToUtf8Atom;
break;
case type_utf8:
func = utf8ToUtf8Atom;
break;
case type_data:
case type_string:
case type_varstring:
func = codepageToUtf8Atom;
break;
}
args.append(*getSizetConstant(toSize/4));
args.append(*getElementPointer(targetVar));
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
if((fromType == type_data) || (fromType == type_string) || (fromType == type_varstring))
args.append(*createConstant(from->queryCharset()->queryCodepageName()));
callProcedure(ctx, func, args);
break;
}
default:
OwnedHqlExpr temp = pure.getTranslatedExpr();
buildExprAssignViaString(ctx, target, temp, to->getStringLen());
return;
}
break;
case type_decimal:
{
CHqlBoundExpr cast;
doBuildExprCast(ctx, to, pure, cast);
ensurePushed(ctx, cast);
_ATOM funcName = to->isSigned() ? DecPopDecimalAtom : DecPopUDecimalAtom;
args.append(*getPointer(target.expr));
args.append(*getSizetConstant(to->getSize()));
args.append(*getSizetConstant(to->getPrecision()));
callProcedure(ctx, funcName, args);
}
break;
case type_swapint:
{
unsigned fromSize = from->getSize();
if (fromType == type_int)
{
if (fromSize != toSize)
{
Owned tempType = makeIntType(toSize, from->isSigned());
pure.expr.setown(ensureExprType(pure.expr, tempType));
}
if (toSize != 1)
{
assignSwapInt(ctx, to, target, pure);
return;
}
}
CHqlBoundExpr cast;
doBuildExprCast(ctx, to, pure, cast);
ctx.addAssign(target.expr, cast.expr);
}
break;
case type_int:
case type_packedint:
{
unsigned fromSize = from->getSize();
if ((fromType == type_swapint) && !((fromSize == 1) && (toSize == 1)))
{
if (fromSize != toSize)
{
CHqlBoundExpr tempInt;
OwnedITypeInfo tempType = makeIntType(fromSize, from->isSigned());
doBuildCastViaTemp(ctx, tempType, pure, tempInt);
CHqlBoundExpr cast;
doBuildExprCast(ctx, to, tempInt, cast);
ctx.addAssign(target.expr, cast.expr);
}
else
assignSwapInt(ctx, to, target, pure);
return;
}
}
//fall through
case type_boolean:
case type_real:
case type_row:
case type_pointer:
{
CHqlBoundExpr cast;
doBuildExprCast(ctx, to, pure, cast);
ctx.addAssign(target.expr, cast.expr);
}
break;
default:
throwCannotCast(from, to);
break;
}
}
void HqlCppTranslator::assignCastUnknownLength(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & pure)
{
assertex(!target.isFixedSize());
// must be dynamically allocated return type
ITypeInfo * to = target.queryType();
ITypeInfo * from = pure.expr->queryType();
type_t toType = to->getTypeCode();
type_t fromType = from->getTypeCode();
IHqlExpression * codepageParam = 0;
HqlExprArray args;
_ATOM funcName = NULL;
// assertex(target.length && target.pointer || to->getTypeCode() == type_varstring || to->getTypeCode() == type_varunicode);
switch (toType)
{
case type_qstring:
{
switch (fromType)
{
case type_qstring:
funcName = qstrToQStrXAtom;
break;
case type_string:
case type_data:
case type_varstring:
if(!queryDefaultTranslation(to->queryCharset(), from->queryCharset()))
{
funcName = strToQStrXAtom;
break;
}
//fall through
default:
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
break;
}
case type_string:
case type_data:
{
unsigned srclen = from->getSize();
switch (fromType)
{
case type_data:
case type_string:
case type_varstring:
{
ICharsetInfo * srcset = from->queryCharset();
ICharsetInfo * tgtset = to->queryCharset();
if (to->getTypeCode() == type_data)
funcName = str2DataXAtom;
else if ((srcset == tgtset) || (from->getTypeCode() == type_data))
{
funcName = str2StrXAtom;
}
else
{
if ((from->getSize() == INFINITE_LENGTH) && !pure.length)
throwError(HQLERR_CastInfiniteString);
ITranslationInfo * translator = queryDefaultTranslation(tgtset, srcset);
funcName = createIdentifierAtom(translator->queryVarRtlFunction());
}
}
break;
case type_qstring:
if(!queryDefaultTranslation(from->queryCharset(), to->queryCharset()))
{
funcName = (toType == type_data) ? qstr2DataXAtom : qstr2StrXAtom;
break;
}
else
{
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
case type_unicode:
{
if(toType == type_data)
funcName = unicode2DataXAtom;
else
{
funcName = unicode2CodepageXAtom;
codepageParam = createConstant(to->queryCharset()->queryCodepageName());
}
break;
}
case type_varunicode:
{
if(toType == type_data)
funcName = vunicode2DataXAtom;
else
{
funcName = vunicode2CodepageXAtom;
codepageParam = createConstant(to->queryCharset()->queryCodepageName());
}
break;
}
case type_utf8:
{
if(toType == type_data)
funcName = utf82DataXAtom;
else
{
funcName = utf82CodepageXAtom;
codepageParam = createConstant(to->queryCharset()->queryCodepageName());
}
break;
}
case type_swapint:
{
CHqlBoundExpr recast;
ITypeInfo * type = makeIntType(from->getSize(), from->isSigned());
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
case type_int:
case type_real:
case type_boolean:
case type_packedint:
{
Owned asciiCharset = getCharset(asciiAtom);
if (to->queryCharset() != asciiCharset)
{
//This should really be handled by the call processing.
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
if (fromType == type_real)
funcName = f2axAtom;
else if (fromType == type_boolean)
funcName = bool2StrXAtom;
else if (from->isSigned())
funcName = (srclen > 4 ? ls82axAtom : ls42axAtom);
else
funcName = (srclen > 4 ? l82axAtom : l42axAtom);
args.append(*pure.getTranslatedExpr());
OwnedHqlExpr call = bindFunctionCall(funcName, args);
buildExprAssign(ctx, target, call);
return;
}
case type_void:
if (pure.expr->getOperator() != no_decimalstack)
{
throwCannotCast(from, to);
break;
}
//fall through
case type_decimal:
{
ensurePushed(ctx, pure);
OwnedHqlExpr call = bindFunctionCall(DecPopStringXAtom, args);
buildExprAssign(ctx, target, call);
return;
}
default:
assertex(!"Unknown copy source type");
return;
}
break;
}
case type_varstring:
{
unsigned srclen = from->getSize();
switch (from->getTypeCode())
{
case type_data:
case type_string:
case type_varstring:
{
ICharsetInfo * srcset = from->queryCharset();
ICharsetInfo * tgtset = to->queryCharset();
if ((srcset == tgtset) || (to->getTypeCode() == type_data) || (from->getTypeCode() == type_data))
{
funcName = str2VStrXAtom;
}
else
{
funcName = estr2VStrXAtom;
}
}
break;
case type_unicode:
{
funcName = unicode2VCodepageXAtom;
codepageParam = createConstant(to->queryCharset()->queryCodepageName());
}
break;
case type_varunicode:
{
funcName = vunicode2VCodepageXAtom;
codepageParam = createConstant(to->queryCharset()->queryCodepageName());
}
break;
case type_qstring:
case type_utf8:
{
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(from->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
case type_swapint:
{
CHqlBoundExpr recast;
ITypeInfo * type = makeIntType(from->getSize(), from->isSigned());
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
case type_int:
case type_packedint:
{
//l2an4(tgtlen, tgt, expr);
if (from->isSigned())
funcName = (srclen > 4 ? ls82vxAtom : ls42vxAtom);
else
funcName = (srclen > 4 ? l82vxAtom : l42vxAtom);
break;
}
case type_boolean:
{
funcName = bool2VStrXAtom;
break;
}
case type_real:
{
funcName = f2vxAtom;;
break;
}
case type_void:
if (pure.expr->getOperator() != no_decimalstack)
{
throwCannotCast(from, to);
break;
}
//fall through
case type_decimal:
{
ensurePushed(ctx, pure);
OwnedHqlExpr call = bindFunctionCall(DecPopVStringXAtom, args);
buildExprAssign(ctx, target, call);
return;
}
default:
assertex(!"Unknown copy source type");
return;
}
break;
}
case type_unicode:
{
switch (fromType)
{
case type_unicode:
funcName = unicode2UnicodeXAtom;
break;
case type_varunicode:
funcName = vunicode2UnicodeXAtom;
break;
case type_utf8:
funcName = utf82UnicodeXAtom;
break;
case type_data:
funcName = codepage2UnicodeXAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_implicitcast, makeStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
case type_string:
funcName = codepage2UnicodeXAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_typetransfer, makeStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
case type_varstring:
funcName = vcodepage2UnicodeXAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_typetransfer, makeVarStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
default:
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
break;
}
case type_varunicode:
{
switch (fromType)
{
case type_unicode:
case type_utf8: // go via unicode
funcName = unicode2VUnicodeXAtom;
break;
case type_varunicode:
funcName = vunicode2VUnicodeXAtom;
break;
case type_string:
case type_data:
funcName = codepage2VUnicodeXAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_typetransfer, makeStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
case type_varstring:
funcName = vcodepage2VUnicodeXAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_typetransfer, makeVarStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
default:
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
break;
}
case type_utf8:
{
switch (fromType)
{
case type_unicode:
case type_varunicode:
funcName = unicodeToUtf8XAtom;
break;
case type_utf8:
funcName = utf8ToUtf8XAtom;
break;
case type_string:
case type_data:
case type_varstring:
funcName = codepageToUtf8XAtom;
codepageParam = createConstant(from->queryCharset()->queryCodepageName());
pure.expr.setown(createValue(no_typetransfer, makeStringType(from->getStringLen(), NULL, NULL), LINK(pure.expr)));
break;
default:
CHqlBoundExpr recast;
ITypeInfo * type = makeStringType(to->getStringLen(), NULL, NULL);
OwnedHqlExpr translated = createValue(no_implicitcast, type, pure.getTranslatedExpr());
buildExpr(ctx, translated, recast);
assignCastUnknownLength(ctx, target, recast);
return;
}
break;
}
case type_set:
if (isSameBasicType(to->queryChildType(), from->queryChildType()))
{
if (!target.isAll)
{
//Ugly. Create a dummy isAll field to assign to..
assertex(!pure.isAll || matchesBoolean(pure.isAll, false));
CHqlBoundTarget tempTarget;
tempTarget.set(target);
tempTarget.isAll.setown(ctx.getTempDeclare(queryBoolType(), NULL));
assignCastUnknownLength(ctx, tempTarget, pure);
return;
}
funcName = set2SetXAtom;
}
else
{
OwnedHqlExpr values = pure.getTranslatedExpr();
buildSetAssignViaBuilder(ctx, target, values);
return;
}
break;
case type_table:
case type_groupedtable:
{
OwnedHqlExpr src = pure.getTranslatedExpr();
buildDatasetAssign(ctx, target, src);
return;
}
default:
assertex(!"Unexpected target type for variable length");
break;
}
args.append(*pure.getTranslatedExpr());
if(codepageParam)
args.append(*codepageParam);
OwnedHqlExpr call = bindFunctionCall(funcName, args);
buildExprAssign(ctx, target, call);
}
void HqlCppTranslator::expandFunctions(bool expandInline)
{
if (expandInline)
{
BuildCtx ctx(*code, prototypeAtom);
ForEachItemIn(idx, code->helpers)
{
IHqlExpression & cur = (IHqlExpression &)code->helpers.item(idx);
StringBuffer init;
if (getProperty(cur.queryChild(0), initfunctionAtom, init))
{
StringBuffer initproto("extern \"C\" void SERVICE_API ");
initproto.append(init).append("(const char *);");
ctx.addQuoted(initproto);
}
expandFunctionPrototype(ctx, &cur, false);
}
}
else
{
CIArray includes;
BuildCtx ctx(*code, includeAtom);
ForEachItemIn(idx, code->helpers)
{
//StringBuffer include;
//IHqlExpression & cur = (IHqlExpression &)code->helpers.item(idx);
// getLibraryName(cur, include);
//MORE!! Get the include name...
}
}
}
void HqlCppTranslator::bindAndPush(BuildCtx & ctx, IHqlExpression * value)
{
CHqlBoundExpr bound;
buildExpr(ctx, value, bound);
ensurePushed(ctx, bound);
}
bool HqlCppTranslator::ensurePushed(BuildCtx & ctx, const CHqlBoundExpr & pure)
{
if (!isPushed(pure))
{
//Temporary solution - create a critical block whenever the decimals are used.
OwnedHqlExpr marker = createAttribute(decimalAtom);
if (!ctx.queryMatchExpr(marker))
{
//If a group with no {} is added, we might get a name clash => make it unique
StringBuffer s;
getUniqueId(s.append("BcdCriticalBlock bcd")).append(";");
ctx.addQuoted(s);
ctx.associateExpr(marker, NULL);
}
ITypeInfo * type = pure.expr->queryType();
HqlExprArray args;
_ATOM funcName = NULL;
switch (type->getTypeCode())
{
case type_data:
case type_string:
case type_varstring:
funcName = DecPushStringAtom;
if (type->queryCharset()->queryName() == ebcdicAtom)
{
CHqlBoundExpr temp;
OwnedHqlExpr translated = pure.getTranslatedExpr();
OwnedHqlExpr cast = createValue(no_cast, getAsciiType(type), translated.getClear());
buildExpr(ctx, cast, temp);
args.append(*getBoundLength(temp));
args.append(*getElementPointer(temp.expr));
}
else
{
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
}
break;
case type_qstring:
funcName = DecPushQStringAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_unicode:
case type_varunicode:
funcName = DecPushUnicodeAtom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_utf8:
funcName = DecPushUtf8Atom;
args.append(*getBoundLength(pure));
args.append(*getElementPointer(pure.expr));
break;
case type_decimal:
funcName = type->isSigned() ? DecPushDecimalAtom : DecPushUDecimalAtom;
args.append(*getPointer(pure.expr));
args.append(*getSizetConstant(type->getSize()));
args.append(*getSizetConstant(type->getPrecision()));
break;
case type_swapint:
{
CHqlBoundExpr copyPure;
copyPure.set(pure);
//cast via intermediate int.
OwnedITypeInfo tempType = makeIntType(type->getSize(), type->isSigned());
CHqlBoundExpr boundCast;
doBuildExprCast(ctx, tempType, copyPure, boundCast);
funcName = type->isSigned() ? DecPushInt64Atom : DecPushUInt64Atom;
args.append(*boundCast.expr.getLink());
break;
}
//fall through
case type_int:
case type_packedint:
//more signed/unsigned and optimize the length...
funcName = type->isSigned() ? DecPushInt64Atom : DecPushUInt64Atom;
args.append(*pure.expr.getLink());
break;
case type_enumerated:
throwError2(HQLERR_CastXNotImplemented, "map", "decimal");
break;
case type_boolean:
funcName = DecPushLongAtom;
args.append(*pure.expr.getLink());
break;
case type_real:
funcName = DecPushRealAtom;
args.append(*pure.expr.getLink());
break;
default:
throwError2(HQLERR_CastXNotImplemented, "unknown", "varstring");
break;
}
if (funcName)
callProcedure(ctx, funcName, args);
return true;
}
return false;
}
static StringBuffer & appendCapital(StringBuffer & s, StringBuffer & _name)
{
const char * name = _name.str();
if (name && name[0])
{
s.append((char)toupper(*name));
s.append(name+1);
}
return s;
}
void HqlCppTranslator::expandFunctionReturnType(StringBuffer & prefix, StringBuffer & params, ITypeInfo * retType, IHqlExpression * attrs)
{
type_t tc = retType->getTypeCode();
switch (tc)
{
case type_varstring:
case type_varunicode:
if (retType->getSize() == UNKNOWN_LENGTH)
{
generateTypeCpp(prefix, retType, NULL);
break;
}
//fall through
case type_qstring:
case type_string:
case type_data:
case type_unicode:
case type_utf8:
{
OwnedITypeInfo ptrType = makeReferenceModifier(LINK(retType));
prefix.append("void");
if (retType->getSize() == UNKNOWN_LENGTH)
{
if (retType->getTypeCode() != type_varstring)
params.append("size32_t & __lenResult,");
// if (hasConstModifier(retType))
// params.append("const ");
generateTypeCpp(params, retType, NULL);
params.append(" & __result");
}
else
{
generateTypeCpp(params, ptrType, "__result");
}
break;
}
case type_transform:
prefix.append("size32_t");
params.append("ARowBuilder & __self");
break;
case type_table:
case type_groupedtable:
if (hasStreamedModifier(retType))
{
prefix.append("IRowStream *");
params.append("IEngineRowAllocator * _resultAllocator");
}
else if (hasLinkCountedModifier(retType))
{
prefix.append("void");
params.append("size32_t & __countResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("byte * * & __result");
if (hasNonNullRecord(retType) && getBoolProperty(attrs, allocatorAtom, true))
params.append(", IEngineRowAllocator * _resultAllocator");
}
else
{
prefix.append("void");
params.append("size32_t & __lenResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("void * & __result");
}
break;
case type_set:
{
prefix.append("void");
if (!getBoolProperty(attrs, oldSetFormatAtom))
{
params.append("bool & __isAllResult,");
params.append("size32_t & __lenResult,");
}
else
params.append("unsigned & __numResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("void * & __result");
break;
}
case type_row:
prefix.append("void");
params.append("byte * __result");
break;
default:
generateTypeCpp(prefix, retType, NULL);
break;
}
}
bool HqlCppTranslator::expandFunctionPrototype(StringBuffer & s, IHqlExpression * funcdef, bool force)
{
IHqlExpression *body = funcdef->queryChild(0);
IHqlExpression *formals = funcdef->queryChild(1);
if (!force && (body->hasProperty(includeAtom) || body->hasProperty(ctxmethodAtom) || body->hasProperty(gctxmethodAtom) || body->hasProperty(methodAtom) || body->hasProperty(sysAtom) || body->hasProperty(omethodAtom) ))
return false;
enum { ServiceApi, RtlApi, BcdApi, CApi, LocalApi } api = ServiceApi;
if (body->hasProperty(eclrtlAtom))
api = RtlApi;
else if (body->hasProperty(bcdAtom))
api = BcdApi;
else if (body->hasProperty(cAtom))
api = CApi;
else if (body->hasProperty(localAtom))
api = LocalApi;
s.append("extern ");
if ((api == ServiceApi) || api == CApi)
s.append("\"C\" ");
switch (api)
{
case ServiceApi: s.append(" SERVICE_API"); break;
case RtlApi: s.append(" RTL_API"); break;
case BcdApi: s.append(" BCD_API"); break;
}
s.append(" ");
StringBuffer returnParameters;
ITypeInfo * retType = funcdef->queryType()->queryChildType();
expandFunctionReturnType(s, returnParameters, retType, body);
switch (api)
{
case CApi:
switch (options.targetCompiler)
{
case Vs6CppCompiler:
s.append(" _cdecl");
break;
}
break;
case BcdApi:
switch (options.targetCompiler)
{
case Vs6CppCompiler:
s.append(" __fastcall");
break;
}
break;
}
s.append(" ");
getProperty(body, entrypointAtom, s);
bool firstParam = true;
if (!body->hasProperty(entrypointAtom))
{
s.append("/*").append(body->queryName()).append("*/");
}
s.append('(');
if (body->hasProperty(contextAtom))
{
s.append("ICodeContext * ctx");
firstParam = false;
}
else if (body->hasProperty(globalContextAtom) )
{
s.append("IGlobalCodeContext * gctx");
firstParam = false;
}
else if (body->hasProperty(userMatchFunctionAtom))
{
s.append("IMatchWalker * results");
firstParam = false;
}
if (returnParameters.length())
{
if (!firstParam)
s.append(',');
s.append(returnParameters);
firstParam = false;
}
ForEachChild(i, formals)
{
if (!firstParam)
s.append(',');
else
firstParam = false;
IHqlExpression * param = formals->queryChild(i);
ITypeInfo *paramType = param->queryType();
//Case is significant if these parameters are use for BEGINC++ sections
_ATOM paramName = param->queryName();
StringBuffer paramNameText;
paramNameText.append(paramName).toLowerCase();
bool isOut = false;
bool isConst = false;
unsigned maxAttr = param->numChildren();
unsigned attrIdx;
for (attrIdx = 0; attrIdx < maxAttr; attrIdx++)
{
IHqlExpression * attr = param->queryChild(attrIdx);
if (attr->isAttribute())
{
if (attr->queryName() == constAtom)
isConst = true;
else if (attr->queryName() == outAtom)
isOut = true;
}
}
switch (paramType->getTypeCode())
{
case type_string:
case type_qstring:
case type_data:
case type_unicode:
case type_utf8:
case type_table:
case type_groupedtable:
if (paramType->getSize() == UNKNOWN_LENGTH)
{
s.append("size32_t");
if (isOut)
s.append(" &");
if (paramName)
appendCapital(s.append(" len"), paramNameText);
s.append(",");
}
break;
case type_set:
if (!queryProperty(paramType, oldSetFormatAtom))
{
s.append("bool");
if (paramName)
appendCapital(s.append(" isAll"), paramNameText);
s.append(",size32_t ");
if(paramName)
appendCapital(s.append(" len"), paramNameText);
}
else
{
s.append("unsigned");
if(paramName)
appendCapital(s.append(" num"), paramNameText);
}
s.append(",");
break;
case type_row:
isConst = true;
break;
}
bool nameappended = false;
switch (paramType->getTypeCode())
{
case type_record:
s.append("IOutputMetaData * ");
break;
case type_table:
case type_groupedtable:
if (isConst)
s.append("const ");
s.append("void *");
if (isOut)
s.append(" &");
break;
case type_set:
if (!queryProperty(paramType, oldSetFormatAtom))
{
if (isConst)
s.append("const ");
s.append("void *");
if (isOut)
s.append(" &");
break;
}
else
{
ITypeInfo* childType = paramType->queryChildType();
if(!childType)
break;
if(isStringType(childType)) {
// process stringn and varstringn specially.
if(childType->getSize() > 0) {
s.append("char ");
if(paramName) {
s.append(paramNameText);
nameappended = true;
}
s.append("[][");
unsigned setlen = childType->getSize();
s.append(setlen);
s.append("]");
}
// Process string and varstring specially
else {
s.append("char *");
if(paramName) {
s.append(paramNameText);
nameappended = true;
}
s.append("[]");
}
}
else
{
OwnedITypeInfo pointerType = makePointerType(LINK(childType));
generateTypeCpp(s, pointerType, NULL);
}
break;
}
// Other set types just fall through and will be treated like other types.
case type_qstring: case type_string: case type_varstring: case type_data:
default:
{
if (isConst)
s.append("const ");
Owned argType = LINK(paramType);
if (argType->getTypeCode() == type_function)
argType.setown(makePointerType(LINK(argType)));
if (isTypePassedByAddress(argType))
argType.setown(makeReferenceModifier(LINK(argType)));
generateTypeCpp(s, argType, paramName->str());
nameappended = true;
if (isOut)
s.append(" &");
break;
}
}
if (paramName && !nameappended)
s.append(" ").append(paramNameText);
}
s.append(")");
return true;
}
void HqlCppTranslator::expandFunctionPrototype(BuildCtx & ctx, IHqlExpression * funcdef, bool force)
{
StringBuffer s;
if (expandFunctionPrototype(s, funcdef, force))
{
s.append(";");
ctx.addQuoted(s);
}
}
//Replace no_param with whatever they will have been bound to
static IHqlExpression * replaceInlineParameters(IHqlExpression * funcdef, IHqlExpression * expr)
{
IHqlExpression * body = funcdef->queryChild(0);
assertex(!body->hasProperty(oldSetFormatAtom));
IHqlExpression * formals = funcdef->queryChild(1);
HqlMapTransformer simpleTransformer;
StringBuffer paramNameText, temp;
ForEachChild(i, formals)
{
IHqlExpression * param = formals->queryChild(i);
ITypeInfo *paramType = param->queryType();
CHqlBoundExpr bound;
//Case is significant if these parameters are use for BEGINC++ sections
_ATOM paramName = param->queryName();
paramNameText.clear().append(paramName).toLowerCase();
Linked type = paramType;
switch (paramType->getTypeCode())
{
case type_set:
{
appendCapital(temp.clear().append("isAll"), paramNameText);
bound.isAll.setown(createVariable(temp.str(), makeBoolType()));
}
//fall through
case type_string:
case type_qstring:
case type_data:
case type_unicode:
case type_utf8:
case type_table:
case type_groupedtable:
if (paramType->getSize() == UNKNOWN_LENGTH)
{
appendCapital(temp.clear().append("len"), paramNameText);
bound.length.setown(createVariable(temp.str(), LINK(sizetType)));
}
type.setown(makeReferenceModifier(LINK(type)));
break;
}
bound.expr.setown(createVariable(paramNameText.str(), LINK(type)));
OwnedHqlExpr replacement = bound.getTranslatedExpr();
simpleTransformer.setMapping(param, replacement);
}
return simpleTransformer.transformRoot(expr);
}
void HqlCppTranslator::doBuildUserFunctionReturn(BuildCtx & ctx, ITypeInfo * type, IHqlExpression * value)
{
if (!options.spotCSE)
{
doBuildFunctionReturn(ctx, type, value);
return;
}
switch (value->getOperator())
{
case no_if:
if (false)///disable for the moment - look at changes in klogermann11 to see why, some v.good, some bad.
{
//optimize the way that cses are spotted to minimise unnecessary calculations
OwnedHqlExpr branches = createComma(LINK(value->queryChild(1)), LINK(value->queryChild(2)));
OwnedHqlExpr cond = LINK(value->queryChild(0));
spotScalarCSE(cond, branches, NULL, NULL);
BuildCtx subctx(ctx);
IHqlStmt * stmt = buildFilterViaExpr(subctx, cond);
doBuildUserFunctionReturn(subctx, type, branches->queryChild(0));
subctx.selectElse(stmt);
doBuildUserFunctionReturn(subctx, type, branches->queryChild(1));
break;
}
default:
{
OwnedHqlExpr optimized = spotScalarCSE(value);
if (value->isAction())
buildStmt(ctx, value);
else
doBuildFunctionReturn(ctx, type, optimized);
break;
}
}
}
void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * externalDef, IHqlExpression * funcdef)
{
IHqlExpression *body = funcdef->queryChild(0);
ITypeInfo * returnType = funcdef->queryType()->queryChildType();
if (body->getOperator() == no_outofline)
body = body->queryChild(0);
StringBuffer s;
BuildCtx funcctx(*code, helperAtom);
if (options.spanMultipleCpp)
{
const bool inChildActivity = true; // assume the worse
OwnedHqlExpr pass = getSizetConstant(cppIndexNextActivity(inChildActivity));
funcctx.addGroupPass(pass);
}
expandFunctionPrototype(s, externalDef, false);
if (body->getOperator() == no_cppbody)
{
if (!allowEmbeddedCpp())
throwError(HQLERR_EmbeddedCppNotAllowed);
IHqlExpression * location = queryLocation(body);
const char * locationFilename = location ? location->querySourcePath()->str() : NULL;
unsigned startLine = location ? location->getStartLine() : 0;
IHqlExpression * cppBody = body->queryChild(0);
if (cppBody->getOperator() == no_record)
cppBody = body->queryChild(1);
StringBuffer text;
cppBody->queryValue()->getStringValue(text);
//remove /r so we don't end up with mixed format end of lines.
text.setLength(cleanupEmbeddedCpp(text.length(), (char*)text.str()));
const char * start = text.str();
loop
{
char next = *start;
if (next == '\n')
startLine++;
else if (next != '\r')
break;
start++;
}
const char * body = start;
const char * cppSeparatorText = "#body";
const char * separator = strstr(body, cppSeparatorText);
if (separator)
{
text.setCharAt(separator-text.str(), 0);
if (location)
funcctx.addLine(locationFilename, startLine);
funcctx.addQuoted(body);
if (location)
funcctx.addLine();
body = separator + strlen(cppSeparatorText);
if (*body == '\r') body++;
if (*body == '\n') body++;
startLine += memcount(body-start, start, '\n');
}
funcctx.addQuotedCompound(s);
if (location)
funcctx.addLine(locationFilename, startLine);
funcctx.addQuoted(body);
if (location)
funcctx.addLine();
}
else
{
funcctx.addQuotedCompound(s);
OwnedHqlExpr newBody = replaceInlineParameters(funcdef, body);
newBody.setown(foldHqlExpression(newBody));
doBuildUserFunctionReturn(funcctx, returnType, newBody);
}
}
//---------------------------------------------------------------------------
void HqlCppTranslator::doBuildPureSubExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
{
unsigned max = expr->numChildren();
if (max == 0)
tgt.expr.set(expr);
else
{
HqlExprArray args;
unsigned idx = 0;
CHqlBoundExpr bound;
for (idx = 0; idx < max; idx++)
{
buildExpr(ctx, expr->queryChild(idx), bound);
args.append(*bound.expr.getClear());
}
tgt.expr.setown(expr->clone(args));
}
}
//---------------------------------------------------------------------------
IHqlExpression * HqlCppTranslator::getListLength(BuildCtx & ctx, IHqlExpression * expr)
{
CHqlBoundExpr bound;
buildExpr(ctx, expr, bound);
return getBoundLength(bound);
}
IHqlExpression * HqlCppTranslator::getBoundCount(const CHqlBoundExpr & bound)
{
if (bound.count)
return bound.count.getLink();
ITypeInfo * type = bound.expr->queryType();
switch (type->getTypeCode())
{
case type_array:
{
if (bound.length)
return convertBetweenCountAndSize(bound, true);
unsigned size = type->getSize();
if (size != UNKNOWN_LENGTH)
return getSizetConstant(size / type->queryChildType()->getSize());
UNIMPLEMENTED;
}
case type_table:
case type_groupedtable:
case type_set:
if (bound.length)
return convertBetweenCountAndSize(bound, true);
UNIMPLEMENTED;
default:
UNIMPLEMENTED;
}
}
IHqlExpression * HqlCppTranslator::getBoundLength(const CHqlBoundExpr & bound)
{
if (bound.length)
return bound.length.getLink();
ITypeInfo * type = bound.expr->queryType();
if (bound.expr->queryValue())
return getSizetConstant(type->getStringLen());
switch (type->getTypeCode())
{
case type_varstring:
{
HqlExprArray args;
args.append(*getElementPointer(bound.expr));
return bindTranslatedFunctionCall(strlenAtom, args);
}
case type_varunicode:
{
HqlExprArray args;
args.append(*getElementPointer(bound.expr));
return bindTranslatedFunctionCall(unicodeStrlenAtom, args);
}
case type_set:
case type_array:
case type_table:
case type_groupedtable:
assertex(!isArrayRowset(type));
if (bound.count)
return convertBetweenCountAndSize(bound, false);
UNIMPLEMENTED;
case type_utf8:
{
assertex(type->getSize() != UNKNOWN_LENGTH);
HqlExprArray args;
args.append(*getSizetConstant(type->getSize()));
args.append(*getElementPointer(bound.expr));
return bindTranslatedFunctionCall(utf8LengthAtom, args);
}
default:
return getSizetConstant(type->getStringLen());
}
}
IHqlExpression * HqlCppTranslator::getBoundSize(ITypeInfo * type, IHqlExpression * length, IHqlExpression * data)
{
type_t tc = type->getTypeCode();
ITypeInfo * lengthType = length->queryType();
switch (tc)
{
case type_qstring:
{
if (length->queryValue())
return getSizetConstant((size32_t)rtlQStrSize((size32_t)length->queryValue()->getIntValue()));
HqlExprArray args;
args.append(*LINK(length));
return bindTranslatedFunctionCall(qstrSizeAtom, args);
}
case type_varstring:
return adjustValue(length, 1);
case type_varunicode:
{
OwnedHqlExpr temp = adjustValue(length, 1);
return multiplyValue(temp, 2);
}
case type_unicode:
return multiplyValue(length, 2);
case type_utf8:
{
assertex(data);
if (data->queryValue())
return getSizetConstant(data->queryValue()->getSize());
HqlExprArray args;
args.append(*LINK(length));
args.append(*getElementPointer(data));
return bindTranslatedFunctionCall(utf8SizeAtom, args);
}
case type_array:
case type_set:
return LINK(length);
default:
return LINK(length);
}
}
IHqlExpression * HqlCppTranslator::getBoundSize(const CHqlBoundExpr & bound)
{
ITypeInfo * type = bound.expr->queryType();
if (bound.length)
return getBoundSize(type, bound.length, bound.expr);
type_t tc = type->getTypeCode();
if (tc == type_row)
{
if (hasReferenceModifier(type))
return getSizetConstant(sizeof(void*));
IHqlExpression * record = ::queryRecord(type);
ColumnToOffsetMap * map = queryRecordOffsetMap(record);
if (map->isFixedWidth())
return getSizetConstant(map->getFixedRecordSize());
//call meta function mm.queryRecordSize(&row)
StringBuffer metaInstance, temp;
buildMetaForRecord(metaInstance, record);
temp.append(metaInstance).append(".getRecordSize(");
OwnedHqlExpr rowAddr = getPointer(bound.expr);
generateExprCpp(temp, rowAddr);
temp.append(")");
return createQuoted(temp.str(), LINK(sizetType));
}
if (type->getSize() != UNKNOWN_LENGTH)
return getSizetConstant(type->getSize());
OwnedHqlExpr length = getBoundLength(bound);
return getBoundSize(type, length, bound.expr);
}
IHqlExpression * HqlCppTranslator::getFirstCharacter(IHqlExpression * source)
{
if (source->getOperator() == no_constant)
{
StringBuffer temp;
source->queryValue()->getStringValue(temp);
return createUIntConstant((unsigned char)temp.charAt(0));
}
return createValue(no_index, makeCharType(), LINK(source), getZero());
}
IHqlExpression * HqlCppTranslator::getElementPointer(IHqlExpression * source)
{
ITypeInfo * srcType = source->queryType();
switch (srcType->getTypeCode())
{
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_utf8:
case type_varunicode:
case type_set:
break;
default:
throwUnexpectedType(srcType);
}
if (source->getOperator() == no_constant)
return LINK(source);
OwnedHqlExpr pointer = getPointer(source);
return ensureIndexable(pointer);
}
/* All in params: NOT linked */
IHqlExpression * HqlCppTranslator::getIndexedElementPointer(IHqlExpression * source, IHqlExpression * index)
{
ITypeInfo * srcType = source->queryType();
switch (srcType->getTypeCode())
{
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_utf8:
case type_varunicode:
break;
default:
throwUnexpectedType(srcType);
}
if (!index)
return getElementPointer(source);
IValue * value = index->queryValue();
if (value && value->getIntValue() == 0)
return getElementPointer(source);
ITypeInfo * refType = LINK(srcType);
if (!srcType->isReference())
refType = makeReferenceModifier(refType);
OwnedHqlExpr temp;
if (srcType->getTypeCode() == type_utf8)
{
HqlExprArray args;
args.append(*LINK(index));
args.append(*getElementPointer(source));
temp.setown(bindTranslatedFunctionCall(utf8SizeAtom, args));
index = temp;
}
//special case string indexing
if (source->getOperator() != no_constant)
{
if (!srcType->isReference() && !hasWrapperModifier(srcType))
return createValue(no_address, refType, createValue(no_index, makeCharType(), ensureIndexable(source), LINK(index)));
}
return createValue(no_add, refType, ensureIndexable(source), LINK(index));
}
IHqlExpression * HqlCppTranslator::getIndexedElementPointer(IHqlExpression * source, unsigned index)
{
if (!index)
return getElementPointer(source);
OwnedHqlExpr ival = getSizetConstant(index);
return getIndexedElementPointer(source, ival);
}
IHqlExpression * HqlCppTranslator::needFunction(_ATOM name)
{
HqlDummyLookupContext dummyctx(errors);
return internalScope->lookupSymbol(name, LSFsharedOK, dummyctx);
}
unsigned HqlCppTranslator::processHint(IHqlExpression * expr)
{
unsigned oldHints = hints;
_ATOM hint = NULL; // MORE how do I get this?
if (hint == sizeAtom)
hints = (hints & ~(HintSpeed|HintSize)) | HintSize;
else if (hint == speedAtom)
hints = (hints & ~(HintSize|HintSpeed)) | HintSpeed;
return oldHints;
}
bool HqlCppTranslator::childrenRequireTemp(BuildCtx & ctx, IHqlExpression * expr, bool includeChildren)
{
unsigned numArgs = expr->numChildren();
for (unsigned index = 0; index < numArgs; index++)
if (requiresTemp(ctx, expr->queryChild(index), includeChildren))
return true;
return false;
}
bool HqlCppTranslator::requiresTemp(BuildCtx & ctx, IHqlExpression * expr, bool includeChildren)
{
switch (expr->getOperator())
{
case no_attr:
case no_attr_link:
case no_attr_expr:
case no_quoted:
case no_variable:
case no_constant:
case no_translated:
case no_matchtext:
case no_matchunicode:
case no_matchlength:
case no_matchattr:
case no_matchrow:
case no_matchutf8:
case no_libraryinput:
return false;
case no_getresult:
case no_getgraphresult:
case no_workunit_dataset:
return false; // if in an activity, then will be in setContext, if not then don't really care
case no_preservemeta:
return requiresTemp(ctx, expr->queryChild(0), includeChildren);
case no_alias:
{
if (expr->isPure() && ctx.queryMatchExpr(expr->queryChild(0)))
return false;
if (!containsActiveDataset(expr)) // generates a earlier temp even if generating within the onCreate() function
return false;
return true;
}
case no_select:
return expr->hasProperty(newAtom);
case no_field:
throwUnexpected();
return false; // more, depends on whether conditional etc.
case no_sizeof:
case no_offsetof:
return false; /// auto creates one anyway.
case no_typetransfer:
switch (expr->queryChild(0)->queryType()->getTypeCode())
{
case type_qstring:
case type_string:
case type_data:
case type_varstring:
break;
default:
return true;
}
break;
case no_substring:
{
SubStringInfo info(expr);
if (!info.canGenerateInline() && !expr->hasProperty(quickAtom))
return true;
break;
}
case no_call:
case no_externalcall:
{
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_utf8:
return true;
}
break;
}
case no_cast:
case no_implicitcast:
{
ITypeInfo * type = expr->queryType();
IHqlExpression * child = expr->queryChild(0);
switch (type->getTypeCode())
{
case type_string:
case type_data:
if (!canRemoveStringCast(type, child->queryType()))
return true;
break;
case type_varstring:
if ((type->getSize() != UNKNOWN_LENGTH) || (child->queryType()->getTypeCode() != type_varstring))
return true;
break;
}
}
break;
case no_eq:
case no_ne:
case no_lt:
case no_le:
case no_gt:
case no_ge:
{
if (!includeChildren)
return false;
unsigned numArgs = expr->numChildren();
for (unsigned index = 0; index < numArgs; index++)
{
OwnedHqlExpr cur = getSimplifyCompareArg(expr->queryChild(index));
//decimal comparisons can't be short circuited because they might cause a bcd stack overflow.
if (cur->queryType()->getTypeCode() == type_decimal)
return true;
if (requiresTemp(ctx, cur, true))
return true;
}
return false;
}
case no_mul:
case no_div:
case no_modulus:
case no_add:
case no_sub:
case no_and:
case no_or:
case no_xor:
case no_lshift:
case no_rshift:
case no_comma:
case no_compound:
case no_band:
case no_bor:
case no_bxor:
case no_pselect:
case no_index:
case no_postinc:
case no_postdec:
case no_negate:
case no_not:
case no_bnot:
case no_address:
case no_deref:
case no_preinc:
case no_predec:
case no_if:
case no_charlen:
break;
case no_order:
case no_crc:
case no_hash:
case no_hash32:
case no_hash64:
case no_hashmd5:
case no_abs:
return true;
default:
return true;
}
if (includeChildren)
return childrenRequireTemp(ctx, expr, includeChildren);
return false;
}
bool HqlCppTranslator::requiresTempAfterFirst(BuildCtx & ctx, IHqlExpression * expr)
{
unsigned numArgs = expr->numChildren();
for (unsigned index = 1; index < numArgs; index++)
if (requiresTemp(ctx, expr->queryChild(index), true))
return true;
return false;
}
void HqlCppTranslator::useFunction(IHqlExpression * func)
{
code->useFunction(func);
}
void HqlCppTranslator::useLibrary(const char * libname)
{
code->useLibrary(libname);
}
//===========================================================================
static unique_id_t queryInstance = 0;
HqlQueryInstance::HqlQueryInstance()
{
instance = ++queryInstance;
}
StringBuffer & HqlQueryInstance::queryDllName(StringBuffer & out)
{
return out.append("query").append(instance);
}