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