Версия ядра клиента и сервера AX 2009 - 5.0.1500.2985, т.е. RU5.
Ниже приводится тестовый пример, который
при вызове с клиента гарантированно валит AOS на test2():
X++:
CustTable test1(CustTable _custTableOther = null, CustGroupId _custGroupId = '')
{
CustTable custTable;
;
if (_custTableOther)
{
select firstonly custTable where custTable.CustGroup == _custTableOther.CustGroup;
}
else
{
select firstonly custTable where custTable.CustGroup == _custGroupId;
}
return custTable;
}
CustTable test2(CustTable _custTableOther = null, CustGroupId _custGroupId = '')
{
CustTable custTable;
;
select firstonly custTable
where ( ( _custTableOther
&& custTable.CustGroup == _custTableOther.CustGroup
)
|| ( !_custTableOther
&& custTable.CustGroup == _custGroupId
)
)
;
return custTable;
}
;
test1();
test2();
Дело, судя по всему в том, что AOS как-то некорректно пытается получить доступ к буферу _custTableOther в том случае, если в качестве него передан null. И воспроизводится это именно при вызове с клиента - при выполнении кода на сервере все отрабатывает нормально; тот же тестовый джобик можно запустить на сервер, создав для него специальный пункт меню.
Ниже приводится еще один джобик, который эвристически пытается найти схожие конструкции в коде (для ускорения запускать его лучше на сервере). При этом сравнительно часто встречаются ложные срабатывания, но в целом если удастся отловить 1-2 таких места (преимущественно в своем коде

), то уже время будет потрачено не впустую. В стандартном коде было найдено несколько "подозрительных" мест, но при попытке их эксплойта выяснилось, что они к сваливанию AOS'а не приводят. Подозрения пали на следующие методы, схожим образом использующие свои табличные параметры:
- \Classes\SalesFormLetter\reselect - но в нем явно проверяется, что метод отрабатывает на сервере;
- \Classes\RTax25RegTransEngine\updateTotals - здесь перед выполнением запроса идет проверка if (!_mainTrans && !_origTrans), которая неявно инициализирует оба табличных буфера;
- \Classes\RTax25RegTransEngine_TaxRemainGoods\updateTotals - аналогично RTax25RegTransEngine
X++:
UtilEntryLevel currentLayer;
UtilElements utilElements;
DictMethod dictMethod;
xRefPath nodePath;
str source;
str pattern;
Set setOfParmNameExclusions;
Set setOfRecordParmNames;
SetEnumerator setEnum;
boolean currentAOLayerOnly;
Counter cnFound;
Counter n;
;
setOfParmNameExclusions = new Set( Types::String );
setOfParmNameExclusions.add( '_inventDimCriteria' );
setOfParmNameExclusions.add( '_inventDimParm' );
setOfParmNameExclusions.add( '_inventDimParmCriteria' );
currentLayer = currentAOLayer();
currentAOLayerOnly = Box::yesNo( strfmt( @"Искать только на слое %1?", currentLayer ), DialogButton::Yes ) == DialogButton::Yes;
while select utilElements
where ( utilElements.recordType == UtilElementType::ClassInstanceMethod
|| utilElements.recordType == UtilElementType::ClassStaticMethod
|| utilElements.recordType == UtilElementType::TableInstanceMethod
|| utilElements.recordType == UtilElementType::TableStaticMethod
)
&& ( !currentAOLayerOnly
|| utilElements.utilLevel == currentLayer
)
{
dictMethod = new DictMethod( utilElements.recordType, utilElements.parentId, utilElements.name );
if ( dictMethod
&& dictMethod.compiledOk()
&& dictMethod.parameterCnt() > 0
)
{
setOfRecordParmNames = null;
for (n = 1; n <= dictMethod.parameterCnt(); n++)
{
if ( dictMethod.parameterType( n ) == Types::Record
&& ( utilElements.utilLevel == currentLayer
|| !setOfParmNameExclusions.in( dictMethod.parameterName( n ) )
)
)
{
if (!setOfRecordParmNames)
{
setOfRecordParmNames = new Set( Types::String );
}
setOfRecordParmNames.add( dictMethod.parameterName( n ) );
}
}
if (setOfRecordParmNames && !setOfRecordParmNames.empty())
{
source = conpeek( utilElements.source, 1 );
setEnum = setOfRecordParmNames.getEnumerator();
while (setEnum.moveNext())
{
pattern = strfmt( @"select [^;{]+ where [^;{]*[^a-z0-9_;{.]%1[^a-z0-9_.]", setEnum.current() );
if (match( pattern, source ))
{
nodePath = xUtilElements::getNodePathRough( utilElements );
info( strfmt( @"%1 - %2 (%3)", nodePath, setEnum.current(), utilElements.utilLevel ), '', SysInfoAction_Editor::newOpen( nodePath ) );
break;
}
}
}
}
}