Merge lp:~marcustomlinson/v8-cpp/v8runner into lp:v8-cpp

Proposed by Marcus Tomlinson on 2015-07-02
Status: Merged
Merged at revision: 11
Proposed branch: lp:~marcustomlinson/v8-cpp/v8runner
Merge into: lp:v8-cpp
Diff against target: 491 lines (+360/-14)
12 files modified
.bzrignore (+4/-0)
CMakeLists.txt (+5/-3)
deps/build-deps.sh (+30/-0)
src/CMakeLists.txt (+24/-3)
src/internal/require.h (+137/-0)
src/module.h (+23/-5)
src/run.h (+95/-0)
src/v8-cpp.h (+1/-0)
test/CMakeLists.txt (+7/-3)
test/addon.cpp (+1/-0)
v8runner/CMakeLists.txt (+9/-0)
v8runner/v8runner.cpp (+24/-0)
To merge this branch: bzr merge lp:~marcustomlinson/v8-cpp/v8runner
Reviewer Review Type Date Requested Status
Marcus Tomlinson Pending
Review via email: mp+263641@code.launchpad.net

Commit message

Added v8runner executable that runs a JS script and supports the "require()" keyword to load in V8 (incl. Node) modules.

To post a comment you must log in.
lp:~marcustomlinson/v8-cpp/v8runner updated on 2015-07-02
33. By Marcus Tomlinson on 2015-07-02

Retrieve dependencies on build rather than cmake

34. By Marcus Tomlinson on 2015-07-02

