diff --git a/canister_templates/experimental.wasm b/canister_templates/experimental.wasm index 9a57d6cdff..04e2d7370d 100644 Binary files a/canister_templates/experimental.wasm and b/canister_templates/experimental.wasm differ diff --git a/canister_templates/stable.wasm b/canister_templates/stable.wasm index 7b2cd12e4f..337bd195aa 100644 Binary files a/canister_templates/stable.wasm and b/canister_templates/stable.wasm differ diff --git a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/execute_method_js.rs b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/execute_method_js.rs index 85a18de052..1ec46bbc39 100644 --- a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/execute_method_js.rs +++ b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/execute_method_js.rs @@ -14,12 +14,14 @@ pub extern "C" fn execute_method_js(function_index: i32, pass_arg_data: i32) { runtime.run_with_context(|context| { let global = context.get_global(); - let canister_class_instance = global.get("_azleCanisterClassInstance"); + let exported_canister_class_instance = global.get("_azleExportedCanisterClassInstance"); - let callbacks = if matches!(canister_class_instance, JsValue::UnDefined) { + let callbacks = if matches!(exported_canister_class_instance, JsValue::UnDefined) { global.get("_azleCallbacks") } else { - canister_class_instance.get("_azleCallbacks").unwrap() + exported_canister_class_instance + .get("_azleCallbacks") + .unwrap() }; let method_callback = callbacks.get(&function_name).unwrap(); diff --git a/src/build/stable/commands/compile/javascript.ts b/src/build/stable/commands/compile/javascript.ts index 46f13a9630..27e471d421 100644 --- a/src/build/stable/commands/compile/javascript.ts +++ b/src/build/stable/commands/compile/javascript.ts @@ -25,20 +25,20 @@ function getPrelude(main: string): string { export function handleClassApiCanister(): string { return /*TS*/ ` - const canisterClassInstance = new Canister.default(); - globalThis._azleCanisterClassInstance = canisterClassInstance; + const exportedCanisterClassInstance = new Canister.default(); + globalThis._azleExportedCanisterClassInstance = exportedCanisterClassInstance; - const canisterIdlType = IDL.Service(canisterClassInstance._azleCanisterMethodIdlTypes); + const canisterIdlType = IDL.Service(exportedCanisterClassInstance._azleCanisterMethodIdlTypes); const candid = canisterIdlType.accept(new DidVisitor(), { ...getDefaultVisitorData(), isFirstService: true, - systemFuncs: canisterClassInstance._azleInitAndPostUpgradeIdlTypes + systemFuncs: exportedCanisterClassInstance._azleInitAndPostUpgradeIdlTypes }); globalThis._azleGetCandidAndMethodMeta = () => { return JSON.stringify({ candid: toDidString(candid), - methodMeta: canisterClassInstance._azleMethodMeta + methodMeta: exportedCanisterClassInstance._azleMethodMeta }); }; `; @@ -111,7 +111,7 @@ function experimentalMessage(importName: string): string { function handleBenchmarking(): string { return /*TS*/ ` if (globalThis._azleRecordBenchmarks === true) { - const methodMeta = canisterClassInstance._azleMethodMeta; + const methodMeta = exportedCanisterClassInstance._azleMethodMeta; globalThis._azleCanisterMethodNames = Object.entries(methodMeta).reduce((acc, [key, value]) => { if (value === undefined) { diff --git a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/execute_method_js.rs b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/execute_method_js.rs index 995d6e8f74..18d1585135 100644 --- a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/execute_method_js.rs +++ b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/execute_method_js.rs @@ -29,18 +29,24 @@ fn execute_method_js_with_result( pass_arg_data: bool, ) -> Result<(), Box> { quickjs_with_ctx(|ctx| { - let canister_class_instance: Object = ctx + let exported_canister_class_instance: Object = ctx .clone() .globals() - .get("_azleCanisterClassInstance") - .map_err(|e| format!("Failed to get globalThis._azleCanisterClassInstance: {e}"))?; + .get("_azleExportedCanisterClassInstance") + .map_err(|e| { + format!("Failed to get globalThis._azleExportedCanisterClassInstance: {e}") + })?; - let callbacks: Object = canister_class_instance + let callbacks: Object = exported_canister_class_instance .get("_azleCallbacks") - .map_err(|e| format!("Failed to get canisterClassInstance._azleCallbacks: {e}"))?; + .map_err(|e| { + format!("Failed to get exportedCanisterClassInstance._azleCallbacks: {e}") + })?; let method_callback: Function = callbacks.get(&function_name).map_err(|e| { - format!("Failed to get canisterClassInstance._azleCallbacks[{function_name}]: {e}") + format!( + "Failed to get exportedCanisterClassInstance._azleCallbacks[{function_name}]: {e}" + ) })?; let candid_args = if pass_arg_data { diff --git a/src/lib/stable/canister_methods/heartbeat.ts b/src/lib/stable/canister_methods/heartbeat.ts index c639d3f5dd..90a43662cd 100644 --- a/src/lib/stable/canister_methods/heartbeat.ts +++ b/src/lib/stable/canister_methods/heartbeat.ts @@ -2,7 +2,7 @@ import { decoratorArgumentsHandler, MethodType } from '.'; export function heartbeat( originalMethod: MethodType, - context: ClassMethodDecoratorContext + context: ClassMethodDecoratorContext> ): void { decoratorArgumentsHandler('heartbeat', originalMethod, context); } diff --git a/src/lib/stable/canister_methods/index.ts b/src/lib/stable/canister_methods/index.ts index 24c79e741c..52e9e1ce1d 100644 --- a/src/lib/stable/canister_methods/index.ts +++ b/src/lib/stable/canister_methods/index.ts @@ -1,11 +1,22 @@ import { IDL } from '@dfinity/candid'; +import { MethodMeta } from '../../../build/stable/utils/types'; import { handleUncaughtError } from '../error'; import { CanisterMethodMode, executeAndReplyWithCandidSerde } from '../execute_with_candid_serde'; +export interface ExportedCanisterClass { + _azleCallbacks?: { + [key: string]: MethodType; + }; + _azleCanisterMethodIdlTypes?: { [key: string]: IDL.FuncClass }; + _azleCanisterMethodsIndex?: number; + _azleInitAndPostUpgradeIdlTypes?: IDL.FuncClass[]; + _azleMethodMeta?: MethodMeta; +} + export type MethodType = ( this: This, ...args: Args @@ -13,23 +24,32 @@ export type MethodType = ( export type DecoratorFunction = ( originalMethod: MethodType, - context: ClassMethodDecoratorContext + context: ClassMethodDecoratorContext> ) => MethodType; export function decoratorArgumentsHandler( canisterMethodMode: CanisterMethodMode, param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext | IDL.Type, + param2?: + | ClassMethodDecoratorContext> + | IDL.Type, param3?: { composite?: boolean; manual?: boolean } -): MethodType | DecoratorFunction { - const decoratorIsOverloadWithoutParams = isDecoratorOverloadWithoutParams( - param1, - param2 - ); - - if (decoratorIsOverloadWithoutParams === true) { - const originalMethod = param1 as MethodType; - const context = param2 as ClassMethodDecoratorContext; +): + | MethodType + | DecoratorFunction { + const decoratorIsOverloadedWithoutParams = + isDecoratorOverloadedWithoutParams(param1, param2); + + if (decoratorIsOverloadedWithoutParams === true) { + const originalMethod = param1 as MethodType< + ExportedCanisterClass, + Args, + Return + >; + const context = param2 as ClassMethodDecoratorContext< + ExportedCanisterClass, + MethodType + >; return decoratorImplementation( canisterMethodMode, @@ -42,9 +62,12 @@ export function decoratorArgumentsHandler( const options = param3; return ( - originalMethod: MethodType, - context: ClassMethodDecoratorContext - ): MethodType => { + originalMethod: MethodType, + context: ClassMethodDecoratorContext< + ExportedCanisterClass, + MethodType + > + ): MethodType => { return decoratorImplementation( canisterMethodMode, originalMethod, @@ -57,9 +80,11 @@ export function decoratorArgumentsHandler( } } -function isDecoratorOverloadWithoutParams( +function isDecoratorOverloadedWithoutParams( param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext | IDL.Type + param2?: + | ClassMethodDecoratorContext> + | IDL.Type ): boolean { return ( typeof param1 === 'function' && @@ -71,55 +96,55 @@ function isDecoratorOverloadWithoutParams( ); } -function decoratorImplementation( +function decoratorImplementation< + This extends ExportedCanisterClass, + Args extends any[], + Return +>( canisterMethodMode: CanisterMethodMode, originalMethod: MethodType, - context: ClassMethodDecoratorContext, + context: ClassMethodDecoratorContext>, paramIdlTypes?: IDL.Type[], returnIdlType?: IDL.Type, options?: { composite?: boolean; manual?: boolean } ): MethodType { context.addInitializer(function () { - let canisterClassInstance = this as any; - - if (canisterClassInstance._azleCanisterMethodsIndex === undefined) { - canisterClassInstance._azleCanisterMethodsIndex = 0; + if (this._azleCanisterMethodsIndex === undefined) { + this._azleCanisterMethodsIndex = 0; } - if (canisterClassInstance._azleCanisterMethodIdlTypes === undefined) { - canisterClassInstance._azleCanisterMethodIdlTypes = {}; + if (this._azleCanisterMethodIdlTypes === undefined) { + this._azleCanisterMethodIdlTypes = {}; } - if ( - canisterClassInstance._azleInitAndPostUpgradeIdlTypes === undefined - ) { - canisterClassInstance._azleInitAndPostUpgradeIdlTypes = []; + if (this._azleInitAndPostUpgradeIdlTypes === undefined) { + this._azleInitAndPostUpgradeIdlTypes = []; } - if (canisterClassInstance._azleMethodMeta === undefined) { - canisterClassInstance._azleMethodMeta = { + if (this._azleMethodMeta === undefined) { + this._azleMethodMeta = { queries: [], updates: [] }; } - if (canisterClassInstance._azleCallbacks === undefined) { - canisterClassInstance._azleCallbacks = {}; + if (this._azleCallbacks === undefined) { + this._azleCallbacks = {}; } const name = context.name as string; - const index = canisterClassInstance._azleCanisterMethodsIndex++; + const index = this._azleCanisterMethodsIndex++; const indexString = index.toString(); if (canisterMethodMode === 'query') { - canisterClassInstance._azleMethodMeta.queries?.push({ + this._azleMethodMeta.queries?.push({ name, index, composite: options?.composite ?? false }); - canisterClassInstance._azleCanisterMethodIdlTypes[name] = IDL.Func( + this._azleCanisterMethodIdlTypes[name] = IDL.Func( paramIdlTypes ?? [], returnIdlType === undefined ? [] : [returnIdlType], ['query'] @@ -127,68 +152,68 @@ function decoratorImplementation( } if (canisterMethodMode === 'update') { - canisterClassInstance._azleMethodMeta.updates?.push({ + this._azleMethodMeta.updates?.push({ name, index }); - canisterClassInstance._azleCanisterMethodIdlTypes[name] = IDL.Func( + this._azleCanisterMethodIdlTypes[name] = IDL.Func( paramIdlTypes ?? [], returnIdlType === undefined ? [] : [returnIdlType] ); } if (canisterMethodMode === 'init') { - canisterClassInstance._azleMethodMeta.init = { + this._azleMethodMeta.init = { name, index }; - canisterClassInstance._azleInitAndPostUpgradeIdlTypes.push( + this._azleInitAndPostUpgradeIdlTypes.push( IDL.Func(paramIdlTypes ?? [], [], ['init']) ); } if (canisterMethodMode === 'postUpgrade') { - canisterClassInstance._azleMethodMeta.post_upgrade = { + this._azleMethodMeta.post_upgrade = { name, index }; - canisterClassInstance._azleInitAndPostUpgradeIdlTypes.push( + this._azleInitAndPostUpgradeIdlTypes.push( IDL.Func(paramIdlTypes ?? [], [], ['post_upgrade']) ); } if (canisterMethodMode === 'preUpgrade') { - canisterClassInstance._azleMethodMeta.pre_upgrade = { + this._azleMethodMeta.pre_upgrade = { name, index }; } if (canisterMethodMode === 'heartbeat') { - canisterClassInstance._azleMethodMeta.heartbeat = { + this._azleMethodMeta.heartbeat = { name, index }; } if (canisterMethodMode === 'inspectMessage') { - canisterClassInstance._azleMethodMeta.inspect_message = { + this._azleMethodMeta.inspect_message = { name, index }; } - canisterClassInstance._azleCallbacks[indexString] = async ( + this._azleCallbacks[indexString] = async ( args?: Uint8Array ): Promise => { try { await executeAndReplyWithCandidSerde( canisterMethodMode, args ?? new Uint8Array(), - originalMethod.bind(canisterClassInstance), + originalMethod.bind(this), // TODO manually test context and ensure we have automatic tests for this paramIdlTypes ?? [], returnIdlType, options?.manual ?? false diff --git a/src/lib/stable/canister_methods/init.ts b/src/lib/stable/canister_methods/init.ts index d734ec0eb3..e0a4828e4b 100644 --- a/src/lib/stable/canister_methods/init.ts +++ b/src/lib/stable/canister_methods/init.ts @@ -1,22 +1,29 @@ import { IDL } from '@dfinity/candid'; -import { decoratorArgumentsHandler, DecoratorFunction, MethodType } from '.'; +import { + decoratorArgumentsHandler, + DecoratorFunction, + ExportedCanisterClass, + MethodType +} from '.'; export function init( originalMethod: MethodType, - context: ClassMethodDecoratorContext -): MethodType; + context: ClassMethodDecoratorContext> +): MethodType; export function init( paramIdlTypes?: IDL.Type[] ): ( originalMethod: MethodType, - context: ClassMethodDecoratorContext -) => MethodType; + context: ClassMethodDecoratorContext> +) => MethodType; export function init( param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext -): MethodType | DecoratorFunction { + param2?: ClassMethodDecoratorContext> +): + | MethodType + | DecoratorFunction { return decoratorArgumentsHandler('init', param1, param2); } diff --git a/src/lib/stable/canister_methods/inspect_message.ts b/src/lib/stable/canister_methods/inspect_message.ts index dbcf9a72de..2b8f71d624 100644 --- a/src/lib/stable/canister_methods/inspect_message.ts +++ b/src/lib/stable/canister_methods/inspect_message.ts @@ -3,7 +3,7 @@ import { decoratorArgumentsHandler, MethodType } from '.'; // TODO explain here in a jsdoc that the dev can get the raw args using argDataRaw export function inspectMessage( originalMethod: MethodType, - context: ClassMethodDecoratorContext + context: ClassMethodDecoratorContext> ): void { decoratorArgumentsHandler('inspectMessage', originalMethod, context); } diff --git a/src/lib/stable/canister_methods/post_upgrade.ts b/src/lib/stable/canister_methods/post_upgrade.ts index 68e2ca3fb8..e73e5ea69c 100644 --- a/src/lib/stable/canister_methods/post_upgrade.ts +++ b/src/lib/stable/canister_methods/post_upgrade.ts @@ -1,22 +1,29 @@ import { IDL } from '@dfinity/candid'; -import { decoratorArgumentsHandler, DecoratorFunction, MethodType } from '.'; +import { + decoratorArgumentsHandler, + DecoratorFunction, + ExportedCanisterClass, + MethodType +} from '.'; export function postUpgrade( originalMethod: MethodType, - context: ClassMethodDecoratorContext -): MethodType; + context: ClassMethodDecoratorContext> +): MethodType; export function postUpgrade( paramIdlTypes?: IDL.Type[] ): ( originalMethod: MethodType, - context: ClassMethodDecoratorContext -) => MethodType; + context: ClassMethodDecoratorContext> +) => MethodType; export function postUpgrade( param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext -): MethodType | DecoratorFunction { + param2?: ClassMethodDecoratorContext> +): + | MethodType + | DecoratorFunction { return decoratorArgumentsHandler('postUpgrade', param1, param2); } diff --git a/src/lib/stable/canister_methods/pre_upgrade.ts b/src/lib/stable/canister_methods/pre_upgrade.ts index 23db4e8c85..1dc13bfc72 100644 --- a/src/lib/stable/canister_methods/pre_upgrade.ts +++ b/src/lib/stable/canister_methods/pre_upgrade.ts @@ -2,7 +2,7 @@ import { decoratorArgumentsHandler, MethodType } from '.'; export function preUpgrade( originalMethod: MethodType, - context: ClassMethodDecoratorContext + context: ClassMethodDecoratorContext> ): void { decoratorArgumentsHandler('preUpgrade', originalMethod, context); } diff --git a/src/lib/stable/canister_methods/query.ts b/src/lib/stable/canister_methods/query.ts index 57427827c3..432d64cdd6 100644 --- a/src/lib/stable/canister_methods/query.ts +++ b/src/lib/stable/canister_methods/query.ts @@ -1,11 +1,16 @@ import { IDL } from '@dfinity/candid'; -import { decoratorArgumentsHandler, DecoratorFunction, MethodType } from '.'; +import { + decoratorArgumentsHandler, + DecoratorFunction, + ExportedCanisterClass, + MethodType +} from '.'; export function query( originalMethod: MethodType, - context: ClassMethodDecoratorContext -): MethodType; + context: ClassMethodDecoratorContext> +): MethodType; export function query( paramIdlTypes?: IDL.Type[], @@ -16,13 +21,17 @@ export function query( } ): ( originalMethod: MethodType, - context: ClassMethodDecoratorContext -) => MethodType; + context: ClassMethodDecoratorContext> +) => MethodType; export function query( param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext | IDL.Type, + param2?: + | ClassMethodDecoratorContext> + | IDL.Type, param3?: { composite?: boolean; manual?: boolean } -): MethodType | DecoratorFunction { +): + | MethodType + | DecoratorFunction { return decoratorArgumentsHandler('query', param1, param2, param3); } diff --git a/src/lib/stable/canister_methods/update.ts b/src/lib/stable/canister_methods/update.ts index 818426bbb6..37f00e84f8 100644 --- a/src/lib/stable/canister_methods/update.ts +++ b/src/lib/stable/canister_methods/update.ts @@ -1,11 +1,16 @@ import { IDL } from '@dfinity/candid'; -import { decoratorArgumentsHandler, DecoratorFunction, MethodType } from '.'; +import { + decoratorArgumentsHandler, + DecoratorFunction, + ExportedCanisterClass, + MethodType +} from '.'; export function update( originalMethod: MethodType, - context: ClassMethodDecoratorContext -): MethodType; + context: ClassMethodDecoratorContext> +): MethodType; export function update( paramIdlTypes?: IDL.Type[], @@ -15,13 +20,17 @@ export function update( } ): ( originalMethod: MethodType, - context: ClassMethodDecoratorContext -) => MethodType; + context: ClassMethodDecoratorContext> +) => MethodType; export function update( param1?: MethodType | IDL.Type[], - param2?: ClassMethodDecoratorContext | IDL.Type, + param2?: + | ClassMethodDecoratorContext> + | IDL.Type, param3?: { manual?: boolean } -): MethodType | DecoratorFunction { +): + | MethodType + | DecoratorFunction { return decoratorArgumentsHandler('update', param1, param2, param3); } diff --git a/src/lib/stable/globals.ts b/src/lib/stable/globals.ts index f0a34e8d5a..ea589ebd9f 100644 --- a/src/lib/stable/globals.ts +++ b/src/lib/stable/globals.ts @@ -3,13 +3,13 @@ import { TextDecoder, TextEncoder } from '@sinonjs/text-encoding'; import { AzleIcExperimental } from '../experimental/ic/azle_ic_experimental'; import { jsonReplacer } from '../stable/stable_structures/stable_json'; +import { ExportedCanisterClass } from './canister_methods'; import { print } from './ic_apis'; import { AzleIcStable } from './ic_apis/azle_ic_stable'; -// TODO move the types over into the experimental global types that we just moved declare global { // eslint-disable-next-line no-var - var _azleCanisterClassInstance: any; + var _azleExportedCanisterClassInstance: ExportedCanisterClass; // eslint-disable-next-line no-var var _azleCanisterMethodNames: { [key: string]: string }; // eslint-disable-next-line no-var