├── clj-v8 ├── test │ └── v8 │ │ ├── test.txt │ │ └── test_core.clj ├── .gitignore ├── project.clj └── src │ └── v8 │ └── core.clj ├── clj-v8-native ├── src │ └── v8wrapper │ │ ├── test_file.hamlc │ │ ├── testgrind.c │ │ ├── v8wrapper.h │ │ ├── Makefile.linux │ │ ├── Makefile.linux.32 │ │ ├── Makefile.macosx │ │ ├── test.cpp │ │ ├── v8wrapper.cc │ │ └── suppressions.txt ├── send-to-clojars.sh ├── test │ ├── test.clj │ └── clj ├── build │ └── native │ │ ├── linux │ │ ├── x86 │ │ │ ├── libv8.so.clj-v8 │ │ │ └── libv8wrapper.so │ │ └── x86_64 │ │ │ ├── libv8.so.clj-v8 │ │ │ └── libv8wrapper.so │ │ └── macosx │ │ └── x86_64 │ │ ├── libv8.dylib.clj-v8 │ │ └── libv8wrapper.dylib ├── create-jar.sh ├── pom.xml ├── local-mvn-install.sh └── build.sh ├── .gitmodules ├── .gitignore ├── clj └── README.md /clj-v8/test/v8/test.txt: -------------------------------------------------------------------------------- 1 | 123 2 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/test_file.hamlc: -------------------------------------------------------------------------------- 1 | %div 2 | .subsection -------------------------------------------------------------------------------- /clj-v8/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | native 3 | *.jar 4 | lib/ 5 | classes/ 6 | .lein* 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "clj-v8-native/src/v8"] 2 | path = clj-v8-native/src/v8 3 | url = https://github.com/v8/v8 4 | -------------------------------------------------------------------------------- /clj-v8-native/send-to-clojars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | scp -i ~/.leiningen/id_rsa pom.xml *.jar clojars@clojars.org: 6 | -------------------------------------------------------------------------------- /clj-v8-native/test/test.clj: -------------------------------------------------------------------------------- 1 | (use 'net.n01se.clojure-jna) 2 | (def jrun (jna-fn String v8wrapper/run)) 3 | 4 | 5 | (println (jrun "3/0")) 6 | 7 | -------------------------------------------------------------------------------- /clj-v8-native/build/native/linux/x86/libv8.so.clj-v8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/linux/x86/libv8.so.clj-v8 -------------------------------------------------------------------------------- /clj-v8-native/build/native/linux/x86/libv8wrapper.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/linux/x86/libv8wrapper.so -------------------------------------------------------------------------------- /clj-v8-native/build/native/linux/x86_64/libv8.so.clj-v8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/linux/x86_64/libv8.so.clj-v8 -------------------------------------------------------------------------------- /clj-v8-native/build/native/linux/x86_64/libv8wrapper.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/linux/x86_64/libv8wrapper.so -------------------------------------------------------------------------------- /clj-v8-native/build/native/macosx/x86_64/libv8.dylib.clj-v8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/macosx/x86_64/libv8.dylib.clj-v8 -------------------------------------------------------------------------------- /clj-v8-native/build/native/macosx/x86_64/libv8wrapper.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-v8/master/clj-v8-native/build/native/macosx/x86_64/libv8wrapper.dylib -------------------------------------------------------------------------------- /clj-v8-native/create-jar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=`grep '' pom.xml | sed -Ee 's#.*(.*).*#\1#'` 4 | 5 | rm -f clj-v8-native-$VERSION.jar 6 | jar -cMf clj-v8-native-$VERSION.jar -C build/ lib -C build native pom.xml 7 | -------------------------------------------------------------------------------- /clj-v8-native/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | clj-v8-native 5 | clj-v8-native 6 | 0.1.4 7 | clj-v8-native 8 | 9 | -------------------------------------------------------------------------------- /clj-v8-native/local-mvn-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=`grep '' pom.xml | sed -Ee 's#.*(.*).*#\1#'` 4 | MVNPATH=~/.m2/repository/clj-v8-native/clj-v8-native/$VERSION 5 | 6 | mkdir -p $MVNPATH 7 | rm -f $MVNPATH/* 8 | 9 | cp clj-v8-native-$VERSION.jar $MVNPATH 10 | cp pom.xml $MVNPATH/clj-v8-native-$VERSION.pom 11 | 12 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/testgrind.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "v8wrapper.h" 3 | 4 | 5 | int main(void) { 6 | wchar_t foo[] = L"function addOne(x) { return x + 1; }; addOne(3);"; 7 | 8 | //while (1) { 9 | wchar_t *result = run(foo); 10 | printf("%ls\n", result); 11 | cleanup(result); 12 | //} 13 | 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /clj-v8-native/*.jar 2 | /clj-v8-native/src/v8/ 3 | /clj-v8-native/src/v8wrapper/v8wrapper.o 4 | /clj-v8-native/src/v8wrapper/libv8wrapper.dylib 5 | /clj-v8-native/src/v8wrapper/libv8wrapper.so 6 | /clj-v8-native/src/v8wrapper/test 7 | /clj-v8-native/src/v8wrapper/test.o 8 | /clj-v8/target 9 | /clj-v8-native/src/v8wrapper/libv8.dylib.clj-v8 10 | /clj-v8-native/src/v8wrapper/libv8.so.clj-v8 11 | -------------------------------------------------------------------------------- /clj: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Clojure wrapper script. 3 | 4 | CLOJURE=$CLASSPATH:/usr/local/Cellar/clojure/1.4.0/clojure-1.4.0.jar:${PWD}:lib/jna.jar:lib/platform.jar:clojure-jna/src 5 | 6 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./build/native/macosx/x86_64:./build/native/linux/x86:./build/native/linux/x86_64 7 | export DYLD_LIBRARY_PATH=./build/native/macosx/x86_64:./build/native/linux/x86:./build/native/linux/x86_64 8 | 9 | if [ "$#" -eq 0 ]; then 10 | java -cp "$CLOJURE" clojure.main --repl 11 | else 12 | java -cp "$CLOJURE" clojure.main "$@" 13 | fi 14 | -------------------------------------------------------------------------------- /clj-v8-native/test/clj: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Clojure wrapper script. 3 | 4 | CLOJURE=$CLASSPATH:/usr/local/Cellar/clojure/1.4.0/clojure-1.4.0.jar:${PWD}:lib/jna.jar:lib/platform.jar:clojure-jna/src 5 | 6 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./build/native/macosx/x86_64:./build/native/linux/x86:./build/native/linux/x86_64 7 | export DYLD_LIBRARY_PATH=./build/native/macosx/x86_64:./build/native/linux/x86:./build/native/linux/x86_64 8 | 9 | if [ "$#" -eq 0 ]; then 10 | java -cp "$CLOJURE" clojure.main --repl 11 | else 12 | java -cp "$CLOJURE" clojure.main "$@" 13 | fi 14 | -------------------------------------------------------------------------------- /clj-v8/project.clj: -------------------------------------------------------------------------------- 1 | (defproject clj-v8 "0.1.3" 2 | :description "A Clojure wrapper for the v8 Javascript library" 3 | :dependencies [[org.clojure/clojure "1.3.0"] 4 | [org.clojars.elmom/clojure-jna "0.9"] 5 | [net.java.dev.jna/jna "3.5.1"] 6 | [clj-v8-native/clj-v8-native "0.1.3"]] 7 | :dev-dependencies [[midje "1.3.1"]] 8 | :profiles {:dev {:dependencies [[midje "1.3.1" :exclusions [org.clojure/clojure]]]}} 9 | :jvm-opts ["-Djava.library.path=target/native/macosx/x86_64:target/native/linux/x86_64:target/native/linux/x86:native/macosx/x86_64:native/linux/x86_64:native/linux/x86:"]) 10 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/v8wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef _V8WRAPPER_H 2 | #define _V8WRAPPER_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* Single object to hold values in Java-land */ 9 | struct _v8tuple; 10 | typedef struct _v8tuple v8tuple; 11 | 12 | v8tuple* create_tuple(); 13 | int cleanup_tuple(v8tuple* tuple); 14 | 15 | // compiles and executes javascript and returns the script return value as string 16 | wchar_t *run(v8tuple*, wchar_t *js); 17 | 18 | // free memory returned from the last run call 19 | int cleanup(void *lastresult); 20 | 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif // _V8WRAPPER_H 27 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/Makefile.linux: -------------------------------------------------------------------------------- 1 | V8_INC=../v8/include 2 | V8_LIB=../v8/out/x64.release/lib.target 3 | V8=libv8.so.clj-v8 4 | 5 | CC=g++ 6 | CFLAGS=-c -fPIC -I$(V8_INC) -Wall 7 | COMMON_LDFLAGS=-Wl,--rpath -Wl,. -L. $(V8) 8 | LIBRARY_LDFLAGS=-shared $(COMMON_LDFLAGS) 9 | SOURCES=v8wrapper.cc 10 | OBJECTS=$(SOURCES:.cc=.o) 11 | TARGET=libv8wrapper.so 12 | MAKEFILES=Makefile.macosx Makefile.linux Makefile.linux.32 13 | 14 | all: $(TARGET) 15 | 16 | clean: 17 | rm -f *.o *.so *.dylib test 18 | 19 | .cc.o: $(MAKEFILES) 20 | $(CC) $(CFLAGS) $< -o $@ 21 | 22 | $(V8): $(MAKEFILES) $(OBJECTS) 23 | cp $(V8_LIB)/$(V8) . 24 | 25 | $(TARGET): $(OBJECTS) $(MAKEFILES) $(V8) 26 | $(CC) -o $(TARGET) $(OBJECTS) $(LIBRARY_LDFLAGS) 27 | 28 | test: $(TARGET) test.cpp $(MAKEFILES) $(V8) 29 | $(CC) $(CFLAGS) -ggdb3 test.cpp -o test.o 30 | $(CC) -o ./test test.o $(TARGET) -ggdb3 $(COMMON_LDFLAGS) -lboost_thread-mt -lboost_system-mt 31 | 32 | 33 | check: test 34 | ./test 35 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/Makefile.linux.32: -------------------------------------------------------------------------------- 1 | V8_INC=../v8/include 2 | V8_LIB=../v8/out/ia32.release/lib.target 3 | V8=libv8.so.clj-v8 4 | 5 | CC=g++ 6 | CFLAGS=-c -m32 -fPIC -I$(V8_INC) -Wall 7 | COMMON_LDFLAGS=-Wl,--rpath -Wl,. -L. $(V8) -m32 8 | LIBRARY_LDFLAGS=-shared $(COMMON_LDFLAGS) 9 | SOURCES=v8wrapper.cc 10 | OBJECTS=$(SOURCES:.cc=.o) 11 | TARGET=libv8wrapper.so 12 | MAKEFILES=Makefile.macosx Makefile.linux Makefile.linux.32 13 | 14 | all: $(TARGET) 15 | 16 | clean: 17 | rm -f *.o *.so *.dylib test 18 | 19 | .cc.o: $(MAKEFILES) 20 | $(CC) $(CFLAGS) $< -o $@ 21 | 22 | # use $(OBJECTS) because the filename is the same on 32bit as 64bit 23 | $(V8): $(MAKEFILES) $(OBJECTS) 24 | cp $(V8_LIB)/$(V8) . 25 | 26 | $(TARGET): $(OBJECTS) $(MAKEFILES) $(V8) 27 | $(CC) -o $(TARGET) $(OBJECTS) $(LIBRARY_LDFLAGS) 28 | 29 | test: $(TARGET) test.cpp $(MAKEFILES) $(V8) 30 | $(CC) $(CFLAGS) -ggdb3 test.cpp -o test.o 31 | $(CC) -o ./test test.o $(TARGET) -ggdb3 $(COMMON_LDFLAGS) -lboost_thread-mt -lboost_system-mt 32 | 33 | 34 | check: test 35 | ./test 36 | -------------------------------------------------------------------------------- /clj-v8/test/v8/test_core.clj: -------------------------------------------------------------------------------- 1 | (ns v8.test-core 2 | (:require [v8.core :as v8]) 3 | (:use [midje.sweet])) 4 | 5 | (fact "run-script works" 6 | (v8/run-script "123") => "123") 7 | 8 | (fact "run-script works" 9 | (v8/run-script "readFile('test/v8/test.txt');") => "123\n") 10 | 11 | (fact "simple expressions" 12 | (v8/run-script "3/0") => "Infinity") 13 | 14 | (fact "complex code works" 15 | (v8/run-script "(function(){ return 5; })();") => "5") 16 | 17 | (fact "Unicode won't ☔ on my parade" 18 | (v8/run-script "\"šećiđon☔\"") => "šećiđon☔") 19 | 20 | (fact "syntax error doesnt die" 21 | (v8/run-script "sjd2-23=dfv;2-") => (throws Exception)) 22 | 23 | (fact "multuple scripts in parallel work" 24 | (pmap v8/run-script (repeat 20 "(function(){ return 5; })();")) => (repeat 20 "5")) 25 | 26 | (fact "running two scripts on same context" 27 | (let [cx (v8/create-context)] 28 | (v8/run-script-in-context cx "x = 17; y = {a: 6};") 29 | (v8/run-script-in-context cx "x;") => "17" 30 | (v8/run-script-in-context cx "y.a;") => "6")) 31 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/Makefile.macosx: -------------------------------------------------------------------------------- 1 | V8_INC=../v8/include 2 | V8_LIB=../v8/out/x64.release 3 | V8=libv8.dylib.clj-v8 4 | 5 | CC=g++ 6 | CFLAGS=-c -fPIC -I$(V8_INC) -Wall 7 | COMMON_LDFLAGS=--Wl,-macosx_version_min=10.7 -Wl,-rpath -Wl,. -L. ./$(V8) 8 | LIBRARY_LDFLAGS=-shared -dylib $(COMMON_LDFLAGS) 9 | SOURCES=v8wrapper.cc 10 | OBJECTS=$(SOURCES:.cc=.o) 11 | TARGET=libv8wrapper.dylib 12 | MAKEFILES=Makefile.macosx Makefile.linux Makefile.linux.32 13 | 14 | all: $(TARGET) 15 | 16 | clean: 17 | rm -f *.o *.so *.dylib test 18 | 19 | .cc.o: $(MAKEFILES) 20 | $(CC) $(CFLAGS) $< -o $@ 21 | 22 | $(V8): $(MAKEFILES) $(OBJECTS) 23 | cp $(V8_LIB)/libv8.so.clj-v8 $(V8) 24 | install_name_tool -id $(V8) $(V8) 25 | 26 | $(TARGET): $(OBJECTS) $(MAKEFILES) $(V8) 27 | $(CC) -o $(TARGET) $(OBJECTS) $(LIBRARY_LDFLAGS) 28 | install_name_tool -change $(V8) @loader_path/$(V8) $(TARGET) 29 | 30 | test: $(TARGET) test.cpp $(MAKEFILES) $(V8) 31 | $(CC) $(CFLAGS) -ggdb3 test.cpp -o test.o 32 | $(CC) -o ./test test.o $(TARGET) -ggdb3 $(COMMON_LDFLAGS) -lboost_thread-mt -lboost_system-mt 33 | 34 | 35 | check: test 36 | ./test 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | clj-v8 2 | ====== 3 | 4 | Clojure wrapper for the v8 JS engine 5 | 6 | 7 | Organization 8 | ----------------- 9 | 10 | clj-v8 contains the clj-v8 jar. clj-v8-native contains the native deps only. 11 | 12 | 13 | Rebuilding from scratch 14 | --------------------------- 15 | 16 | The short version: 17 | 18 | 0. Make sure you have 19 | - svn 20 | - git 21 | - a C++ toolchain (for Linux, you need a 64 bit machine with 22 | a multilib C++ compiler) 23 | 24 | 1. Unpack the tarball 25 | 26 | 2. Get into the directory which you unpacked and just do a ./build.sh 27 | It'll put all the binary artefacts into ./build 28 | You'll need to do it once on OSX and once on a 64 bit Linux box, but we keep 29 | the built files in the repository for ease of cross-platform development. 30 | 31 | 3. Get the build dirs from the two build machines into one place 32 | then call ./create-jar.sh 33 | 34 | 35 | Read build.sh to see how it all fits together. 36 | 37 | Authors 38 | -------- 39 | 40 | clj-v8 and clj-v8-wrapper was built by Paul Biggar and Ivan 41 | Stojic from CircleCi (https://circleci.com) 42 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "v8wrapper.h" 8 | #include 9 | 10 | 11 | // returns size, chars as an output parameter 12 | long read(const char* name, char** result) { 13 | FILE* file = fopen(name, "rb"); 14 | if (file == NULL) return -1; 15 | 16 | fseek(file, 0, SEEK_END); 17 | int size = ftell(file); 18 | rewind(file); 19 | 20 | char* chars = new char[size + 1]; 21 | chars[size] = '\0'; 22 | for (int i = 0; i < size;) { 23 | int read = static_cast(fread(&chars[i], 1, size - i, file)); 24 | i += read; 25 | } 26 | fclose(file); 27 | *result = chars; 28 | return size; 29 | } 30 | 31 | 32 | void run_v8() { 33 | // read file 34 | char* source8 = NULL; 35 | long size = read("test_file.js", &source8); 36 | if (source8 == NULL or size == -1) 37 | return; 38 | 39 | // convert to 16 bit chars 40 | wchar_t* source16 = (wchar_t*)(calloc(size+1, 4)); 41 | for (int i = 0; i < size; i++) { 42 | source16[i] = (wchar_t)(source8[i]); 43 | } 44 | free(source8); 45 | 46 | // get a tuple to use 47 | v8tuple* tuple = create_tuple(); 48 | 49 | // run test 50 | wchar_t* result = run(tuple, source16); 51 | free(source16); 52 | 53 | cleanup_tuple(tuple); 54 | 55 | wchar_t format_str[3] = {(wchar_t)('%'), (wchar_t)('S'), (wchar_t)('\0')}; 56 | wprintf(format_str, result); 57 | cleanup(result); 58 | 59 | } 60 | 61 | int main() 62 | { 63 | int count = 20; 64 | 65 | boost::thread ts[count]; 66 | 67 | for (int i = 0; i < count; i++) { 68 | printf("starting thread %i\n", i); 69 | ts[i] = boost::thread(run_v8); 70 | } 71 | 72 | for (int i = 0; i < count; i++) { 73 | ts[i].join(); 74 | printf("ending thread %i\n", i); 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /clj-v8-native/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function create_output_dirs { 4 | mkdir -p build/lib 5 | mkdir -p build/native/{macosx,linux} 6 | mkdir -p build/native/macosx/x86_64 7 | mkdir -p build/native/linux/{x86,x86_64} 8 | } 9 | 10 | function detect { 11 | if [ "`uname`" = "Darwin" ]; then 12 | PLATFORM=macosx 13 | 14 | elif [ "`uname`" = "Linux" ]; then 15 | PLATFORM=linux 16 | 17 | if [ "`uname -m`" != "x86_64" ]; then 18 | echo "This is a 32-bit only Linux. I'd suggest you find a 64-bit build box. Sorry." 19 | exit 20 | fi 21 | 22 | else 23 | echo "I don't know how to build/package everything on `uname` platform. Sorry" 24 | exit 25 | fi 26 | } 27 | 28 | function build_v8 { 29 | cd src/v8 30 | make dependencies 31 | 32 | if [ "$PLATFORM" = "macosx" ]; then 33 | echo Building Mac OS X 64 bit 34 | make debuggersupport=off library=shared soname_version=clj-v8 x64.release 35 | else 36 | echo Building Linux 32 bit 37 | make debuggersupport=off library=shared soname_version=clj-v8 ia32.release 38 | 39 | echo Building Linux 64 bit 40 | make debuggersupport=off library=shared soname_version=clj-v8 x64.release 41 | fi 42 | } 43 | 44 | function build_and_copy_v8w { 45 | cd src/v8wrapper 46 | 47 | if [ "$PLATFORM" = "macosx" ]; then 48 | make -f Makefile.$PLATFORM clean all 49 | 50 | echo "=== Copying v8wrapper and v8 === " 51 | cp libv8wrapper.dylib ../../build/native/macosx/x86_64 52 | cp libv8.dylib.clj-v8 ../../build/native/macosx/x86_64/ 53 | else 54 | make -f Makefile.$PLATFORM clean all 55 | echo "=== Copying v8wrapper and v8 (64bit) === " 56 | cp libv8wrapper.so ../../build/native/linux/x86_64 57 | cp libv8.so.clj-v8 ../../build/native/linux/x86_64/ 58 | 59 | make -f Makefile.$PLATFORM.32 clean all 60 | echo "=== Copying v8wrapper and v8 (32bit) === " 61 | cp libv8wrapper.so ../../build/native/linux/x86 62 | cp libv8.so.clj-v8 ../../build/native/linux/x86/ 63 | fi 64 | } 65 | 66 | echo "==== Detecting environment ====" 67 | detect 68 | 69 | echo "==== Preparing ====" 70 | create_output_dirs 71 | 72 | echo "==== Building v8 ====" 73 | (build_v8) 74 | 75 | echo "==== Building v8wrapper ====" 76 | (build_and_copy_v8w) 77 | -------------------------------------------------------------------------------- /clj-v8/src/v8/core.clj: -------------------------------------------------------------------------------- 1 | ;; See https://github.com/overtone/overtone/blob/master/src/overtone/jna_path.clj 2 | ;; This should let us read straight from jars 3 | (defonce __SET_JNA_PATH__ 4 | (System/setProperty "jna.library.path" (System/getProperty "java.library.path"))) 5 | 6 | (ns v8.core 7 | (:require [clojure.string :as str]) 8 | (:import [com.sun.jna WString Native Memory Pointer NativeLibrary])) 9 | 10 | ;; On linux, libraries are properly loaded using 11 | ;; java.library.path/jna.library.path. However, that's only for libraries loaded 12 | ;; directly from JNA. Libraries loaded by those libraries do not use 13 | ;; java.library.path, only LD_LIBRARY_PATH, which is only set at the very start 14 | ;; of the process and cannot be set in code. The solution is to load 15 | ;; libv8.clj-v8 oursevles so that it is already loaded when v8wrapper is loaded. 16 | ;; 17 | ;; In addition, while JNA allows you to specify the full name of a lib (that is, 18 | ;; containing the "lib" and the version), it only allows this when the version 19 | ;; number is all digits and dots. We can work around it with an absolute path. 20 | (when (com.sun.jna.Platform/isLinux) 21 | (doseq [path (str/split (System/getProperty "jna.library.path") #":")] 22 | (let [abs (-> (str path "/" "libv8.so.clj-v8") java.io.File. .getAbsolutePath)] 23 | (com.sun.jna.NativeLibrary/getInstance abs)))) 24 | 25 | (def LIBRARY (com.sun.jna.NativeLibrary/getInstance "v8wrapper")) 26 | 27 | 28 | 29 | (def run-fn (.getFunction LIBRARY "run")) 30 | (def create-tuple-fn (.getFunction LIBRARY "create_tuple")) 31 | (def cleanup-tuple-fn (.getFunction LIBRARY "cleanup_tuple")) 32 | 33 | (defn create-context 34 | "Creates a V8 context and associated structures" 35 | [] 36 | (.invokePointer create-tuple-fn (into-array []))) 37 | 38 | (defn run-script-in-context 39 | "Compile and run a JS script within the given context" 40 | [cx script] 41 | (let [result (.invoke run-fn Memory (object-array [cx (new WString script)])) 42 | strresult (if (nil? result) nil (.getString result 0 true))] 43 | (when (not= (. Native getLastError) 0) 44 | (if (nil? result) 45 | (throw (Exception. "V8 reported error, but message is null!")) 46 | (throw (Exception. (str "V8 error: " strresult))))) 47 | strresult)) 48 | 49 | (defn cleanup-context 50 | "Cleans the memory from a context" 51 | [cx] 52 | (.invokeVoid cleanup-tuple-fn (into-array [cx]))) 53 | 54 | (defn run-script 55 | "Compiles and runs a JS file" 56 | [script] 57 | (let [cx (create-context)] 58 | (try 59 | (run-script-in-context cx script) 60 | (finally 61 | (cleanup-context cx))))) 62 | -------------------------------------------------------------------------------- /clj-v8-native/src/v8wrapper/v8wrapper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "v8wrapper.h" 9 | 10 | using namespace v8; 11 | 12 | const char* ToCString(const String::Utf8Value& value) { 13 | return *value ? *value : ""; 14 | } 15 | 16 | void ReportException(v8::TryCatch* try_catch) { 17 | v8::HandleScope handle_scope; 18 | v8::String::Utf8Value exception(try_catch->Exception()); 19 | const char* exception_string = ToCString(exception); 20 | v8::Handle message = try_catch->Message(); 21 | if (message.IsEmpty()) { 22 | // V8 didn't provide any extra information about this error; just 23 | // print the exception. 24 | fprintf(stderr, "%s\n", exception_string); 25 | } else { 26 | // Print (filename):(line number): (message). 27 | v8::String::Utf8Value filename(message->GetScriptResourceName()); 28 | const char* filename_string = ToCString(filename); 29 | int linenum = message->GetLineNumber(); 30 | fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string); 31 | // Print line of source code. 32 | v8::String::Utf8Value sourceline(message->GetSourceLine()); 33 | const char* sourceline_string = ToCString(sourceline); 34 | fprintf(stderr, "%s\n", sourceline_string); 35 | // Print wavy underline (GetUnderline is deprecated). 36 | int start = message->GetStartColumn(); 37 | for (int i = 0; i < start; i++) { 38 | fprintf(stderr, " "); 39 | } 40 | int end = message->GetEndColumn(); 41 | for (int i = start; i < end; i++) { 42 | fprintf(stderr, "^"); 43 | } 44 | fprintf(stderr, "\n"); 45 | v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 46 | if (stack_trace.length() > 0) { 47 | const char* stack_trace_string = ToCString(stack_trace); 48 | fprintf(stderr, "%s\n", stack_trace_string); 49 | } 50 | } 51 | } 52 | 53 | Handle ReadFile(const char* name) { 54 | FILE* file = fopen(name, "rb"); 55 | if (file == NULL) return Handle(); 56 | 57 | fseek(file, 0, SEEK_END); 58 | int size = ftell(file); 59 | rewind(file); 60 | 61 | char* chars = new char[size + 1]; 62 | chars[size] = '\0'; 63 | for (int i = 0; i < size;) { 64 | int read = static_cast(fread(&chars[i], 1, size - i, file)); 65 | i += read; 66 | } 67 | fclose(file); 68 | Handle result = String::New(chars, size); 69 | delete[] chars; 70 | return result; 71 | } 72 | 73 | // The callback that is invoked by v8 whenever the JavaScript 'read' 74 | // function is called. This function loads the content of the file named in 75 | // the argument into a JavaScript string. 76 | v8::Handle Read(const v8::Arguments& args) { 77 | if (args.Length() != 1) { 78 | return v8::ThrowException(v8::String::New("Bad parameters")); 79 | } 80 | v8::String::Utf8Value file(args[0]); 81 | if (*file == NULL) { 82 | return v8::ThrowException(v8::String::New("Error loading file")); 83 | } 84 | v8::Handle source = ReadFile(*file); 85 | if (source.IsEmpty()) { 86 | return v8::ThrowException(v8::String::New("Error loading file")); 87 | } 88 | return source; 89 | } 90 | 91 | 92 | 93 | wchar_t *val2wchar(const Handle v) { 94 | String::Value ucsstr(v); 95 | 96 | int length = ucsstr.length(); 97 | wchar_t *result = (wchar_t*) calloc(sizeof(wchar_t), length+1); 98 | 99 | for(int i = 0; i < length; i++) { 100 | result[i] = (*ucsstr)[i]; 101 | } 102 | 103 | return result; 104 | } 105 | 106 | Handle wchar2v8string(wchar_t* w) { 107 | int length = wcslen(w); 108 | uint16_t *u16 = (uint16_t*) calloc(length+1, sizeof(uint16_t)); 109 | 110 | for(int i = 0; i < length; i++) { 111 | u16[i] = w[i]; 112 | } 113 | 114 | Handle result = String::New(u16); 115 | free(u16); 116 | 117 | return result; 118 | } 119 | 120 | 121 | 122 | Handle run_scoped(Handle src) { 123 | v8::HandleScope handle_scope; 124 | 125 | TryCatch trycatch; 126 | Handle result; 127 | 128 | Handle