Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
  • Loading branch information...
commit 76bc2d1ed7f13fb329c33f48756ea3c24c59a6ea 1 parent 0a7aada
Yuki Yugui Sonoda yugui authored
49 ChangeLog
View
@@ -1,6 +1,53 @@
+Thu May 17 11:33:07 2012 Yuki Yugui Sonoda <yugui@google.com>
+
+ 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 <nobu@ruby-lang.org>
- * 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.
2  Makefile.in
View
@@ -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@
2  common.mk
View
@@ -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
130 configure.in
View
@@ -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 <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@include <sys/time.h>
@%:@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 <sys/types.h>]) 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"],
4 dir.c
View
@@ -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 <errno.h>
15 dln.c
View
@@ -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 */
50 ext/Setup.nacl
View
@@ -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
2  ext/extmk.rb
View
@@ -530,7 +530,7 @@ def initialize(src)
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)
2  ext/pty/extconf.rb
View
@@ -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")
22 file.c
View
@@ -60,6 +60,13 @@ int flock(int, int);
#include <sys/types.h>
#include <sys/stat.h>
+#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 <sys/mkdev.h>
#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)
{
6 gc.c
View
@@ -33,6 +33,12 @@
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#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__
#include <windows.h>
4 gc.h
View
@@ -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));
30 io.c
View
@@ -28,7 +28,9 @@
#if defined HAVE_NET_SOCKET_H
# include <net/socket.h>
#elif defined HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
+# ifndef __native_client__
+# include <sys/socket.h>
+# 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 <sys/ioctl.h>
#endif
+#if defined(__native_client__) && defined(NACL_NEWLIB)
+# include "nacl/ioctl.h"
+#endif
#if defined(HAVE_FCNTL_H) || defined(_WIN32)
#include <fcntl.h>
#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 {
1  iseq.c
View
@@ -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);
}
-
3  missing/flock.c
View
@@ -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.
60 nacl/GNUmakefile.in
View
@@ -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)
34 nacl/README.nacl
View
@@ -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
70 nacl/create_nmf.rb
View
@@ -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
+
+
15 nacl/dirent.h
View
@@ -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
150 nacl/example.html
View
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Ruby Example</title>
+
+ <script type="text/javascript">
+ RubyModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+ rubyReady = false;
+
+ // Indicate load success.
+ function moduleDidLoad() {
+ RubyModule = document.getElementById('ruby');
+ form = document.getElementById('source-form');
+ form.style.display = "block";
+ updateStatus('SUCCESS');
+ }
+
+ function evalSource() {
+ if (rubyReady) {
+ RubyModule.postMessage('eval:' + document.getElementById("source").value);
+ } else {
+ throw "Not yet ready";
+ }
+ return false;
+ }
+
+ function RubyError(message) {
+ this.message = message;
+ this.toString = function() {
+ return message;
+ }
+ }
+
+ function FatalError(message) {
+ this.message = message;
+ }
+
+ // The 'message' event handler. This handler is fired when the NaCl module
+ // posts a message to the browser by calling PPB_Messaging.PostMessage()
+ // (in C) or pp::Instance.PostMessage() (in C++). This implementation
+ // simply displays the content of the message in an alert panel.
+ function handleMessage(message_event) {
+ var raw = message_event.data;
+ var components;
+ if (raw.indexOf("error") == 0) {
+ components = raw.split(":", 2);
+ throw new RubyError(components[1]);
+ } else if (raw.indexOf("return") == 0) {
+ components = raw.split(":", 2);
+ document.getElementById("result").value = components[1];
+ } else if (raw == "rubyReady") {
+ rubyReady = true;
+ } else {
+ throw new FatalError(raw);
+ }
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ if (RubyModule == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client Module Ruby</h1>
+<p>
+ <!-- Load the published .nexe. This includes the 'nacl' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL
+ for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'nacl' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: Since this NaCl module does not use any real-estate in the browser,
+ it's width and height are set to 0.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="ruby"
+ width="0" height="0"
+ src="ruby.nmf"
+ type="application/x-nacl" />
+ <form id="source-form" action="#" method="post" style="display:none"
+ onsubmit="evalSource(); return false">
+ <table>
+ <tbody>
+ <tr>
+ <th>Source</th>
+ <td>
+ <textarea rows="10" cols="80" id="source"></textarea>
+ <input type="submit" />
+ </td>
+ </tr>
+ <tr>
+ <th>Result</th>
+ <td>
+ <textarea rows="10" cols="80" id="result"></textarea>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+ </div>
+</p>
+
+<h2>Status</h2>
+<div id="status_field">NO-STATUS</div>
+</body>
+</html>
7 nacl/ioctl.h
View
@@ -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
61 nacl/nacl-config.rb
View
@@ -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
109 nacl/package.rb
View
@@ -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
924 nacl/pepper_main.c
View
@@ -0,0 +1,924 @@
+/******************************************************************************
+ Copyright 2012 Google Inc. All Rights Reserved.
+ Author: yugui@google.com (Yugui Sonoda)
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#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 <embed> 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 <embed> tag, for example:
+ * <embed id="nacl_module" dimensions="2">
+ * 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 <embed> 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;
+ }
+}
8 nacl/resource.h
View
@@ -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
7 nacl/select.h
View
@@ -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
6 nacl/signal.h
View
@@ -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
10 nacl/stat.h
View
@@ -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
9 nacl/unistd.h
View
@@ -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
11 nacl/utime.h
View
@@ -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 <utime.h>
+int utime(const char *filename, const struct utimbuf *times);
+int utimes(const char *filename, const struct timeval times[2]);
+#endif
37 process.c
View
@@ -62,6 +62,11 @@
#endif
#include <sys/stat.h>
+#if defined(__native_client__) && defined(NACL_NEWLIB)
+# include "nacl/stat.h"
+# include "nacl/unistd.h"
+#endif
+
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
@@ -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,
7 signal.c
View
@@ -18,6 +18,10 @@
#include <errno.h>
#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)
{
10 thread.c
View
@@ -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);
}
-
19 thread_pthread.c
View
@@ -27,6 +27,9 @@
#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#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)