Skip to content

Commit

Permalink
Merge pull request #2422 from demergent-labs/rc_final_rundown
Browse files Browse the repository at this point in the history
Rc final rundown
  • Loading branch information
lastmjs authored Jan 16, 2025
2 parents 2ffd199 + b5fc3f7 commit 0d6eff5
Show file tree
Hide file tree
Showing 58 changed files with 840 additions and 175 deletions.
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This NOTICE pertains to some code snippets shown in [README.md](/README.md).
This NOTICE pertains to some code snippets shown in [README.md](/README.md), various `JSDocs`, and other documentation.

Copyright 2021 DFINITY Stiftung

Expand Down
Binary file modified canister_templates/experimental.wasm
Binary file not shown.
Binary file modified canister_templates/stable.wasm
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ic_cdk::api::canister_version;
use rquickjs::{Ctx, Function, Result};
use rquickjs::{BigInt, Ctx, Function, Result};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(ctx, || canister_version())
Function::new(ctx.clone(), move || {
BigInt::from_u64(ctx.clone(), canister_version())
})
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use ic_cdk::api::performance_counter;
use rquickjs::{Ctx, Function, Result};
use rquickjs::{BigInt, Ctx, Function, Result};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(ctx, |counter_type: u32| -> u64 {
performance_counter(counter_type)
Function::new(ctx.clone(), move |counter_type: u32| -> Result<BigInt> {
BigInt::from_u64(ctx.clone(), performance_counter(counter_type))
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use core::time::Duration;

use ic_cdk::trap;
use ic_cdk_timers::{set_timer, TimerId};
use rquickjs::{Ctx, Function, Object, Result};
use rquickjs::{BigInt, Ctx, Function, Object, Result};
use slotmap::Key;

use crate::{error::quickjs_call_with_error_handling, ic::throw_error, quickjs_with_ctx};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(
ctx.clone(),
move |delay: String, callback_id: String| -> Result<u64> {
move |delay: String, callback_id: String| -> Result<BigInt> {
let delay: u64 = delay.parse().map_err(|e| throw_error(ctx.clone(), e))?;
let delay_duration = Duration::new(delay, 0);

Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn get_function(ctx: Ctx) -> Result<Function> {
let timer_id: TimerId = set_timer(delay_duration, closure);
let timer_id_u64: u64 = timer_id.data().as_ffi();

Ok(timer_id_u64)
Ok(BigInt::from_u64(ctx.clone(), timer_id_u64)?)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use core::time::Duration;

use ic_cdk::trap;
use ic_cdk_timers::{set_timer_interval, TimerId};
use rquickjs::{Ctx, Function, Object, Result};
use rquickjs::{BigInt, Ctx, Function, Object, Result};
use slotmap::Key;

use crate::{error::quickjs_call_with_error_handling, ic::throw_error, quickjs_with_ctx};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(
ctx.clone(),
move |interval: String, callback_id: String| -> Result<u64> {
move |interval: String, callback_id: String| -> Result<BigInt> {
let interval: u64 = interval.parse().map_err(|e| throw_error(ctx.clone(), e))?;
let interval_duration = Duration::new(interval, 0);

Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn get_function(ctx: Ctx) -> Result<Function> {
let timer_id: TimerId = set_timer_interval(interval_duration, closure);
let timer_id_u64: u64 = timer_id.data().as_ffi();

Ok(timer_id_u64)
Ok(BigInt::from_u64(ctx.clone(), timer_id_u64)?)
},
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ic_cdk::api::time;
use rquickjs::{Ctx, Function, Result};
use rquickjs::{BigInt, Ctx, Function, Result};

pub fn get_function(ctx: Ctx) -> Result<Function> {
Function::new(ctx, || time())
Function::new(ctx.clone(), move || -> Result<BigInt> {
BigInt::from_u64(ctx.clone(), time())
})
}
22 changes: 22 additions & 0 deletions src/lib/stable/did_file/to_did_string.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import { CandidTypesDefs, VisitorResult } from './visitor/index.js';

/**
* Converts a Candid type visitor result into a formatted Candid interface definition string.
* Used to generate .did files from TypeScript canister definitions.
*
* @param result - The visitor result containing Candid type definitions and service interface
* @returns A formatted string containing the complete Candid interface definition
*
* @remarks
* - Combines named type definitions with the service interface
* - Handles recursive type definitions (currently using numeric suffixes)
* - Preserves type relationships and structure
* - Adds proper newlines for formatting
*
* @example
* const visitorResult = visitCanister(canisterClass);
* const didString = toDidString(visitorResult);
* // Result:
* // type MyRecord = record { field: text };
* // service : {
* // method : (MyRecord) -> (bool);
* // }
*/
export function toDidString(result: VisitorResult): string {
// TODO it would be nice to have names for the rec types instead of rec_1, rec_2 etc
// TODO Once types have names we should deduplicate the init and post_upgrade param types
Expand Down
46 changes: 46 additions & 0 deletions src/lib/stable/did_file/visitor/did_visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,51 @@ import { visitTuple } from './visit/tuple';
import { visitVariant } from './visit/variant';
import { visitVec } from './visit/vec';

/**
* Configuration data passed through the Candid type visitor system.
* Controls visitor behavior and tracks state during traversal.
*/
export type VisitorData = {
/** Tracks recursive types to prevent infinite loops */
usedRecClasses: IDL.RecClass[];
/** Indicates if currently visiting a service definition */
isOnService: boolean;
/** Indicates if this is the first/primary service being processed */
isFirstService: boolean;
/** Collection of system functions (init, postUpgrade) to process */
systemFuncs: IDL.FuncClass[];
};

/**
* Result tuple returned by visitor operations.
* Combines Candid definitions with their associated type definitions.
*/
export type VisitorResult = [CandidDef, CandidTypesDefs];

/**
* Name of a Candid type definition.
* Used as keys in the type definition map.
*/
export type TypeName = string;

/**
* String representation of a Candid type or definition.
* The actual Candid syntax for a type, method, or service.
*/
export type CandidDef = string;

/**
* Map of named type definitions in a Candid interface.
* Keys are type names, values are their Candid definitions.
*/
export type CandidTypesDefs = { [key: TypeName]: CandidDef };

/**
* Creates default visitor configuration data.
* Used to initialize the visitor system for processing a canister's types.
*
* @returns Fresh VisitorData with empty tracking collections
*/
export function getDefaultVisitorData(): VisitorData {
return {
usedRecClasses: [],
Expand All @@ -31,6 +64,19 @@ export function getDefaultVisitorData(): VisitorData {
};
}

/**
* Visitor implementation for converting TypeScript/IDL types to Candid definitions.
* Extends the IDL.Visitor to handle all Candid type constructs.
*
* Used to generate .did interface files from canister class definitions.
* Processes types recursively while maintaining proper scoping and type relationships.
*
* @example
* const visitor = new DidVisitor();
* const myType = new IDL.Service({...implementation});
* const result = myType.accept(visitor, getDefaultVisitorData());
* const candidString = toDidString(result);
*/
export class DidVisitor extends IDL.Visitor<VisitorData, VisitorResult> {
visitService(t: IDL.ServiceClass, data: VisitorData): VisitorResult {
return visitService(t, this, data);
Expand Down
19 changes: 19 additions & 0 deletions src/lib/stable/did_file/visitor/escape_candid_keywords.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* @internal
*
* Internal list of Candid language keywords that need to be escaped in identifiers.
*
* These keywords cannot be used as raw identifiers in Candid and must be quoted when used as field names.
*/
const CANDID_KEYWORDS = [
'blob',
'bool',
Expand Down Expand Up @@ -25,6 +32,18 @@ const CANDID_KEYWORDS = [
'vec'
];

/**
* @internal
*
* Internal helper that quotes Candid keywords when they appear as identifiers.
*
* @param key - The identifier to potentially escape
* @returns The identifier, quoted if it's a Candid keyword
*
* @example
* escapeCandidKeywords('text') // returns '"text"'
* escapeCandidKeywords('myField') // returns 'myField'
*/
export function escapeCandidKeywords(key: string): string {
if (CANDID_KEYWORDS.includes(key)) {
return `"${key}"`;
Expand Down
15 changes: 15 additions & 0 deletions src/lib/stable/did_file/visitor/extract_candid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { CandidDef, CandidTypesDefs } from './did_visitor';

/**
* @internal
*
* Internal helper for the Candid visitor system that combines multiple visitor results.
* Separates Candid definitions from their associated type definitions.
*
* @param paramInfo - Array of visitor results, each containing a Candid definition and its type definitions
* @returns Tuple of [Array of Candid definitions, Combined type definitions map]
*
* @remarks
* Used by visitor components to:
* - Extract Candid definitions for method parameters, records, variants etc.
* - Merge type definitions from multiple visited nodes
* - Maintain type relationships in the final Candid interface
*/
export function extractCandid(
paramInfo: [CandidDef, CandidTypesDefs][]
): [CandidDef[], CandidTypesDefs] {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { IDL } from '@dfinity/candid';
import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';
import { extractCandid } from '../extract_candid';

/**
* @internal
* Visitor for function types in Candid generation.
*/
export function visitFunc(
t: IDL.FuncClass,
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/opt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { IDL } from '@dfinity/candid';

import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';

/**
* @internal
* Visitor for optional types in Candid generation.
*/
export function visitOpt<T>(
ty: IDL.Type<T>,
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { IDL } from '@dfinity/candid';

import { VisitorResult } from '../did_visitor';

/**
* @internal
* Visitor for primitive types in Candid generation.
*/
export function visitPrimitive<T>(t: IDL.PrimitiveType<T>): VisitorResult {
return [t.display(), {}];
}
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';
import { escapeCandidKeywords } from '../escape_candid_keywords';
import { extractCandid } from '../extract_candid';

/**
* @internal
* Visitor for record types in Candid generation.
*/
export function visitRecord(
fields: [string, IDL.Type<any>][],
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/recursive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { IDL } from '@dfinity/candid';

import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';

/**
* @internal
* Visitor for recursive types in Candid generation.
*/
export function visitRecursive<T>(
t: IDL.RecClass<T>,
ty: IDL.ConstructType<T>,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
import { escapeCandidKeywords } from '../escape_candid_keywords';
import { extractCandid } from '../extract_candid';

/**
* @internal
* Visitor for service definitions in Candid generation.
*/
export function visitService(
t: IDL.ServiceClass,
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/tuple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { IDL } from '@dfinity/candid';
import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';
import { extractCandid } from '../extract_candid';

/**
* @internal
* Visitor for tuple types in Candid generation.
*/
export function visitTuple(
components: IDL.Type<any>[],
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';
import { escapeCandidKeywords } from '../escape_candid_keywords';
import { extractCandid } from '../extract_candid';

/**
* @internal
* Visitor for variant types in Candid generation.
*/
export function visitVariant(
fields: [string, IDL.Type<any>][],
didVisitor: DidVisitor,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stable/did_file/visitor/visit/vec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { IDL } from '@dfinity/candid';

import { DidVisitor, VisitorData, VisitorResult } from '../did_visitor';

/**
* @internal
* Visitor for vector types in Candid generation.
*/
export function visitVec<T>(
ty: IDL.Type<T>,
didVisitor: DidVisitor,
Expand Down
8 changes: 8 additions & 0 deletions src/lib/stable/error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { trap } from './ic_apis/trap';

/**
* Handles uncaught errors in the canister execution environment by converting them
* to a formatted error message and calling IC trap. This function ensures that all
* uncaught errors are properly reported with stack traces before halting execution.
*
* @param rawError - The raw error value to handle. Can be an Error object or any other value
* @returns never - This function always traps and never returns
*/
export function handleUncaughtError(rawError: any): never {
if (rawError instanceof Error) {
const error = rawError;
Expand Down
Loading

0 comments on commit 0d6eff5

Please sign in to comment.