Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dist/
*.tsbuildinfo
**/node_modules/
**/.pnpm-store/
compiler/back-ends/ts-gen/tests/workdir/
CMakeCache.txt
CMakeFiles/
Makefile
Expand Down
30 changes: 7 additions & 23 deletions ROSE/makesnaccrose.bat
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
@echo off
cls
setlocal EnableExtensions

SET COMPILER=
set "COMPILER="
call "%~dp0..\scripts\ensure_compiler.bat"
if errorlevel 1 goto end
set "COMPILER=%SNACC_COMPILER%"

echo Searching for esnacc executable in output directory
SET PATH=%PATH%;%CD%\..\output\bin\
where esnaccd.exe 2>nul
IF %ERRORLEVEL% == 0 SET COMPILER=esnaccd.exe
IF NOT "%COMPILER%" == "" GOTO start
where esnacc.exe 2>nul
IF %ERRORLEVEL% == 0 SET COMPILER=esnacc.exe
IF NOT "%COMPILER%" == "" GOTO start

echo Searching for esnacc executable in global buildtools
SET PATH=%PATH%;%CD%\..\..\..\buildtools\
where esnacc7.exe 2>nul
IF %ERRORLEVEL% == 0 SET COMPILER=esnacc7.exe
IF NOT "%COMPILER%" == "" GOTO start

echo Could not find esnaccd.exe or esnacc.exe, please build the compiler first
goto end

:start
echo %COMPILER% -ValidationLevel 0 -C -x -p -e -d -j SNACCROSE.asn1
%COMPILER% -ValidationLevel 0 -C -x -p -e -d -j SNACCROSE.asn1
"%COMPILER%" -ValidationLevel 0 -C -x -p -e -d -j SNACCROSE.asn1
if NOT %ERRORLEVEL% == 0 pause
move SNACCROSE.cpp ..\cpp-lib\src\SNACCROSE.cpp >NUL
move SNACCROSE.h ..\cpp-lib\include\SNACCROSE.h >NUL

echo %COMPILER% -ValidationLevel 0 -JTE -j SNACCROSE.asn1
%COMPILER% -ValidationLevel 0 -JTE -j SNACCROSE.asn1
"%COMPILER%" -ValidationLevel 0 -JTE -j SNACCROSE.asn1
if NOT %ERRORLEVEL% == 0 pause
move SNACCROSE.ts ..\compiler\back-ends\ts-gen\gluecode\SNACCROSE.ts >NUL
move SNACCROSE_Converter.ts ..\compiler\back-ends\ts-gen\gluecode\SNACCROSE_Converter.ts >NUL
Expand Down
63 changes: 39 additions & 24 deletions compiler/back-ends/c++-gen/gen-code.c
Original file line number Diff line number Diff line change
Expand Up @@ -4584,48 +4584,63 @@ void PrintROSECode(FILE* src, FILE* hdr, FILE* hdrInterface, ModuleList* mods, M

// Constructor
fprintf(hdr, "\t%s(SnaccROSESender* pBase);\n", m->ROSEClassName);
fprintf(src, "%s::%s(SnaccROSESender* pBase) : SnaccROSEComponent(pBase)\n", m->ROSEClassName, m->ROSEClassName);
fprintf(src, "{\n");
fprintf(src, "}\n\n");

// Function for triggering the registration of all Operations (name/id)
fprintf(hdr, "\t// Registers all known operations on a listener lookup table at startup (UCAAS-1485)\n");
fprintf(hdr, "\tstatic void RegisterOperations(SnaccRoseOperationLookup& lookup);\n");
FOR_EACH_LIST_ELMT(vd, m->valueDefs)
{
if (IsDeprecatedNoOutputOperation(m, vd->definedName))
continue;
if (vd->value->basicValue->choiceId != BASICVALUE_INTEGER)
continue;
if (vd->value->type->basicType->choiceId != BASICTYPE_MACROTYPE)
continue;
if (vd->value->type->basicType->a.macroType->choiceId != MACROTYPE_ROSOPERATION)
continue;
if (!iFirstIIDFound)
{
iFirstIIDFound = 1;
fprintf(hdr, "\tstatic const int m_iid = %d;\n", vd->value->basicValue->a.integer);
}
break;
}

fprintf(src, "void %s::RegisterOperations(SnaccRoseOperationLookup& lookup)\n", m->ROSEClassName);
fprintf(src, "{\n");
fprintf(src, "namespace\n{\n");
fprintf(src, "constexpr const char kModuleName[] = \"%s\";\n", m->moduleName);
if (gMajorInterfaceVersion >= 0)
{
long long lPatchVersion = GetModulePatchVersion(m->moduleName);
char* szNumericDate = ConvertUnixTimeToNumericDate(lPatchVersion);
if (szNumericDate)
{
fprintf(src, "\tlookup.RegisterModuleVersion(\"%s\", \"%i.0.%s\");\n", m->moduleName, gMajorInterfaceVersion, szNumericDate);
fprintf(src, "constexpr const char kModuleVersion[] = \"%i.0.%s\";\n", gMajorInterfaceVersion, szNumericDate);
free(szNumericDate);
}
else
fprintf(src, "constexpr const char kModuleVersion[] = \"\";\n");
}
else
fprintf(src, "constexpr const char kModuleVersion[] = \"\";\n");
fprintf(src, "} // namespace\n\n");

fprintf(src, "%s::%s(SnaccROSESender* pBase) : SnaccROSEComponent(pBase)\n", m->ROSEClassName, m->ROSEClassName);
fprintf(src, "{\n");
fprintf(src, "}\n\n");

// Function for triggering the registration of all Operations (name/id)
fprintf(hdr, "\t// Registers all known operations on a listener lookup table at startup (UCAAS-1485)\n");
fprintf(hdr, "\tstatic void RegisterOperations(SnaccRoseOperationLookup& lookup);\n");

fprintf(src, "void %s::RegisterOperations(SnaccRoseOperationLookup& lookup)\n", m->ROSEClassName);
fprintf(src, "{\n");
if (gMajorInterfaceVersion >= 0)
fprintf(src, "\tRegisterModuleVersion(lookup, kModuleName, kModuleVersion);\n");
FOR_EACH_LIST_ELMT(vd, m->valueDefs)
{
if (IsDeprecatedNoOutputOperation(m, vd->definedName))
continue;
if (PrintROSEOperationRegistrationLookup(src, r, m, vd) && !iFirstIIDFound)
{
iFirstIIDFound = 1;
fprintf(hdr, "\tstatic const int m_iid = %d;\n", vd->value->basicValue->a.integer);
}
PrintROSEOperationRegistration(src, r, m, vd);
}
fprintf(src, "}\n\n");

if (iFirstIIDFound)
{
fprintf(hdr, "protected:\n");
fprintf(hdr, "\tvoid RegisterOperation(unsigned int uiOpID, const char* szOpName, bool bIsEvent = false, unsigned long long ullAddedUnix = 0, unsigned long long ullDeprecatedUnix = 0)\n");
fprintf(hdr, "\t{\n");
fprintf(hdr, "\t\tSnaccROSEComponent::RegisterOperation(uiOpID, szOpName, m_iid, \"%s\", bIsEvent, ullAddedUnix, ullDeprecatedUnix);\n", m->moduleName);
fprintf(hdr, "\t}\n\n");
fprintf(hdr, "public:\n");
}

fflush(src);
fflush(hdr);

Expand Down
9 changes: 6 additions & 3 deletions compiler/back-ends/c++-gen/gen-vals.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ int PrintROSEOperationRegistration(FILE* src, CxxRules* r, Module* mod, ValueDef
/*
* put instantiation in src file
*/
fprintf(src, "\tRegisterOperation(");
fprintf(src, "%d, \"", v->value->basicValue->a.integer);
fprintf(src, "\tRegisterOperation(lookup, m_iid, kModuleName, OPID_");
PrintCxxValueDefsName(src, r, v);
fprintf(src, ", \"");
PrintCxxValueDefsName(src, r, v);
fprintf(src, "\"");
if (bIsEvent)
Expand Down Expand Up @@ -158,7 +159,9 @@ int PrintROSEOperationRegistrationLookup(FILE* src, CxxRules* r, Module* mod, Va
}

fprintf(src, "\tlookup.RegisterOperation(");
fprintf(src, "%d, \"", v->value->basicValue->a.integer);
fprintf(src, "OPID_");
PrintCxxValueDefsName(src, r, v);
fprintf(src, ", \"");
PrintCxxValueDefsName(src, r, v);
fprintf(src, "\", m_iid, \"%s\"", mod->moduleName);
if (bIsEvent)
Expand Down
126 changes: 124 additions & 2 deletions compiler/back-ends/ts-gen/gluecode/TSASN1Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import {
IROSELogger,
ISendInvokeContext,
ReceiveInvokeContext,
RemoteCapabilityMode,
snaccAssert,
snaccAssertFail,
ASN1ByteArray,
ROSEBase,
} from "./TSROSEBase.js";
Expand Down Expand Up @@ -301,6 +304,10 @@ export abstract class TSASN1Base implements IASN1Transport {
private handlersByName = new Map<string, Handler>();
// Holds all loaded modules with operations registered on this stub
private loadedModulesByName = new Map<string, ILoadedModuleInfo>();
// Peer negotiate snapshot applied on this stub (client/server outbound gating)
private remoteModuleCapabilitiesByName = new Map<string, ILoadedModuleInfo>();
private remoteModuleCapabilitiesSet = false;
private remoteCapabilityMode = RemoteCapabilityMode.Disabled;
// The Logger Callback which must be set with the SetLogger Method
protected logger?: IROSELogger;
// Logs the raw transport (inbound before decoding, outbound after encoding)
Expand Down Expand Up @@ -395,7 +402,7 @@ export abstract class TSASN1Base implements IASN1Transport {
);
this.handlersByID.set(operationID, handler);
this.handlersByName.set(operationName, handler);
this.trackRegisteredOperation(operationID, moduleName, addedUnix, deprecatedUnix, isEvent);
this.trackRegisteredOperation(operationID, operationName, moduleName, addedUnix, deprecatedUnix, isEvent);
} else {
// trying to re-register a handler for an already registered operationID, this should not happen and indicates a problem in the calling code
debugger;
Expand All @@ -407,6 +414,7 @@ export abstract class TSASN1Base implements IASN1Transport {
*/
private trackRegisteredOperation(
operationID: number,
operationName: string,
moduleName: string,
addedUnix: number,
deprecatedUnix: number,
Expand All @@ -423,7 +431,7 @@ export abstract class TSASN1Base implements IASN1Transport {
this.loadedModulesByName.set(moduleName, module);
}

const info: IOpVersionInfo = { addedUnix, deprecatedUnix };
const info: IOpVersionInfo = { addedUnix, deprecatedUnix, opName: operationName };
if (isEvent)
module.events.set(operationID, info);
else
Expand Down Expand Up @@ -500,6 +508,120 @@ export abstract class TSASN1Base implements IASN1Transport {
return this.loadedModulesByName;
}

/**
* Resolves operation name from operation id via the registered handlers.
*/
public lookUpName(operationID: number): string | undefined {
return this.handlersByID.get(operationID)?.operationName;
}

/**
* Resolves operation id from operation name via the registered handlers.
*/
public lookUpID(operationName: string): number | undefined {
return this.handlersByName.get(operationName)?.operationID;
}

/**
* Resolves ASN.1 module name owning the given operation id on this stub.
*/
public lookUpModuleName(operationID: number): string | undefined {
return this.handlersByID.get(operationID)?.moduleName;
}

/**
* Configures whether outbound invokes are gated on a negotiate snapshot. Default Disabled.
*/
public setRemoteCapabilityMode(mode: RemoteCapabilityMode): void {
this.remoteCapabilityMode = mode;
}

/**
* Returns the current remote capability gating mode for outbound invokes.
*/
public getRemoteCapabilityMode(): RemoteCapabilityMode {
return this.remoteCapabilityMode;
}

/**
* Stores the peer module snapshot from asnNegotiateInterface (or equivalent).
*/
public applyRemoteModuleCapabilities(remote: ReadonlyMap<string, ILoadedModuleInfo>): void {
this.remoteModuleCapabilitiesByName = new Map(
[...remote.entries()].map(([moduleName, moduleInfo]) => [
moduleName,
{
moduleName: moduleInfo.moduleName,
version: moduleInfo.version,
invokes: new Map(moduleInfo.invokes),
events: new Map(moduleInfo.events),
},
]),
);
this.remoteModuleCapabilitiesSet = true;
}

/**
* Clears the applied remote capability snapshot (disconnect / legacy fallback).
*/
public clearRemoteModuleCapabilities(): void {
this.remoteModuleCapabilitiesByName.clear();
this.remoteModuleCapabilitiesSet = false;
}

/**
* True after applyRemoteModuleCapabilities() was called (even when the map is empty).
*/
public hasRemoteModuleCapabilities(): boolean {
return this.remoteModuleCapabilitiesSet;
}

/**
* Returns true when the negotiate snapshot offers this invoke OPID.
* Debug assert when hasRemoteModuleCapabilities() is false.
*/
public isSupportedOperation(operationID: number): boolean {
snaccAssert(
this.remoteModuleCapabilitiesSet,
"isSupportedOperation requires applyRemoteModuleCapabilities first",
);
return this.internalIsRemoteOperationSupported(operationID);
}

/**
* Local reject for outbound invokes blocked by remote capability gating.
* Events (invokeID 99999) are never gated here.
*/
protected tryRejectRemoteNotCapable(invoke: ROSEInvoke): ROSEReject | undefined {
if (invoke.invokeID === 99999)
return undefined;
if (this.remoteCapabilityMode !== RemoteCapabilityMode.Enabled || !this.remoteModuleCapabilitiesSet)
return undefined;
if (this.internalIsRemoteOperationSupported(invoke.operationID))
return undefined;
snaccAssertFail(
`Outbound invoke blocked: operation not offered by remote (${invoke.operationName}, ${invoke.operationID})`,
);
return createInvokeReject(
invoke,
CustomInvokeProblemEnum.remoteNotCapable,
`Operation ${invoke.operationName} (${invoke.operationID}) is not offered by the remote peer`,
);
}

/**
* Returns true when the applied remote snapshot lists the invoke OPID for its module.
*/
private internalIsRemoteOperationSupported(operationID: number): boolean {
const moduleName = this.lookUpModuleName(operationID);
if (!moduleName)
return false;
const module = this.remoteModuleCapabilitiesByName.get(moduleName);
if (!module)
return false;
return module.invokes.has(operationID);
}

/**
* Retrieves version information for a loaded asn1 module (a module that has registere ROSE invoke handlers)
*
Expand Down
4 changes: 4 additions & 0 deletions compiler/back-ends/ts-gen/gluecode/TSASN1Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ export abstract class TSASN1Client extends TSASN1Base implements IASN1Transport
* If no timeout was specified we resolve in undefined to cleanup the promise object
*/
public async sendInvoke(data: IASN1InvokeData): Promise<ROSEReject | ROSEResult | ROSEError | undefined> {
const localReject = this.tryRejectRemoteNotCapable(data.invoke);
if (localReject)
return localReject;

return new Promise((resolve): void => {
let resolveUndefined = true;

Expand Down
4 changes: 4 additions & 0 deletions compiler/back-ends/ts-gen/gluecode/TSASN1Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export class TSASN1Server extends TSASN1Base implements IASN1Transport {
* If no timeout was specified we resolve in undefined to cleanup the promise object
*/
public async sendInvoke(data: IASN1InvokeData): Promise<ROSEReject | ROSEResult | ROSEError | undefined> {
const localReject = this.tryRejectRemoteNotCapable(data.invoke);
if (localReject)
return localReject;

const clientConnectionID = data.invokeContext.clientConnectionID || data.invoke.sessionID;
if (!clientConnectionID) {
return createInvokeReject(
Expand Down
Loading
Loading