From 2d372fbc370f116f05097e16ebdcb167733a7962 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Sat, 11 Feb 2023 15:45:42 +0530 Subject: [PATCH 1/5] feat: complete initial tuple & enum support --- ts-packages/contract-metadata/src/specs.ts | 13 +-- ts-packages/contract-metadata/src/types.ts | 12 +-- ts-packages/transform/src/ast.ts | 4 + .../transform/src/metadata/generator.ts | 99 ++++++++++++++++++- .../transform/src/metadata/typeInfo.ts | 13 +++ .../transform/src/metadata/typeResolver.ts | 78 ++++++++++++++- 6 files changed, 204 insertions(+), 15 deletions(-) diff --git a/ts-packages/contract-metadata/src/specs.ts b/ts-packages/contract-metadata/src/specs.ts index d35e5e1..42d96bc 100644 --- a/ts-packages/contract-metadata/src/specs.ts +++ b/ts-packages/contract-metadata/src/specs.ts @@ -86,9 +86,10 @@ export type IPrimitiveDef = Def<{ }>; export type ITupleDef = Def<{ - readonly tuple: { - readonly fields: Array; - }; + readonly tuple: Array; + // readonly tuple: { + // readonly fields: Array; + // }; }>; export type IArrayDef = Def<{ @@ -118,14 +119,14 @@ export type IVariantDef = Def<{ export interface IVariant { readonly name: string; - readonly fields: Array; - readonly discriminant: number | null; + readonly fields: Array | null; + readonly index: number; } export interface IField { readonly name: string | null; readonly type: number; - readonly typeName: string; + readonly typeName: string | null; } export interface IContract { diff --git a/ts-packages/contract-metadata/src/types.ts b/ts-packages/contract-metadata/src/types.ts index 37cb79f..b2fa767 100644 --- a/ts-packages/contract-metadata/src/types.ts +++ b/ts-packages/contract-metadata/src/types.ts @@ -77,9 +77,7 @@ export class TupleDef implements Type { toMetadata(): ITupleDef { return { def: { - tuple: { - fields: this.fields, - }, + tuple: this.fields, }, path: null, }; @@ -195,15 +193,15 @@ export class VariantDef implements Type { export class Variant implements ToMetadata { constructor( public readonly name: string, - public readonly fields: Array, - public readonly discriminant: number | null, + public readonly fields: Array | null, + public readonly index: number, ) {} toMetadata(): IVariant { return { name: this.name, - fields: this.fields.map((f) => f.toMetadata()), - discriminant: this.discriminant, + fields: this.fields ? this.fields.map((f) => f.toMetadata()) : null, + index: this.index, }; } } diff --git a/ts-packages/transform/src/ast.ts b/ts-packages/transform/src/ast.ts index 7a2b65c..5b5fd8f 100644 --- a/ts-packages/transform/src/ast.ts +++ b/ts-packages/transform/src/ast.ts @@ -14,6 +14,10 @@ import blake from "blakejs"; * Ask supported decorators. */ export enum ContractDecoratorKind { + // tuple + Tuple = "tuple", + Enumeration = "enumeration", + Variant = "variant", SpreadLayout = "spreadLayout", PackedLayout = "packedLayout", Contract = "contract", diff --git a/ts-packages/transform/src/metadata/generator.ts b/ts-packages/transform/src/metadata/generator.ts index a85db3f..5728920 100644 --- a/ts-packages/transform/src/metadata/generator.ts +++ b/ts-packages/transform/src/metadata/generator.ts @@ -34,6 +34,9 @@ import { SequenceDef, ArrayDef, VersionedContractMetadata, + TupleDef, + VariantDef, + Variant, } from "ask-contract-metadata"; import * as metadata from "ask-contract-metadata"; import { ASK_VERSION } from "../consts"; @@ -53,7 +56,7 @@ import { } from "."; import debug from "debug"; import { uniqBy } from "lodash"; -import { ArrayTypeInfo } from "./typeInfo"; +import { ArrayTypeInfo, TupleTypeInfo, VariantTypeInfo } from "./typeInfo"; export const LANGUAGE = `Ask! ${ASK_VERSION}`; export const COMPILER = `asc ${version}`; @@ -183,11 +186,24 @@ export class MetadataGenerator { } case metadata.TypeKind.Composite: { + // log("class name", info.type?.getClass()?.name); const def = this.genCompositeType(resolver, info as CompositeTypeInfo); typeSpecs[info.index] = new metadata.TypeWithId(info.index, def); break; } + case metadata.TypeKind.Tuple: { + const def = this.genTupleType(resolver, info as TupleTypeInfo); + typeSpecs[info.index] = new metadata.TypeWithId(info.index, def); + break; + } + + case metadata.TypeKind.Variant: { + const def = this.genVariantType(resolver, info as VariantTypeInfo); + typeSpecs[info.index] = new metadata.TypeWithId(info.index, def); + break; + } + default: { assert(false, `Ask-lang: unspported type kind in metadata: ${info.kind}`); } @@ -198,6 +214,87 @@ export class MetadataGenerator { return typeSpecs; } + private genTupleType(resolver: TypeResolver, info: TupleTypeInfo): TupleDef { + const types = resolver.resolvedTypes(); + return new TupleDef( + info.types.map((type) => { + const fieldTypeInfo = types.get(type); + assert(fieldTypeInfo != null, `Ask-lang: '${type.getClass()?.name} not found`); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return fieldTypeInfo!.index; + }), + ); + } + + private genVariantType(resolver: TypeResolver, info: VariantTypeInfo): VariantDef { + assert( + info.fields.length > 0, + `Ask-lang: Composite def ${info.type?.toString()} fields are empty`, + ); + const variants: metadata.Variant[] = info.fields.map( + ({ field, isEmpty, innerFields }, idx) => { + const fieldDecl = field.prototype.declaration as FieldDeclaration; + + if (isEmpty) { + // empty variant + return new Variant( + fieldDecl.name.range.toString(), + null, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + idx, + ); + } else { + // either composite or tuple + const types = resolver.resolvedTypes(); + assert(innerFields !== null); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const variantFields: metadata.Field[] = innerFields!.map((variantField) => { + // field is named + if (variantField instanceof Field) { + const fieldDecl = variantField.prototype + .declaration as FieldDeclaration; + const fieldTypeInfo = types.get(variantField.type); + assert( + fieldTypeInfo != null, + `Ask-lang: '${ + variantField.name + }: ${variantField.type.toString()}' not found`, + ); + const ret = new metadata.Field( + fieldDecl.name.range.toString(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldTypeInfo!.index, + // we make sure all fields gived a type explicitly + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldDecl.type!.range.toString(), + ); + return ret; + } else { + // type is unnamed + const fieldTypeInfo = types.get(variantField); + assert( + fieldTypeInfo != null, + `Ask-lang: type '${variantField.toString()}' not found`, + ); + const ret = new metadata.Field( + null, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldTypeInfo!.index, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + variantField.toString(), + ); + return ret; + } + }); + + return new Variant(fieldDecl.name.range.toString(), variantFields, idx); + } + }, + ); + let path = info.type ? info.type.toString() : "unknown"; + return new VariantDef(variants).setPath([path]); + } + private genCompositeType(resolver: TypeResolver, info: CompositeTypeInfo): CompositeDef { assert( info.fields.length > 0, diff --git a/ts-packages/transform/src/metadata/typeInfo.ts b/ts-packages/transform/src/metadata/typeInfo.ts index 5dd0060..a0b7368 100644 --- a/ts-packages/transform/src/metadata/typeInfo.ts +++ b/ts-packages/transform/src/metadata/typeInfo.ts @@ -1,5 +1,6 @@ import * as metadata from "ask-contract-metadata"; import { Field, Type } from "visitor-as/as"; +import { VariantField } from "./typeResolver"; /** * TypeInfo contains some type infos needed for metadata types @@ -12,12 +13,24 @@ export abstract class TypeInfo { ) {} } +export class TupleTypeInfo extends TypeInfo { + constructor(type: Type, index: number, public readonly types: Type[]) { + super(type, index, metadata.TypeKind.Tuple); + } +} + export class CompositeTypeInfo extends TypeInfo { constructor(type: Type | null, index: number, public readonly fields: Field[] | Type[]) { super(type, index, metadata.TypeKind.Composite); } } +export class VariantTypeInfo extends TypeInfo { + constructor(type: Type | null, index: number, public readonly fields: VariantField[]) { + super(type, index, metadata.TypeKind.Variant); + } +} + /** * AssemblyScript has no native fixed array. So we define these types by hand. */ diff --git a/ts-packages/transform/src/metadata/typeResolver.ts b/ts-packages/transform/src/metadata/typeResolver.ts index 5cc446c..e12326f 100644 --- a/ts-packages/transform/src/metadata/typeResolver.ts +++ b/ts-packages/transform/src/metadata/typeResolver.ts @@ -22,12 +22,21 @@ import { TypeInfo, ArrayTypeInfo, SequenceTypeInfo, + TupleTypeInfo, + VariantTypeInfo, } from "./typeInfo"; +import { hasDecorator } from "../util"; +import { ContractDecoratorKind } from "../ast"; +import { FieldDeclaration } from "assemblyscript"; const log = debug("TypeResolver"); export type TypeInfoMap = Map; - +export interface VariantField { + field: Field; + innerFields: Array | null; + isEmpty: bool; +} /** * TypeResolver resolve an entrypoint contract type infos, and collect some useful type infos. */ @@ -251,6 +260,14 @@ export class TypeResolver { this.resolveScaleType(typeArgs[0]); this.types.set(type, new SequenceTypeInfo(type, this.currentIndex++, typeArgs[0])); } + } else if (isTuple(clz)) { + // get all fields + const types = this.resovleCompositeField(type).map((field) => field.type); + this.types.set(type, new TupleTypeInfo(type, this.currentIndex++, types)); + } else if (isEnumeration(clz)) { + // it's an enumeration + const fields = this.resolveVariantField(type); + this.types.set(type, new VariantTypeInfo(type, this.currentIndex++, fields)); } else { // It's composite type const fields = this.resovleCompositeField(type); @@ -258,6 +275,53 @@ export class TypeResolver { } } + private resolveVariantField(type: Type): VariantField[] { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const clz: Class = type.getClass()!; + assert(clz.declaration.kind === NodeKind.CLASSDECLARATION); + const fields: VariantField[] = []; + + // TODO: forbid class inherihance + + const decl = clz.declaration as ClassDeclaration; + for (let i = decl.members.length - 1; i >= 0; i--) { + let member = decl.members[i]; + // we only need to know non-static field type + if (member.isAny(CommonFlags.STATIC)) { + continue; + } + if (member.kind != NodeKind.FIELDDECLARATION) { + continue; + } + // if not decorated with @variant we skip + if (!isVariant(member as FieldDeclaration)) { + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const elem = clz!.members!.get(member.name.text); + assert(elem?.kind === ElementKind.FIELD); + const field = elem as Field; + + const fieldClz = field.type.getClass(); + const isTupleVaraint = fieldClz && isTuple(fieldClz); + // TODO: better way to find empty variants + const isEmpty = field.type.isNumericValue; + + let innerFields: Array | null = null; + + if (!isEmpty) { + // tuple variant + innerFields = isTupleVaraint + ? this.resovleCompositeField(field.type).map((f) => f.type) + : this.resovleCompositeField(field.type); + } + fields.push({ field, isEmpty: field.type.isNumericValue, innerFields }); + } + + return fields; + } + private resovleCompositeField(type: Type): Field[] { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const clz: Class = type.getClass()!; @@ -387,3 +451,15 @@ function getFirstValue(map: Map): V { } return v; } + +function isTuple(clz: Class): bool { + return hasDecorator(clz.decoratorNodes, ContractDecoratorKind.Tuple); +} + +function isEnumeration(clz: Class): bool { + return hasDecorator(clz.decoratorNodes, ContractDecoratorKind.Enumeration); +} + +function isVariant(member: FieldDeclaration): bool { + return hasDecorator(member.decorators, ContractDecoratorKind.Variant); +} From 7f3bd91b862cffdce5f604bbe6f6c36412ab1978 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Sat, 11 Feb 2023 16:58:15 +0530 Subject: [PATCH 2/5] fix: variants def --- ts-packages/contract-metadata/src/specs.ts | 2 +- ts-packages/contract-metadata/src/types.ts | 2 +- ts-packages/transform/src/metadata/generator.ts | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ts-packages/contract-metadata/src/specs.ts b/ts-packages/contract-metadata/src/specs.ts index 42d96bc..3d568f0 100644 --- a/ts-packages/contract-metadata/src/specs.ts +++ b/ts-packages/contract-metadata/src/specs.ts @@ -113,7 +113,7 @@ export type ICompositeDef = Def<{ }>; export type IVariantDef = Def<{ - readonly variants: Array; + readonly variant: { readonly variants: Array }; // readonly path: Array; }>; diff --git a/ts-packages/contract-metadata/src/types.ts b/ts-packages/contract-metadata/src/types.ts index b2fa767..156ae45 100644 --- a/ts-packages/contract-metadata/src/types.ts +++ b/ts-packages/contract-metadata/src/types.ts @@ -163,7 +163,7 @@ export class VariantDef implements Type { toMetadata(): IVariantDef { return { def: { - variants: this.variants.map((v) => v.toMetadata()), + variant: { variants: this.variants.map((v) => v.toMetadata()) }, }, path: this.path.length > 0 ? this.path : null, }; diff --git a/ts-packages/transform/src/metadata/generator.ts b/ts-packages/transform/src/metadata/generator.ts index 5728920..a9a0a3c 100644 --- a/ts-packages/transform/src/metadata/generator.ts +++ b/ts-packages/transform/src/metadata/generator.ts @@ -234,6 +234,7 @@ export class MetadataGenerator { const variants: metadata.Variant[] = info.fields.map( ({ field, isEmpty, innerFields }, idx) => { const fieldDecl = field.prototype.declaration as FieldDeclaration; + const index = info.fields.length - idx - 1; if (isEmpty) { // empty variant @@ -241,7 +242,7 @@ export class MetadataGenerator { fieldDecl.name.range.toString(), null, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - idx, + index, ); } else { // either composite or tuple @@ -287,7 +288,7 @@ export class MetadataGenerator { } }); - return new Variant(fieldDecl.name.range.toString(), variantFields, idx); + return new Variant(fieldDecl.name.range.toString(), variantFields, index); } }, ); From 64999c572371c29c12051c32ec273ff78dcede3f Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 14 Feb 2023 12:43:37 +0530 Subject: [PATCH 3/5] feat: add support for custom variant names --- ts-packages/transform/src/ast.ts | 48 ++++++- .../transform/src/metadata/generator.ts | 118 +++++++----------- .../transform/src/metadata/typeResolver.ts | 22 +++- 3 files changed, 107 insertions(+), 81 deletions(-) diff --git a/ts-packages/transform/src/ast.ts b/ts-packages/transform/src/ast.ts index 5b5fd8f..677df05 100644 --- a/ts-packages/transform/src/ast.ts +++ b/ts-packages/transform/src/ast.ts @@ -7,7 +7,7 @@ import { } from "visitor-as/as"; import { DecoratorConfig, extractConfigFromDecorator, extractDecorator } from "./util"; import { utils } from "visitor-as/dist"; -import { DecoratorNode } from "assemblyscript"; +import { DecoratorNode, FieldDeclaration } from "assemblyscript"; import blake from "blakejs"; /** @@ -323,6 +323,52 @@ export class EventDeclaration implements AskNode { } } +/** + * Varinat Declaration represents a `@variant` info + */ +export class VariantDeclaration implements AskNode { + public readonly contractKind: ContractDecoratorKind = ContractDecoratorKind.Variant; + constructor( + /** + * Event Id + */ + public readonly index: number | null, + public readonly name: string | null, + public readonly variant: FieldDeclaration, + ) {} + + /** + * Collect variant infos from config. + * @param node + * @param cfg + * @returns + */ + static extractFrom(emitter: DiagnosticEmitter, node: FieldDeclaration): VariantDeclaration { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const decorator = extractDecorator(emitter, node, ContractDecoratorKind.Variant)!; + const cfg = extractConfigFromDecorator(emitter, decorator); + const variantIndex = cfg.get("index"); + const variantName = cfg.get("name"); + + const name = variantName ? variantName.substring(1, variantName.length - 1) : null; + let index = null; + if (variantIndex) { + if (!isNaN(+variantIndex)) { + index = +variantIndex; + } else { + emitter.errorRelated( + DiagnosticCode.User_defined_0, + node.range, + decorator.range, + `Ask-lang: '@variant' index config is illegal`, + ); + } + } + + return new VariantDeclaration(index, name, utils.cloneNode(node)); + } +} + function getParamsTypeName(fn: FunctionTypeNode): string[] { return fn.parameters.map((p) => p.type.range.toString()); } diff --git a/ts-packages/transform/src/metadata/generator.ts b/ts-packages/transform/src/metadata/generator.ts index a9a0a3c..0b433a1 100644 --- a/ts-packages/transform/src/metadata/generator.ts +++ b/ts-packages/transform/src/metadata/generator.ts @@ -15,6 +15,7 @@ import { Class, ClassDeclaration, DiagnosticCode, + Type, } from "assemblyscript"; import { version } from "visitor-as/as"; import { @@ -232,14 +233,15 @@ export class MetadataGenerator { `Ask-lang: Composite def ${info.type?.toString()} fields are empty`, ); const variants: metadata.Variant[] = info.fields.map( - ({ field, isEmpty, innerFields }, idx) => { + ({ field, isEmpty, innerFields, variantDecl }, idx) => { const fieldDecl = field.prototype.declaration as FieldDeclaration; const index = info.fields.length - idx - 1; + const name = variantDecl.name ?? fieldDecl.name.range.toString(); if (isEmpty) { // empty variant return new Variant( - fieldDecl.name.range.toString(), + name, null, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion index, @@ -249,46 +251,10 @@ export class MetadataGenerator { const types = resolver.resolvedTypes(); assert(innerFields !== null); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const variantFields: metadata.Field[] = innerFields!.map((variantField) => { - // field is named - if (variantField instanceof Field) { - const fieldDecl = variantField.prototype - .declaration as FieldDeclaration; - const fieldTypeInfo = types.get(variantField.type); - assert( - fieldTypeInfo != null, - `Ask-lang: '${ - variantField.name - }: ${variantField.type.toString()}' not found`, - ); - const ret = new metadata.Field( - fieldDecl.name.range.toString(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldTypeInfo!.index, - // we make sure all fields gived a type explicitly - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldDecl.type!.range.toString(), - ); - return ret; - } else { - // type is unnamed - const fieldTypeInfo = types.get(variantField); - assert( - fieldTypeInfo != null, - `Ask-lang: type '${variantField.toString()}' not found`, - ); - const ret = new metadata.Field( - null, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldTypeInfo!.index, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - variantField.toString(), - ); - return ret; - } - }); - - return new Variant(fieldDecl.name.range.toString(), variantFields, index); + const innerVariantFields: metadata.Field[] = innerFields!.map((variantField) => + this.convertToMetadataField(types, variantField), + ); + return new Variant(name, innerVariantFields, index); } }, ); @@ -302,42 +268,46 @@ export class MetadataGenerator { `Ask-lang: Composite def ${info.type?.toString()} fields are empty`, ); const types = resolver.resolvedTypes(); - const fields: metadata.Field[] = info.fields.map((field) => { - // field is named - if (field instanceof Field) { - const fieldDecl = field.prototype.declaration as FieldDeclaration; - const fieldTypeInfo = types.get(field.type); - assert( - fieldTypeInfo != null, - `Ask-lang: '${field.name}: ${field.type.toString()}' not found`, - ); - const ret = new metadata.Field( - fieldDecl.name.range.toString(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldTypeInfo!.index, - // we make sure all fields gived a type explicitly - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldDecl.type!.range.toString(), - ); - return ret; - } else { - // type is unnamed - const fieldTypeInfo = types.get(field); - assert(fieldTypeInfo != null, `Ask-lang: type '${field.toString()}' not found`); - const ret = new metadata.Field( - null, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fieldTypeInfo!.index, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - field.toString(), - ); - return ret; - } - }); + const fields: metadata.Field[] = info.fields.map((field) => + this.convertToMetadataField(types, field), + ); let path = info.type ? info.type.toString() : "unknown"; return new CompositeDef(fields).setPath([path]); } + convertToMetadataField(types: TypeInfoMap, field: Field | Type): metadata.Field { + // field is named + if (field instanceof Field) { + const fieldDecl = field.prototype.declaration as FieldDeclaration; + const fieldTypeInfo = types.get(field.type); + assert( + fieldTypeInfo != null, + `Ask-lang: '${field.name}: ${field.type.toString()}' not found`, + ); + const ret = new metadata.Field( + fieldDecl.name.range.toString(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldTypeInfo!.index, + // we make sure all fields gived a type explicitly + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldDecl.type!.range.toString(), + ); + return ret; + } else { + // type is unnamed + const fieldTypeInfo = types.get(field); + assert(fieldTypeInfo != null, `Ask-lang: type '${field.toString()}' not found`); + const ret = new metadata.Field( + null, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fieldTypeInfo!.index, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + field.toString(), + ); + return ret; + } + } + private genMessages(resolver: TypeResolver): MessageSpec[] { log(this.genMessages.name); diff --git a/ts-packages/transform/src/metadata/typeResolver.ts b/ts-packages/transform/src/metadata/typeResolver.ts index e12326f..4cb703b 100644 --- a/ts-packages/transform/src/metadata/typeResolver.ts +++ b/ts-packages/transform/src/metadata/typeResolver.ts @@ -26,13 +26,14 @@ import { VariantTypeInfo, } from "./typeInfo"; import { hasDecorator } from "../util"; -import { ContractDecoratorKind } from "../ast"; +import { ContractDecoratorKind, VariantDeclaration } from "../ast"; import { FieldDeclaration } from "assemblyscript"; const log = debug("TypeResolver"); export type TypeInfoMap = Map; export interface VariantField { + variantDecl: VariantDeclaration; field: Field; innerFields: Array | null; isEmpty: bool; @@ -294,10 +295,15 @@ export class TypeResolver { continue; } // if not decorated with @variant we skip - if (!isVariant(member as FieldDeclaration)) { + if (!isVariantField(member as FieldDeclaration)) { continue; } + const variantDecl = VariantDeclaration.extractFrom( + this.program, + member as FieldDeclaration, + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const elem = clz!.members!.get(member.name.text); assert(elem?.kind === ElementKind.FIELD); @@ -305,8 +311,7 @@ export class TypeResolver { const fieldClz = field.type.getClass(); const isTupleVaraint = fieldClz && isTuple(fieldClz); - // TODO: better way to find empty variants - const isEmpty = field.type.isNumericValue; + const isEmpty = isEmptyVariantField(field); let innerFields: Array | null = null; @@ -316,7 +321,7 @@ export class TypeResolver { ? this.resovleCompositeField(field.type).map((f) => f.type) : this.resovleCompositeField(field.type); } - fields.push({ field, isEmpty: field.type.isNumericValue, innerFields }); + fields.push({ field, isEmpty: field.type.isNumericValue, innerFields, variantDecl }); } return fields; @@ -460,6 +465,11 @@ function isEnumeration(clz: Class): bool { return hasDecorator(clz.decoratorNodes, ContractDecoratorKind.Enumeration); } -function isVariant(member: FieldDeclaration): bool { +function isVariantField(member: FieldDeclaration): bool { return hasDecorator(member.decorators, ContractDecoratorKind.Variant); } + +function isEmptyVariantField(field: Field): bool { + // TODO: better way to find empty variants + return isVariantField(field.declaration as FieldDeclaration) && field.type.isNumericValue; +} From 3f9f38d75f4472035f16a91b0aea4f48e4c60a83 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 15 Feb 2023 13:18:31 +0530 Subject: [PATCH 4/5] fix: isEmpty value --- ts-packages/transform/src/metadata/typeResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts-packages/transform/src/metadata/typeResolver.ts b/ts-packages/transform/src/metadata/typeResolver.ts index 4cb703b..ee757c0 100644 --- a/ts-packages/transform/src/metadata/typeResolver.ts +++ b/ts-packages/transform/src/metadata/typeResolver.ts @@ -321,7 +321,7 @@ export class TypeResolver { ? this.resovleCompositeField(field.type).map((f) => f.type) : this.resovleCompositeField(field.type); } - fields.push({ field, isEmpty: field.type.isNumericValue, innerFields, variantDecl }); + fields.push({ field, isEmpty, innerFields, variantDecl }); } return fields; From e0e22609595fc1f8eea5436308cebbe3ba55d784 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Sat, 18 Feb 2023 16:33:15 +0530 Subject: [PATCH 5/5] feat: add `debugMessage()` method in env --- as-packages/as-contract-runtime/assembly/seal0.ts | 2 +- as-packages/lang/assembly/env/onchain.ts | 6 ++++++ as-packages/lang/assembly/interfaces/backend.ts | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/as-packages/as-contract-runtime/assembly/seal0.ts b/as-packages/as-contract-runtime/assembly/seal0.ts index abc1b7b..5f58f54 100644 --- a/as-packages/as-contract-runtime/assembly/seal0.ts +++ b/as-packages/as-contract-runtime/assembly/seal0.ts @@ -1,6 +1,6 @@ import { Ptr, ReturnCode, Size } from "."; -export * from "./unstable"; +// export * from "./unstable"; // Set the value at the given key in the contract storage. diff --git a/as-packages/lang/assembly/env/onchain.ts b/as-packages/lang/assembly/env/onchain.ts index d4297d9..fedfe73 100644 --- a/as-packages/lang/assembly/env/onchain.ts +++ b/as-packages/lang/assembly/env/onchain.ts @@ -44,6 +44,12 @@ export class EnvInstance implements TypedEnvBackend { return ScaleDeserializer.deserialize(BytesBuffer.wrap(storageBuffer.buffer)); } + @inline + debugMessage(message: string): void { + const buffer = String.UTF8.encode(message); + seal0.seal_debug_message(changetype(buffer), buffer.byteLength); + } + setContractStorage( key: K, value: V diff --git a/as-packages/lang/assembly/interfaces/backend.ts b/as-packages/lang/assembly/interfaces/backend.ts index f7dbc2d..e838fc9 100644 --- a/as-packages/lang/assembly/interfaces/backend.ts +++ b/as-packages/lang/assembly/interfaces/backend.ts @@ -49,6 +49,8 @@ export interface EnvBackend { */ returnValue(flags: u32, value: V): void; + debugMessage(message: string): void; + // TODO: add more methods }