NodeJS Support, including Ant build script to download Nodejs Core Library files
This commit is contained in:
parent
8855f80c97
commit
9de3da19fb
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.cast.js.nodejs"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.cast.js"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.core"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.util"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="src" path="testdata"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.ibm.wala.cast.js.nodejs.test</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,43 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.nodejs.NodejsCallGraphBuilderUtil;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
|
||||
/**
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsRequireJsonTest {
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireJsonTest/index.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Lempty/jsonModule>"));
|
||||
assertTrue(cgString.contains("Lnested/jsonModule>"));
|
||||
assertTrue(cgString.contains("Lpackage/jsonModule>"));
|
||||
assertTrue(!cgString.contains("?"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.nodejs.NodejsCallGraphBuilderUtil;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
|
||||
/**
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsRequireTargetSelectorResolveTest {
|
||||
|
||||
@Test
|
||||
public void testRequireSimple() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireTargetSelectorResolve/requireSimple/index.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Lmod/nodejsModule/moduleSource/exec>"));
|
||||
assertTrue(cgString.contains("Lmod/nodejsModule/moduleSource/SomeClass/hello>"));
|
||||
assertTrue(!cgString.contains("?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireStaticCircular() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireTargetSelectorResolve/requireStaticCircular/index.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Llib1/nodejsModule/moduleSource/lib1>"));
|
||||
assertTrue(cgString.contains("Llib2/nodejsModule/moduleSource/lib2>"));
|
||||
assertTrue(!cgString.contains("?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireDynamic() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireTargetSelectorResolve/requireDynamic/index.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Llib1/nodejsModule/moduleSource/lib1>"));
|
||||
assertTrue(cgString.contains("Llib2/nodejsModule/moduleSource/lib2>"));
|
||||
assertTrue(!cgString.contains("?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireNodeModules() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireTargetSelectorResolve/requireNodeModules/index.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Lnode_modules_lib_node_modules_sublib_sublib/nodejsModule/moduleSource"));
|
||||
assertTrue(!cgString.contains("?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireCoreModules() throws Exception {
|
||||
URL fileUrl = getClass().getClassLoader().getResource("NodejsRequireTargetSelectorResolve/requireCoreModules.js");
|
||||
File file = new File(fileUrl.toURI());
|
||||
|
||||
PropagationCallGraphBuilder builder = NodejsCallGraphBuilderUtil.makeCGBuilder(file);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
String cgString = CG.toString();
|
||||
|
||||
assertTrue(cgString.contains("Lutil/nodejsModule/moduleSource/util"));
|
||||
assertTrue(cgString.contains("Lhttps/nodejsModule/moduleSource/https"));
|
||||
assertTrue(cgString.contains("Lhttp/nodejsModule/moduleSource/http"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,3 @@
|
|||
var empty = require('./empty.json');
|
||||
var empty = require('./nested.json');
|
||||
var empty = require('./package.json');
|
|
@ -0,0 +1 @@
|
|||
{ num: 7, obj: { val1: "hello", num: 77 } }
|
15
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireJsonTest/package.json
vendored
Normal file
15
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireJsonTest/package.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "test2",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"poc-lettersonly-gm": "^1.0.1",
|
||||
"validator": "^5.5.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
var util = require('util');
|
||||
var util = require('https');
|
|
@ -0,0 +1,9 @@
|
|||
var lib1 = myRequire('lib1', true);
|
||||
lib1();
|
||||
|
||||
function myRequire(name, local) {
|
||||
var prefix = '';
|
||||
if (local) prefix = './';
|
||||
|
||||
return require(prefix+name);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
var lib2 = require('./lib2');
|
||||
|
||||
module.exports = function lib1() {
|
||||
lib2();
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
var lib1 = require('./lib1');
|
||||
|
||||
module.exports = function lib2() {
|
||||
lib1();
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
require('lib');
|
0
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib/helper.js
generated
vendored
Normal file
0
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib/helper.js
generated
vendored
Normal file
2
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib/index.js
generated
vendored
Normal file
2
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib/index.js
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
require('./helper');
|
||||
require('sublib');
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"main": "sublib.js"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
require('lib2');
|
2
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib2/index.js
generated
vendored
Normal file
2
com.ibm.wala.cast.js.nodejs.test/testdata/NodejsRequireTargetSelectorResolve/requireNodeModules/node_modules/lib2/index.js
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
module.exports = function lib2() {
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
var mod = require('./mod');
|
||||
mod.exec();
|
|
@ -0,0 +1,8 @@
|
|||
function SomeClass() {
|
||||
this.hello = function hello() {}
|
||||
}
|
||||
|
||||
exports.exec = function exec() {
|
||||
var c = new SomeClass();
|
||||
c.hello();
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
var lib1 = require('./lib1');
|
||||
lib1();
|
|
@ -0,0 +1,5 @@
|
|||
var lib2 = require('./lib2');
|
||||
|
||||
module.exports = function lib1() {
|
||||
lib2();
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
var lib1 = require('./lib1');
|
||||
|
||||
module.exports = function lib2() {
|
||||
lib1();
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="dat">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.cast"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.cast.js"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.cast.js.rhino"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.core"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.util"/>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.ibm.wala.shrike"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.ibm.wala.cast.js.nodejs</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,39 @@
|
|||
<project name="com.ibm.wala.cast.js.nodejs" default="download-core-libs" basedir=".">
|
||||
<description>
|
||||
WALA Nodejs Front-end
|
||||
</description>
|
||||
<property name="nodejs-release" location="nodejs.zip" />
|
||||
<property name="core-modules" location="dat/core-modules" />
|
||||
|
||||
<target name="download-core-libs">
|
||||
<!-- 0a604e92e258c5ee2752d763e50721e35053f135 is v0.12.4-release -->
|
||||
<get src="https://api.github.com/repos/nodejs/node/zipball/0a604e92e258c5ee2752d763e50721e35053f135"
|
||||
dest="${nodejs-release}" />
|
||||
|
||||
<tempfile property="unzip-dest" />
|
||||
|
||||
<unzip src="${nodejs-release}"
|
||||
dest="${unzip-dest}">
|
||||
|
||||
<patternset>
|
||||
<include name="*/lib/**" />
|
||||
</patternset>
|
||||
</unzip>
|
||||
|
||||
<mkdir dir="${core-modules}" />
|
||||
|
||||
<move todir="${core-modules}"
|
||||
flatten="true">
|
||||
|
||||
<fileset dir="${unzip-dest}">
|
||||
<include name="*/lib/*.js" />
|
||||
</fileset>
|
||||
</move>
|
||||
|
||||
<!-- Cleanup -->
|
||||
<delete includeemptydirs="true">
|
||||
<fileset dir="${unzip-dest}" includes="**" />
|
||||
<fileset file="${nodejs-release}" />
|
||||
</delete>
|
||||
</target>
|
||||
</project>
|
|
@ -0,0 +1,4 @@
|
|||
rules:
|
||||
# Custom rules in tools/eslint-rules
|
||||
require-buffer: 2
|
||||
buffer-constructor: 2
|
|
@ -0,0 +1 @@
|
|||
*.js
|
|
@ -0,0 +1,13 @@
|
|||
var timerId = 0;
|
||||
|
||||
function setTimeout(cb, t) {
|
||||
cb();
|
||||
return timerId++;
|
||||
}
|
||||
|
||||
function setInterval(cb, t) {
|
||||
return setTimeout(function () {
|
||||
cb();
|
||||
setInterval(cb, t);
|
||||
}, t);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
(function jsonModule() {
|
||||
return /*/ WALA-INSERT-CODE-HERE /*/;
|
||||
})();
|
|
@ -0,0 +1,12 @@
|
|||
(function nodejsModule() {
|
||||
var module = {
|
||||
require: function __WALA__require(id) {},
|
||||
exports: {}
|
||||
};
|
||||
|
||||
(function moduleSource(exports, require, module, __filename, __dirname) {
|
||||
/*/ WALA-INSERT-CODE-HERE /*/
|
||||
})(module.exports, module.require, module, '/*/ WALA-INSERT-FILENAME-HERE /*/', '/*/ WALA-INSERT-DIRNAME-HERE /*/');
|
||||
|
||||
return module.exports;
|
||||
})();
|
|
@ -0,0 +1,34 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.ibm.wala.cast.js.nodejs</groupId>
|
||||
<artifactId>com.ibm.wala.cast.js.nodejs</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>dat</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20160212</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,114 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.ibm.wala.cast.ipa.callgraph.CAstAnalysisScope;
|
||||
import com.ibm.wala.cast.ipa.callgraph.CAstCallGraphUtil;
|
||||
import com.ibm.wala.cast.ipa.callgraph.StandardFunctionTargetSelector;
|
||||
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSZeroOrOneXCFABuilder;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptEntryPoints;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.PropertyNameContextSelector;
|
||||
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.translator.JavaScriptTranslatorFactory;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.classLoader.Language;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.classLoader.SourceModule;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
||||
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXInstanceKeys;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.ssa.IRFactory;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.WalaException;
|
||||
|
||||
/**
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsCallGraphBuilderUtil extends JSCallGraphUtil {
|
||||
|
||||
public static PropagationCallGraphBuilder makeCGBuilder(File mainFile)
|
||||
throws IOException, IllegalArgumentException, CancelException, WalaException {
|
||||
return makeCGBuilder(mainFile.getParentFile(), mainFile);
|
||||
}
|
||||
|
||||
public static PropagationCallGraphBuilder makeCGBuilder(File workingDir, File mainFile)
|
||||
throws IOException, IllegalArgumentException, CancelException, WalaException {
|
||||
JavaScriptTranslatorFactory translatorFactory = new CAstRhinoTranslatorFactory();
|
||||
JSCallGraphUtil.setTranslatorFactory(translatorFactory);
|
||||
|
||||
Language language = JavaScriptLoader.JS;
|
||||
Collection<Language> languages = Collections.singleton(language);
|
||||
|
||||
IRFactory<IMethod> irFactory = new AstIRFactory.AstDefaultIRFactory();
|
||||
AnalysisCache cache = new AnalysisCache(irFactory);
|
||||
|
||||
JavaScriptLoaderFactory loaders = new JavaScriptLoaderFactory(translatorFactory, null);
|
||||
|
||||
SourceFileModule mainSourceModule = CAstCallGraphUtil.makeSourceModule(mainFile.toURI().toURL(),
|
||||
mainFile.getName());
|
||||
String mainFileClassName = NodejsRequiredSourceModule.convertFileToClassName(workingDir, mainFile);
|
||||
|
||||
SourceModule[] files = new SourceModule[] {
|
||||
JSCallGraphUtil.getPrologueFile("prologue.js"),
|
||||
JSCallGraphUtil.getPrologueFile("extended-prologue.js"),
|
||||
new NodejsRequiredSourceModule(mainFileClassName, mainFile, mainSourceModule) };
|
||||
|
||||
CAstAnalysisScope scope = new CAstAnalysisScope(files, loaders, languages);
|
||||
|
||||
IClassHierarchy cha = ClassHierarchy.make(scope, loaders, language, null);
|
||||
com.ibm.wala.cast.js.util.Util.checkForFrontEndErrors(cha);
|
||||
|
||||
// Make Script Roots
|
||||
Iterable<Entrypoint> roots = new JavaScriptEntryPoints(cha, loaders.getTheLoader());
|
||||
|
||||
// Make Options
|
||||
JSAnalysisOptions options = new JSAnalysisOptions(scope, roots);
|
||||
options.setUseConstantSpecificKeys(true);
|
||||
options.setUseStacksForLexicalScoping(true);
|
||||
options.setHandleCallApply(true);
|
||||
// Important to be able to identify what file are required
|
||||
options.setTraceStringConstants(true);
|
||||
|
||||
com.ibm.wala.ipa.callgraph.impl.Util.addDefaultSelectors(options, cha);
|
||||
|
||||
MethodTargetSelector baseSelector = new StandardFunctionTargetSelector(cha, options.getMethodTargetSelector());
|
||||
NodejsRequireTargetSelector requireTargetSelector = new NodejsRequireTargetSelector(workingDir, baseSelector);
|
||||
options.setSelector(requireTargetSelector);
|
||||
|
||||
JSCFABuilder builder = new JSZeroOrOneXCFABuilder(cha, options, cache, null, null,
|
||||
ZeroXInstanceKeys.ALLOCATIONS, true);
|
||||
|
||||
// A little hacky, but the instance of RequireTargetSelector is required to build the CallGraphBuilder
|
||||
// and the RequireTargetSelector also needs the CallGraphBuilder instance.
|
||||
requireTargetSelector.setCallGraphBuilder(builder);
|
||||
|
||||
ContextSelector contextSelector = new PropertyNameContextSelector(cache, 2, builder.getContextSelector());
|
||||
builder.setContextSelector(contextSelector);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
||||
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptMethods;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
import com.ibm.wala.classLoader.IClass;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.classLoader.SourceModule;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.ConcreteTypeKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.collections.HashMapFactory;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.intset.OrdinalSet;
|
||||
import com.ibm.wala.util.ssa.ClassLookupException;
|
||||
|
||||
/**
|
||||
* This class is used by WALA internals to resolve to what functions a call
|
||||
* could potentially invoke.
|
||||
*
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsRequireTargetSelector implements MethodTargetSelector {
|
||||
|
||||
private File rootDir;
|
||||
private MethodTargetSelector base;
|
||||
private PropagationCallGraphBuilder builder;
|
||||
|
||||
private HashMap<String, IMethod> previouslyRequired = HashMapFactory.make();
|
||||
|
||||
public NodejsRequireTargetSelector(File rootDir, MethodTargetSelector base) {
|
||||
this.rootDir = rootDir;
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
public void setCallGraphBuilder(PropagationCallGraphBuilder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic idea: If the called method is named "__WALA__require", it is most likely
|
||||
* the require-function mock from the module-wrapper. To figure out what file
|
||||
* shall be required, pointer analysis is used to identify strings that can
|
||||
* flow into the require call. That file is than loaded, wrapped into the
|
||||
* module wrapper and returned as method that will be invoked. Therefore,
|
||||
* there will never be an call graph edge to the require function call,
|
||||
* the require function is replaced by the file that is included through
|
||||
* the require call.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass receiver) {
|
||||
PointerAnalysis<InstanceKey> pointerAnalysis = builder.getPointerAnalysis();
|
||||
JavaScriptLoader jsLoader = (JavaScriptLoader) builder.getClassHierarchy().getLoader(JavaScriptTypes.jsLoader);
|
||||
|
||||
IMethod calledMethod = base.getCalleeTarget(caller, site, receiver);
|
||||
|
||||
if (calledMethod != null && calledMethod.getDeclaringClass().toString().endsWith("/__WALA__require")) {
|
||||
JavaScriptInvoke callInstr = getInvokeInstruction(caller, site);
|
||||
|
||||
Set<String> targets = getRequireTargets(pointerAnalysis, caller, callInstr);
|
||||
if (targets.size() == 0) {
|
||||
// There is no possible call target
|
||||
throw new RuntimeException("No require target found in method: "+caller.getMethod());
|
||||
}
|
||||
|
||||
for (String target : targets) {
|
||||
try {
|
||||
File workingDir = new File(receiver.getSourceFileName()).getParentFile();
|
||||
SourceModule sourceModule = resolve(rootDir, workingDir, target);
|
||||
if (previouslyRequired.containsKey(sourceModule.getClassName())) {
|
||||
return previouslyRequired.get(sourceModule.getClassName());
|
||||
}
|
||||
|
||||
String className = "L" + sourceModule.getClassName() + "/nodejsModule";
|
||||
if (sourceModule instanceof NodejsRequiredSourceModule
|
||||
&& ((NodejsRequiredSourceModule) sourceModule).getFile().toString().endsWith(".json")) {
|
||||
className = "L" + sourceModule.getClassName() + "/jsonModule";
|
||||
}
|
||||
|
||||
JSCallGraphUtil.loadAdditionalFile(builder.getClassHierarchy(), jsLoader, sourceModule);
|
||||
IClass script = builder.getClassHierarchy()
|
||||
.lookupClass(TypeReference.findOrCreate(jsLoader.getReference(), className));
|
||||
|
||||
IMethod method = script.getMethod(JavaScriptMethods.fnSelector);
|
||||
previouslyRequired.put(sourceModule.getClassName(), method);
|
||||
|
||||
return method;
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return calledMethod;
|
||||
}
|
||||
|
||||
private JavaScriptInvoke getInvokeInstruction(CGNode caller, CallSiteReference site) {
|
||||
IR callerIR = caller.getIR();
|
||||
SSAAbstractInvokeInstruction callInstrs[] = callerIR.getCalls(site);
|
||||
assert callInstrs.length == 1;
|
||||
return (JavaScriptInvoke) callInstrs[0];
|
||||
}
|
||||
|
||||
private Set<String> getRequireTargets(PointerAnalysis<InstanceKey> pointerAnalysis, CGNode caller,
|
||||
JavaScriptInvoke callInstr) {
|
||||
HashSet<String> set = HashSetFactory.make();
|
||||
|
||||
PointerKey pk = builder.getPointerKeyForLocal(caller, callInstr.getUse(2));
|
||||
OrdinalSet<InstanceKey> instanceKeys = pointerAnalysis.getPointsToSet(pk);
|
||||
|
||||
for (InstanceKey instanceKey : instanceKeys) {
|
||||
if (instanceKey instanceof ConstantKey<?>) {
|
||||
Object value = ((ConstantKey<?>) instanceKey).getValue();
|
||||
if (value instanceof String) {
|
||||
set.add((String) value);
|
||||
}
|
||||
else {
|
||||
System.err.println("NodejsRequireTargetSelector: Unexpected value: " + value);
|
||||
return HashSetFactory.make();
|
||||
}
|
||||
}
|
||||
else if (instanceKey instanceof ConcreteTypeKey) {
|
||||
// Cannot do anything with this information...
|
||||
}
|
||||
else {
|
||||
System.err.println("NodejsRequireTargetSelector: Unexpected instanceKey: " + instanceKey.getClass() + " -- " + instanceKey);
|
||||
return HashSetFactory.make();
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the Nodejs require.resolve algorithm,
|
||||
* see https://nodejs.org/api/modules.html#modules_all_together
|
||||
*
|
||||
* require(X) from module at path Y
|
||||
* 1. If X is a core module,
|
||||
* a. return the core module
|
||||
* b. STOP
|
||||
* 2. If X begins with './' or '/' or '../'
|
||||
* a. LOAD_AS_FILE(Y + X)
|
||||
* b. LOAD_AS_DIRECTORY(Y + X)
|
||||
* 3. LOAD_NODE_MODULES(X, dirname(Y))
|
||||
* 4. THROW "not found"
|
||||
*
|
||||
* @param dir Y in the pseudo algorithm
|
||||
* @param target X in the pseudo algorithm
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static SourceFileModule resolve(File rootDir, File dir, String target) throws IOException {
|
||||
if (NodejsRequiredCoreModule.isCoreModule(target))
|
||||
return NodejsRequiredCoreModule.make(target);
|
||||
|
||||
if (target.startsWith("./") || target.startsWith("/") || target.startsWith("../")) {
|
||||
SourceFileModule module = loadAsFile(rootDir, new File(dir, target));
|
||||
if (module != null) return module;
|
||||
|
||||
module = loadAsDirectory(rootDir, new File(dir, target));
|
||||
if (module != null) return module;
|
||||
}
|
||||
|
||||
SourceFileModule module = loadNodeModules(rootDir, dir, target);
|
||||
if (module != null) return module;
|
||||
|
||||
throw new ClassLookupException("Required module not found: "+target+" in "+dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* LOAD_AS_FILE(X)
|
||||
* 1. If X is a file, load X as JavaScript text. STOP
|
||||
* 2. If X.js is a file, load X.js as JavaScript text. STOP
|
||||
* 3. If X.json is a file, parse X.json to a JavaScript Object. STOP
|
||||
* 4. If X.node is a file, load X.node as binary addon. STOP
|
||||
*
|
||||
* @param f
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private static SourceFileModule loadAsFile(File rootDir, File f) throws IOException {
|
||||
// 1.
|
||||
if (f.isFile())
|
||||
return NodejsRequiredSourceModule.make(rootDir, f);
|
||||
|
||||
// 2.
|
||||
File jsFile = new File(f+".js");
|
||||
if (jsFile.isFile())
|
||||
return NodejsRequiredSourceModule.make(rootDir, jsFile);
|
||||
|
||||
// 3.
|
||||
File jsonFile = new File(f+".json");
|
||||
if (jsonFile.isFile())
|
||||
return NodejsRequiredSourceModule.make(rootDir, jsonFile);
|
||||
|
||||
// Skip 4. step
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* LOAD_AS_DIRECTORY(X)
|
||||
* 1. If X/package.json is a file,
|
||||
* a. Parse X/package.json, and look for "main" field.
|
||||
* b. let M = X + (json main field)
|
||||
* c. LOAD_AS_FILE(M)
|
||||
* 2. If X/index.js is a file, load X/index.js as JavaScript text. STOP
|
||||
* 3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
|
||||
* 4. If X/index.node is a file, load X/index.node as binary addon. STOP
|
||||
*
|
||||
* @param d
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private static SourceFileModule loadAsDirectory(File rootDir, File d) throws IOException {
|
||||
// 1.
|
||||
File packageJsonFile = new File(d, "package.json");
|
||||
if (packageJsonFile.isFile()) {
|
||||
// 1.a.
|
||||
String packageJsonContent = FileUtils.readFileToString(packageJsonFile);
|
||||
JSONObject packageJson = new JSONObject(packageJsonContent);
|
||||
if (packageJson.has("main")) {
|
||||
String mainFileName = packageJson.getString("main");
|
||||
|
||||
// 1.b.
|
||||
File mainFile = new File(d, mainFileName);
|
||||
|
||||
// 1.c.
|
||||
return loadAsFile(rootDir, mainFile);
|
||||
}
|
||||
}
|
||||
|
||||
// 2.
|
||||
File jsFile = new File(d, "index.js");
|
||||
if (jsFile.isFile())
|
||||
return NodejsRequiredSourceModule.make(rootDir, jsFile);
|
||||
|
||||
// 3.
|
||||
File jsonFile = new File(d, "index.json");
|
||||
if (jsonFile.isFile())
|
||||
return NodejsRequiredSourceModule.make(rootDir, jsonFile);
|
||||
|
||||
// Skip 4. step
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* LOAD_NODE_MODULES(X, START)
|
||||
* 1. let DIRS=NODE_MODULES_PATHS(START)
|
||||
* 2. for each DIR in DIRS:
|
||||
* a. LOAD_AS_FILE(DIR/X)
|
||||
* b. LOAD_AS_DIRECTORY(DIR/X)
|
||||
*
|
||||
* @param dir
|
||||
* @param target
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private static SourceFileModule loadNodeModules(File rootDir, File d, String target) throws IOException {
|
||||
List<File> dirs = nodeModulePaths(rootDir, d);
|
||||
for (File dir : dirs) {
|
||||
SourceFileModule module = loadAsFile(rootDir, new File(dir, target));
|
||||
if (module != null) return module;
|
||||
|
||||
module = loadAsDirectory(rootDir, new File(dir, target));
|
||||
if (module != null) return module;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* NODE_MODULES_PATHS(START)
|
||||
* 1. let PARTS = path split(START)
|
||||
* 2. let I = count of PARTS - 1
|
||||
* 3. let DIRS = []
|
||||
* 4. while I >= 0,
|
||||
* a. if PARTS[I] = "node_modules" CONTINUE
|
||||
* b. DIR = path join(PARTS[0 .. I] + "node_modules")
|
||||
* c. DIRS = DIRS + DIR
|
||||
* d. let I = I - 1
|
||||
* 5. return DIRS
|
||||
*
|
||||
* @param d
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private static List<File> nodeModulePaths(File rootDir, File d) throws IOException {
|
||||
LinkedList<File> dirs = new LinkedList<>();
|
||||
|
||||
while (d.getCanonicalPath().startsWith(rootDir.getCanonicalPath()) && d.toPath().getNameCount() > 0) {
|
||||
// 4.a.
|
||||
if (!d.getName().equals("node_modules")) {
|
||||
// 4.b. and 4.c.
|
||||
dirs.add(new File(d, "node_modules"));
|
||||
}
|
||||
|
||||
// 4.d.
|
||||
d = d.getParentFile();
|
||||
}
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.ibm.wala.cast.ipa.callgraph.CAstCallGraphUtil;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
|
||||
/**
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsRequiredCoreModule extends NodejsRequiredSourceModule {
|
||||
|
||||
/**
|
||||
* Core modules list for Nodejs v6.2.2
|
||||
* https://github.com/nodejs/node/blob/v6.2.2/lib/internal/module.js
|
||||
*/
|
||||
private static final Set<String> CORE_MODULES = HashSetFactory.make(Arrays.asList(
|
||||
"assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "fs", "http",
|
||||
"https", "net", "os", "path", "punycode", "querystring", "readline", "repl", "stream", "string_decoder",
|
||||
"tls", "tty", "url", "util", "v8", "vm", "zlib",
|
||||
|
||||
// Non-public files
|
||||
"timers", "constants", "freelist", "smalloc",
|
||||
"_debugger", "_http_agent", "_http_client", "_http_common", "_http_incoming", "_http_outgoing",
|
||||
"_http_server", "_linklist", "_stream_duplex", "_stream_passthrough", "_stream_readable",
|
||||
"_stream_transform", "_stream_writable", "_tls_common", "_tls_legacy", "_tls_wrap"));
|
||||
|
||||
private final static File CORE_MODULES_FOLDER = new File(NodejsRequiredCoreModule.class.getClassLoader().getResource("core-modules").getPath());
|
||||
|
||||
protected NodejsRequiredCoreModule(File f, SourceFileModule clonedFrom) throws IOException {
|
||||
super(FilenameUtils.getBaseName(f.getName()), f, clonedFrom);
|
||||
}
|
||||
|
||||
public static NodejsRequiredCoreModule make(String name) throws IOException {
|
||||
File file = new File(CORE_MODULES_FOLDER, name+".js");
|
||||
SourceFileModule sourceFileModule = CAstCallGraphUtil.makeSourceModule(file.toURI().toURL(), file.getName());
|
||||
return new NodejsRequiredCoreModule(file, sourceFileModule);
|
||||
}
|
||||
|
||||
public static boolean isCoreModule(String name) {
|
||||
return CORE_MODULES.contains(name);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2016 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Brian Pfretzschner - initial implementation
|
||||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.nodejs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.ibm.wala.cast.ipa.callgraph.CAstCallGraphUtil;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
|
||||
/**
|
||||
* This class is intended to be used whenever a JavaScript module is dynamically
|
||||
* required by JavaScript (CommonJS). The required file will be loaded and
|
||||
* wrapped in a function call to simulate the real behavior of a CommonJS
|
||||
* environment. The resulting function will be named GLOBAL_PREFIX + relative
|
||||
* file-name. To retrieve the final function name, use getFunctioName().
|
||||
*
|
||||
* @author Brian Pfretzschner <brian.pfretzschner@gmail.com>
|
||||
*/
|
||||
public class NodejsRequiredSourceModule extends SourceFileModule {
|
||||
|
||||
private final static String MODULE_WRAPPER_FILENAME = "module-wrapper.js";
|
||||
private final static String JSON_WRAPPER_FILENAME = "json-wrapper.js";
|
||||
|
||||
private final static String FILENAME_PLACEHOLDER = "/*/ WALA-INSERT-FILENAME-HERE /*/";
|
||||
private final static String DIRNAME_PLACEHOLDER = "/*/ WALA-INSERT-DIRNAME-HERE /*/";
|
||||
private final static String CODE_PLACEHOLDER = "/*/ WALA-INSERT-CODE-HERE /*/";
|
||||
|
||||
private static String MODULE_WRAPPER_SOURCE = null;
|
||||
private static String JSON_WRAPPER_SOURCE = null;
|
||||
|
||||
private final String className;
|
||||
|
||||
/**
|
||||
* @param workingDir
|
||||
* Must be a direct or indirect parent folder of file f.
|
||||
* @param f
|
||||
* Must be a file located below folder workingDir.
|
||||
* @param clonedFrom
|
||||
* @throws IOException
|
||||
*/
|
||||
protected NodejsRequiredSourceModule(String className, File f, SourceFileModule clonedFrom) throws IOException {
|
||||
super(f, clonedFrom);
|
||||
|
||||
// Generate className based on the given file name
|
||||
this.className = className;
|
||||
assert className.matches("[a-zA-Z_$][0-9a-zA-Z_$]*") : "Invalid className: " + className;
|
||||
|
||||
if (MODULE_WRAPPER_SOURCE == null || JSON_WRAPPER_SOURCE == null) {
|
||||
// Populate the cache that hold the module wrapper source code
|
||||
loadWrapperSources();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
InputStream inputStream = super.getInputStream();
|
||||
String moduleSource = null;
|
||||
try {
|
||||
moduleSource = IOUtils.toString(inputStream);
|
||||
} catch (IOException e) {
|
||||
Assertions.UNREACHABLE(e.getMessage());
|
||||
}
|
||||
|
||||
String wrapperSource = null;
|
||||
String ext = FilenameUtils.getExtension(getFile().toString()).toLowerCase();
|
||||
if (ext.equals("js")) {
|
||||
// JS file -> use module wrapper
|
||||
wrapperSource = MODULE_WRAPPER_SOURCE;
|
||||
}
|
||||
else if (ext.equals("json")) {
|
||||
// JSON file -> use JSON wrapper
|
||||
wrapperSource = JSON_WRAPPER_SOURCE;
|
||||
}
|
||||
else {
|
||||
// No clue -> try module wrapper
|
||||
System.err.println("NodejsRequiredSourceModule: Unsupported file type ("+ext+"), continue anyway.");
|
||||
wrapperSource = MODULE_WRAPPER_SOURCE;
|
||||
}
|
||||
|
||||
String wrappedModuleSource = wrapperSource
|
||||
.replace(FILENAME_PLACEHOLDER, getFile().getName())
|
||||
.replace(DIRNAME_PLACEHOLDER, getFile().getParent().toString())
|
||||
.replace(CODE_PLACEHOLDER, moduleSource);
|
||||
|
||||
return IOUtils.toInputStream(wrappedModuleSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
private void loadWrapperSources() throws IOException {
|
||||
MODULE_WRAPPER_SOURCE = loadWrapperSource(MODULE_WRAPPER_FILENAME);
|
||||
JSON_WRAPPER_SOURCE = loadWrapperSource(JSON_WRAPPER_FILENAME);
|
||||
}
|
||||
|
||||
private String loadWrapperSource(String filename) throws IOException {
|
||||
URL url = NodejsRequiredSourceModule.class.getClassLoader().getResource(filename);
|
||||
Path wrapperPath = Paths.get(url.getPath());
|
||||
byte[] wrapperSourceBin = Files.readAllBytes(wrapperPath);
|
||||
return new String(wrapperSourceBin, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a className based on the file name and path of the module file.
|
||||
* The path should be encoded in the className since the file name is not unique.
|
||||
*
|
||||
* @param rootDir
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
public static String convertFileToClassName(File rootDir, File file) {
|
||||
URI normalizedWorkingDirURI = rootDir.getAbsoluteFile().toURI().normalize();
|
||||
URI normalizedFileURI = file.getAbsoluteFile().toURI().normalize();
|
||||
String relativePath = normalizedWorkingDirURI.relativize(normalizedFileURI).getPath();
|
||||
|
||||
return FilenameUtils.removeExtension(relativePath)
|
||||
.replace("/", "_")
|
||||
.replace("-", "__")
|
||||
.replace(".", "__");
|
||||
}
|
||||
|
||||
public static NodejsRequiredSourceModule make(File rootDir, File file) throws IOException {
|
||||
String className = convertFileToClassName(rootDir, file);
|
||||
SourceFileModule sourceFileModule = CAstCallGraphUtil.makeSourceModule(file.toURI().toURL(), file.getName());
|
||||
return new NodejsRequiredSourceModule(className, file, sourceFileModule);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue