Skip to content
Draft
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
2 changes: 1 addition & 1 deletion as-packages/as-contract-runtime/assembly/seal0.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
6 changes: 6 additions & 0 deletions as-packages/lang/assembly/env/onchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export class EnvInstance implements TypedEnvBackend {
return ScaleDeserializer.deserialize<T>(BytesBuffer.wrap(storageBuffer.buffer));
}

@inline
debugMessage(message: string): void {
const buffer = String.UTF8.encode(message);
seal0.seal_debug_message(changetype<u32>(buffer), buffer.byteLength);
}

setContractStorage<K extends IKey, V>(
key: K,
value: V
Expand Down
2 changes: 2 additions & 0 deletions as-packages/lang/assembly/interfaces/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export interface EnvBackend {
*/
returnValue<V>(flags: u32, value: V): void;

debugMessage(message: string): void;

// TODO: add more methods
}

Expand Down
15 changes: 8 additions & 7 deletions ts-packages/contract-metadata/src/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ export type IPrimitiveDef = Def<{
}>;

export type ITupleDef = Def<{
readonly tuple: {
readonly fields: Array<number>;
};
readonly tuple: Array<number>;
// readonly tuple: {
// readonly fields: Array<number>;
// };
}>;

export type IArrayDef = Def<{
Expand All @@ -112,20 +113,20 @@ export type ICompositeDef = Def<{
}>;

export type IVariantDef = Def<{
readonly variants: Array<IVariant>;
readonly variant: { readonly variants: Array<IVariant> };
// readonly path: Array<string>;
}>;

export interface IVariant {
readonly name: string;
readonly fields: Array<IField>;
readonly discriminant: number | null;
readonly fields: Array<IField> | null;
readonly index: number;
}

export interface IField {
readonly name: string | null;
readonly type: number;
readonly typeName: string;
readonly typeName: string | null;
}

export interface IContract {
Expand Down
14 changes: 6 additions & 8 deletions ts-packages/contract-metadata/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ export class TupleDef implements Type {
toMetadata(): ITupleDef {
return {
def: {
tuple: {
fields: this.fields,
},
tuple: this.fields,
},
path: null,
};
Expand Down Expand Up @@ -165,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,
};
Expand Down Expand Up @@ -195,15 +193,15 @@ export class VariantDef implements Type {
export class Variant implements ToMetadata {
constructor(
public readonly name: string,
public readonly fields: Array<Field>,
public readonly discriminant: number | null,
public readonly fields: Array<Field> | 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,
};
}
}
Expand Down
52 changes: 51 additions & 1 deletion ts-packages/transform/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ 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";

/**
* Ask supported decorators.
*/
export enum ContractDecoratorKind {
// tuple
Tuple = "tuple",
Enumeration = "enumeration",
Variant = "variant",
SpreadLayout = "spreadLayout",
PackedLayout = "packedLayout",
Contract = "contract",
Expand Down Expand Up @@ -319,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());
}
Expand Down
134 changes: 101 additions & 33 deletions ts-packages/transform/src/metadata/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Class,
ClassDeclaration,
DiagnosticCode,
Type,
} from "assemblyscript";
import { version } from "visitor-as/as";
import {
Expand All @@ -34,6 +35,9 @@ import {
SequenceDef,
ArrayDef,
VersionedContractMetadata,
TupleDef,
VariantDef,
Variant,
} from "ask-contract-metadata";
import * as metadata from "ask-contract-metadata";
import { ASK_VERSION } from "../consts";
Expand All @@ -53,7 +57,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}`;
Expand Down Expand Up @@ -183,11 +187,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}`);
}
Expand All @@ -198,48 +215,99 @@ export class MetadataGenerator {
return typeSpecs;
}

private genCompositeType(resolver: TypeResolver, info: CompositeTypeInfo): CompositeDef {
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 types = resolver.resolvedTypes();
const fields: metadata.Field[] = info.fields.map((field) => {
// field is named
if (field instanceof Field) {
const variants: metadata.Variant[] = info.fields.map(
({ field, isEmpty, innerFields, variantDecl }, idx) => {
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,
const index = info.fields.length - idx - 1;
const name = variantDecl.name ?? fieldDecl.name.range.toString();

if (isEmpty) {
// empty variant
return new Variant(
name,
null,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
index,
);
} else {
// either composite or tuple
const types = resolver.resolvedTypes();
assert(innerFields !== null);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
field.toString(),
);
return ret;
}
});
const innerVariantFields: metadata.Field[] = innerFields!.map((variantField) =>
this.convertToMetadataField(types, variantField),
);
return new Variant(name, innerVariantFields, index);
}
},
);
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,
`Ask-lang: Composite def ${info.type?.toString()} fields are empty`,
);
const types = resolver.resolvedTypes();
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);

Expand Down
13 changes: 13 additions & 0 deletions ts-packages/transform/src/metadata/typeInfo.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
*/
Expand Down
Loading