Skip to content

Commit

Permalink
Add DependencyExtractor skeleton.
Browse files Browse the repository at this point in the history
Summary:
Add the DependencyExtractor class, which takes an AST and extracts
the dependencies using RecursiveVisitor.

It currently does nothing.

Create a CLI for it to be able to test it.

Reviewed By: tmikov

Differential Revision: D21091835

fbshipit-source-id: a7ac6caa021ef610000acd486976ad6252b852b7
  • Loading branch information
avp authored and facebook-github-bot committed Jun 25, 2020
1 parent 1f798b0 commit 319414b
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ list(APPEND HERMES_TEST_DEPS
hbc-attribute
hbc-deltaprep
hbc-diff
dependency-extractor
)

set(HERMES_LIT_TEST_PARAMS
Expand All @@ -590,6 +591,7 @@ set(HERMES_LIT_TEST_PARAMS
jit_enabled=${HERMESVM_JIT}
jit_disassembler_enabled=${HERMESVM_JIT_DISASSEMBLER}
hbc_deltaprep=${HERMES_TOOLS_OUTPUT_DIR}/hbc-deltaprep
dependency_extractor=${HERMES_TOOLS_OUTPUT_DIR}/dependency-extractor
FileCheck=${HERMES_TOOLS_OUTPUT_DIR}//FileCheck
hermes=${HERMES_TOOLS_OUTPUT_DIR}/hermes
hermesc=${HERMES_TOOLS_OUTPUT_DIR}/hermesc
Expand Down
74 changes: 74 additions & 0 deletions include/hermes/DependencyExtractor/DependencyExtractor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#ifndef HERMES_AST_DEPENDENCYEXTRACTOR_H
#define HERMES_AST_DEPENDENCYEXTRACTOR_H

#include "hermes/AST/Context.h"
#include "hermes/AST/ESTree.h"

namespace hermes {

/// The kind of dependency, used to distinguish between the different ways to
/// pull an external module into the file.
enum class DependencyKind {
// import {x} from 'source';
ESM,
// import type {x} from 'source';
Type,
// require('source');
Require,
// import('source');
Async,
// JSResource('source');
Resource,
// PrefetchedJSResource('source');
PrefetchedResource,
// graphql`fragment source` and others;
GraphQL,
};

inline const char *dependencyKindStr(DependencyKind kind) {
switch (kind) {
case DependencyKind::ESM:
return "ESM";
case DependencyKind::Type:
return "Type";
case DependencyKind::Require:
return "Require";
case DependencyKind::Async:
return "Async";
case DependencyKind::Resource:
return "Resource";
case DependencyKind::PrefetchedResource:
return "PrefetchedResource";
case DependencyKind::GraphQL:
return "GraphQL";
}
}

/// Represents a single dependency from a source file.
struct Dependency {
/// The target file name of the dependency.
/// For example, `require('name')` is used to indicate a Require dependency on
/// 'name'.
std::string name;

/// The kind of the dependency.
DependencyKind kind;
};

/// Extract any dependencies in \p node and its children.
/// \return a list of Dependency indicating what the target is, as well as the
/// kind of dependency.
std::vector<Dependency> extractDependencies(
Context &astContext,
ESTree::Node *node);

} // namespace hermes

#endif
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_subdirectory(VM)
add_subdirectory(Inst)
add_subdirectory(FrontEndDefs)
add_subdirectory(CompilerDriver)
add_subdirectory(DependencyExtractor)
add_subdirectory(ADT)
add_subdirectory(AST)
add_subdirectory(FlowParser)
Expand Down
15 changes: 15 additions & 0 deletions lib/DependencyExtractor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

set(source_files
DependencyExtractor.cpp
)

add_hermes_library(hermesDependencyExtractor STATIC ${source_files}
LINK_LIBS
hermesAST
hermesParser
hermesSupport
)
52 changes: 52 additions & 0 deletions lib/DependencyExtractor/DependencyExtractor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "hermes/DependencyExtractor/DependencyExtractor.h"

#include "hermes/AST/RecursiveVisitor.h"

namespace hermes {

using namespace hermes::ESTree;

/// Visitor for extracting dependencies from a given node.
/// Reports errors on encountering nodes which should introduce dependencies
/// but are malformed.
class DependencyExtractor {
/// Storage for the extracted dependencies.
std::vector<Dependency> deps_{};

/// SourceErrorManager for reporting errors, e.g. for invalid require() calls.
SourceErrorManager &sm_;

public:
DependencyExtractor(Context &astContext)
: sm_(astContext.getSourceErrorManager()) {}

std::vector<Dependency> &getDeps() {
return deps_;
}

/// Perform the extraction on whole AST.
void doIt(Node *rootNode) {
visitESTreeNode(*this, rootNode);
}

/// Stub which catches all nodes we don't need to directly extract
/// dependencies from.
void visit(Node *node) {
visitESTreeChildren(*this, node);
}
};

std::vector<Dependency> extractDependencies(Context &astContext, Node *node) {
DependencyExtractor extract{astContext};
extract.doIt(node);
return std::move(extract.getDeps());
}

} // namespace hermes
1 change: 1 addition & 0 deletions test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ config.substitutions.append(("%hdb", lit_config.params["hdb"].replace('\\', '/')
config.substitutions.append(("%hbcdump", lit_config.params["hbcdump"].replace('\\', '/')))
config.substitutions.append(("%hbc-deltaprep", lit_config.params["hbc_deltaprep"].replace('\\', '/')))
config.substitutions.append(("%hbc-diff", lit_config.params["hbc_diff"].replace('\\', '/')))
config.substitutions.append(("%dependency-extractor", lit_config.params["dependency_extractor"].replace('\\', '/')))
1 change: 1 addition & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_subdirectory(hbc-attribute)
add_subdirectory(jsi)
add_subdirectory(emhermesc)
add_subdirectory(fuzzers)
add_subdirectory(dependency-extractor)

if (HERMESVM_API_TRACE)
add_subdirectory(synth)
Expand Down
17 changes: 17 additions & 0 deletions tools/dependency-extractor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

set(HERMES_LINK_COMPONENTS LLVHSupport)

add_hermes_tool(dependency-extractor
dependency-extractor.cpp
${ALL_HEADER_FILES}
)

target_link_libraries(dependency-extractor
hermesAST
hermesDependencyExtractor
hermesParser
)
70 changes: 70 additions & 0 deletions tools/dependency-extractor/dependency-extractor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "hermes/DependencyExtractor/DependencyExtractor.h"
#include "hermes/Parser/JSParser.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"

static llvm::cl::opt<std::string> InputFilename(
llvm::cl::desc("input file"),
llvm::cl::Positional);

using namespace hermes;

int main(int argc, char **argv) {
// Normalize the arg vector.
llvm::InitLLVM initLLVM(argc, argv);
llvm::sys::PrintStackTraceOnErrorSignal("dependency-extractor");
llvm::PrettyStackTraceProgram X(argc, argv);
llvm::llvm_shutdown_obj Y;
llvm::cl::ParseCommandLineOptions(
argc, argv, "Hermes JS dependency extractor\n");

llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBufOrErr =
llvm::MemoryBuffer::getFile(InputFilename);

if (!fileBufOrErr) {
llvm::errs() << "Error: fail to open file: " << InputFilename << ": "
<< fileBufOrErr.getError().message() << "\n";
return -1;
}

auto fileBuf = std::move(fileBufOrErr.get());

auto context = std::make_shared<Context>();
#if HERMES_PARSE_JSX
context->setParseJSX(true);
#endif
#if HERMES_PARSE_FLOW
context->setParseFlow(true);
#endif

int fileBufId =
context->getSourceErrorManager().addNewSourceBuffer(std::move(fileBuf));
auto mode = parser::FullParse;

parser::JSParser jsParser(*context, fileBufId, mode);
llvm::Optional<ESTree::ProgramNode *> parsedJs = jsParser.parse();

if (!parsedJs)
return -1;

auto deps = extractDependencies(*context, *parsedJs);
for (const auto &dep : deps) {
llvm::outs() << dependencyKindStr(dep.kind) << " | " << dep.name << '\n';
}

return 0;
}

0 comments on commit 319414b

Please sign in to comment.