Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
bdemann committed Sep 30, 2023
1 parent 06bec7c commit 2758f2f
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 61 deletions.
11 changes: 5 additions & 6 deletions examples/canister/src/index.did
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
type rec_0 = record {someCanister:service {query1:() -> (bool) query; update1:() -> (text) }};
service: () -> {
canisterParam: (service {query1:() -> (bool) query; update1:() -> (text) }) -> (service {query1:() -> (bool) query; update1:() -> (text) }) query;
canisterReturnType: () -> (service {query1:() -> (bool) query; update1:() -> (text) }) query;
canisterNestedReturnType: () -> (rec_0);
canisterList: (vec service {query1:() -> (bool) query; update1:() -> (text) }) -> (vec service {query1:() -> (bool) query; update1:() -> (text) });
canisterCrossCanisterCall: (service {query1:() -> (bool) query; update1:() -> (text) }) -> (text);
canisterCrossCanisterCall: (service {query1: () -> (bool) query; update1: () -> (text) ;}) -> (text) ;
canisterList: (vec service {query1: () -> (bool) query; update1: () -> (text) ;}) -> (vec service {query1: () -> (bool) query; update1: () -> (text) ;}) ;
canisterNestedReturnType: () -> (record {someCanister:service {query1: () -> (bool) query; update1: () -> (text) ;}}) ;
canisterParam: (service {query1: () -> (bool) query; update1: () -> (text) ;}) -> (service {query1: () -> (bool) query; update1: () -> (text) ;}) query;
canisterReturnType: () -> (service {query1: () -> (bool) query; update1: () -> (text) ;}) query;
}
2 changes: 1 addition & 1 deletion examples/canister/src/some_canister.did
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
service: () -> {
query1: () -> (bool) query;
update1: () -> (text);
update1: () -> (text) ;
}
15 changes: 8 additions & 7 deletions src/compiler/generate_candid_and_canister_methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ export function generateCandidAndCanisterMethods(mainJs: string): {
const script = new vm.Script(mainJs);
script.runInContext(context);

const candidInfo = (sandbox.exports as any).canisterMethods
.getIDL([])
.accept(new DidVisitor(), {
...DEFAULT_VISITOR_DATA,
isFirstService: true
});
const canisterMethods = (sandbox.exports as any).canisterMethods;

const candidInfo = canisterMethods.getIDL([]).accept(new DidVisitor(), {
...DEFAULT_VISITOR_DATA,
isFirstService: true,
systemFuncs: canisterMethods.getSystemFunctionIDLs()
});

return {
candid: DidResultToCandidString(candidInfo),
canisterMethods: (sandbox.exports as any).canisterMethods
canisterMethods: canisterMethods
};
}
72 changes: 56 additions & 16 deletions src/lib_functional/candid/reference/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,19 @@ export function Canister<T extends CanisterOptions>(
returnFunction.queries = queries;
returnFunction.updates = updates;
returnFunction.callbacks = callbacks;
returnFunction.getIDL = (parents: Parent[]): IDL.ServiceClass => {
(returnFunction.getSystemFunctionIDLs = (
parents: Parent[]
): IDL.FuncClass[] => {
const serviceFunctionInfo: ServiceFunctionInfo = serviceOptions;

const record = Object.entries(serviceFunctionInfo).reduce(
(accumulator, [methodName, functionInfo]) => {
return Object.entries(serviceFunctionInfo).reduce(
(accumulator, [_methodName, functionInfo]) => {
const mode = functionInfo(parentOrUndefined).mode;
if (mode === 'update' || mode === 'query') {
// We don't want init, post upgrade, etc showing up in the idl
return accumulator;
}

const paramRealIdls = toParamIDLTypes(
functionInfo(parentOrUndefined).paramsIdls,
parents
Expand All @@ -227,23 +235,55 @@ export function Canister<T extends CanisterOptions>(
functionInfo(parentOrUndefined).returnIdl,
parents
);

const annotations = [functionInfo(parentOrUndefined).mode];

return {
return [
...accumulator,
[methodName]: IDL.Func(
paramRealIdls,
returnRealIdl,
annotations
)
};
IDL.Func(paramRealIdls, returnRealIdl, [mode])
];
},
{} as Record<string, IDL.FuncClass>
[] as IDL.FuncClass[]
);
}),
(returnFunction.getIDL = (parents: Parent[]): IDL.ServiceClass => {
const serviceFunctionInfo: ServiceFunctionInfo = serviceOptions;

return IDL.Service(record);
};
const record = Object.entries(serviceFunctionInfo).reduce(
(accumulator, [methodName, functionInfo]) => {
const paramRealIdls = toParamIDLTypes(
functionInfo(parentOrUndefined).paramsIdls,
parents
);
const returnRealIdl = toReturnIDLType(
functionInfo(parentOrUndefined).returnIdl,
parents
);

const mode = functionInfo(parentOrUndefined).mode;
let annotations: string[] = [];
if (mode === 'update') {
// do nothing
console.log('This is an update');
console.log(methodName);
} else if (mode === 'query') {
annotations = ['query'];
} else {
// We don't want init, post upgrade, etc showing up in the idl
return accumulator;
}

return {
...accumulator,
[methodName]: IDL.Func(
paramRealIdls,
returnRealIdl,
annotations
)
};
},
{} as Record<string, IDL.FuncClass>
);

return IDL.Service(record);
});

if (originalPrincipal !== undefined && originalPrincipal._isPrincipal) {
return returnFunction(originalPrincipal);
Expand Down
3 changes: 3 additions & 0 deletions src/lib_new/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export function toIDLType(idl: CandidClass, parents: Parent[]): IDL.Type<any> {
}
return idl.getIDL(parents);
}
if (idl._azleIsCanister) {
return toIDLType(idl(), parents);
}
// if (idl.display === undefined || idl.getIDL === undefined) {
// throw Error(`${JSON.stringify(idl)} is not a candid type`);
// }
Expand Down
75 changes: 52 additions & 23 deletions src/lib_new/visitors/did_visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type VisitorData = {
usedRecClasses: IDL.RecClass[];
isOnService: boolean;
isFirstService: boolean;
systemFuncs: IDL.FuncClass[];
};
type VisitorResult = [CandidDef, CandidTypesDefs];

Expand All @@ -17,7 +18,8 @@ type VisitorResult = [CandidDef, CandidTypesDefs];
export const DEFAULT_VISITOR_DATA: VisitorData = {
usedRecClasses: [],
isOnService: false,
isFirstService: false
isFirstService: false,
systemFuncs: []
};

export function DidResultToCandidString(result: VisitorResult): string {
Expand Down Expand Up @@ -56,6 +58,13 @@ export function extractCandid(
return [paramCandid, candidTypeDefs];
}

function hch(value: any) {
if (value._azleIsCanister) {
return value().getIDL();
}
return value;
}

export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
visitService(t: IDL.ServiceClass, data: VisitorData): VisitorResult {
// To get all of the candid types we need to look at all of the methods
Expand All @@ -71,22 +80,31 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
t._fields
.filter(([_name, func]) => isOtherFunction(func))
.map(([_name, func]) =>
func.accept(this, { ...data, isOnService: true })
func.accept(this, {
...data,
isOnService: true,
isFirstService: false
})
)
)[1];

const isQueryOrUpdateFunction = (func: IDL.FuncClass) => {
return (
func.annotations.includes('update') ||
func.annotations.includes('query')
func.annotations.includes('query') ||
func.annotations.length === 0 //updates have not annotations
);
};
// To get all of the canister methods we need to only look at update and query
const canisterMethods = extractCandid(
t._fields
.filter(([_name, func]) => isQueryOrUpdateFunction(func))
.map(([_name, func]) =>
func.accept(this, { ...data, isOnService: true })
func.accept(this, {
...data,
isOnService: true,
isFirstService: false
})
)
);
const canisterMethodsNames = t._fields
Expand All @@ -99,17 +117,25 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
func.annotations.includes('postUpgrade');
// To get the service params we need to look at the init function
const initMethod = extractCandid(
t._fields
.filter(([_name, func]) => isInitFunction(func))
.map(([_name, initFunc]) =>
initFunc.accept(this, { ...data, isOnService: true })
data.systemFuncs
.filter((func) => isInitFunction(func))
.map((initFunc) =>
initFunc.accept(this, {
...data,
isOnService: true,
isFirstService: false
})
)
);
const postMethod = extractCandid(
t._fields
.filter(([_name, func]) => isPostUpgradeFunction(func))
.map(([_name, initFunc]) =>
initFunc.accept(this, { ...data, isOnService: true })
data.systemFuncs
.filter((func) => isPostUpgradeFunction(func))
.map((initFunc) =>
initFunc.accept(this, {
...data,
isOnService: true,
isFirstService: false
})
)
);
const initMethodCandidString = initMethod[0];
Expand Down Expand Up @@ -144,18 +170,21 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
...postMethod[1]
};

const tab = data.isFirstService ? ' ' : '';
const func_separator = data.isFirstService ? '\n' : ' ';

const funcStrings = canisterMethods[0]
.map((value, index) => {
return ` ${canisterMethodsNames[index]}: ${value};`;
return `${tab}${canisterMethodsNames[index]}: ${value};`;
})
.join('\n');
.join(func_separator);
if (data.isFirstService) {
return [
`service: ${canisterParamsString} -> {\n${funcStrings}\n}`,
candidTypes
];
}
return [`service {\n${funcStrings}\n}`, candidTypes];
return [`service {${funcStrings}}`, candidTypes];
}
visitPrimitive<T>(
t: IDL.PrimitiveType<T>,
Expand All @@ -169,7 +198,7 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
data: VisitorData
): VisitorResult {
const fields = components.map((value) =>
value.accept(this, { ...data, isOnService: false })
hch(value).accept(this, { ...data, isOnService: false })
);
const candid = extractCandid(fields);
return [`record {${candid[0].join('; ')}}`, candid[1]];
Expand All @@ -179,24 +208,24 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
ty: IDL.Type<T>,
data: VisitorData
): VisitorResult {
const candid = ty.accept(this, { ...data, isOnService: false });
const candid = hch(ty).accept(this, { ...data, isOnService: false });
return [`opt ${candid[0]}`, candid[1]];
}
visitVec<T>(
t: IDL.VecClass<T>,
ty: IDL.Type<T>,
data: VisitorData
): VisitorResult {
const candid = ty.accept(this, { ...data, isOnService: false });
const candid = hch(ty).accept(this, { ...data, isOnService: false });
return [`vec ${candid[0]}`, candid[1]];
}
visitFunc(t: IDL.FuncClass, data: VisitorData): VisitorResult {
const argsTypes = t.argTypes.map((value) =>
value.accept(this, { ...data, isOnService: false })
hch(value).accept(this, { ...data, isOnService: false })
);
const candidArgs = extractCandid(argsTypes);
const retsTypes = t.retTypes.map((value) =>
value.accept(this, { ...data, isOnService: false })
hch(value).accept(this, { ...data, isOnService: false })
);
const candidRets = extractCandid(retsTypes);
const args = candidArgs[0].join(', ');
Expand All @@ -220,7 +249,7 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
// Everything else will just be the normal inline candid def
const usedRecClasses = data.usedRecClasses;
if (!usedRecClasses.includes(t)) {
const candid = ty.accept(this, {
const candid = hch(ty).accept(this, {
usedRecClasses: [...usedRecClasses, t],
isOnService: false,
isFirstService: false
Expand All @@ -237,7 +266,7 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
data: VisitorData
): VisitorResult {
const candidFields = fields.map(([key, value]) =>
value.accept(this, { ...data, isOnService: false })
hch(value).accept(this, { ...data, isOnService: false })
);
const candid = extractCandid(candidFields);
const field_strings = fields.map(
Expand All @@ -251,7 +280,7 @@ export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
data: VisitorData
): VisitorResult {
const candidFields = fields.map(([key, value]) =>
value.accept(this, { ...data, isOnService: false })
hch(value).accept(this, { ...data, isOnService: false })
);
const candid = extractCandid(candidFields);
const fields_string = fields.map(
Expand Down
Loading

0 comments on commit 2758f2f

Please sign in to comment.