refactoring to integrate field-based cg builders with Eclipse-based
JavaScript front end
This commit is contained in:
parent
beab92e359
commit
d528c16b0f
|
@ -84,7 +84,7 @@ public class CGUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
return builder.buildCallGraph(new NullProgressMonitor());
|
||||
return builder.buildCallGraph(roots, new NullProgressMonitor());
|
||||
} catch (CancelException e) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ Bundle-ClassPath: .,
|
|||
lib/jericho-html-3.2.jar
|
||||
Bundle-Activator: com.ibm.wala.cast.js.JavaScriptPlugin
|
||||
Bundle-Vendor: IBM
|
||||
Export-Package: com.ibm.wala.cast.js,
|
||||
Export-Package: .,
|
||||
com.ibm.wala.cast.js,
|
||||
com.ibm.wala.cast.js.analysis.typeInference,
|
||||
com.ibm.wala.cast.js.callgraph.fieldbased,
|
||||
com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph,
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.Set;
|
|||
|
||||
import com.ibm.wala.cast.ipa.callgraph.AstContextInsensitiveSSAContextInterpreter;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraphBuilder;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.AbstractVertexVisitor;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.CallVertex;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.FuncVertex;
|
||||
|
@ -25,8 +26,10 @@ import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VertexFactor
|
|||
import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraph;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptConstructTargetSelector;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptConstructTargetSelector.JavaScriptConstructor;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptEntryPoints;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionDotCallTargetSelector;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptMethods;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.cast.types.AstMethodReference;
|
||||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
|
@ -46,6 +49,7 @@ import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterprete
|
|||
import com.ibm.wala.ipa.callgraph.propagation.cfa.nCFAContextSelector;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
|
@ -87,6 +91,11 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected FlowGraph flowGraphFactory() {
|
||||
FlowGraphBuilder builder = new FlowGraphBuilder(cha, cache);
|
||||
return builder.buildFlowGraph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a flow graph for the program to be analysed.
|
||||
*/
|
||||
|
@ -95,7 +104,7 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
/**
|
||||
* Main entry point: builds a flow graph, then extracts a call graph and returns it.
|
||||
*/
|
||||
public JSCallGraph buildCallGraph(IProgressMonitor monitor) throws CancelException {
|
||||
public JSCallGraph buildCallGraph(Iterable<Entrypoint> eps, IProgressMonitor monitor) throws CancelException {
|
||||
long fgBegin, fgEnd, cgBegin, cgEnd;
|
||||
|
||||
if(LOG_TIMINGS) fgBegin = System.currentTimeMillis();
|
||||
|
@ -108,7 +117,7 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
cgBegin = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
JSCallGraph cg = extract(flowGraph, monitor);
|
||||
JSCallGraph cg = extract(flowGraph, eps, monitor);
|
||||
|
||||
if(LOG_TIMINGS) {
|
||||
cgEnd = System.currentTimeMillis();
|
||||
|
@ -122,15 +131,13 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
* Extract a call graph from a given flow graph.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected JSCallGraph extract(FlowGraph flowgraph, IProgressMonitor monitor) throws CancelException {
|
||||
protected JSCallGraph extract(FlowGraph flowgraph, Iterable<Entrypoint> eps, IProgressMonitor monitor) throws CancelException {
|
||||
// set up call graph
|
||||
final JSCallGraph cg = new JSCallGraph(cha, options, cache);
|
||||
cg.init();
|
||||
cg.setInterpreter(new DelegatingSSAContextInterpreter(new AstContextInsensitiveSSAContextInterpreter(options, cache), new DefaultSSAInterpreter(options, cache)));
|
||||
|
||||
|
||||
// set up call edges from fake root to all script nodes
|
||||
JavaScriptEntryPoints eps = new JavaScriptEntryPoints(cha, cha.getLoader(JavaScriptTypes.jsLoader));
|
||||
|
||||
// set up call edges from fake root to all script nodes
|
||||
AbstractRootMethod fakeRootMethod = (AbstractRootMethod)cg.getFakeRootNode().getMethod();
|
||||
CGNode fakeRootNode = cg.findOrCreateNode(fakeRootMethod, Everywhere.EVERYWHERE);
|
||||
for(Iterator<Entrypoint> iter = eps.iterator(); iter.hasNext();) {
|
||||
|
@ -145,34 +152,33 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
// now add genuine call edges
|
||||
Set<Pair<CallVertex, FuncVertex>> edges = extractCallGraphEdges(flowgraph, monitor);
|
||||
|
||||
for (Pair<CallVertex, FuncVertex> edge : edges) {
|
||||
CallVertex callVertex = edge.fst;
|
||||
FuncVertex targetVertex = edge.snd;
|
||||
IClass kaller = callVertex.getCaller().getIClass();
|
||||
CGNode caller = cg.findOrCreateNode(kaller.getMethod(AstMethodReference.fnSelector), Everywhere.EVERYWHERE);
|
||||
CallSiteReference site = callVertex.getSite();
|
||||
IMethod target = targetSelector.getCalleeTarget(caller, site, targetVertex.getIClass());
|
||||
boolean isFunctionPrototypeCall = target != null
|
||||
&& target.getName().toString().startsWith(JavaScriptFunctionDotCallTargetSelector.SYNTHETIC_CALL_METHOD_PREFIX);
|
||||
if (isFunctionPrototypeCall) {
|
||||
handleFunctionPrototypeCallInvocation(flowgraph, monitor, cg, callVertex, caller, site, target);
|
||||
} else {
|
||||
addEdgeToJSCallGraph(cg, site, target, caller);
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<CallVertex, FuncVertex> edge : edges) {
|
||||
CallVertex callVertex = edge.fst;
|
||||
FuncVertex targetVertex = edge.snd;
|
||||
IClass kaller = callVertex.getCaller().getIClass();
|
||||
CGNode caller = cg.findOrCreateNode(kaller.getMethod(AstMethodReference.fnSelector), Everywhere.EVERYWHERE);
|
||||
CallSiteReference site = callVertex.getSite();
|
||||
IMethod target = targetSelector.getCalleeTarget(caller, site, targetVertex.getIClass());
|
||||
boolean isFunctionPrototypeCall = target != null
|
||||
&& target.getName().toString().startsWith(JavaScriptFunctionDotCallTargetSelector.SYNTHETIC_CALL_METHOD_PREFIX);
|
||||
if (isFunctionPrototypeCall) {
|
||||
handleFunctionPrototypeCallInvocation(flowgraph, monitor, cg, callVertex, caller, site, target);
|
||||
} else {
|
||||
addEdgeToJSCallGraph(cg, site, target, caller);
|
||||
}
|
||||
}
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
private void handleFunctionPrototypeCallInvocation(FlowGraph flowgraph, IProgressMonitor monitor, final JSCallGraph cg,
|
||||
private boolean handleFunctionPrototypeCallInvocation(FlowGraph flowgraph, IProgressMonitor monitor, final JSCallGraph cg,
|
||||
CallVertex callVertex, CGNode caller, CallSiteReference site,
|
||||
IMethod target) throws CancelException {
|
||||
// use to get 1-level of call string for Function.prototype.call, to
|
||||
// preserve the precision of the field-based call graph
|
||||
final nCFAContextSelector functionPrototypeCallSelector = new nCFAContextSelector(1, new ContextInsensitiveSelector());
|
||||
Context calleeContext = functionPrototypeCallSelector.getCalleeTarget(caller, site, target, null);
|
||||
addCGEdgeWithContext(cg, site, target, caller, calleeContext);
|
||||
boolean ret = addCGEdgeWithContext(cg, site, target, caller, calleeContext);
|
||||
CGNode functionPrototypeCallNode = cg.findOrCreateNode(target, calleeContext);
|
||||
// need to create nodes for reflective targets of call, and then add them
|
||||
// as callees of the synthetic method
|
||||
|
@ -181,29 +187,36 @@ public abstract class FieldBasedCallGraphBuilder {
|
|||
CallSiteReference reflectiveCallSite = functionPrototypeCallNode.getIR().iterateCallSites().next();
|
||||
for (FuncVertex f : reflectiveTargets) {
|
||||
IMethod reflectiveTgtMethod = targetSelector.getCalleeTarget(functionPrototypeCallNode, reflectiveCallSite, f.getIClass());
|
||||
addEdgeToJSCallGraph(cg, reflectiveCallSite, reflectiveTgtMethod, functionPrototypeCallNode);
|
||||
ret |= addEdgeToJSCallGraph(cg, reflectiveCallSite, reflectiveTgtMethod, functionPrototypeCallNode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private IMethod addEdgeToJSCallGraph(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller)
|
||||
private boolean addEdgeToJSCallGraph(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller)
|
||||
throws CancelException {
|
||||
return addCGEdgeWithContext(cg, site, target, caller, Everywhere.EVERYWHERE);
|
||||
}
|
||||
|
||||
Set<IClass> constructedTypes = HashSetFactory.make();
|
||||
|
||||
Everywhere targetContext = Everywhere.EVERYWHERE;
|
||||
@SuppressWarnings("deprecation")
|
||||
private IMethod addCGEdgeWithContext(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller,
|
||||
private boolean addCGEdgeWithContext(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller,
|
||||
Context targetContext) throws CancelException {
|
||||
boolean ret = false;
|
||||
if(target != null) {
|
||||
CGNode callee = cg.findOrCreateNode(target, targetContext);
|
||||
// add nodes first, to be on the safe side
|
||||
cg.addNode(caller); cg.addNode(callee);
|
||||
cg.addNode(caller);
|
||||
cg.addNode(callee);
|
||||
// add callee as successor of caller
|
||||
cg.addEdge(caller, callee);
|
||||
// add as site-specific target
|
||||
caller.addTarget(site, callee);
|
||||
ret = !cg.getPossibleTargets(caller, site).contains(callee);
|
||||
if (ret) {
|
||||
cg.addEdge(caller, callee);
|
||||
caller.addTarget(site, callee);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -53,8 +53,7 @@ public class OptimisticCallgraphBuilder extends FieldBasedCallGraphBuilder {
|
|||
|
||||
@Override
|
||||
public FlowGraph buildFlowGraph(IProgressMonitor monitor) throws CancelException {
|
||||
FlowGraphBuilder builder = new FlowGraphBuilder(cha, cache);
|
||||
FlowGraph flowgraph = builder.buildFlowGraph();
|
||||
FlowGraph flowgraph = flowGraphFactory();
|
||||
|
||||
// keep track of which call edges we already know about
|
||||
Set<Pair<CallVertex, FuncVertex>> knownEdges = HashSetFactory.make();
|
||||
|
|
|
@ -46,9 +46,8 @@ public class PessimisticCallGraphBuilder extends FieldBasedCallGraphBuilder {
|
|||
|
||||
@Override
|
||||
public FlowGraph buildFlowGraph(IProgressMonitor monitor) {
|
||||
FlowGraphBuilder builder = new FlowGraphBuilder(cha, cache);
|
||||
FlowGraph flowgraph = builder.buildFlowGraph();
|
||||
resolveLocalCalls(flowgraph);
|
||||
FlowGraph flowgraph = flowGraphFactory();
|
||||
resolveLocalCalls(flowgraph);
|
||||
return flowgraph;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,33 +79,42 @@ public class FlowGraphBuilder {
|
|||
|
||||
addPrimitives(flowgraph);
|
||||
|
||||
for(IClass klass : cha) {
|
||||
for(IMethod method : klass.getDeclaredMethods()) {
|
||||
if(method.getDescriptor().equals(AstMethodReference.fnDesc)) {
|
||||
IR ir = cache.getIR(method);
|
||||
FlowGraphSSAVisitor visitor = new FlowGraphSSAVisitor(ir, flowgraph);
|
||||
|
||||
// first visit normal instructions
|
||||
SSAInstruction[] normalInstructions = ir.getInstructions();
|
||||
for(int i=0;i<normalInstructions.length;++i)
|
||||
if(normalInstructions[i] != null) {
|
||||
visitor.instructionIndex = i;
|
||||
normalInstructions[i].visit(visitor);
|
||||
}
|
||||
|
||||
// now visit phis and catches
|
||||
visitor.instructionIndex = -1;
|
||||
for(Iterator<? extends SSAInstruction> iter=ir.iteratePhis();iter.hasNext();)
|
||||
iter.next().visit(visitor);
|
||||
|
||||
for(Iterator<SSAInstruction> iter=ir.iterateCatchInstructions();iter.hasNext();)
|
||||
iter.next().visit(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
visitProgram(flowgraph);
|
||||
|
||||
return flowgraph;
|
||||
}
|
||||
|
||||
protected void visitProgram(FlowGraph flowgraph) {
|
||||
for(IClass klass : cha) {
|
||||
for(IMethod method : klass.getDeclaredMethods()) {
|
||||
if(method.getDescriptor().equals(AstMethodReference.fnDesc))
|
||||
visitFunction(flowgraph, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitFunction(FlowGraph flowgraph, IMethod method) {
|
||||
{
|
||||
IR ir = cache.getIR(method);
|
||||
FlowGraphSSAVisitor visitor = new FlowGraphSSAVisitor(ir, flowgraph);
|
||||
|
||||
// first visit normal instructions
|
||||
SSAInstruction[] normalInstructions = ir.getInstructions();
|
||||
for(int i=0;i<normalInstructions.length;++i)
|
||||
if(normalInstructions[i] != null) {
|
||||
visitor.instructionIndex = i;
|
||||
normalInstructions[i].visit(visitor);
|
||||
}
|
||||
|
||||
// now visit phis and catches
|
||||
visitor.instructionIndex = -1;
|
||||
for(Iterator<? extends SSAInstruction> iter=ir.iteratePhis();iter.hasNext();)
|
||||
iter.next().visit(visitor);
|
||||
|
||||
for(Iterator<SSAInstruction> iter=ir.iterateCatchInstructions();iter.hasNext();)
|
||||
iter.next().visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
// primitive functions that are treated specially
|
||||
private static String[] primitiveFunctions = { "Object", "Function", "Array", "String", "Number", "RegExp" };
|
||||
|
|
|
@ -106,5 +106,4 @@ public class JSCallGraph extends AstCallGraph {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public class JSCallGraphUtil extends com.ibm.wala.cast.ipa.callgraph.CAstCallGra
|
|||
return ClassHierarchy.make(scope, loaders, JavaScriptLoader.JS);
|
||||
}
|
||||
|
||||
public static Iterable<Entrypoint> makeScriptRoots(IClassHierarchy cha) {
|
||||
public static JavaScriptEntryPoints makeScriptRoots(IClassHierarchy cha) {
|
||||
return new JavaScriptEntryPoints(cha, cha.getLoader(JavaScriptTypes.jsLoader));
|
||||
}
|
||||
|
||||
|
|
|
@ -55,22 +55,28 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
private final Map<Object, IMethod> constructors = HashMapFactory.make();
|
||||
|
||||
class JavaScriptConstructor extends JavaScriptSummarizedFunction {
|
||||
public static class JavaScriptConstructor extends JavaScriptSummarizedFunction {
|
||||
private final String toStringExtra;
|
||||
|
||||
private JavaScriptConstructor(MethodReference ref, MethodSummary summary, IClass declaringClass, String toStringExtra) {
|
||||
private final IClass constructorForType;
|
||||
|
||||
private JavaScriptConstructor(MethodReference ref, MethodSummary summary, IClass declaringClass, IClass constructorForType, String toStringExtra) {
|
||||
super(ref, summary, declaringClass);
|
||||
this.toStringExtra = toStringExtra;
|
||||
this.constructorForType = constructorForType;
|
||||
}
|
||||
|
||||
private JavaScriptConstructor(MethodReference ref, MethodSummary summary, IClass declaringClass) {
|
||||
this(ref, summary, declaringClass, "");
|
||||
private JavaScriptConstructor(MethodReference ref, MethodSummary summary, IClass declaringClass, IClass constructorForType) {
|
||||
this(ref, summary, declaringClass, constructorForType, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<ctor for " + getReference().getDeclaringClass() + toStringExtra + ">";
|
||||
}
|
||||
|
||||
public IClass constructedType() {
|
||||
return constructorForType;
|
||||
}
|
||||
}
|
||||
|
||||
public JavaScriptConstructTargetSelector(IClassHierarchy cha, MethodTargetSelector base) {
|
||||
|
@ -106,7 +112,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(9, new ConstantValue("__proto__"));
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cls);
|
||||
}
|
||||
|
||||
private IMethod makeUnaryValueConstructor(IClass cls) {
|
||||
|
@ -131,7 +137,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(7, new ConstantValue("__proto__"));
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cls);
|
||||
}
|
||||
|
||||
private IMethod makeValueConstructor(IClass cls, int nargs, Object value) {
|
||||
|
@ -171,7 +177,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(6, new ConstantValue("__proto__"));
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Object));
|
||||
}
|
||||
|
||||
private IMethod makeUnaryObjectConstructor(IClass cls) {
|
||||
|
@ -182,7 +188,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
S.addStatement(insts.ReturnInstruction(2, false));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Object));
|
||||
}
|
||||
|
||||
private IMethod makeObjectConstructor(IClass cls, int nargs) {
|
||||
|
@ -234,7 +240,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(7, new ConstantValue("__proto__"));
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Array));
|
||||
}
|
||||
|
||||
private IMethod makeArrayContentsConstructor(IClass cls, int nargs) {
|
||||
|
@ -270,7 +276,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(vn, new ConstantValue("__proto__"));
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Array));
|
||||
}
|
||||
|
||||
private IMethod makeArrayConstructor(IClass cls, int nargs) {
|
||||
|
@ -291,7 +297,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
S.addStatement(insts.ReturnInstruction(2, false));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.String));
|
||||
}
|
||||
|
||||
private IMethod makeUnaryStringCall(IClass cls) {
|
||||
|
@ -308,7 +314,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
S.addStatement(insts.ReturnInstruction(5, false));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.String));
|
||||
}
|
||||
|
||||
private IMethod makeStringCall(IClass cls, int nargs) {
|
||||
|
@ -331,7 +337,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
S.addStatement(insts.ReturnInstruction(2, false));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Number));
|
||||
}
|
||||
|
||||
private IMethod makeUnaryNumberCall(IClass cls) {
|
||||
|
@ -348,7 +354,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
S.addStatement(insts.ReturnInstruction(5, false));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
return new JavaScriptConstructor(ref, S, cls);
|
||||
return new JavaScriptConstructor(ref, S, cls, cha.lookupClass(JavaScriptTypes.Number));
|
||||
}
|
||||
|
||||
private IMethod makeNumberCall(IClass cls, int nargs) {
|
||||
|
@ -395,9 +401,9 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
//S.addConstant(8, new ConstantValue("__proto__"));
|
||||
|
||||
if (receiver != cls)
|
||||
return record(tableKey, new JavaScriptConstructor(ref, S, receiver, "(" + cls.getReference().getName() + ")"));
|
||||
return record(tableKey, new JavaScriptConstructor(ref, S, receiver, cls, "(" + cls.getReference().getName() + ")"));
|
||||
else
|
||||
return record(tableKey, new JavaScriptConstructor(ref, S, receiver));
|
||||
return record(tableKey, new JavaScriptConstructor(ref, S, receiver, cls));
|
||||
}
|
||||
|
||||
private int ctorCount = 0;
|
||||
|
@ -409,11 +415,11 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
return makeFunctionConstructor(cls, cls);
|
||||
} else if (nargs == 1) {
|
||||
if (ST.isStringConstant(callStmt.getUse(1))) {
|
||||
TypeReference ref = TypeReference.findOrCreate(JavaScriptTypes.jsLoader, TypeName.string2TypeName((String) ST
|
||||
TypeReference ref = TypeReference.findOrCreate(JavaScriptTypes.jsLoader, TypeName.string2TypeName(ST
|
||||
.getStringValue(callStmt.getUse(1))));
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println(("ctor type name is " + (String) ST.getStringValue(callStmt.getUse(1))));
|
||||
System.err.println(("ctor type name is " + ST.getStringValue(callStmt.getUse(1))));
|
||||
}
|
||||
|
||||
IClass cls2 = cha.lookupClass(ref);
|
||||
|
@ -511,7 +517,7 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
//S.addConstant(nargs + 9, new ConstantValue("__proto__"));
|
||||
|
||||
return record(key, new JavaScriptConstructor(ref, S, cls));
|
||||
return record(key, new JavaScriptConstructor(ref, S, cls, cls));
|
||||
}
|
||||
|
||||
private IMethod findOrCreateConstructorMethod(IR callerIR, SSAAbstractInvokeInstruction callStmt, IClass receiver, int nargs) {
|
||||
|
|
|
@ -103,4 +103,16 @@ public abstract class ScriptEntryPoints implements Iterable<Entrypoint> {
|
|||
return ES.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
public Entrypoint make(String scriptName) {
|
||||
IClass cls = cha.lookupClass(TypeReference.findOrCreate(scriptType.getClassLoader().getReference(), scriptName));
|
||||
assert cls != null && cha.isSubclassOf(cls, scriptType) && !cls.isAbstract();
|
||||
for (IMethod method : cls.getDeclaredMethods()) {
|
||||
if (keep(method)) {
|
||||
return new ScriptEntryPoint(method);
|
||||
}
|
||||
}
|
||||
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1092,6 +1092,8 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
|
||||
private final int[] instructionToBlockMap;
|
||||
|
||||
private final int[] pcMap;
|
||||
|
||||
private final String functionName;
|
||||
|
||||
private final SymbolTable symtab;
|
||||
|
@ -1149,7 +1151,8 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
this.symtab = symtab;
|
||||
functionName = n.getName();
|
||||
instructionToBlockMap = new int[liveBlocks.size()];
|
||||
|
||||
pcMap = hasDeadBlocks? new int[ icfg.currentInstruction ]: null;
|
||||
|
||||
final Map<PreBasicBlock, Collection<PreBasicBlock>> normalEdges =
|
||||
hasDeadBlocks? HashMapFactory.<PreBasicBlock,Collection<PreBasicBlock>>make() : null;
|
||||
final Map<PreBasicBlock, Collection<PreBasicBlock>> exceptionalEdges =
|
||||
|
@ -1179,6 +1182,14 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
PreBasicBlock block = blocks.get(i);
|
||||
block.setGraphNodeId(-1);
|
||||
if (liveBlocks.contains(block)) {
|
||||
if (hasDeadBlocks) {
|
||||
int offset = 0;
|
||||
for(int oldPC = block.getFirstInstructionIndex();
|
||||
offset < block.instructions().size();
|
||||
oldPC++, offset++) {
|
||||
pcMap[instruction + offset] = oldPC;
|
||||
}
|
||||
}
|
||||
if (block.getFirstInstructionIndex() >= 0) {
|
||||
block.setFirstIndex(instruction);
|
||||
block.setLastIndex((instruction += block.instructions().size()) - 1);
|
||||
|
@ -1277,12 +1288,12 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
|
||||
@Override
|
||||
public int getProgramCounter(int index) {
|
||||
return index;
|
||||
return pcMap == null? index: pcMap[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
SSAInstruction[] insts = (SSAInstruction[]) getInstructions();
|
||||
SSAInstruction[] insts = getInstructions();
|
||||
StringBuffer s = new StringBuffer("CAst CFG of " + functionName);
|
||||
int params[] = symtab.getParameterValueNumbers();
|
||||
for (int i = 0; i < params.length; i++)
|
||||
|
@ -1290,7 +1301,7 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
s.append("\n");
|
||||
|
||||
for (int i = 0; i < getNumberOfNodes(); i++) {
|
||||
PreBasicBlock bb = (PreBasicBlock) getNode(i);
|
||||
PreBasicBlock bb = getNode(i);
|
||||
s.append(bb).append("\n");
|
||||
|
||||
for (Iterator ss = getSuccNodes(bb); ss.hasNext();)
|
||||
|
@ -1843,12 +1854,12 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
|
|||
|
||||
@Override
|
||||
public boolean isLexicallyScoped(Symbol s) {
|
||||
return ((AbstractScope) getEntityScope()).isLexicallyScoped(s);
|
||||
return getEntityScope().isLexicallyScoped(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CAstEntity getEntity() {
|
||||
return ((AbstractScope) getEntityScope()).getEntity();
|
||||
return getEntityScope().getEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.ide.jsdt.tests"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -XX:MaxPermSize=512m -Xmx2048m -Xdock:icon=../Resources/Eclipse.icns -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -XX:MaxPermSize=512m -Xmx2048m -ea -Xdock:icon=../Resources/Eclipse.icns -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<stringAttribute key="product" value="org.eclipse.sdk.ide"/>
|
||||
<booleanAttribute key="run_in_ui_thread" value="true"/>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph;
|
||||
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.util.functions.Function;
|
||||
|
||||
public class FilteredFlowGraphBuilder extends FlowGraphBuilder {
|
||||
|
||||
private final Function<IMethod, Boolean> filter;
|
||||
|
||||
public FilteredFlowGraphBuilder(IClassHierarchy cha, AnalysisCache cache, Function<IMethod, Boolean> filter) {
|
||||
super(cha, cache);
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitFunction(FlowGraph flowgraph, IMethod method) {
|
||||
if (filter.apply(method)) {
|
||||
super.visitFunction(flowgraph, method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ package com.ibm.wala.cast.js.client;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
|
||||
|
@ -20,6 +21,9 @@ import com.ibm.wala.cast.ipa.callgraph.CAstAnalysisScope;
|
|||
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.FieldBasedCallGraphBuilder;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.PessimisticCallGraphBuilder;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FilteredFlowGraphBuilder;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph;
|
||||
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraphBuilder;
|
||||
import com.ibm.wala.cast.js.client.impl.ZeroCFABuilderFactory;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
|
||||
|
@ -27,7 +31,9 @@ import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
|||
import com.ibm.wala.cast.js.loader.JavaScriptLoaderFactory;
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.cast.loader.AstMethod;
|
||||
import com.ibm.wala.classLoader.ClassLoaderFactory;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.ide.client.EclipseProjectSourceAnalysisEngine;
|
||||
import com.ibm.wala.ide.util.JavaScriptEclipseProjectPath;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
|
@ -41,6 +47,8 @@ import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.NullProgressMonitor;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.functions.Function;
|
||||
|
||||
public class EclipseJavaScriptAnalysisEngine extends EclipseProjectSourceAnalysisEngine<IJavaScriptProject> {
|
||||
|
||||
|
@ -97,8 +105,45 @@ public class EclipseJavaScriptAnalysisEngine extends EclipseProjectSourceAnalysi
|
|||
}
|
||||
|
||||
public CallGraph getFieldBasedCallGraph() throws CancelException {
|
||||
Iterable<Entrypoint> roots = JSCallGraphUtil.makeScriptRoots(getClassHierarchy());
|
||||
FieldBasedCallGraphBuilder builder = new PessimisticCallGraphBuilder(getClassHierarchy(), getDefaultOptions(roots), makeDefaultCache());
|
||||
return builder.buildCallGraph(new NullProgressMonitor());
|
||||
return getFieldBasedCallGraph(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()));
|
||||
}
|
||||
|
||||
public CallGraph getFieldBasedCallGraph(String scriptName) throws CancelException {
|
||||
Set<Entrypoint> eps= HashSetFactory.make();
|
||||
eps.add(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()).make(scriptName));
|
||||
eps.add(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()).make("Lprologue.js"));
|
||||
return getFieldBasedCallGraph(eps);
|
||||
}
|
||||
|
||||
private String getScriptName(AstMethod m) {
|
||||
String fileName = m.getSourcePosition().getURL().getFile();
|
||||
return fileName.substring(fileName.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
protected CallGraph getFieldBasedCallGraph(Iterable<Entrypoint> roots) throws CancelException {
|
||||
final Set<String> scripts = HashSetFactory.make();
|
||||
for(Entrypoint e : roots) {
|
||||
String scriptName = getScriptName(((AstMethod)e.getMethod()));
|
||||
scripts.add(scriptName);
|
||||
}
|
||||
|
||||
FieldBasedCallGraphBuilder builder = new PessimisticCallGraphBuilder(getClassHierarchy(), getDefaultOptions(roots), makeDefaultCache()) {
|
||||
@Override
|
||||
protected FlowGraph flowGraphFactory() {
|
||||
FlowGraphBuilder b = new FilteredFlowGraphBuilder(cha, cache, new Function<IMethod, Boolean>() {
|
||||
@Override
|
||||
public Boolean apply(IMethod object) {
|
||||
if (object instanceof AstMethod) {
|
||||
return scripts.contains(getScriptName((AstMethod)object));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return b.buildFlowGraph();
|
||||
}
|
||||
};
|
||||
|
||||
return builder.buildCallGraph(roots, new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,19 +2,25 @@ package com.ibm.wala.cast.js.client;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
|
||||
|
||||
import com.ibm.wala.cast.ipa.callgraph.CAstAnalysisScope;
|
||||
import com.ibm.wala.cast.js.html.WebPageLoaderFactory;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
import com.ibm.wala.classLoader.ClassLoaderFactory;
|
||||
import com.ibm.wala.ide.util.EclipseWebProjectPath;
|
||||
import com.ibm.wala.ide.util.JavaScriptEclipseProjectPath;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisScope;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
||||
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
|
||||
public class EclipseWebAnalysisEngine extends EclipseJavaScriptAnalysisEngine {
|
||||
|
||||
|
@ -37,4 +43,13 @@ public class EclipseWebAnalysisEngine extends EclipseJavaScriptAnalysisEngine {
|
|||
return new EclipseWebProjectPath(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallGraph getFieldBasedCallGraph(String scriptName) throws CancelException {
|
||||
Set<Entrypoint> eps= HashSetFactory.make();
|
||||
eps.add(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()).make(scriptName));
|
||||
eps.add(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()).make("Lpreamble.js"));
|
||||
eps.add(JSCallGraphUtil.makeScriptRoots(getClassHierarchy()).make("Lprologue.js"));
|
||||
return getFieldBasedCallGraph(eps);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.ibm.wala.ide.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
@ -12,15 +13,21 @@ import org.eclipse.core.runtime.IPath;
|
|||
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
|
||||
|
||||
import com.ibm.wala.cast.ir.translator.TranslatorToCAst.Error;
|
||||
import com.ibm.wala.cast.js.JavaScriptPlugin;
|
||||
import com.ibm.wala.cast.js.html.MappedSourceModule;
|
||||
import com.ibm.wala.cast.js.html.WebUtil;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
||||
import com.ibm.wala.classLoader.FileModule;
|
||||
import com.ibm.wala.classLoader.Module;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.ide.classloader.EclipseSourceDirectoryTreeModule;
|
||||
import com.ibm.wala.util.collections.MapUtil;
|
||||
import com.ibm.wala.util.io.FileProvider;
|
||||
|
||||
public class EclipseWebProjectPath extends JavaScriptEclipseProjectPath {
|
||||
|
||||
private boolean addedPreamble;
|
||||
|
||||
public EclipseWebProjectPath(IJavaScriptProject p) throws IOException, CoreException {
|
||||
super(p);
|
||||
}
|
||||
|
@ -36,6 +43,11 @@ public class EclipseWebProjectPath extends JavaScriptEclipseProjectPath {
|
|||
try {
|
||||
scripts = WebUtil.extractScriptFromHTML(new URL(urlString)).fst;
|
||||
s.addAll(scripts);
|
||||
if (! addedPreamble) {
|
||||
File preamble = getProlgueFile("preamble.js");
|
||||
s.add(new SourceFileModule(preamble, "preamble.js", null));
|
||||
addedPreamble = true;
|
||||
}
|
||||
} catch (MalformedURLException e1) {
|
||||
assert false : "internal error constructing URL " + urlString;
|
||||
} catch (Error e1) {
|
||||
|
|
|
@ -10,13 +10,10 @@
|
|||
*******************************************************************************/
|
||||
package com.ibm.wala.ide.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
@ -25,21 +22,28 @@ import org.eclipse.wst.jsdt.core.IJavaScriptProject;
|
|||
import org.eclipse.wst.jsdt.core.JavaScriptCore;
|
||||
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
|
||||
|
||||
import com.ibm.wala.cast.ir.translator.TranslatorToCAst.Error;
|
||||
import com.ibm.wala.cast.js.html.MappedSourceModule;
|
||||
import com.ibm.wala.cast.js.html.WebUtil;
|
||||
import com.ibm.wala.cast.js.JavaScriptPlugin;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.cast.loader.CAstAbstractLoader;
|
||||
import com.ibm.wala.classLoader.FileModule;
|
||||
import com.ibm.wala.classLoader.Module;
|
||||
import com.ibm.wala.classLoader.SourceModule;
|
||||
import com.ibm.wala.classLoader.SourceURLModule;
|
||||
import com.ibm.wala.ide.classloader.EclipseSourceDirectoryTreeModule;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
import com.ibm.wala.util.collections.MapUtil;
|
||||
import com.ibm.wala.util.io.FileProvider;
|
||||
|
||||
public class JavaScriptEclipseProjectPath extends EclipseProjectPath<IIncludePathEntry, IJavaScriptProject> {
|
||||
|
||||
protected File getProlgueFile(String file) {
|
||||
try {
|
||||
FileProvider fileProvider = new EclipseFileProvider(JavaScriptPlugin.getDefault());
|
||||
JavaScriptLoader.addBootstrapFile(file);
|
||||
return fileProvider.getFile("dat/" + file, getClass().getClassLoader());
|
||||
} catch (IOException e) {
|
||||
assert false : "cannot find " + file;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum JSLoader implements ILoader {
|
||||
JAVASCRIPT(JavaScriptTypes.jsLoader);
|
||||
|
||||
|
@ -58,6 +62,10 @@ public class JavaScriptEclipseProjectPath extends EclipseProjectPath<IIncludePat
|
|||
protected JavaScriptEclipseProjectPath(IJavaScriptProject p) throws IOException,
|
||||
CoreException {
|
||||
super(p.getProject(), AnalysisScopeType.SOURCE_FOR_PROJ_AND_LINKED_PROJS);
|
||||
|
||||
List<Module> s = MapUtil.findOrCreateList(modules, JSLoader.JAVASCRIPT);
|
||||
File preamble = getProlgueFile("prologue.js");
|
||||
s.add(new SourceFileModule(preamble, "prologue.js", null));
|
||||
}
|
||||
|
||||
public static JavaScriptEclipseProjectPath make(IJavaScriptProject p) throws IOException, CoreException {
|
||||
|
|
|
@ -198,11 +198,11 @@ public class EclipseFileProvider extends FileProvider {
|
|||
if (p == null) {
|
||||
return getFileFromClassLoader(fileName, loader);
|
||||
} else {
|
||||
File f = getFileFromPlugin(p, fileName);
|
||||
if (f == null) {
|
||||
f = getFileFromClassLoader(fileName, loader);
|
||||
}
|
||||
return f;
|
||||
try {
|
||||
return getFileFromPlugin(p, fileName);
|
||||
} catch (IOException e) {
|
||||
return getFileFromClassLoader(fileName, loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue