WALA icon indicating copy to clipboard operation
WALA copied to clipboard

Static Analysis for Javascript frontend frameworks

Open Fakorede opened this issue 2 years ago • 2 comments

Good day,

I have been able to setup WALA locally using the wala-start repo, and do analysis of a simple javascript function. However, I'm trying to use WALA to generate the call graph/ir for a simple React code snippet but it doesn't work. Any pointers is very much appreciated.

Thanks.

Fakorede avatar Sep 24 '23 15:09 Fakorede

Hi @Fakorede if you could give more specific info on what goes wrong that would be helpful. I wouldn't be surprised if WALA runs into scalability issues as React codes can be huge if you include the full React library.

msridhar avatar Oct 03 '23 23:10 msridhar

Thanks for your response Professor @msridhar. I am using the below code to generate IR:

public static void generateIR(String filename) throws ClassHierarchyException {
        JSCallGraphUtil.setTranslatorFactory(
                new CAstRhinoTranslatorFactory());
        IClassHierarchy cha =
                JSCallGraphUtil.makeHierarchyForScripts(filename);

        IRFactory<IMethod> factory = AstIRFactory.makeDefaultFactory();
        for (IClass klass : cha) {
            if (!klass.getName().toString().startsWith("prologue.js")) {
                IMethod m = klass.getMethod(AstMethodReference.fnSelector);
                if (m != null) {
                    IR ir = factory.makeIR(m, Everywhere.EVERYWHERE,
                            new SSAOptions());
                    System.out.println(ir);
                }
            }
        }
    }

I have created a file which contains a simple javascript function (as below), tested it and works quite alright

function testFunctionWithCallback() {
    var b = 3
    var c = 5
    var a = f(b * c)
}

If I replace the the js function with a sample react code snippet as below, i observe the IMethod for every IClass is null.

import { useState } from 'react';

export default function Form() {
  const [answer, setAnswer] = useState('');
  const [error, setError] = useState(null);
  const [status, setStatus] = useState('typing');

  if (status === 'success') {
    return <h1>That's right!</h1>
  }

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus('submitting');
    try {
      await submitForm(answer);
      setStatus('success');
    } catch (err) {
      setStatus('typing');
      setError(err);
    }
  }

  function handleTextareaChange(e) {
    setAnswer(e.target.value);
  }

  return (
    <>
      <h2>City quiz</h2>
      <p>
        In which city is there a billboard that turns air into drinkable water?
      </p>
      <form onSubmit={handleSubmit}>
        <textarea
          value={answer}
          onChange={handleTextareaChange}
          disabled={status === 'submitting'}
        />
        <br />
        <button disabled={
          answer.length === 0 ||
          status === 'submitting'
        }>
          Submit
        </button>
        {error !== null &&
          <p className="Error">
            {error.message}
          </p>
        }
      </form>
    </>
  );
}

function submitForm(answer) {
  // Pretend it's hitting the network.
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      let shouldError = answer.toLowerCase() !== 'lima'
      if (shouldError) {
        reject(new Error('Good guess but a wrong answer. Try again!'));
      } else {
        resolve();
      }
    }, 1500);
  });
}

Output attached below. Thanks sir.

Screenshot 2023-10-12 at 20 54 14

Fakorede avatar Oct 12 '23 19:10 Fakorede