Oops, fixed v8cpp_script_path_

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2015-06-10 05:57:04 +0000
3+++ .bzrignore 2015-07-02 11:58:46 +0000
4@@ -1,1 +1,5 @@
5 ./CMakeLists.txt.user
6+./deps/.gclient
7+./deps/.gclient_entries
8+./deps/depot_tools
9+./deps/v8
10
11=== modified file 'CMakeLists.txt'
12--- CMakeLists.txt 2015-06-10 06:20:49 +0000
13+++ CMakeLists.txt 2015-07-02 11:58:46 +0000
14@@ -13,12 +13,14 @@
15 -fPIC
16 )
17
18-find_package(Node REQUIRED)
19-
20 include_directories(
21- ${NODE_INCLUDE_DIRS}
22 ${CMAKE_CURRENT_SOURCE_DIR}/src
23+ ${CMAKE_CURRENT_SOURCE_DIR}/deps/v8/include
24+)
25+link_directories(
26+ ${CMAKE_CURRENT_SOURCE_DIR}/deps/v8/out/native/obj.target/tools/gyp
27 )
28
29 add_subdirectory(src)
30 add_subdirectory(test)
31+add_subdirectory(v8runner)
32
33=== added directory 'deps'
34=== added file 'deps/build-deps.sh'
35--- deps/build-deps.sh 1970-01-01 00:00:00 +0000
36+++ deps/build-deps.sh 2015-07-02 11:58:46 +0000
37@@ -0,0 +1,30 @@
38+#!/bin/bash
39+
40+DIR=$(readlink -f $(dirname ${BASH_SOURCE[0]}))
41+
42+cd $DIR
43+
44+if ! [ -d "depot_tools" ]; then
45+ git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
46+fi
47+
48+if ! [ -d "v8" ]; then
49+ git clone --depth 1 --branch 3.28.73 https://chromium.googlesource.com/v8/v8.git
50+fi
51+
52+export PATH=./depot_tools:"$PATH"
53+gclient config --spec 'solutions = [
54+ { "name" : "v8",
55+ "url" : "https://chromium.googlesource.com/v8/v8.git",
56+ "deps_file" : "DEPS",
57+ "managed" : False,
58+ "custom_deps" : {
59+ },
60+ "safesync_url": "",
61+ },
62+]
63+cache_dir = None'
64+gclient sync
65+
66+cd v8
67+make -j3 native i18nsupport=off
68
69=== modified file 'src/CMakeLists.txt'
70--- src/CMakeLists.txt 2015-06-10 05:57:04 +0000
71+++ src/CMakeLists.txt 2015-07-02 11:58:46 +0000
72@@ -4,7 +4,28 @@
73 *.h
74 )
75
76-add_custom_target(
77- source_files
78- SOURCES ${SRC_FILES}
79+add_library(
80+ v8-cpp SHARED
81+ ${SRC_FILES}
82+)
83+
84+set_target_properties(
85+ v8-cpp PROPERTIES
86+ LINKER_LANGUAGE CXX
87+)
88+
89+target_link_libraries(
90+ v8-cpp
91+
92+ v8_base
93+ v8_libbase
94+ v8_snapshot
95+
96+ dl
97+ pthread
98+)
99+
100+add_custom_command(
101+ TARGET v8-cpp PRE_LINK
102+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../deps/build-deps.sh
103 )
104
105=== added file 'src/internal/require.h'
106--- src/internal/require.h 1970-01-01 00:00:00 +0000
107+++ src/internal/require.h 2015-07-02 11:58:46 +0000
108@@ -0,0 +1,137 @@
109+/*
110+ * Copyright (C) 2015 Canonical Ltd
111+ *
112+ * This program is free software: you can redistribute it and/or modify
113+ * it under the terms of the GNU Lesser General Public License version 3 as
114+ * published by the Free Software Foundation.
115+ *
116+ * This program is distributed in the hope that it will be useful,
117+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
118+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
119+ * GNU Lesser General Public License for more details.
120+ *
121+ * You should have received a copy of the GNU Lesser General Public License
122+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
123+ *
124+ * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
125+ */
126+
127+#pragma once
128+
129+#include <dlfcn.h>
130+#include <iostream>
131+
132+namespace v8cpp
133+{
134+namespace internal
135+{
136+
137+using ModuleInitFunc = void(v8::Handle<v8::Object> exports);
138+ModuleInitFunc* node_init_func_;
139+std::string v8cpp_script_path_;
140+
141+struct NodeModule
142+{
143+ int nm_version;
144+ unsigned int nm_flags;
145+ void* nm_dso_handle;
146+ const char* nm_filename;
147+ ModuleInitFunc* nm_register_func;
148+ //...
149+};
150+
151+extern "C" void node_module_register(void* m)
152+{
153+ auto mp = static_cast<NodeModule*>(m);
154+
155+ // For now we only know that version 14 works here
156+ if (mp->nm_version == 14)
157+ {
158+ node_init_func_ = mp->nm_register_func;
159+ }
160+ else
161+ {
162+ std::cerr << "node_module_register(): ignoring node module. nm_version " << mp->nm_version << " not supported"
163+ << std::endl;
164+ }
165+}
166+
167+class Console
168+{
169+public:
170+ inline static void log(v8::FunctionCallbackInfo<v8::Value> const& args)
171+ {
172+ for (int i = 0; i < args.Length(); ++i)
173+ {
174+ if (args[i]->IsInt32())
175+ {
176+ std::cout << v8cpp::from_v8<int>(v8::Isolate::GetCurrent(), args[i]) << std::endl;
177+ }
178+ if (args[i]->IsUint32())
179+ {
180+ std::cout << v8cpp::from_v8<unsigned int>(v8::Isolate::GetCurrent(), args[i]) << std::endl;
181+ }
182+ else if (args[i]->IsNumber())
183+ {
184+ std::cout << v8cpp::from_v8<float>(v8::Isolate::GetCurrent(), args[i]) << std::endl;
185+ }
186+ else if (args[i]->IsBoolean())
187+ {
188+ std::cout << v8cpp::from_v8<bool>(v8::Isolate::GetCurrent(), args[i]) << std::endl;
189+ }
190+ else if (args[i]->IsString())
191+ {
192+ std::cout << v8cpp::from_v8<std::string>(v8::Isolate::GetCurrent(), args[i]) << std::endl;
193+ }
194+ }
195+ std::cout.flush();
196+ }
197+};
198+
199+inline v8::Local<v8::Object> require(std::string const& module_path)
200+{
201+ node_init_func_ = nullptr;
202+ v8::Local<v8::Object> exports = v8::Object::New(v8::Isolate::GetCurrent());
203+
204+ // Try append ".node" to module_path
205+ std::string suffixed_module_path = v8cpp_script_path_ + module_path + ".node";
206+ auto module = dlopen(suffixed_module_path.c_str(), RTLD_LAZY);
207+ if (!module)
208+ {
209+ // Didn't work, now try append ".so" to module_path
210+ suffixed_module_path = v8cpp_script_path_ + module_path + ".so";
211+ module = dlopen(suffixed_module_path.c_str(), RTLD_LAZY);
212+ if (!module)
213+ {
214+ // Still didn't work, just try module_path as is
215+ suffixed_module_path = v8cpp_script_path_ + module_path;
216+ module = dlopen(suffixed_module_path.c_str(), RTLD_LAZY);
217+ if (!module)
218+ {
219+ std::cerr << "dlopen failed: " << dlerror() << std::endl;
220+ return exports;
221+ }
222+ }
223+ }
224+
225+ if (node_init_func_)
226+ {
227+ node_init_func_(exports);
228+ }
229+ else
230+ {
231+ auto v8cpp_init_func = (ModuleInitFunc*)dlsym(module, "init_module");
232+ if (!v8cpp_init_func)
233+ {
234+ std::cerr << "dlsym failed: " << dlerror() << std::endl;
235+ return exports;
236+ }
237+
238+ v8cpp_init_func(exports);
239+ }
240+
241+ return exports;
242+}
243+
244+} // namespace internal
245+} // namespace v8cpp
246
247=== modified file 'src/module.h'
248--- src/module.h 2015-06-11 05:51:30 +0000
249+++ src/module.h 2015-07-02 11:58:46 +0000
250@@ -32,7 +32,7 @@
251 public:
252 explicit Module(v8::Isolate* isolate)
253 : isolate_(isolate)
254- , object_(v8::ObjectTemplate::New(isolate))
255+ , object_template_(v8::ObjectTemplate::New(isolate))
256 {
257 }
258
259@@ -40,7 +40,7 @@
260 template <typename T>
261 Module& add_class(char const* name, Class<T>& cl)
262 {
263- object_->Set(to_v8(isolate_, name), cl.class_.proto_template()->GetFunction());
264+ object_template_->Set(to_v8(isolate_, name), cl.class_.proto_template()->GetFunction());
265 return *this;
266 }
267
268@@ -48,19 +48,37 @@
269 template <typename F>
270 Module& add_function(char const* name, F func)
271 {
272- object_->Set(to_v8(isolate_, name), internal::export_function(isolate_, func));
273+ object_template_->Set(to_v8(isolate_, name), internal::export_function(isolate_, func));
274 return *this;
275 }
276
277 // Create an instance of this module in V8
278 v8::Local<v8::Object> create_prototype()
279 {
280- return object_->NewInstance();
281+ return object_template_->NewInstance();
282+ }
283+
284+ v8::Handle<v8::ObjectTemplate> object_template()
285+ {
286+ return object_template_;
287 }
288
289 private:
290 v8::Isolate* isolate_;
291- v8::Handle<v8::ObjectTemplate> object_;
292+ v8::Handle<v8::ObjectTemplate> object_template_;
293 };
294
295+// Create a V8-cpp module by exposing it's init function
296+#define V8CPP_MODULE(module_name, init_func)\
297+extern "C"\
298+{\
299+ namespace v8cpp\
300+ {\
301+ void init_module(Handle<Object> exports)\
302+ {\
303+ init_func(exports);\
304+ }\
305+ }\
306+}
307+
308 } // namespace v8cpp
309
310=== added file 'src/run.h'
311--- src/run.h 1970-01-01 00:00:00 +0000
312+++ src/run.h 2015-07-02 11:58:46 +0000
313@@ -0,0 +1,95 @@
314+/*
315+ * Copyright (C) 2015 Canonical Ltd
316+ *
317+ * This program is free software: you can redistribute it and/or modify
318+ * it under the terms of the GNU Lesser General Public License version 3 as
319+ * published by the Free Software Foundation.
320+ *
321+ * This program is distributed in the hope that it will be useful,
322+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
323+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
324+ * GNU Lesser General Public License for more details.
325+ *
326+ * You should have received a copy of the GNU Lesser General Public License
327+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
328+ *
329+ * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
330+ */
331+
332+#pragma once
333+
334+#include <internal/require.h>
335+
336+#include <fstream>
337+
338+namespace v8cpp
339+{
340+
341+template <typename T = v8::Handle<v8::Value>>
342+T run_script(v8::Isolate* isolate, std::string const& source, std::string const& filename = "")
343+{
344+ // Create an isolate scope.
345+ v8::Isolate::Scope isolate_scope(isolate);
346+
347+ // Create a stack-allocated handle scope.
348+ v8::HandleScope handle_scope(isolate);
349+
350+ // Prepare console class
351+ v8cpp::Class<internal::Console> console(isolate);
352+ console
353+ .set_constructor()
354+ .add_method("log", &internal::Console::log);
355+
356+ // Create a new context that supports "require()" and "console".
357+ v8cpp::Module module(isolate);
358+ {
359+ v8::Context::Scope context_scope(v8::Context::New(isolate));
360+
361+ // Store the script filename for use in require() later
362+ internal::v8cpp_script_path_.clear();
363+ std::size_t found = filename.find_last_of("/");
364+ if (found != std::string::npos)
365+ {
366+ internal::v8cpp_script_path_ = filename.substr(0, found) + "/";
367+ }
368+
369+ module.add_function("require", &internal::require);
370+ module.add_class("console", console);
371+ }
372+ v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, module.object_template());
373+
374+ // Enter the context for compiling and running the script.
375+ v8::Context::Scope context_scope(context);
376+
377+ // Compile the script.
378+ v8::Local<v8::Script> script = v8::Script::Compile(v8cpp::to_v8(isolate, source), v8cpp::to_v8(isolate, filename));
379+
380+ // Run the script.
381+ if (script.IsEmpty())
382+ {
383+ if (!filename.empty())
384+ {
385+ throw std::runtime_error("run_script(): Failed to compile script file: \"" + filename + "\"");
386+ }
387+ else
388+ {
389+ throw std::runtime_error("run_script(): Failed to compile script.");
390+ }
391+ }
392+
393+ return v8cpp::from_v8<T>(isolate, script->Run());
394+}
395+
396+template <typename T = v8::Handle<v8::Value>>
397+T run_script_file(v8::Isolate* isolate, std::string const& filename)
398+{
399+ std::ifstream stream(filename.c_str());
400+ if (!stream)
401+ {
402+ throw std::runtime_error("run_script_file(): Failed to locate script file: \"" + filename + "\"");
403+ }
404+
405+ std::istreambuf_iterator<char> begin(stream), end;
406+ return run_script<T>(isolate, std::string(begin, end), filename);
407+}
408+}
409
410=== modified file 'src/v8-cpp.h'
411--- src/v8-cpp.h 2015-06-10 05:57:04 +0000
412+++ src/v8-cpp.h 2015-07-02 11:58:46 +0000
413@@ -20,3 +20,4 @@
414 #include <class.h>
415 #include <convert.h>
416 #include <module.h>
417+#include <run.h>
418
419=== modified file 'test/CMakeLists.txt'
420--- test/CMakeLists.txt 2015-06-10 05:57:04 +0000
421+++ test/CMakeLists.txt 2015-07-02 11:58:46 +0000
422@@ -1,9 +1,13 @@
423+find_package(Node REQUIRED)
424+
425+include_directories(
426+ ${NODE_INCLUDE_DIRS}
427+)
428+
429 file(
430 GLOB
431 TEST_FILES
432- *.h
433- *.cpp
434- *.js
435+ *.h *.cpp *.js
436 )
437
438 configure_file(test.js test.js)
439
440=== modified file 'test/addon.cpp'
441--- test/addon.cpp 2015-06-22 16:40:30 +0000
442+++ test/addon.cpp 2015-07-02 11:58:46 +0000
443@@ -119,3 +119,4 @@
444 }
445
446 NODE_MODULE(addon, InitAll)
447+V8CPP_MODULE(addon, InitAll)
448
449=== added directory 'v8runner'
450=== added file 'v8runner/CMakeLists.txt'
451--- v8runner/CMakeLists.txt 1970-01-01 00:00:00 +0000
452+++ v8runner/CMakeLists.txt 2015-07-02 11:58:46 +0000
453@@ -0,0 +1,9 @@
454+add_executable(
455+ v8runner
456+ v8runner.cpp
457+)
458+
459+target_link_libraries(
460+ v8runner
461+ v8-cpp
462+)
463
464=== added file 'v8runner/v8runner.cpp'
465--- v8runner/v8runner.cpp 1970-01-01 00:00:00 +0000
466+++ v8runner/v8runner.cpp 2015-07-02 11:58:46 +0000
467@@ -0,0 +1,24 @@
468+#include <v8-cpp.h>
469+
470+using namespace v8;
471+
472+int main(int argc, char* argv[])
473+{
474+ // Check args.
475+ if (argc != 2)
476+ {
477+ std::cerr << "usage: v8runner script.js" << std::endl;
478+ return -1;
479+ }
480+
481+ // Create a new isolate to run our script in.
482+ Isolate* isolate = Isolate::New();
483+
484+ // Run script.
485+ v8cpp::run_script_file(isolate, argv[1]);
486+
487+ // Clean up.
488+ isolate->Dispose();
489+
490+ return 0;
491+}

Subscribers

People subscribed via source and target branches

to all changes: