Skip to content

Commit fe235bc

Browse files
committed
refactor: simplify capturing of decorator config by storing it directly with the function
1 parent 78b3de5 commit fe235bc

File tree

9 files changed

+40
-66
lines changed

9 files changed

+40
-66
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
},
7373
"dependencies": {
7474
"@algorandfoundation/algorand-typescript": "^1.0.0-beta.20",
75-
"@algorandfoundation/puya-ts": "^1.0.0-beta.28",
75+
"@algorandfoundation/puya-ts": "^1.0.0-beta.30",
7676
"elliptic": "^6.5.7",
7777
"js-sha256": "^0.11.0",
7878
"js-sha3": "^0.9.3",

src/abi-metadata.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type { AbiMethodConfig, BareMethodConfig, CreateOptions, OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
1+
import type { CreateOptions, OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
22
import js_sha512 from 'js-sha512'
33
import type { TypeInfo } from './encoders'
4-
import { Contract } from './impl/contract'
4+
import { Arc4MethodConfigSymbol, Contract } from './impl/contract'
55
import { getArc4TypeName as getArc4TypeNameForARC4Encoded } from './impl/encoded-types'
6+
import type { DeliberateAny } from './typescript-helpers'
67

78
export interface AbiMetadata {
89
methodName: string
@@ -13,9 +14,8 @@ export interface AbiMetadata {
1314
onCreate?: CreateOptions
1415
allowActions?: OnCompleteActionStr[]
1516
}
16-
export const AbiMetaSymbol = Symbol('AbiMetadata')
17-
export const isContractProxy = Symbol('isContractProxy')
18-
const metadataStore: Map<{ new (): Contract }, Record<string, AbiMetadata>> = new Map()
17+
18+
const metadataStore: WeakMap<{ new (): Contract }, Record<string, AbiMetadata>> = new WeakMap()
1919
export const attachAbiMetadata = (contract: { new (): Contract }, methodName: string, metadata: AbiMetadata): void => {
2020
if (!metadataStore.has(contract)) {
2121
metadataStore.set(contract, {})
@@ -24,17 +24,6 @@ export const attachAbiMetadata = (contract: { new (): Contract }, methodName: st
2424
metadatas[methodName] = metadata
2525
}
2626

27-
export const captureMethodConfig = <T extends Contract>(
28-
contract: T,
29-
methodName: string,
30-
config?: AbiMethodConfig<T> | BareMethodConfig,
31-
): void => {
32-
const metadata = getContractMethodAbiMetadata(contract, methodName)
33-
metadata.name = (config as AbiMethodConfig<T>)?.name ?? methodName
34-
metadata.onCreate = config?.onCreate ?? 'disallow'
35-
metadata.allowActions = ([] as OnCompleteActionStr[]).concat(config?.allowActions ?? 'NoOp')
36-
}
37-
3827
export const getContractAbiMetadata = <T extends Contract>(contract: T | { new (): T }): Record<string, AbiMetadata> => {
3928
// Initialize result object to store merged metadata
4029
const result: Record<string, AbiMetadata> = {}
@@ -52,7 +41,10 @@ export const getContractAbiMetadata = <T extends Contract>(contract: T | { new (
5241
const classMetadata = currentMetadata
5342
for (const [methodName, metadata] of Object.entries(classMetadata)) {
5443
if (!(methodName in result)) {
55-
result[methodName] = metadata
44+
result[methodName] = {
45+
...metadata,
46+
...(currentClass.prototype as DeliberateAny)?.[methodName]?.[Arc4MethodConfigSymbol],
47+
}
5648
}
5749
}
5850
}

src/impl/contract.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { arc4 } from '@algorandfoundation/algorand-typescript'
2+
import type { OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
23
import type { DeliberateAny } from '../typescript-helpers'
34
import { BaseContract } from './base-contract'
45

@@ -10,18 +11,29 @@ export class Contract extends BaseContract {
1011
}
1112
}
1213

13-
export function abimethod<TContract extends Contract>(_config?: arc4.AbiMethodConfig<TContract>) {
14+
export const Arc4MethodConfigSymbol = Symbol('Arc4MethodConfig')
15+
export function abimethod<TContract extends Contract>(config?: arc4.AbiMethodConfig<TContract>) {
1416
return function <TArgs extends DeliberateAny[], TReturn>(
1517
target: (this: TContract, ...args: TArgs) => TReturn,
1618
): (this: TContract, ...args: TArgs) => TReturn {
19+
;(target as DeliberateAny)[Arc4MethodConfigSymbol] = {
20+
...config,
21+
onCreate: config?.onCreate ?? 'disallow',
22+
allowActions: ([] as OnCompleteActionStr[]).concat(config?.allowActions ?? 'NoOp'),
23+
}
1724
return target
1825
}
1926
}
2027

21-
export function baremethod<TContract extends Contract>(_config?: arc4.BareMethodConfig) {
28+
export function baremethod<TContract extends Contract>(config?: arc4.BareMethodConfig) {
2229
return function <TArgs extends DeliberateAny[], TReturn>(
2330
target: (this: TContract, ...args: TArgs) => TReturn,
2431
): (this: TContract, ...args: TArgs) => TReturn {
32+
;(target as DeliberateAny)[Arc4MethodConfigSymbol] = {
33+
...config,
34+
onCreate: config?.onCreate ?? 'disallow',
35+
allowActions: ([] as OnCompleteActionStr[]).concat(config?.allowActions ?? 'NoOp'),
36+
}
2537
return target
2638
}
2739
}

src/runtime-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { AccountCls } from './impl/reference'
99
import type { DeliberateAny } from './typescript-helpers'
1010
import { nameOfType } from './util'
1111

12-
export { attachAbiMetadata, captureMethodConfig } from './abi-metadata'
12+
export { attachAbiMetadata } from './abi-metadata'
1313
export { emitImpl } from './impl/emit'
1414
export * from './impl/encoded-types'
1515
export { decodeArc4Impl, encodeArc4Impl } from './impl/encoded-types'

src/subcontexts/contract-context.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Account, Application, Asset, contract, LocalState } from '@algorandfoundation/algorand-typescript'
22
import type { ARC4Encoded } from '@algorandfoundation/algorand-typescript/arc4'
33
import type { AbiMetadata } from '../abi-metadata'
4-
import { AbiMetaSymbol, getArc4Selector, getContractAbiMetadata, getContractMethodAbiMetadata, isContractProxy } from '../abi-metadata'
4+
import { getArc4Selector, getContractAbiMetadata, getContractMethodAbiMetadata } from '../abi-metadata'
55
import { BytesMap } from '../collections/custom-key-map'
66
import { checkRoutingConditions } from '../context-helpers/context-util'
77
import { lazyContext } from '../context-helpers/internal-context'
@@ -232,14 +232,9 @@ export class ContractContext {
232232
appData.isCreating = isArc4 && hasCreateMethods(t! as Contract)
233233
const instance = new Proxy(t!, {
234234
get(target, prop, receiver) {
235-
if (prop === isContractProxy) {
236-
return true
237-
}
238-
if (prop === AbiMetaSymbol) {
239-
return isArc4 ? getContractAbiMetadata(target as Contract) : undefined
240-
}
241-
const abiMetadata = isArc4 ? getContractMethodAbiMetadata(target as Contract, prop as string) : undefined
242235
const orig = Reflect.get(target, prop, receiver)
236+
const abiMetadata =
237+
isArc4 && typeof orig === 'function' ? getContractMethodAbiMetadata(target as Contract, orig.name) : undefined
243238
const isProgramMethod = prop === 'approvalProgram' || prop === 'clearStateProgram'
244239
const isAbiMethod = isArc4 && abiMetadata
245240
if (isAbiMethod || isProgramMethod) {

src/subcontexts/transaction-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { bytes, Contract, uint64 } from '@algorandfoundation/algorand-typescript'
22
import { TransactionType } from '@algorandfoundation/algorand-typescript'
3-
import { AbiMetaSymbol, type AbiMetadata } from '../abi-metadata'
3+
import { getContractAbiMetadata, type AbiMetadata } from '../abi-metadata'
44
import { TRANSACTION_GROUP_MAX_SIZE } from '../constants'
55
import { checkRoutingConditions } from '../context-helpers/context-util'
66
import { lazyContext } from '../context-helpers/internal-context'
@@ -201,7 +201,7 @@ export class TransactionContext {
201201
...args: TParams
202202
): DeferredAppCall<TParams, TReturn> {
203203
const appId = lazyContext.ledger.getApplicationForContract(contract)
204-
const abiMetadata = (contract as DeliberateAny)[AbiMetaSymbol]?.[methodName]
204+
const abiMetadata = getContractAbiMetadata(contract)[methodName as string]
205205
const txns = ContractContext.createMethodCallTxns(contract, abiMetadata, ...args)
206206
return new DeferredAppCall(appId.id, txns, method, abiMetadata, args)
207207
}

src/test-transformer/node-factory.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,6 @@ export const nodeFactory = {
7373
)
7474
},
7575

76-
captureMethodConfig(classIdentifier: ts.Identifier, method: ts.MethodDeclaration, callExpression: ts.CallExpression) {
77-
const methodName = getPropertyNameAsString(method.name)
78-
return factory.createExpressionStatement(
79-
factory.createCallExpression(
80-
factory.createPropertyAccessExpression(factory.createIdentifier('runtimeHelpers'), factory.createIdentifier('captureMethodConfig')),
81-
undefined,
82-
[classIdentifier, methodName, ...callExpression.arguments],
83-
),
84-
)
85-
},
86-
8776
captureGenericTypeInfo(x: ts.Expression, info: string) {
8877
return factory.createCallExpression(
8978
factory.createPropertyAccessExpression(
@@ -130,8 +119,4 @@ export const nodeFactory = {
130119
}
131120
return node
132121
},
133-
134-
callDecoratorMethod(node: ts.CallExpression, className: ts.Identifier) {
135-
return factory.updateCallExpression(node, node.expression, node.typeArguments, [className, ...node.arguments])
136-
},
137122
} satisfies Record<string, (...args: DeliberateAny[]) => ts.Node>

src/test-transformer/visitors.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ class ExpressionVisitor {
131131
private helper: VisitorHelper,
132132
private expressionNode: ts.Expression,
133133
private stubbedFunctionName?: string,
134-
private className?: ts.Identifier,
135-
private methodNode?: ts.MethodDeclaration,
136134
) {}
137135

138136
public result(): ts.Expression {
@@ -168,16 +166,12 @@ class ExpressionVisitor {
168166
const targetType = ptypes.ptypeToArc4EncodedType(type, this.helper.sourceLocation(node))
169167
const targetTypeInfo = getGenericTypeInfo(targetType)
170168
infoArg = targetTypeInfo
171-
} else if (isCallingDecoratorMethod(stubbedFunctionName)) {
172-
this.helper.additionalStatements.push(nodeFactory.captureMethodConfig(this.className!, this.methodNode!, updatedNode))
173169
}
174170

175171
updatedNode = stubbedFunctionName
176172
? isCallingMethodSelector(stubbedFunctionName)
177173
? nodeFactory.callMethodSelectorFunction(updatedNode)
178-
: isCallingDecoratorMethod(stubbedFunctionName)
179-
? updatedNode
180-
: nodeFactory.callStubbedFunction(stubbedFunctionName, updatedNode, infoArg)
174+
: nodeFactory.callStubbedFunction(stubbedFunctionName, updatedNode, infoArg)
181175
: updatedNode
182176
}
183177
return needsToCaptureTypeInfo
@@ -214,8 +208,6 @@ class FunctionOrMethodVisitor {
214208
constructor(
215209
protected context: ts.TransformationContext,
216210
protected helper: VisitorHelper,
217-
protected className?: ts.Identifier,
218-
protected methodNode?: ts.MethodDeclaration,
219211
) {}
220212
protected visit = (node: ts.Node): ts.Node => {
221213
return ts.visitEachChild(this.updateNode(node), this.visit, this.context)
@@ -276,7 +268,7 @@ class FunctionOrMethodVisitor {
276268
if (ts.isCallExpression(node)) {
277269
const stubbedFunctionName = tryGetStubbedFunctionName(node, this.helper)
278270
if (stubbedFunctionName) {
279-
return new ExpressionVisitor(this.context, this.helper, node, stubbedFunctionName, this.className, this.methodNode).result()
271+
return new ExpressionVisitor(this.context, this.helper, node, stubbedFunctionName).result()
280272
}
281273
}
282274

@@ -301,10 +293,9 @@ class MethodDecVisitor extends FunctionOrMethodVisitor {
301293
constructor(
302294
context: ts.TransformationContext,
303295
helper: VisitorHelper,
304-
methodNode: ts.MethodDeclaration,
305-
className: ts.Identifier | undefined,
296+
private methodNode: ts.MethodDeclaration,
306297
) {
307-
super(context, helper, className, methodNode)
298+
super(context, helper)
308299
}
309300

310301
public result(): ts.MethodDeclaration {
@@ -338,7 +329,7 @@ class ClassVisitor {
338329
}
339330
}
340331

341-
return new MethodDecVisitor(this.context, this.helper, node, this.classDec.name).result()
332+
return new MethodDecVisitor(this.context, this.helper, node).result()
342333
}
343334

344335
if (ts.isCallExpression(node)) {
@@ -429,11 +420,10 @@ const tryGetStubbedFunctionName = (node: ts.CallExpression, helper: VisitorHelpe
429420
if (sourceFileName && !algotsModulePaths.some((s) => sourceFileName.includes(s))) return undefined
430421
}
431422
const functionName = functionSymbol?.getName() ?? identityExpression.text
432-
const stubbedFunctionNames = ['interpretAsArc4', 'decodeArc4', 'encodeArc4', 'emit', 'methodSelector', 'abimethod', 'baremethod']
423+
const stubbedFunctionNames = ['interpretAsArc4', 'decodeArc4', 'encodeArc4', 'emit', 'methodSelector']
433424
return stubbedFunctionNames.includes(functionName) ? functionName : undefined
434425
}
435426

436427
const isCallingDecodeArc4 = (functionName: string | undefined): boolean => ['decodeArc4', 'encodeArc4'].includes(functionName ?? '')
437428
const isCallingEmit = (functionName: string | undefined): boolean => 'emit' === (functionName ?? '')
438429
const isCallingMethodSelector = (functionName: string | undefined): boolean => 'methodSelector' === (functionName ?? '')
439-
const isCallingDecoratorMethod = (functionName: string | undefined): boolean => ['abimethod', 'baremethod'].includes(functionName ?? '')

0 commit comments

Comments
 (0)