package com.eas.client.scripts;
import com.eas.concurrent.CallableConsumer;
import com.eas.script.ParsedJs;
import com.eas.script.Scripts;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
*
* @author kl, mg refactoring
*/
public class DependenciesWalker {
public static final String REQUIRE_FUNCTION_NAME = "require";
public static final String DEFINE_FUNCTION_NAME = "define";
public static final String MODEL = "model";
public static final String LOAD_ENTITY = "loadEntity";
public static final String SERVER_MODULE = "ServerModule";
public static final String RPC_PROXY = "Proxy";
private final Set<String> dependencies = new HashSet<>();
private final Set<String> queryDependencies = new HashSet<>();
private final Set<String> serverDependencies = new HashSet<>();
private ParsedJs parsedSource;
private String source;
private CallableConsumer<Boolean, String> validator;
public DependenciesWalker(String aSource) {
this(aSource, null);
}
public DependenciesWalker(String aSource, CallableConsumer<Boolean, String> aValidator) {
super();
source = aSource;
validator = aValidator;
}
public void walk() {
parsedSource = Scripts.parseJs(source);
SymbolsResolver outerDefinedFinder = new SymbolsResolver();
parsedSource.getAst().accept(outerDefinedFinder);
parsedSource.getAst().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private final Deque<CallNode> calls = new LinkedList<>();
@Override
public boolean enterCallNode(CallNode callNode) {
calls.push(callNode);
return super.enterCallNode(callNode);
}
@Override
public Node leaveCallNode(CallNode callNode) {
calls.pop();
return super.leaveCallNode(callNode);
}
@Override
public boolean enterLiteralNode(LiteralNode<?> literalNode) {
if (literalNode.getType().isString() && !calls.isEmpty()) {
String value = literalNode.getString();
CallNode lastCall = calls.peek();
//boolean arrayAtFirstArg = lastCall.getArgs().size() >= 1 && lastCall.getArgs().get(0) instanceof LiteralNode.ArrayLiteralNode;
boolean atFirstArg = lastCall.getArgs().size() >= 1 && lastCall.getArgs().get(0) == literalNode;
Expression fe = lastCall.getFunction();
if (fe instanceof AccessNode) {
AccessNode lastAccess = (AccessNode) fe;
String funcName = lastAccess.getProperty();
if (/*lastCall.isNew() && */atFirstArg) {
switch (funcName) {
case RPC_PROXY:
case SERVER_MODULE:
putServerDependence(value);
break;
case LOAD_ENTITY:
if (lastAccess.getBase() instanceof IdentNode) {
//String baseName = ((IdentNode) lastAccess.getBase()).getName();
if (LOAD_ENTITY.equals(funcName)) {
queryDependencies.add(value);
}
}
break;
}
}
}
}
return super.enterLiteralNode(literalNode);
}
});
dependencies.removeAll(Arrays.asList(new String[]{REQUIRE_FUNCTION_NAME, DEFINE_FUNCTION_NAME}));
outerDefinedFinder.getGlobalSymbols().forEach((String aIfGlobalDependency) -> {
try {
if (!REQUIRE_FUNCTION_NAME.equals(aIfGlobalDependency)
&& !DEFINE_FUNCTION_NAME.equals(aIfGlobalDependency)
&& validator != null && validator.call(aIfGlobalDependency)) {
dependencies.add(aIfGlobalDependency);
}
} catch (Exception ex) {
Logger.getLogger(DependenciesWalker.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
public Set<String> getQueryDependencies() {
return queryDependencies;
}
public Set<String> getDependencies() {
return dependencies;
}
/**
* @return the serverDependencies
*/
public Set<String> getServerDependencies() {
return serverDependencies;
}
/**
* @param aModuleName
*/
public void putServerDependence(String aModuleName) {
if (!serverDependencies.contains(aModuleName)) {
serverDependencies.add(aModuleName);
}
}
}