From 76bc2d1ed7f13fb329c33f48756ea3c24c59a6ea Mon Sep 17 00:00:00 2001 From: yugui Date: Thu, 17 May 2012 02:48:59 +0000 Subject: Imports Ruby's port to NativeClient (a.k.a NaCl). Patch by Google Inc. [ruby-core:45073]. * configure.in (RUBY_NACL): New M4 func to configure variables for NaCl. (RUBY_NACL_CHECK_PEPPER_TYPES): New M4 func to check the old names of Pepper interface types. (BTESTRUBY): New variable to specify which ruby should be run on "make btest". NaCl can run the built binary by sel_ldr, but it need rbconfig.rb. So this variable is distinguished from $MINIRUBY. * thread_pthread.c: Disabled some features on NaCl. * io.c: ditto. * process.c: ditto. * signal.c: ditto. * file.c: ditto. * missing/flock.c: ditto. * nacl/pepper_main.c: An example implementation of Pepper application that embeds Ruby. * nacl/example.html: An example of web page that uses the Pepper application. * nacl/nacl-config.rb: Detects variants of NaCl SDK. * nacl/GNUmakefile.in: Makefile template for NaCl specific build process. * nacl/package.rb: script for packaging a NaCl-Ruby embedding application. * nacl/reate_nmf.rb: Wrapper script of create_nmf.py * dln.c (dln_load): Added a hack to call on NaCl. * util.c (ruby_getcwd): Path to the current directort is not available on NaCl. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35672 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 49 ++- Makefile.in | 2 + common.mk | 2 +- configure.in | 130 +++++++- dir.c | 4 + dln.c | 15 + ext/Setup.nacl | 50 +++ ext/extmk.rb | 2 +- ext/pty/extconf.rb | 2 +- file.c | 22 ++ gc.c | 6 + gc.h | 4 +- io.c | 30 +- iseq.c | 1 - missing/flock.c | 3 +- nacl/GNUmakefile.in | 60 ++++ nacl/README.nacl | 34 ++ nacl/create_nmf.rb | 70 ++++ nacl/dirent.h | 15 + nacl/example.html | 150 +++++++++ nacl/ioctl.h | 7 + nacl/nacl-config.rb | 61 ++++ nacl/package.rb | 109 +++++++ nacl/pepper_main.c | 924 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nacl/resource.h | 8 + nacl/select.h | 7 + nacl/signal.h | 6 + nacl/stat.h | 10 + nacl/unistd.h | 9 + nacl/utime.h | 11 + process.c | 37 ++- signal.c | 7 +- thread.c | 10 +- thread_pthread.c | 19 ++ util.c | 10 + vm_core.h | 4 + win32/Makefile.sub | 1 + 37 files changed, 1855 insertions(+), 36 deletions(-) create mode 100644 ext/Setup.nacl create mode 100644 nacl/GNUmakefile.in create mode 100644 nacl/README.nacl create mode 100644 nacl/create_nmf.rb create mode 100644 nacl/dirent.h create mode 100644 nacl/example.html create mode 100644 nacl/ioctl.h create mode 100644 nacl/nacl-config.rb create mode 100644 nacl/package.rb create mode 100644 nacl/pepper_main.c create mode 100644 nacl/resource.h create mode 100644 nacl/select.h create mode 100644 nacl/signal.h create mode 100644 nacl/stat.h create mode 100644 nacl/unistd.h create mode 100644 nacl/utime.h diff --git a/ChangeLog b/ChangeLog index 1d4a15573f..554db48721 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,53 @@ +Thu May 17 11:33:07 2012 Yuki Yugui Sonoda + + Imports Ruby's port to NativeClient (a.k.a NaCl). + Patch by Google Inc. [ruby-core:45073]. + + * configure.in (RUBY_NACL): New M4 func to configure variables for + NaCl. + (RUBY_NACL_CHECK_PEPPER_TYPES): New M4 func to check the old names + of Pepper interface types. + (BTESTRUBY): New variable to specify which ruby should be run on + "make btest". NaCl can run the built binary by sel_ldr, but it need + rbconfig.rb. So this variable is distinguished from $MINIRUBY. + + * thread_pthread.c: Disabled some features on NaCl. + + * io.c: ditto. + + * process.c: ditto. + + * signal.c: ditto. + + * file.c: ditto. + + * missing/flock.c: ditto. + + * nacl/pepper_main.c: An example implementation of Pepper application + that embeds Ruby. + + * nacl/example.html: An example of web page that uses the Pepper + application. + + * nacl/nacl-config.rb: Detects variants of NaCl SDK. + + * nacl/GNUmakefile.in: Makefile template for NaCl specific build + process. + + * nacl/package.rb: script for packaging a NaCl-Ruby embedding + application. + + * nacl/reate_nmf.rb: Wrapper script of create_nmf.py + + * dln.c (dln_load): Added a hack to call on NaCl. + + * util.c (ruby_getcwd): Path to the current directort is not available + on NaCl. + Thu May 17 10:54:58 2012 Nobuyoshi Nakada - * ext/tk/extconf.rb: add -l options to $libs not $LDFLAGS, to be + * ext/tk/extconf.rb: add -l options to $libs not $LDFLAGS, + to be passed to EXTLIBS in exts.mk. * enc/encinit.c.erb: use %-lines to adjust indent in the generated file. diff --git a/Makefile.in b/Makefile.in index 721dc2ae03..7b86d8c3ec 100644 --- a/Makefile.in +++ b/Makefile.in @@ -90,6 +90,8 @@ MINIRUBY = @MINIRUBY@\ $(MINIRUBYOPT) RUNRUBY = @RUNRUBY@ $(RUNRUBYOPT) -- $(RUN_OPTS) XRUBY = @XRUBY@ +BTESTRUBY = @BTESTRUBY@\ + $(MINIRUBYOPT) INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ diff --git a/common.mk b/common.mk index 28813291ca..4b96921475 100644 --- a/common.mk +++ b/common.mk @@ -472,7 +472,7 @@ check-ruby: test test-ruby btest: $(TEST_RUNNABLE)-btest no-btest: PHONY yes-btest: miniruby$(EXEEXT) PHONY - $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(MINIRUBY)" $(OPTS) + $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY)" $(OPTS) btest-ruby: $(TEST_RUNNABLE)-btest-ruby no-btest-ruby: PHONY diff --git a/configure.in b/configure.in index a398b5ebd7..24e650ba8d 100644 --- a/configure.in +++ b/configure.in @@ -51,6 +51,79 @@ target_cpu=x64 ]) ]) +AC_DEFUN([RUBY_NACL], +[ + AS_CASE(["${host_os}"], +[nacl], [ + ac_cv_exeext=.nexe + host_vendor=chromium + ac_cv_host=chromium + AC_MSG_CHECKING([wheather \$NACL_SDK_ROOT is set]) + if test x"${NACL_SDK_ROOT}" = x; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([You need to set \$NACL_SDK_ROOT environment variable to build for NativeClient]) + fi + AC_MSG_RESULT([yes]) + + nacl_cv_build_variant=glibc + AC_ARG_WITH(newlib, + AS_HELP_STRING([--with-newlib], [uses newlib version of NativeClient SDK]), + [AS_CASE([$withval], + [no], [nacl_cv_build_variant=glibc], + [yes], [nacl_cv_build_variant=newlib])]) + + AS_CASE(["$build_cpu"], + [x86_64|i?86], [nacl_cv_cpu_nick=x86], [nacl_cv_cpu_nick=$build_cpu]) + AS_CASE(["$build_os"], + [linux*], [nacl_cv_os_nick=linux], + [darwin*], [nacl_cv_os_nick=mac], + [cygwin*|mingw*], [nacl_cv_os_nick=win], + [nacl_cv_os_nick=$build_os]) + + host="$host_cpu-chromium-$host_os-" + ac_tool_prefix="$host_cpu-nacl-" + + AC_MSG_CHECKING([NativeClient toolchain]) + if test -d \ + "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_${nacl_cv_build_variant}"; then + NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_${nacl_cv_build_variant}" + else + AS_CASE( + ["${nacl_cv_build_variant}"], + [glibc], [if test \ + -d "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_newlib" \ + -a -d "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}"; then + NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}" + fi], + [newlib], [ NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}" ]) + fi + if test ! -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/${ac_tool_prefix}gcc"; then + if test "${build_cpu}" = i686 -a -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/nacl-gcc"; then + ac_tool_prefix=nacl- + fi + if test "${build_cpu}" = x86_64 -a -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/nacl-gcc"; then + ac_tool_prefix=nacl64- + fi + fi + if test -z "${NACL_TOOLCHAIN}"; then + AC_MSG_ERROR([Unrecognized --host and --build combination or NaCl SDK is not installed]) + fi + PATH="${PATH}:${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/bin" + AC_MSG_RESULT(${NACL_TOOLCHAIN}) + + AC_SUBST(NACL_TOOLCHAIN) + AC_SUBST(NACL_SDK_ROOT) + AC_SUBST(NACL_SDK_VARIANT, nacl_cv_build_variant) +])]) + +AC_DEFUN([RUBY_NACL_CHECK_PEPPER_TYPES], +[ + AC_CHECK_TYPES([struct PPB_Core, struct PPB_Messaging, struct PPB_Var, + struct PPB_URLLoader, struct PPB_URLRequestInfo, + struct PPB_URLResponseInfo, struct PPB_FileRef, + struct PPP_Instance]) +]) + AC_DEFUN([RUBY_CPPOUTFILE], [AC_CACHE_CHECK(whether ${CPP} accepts -o, rb_cv_cppoutfile, [cppflags=$CPPFLAGS @@ -283,6 +356,7 @@ if test -z "${CXXFLAGS+set}"; then cxxflags="$cxxflags "'${optflags} ${debugflags} ${warnflags}' fi +RUBY_NACL if test x"${build}" != x"${host}"; then AC_CHECK_TOOL(CC, gcc) fi @@ -377,6 +451,7 @@ AC_SUBST(MAKEDIRS) AC_CHECK_PROGS(DOT, dot) AC_CHECK_PROGS(DOXYGEN, doxygen) +AS_CASE(["${host_os}"], [nacl], [AC_PATH_PROG(PYTHON, python)]) AC_CHECK_PROG(PKG_CONFIG, pkg-config, [pkg-config], [], [], [`"$as_dir/$ac_word$ac_exec_ext" --print-errors --version > /dev/null 2>&1 || echo "$as_dir/$ac_word$ac_exec_ext"`]) @@ -509,7 +584,7 @@ if test "$GCC" = yes; then # -fstack-protector AS_CASE(["$target_os"], - [mingw*], [ + [mingw*|nacl], [ stack_protector=no ], [ @@ -1135,6 +1210,14 @@ main() ], [superux*], [ ac_cv_func_setitimer=no ], +[nacl], [ + LIBS="-lm -lnosys $LIBS" + if test "${nacl_cv_build_variant}" = "newlib"; then + RUBY_APPEND_OPTION(CPPFLAGS, -DNACL_NEWLIB) + else + RUBY_APPEND_OPTION(XCFLAGS, -fPIC) + fi + ], [ LIBS="-lm $LIBS"]) AC_CHECK_LIB(crypt, crypt) AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV @@ -1201,6 +1284,9 @@ AC_CHECK_MEMBERS([struct stat.st_ctimensec]) AC_CHECK_TYPES([struct timespec], [], [], [@%:@ifdef HAVE_TIME_H @%:@include +@%:@endif +@%:@ifdef HAVE_SYS_TIME_H +@%:@include @%:@endif]) AC_CHECK_TYPES([struct timezone], [], [], [@%:@ifdef HAVE_TIME_H @@ -1263,6 +1349,8 @@ RUBY_DEFINT(intptr_t, void*) RUBY_DEFINT(uintptr_t, void*, unsigned) RUBY_DEFINT(ssize_t, size_t, [], [@%:@include ]) dnl may differ from int, so not use AC_TYPE_SSIZE_T. +RUBY_NACL_CHECK_PEPPER_TYPES + AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address, [rb_cv_stack_end_address=no for addr in __libc_stack_end _SEND; do @@ -1451,11 +1539,11 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall __syscall chroot ge getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\ getpriority getrlimit setrlimit sysconf close getpwnam_r getgrnam_r\ dlopen sigprocmask sigaction sigsetjmp _setjmp _longjmp\ - setsid telldir seekdir fchmod cosh sinh tanh log2 round\ + setsid telldir seekdir fchmod cosh sinh tanh log2 round llabs\ setuid setgid daemon select_large_fdset setenv unsetenv\ mktime timegm gmtime_r clock_gettime gettimeofday poll ppoll\ pread sendfile shutdown sigaltstack dl_iterate_phdr\ - dup3 pipe2 posix_memalign memalign) + dup3 pipe2 posix_memalign memalign ioctl) AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value, [AC_TRY_COMPILE([ @@ -1917,7 +2005,8 @@ if test x"$enable_pthread" = xyes; then pthread_getattr_np pthread_attr_get_np pthread_attr_getstack\ pthread_get_stackaddr_np pthread_get_stacksize_np \ thr_stksegment pthread_stackseg_np pthread_getthrds_np \ - pthread_condattr_setclock pthread_sigmask) + pthread_cond_initialize pthread_condattr_setclock pthread_condattr_init \ + pthread_sigmask) fi if test x"$ac_cv_header_ucontext_h" = xyes; then if test x"$rb_with_pthread" = xyes; then @@ -2034,11 +2123,14 @@ if test "$rb_cv_binary_elf" = yes; then if test "$with_dln_a_out" = yes; then AC_MSG_ERROR(dln_a_out does not work with ELF) fi - AC_LIBOBJ([addr2line]) + AC_CHECK_HEADERS([elf.h elf_abi.h]) + if test $ac_cv_header_elf_h = yes -o $ac_cv_header_elf_abi_h = yes; then + AC_LIBOBJ([addr2line]) + fi fi AS_CASE(["$target_os"], -[linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu], [ +[linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu | nacl], [ if test "$rb_cv_binary_elf" = no; then with_dln_a_out=yes else @@ -2218,6 +2310,7 @@ if test "$with_dln_a_out" != yes; then rb_cv_dlopen=yes], [os2-emx*], [ LDFLAGS="$LDFLAGS -Zomf" ], + [nacl], [ LDSHARED='$(CC) -shared' ], [ : ${LDSHARED='$(LD)'}]) AC_MSG_RESULT($rb_cv_dlopen) fi @@ -2342,6 +2435,9 @@ AS_CASE(["$target_os"], [*djgpp*], [ setup=Setup.dj ], + [nacl], [ + setup=Setup.nacl + ], [ setup=Setup ]) @@ -2352,6 +2448,7 @@ if test "$prefix" = NONE; then prefix=$ac_default_prefix fi +BTESTRUBY='$(MINIRUBY)' if test x"$cross_compiling" = xyes; then test x"$MINIRUBY" = x && MINIRUBY="${RUBY-$BASERUBY} -I`pwd` "-r'$(arch)-fake' XRUBY_LIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["libdir"]']` @@ -2364,6 +2461,20 @@ if test x"$cross_compiling" = xyes; then RUNRUBY='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`' XRUBY='$(MINIRUBY)' TEST_RUNNABLE=no + + if test "$host_os" = "nacl"; then + if test "$build_cpu" = "$host_cpu" || test "${nacl_cv_cpu_nick}" = "x86" -a "$host_cpu" = "i686"; then + nacl_cv_sel_ldr='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb sel_ldr`' + nacl_cv_irt_core='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb irt_core`' + nacl_cv_runnable_ld='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb runnable_ld`' + nacl_cv_host_lib='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb host_lib`' + TEST_RUNNABLE=yes + BTESTRUBY="${nacl_cv_sel_ldr} -a -B ${nacl_cv_irt_core} -w 1:3 -w 2:4" + BTESTRUBY="$BTESTRUBY -- ${nacl_cv_runnable_ld} --library-path ${nacl_cv_host_lib}" + BTESTRUBY="$BTESTRUBY `pwd`/"'miniruby$(EXEEXT) -I`cd $(srcdir)/lib; pwd` -I.' + BTESTRUBY="$BTESTRUBY"' -I$(EXTOUT)/common 3>&1 4>&2 1>/dev/null 2>/dev/null ' + fi + fi else MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib -I.' MINIRUBY="$MINIRUBY"' -I$(EXTOUT)/common' @@ -2374,6 +2485,7 @@ else fi AC_SUBST(TEST_RUNNABLE) AC_SUBST(MINIRUBY) +AC_SUBST(BTESTRUBY) AC_SUBST(PREP) AC_SUBST(RUNRUBY) AC_SUBST(XRUBY) @@ -2600,6 +2712,7 @@ AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [ SYMBOL_PREFIX="$rb_cv_symbol_prefix" test "x$SYMBOL_PREFIX" = xNONE && SYMBOL_PREFIX='' MINIDLNOBJ=dmydln.o + AS_CASE(["$target_os"], [linux*], [ ], @@ -2679,7 +2792,10 @@ AS_CASE(["$target_os"], AS_CASE(["$YACC"],[*yacc*], [ XCFLAGS="$XCFLAGS -DYYMAXDEPTH=300" YACC="$YACC -Nl40000 -Nm40000" - ])]) + ])], + [nacl], [ + FIRSTMAKEFILE=GNUmakefile:nacl/GNUmakefile.in + ]) MINIOBJS="$MINIDLNOBJ" AS_CASE(["$THREAD_MODEL"], diff --git a/dir.c b/dir.c index 8476c0e1d3..ac4b1c614b 100644 --- a/dir.c +++ b/dir.c @@ -44,6 +44,10 @@ # include "win32/dir.h" # endif #endif +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/dirent.h" +# include "nacl/stat.h" +#endif #include diff --git a/dln.c b/dln.c index 38e1dcd4b4..cc15370fbc 100644 --- a/dln.c +++ b/dln.c @@ -1318,13 +1318,28 @@ dln_load(const char *file) # define RTLD_GLOBAL 0 #endif +#ifdef __native_client__ + char* p, *orig; + if (file[0] == '.' && file[1] == '/') file+=2; + orig = strdup(file); + for (p = file; *p; ++p) { + if (*p == '/') *p = '_'; + } +#endif /* Load file */ if ((handle = (void*)dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) { +#ifdef __native_client__ + free(orig); +#endif error = dln_strerror(); goto failed; } init_fct = (void(*)())(VALUE)dlsym(handle, buf); +#ifdef __native_client__ + strcpy(file, orig); + free(orig); +#endif #if defined __SYMBIAN32__ if (init_fct == NULL) { init_fct = (void(*)())dlsym(handle, "1"); /* Some Symbian versions do not support symbol table in DLL, ordinal numbers only */ diff --git a/ext/Setup.nacl b/ext/Setup.nacl new file mode 100644 index 0000000000..3e252fd765 --- /dev/null +++ b/ext/Setup.nacl @@ -0,0 +1,50 @@ +# #option nodynamic +# +# #Win32API +# bigdecimal +# continuation +# coverage +# #curses +# date +# #dbm +# digest/bubblebabble +# digest +# digest/md5 +# digest/rmd160 +# digest/sha1 +# digest/sha2 +# dl +# dl/callback +# #dl/win32 +# etc +# fcntl +# fiber +# #fiddle +# #gdbm +# #iconv +# io/console +# io/nonblock +# io/wait +# #json +# json/generator +# json/parser +# mathn/complex +# mathn/rational +# nkf +# objspace +# #openssl +# pathname +# #psych +# #pty +# racc/cparse +# #readline +# ripper +# #sdbm +# #socket +# stringio +# strscan +# syck +# #syslog +# #tk +# #tk/tkutil +# #zlib diff --git a/ext/extmk.rb b/ext/extmk.rb index 7122cc0f67..bc00b853e3 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -530,7 +530,7 @@ unless $extlist.empty? list = $extlist.dup built = [] while e = list.shift - s,t,i,r = e + s,t,i,r,os = e if r and !(r -= built).empty? l = list.size if (while l > 0; break true if r.include?(list[l-=1][1]) end) diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb index 3d6d0f1e46..890269f857 100644 --- a/ext/pty/extconf.rb +++ b/ext/pty/extconf.rb @@ -1,6 +1,6 @@ require 'mkmf' -if /mswin|mingw|bccwin/ !~ RUBY_PLATFORM +if /mswin|mingw|bccwin|nacl/ !~ RUBY_PLATFORM have_header("sys/stropts.h") have_func("setresuid") have_header("libutil.h") diff --git a/file.c b/file.c index 59de83c5fd..bb1ab371f8 100644 --- a/file.c +++ b/file.c @@ -60,6 +60,13 @@ int flock(int, int); #include #include +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/utime.h" +# include "nacl/stat.h" +# include "nacl/unistd.h" +#endif + + #ifdef HAVE_SYS_MKDEV_H #include #endif @@ -3358,7 +3365,11 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l struct stat sbuf; int ret; VALUE testpath2 = rb_str_encode_ospath(testpath); +#ifdef __native_client__ + ret = stat(RSTRING_PTR(testpath2), &sbuf); +#else ret = lstat(RSTRING_PTR(testpath2), &sbuf); +#endif if (ret == -1) { if (errno == ENOENT) { if (strict || !last || *unresolved_firstsep) @@ -3403,6 +3414,13 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l } } +#ifdef __native_client__ +VALUE +rb_realpath_internal(VALUE basedir, VALUE path, int strict) +{ + return path; +} +#else VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict) { @@ -3476,6 +3494,7 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict) OBJ_TAINT(resolved); return resolved; } +#endif /* * call-seq: @@ -5142,6 +5161,9 @@ rb_path_check(const char *path) } #ifndef _WIN32 +#ifdef __native_client__ +__attribute__((noinline)) +#endif int rb_file_load_ok(const char *path) { diff --git a/gc.c b/gc.c index a5ada1c1dc..4a96127bf2 100644 --- a/gc.c +++ b/gc.c @@ -32,6 +32,12 @@ #ifdef HAVE_SYS_RESOURCE_H #include +#endif +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/resource.h" +# undef HAVE_POSIX_MEMALIGN +# undef HAVE_MEMALIGN + #endif #if defined _WIN32 || defined __CYGWIN__ diff --git a/gc.h b/gc.h index df2cf7dd79..094b941061 100644 --- a/gc.h +++ b/gc.h @@ -2,9 +2,9 @@ #ifndef RUBY_GC_H #define RUBY_GC_H 1 -#if defined(__x86_64__) && defined(__GNUC__) +#if defined(__x86_64__) && defined(__GNUC__) && !defined(__native_client__) #define SET_MACHINE_STACK_END(p) __asm__("movq\t%%rsp, %0" : "=r" (*(p))) -#elif defined(__i386) && defined(__GNUC__) +#elif defined(__i386) && defined(__GNUC__) && !defined(__native_client__) #define SET_MACHINE_STACK_END(p) __asm__("movl\t%%esp, %0" : "=r" (*(p))) #else NOINLINE(void rb_gc_set_stack_end(VALUE **stack_end_p)); diff --git a/io.c b/io.c index 40203b3575..ad2394daf6 100644 --- a/io.c +++ b/io.c @@ -28,7 +28,9 @@ #if defined HAVE_NET_SOCKET_H # include #elif defined HAVE_SYS_SOCKET_H -# include +# ifndef __native_client__ +# include +# endif #endif #if defined(__BOW__) || defined(__CYGWIN__) || defined(_WIN32) || defined(__EMX__) || defined(__BEOS__) || defined(__HAIKU__) @@ -47,6 +49,9 @@ #if defined(HAVE_SYS_IOCTL_H) && !defined(_WIN32) #include #endif +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/ioctl.h" +#endif #if defined(HAVE_FCNTL_H) || defined(_WIN32) #include #elif defined(HAVE_SYS_FCNTL_H) @@ -161,7 +166,7 @@ void rb_maygvl_fd_fix_cloexec(int fd) { /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */ -#ifdef F_GETFD +#if defined(F_GETFD) && !defined(__native_client__) int flags, flags2, ret; flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */ if (flags == -1) { @@ -187,7 +192,6 @@ rb_fd_fix_cloexec(int fd) if (max_file_descriptor < fd) max_file_descriptor = fd; } - int rb_cloexec_open(const char *pathname, int flags, mode_t mode) { @@ -1502,6 +1506,7 @@ rb_io_set_pos(VALUE io, VALUE offset) static void clear_readconv(rb_io_t *fptr); +#if defined(HAVE_FSYNC) || !defined(_WIN32) /* * call-seq: * ios.rewind -> 0 @@ -1540,6 +1545,7 @@ rb_io_rewind(VALUE io) return INT2FIX(0); } +#endif static int io_fillbuf(rb_io_t *fptr) @@ -1629,6 +1635,7 @@ rb_io_eof(VALUE io) return Qfalse; } +#ifdef HAVE_FSYNC /* * call-seq: * ios.sync -> true or false @@ -1683,8 +1690,6 @@ rb_io_set_sync(VALUE io, VALUE sync) return sync; } -#ifdef HAVE_FSYNC - /* * call-seq: * ios.fsync -> 0 or nil @@ -1709,14 +1714,19 @@ rb_io_fsync(VALUE io) if (io_fflush(fptr) < 0) rb_sys_fail(0); -#ifndef _WIN32 /* already called in io_fflush() */ +# ifndef _WIN32 /* already called in io_fflush() */ if ((int)rb_thread_io_blocking_region(nogvl_fsync, fptr, fptr->fd) < 0) rb_sys_fail_path(fptr->pathv); -#endif +# endif return INT2FIX(0); } #else -#define rb_io_fsync rb_f_notimplement +# define rb_io_fsync rb_f_notimplement +# define rb_io_sync rb_f_notimplement +static VALUE rb_io_set_sync(VALUE io, VALUE sync) { + rb_notimplement(); + /* NEVER REACHED */ return Qundef; +} #endif #ifdef HAVE_FDATASYNC @@ -8200,10 +8210,10 @@ rb_f_select(int argc, VALUE *argv, VALUE obj) #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) typedef unsigned long ioctl_req_t; - #define NUM2IOCTLREQ(num) NUM2ULONG(num) +# define NUM2IOCTLREQ(num) NUM2ULONG(num) #else typedef int ioctl_req_t; - #define NUM2IOCTLREQ(num) NUM2INT(num) +# define NUM2IOCTLREQ(num) NUM2INT(num) #endif struct ioctl_arg { diff --git a/iseq.c b/iseq.c index ec9ac5fedd..ff0bcadd69 100644 --- a/iseq.c +++ b/iseq.c @@ -1548,4 +1548,3 @@ Init_ISeq(void) rb_define_singleton_method(rb_cISeq, "disasm", iseq_s_disasm, 1); rb_define_singleton_method(rb_cISeq, "disassemble", iseq_s_disasm, 1); } - diff --git a/missing/flock.c b/missing/flock.c index 763c49d675..435d66aefb 100644 --- a/missing/flock.c +++ b/missing/flock.c @@ -1,7 +1,8 @@ #include "ruby/config.h" +#include "ruby/ruby.h" #if defined _WIN32 -#elif defined HAVE_FCNTL && defined HAVE_FCNTL_H +#elif defined HAVE_FCNTL && defined HAVE_FCNTL_H && !defined(__native_client__) /* These are the flock() constants. Since this sytems doesn't have flock(), the values of the constants are probably not available. diff --git a/nacl/GNUmakefile.in b/nacl/GNUmakefile.in new file mode 100644 index 0000000000..ebb3bfb401 --- /dev/null +++ b/nacl/GNUmakefile.in @@ -0,0 +1,60 @@ +# Copyright 2012 Google Inc. All Rights Reserved. +# Author: yugui@google.com (Yugui Sonoda) + +include Makefile +-include uncommon.mk + +NACL_SDK_ROOT=@NACL_SDK_ROOT@ +NACL_TOOLCHAIN=@NACL_TOOLCHAIN@ +NACL_TOOLCHAIN_DIR=$(NACL_SDK_ROOT)/toolchain/$(NACL_TOOLCHAIN) +PATH+=:$(NACL_TOOLCHAIN_DIR)/bin +PYTHON=@PYTHON@ + +PPROGRAM=pepper-$(PROGRAM) +PEPPER_LIBS=-lppapi +PROGRAM_NMF=$(PROGRAM:.nexe=.nmf) +PPROGRAM_NMF=$(PPROGRAM:.nexe=.nmf) + +GNUmakefile: $(srcdir)/nacl/GNUmakefile.in +$(PPROGRAM): $(PROGRAM) pepper_main.$(OBJEXT) + $(Q)$(MAKE) $(MFLAGS) PROGRAM=$(PPROGRAM) MAINOBJ="pepper_main.$(OBJEXT)" LIBS="$(LIBS) $(PEPPER_LIBS)" program +$(PROGRAM_NMF) $(PPROGRAM_NMF): $(@:.nmf=.nexe) nacl/create_nmf.rb + +.PHONY: pprogram package show_naclflags +.SUFFIXES: .nexe .nmf +.nexe.nmf: + $(ECHO) generating manifest $@ + $(Q)$(MINIRUBY) $(srcdir)/nacl/create_nmf.rb --verbose=$(V) $(@:.nmf=.nexe) $@ + +pepper_main.$(OBJEXT): $(srcdir)/nacl/pepper_main.c + @$(ECHO) compiling nacl/pepper_main.c + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(srcdir)/nacl/pepper_main.c +ruby.$(OBJEXT): + @$(ECHO) compiling $< + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@.tmp -c $< + $(Q) $(OBJCOPY) --weaken-symbol=rb_load_file $@.tmp $@ + @-$(RM) $@.tmp +file.$(OBJEXT): + @$(ECHO) compiling $< + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@.tmp -c $< + $(Q) $(OBJCOPY) --weaken-symbol=rb_file_load_ok $@.tmp $@ + @-$(RM) $@.tmp + +all: pprogram +main: $(PROGRAM_NMF) +pprogram: showflags $(PPROGRAM) $(PPROGRAM_NMF) +program: $(PROGRAM_NMF) +prog: pprogram + +package: pprogram install-lib install-ext-comm install-ext-arch + $(ECHO) generating manifest $@ + $(Q)$(MINIRUBY) $(srcdir)/nacl/package.rb $(prefix) + +showflags: show_naclflags + +show_naclflags: + @echo " PATH = $(PATH)" + @echo " NACL_SDK_ROOT = $(NACL_SDK_ROOT)" + +clean-local:: + -$(RM) $(PPROGRAM) pepper_main.$(OBJEXT) $(PROGRAM_NMF) $(PPRGORAM_NMF) diff --git a/nacl/README.nacl b/nacl/README.nacl new file mode 100644 index 0000000000..0db9971797 --- /dev/null +++ b/nacl/README.nacl @@ -0,0 +1,34 @@ +=begin += Native Client port of Ruby + += How to build +== Prerequisites +You need to install the following things before building NaCl port of Ruby. +* Ruby 1.9.3 or later +* Python 2.6 or later +* NativeClient SDK pepper 18 or later +* GNU make + +== Steps +(1) Extract all files from the tarball: + $ tar xzf ruby-X.Y.Z.tar.gz +(2) Set NACL_SDK_ROOT environment vairanble to the path to the Native Client SDK you installed: + $ export NACL_SDK_ROOT=/home/yugui/src/nacl_sdk/pepper_16 +(3) Configure + $ ./configure --prefix=/tmp/nacl-ruby --host=x86_64-nacl --with-baseruby=/path/to/ruby-1.9.3 +(4) Make + $ make + $ make package + +Now you have ruby.nexe. You can run it by sel_ldr in NaCl SDK. + +"make package" installs "pepper-ruby.nexe", an example Pepper application that +embeds Ruby", and libraries to $prefix. You can run it by the following steps: +(5) Publish the $prefix directory on a web server +(6) Visit the example.html on the web server by a browser that implements Pepper 18 or later. + -- e.g., Chrome 18 implements Pepper 18, Chrome 19 implements Pepper 19, ... + += Copyright +* Copyright 2012 Google Inc. All Rights Reserved. +* Author: yugui@google.com (Yugui Sonoda) +=end diff --git a/nacl/create_nmf.rb b/nacl/create_nmf.rb new file mode 100644 index 0000000000..cdfe7ad239 --- /dev/null +++ b/nacl/create_nmf.rb @@ -0,0 +1,70 @@ +#!/usr/bin/ruby +# Copyright:: Copyright 2012 Google Inc. +# License:: All Rights Reserved. +# Original Author:: Yugui Sonoda (mailto:yugui@google.com) +# +# Wrapper for create_nmf.py / generate_nmf.py + +require File.join(File.dirname(__FILE__), 'nacl-config') + +include NaClConfig +$verbosity = 0 + +def usage_and_exit + $stderr.puts "Usage: #{$PROGRAM_NAME} [--verbose=N] path/to/input.nexe path/to/output.nmf" + exit false +end + +def create_dynamically_linked(nmf, exe) + cmd = [ + PYTHON, CREATE_NMF, + '-o', nmf, + '-D', OBJDUMP, + '-L', HOST_LIB, + exe + ] + puts cmd.join(' ') if $verbosity > 0 + exec(*cmd) +end + +def create_statically_linked(nmf, exe) + File.open(nmf, "w") {|f| + f.write <<-EOS.gsub(/^ {6}/, '') + { + "program": { + "#{ARCH}": { + "url": "#{exe}" + } + } + } + EOS + } +end + +def main + while m = ARGV.first.match(/--([a-z-]+)(?:=(\S+))?/) + case m[1] + when 'verbose' + usage_and_exit unless m[2][/\A[0-9]+\z/] + $verbosity = m[2].to_i + when 'help' + usage_end_exit + end + ARGV.shift + end + + usage_and_exit if ARGV.size < 2 + + exe, nmf = ARGV[0], ARGV[1] + if newlib? + create_statically_linked(nmf, exe) + else + create_dynamically_linked(nmf, exe) + end +end + +if __FILE__ == $0 + main() +end + + diff --git a/nacl/dirent.h b/nacl/dirent.h new file mode 100644 index 0000000000..31bdad31b7 --- /dev/null +++ b/nacl/dirent.h @@ -0,0 +1,15 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * Author: yugui@google.com (Yugui Sonoda) + */ +#ifndef RUBY_NACL_DIRENT_H +#define RUBY_NACL_DIRENT_H + +/* NaCl SDK 0.3 has implementations of dir functions but no declaration in + * dirent.h */ +int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); +void rewinddir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp, long offset); + +#endif diff --git a/nacl/example.html b/nacl/example.html new file mode 100644 index 0000000000..3cc33298dd --- /dev/null +++ b/nacl/example.html @@ -0,0 +1,150 @@ + + + + Ruby Example + + + + + +

Native Client Module Ruby

+

+ +

+ + + + +
+

+ +

Status

+
NO-STATUS
+ + diff --git a/nacl/ioctl.h b/nacl/ioctl.h new file mode 100644 index 0000000000..0a18eeb3f5 --- /dev/null +++ b/nacl/ioctl.h @@ -0,0 +1,7 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: yugui@google.com (Yugui Sonoda) +#ifndef RUBY_NACL_IOCTL_H +#define RUBY_NACL_IOCTL_H +int ioctl(int fd, int request, ...); +struct flock{}; +#endif diff --git a/nacl/nacl-config.rb b/nacl/nacl-config.rb new file mode 100644 index 0000000000..94a64aa711 --- /dev/null +++ b/nacl/nacl-config.rb @@ -0,0 +1,61 @@ +#!/usr/bin/ruby +# +# Copyright:: Copyright 2012 Google Inc. +# License:: All Rights Reserved. +# Original Author:: Yugui Sonoda (mailto:yugui@google.com) +# +# Convenient functions/constants for native client specific configurations. +require 'rbconfig' + +module NaClConfig + config = RbConfig::CONFIG + + cpu_nick = config['host_alias'].sub(/-gnu$|-newlib$/, '').sub(/-nacl$/, '') + ARCH = cpu_nick.sub('x86_64', 'x86-64').sub(/i.86/, 'x86-32') + HOST = ARCH.sub(/x86-../, 'x86_64') + '-nacl' + + lib_suffix = config['host_cpu'][/i.86/] ? '32' : '' + PYTHON = config['PYTHON'] + OBJDUMP = config['OBJDUMP'] + SDK_ROOT = config['NACL_SDK_ROOT'] + CREATE_NMF = [ + File.join(SDK_ROOT, 'build_tools', 'nacl_sdk_scons', 'site_tools', 'create_nmf.py'), + File.join(SDK_ROOT, 'tools', 'create_nmf.py') + ].find{|path| File.exist?(path) } + HOST_LIB = File.join(SDK_ROOT, 'toolchain', config['NACL_TOOLCHAIN'], HOST, "lib#{lib_suffix}") + + INSTALL_PROGRAM = config['INSTALL_PROGRAM'] + INSTALL_LIBRARY = config['INSTALL_DATA'] + + SEL_LDR = [ + File.join(SDK_ROOT, 'toolchain', config['NACL_TOOLCHAIN'], 'bin', "sel_ldr_#{cpu_nick}"), + File.join(SDK_ROOT, 'tools', "sel_ldr_#{cpu_nick}") + ].find{|path| File.executable?(path)} + IRT_CORE = [ + File.join(SDK_ROOT, 'toolchain', config['NACL_TOOLCHAIN'], 'bin', "irt_core_#{cpu_nick}.nexe"), + File.join(SDK_ROOT, 'tools', "irt_core_#{cpu_nick}.nexe") + ].find{|path| File.executable?(path)} + RUNNABLE_LD = File.join(HOST_LIB, 'runnable-ld.so') + + module_function + + def newlib? + RbConfig::CONFIG['NACL_SDK_VARIANT'] == 'newlib' + end + + def self.config(name) + if NaClConfig::const_defined?(name.upcase) + NaClConfig::const_get(name.upcase) + elsif NaClConfig::respond_to?(name) and NaClConfig::method(name).arity == 0 + NaClConfig::send(name) + else + raise ArgumentError, "No such config: #{name}" + end + end +end + +if $0 == __FILE__ + ARGV.each do |arg| + puts NaClConfig::config(arg) + end +end diff --git a/nacl/package.rb b/nacl/package.rb new file mode 100644 index 0000000000..f4f50f24ed --- /dev/null +++ b/nacl/package.rb @@ -0,0 +1,109 @@ +#!/usr/bin/ruby +# Copyright:: Copyright 2012 Google Inc. +# License:: All Rights Reserved. +# Original Author:: Yugui Sonoda (mailto:yugui@google.com) +# +# Generates a runnable package of the pepper API example. + +require File.join(File.dirname(__FILE__), 'nacl-config') +require 'json' +require 'find' +require 'fileutils' + +include NaClConfig + +class Installation + include NaClConfig + + SRC_DIRS = [ Dir.pwd, HOST_LIB ] + + def initialize(destdir) + @destdir = destdir + @manifest = { + "files" => {} + } + ruby_libs.each do |path| + raise "Collision of #{path}" if @manifest['files'].key? path + @manifest['files'][path] = { + ARCH => { + "url" => path + } + } + if path[/\.so$/] + alternate_path = path.gsub('/', "_") + raise "Collision of #{alternate_path}" if @manifest['files'].key? alternate_path + @manifest['files'][alternate_path] = { + ARCH => { + "url" => path + } + } + end + end + end + + def manifest + @manifest.dup + end + + def install_program(basename) + do_install_binary(basename, File.join(@destdir, "bin", ARCH)) + @manifest["program"] = { + ARCH => { + "url" => File.join("bin", ARCH, basename) + } + } + end + + def install_library(name, basename) + do_install_binary(basename, File.join(@destdir, "lib", ARCH)) + @manifest["files"][name] = { + ARCH => { + "url" => File.join("lib", ARCH, basename) + } + } + end + + private + def do_install_binary(basename, dest_dir) + full_path = nil + catch(:found) { + SRC_DIRS.each do |path| + full_path = File.join(path, basename) + if File.exist? full_path + throw :found + end + end + raise Errno::ENOENT, "No such file to install: %s" % basename + } + FileUtils.mkdir_p dest_dir + system("#{INSTALL_PROGRAM} #{full_path} #{dest_dir}") + end + + def ruby_libs + Find.find(RbConfig::CONFIG['rubylibdir']).select{|path| File.file?(path) }.map{|path| path.sub("#{@destdir}/", "") } + end +end + +def install(destdir) + inst = Installation.new(destdir) + manifest = JSON.parse(File.read("pepper-ruby.nmf")) + + program = File.basename(manifest['program'][ARCH]['url']) + inst.install_program(program) + + manifest['files'].each do |name, attr| + inst.install_library(name, File.basename(attr[ARCH]["url"])) + end + + File.open(File.join(destdir, "ruby.nmf"), "w") {|f| + f.puts JSON.pretty_generate(inst.manifest) + } +end + +def main + install(ARGV[0]) +end + +if __FILE__ == $0 + main() +end diff --git a/nacl/pepper_main.c b/nacl/pepper_main.c new file mode 100644 index 0000000000..a05a972f92 --- /dev/null +++ b/nacl/pepper_main.c @@ -0,0 +1,924 @@ +/****************************************************************************** + Copyright 2012 Google Inc. All Rights Reserved. + Author: yugui@google.com (Yugui Sonoda) + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/ppb.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_file_ref.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/c/ppb_messaging.h" +#include "ppapi/c/ppb_url_loader.h" +#include "ppapi/c/ppb_url_request_info.h" +#include "ppapi/c/ppb_url_response_info.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/c/ppp.h" +#include "ppapi/c/ppp_instance.h" +#include "ppapi/c/ppp_messaging.h" + +#include "ruby/ruby.h" +#include "vm_core.h" +#include "eval_intern.h" +#include "gc.h" +#include "node.h" + +RUBY_GLOBAL_SETUP + +#ifdef HAVE_STRUCT_PPB_CORE +typedef struct PPB_Core PPB_Core; +#endif +#ifdef HAVE_STRUCT_PPB_MESSAGING +typedef struct PPB_Messaging PPB_Messaging; +#endif +#ifdef HAVE_STRUCT_PPB_VAR +typedef struct PPB_Var PPB_Var; +#endif +#ifdef HAVE_STRUCT_PPB_URLLOADER +typedef struct PPB_URLLoader PPB_URLLoader; +#endif +#ifdef HAVE_STRUCT_PPB_URLREQUESTINFO +typedef struct PPB_URLRequestInfo PPB_URLRequestInfo; +#endif +#ifdef HAVE_STRUCT_PPB_URLRESPONSEINFO +typedef struct PPB_URLResponseInfo PPB_URLResponseInfo; +#endif +#ifdef HAVE_STRUCT_PPP_INSTANCE +typedef struct PPP_Instance PPP_Instance; +#endif + +static PP_Module module_id = 0; +static PPB_Core* core_interface = NULL; +static PPB_Messaging* messaging_interface = NULL; +static PPB_Var* var_interface = NULL; +static PPB_URLLoader* loader_interface = NULL; +static PPB_URLRequestInfo* request_interface = NULL; +static PPB_URLResponseInfo* response_interface = NULL; +static PPB_FileRef* fileref_interface = NULL; +static struct st_table* instance_data = NULL; + +static VALUE instance_table = Qundef; + +static PP_Instance current_instance = 0; + +/****************************************************************************** + * State of instance + ******************************************************************************/ + +static void inst_mark(void *const ptr); +static void inst_free(void *const ptr); +static size_t inst_memsize(void *const ptr); +static const rb_data_type_t pepper_instance_data_type = { + "PepperInstance", + { inst_mark, inst_free, inst_memsize } +}; + +struct PepperInstance { + PP_Instance instance; + PP_Resource url_loader; + VALUE self; + void* async_call_args; + union { + int32_t as_int; + const char* as_str; + VALUE as_value; + } async_call_result; + char buf[1000]; + + pthread_t th; + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +struct PepperInstance* +pruby_get_instance(PP_Instance instance) +{ + VALUE self = rb_hash_aref(instance_table, INT2FIX(instance)); + if (RTEST(self)) { + struct PepperInstance *inst; + TypedData_Get_Struct(self, struct PepperInstance, &pepper_instance_data_type, inst); + return inst; + } + else { + return NULL; + } +} + +#define GET_PEPPER_INSTANCE() (pruby_get_instance(current_instance)) + +struct PepperInstance* +pruby_register_instance(PP_Instance instance) +{ + VALUE obj; + struct PepperInstance *data; + obj = TypedData_Make_Struct(rb_cData, struct PepperInstance, &pepper_instance_data_type, data); + data->self = obj; + data->instance = instance; + data->url_loader = 0; + + pthread_mutex_init(&data->mutex, NULL); + pthread_cond_init(&data->cond, NULL); + + rb_hash_aset(instance_table, INT2FIX(instance), obj); + return data; +} + +int +pruby_unregister_instance(PP_Instance instance) +{ + VALUE inst = rb_hash_delete(instance_table, INT2FIX(instance)); + return RTEST(inst); +} + +static void +inst_mark(void *const ptr) +{ + RUBY_MARK_ENTER("PepperInstance"0); + if (ptr) { + const struct PepperInstance* inst = (struct PepperInstance*)ptr; + RUBY_MARK_UNLESS_NULL(inst->async_call_result.as_value); + } + RUBY_MARK_LEAVE("PepperInstance"0); +} + +static void +inst_free(void *const ptr) +{ + ruby_xfree(ptr); +} + +static size_t +inst_memsize(void *const ptr) +{ + if (ptr) { + const struct PepperInstance* inst = (struct PepperInstance*)ptr; + return sizeof(*inst); + } else { + return 0; + } +} + +void +pruby_async_return_int(void* data, int32_t result) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + instance->async_call_result.as_int = result; + if (pthread_cond_signal(&instance->cond)) { + perror("pepper-ruby:pthread_cond_signal"); + } +} + +void +pruby_async_return_str(void* data, const char *result) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + instance->async_call_result.as_str = result; + if (pthread_cond_signal(&instance->cond)) { + perror("pepper-ruby:pthread_cond_signal"); + } +} + +void +pruby_async_return_value(void* data, VALUE value) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + instance->async_call_result.as_value = value; + if (pthread_cond_signal(&instance->cond)) { + perror("pepper-ruby:pthread_cond_signal"); + } +} +/****************************************************************************** + * Conversion between Ruby's VALUE, Pepper's Var and C string + ******************************************************************************/ + +/** + * Creates a new string PP_Var from C string. The resulting object will be a + * refcounted string object. It will be AddRef()ed for the caller. When the + * caller is done with it, it should be Release()d. + * @param[in] str C string to be converted to PP_Var + * @return PP_Var containing string. + */ +static struct PP_Var +pruby_cstr_to_var(const char* str) +{ +#ifdef PPB_VAR_INTERFACE_1_0 + if (var_interface != NULL) + return var_interface->VarFromUtf8(module_id, str, strlen(str)); + return PP_MakeUndefined(); +#else + return var_interface->VarFromUtf8(str, strlen(str)); +#endif +} + +/** + * Returns a mutable C string contained in the @a var or NULL if @a var is not + * string. This makes a copy of the string in the @a var and adds a NULL + * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on + * the returned string. See the comments for VarToUtf8() in ppapi/c/ppb_var.h + * for more info. The caller is responsible for freeing the returned memory. + * @param[in] var PP_Var containing string. + * @return a mutable C string representation of @a var. + * @note The caller is responsible for freeing the returned string. + */ +static char* +pruby_var_to_cstr(struct PP_Var var) +{ + uint32_t len = 0; + if (var_interface != NULL) { + const char* var_c_str = var_interface->VarToUtf8(var, &len); + if (len > 0) { + char* c_str = (char*)malloc(len + 1); + memcpy(c_str, var_c_str, len); + c_str[len] = '\0'; + return c_str; + } + } + return NULL; +} + +static struct PP_Var +pruby_str_to_var(volatile VALUE str) +{ + if (!RB_TYPE_P(str, T_STRING)) { + fprintf(stderr, "[BUG] Unexpected object type: %x\n", TYPE(str)); + exit(EXIT_FAILURE); + } +#ifdef PPB_VAR_INTERFACE_1_0 + if (var_interface != NULL) { + return var_interface->VarFromUtf8(module_id, RSTRING_PTR(str), RSTRING_LEN(str)); + } +#else + return var_interface->VarFromUtf8(RSTRING_PTR(str), RSTRING_LEN(str)); +#endif + return PP_MakeUndefined(); +} + +static struct PP_Var +pruby_obj_to_var(volatile VALUE obj) +{ + static const char* const error = + "throw 'Failed to convert the result to a JavaScript object';"; + int state; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + obj = rb_obj_as_string(obj); + } + POP_TAG(); + + switch (state) { + case 0: + return pruby_str_to_var(obj); + case TAG_RAISE: + rb_set_errinfo(Qnil); + return pruby_cstr_to_var(error); + default: + fprintf(stderr, "Fatal error white converting the result to a string\n"); + exit(EXIT_FAILURE); + } +} + +int +pruby_var_equal_to_cstr_p(struct PP_Var lhs, const char* rhs) +{ + uint32_t len = 0; + if (var_interface == NULL) { + return 0; + } + else { + const char* const cstr = var_interface->VarToUtf8(lhs, &len); + return strncmp(cstr, rhs, len) == 0; + } +} + +int +pruby_var_prefixed_p(struct PP_Var var, const char* prefix) +{ + uint32_t len = 0; + if (var_interface == NULL) { + return 0; + } + else { + const char* const cstr = var_interface->VarToUtf8(var, &len); + const size_t prefix_len = strlen(prefix); + return len >= prefix_len && memcmp(cstr, prefix, len) == 0; + } +} + + +/****************************************************************************** + * Messaging + ******************************************************************************/ + +/* Posts the given C string as a message. + * @param data pointer to a NULL-terminated string */ +void +pruby_post_cstr(void* data) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + const char* const msg = (const char*)instance->async_call_args; + messaging_interface->PostMessage(instance->instance, + pruby_cstr_to_var(msg)); +} + +/* Posts the given Ruby VALUE as a message. + * @param data a VALUE casted to void* */ +void +pruby_post_value(void* data) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + volatile VALUE value = (VALUE)instance->async_call_args; + messaging_interface->PostMessage(instance->instance, pruby_obj_to_var(value)); +} + + + +/****************************************************************************** + * Ruby initialization + ******************************************************************************/ + +static void +init_loadpath(void) +{ + volatile VALUE path; + VALUE load_path = GET_VM()->load_path; + + path = rb_usascii_str_new_cstr("lib/ruby/2.0.0"); + rb_ary_push(load_path, path); + path = rb_usascii_str_new_cstr("lib/ruby/2.0.0/x86_64-nacl"); + rb_ary_push(load_path, path); + + path = rb_usascii_str_new_cstr("."); + rb_ary_push(load_path, path); +} + +static void* +init_libraries(void* data) +{ + extern void Init_enc(); + extern void Init_ext(); + + int state; + struct PepperInstance* const instance = (struct PepperInstance*)data; + current_instance = instance->instance; + + if (pthread_mutex_lock(&instance->mutex)) { + perror("pepper-ruby:pthread_mutex_lock"); + return 0; + } + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + init_loadpath(); + Init_enc(); + Init_ext(); + } + POP_TAG(); + + pthread_mutex_unlock(&instance->mutex); + + if (state) { + volatile VALUE err = rb_errinfo(); + err = rb_obj_as_string(err); + } else { + instance->async_call_args = "rubyReady"; + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(pruby_post_cstr, instance), 0); + } + return NULL; +} + +static int +init_libraries_if_necessary(void) +{ + static int initialized = 0; + if (!initialized) { + struct PepperInstance* const instance = GET_PEPPER_INSTANCE(); + int err; + initialized = 1; + err = pthread_create(&instance->th, NULL, &init_libraries, instance); + if (err) { + fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err)); + exit(EXIT_FAILURE); + } + pthread_detach(instance->th); + } + return 0; +} + +static int +pruby_init(void) +{ + RUBY_INIT_STACK; + ruby_init(); + + instance_table = rb_hash_new(); + rb_gc_register_mark_object(instance_table); + + return 0; +} + + +/****************************************************************************** + * Ruby evaluation + ******************************************************************************/ + +static void* +pruby_eval(void* data) +{ + struct PepperInstance* const instance = (struct PepperInstance*)data; + volatile VALUE src = (VALUE)instance->async_call_args; + volatile VALUE iseq, result = Qnil; + volatile VALUE filename; + NODE* tree; + volatile int state; + rb_thread_t *th; + rb_env_t *env; + + RUBY_INIT_STACK; + PUSH_TAG(); + + if (pthread_mutex_lock(&instance->mutex)) { + perror("pepper-ruby:pthread_mutex_lock"); + return 0; + } + + if ((state = EXEC_TAG()) == 0) { + th = GET_THREAD(); + SAVE_ROOT_JMPBUF(th, { + th->mild_compile_error++; + tree = rb_compile_string("(pepper-ruby)", src, 1); + th->mild_compile_error--; + if (RTEST(rb_errinfo())) { + rb_exc_raise(rb_errinfo()); + } + + { + VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")); + rb_binding_t *bind; + + GetBindingPtr(toplevel_binding, bind); + GetEnvPtr(bind->env, env); + } + + filename = rb_usascii_str_new("(pepper-ruby)", strlen("(pepper-ruby)")); + th->parse_in_eval--; + th->base_block = &env->block; + iseq = rb_iseq_new_main(tree, filename, filename); + th->parse_in_eval++; + th->base_block = 0; + + result = rb_iseq_eval_main(iseq); + }); + } + POP_TAG(); + + pthread_mutex_unlock(&instance->mutex); + + switch (state) { + case 0: + instance->async_call_args = + rb_str_concat(rb_usascii_str_new_cstr("return:"), + rb_obj_as_string(result)); + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0); + return NULL; + case TAG_RAISE: + result = rb_errinfo(); + rb_set_errinfo(Qnil); + instance->async_call_args = + rb_str_concat(rb_usascii_str_new_cstr("error:"), + rb_obj_as_string(result)); + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0); + return NULL; + default: + fprintf(stderr, "Fatal error\n"); + exit(EXIT_FAILURE); + } +} + + +/****************************************************************************** + * Pepper Module callbacks + ******************************************************************************/ + +/** + * Called when the NaCl module is instantiated on the web page. The identifier + * of the new instance will be passed in as the first argument (this value is + * generated by the browser and is an opaque handle). This is called for each + * instantiation of the NaCl module, which is each time the tag for + * this module is encountered. + * + * If this function reports a failure (by returning @a PP_FALSE), the NaCl + * module will be deleted and DidDestroy will be called. + * @param[in] instance The identifier of the new instance representing this + * NaCl module. + * @param[in] argc The number of arguments contained in @a argn and @a argv. + * @param[in] argn An array of argument names. These argument names are + * supplied in the tag, for example: + * + * will produce two arguments, one named "id" and one named "dimensions". + * @param[in] argv An array of argument values. These are the values of the + * arguments listed in the tag. In the above example, there will + * be two elements in this array, "nacl_module" and "2". The indices of + * these values match the indices of the corresponding names in @a argn. + * @return @a PP_TRUE on success. + */ +static PP_Bool +Instance_DidCreate(PP_Instance instance, + uint32_t argc, const char* argn[], const char* argv[]) +{ + struct PepperInstance* data = pruby_register_instance(instance); + current_instance = instance; + return init_libraries_if_necessary() ? PP_FALSE : PP_TRUE; +} + +/** + * Called when the NaCl module is destroyed. This will always be called, + * even if DidCreate returned failure. This routine should deallocate any data + * associated with the instance. + * @param[in] instance The identifier of the instance representing this NaCl + * module. + */ +static void Instance_DidDestroy(PP_Instance instance) { + struct PepperInstance* data = pruby_get_instance(instance); + core_interface->ReleaseResource(data->url_loader); + pruby_unregister_instance(instance); +} + +/** + * Called when the position, the size, or the clip rect of the element in the + * browser that corresponds to this NaCl module has changed. + * @param[in] instance The identifier of the instance representing this NaCl + * module. + * @param[in] position The location on the page of this NaCl module. This is + * relative to the top left corner of the viewport, which changes as the + * page is scrolled. + * @param[in] clip The visible region of the NaCl module. This is relative to + * the top left of the plugin's coordinate system (not the page). If the + * plugin is invisible, @a clip will be (0, 0, 0, 0). + */ +#ifdef PPP_INSTANCE_INTERFACE_1_0 +static void +Instance_DidChangeView(PP_Instance instance, + const struct PP_Rect* position, + const struct PP_Rect* clip) +{ +} +#else +static void +Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource) +{ +} +#endif + +/** + * Notification that the given NaCl module has gained or lost focus. + * Having focus means that keyboard events will be sent to the NaCl module + * represented by @a instance. A NaCl module's default condition is that it + * will not have focus. + * + * Note: clicks on NaCl modules will give focus only if you handle the + * click event. You signal if you handled it by returning @a true from + * HandleInputEvent. Otherwise the browser will bubble the event and give + * focus to the element on the page that actually did end up consuming it. + * If you're not getting focus, check to make sure you're returning true from + * the mouse click in HandleInputEvent. + * @param[in] instance The identifier of the instance representing this NaCl + * module. + * @param[in] has_focus Indicates whether this NaCl module gained or lost + * event focus. + */ +static void +Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) +{ +} + +/** + * Handler that gets called after a full-frame module is instantiated based on + * registered MIME types. This function is not called on NaCl modules. This + * function is essentially a place-holder for the required function pointer in + * the PPP_Instance structure. + * @param[in] instance The identifier of the instance representing this NaCl + * module. + * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance. + * @return PP_FALSE. + */ +static PP_Bool +Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader) +{ + /* NaCl modules do not need to handle the document load function. */ + return PP_FALSE; +} + + +/** + * Handler for messages coming in from the browser via postMessage. The + * @a var_message can contain anything: a JSON string; a string that encodes + * method names and arguments; etc. For example, you could use JSON.stringify + * in the browser to create a message that contains a method name and some + * parameters, something like this: + * var json_message = JSON.stringify({ "myMethod" : "3.14159" }); + * nacl_module.postMessage(json_message); + * On receipt of this message in @a var_message, you could parse the JSON to + * retrieve the method name, match it to a function call, and then call it with + * the parameter. + * @param[in] instance The instance ID. + * @param[in] message The contents, copied by value, of the message sent from + * browser via postMessage. + */ +void +Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message) +{ + char* const message = pruby_var_to_cstr(var_message); + size_t message_len = strlen(message); + current_instance = instance; + + if (strstr(message, "eval:") != NULL) { + volatile VALUE src; + struct PepperInstance* const instance_data = GET_PEPPER_INSTANCE(); + int err; +#define EVAL_PREFIX_LEN 5 + src = rb_str_new(message + EVAL_PREFIX_LEN, message_len - EVAL_PREFIX_LEN); + instance_data->async_call_args = (void*)src; + err = pthread_create(&instance_data->th, NULL, &pruby_eval, instance_data); + if (err) { + fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err)); + exit(EXIT_FAILURE); + } + pthread_detach(instance_data->th); + } + free(message); +} + +/** + * Entry points for the module. + * Initialize instance interface and scriptable object class. + * @param[in] a_module_id Module ID + * @param[in] get_browser_interface Pointer to PPB_GetInterface + * @return PP_OK on success, any other value on failure. + */ +PP_EXPORT int32_t +PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser_interface) +{ + module_id = a_module_id; + core_interface = (PPB_Core*)(get_browser_interface(PPB_CORE_INTERFACE)); + if (core_interface == NULL) return PP_ERROR_NOINTERFACE; + + var_interface = (PPB_Var*)(get_browser_interface(PPB_VAR_INTERFACE)); + if (var_interface == NULL) return PP_ERROR_NOINTERFACE; + + messaging_interface = (PPB_Messaging*)(get_browser_interface(PPB_MESSAGING_INTERFACE)); + if (messaging_interface == NULL) return PP_ERROR_NOINTERFACE; + + loader_interface = (PPB_URLLoader*)(get_browser_interface(PPB_URLLOADER_INTERFACE)); + if (loader_interface == NULL) return PP_ERROR_NOINTERFACE; + + request_interface = (PPB_URLRequestInfo*)(get_browser_interface(PPB_URLREQUESTINFO_INTERFACE)); + if (request_interface == NULL) return PP_ERROR_NOINTERFACE; + + response_interface = (PPB_URLResponseInfo*)(get_browser_interface(PPB_URLRESPONSEINFO_INTERFACE)); + if (response_interface == NULL) return PP_ERROR_NOINTERFACE; + + fileref_interface = (PPB_FileRef*)(get_browser_interface(PPB_FILEREF_INTERFACE)); + if (fileref_interface == NULL) return PP_ERROR_NOINTERFACE; + + return pruby_init() ? PP_ERROR_FAILED : PP_OK; +} + +/** + * Returns an interface pointer for the interface of the given name, or NULL + * if the interface is not supported. + * @param[in] interface_name name of the interface + * @return pointer to the interface + */ +PP_EXPORT const void* +PPP_GetInterface(const char* interface_name) +{ + if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { + static PPP_Instance instance_interface = { + &Instance_DidCreate, + &Instance_DidDestroy, + &Instance_DidChangeView, + &Instance_DidChangeFocus, + &Instance_HandleDocumentLoad + }; + return &instance_interface; + } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) { + static PPP_Messaging messaging_interface = { + &Messaging_HandleMessage + }; + return &messaging_interface; + } + return NULL; +} + +/** + * Called before the plugin module is unloaded. + */ +PP_EXPORT void +PPP_ShutdownModule() +{ + ruby_cleanup(0); +} + +/****************************************************************************** + * Overwrites rb_file_load_ok + ******************************************************************************/ + +static void +load_ok_internal(void* data, int32_t unused) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + const char *const path = (const char*)instance->async_call_args; + PP_Resource req; + int result; + + instance->url_loader = loader_interface->Create(instance->instance); + req = request_interface->Create(instance->instance); + request_interface->SetProperty( + req, PP_URLREQUESTPROPERTY_METHOD, pruby_cstr_to_var("HEAD")); + request_interface->SetProperty( + req, PP_URLREQUESTPROPERTY_URL, pruby_cstr_to_var(path)); + + result = loader_interface->Open( + instance->url_loader, req, + PP_MakeCompletionCallback(pruby_async_return_int, instance)); + if (result != PP_OK_COMPLETIONPENDING) { + pruby_async_return_int(instance, result); + } +} + +static void +pruby_file_fetch_check_response(void* data, int32_t unused) +{ + /* PPAPI main thread */ + PP_Resource res; + struct PepperInstance* const instance = (struct PepperInstance*)data; + + res = loader_interface->GetResponseInfo(instance->url_loader); + if (res) { + struct PP_Var status = + response_interface->GetProperty(res, PP_URLRESPONSEPROPERTY_STATUSCODE); + if (status.type == PP_VARTYPE_INT32) { + pruby_async_return_int(instance, status.value.as_int / 100 == 2 ? PP_OK : PP_ERROR_FAILED); + return; + } + else { + messaging_interface->PostMessage( + instance->instance, pruby_cstr_to_var("Unexpected type: ResponseInfoInterface::GetProperty")); + } + } + else { + messaging_interface->PostMessage( + instance->instance, pruby_cstr_to_var("Failed to open URL: URLLoaderInterface::GetResponseInfo")); + } + pruby_async_return_int(instance, PP_ERROR_FAILED); +} + + +int +rb_file_load_ok(const char *path) +{ + struct PepperInstance* const instance = GET_PEPPER_INSTANCE(); + if (path[0] == '.' && path[1] == '/') path += 2; + + instance->async_call_args = (void*)path; + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(load_ok_internal, instance), 0); + if (pthread_cond_wait(&instance->cond, &instance->mutex)) { + perror("pepper-ruby:pthread_cond_wait"); + return 0; + } + if (instance->async_call_result.as_int != PP_OK) { + fprintf(stderr, "Failed to open URL: %d: %s\n", + instance->async_call_result.as_int, path); + return 0; + } + + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(pruby_file_fetch_check_response, instance), 0); + if (pthread_cond_wait(&instance->cond, &instance->mutex)) { + perror("pepper-ruby:pthread_cond_wait"); + return 0; + } + return instance->async_call_result.as_int == PP_OK; +} + +/****************************************************************************** + * Overwrites rb_load_file + ******************************************************************************/ + +static void +load_file_internal(void* data, int32_t unused) +{ + /* PPAPI main thread */ + struct PepperInstance* const instance = (struct PepperInstance*)data; + const char *const path = (const char*)instance->async_call_args; + PP_Resource req; + int result; + + instance->url_loader = loader_interface->Create(instance->instance); + req = request_interface->Create(instance->instance); + request_interface->SetProperty( + req, PP_URLREQUESTPROPERTY_METHOD, pruby_cstr_to_var("GET")); + request_interface->SetProperty( + req, PP_URLREQUESTPROPERTY_URL, pruby_cstr_to_var(path)); + + result = loader_interface->Open( + instance->url_loader, req, + PP_MakeCompletionCallback(pruby_async_return_int, instance)); + if (result != PP_OK_COMPLETIONPENDING) { + pruby_async_return_int(instance, result); + } +} + +static void +load_file_read_contents_callback(void *data, int result) +{ + struct PepperInstance* const instance = (struct PepperInstance*)data; + if (result > 0) { + rb_str_buf_cat(instance->async_call_result.as_value, + instance->buf, result); + loader_interface->ReadResponseBody( + instance->url_loader, instance->buf, 1000, PP_MakeCompletionCallback(load_file_read_contents_callback, instance)); + } + else if (result == 0) { + pruby_async_return_value(data, instance->async_call_result.as_value); + } + else { + pruby_async_return_value(data, INT2FIX(result)); + } +} + +static void +load_file_read_contents(void *data, int result) +{ + struct PepperInstance* const instance = (struct PepperInstance*)data; + instance->async_call_result.as_value = rb_str_new(0, 0); + loader_interface->ReadResponseBody( + instance->url_loader, instance->buf, 1000, PP_MakeCompletionCallback(load_file_read_contents_callback, instance)); +} + +void* +rb_load_file(const char *path) +{ + const char *real_path; + struct PepperInstance* instance; + if (path[0] != '.' || path[1] != '/') path += 2; + + instance = GET_PEPPER_INSTANCE(); + + instance->async_call_args = (void*)path; + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(load_file_internal, instance), 0); + if (pthread_cond_wait(&instance->cond, &instance->mutex)) { + perror("pepper-ruby:pthread_cond_wait"); + return 0; + } + if (instance->async_call_result.as_int != PP_OK) { + fprintf(stderr, "Failed to open URL: %d: %s\n", + instance->async_call_result.as_int, path); + return 0; + } + + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(pruby_file_fetch_check_response, instance), 0); + if (pthread_cond_wait(&instance->cond, &instance->mutex)) { + perror("pepper-ruby:pthread_cond_wait"); + return 0; + } + if (instance->async_call_result.as_int != PP_OK) return 0; + + core_interface->CallOnMainThread( + 0, PP_MakeCompletionCallback(load_file_read_contents, instance), 0); + if (pthread_cond_wait(&instance->cond, &instance->mutex)) { + perror("pepper-ruby:pthread_cond_wait"); + return 0; + } + if (FIXNUM_P(instance->async_call_result.as_value)) { + return 0; + } + else if (RB_TYPE_P(instance->async_call_result.as_value, T_STRING)) { + VALUE str = instance->async_call_result.as_value; + return rb_compile_cstr(path, RSTRING_PTR(str), RSTRING_LEN(str), 0); + } + else { + return 0; + } +} diff --git a/nacl/resource.h b/nacl/resource.h new file mode 100644 index 0000000000..57ca53b093 --- /dev/null +++ b/nacl/resource.h @@ -0,0 +1,8 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * Author: yugui@google.com (Yugui Sonoda) + * */ +#ifndef RUBY_NACL_RESOURCE_H +#define RUBY_NACL_RESOURCE_H +int getrusage(int who, struct rusage *usage); +#endif diff --git a/nacl/select.h b/nacl/select.h new file mode 100644 index 0000000000..921721a2fd --- /dev/null +++ b/nacl/select.h @@ -0,0 +1,7 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: yugui@google.com (Yugui Sonoda) +#ifndef RUBY_NACL_SELECT_H +#define RUBY_NACL_SELECT_H +int select(int num_fds, fd_set *in_fds, fd_set *out_fds, + fd_set *ex_fds, struct timeval *timeout); +#endif diff --git a/nacl/signal.h b/nacl/signal.h new file mode 100644 index 0000000000..54832de1fe --- /dev/null +++ b/nacl/signal.h @@ -0,0 +1,6 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: yugui@google.com (Yugui Sonoda) +#ifndef RUBY_NACL_SIGNAL_H +#define RUBY_NACL_SIGNAL_H +int kill(pid_t pid, int signal); +#endif diff --git a/nacl/stat.h b/nacl/stat.h new file mode 100644 index 0000000000..7be40ada7c --- /dev/null +++ b/nacl/stat.h @@ -0,0 +1,10 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * Author: yugui@google.com (Yugui Sonoda) + * */ +#ifndef RUBY_NACL_STAT_H +#define RUBY_NACL_STAT_H +mode_t umask(mode_t mask); +struct stat; +int lstat(const char* path, struct stat* result); +#endif diff --git a/nacl/unistd.h b/nacl/unistd.h new file mode 100644 index 0000000000..1c97390c63 --- /dev/null +++ b/nacl/unistd.h @@ -0,0 +1,9 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: yugui@google.com (Yugui Sonoda) +#ifndef RUBY_NACL_UNISTD_H +#define RUBY_NACL_UNISTD_H +int seteuid(pid_t pid); +int setegid(pid_t pid); +int truncate(const char* path, off_t new_size); +int ftruncate(int fd, off_t new_size); +#endif diff --git a/nacl/utime.h b/nacl/utime.h new file mode 100644 index 0000000000..96910051e4 --- /dev/null +++ b/nacl/utime.h @@ -0,0 +1,11 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * Author: yugui@google.com (Yugui Sonoda) + */ + +#ifndef RUBY_NACL_UTIME_H +#define RUBY_NACL_UTIME_H +#include +int utime(const char *filename, const struct utimbuf *times); +int utimes(const char *filename, const struct timeval times[2]); +#endif diff --git a/process.c b/process.c index c5e6eb72bf..cf9144f716 100644 --- a/process.c +++ b/process.c @@ -62,6 +62,11 @@ #endif #include +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/stat.h" +# include "nacl/unistd.h" +#endif + #ifdef HAVE_SYS_TIMES_H #include @@ -1042,7 +1047,7 @@ security(const char *str) } } -#ifdef HAVE_FORK +#if defined(HAVE_FORK) && !defined(__native_client__) #define try_with_sh(prog, argv) ((saved_errno == ENOEXEC) ? exec_with_sh((prog), (argv)) : (void)0) static void exec_with_sh(const char *prog, char **argv) @@ -1061,13 +1066,20 @@ exec_with_sh(const char *prog, char **argv) #define ALLOC_ARGV_WITH_STR(n, v, s, l) \ (char **)(((s) = ALLOCV_N(char, (v), ARGV_SIZE(n) + (l)) + ARGV_SIZE(n)) - ARGV_SIZE(n)) +#ifdef __native_client__ +static int +proc_exec_v(char **argv, const char *prog) +{ + rb_notimplement(); +} +#else static int proc_exec_v(char **argv, const char *prog) { char fbuf[MAXPATHLEN]; -#if defined(__EMX__) || defined(OS2) +# if defined(__EMX__) || defined(OS2) char **new_argv = NULL; -#endif +# endif if (!prog) prog = argv[0]; @@ -1077,9 +1089,9 @@ proc_exec_v(char **argv, const char *prog) return -1; } -#if defined(__EMX__) || defined(OS2) +# if defined(__EMX__) || defined(OS2) { -#define COMMAND "cmd.exe" +# define COMMAND "cmd.exe" char *extension; if ((extension = strrchr(prog, '.')) != NULL && STRCASECMP(extension, ".bat") == 0) { @@ -1104,18 +1116,19 @@ proc_exec_v(char **argv, const char *prog) } } } -#endif /* __EMX__ */ +# endif /* __EMX__ */ before_exec(); execv(prog, argv); preserving_errno(try_with_sh(prog, argv); after_exec()); -#if defined(__EMX__) || defined(OS2) +# if defined(__EMX__) || defined(OS2) if (new_argv) { xfree(new_argv[0]); xfree(new_argv); } -#endif +# endif return -1; } +#endif int rb_proc_exec_n(int argc, VALUE *argv, const char *prog) @@ -1137,6 +1150,13 @@ rb_proc_exec_n(int argc, VALUE *argv, const char *prog) return ret; } +#ifdef __native_client__ +int +rb_proc_exec(const char *str) +{ + rb_notimplement(); +} +#else int rb_proc_exec(const char *str) { @@ -1206,6 +1226,7 @@ rb_proc_exec(const char *str) return ret; #endif /* _WIN32 */ } +#endif enum { EXEC_OPTION_PGROUP, diff --git a/signal.c b/signal.c index e1b757e8c3..e946337ae7 100644 --- a/signal.c +++ b/signal.c @@ -18,6 +18,10 @@ #include #include "atomic.h" +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/signal.h" +#endif + #ifdef NEED_RUBY_ATOMIC_EXCHANGE rb_atomic_t ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val) @@ -417,8 +421,6 @@ typedef RETSIGTYPE ruby_sigaction_t(int); #define SIGINFO_ARG #endif -#ifdef POSIX_SIGNAL - #ifdef USE_SIGALTSTACK /* alternate stack for SIGSEGV */ void @@ -437,6 +439,7 @@ rb_register_sigaltstack(rb_thread_t *th) } #endif /* USE_SIGALTSTACK */ +#ifdef POSIX_SIGNAL static sighandler_t ruby_signal(int signum, sighandler_t handler) { diff --git a/thread.c b/thread.c index 27504e5e2e..ccf350eee6 100644 --- a/thread.c +++ b/thread.c @@ -2420,6 +2420,11 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src) memcpy(dst->fdset, src->fdset, size); } +#ifdef __native_client__ +int select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +#endif + int rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout) { @@ -3298,7 +3303,9 @@ mutex_free(void *ptr) if (err) rb_bug("%s", err); } native_mutex_destroy(&mutex->lock); +#ifdef HAVE_PTHREAD_COND_INITIALIZE native_cond_destroy(&mutex->cond); +#endif } ruby_xfree(ptr); } @@ -3333,7 +3340,9 @@ mutex_alloc(VALUE klass) obj = TypedData_Make_Struct(klass, rb_mutex_t, &mutex_data_type, mutex); native_mutex_initialize(&mutex->lock); +#ifdef HAVE_PTHREAD_COND_INITIALIZE native_cond_initialize(&mutex->cond, RB_CONDATTR_CLOCK_MONOTONIC); +#endif return obj; } @@ -4781,4 +4790,3 @@ rb_reset_coverages(void) GET_VM()->coverages = Qfalse; rb_remove_event_hook(update_coverage); } - diff --git a/thread_pthread.c b/thread_pthread.c index de8325e087..61b93e1edf 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -27,6 +27,9 @@ #if HAVE_SYS_PRCTL_H #include #endif +#if defined(__native_client__) && defined(NACL_NEWLIB) +# include "nacl/select.h" +#endif static void native_mutex_lock(pthread_mutex_t *lock); static void native_mutex_unlock(pthread_mutex_t *lock); @@ -137,9 +140,11 @@ static void gvl_init(rb_vm_t *vm) { native_mutex_initialize(&vm->gvl.lock); +#ifdef HAVE_PTHREAD_CONDATTR_INIT native_cond_initialize(&vm->gvl.cond, RB_CONDATTR_CLOCK_MONOTONIC); native_cond_initialize(&vm->gvl.switch_cond, RB_CONDATTR_CLOCK_MONOTONIC); native_cond_initialize(&vm->gvl.switch_wait_cond, RB_CONDATTR_CLOCK_MONOTONIC); +#endif vm->gvl.acquired = 0; vm->gvl.waiting = 0; vm->gvl.need_yield = 0; @@ -148,9 +153,11 @@ gvl_init(rb_vm_t *vm) static void gvl_destroy(rb_vm_t *vm) { +#ifdef HAVE_PTHREAD_CONDATTR_INIT native_cond_destroy(&vm->gvl.switch_wait_cond); native_cond_destroy(&vm->gvl.switch_cond); native_cond_destroy(&vm->gvl.cond); +#endif native_mutex_destroy(&vm->gvl.lock); } @@ -232,6 +239,9 @@ native_mutex_destroy(pthread_mutex_t *lock) } } +#ifdef HAVE_PTHREAD_CONDATTR_INIT +int pthread_condattr_init(pthread_condattr_t *attr); + static void native_cond_initialize(rb_thread_cond_t *cond, int flags) { @@ -266,6 +276,7 @@ native_cond_destroy(rb_thread_cond_t *cond) rb_bug_errno("pthread_cond_destroy", r); } } +#endif /* * In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return @@ -439,20 +450,26 @@ Init_native_thread(void) #ifdef USE_SIGNAL_THREAD_LIST native_mutex_initialize(&signal_thread_list_lock); #endif +#ifndef __native_client__ posix_signal(SIGVTALRM, null_func); +#endif } static void native_thread_init(rb_thread_t *th) { +#ifdef HAVE_PTHREAD_CONDATTR_INIT native_cond_initialize(&th->native_thread_data.sleep_cond, RB_CONDATTR_CLOCK_MONOTONIC); +#endif ruby_thread_set_native(th); } static void native_thread_destroy(rb_thread_t *th) { +#ifdef HAVE_PTHREAD_CONDATTR_INIT native_cond_destroy(&th->native_thread_data.sleep_cond); +#endif } #define USE_THREAD_CACHE 0 @@ -1197,6 +1214,7 @@ thread_timer(void *p) static void rb_thread_create_timer_thread(void) { +#ifndef __native_client__ if (!timer_thread_id) { pthread_attr_t attr; int err; @@ -1256,6 +1274,7 @@ rb_thread_create_timer_thread(void) } pthread_attr_destroy(&attr); } +#endif } static int diff --git a/util.c b/util.c index fca9e67541..733045daa4 100644 --- a/util.c +++ b/util.c @@ -462,6 +462,15 @@ ruby_strdup(const char *str) return tmp; } +#ifdef __native_client__ +char * +ruby_getcwd(void) +{ + char *buf = xmalloc(2); + strcpy(buf, "."); + return buf; +} +#else char * ruby_getcwd(void) { @@ -490,6 +499,7 @@ ruby_getcwd(void) #endif return buf; } +#endif /**************************************************************** * diff --git a/vm_core.h b/vm_core.h index 5a9e6bc646..e87d5aed0b 100644 --- a/vm_core.h +++ b/vm_core.h @@ -92,6 +92,10 @@ #endif #endif +#ifdef __native_client__ +#undef OPT_DIRECT_THREADED_CODE +#endif + /* call threaded code */ #if OPT_CALL_THREADED_CODE #if OPT_DIRECT_THREADED_CODE diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 2222b78b06..710e146915 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -281,6 +281,7 @@ XRUBY = $(MINIRUBY) !else XRUBY = $(RUNRUBY) !endif +BTESTRUBY = $(MINIRUBY) !ifndef RUBY RUBY = ruby !endif -- cgit v1.2.3