assemblyscript icon indicating copy to clipboard operation
assemblyscript copied to clipboard

How to make function decorator with transfrom?

Open Umenokin opened this issue 1 year ago • 7 comments

Hello,

I am trying to write a transform class for custom function decoration.

What I want is to take this:

@handler
export function handle(Request req): Response {}

And turn it into something like:

import {processRequest} from 'sdk';

// Ideally name handle to something like inner_handler and remove export just in case
export function handle(requestPtr: usize, requestSize: usize): usize {
  processRequest(requestPtr: usize, requestSize: usize, inner_handler);
}

Basically:

  1. Transform should remove export from handle
  2. Rename handle to say inner_handle
  3. add a simple wrapper function which will import request data from the host
  4. Call decorated function with the new inner_handle name
  5. Export wrapper function instead of the one user wrote.

It is tough to find any information online. Can you recommend any examples or a small code snippet? The way I see it:

  1. Find decorated FucntionPrototype
  2. Rename it and remove the export flag
  3. Add a new FunctionPrototype with the body I need
  4. Add this prototype to the body.

Umenokin avatar Aug 08 '22 05:08 Umenokin

There are some helper lib (however a little bit outdated) with useful examples: https://github.com/willemneal/visitor-as

I hope this helps

MaxGraey avatar Aug 08 '22 06:08 MaxGraey

Thanks @MaxGraey ! But to be honest not so much. I was able to find the element I did which I assume this library helping with? Like so:

afterInitialize(program) {
    program.elementsByName.forEach((el) => {
      if (!(el instanceof FunctionPrototype)) {
        return;
      }

      if (!this.hasDecorator(el, 'handler')) {
        return;
      }

But now, it is not clear how can to inject the code I need into the program. I checked as-json sources but they adding new method to decorated class while I need to inject a function into program and modify they one which is decorated.

Umenokin avatar Aug 08 '22 06:08 Umenokin

For your case you should use afterParse(parser) {...} and manipulate with AST

MaxGraey avatar Aug 08 '22 06:08 MaxGraey

Github has a lot of examples. Ask project for example: https://github.com/ask-lang/ask/blob/main/ts-packages/transform/src/visitors/transform.ts#L207

MaxGraey avatar Aug 08 '22 06:08 MaxGraey

Thanks a lot @MaxGraey it seems I am getting close!

Umenokin avatar Aug 08 '22 07:08 Umenokin

This works (not sure if it's the best way to do it but it works):

import { ClassPrototype, IdentifierExpression } from "assemblyscript"
import { Transform } from "assemblyscript/transform"

let decoratorCallbacks = {}

function processDecorators(program) {
  program.elementsByName.forEach((element) => {
    if (!(element instanceof ClassPrototype)) return
    if (!element.decoratorNodes) return

    // Loop over all the decorators
    for (const node of element.decoratorNodes) {
      if (!(node.name instanceof IdentifierExpression)) continue

      const name = node.name.text
      if (decoratorCallbacks[name]) {
        decoratorCallbacks[name](element)
      }
    }
    
  })
}

decoratorCallbacks["reverse"] = function (element) {
  console.log("Reversing", element.name)
  element.instanceMembers = new Map(Array.from(element.instanceMembers).reverse())
}

class MyTransform extends Transform {
  afterInitialize (program) {
    processDecorators(program)
  }
}
export default MyTransform

This is for classes but it should hopefully by useful

Heath123 avatar Aug 09 '22 15:08 Heath123

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in one week if no further activity occurs. Thank you for your contributions!

github-actions[bot] avatar Sep 16 '22 23:09 github-actions[bot]

@Umenokin did you figure this out in the end?

stwiname avatar Dec 12 '22 01:12 stwiname