1

using Jsoup, I extract JavaScript part in html file. and store it as java String Object.

and I want to extract function list, variables list in js's function using javax.script.ScriptEngine

JavaScript part has several function section.

ex)

function a() {
var a_1;
var a_2
...
}

function b() {
    var b_1;
    var b_2;
...
}

function c() {
    var c_1;
    var c_2;
...
}

My Goals is right below.

List funcList

a b c

List varListA

a_1 a_2 ...

List varListB

b_1 b_2 ...

List varListC

c_1 c_2 ...

How can I extract function list and variables list(or maybe values)?

2
  • Do you want to execute the code or do you want to inspect the code statically? ScriptEngine may be suitable for execution, though it will lack things like the DOM. If you want to inspect the code, an AST parser would be more suitable (e.g. Antlr or Mozilla Rhino's AST parser.) Commented Mar 21, 2013 at 14:14
  • thanks. I want to inspect the code, then modify the code(change function name, variable name.. and so on). I'll try an AST parser!! Commented Mar 22, 2013 at 0:46

3 Answers 3

1

I think you can do this by using javascript introspection after having loaded the javascript in the Engine - e.g. for functions:

ScriptEngine engine;
// create the engine and have it load your javascript
Bindings bind = engine.getBindings(ScriptContext.ENGINE_SCOPE);
Set<String> allAttributes = bind.keySet();
Set<String> allFunctions = new HashSet<String>();
for ( String attr : allAttributes ) {
    if ( "function".equals( engine.eval("typeof " + attr) ) ) {
        allFunctions.add(attr);
    }
}
System.out.println(allFunctions);

I haven't found a way to extract the variables inside functions (local variables) without delving in internal mechanics (and thus unsafe to use) of the javascript scripting engine.

Sign up to request clarification or add additional context in comments.

Comments

0

It is pretty tricky. ScriptEngine API seems not good for inspecting the code. So, I have such kind of pretty ugly solution with instance of and cast operators.

       Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
       for (Map.Entry<String, Object> scopeEntry : bindings.entrySet()) {
           Object value = scopeEntry.getValue();
           String name = scopeEntry.getKey();
           if (value instanceof NativeFunction) {
               log.info("Function -> " + name);
               NativeFunction function = NativeFunction.class.cast(value);
               DebuggableScript debuggableFunction = function.getDebuggableView();
               for (int i = 0; i < debuggableFunction.getParamAndVarCount(); i++) {
                   log.info("First level arg: " + debuggableFunction.getParamOrVarName(i));
               }
           } else if (value instanceof Undefined
                   || value instanceof String
                   || value instanceof Number) {
               log.info("Global arg -> " + name);
           }
       }

1 Comment

What is NativeFunction here ? Code doesn't recognise it.
0

I had similar issue. Maybe it will be helpfull for others. I use groove as script lang. My Task was to retrive all invokable functions from the script. And then filter this functions by some criteria.

Unfortunately this approach is usefull only for groovy...

Get script engine:

public ScriptEngine getEngine() throws Exception {
    if (engine == null)
        engine = new ScriptEngineManager().getEngineByName(scriptType);
    if (engine == null) 
        throw new Exception("Could not find implementation of " + scriptType);
    return engine;
}  

Compile and evaluate script:

public void evaluateScript(String script) throws Exception {
    Bindings bindings = getEngine().getBindings(ScriptContext.ENGINE_SCOPE);
    bindings.putAll(binding);
    try {
        if (engine instanceof Compilable)
            compiledScript = ((Compilable)getEngine()).compile(script);
        getEngine().eval(script);
    } catch (Throwable e) {
        e.printStackTrace();
    } 
}

Get functions from script. I did not found other ways how to get all invokable methods from script except Reflection. Yeah, i know that this approach depends on ScriptEngine implementation, but it's the only one :)

public List getInvokableList() throws ScriptException {                
    List list = new ArrayList();
    try {
        Class compiledClass = compiledScript.getClass();
        Field clasz = compiledClass.getDeclaredField("clasz");       
        clasz.setAccessible(true);
        Class scrClass = (Class)clasz.get(compiledScript);
        Method[] methods = scrClass.getDeclaredMethods();            
        clasz.setAccessible(false);
        for (int i = 0, j = methods.length; i < j; i++) {
            Annotation[] annotations = methods[i].getDeclaredAnnotations();
            boolean ok = false;
            for (int k = 0, m = annotations.length; k < m; k++) {
                ok = annotations[k] instanceof CalculatedField;
                if (ok) break;
            }
            if (ok) 
                list.add(methods[i].getName());
        }
    } catch (NoSuchFieldException e) {
        e.printStackTrace(); 
    } catch (IllegalAccessException e) {

    }
    return list;
}

In my task i don't need all functions, for this i create custom annotation and use it in the script:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CalculatedField {    
}

Script example:

import com.vssk.CalculatedField;

def utilFunc(s) {     
    s
}

@CalculatedField
def func3() {     
    utilFunc('Testing func from groovy')
}

Method to invoke script function by it's name:

public Object executeFunc(String name) throws Exception {
    return ((Invocable)getEngine()).invokeFunction(name);  
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.