forked from facebook/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
emdependency-extractor.cpp
157 lines (131 loc) · 4.39 KB
/
emdependency-extractor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* This is a driver of the dependency extractor intended to be compiled to
* WebAssembly with Emscripten and invoked from JavaScript.
*
* When configuring CMake, don't specify CMAKE_EXE_LINKER_FLAGS, because the
* correct flags are already set for this target.
*
* DependencyExtractor.js is a module exposing the compiler interface to JS.
*/
#include "hermes/AST/Context.h"
#include "hermes/DependencyExtractor/DependencyExtractor.h"
#include "hermes/Parser/JSParser.h"
#include "hermes/Support/Algorithms.h"
#include "hermes/Support/JSONEmitter.h"
#include "llvh/ADT/StringRef.h"
#include "llvh/Support/MemoryBuffer.h"
#include "llvh/Support/raw_ostream.h"
#include <string>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#endif
using namespace hermes;
// Forward declaration.
class Dependencies;
extern "C" {
/// Parse the supplied source and return a Dependencies, which is an opaque
/// structure containing the extracted dependencies or an error message. The
/// result must be freed with \c hermesDependencies_free().
///
/// \param source utf-8 encoded input string. It must be zero terminated.
/// \param sourceSize the length of \c source in bytes, including the
/// terminating zero.
/// \return a new instance of Dependencies.
Dependencies *hermesExtractDependencies(const char *source, size_t sourceSize);
/// Free the Dependencies allocated by \c hermesExtractDependencies().
void hermesDependencies_free(Dependencies *res);
/// \return nullptr if compilation was successful, the error string otherwise.
const char *hermesDependencies_getError(const Dependencies *res);
/// \return a JSON representation of the dependencies as an array of objects,
/// each with a 'name' and 'kind' field.
/// [
/// {
/// "name": "foo.js",
/// "kind": "Type",
/// },
/// {
/// "name": "bar.js",
/// "kind": "Require",
/// },
/// ]
const char *hermesDependencies_getDeps(const Dependencies *res);
} // extern "C"
/// An opaque object containing the result of an extraction.
class Dependencies {
public:
std::string error_;
std::string deps_;
};
EMSCRIPTEN_KEEPALIVE
extern "C" Dependencies *hermesExtractDependencies(
const char *source,
size_t sourceSize) {
std::unique_ptr<Dependencies> result = std::make_unique<Dependencies>();
auto context = std::make_shared<Context>();
#if HERMES_PARSE_JSX
context->setParseJSX(true);
#endif
#if HERMES_PARSE_FLOW
context->setParseFlow(ParseFlowSetting::ALL);
#endif
if (source[sourceSize - 1] != 0) {
result->error_ = "Input source must be zero-terminated";
return result.release();
}
auto fileBuf =
llvh::MemoryBuffer::getMemBuffer(llvh::StringRef{source, sourceSize - 1});
int fileBufId =
context->getSourceErrorManager().addNewSourceBuffer(std::move(fileBuf));
auto mode = parser::FullParse;
parser::JSParser jsParser(*context, fileBufId, mode);
llvh::Optional<ESTree::ProgramNode *> parsedJs = jsParser.parse();
if (!parsedJs) {
result->error_ = "Failed to parse JS source";
return result.release();
}
auto deps = extractDependencies(*context, *parsedJs);
llvh::raw_string_ostream os{result->deps_};
JSONEmitter json{os, false};
json.openArray();
for (const Dependency &dep : deps) {
json.openDict();
json.emitKeyValue("name", dep.name);
json.emitKeyValue("kind", dependencyKindStr(dep.kind));
json.closeDict();
}
json.closeArray();
return result.release();
}
EMSCRIPTEN_KEEPALIVE
extern "C" void hermesDependencies_free(Dependencies *res) {
delete res;
}
EMSCRIPTEN_KEEPALIVE
extern "C" const char *hermesDependencies_getError(const Dependencies *res) {
if (!res || res->error_.empty())
return nullptr;
return res->error_.c_str();
}
EMSCRIPTEN_KEEPALIVE
extern "C" const char *hermesDependencies_getDeps(const Dependencies *res) {
if (!res || !res->error_.empty())
return nullptr;
return res->deps_.c_str();
}
// This is just a dummy main routine to exercise the code. It won't actually
// be called by JS.
int main() {
static const char src1[] = "require('foo');";
auto *res1 = hermesExtractDependencies(src1, sizeof(src1));
assert(!hermesDependencies_getError(res1) && "success expected");
hermesDependencies_free(res1);
return 0;
}