[#44776] [ruby-trunk - Bug #6385][Open] mtime vie File.stat(filename).utime vs File.open(filename, 'r').mtime in Windows — "kolmanv (Kolman Vornovitsky)" <kolmanv@...>

9 messages 2012/05/01

[#44782] [ruby-trunk - Bug #6387][Open] 1.9.3p194 crashed on require in ubuntu — "ywen (Yi Wen)" <hayafirst@...>

12 messages 2012/05/01

[#44795] [ruby-trunk - Bug #6391][Open] Segment Fault while execute make_encmake.rb for Ruby 1.9.3 P194 ( MinGW64) — "raylinn@... (ray linn)" <raylinn@...>

13 messages 2012/05/02

[#44911] [ruby-trunk - Bug #6408][Open] DelegateClass#eql? and <=> don't work as expected — "tenderlovemaking (Aaron Patterson)" <aaron@...>

11 messages 2012/05/06

[#44951] [ruby-trunk - Feature #6414][Open] Destructuring Assignment — "edtsech (Edward Tsech)" <edtsech@...>

14 messages 2012/05/08

[#44958] [ruby-trunk - Feature #6418][Assigned] Supporing a subset of ANSI escape code on Windows — "usa (Usaku NAKAMURA)" <usa@...>

11 messages 2012/05/09

[#45035] [ruby-trunk - Bug #6433][Open] rb_thread_blocking_region(): ubf() function is executed with GVL — ibc (Iñaki Baz Castillo) <ibc@...>

12 messages 2012/05/14

[#45180] [ruby-trunk - Feature #6478][Open] BasicObject#__class__ — "trans (Thomas Sawyer)" <transfire@...>

14 messages 2012/05/22

[#45193] [ruby-trunk - Feature #6482][Open] Add URI requested to Net::HTTP request and response objects — "drbrain (Eric Hodel)" <drbrain@...7.net>

16 messages 2012/05/23

[#45198] [ruby-trunk - Feature #6483][Open] parametric map — "prijutme4ty (Ilya Vorontsov)" <prijutme4ty@...>

14 messages 2012/05/23

[#45222] [ruby-trunk - Feature #6492][Open] Inflate all HTTP Content-Encoding: deflate, gzip, x-gzip responses by default — "drbrain (Eric Hodel)" <drbrain@...7.net>

23 messages 2012/05/24

[#45252] [ruby-trunk - Feature #6499][Open] Array::zip — "prijutme4ty (Ilya Vorontsov)" <prijutme4ty@...>

14 messages 2012/05/26

[#45272] [ruby-trunk - Feature #6503][Open] Support for the NPN extension to TLS/SSL — "igrigorik (Ilya Grigorik)" <ilya@...>

13 messages 2012/05/27

[#45316] [ruby-trunk - Feature #6515][Open] array.c: added method that verifies if an Array is part of another — "lellisga (Li Ellis Galardo)" <lellisga@...>

14 messages 2012/05/30

[ruby-core:45073] [PATCH] Ruby's port to NativeClient

From: Yuki Yugui Sonoda <yugui@...>
Date: 2012-05-16 03:34:09 UTC
List: ruby-core #45073
Hello Matz and rubyists,

Let me greet you from Google.
Recently I wrote the attached patches:

(1) Fixes --with-static-linked-ext

--with-static-linked-ext configure flag has been broken for years.
This patch fixes it.
I'll commit this patch later.


(2) Ruby's port to NativeClient
NativeClient (a.k.a NaCl) is a secure environment for running native
binaries on browsers.
 https://developers.google.com/native-client/

This patch is a proof of concept version of porting Ruby to NaCl.
This patch contains an example version of Ruby interpreter that
communicates with browsers via Pepper API. You can evaluate
expressions, require standard libraries and extensions.
Some features of Ruby, e.g. fork or socket are not available due to
limitation of NaCl. Some other features are temporarily disabled, e.g.
threading.
Unfortunately this port does not yet pass even btest. But it can be fixed later.

I'd like to commit this patch too. May I, Matz?

-- 
Regards,

Yuki Sonoda (Yugui)
yugui@google.com

Attachments (2)

nacl.20120515.diff (70 KB, text/x-diff)
diff --git a/configure.in b/configure.in
index ee7bb05..43f1477 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 <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
     ])
@@ -2600,6 +2696,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 +2776,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 8476c0e..ac4b1c6 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 <errno.h>
 
diff --git a/dln.c b/dln.c
index 38e1dcd..cc15370 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 0000000..3e252fd
--- /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 dc4be84..43929fb 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -62,6 +62,11 @@ def system(*args)
   super
 end
 
+def system(*args)
+  puts args.map{|arg| Shellwords.escape(arg)}.join(' ') if verbose?
+  super
+end
+
 def extract_makefile(makefile, keep = true)
   m = File.read(makefile)
   if !(target = m[/^TARGET[ \t]*=[ \t]*(\S*)/, 1])
@@ -657,6 +662,13 @@ if $configure_only and $command_output
     config_string('RANLIB') do |ranlib|
       mf.puts "\t-$(Q)#{ranlib} $@ 2> /dev/null || true"
     end
+    mf.puts "ext/extinit.#{$OBJEXT}:\n\t$(Q)$(MAKE) $(MFLAGS) V=$(V) $@"
+    mf.puts "ext/libext.#{$LIBEXT}: $(EXTOBJS)"
+    mf.puts "\t$(ECHO) linking static-library $@"
+    mf.puts "\t$(Q) $(AR) #{config_string('ARFLAGS') || 'cru '}$@ $(EXTOBJS)"
+    config_string('RANLIB') do |ranlib|
+      mf.puts "\t-$(Q)#{ranlib} $@ 2> /dev/null || true"
+    end
     mf.puts
     exec = config_string("exec") {|str| str + " "}
     targets.each do |tgt|
diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb
index 3d6d0f1..890269f 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 59de83c..bb1ab37 100644
--- a/file.c
+++ b/file.c
@@ -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)
 {
diff --git a/gc.c b/gc.c
index a5ada1c..4a96127 100644
--- a/gc.c
+++ b/gc.c
@@ -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>
diff --git a/gc.h b/gc.h
index df2cf7d..094b941 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 5997c29..ae01949 100644
--- a/io.c
+++ b/io.c
@@ -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,7 +1506,7 @@ rb_io_set_pos(VALUE io, VALUE offset)
 
 static void clear_readconv(rb_io_t *fptr);
 
-#ifdef HAVE_FSYNC
+#if defined(HAVE_FSYNC) || !defined(_WIN32)
 
 /*
  *  call-seq:
@@ -1542,6 +1546,7 @@ rb_io_rewind(VALUE io)
 
     return INT2FIX(0);
 }
+#endif
 
 static int
 io_fillbuf(rb_io_t *fptr)
@@ -1631,6 +1636,7 @@ rb_io_eof(VALUE io)
     return Qfalse;
 }
 
+#ifdef HAVE_FSYNC
 /*
  *  call-seq:
  *     ios.sync    -> true or false
@@ -1709,14 +1715,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 +8211,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 ec9ac5f..ff0bcad 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 763c49d..435d66a 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 0000000..46912c8
--- /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)$(BASERUBY) -I. -r$(arch)-fake $(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)$(BASERUBY) -I. -r$(arch)-fake $(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 0000000..6001cd3
--- /dev/null
+++ b/nacl/README.nacl
@@ -0,0 +1,26 @@
+=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
+
+= 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 0000000..cdfe7ad
--- /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 0000000..31bdad3
--- /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 0000000..3cc3329
--- /dev/null
+++ b/nacl/example.html
@@ -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>
diff --git a/nacl/ioctl.h b/nacl/ioctl.h
new file mode 100644
index 0000000..0a18eeb
--- /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 0000000..3316165
--- /dev/null
+++ b/nacl/nacl-config.rb
@@ -0,0 +1,32 @@
+#!/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
+
+  ARCH = config['host_alias'].sub(/-gnu$|-newlib$/, '').sub(/-nacl$/, '').
+    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']
+  CREATE_NMF = [
+    File.join(config['NACL_SDK_ROOT'], 'build_tools', 'nacl_sdk_scons', 'site_tools', 'create_nmf.py'),
+    File.join(config['NACL_SDK_ROOT'], 'tools', 'create_nmf.py')
+  ].find{|path| File.exist?(path) }
+  HOST_LIB = File.join(config['NACL_SDK_ROOT'], 'toolchain', config['NACL_TOOLCHAIN'], HOST, "lib#{lib_suffix}")
+
+  INSTALL_PROGRAM = config['INSTALL_PROGRAM']
+  INSTALL_LIBRARY = config['INSTALL_DATA']
+
+  def newlib?
+    RbConfig::CONFIG['NACL_SDK_VARIANT'] == 'newlib'
+  end
+end
diff --git a/nacl/package.rb b/nacl/package.rb
new file mode 100644
index 0000000..f4f50f2
--- /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 0000000..a05a972
--- /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 <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;
+  }
+}
diff --git a/nacl/resource.h b/nacl/resource.h
new file mode 100644
index 0000000..57ca53b
--- /dev/null
+++ b/nacl/resource.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ * Author: yugui@google.com (Yugui Sonoda)
+ * */
+#ifndef RUBY_NACL_RESOURCE_H
+#define RUBY_NACL_RESOURCE_H
+int getrusage(int who, struct rusage *usage);
+#endif
diff --git a/nacl/select.h b/nacl/select.h
new file mode 100644
index 0000000..921721a
--- /dev/null
+++ b/nacl/select.h
@@ -0,0 +1,7 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Author: yugui@google.com (Yugui Sonoda)
+#ifndef RUBY_NACL_SELECT_H
+#define RUBY_NACL_SELECT_H
+int select(int num_fds, fd_set *in_fds, fd_set *out_fds,
+           fd_set *ex_fds, struct timeval *timeout);
+#endif
diff --git a/nacl/signal.h b/nacl/signal.h
new file mode 100644
index 0000000..54832de
--- /dev/null
+++ b/nacl/signal.h
@@ -0,0 +1,6 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Author: yugui@google.com (Yugui Sonoda)
+#ifndef RUBY_NACL_SIGNAL_H
+#define RUBY_NACL_SIGNAL_H
+int kill(pid_t pid, int signal);
+#endif
diff --git a/nacl/stat.h b/nacl/stat.h
new file mode 100644
index 0000000..7be40ad
--- /dev/null
+++ b/nacl/stat.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ * Author: yugui@google.com (Yugui Sonoda)
+ * */
+#ifndef RUBY_NACL_STAT_H
+#define RUBY_NACL_STAT_H
+mode_t umask(mode_t mask);
+struct stat;
+int lstat(const char* path, struct stat* result);
+#endif
diff --git a/nacl/unistd.h b/nacl/unistd.h
new file mode 100644
index 0000000..1c97390
--- /dev/null
+++ b/nacl/unistd.h
@@ -0,0 +1,9 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Author: yugui@google.com (Yugui Sonoda)
+#ifndef RUBY_NACL_UNISTD_H
+#define RUBY_NACL_UNISTD_H
+int seteuid(pid_t pid);
+int setegid(pid_t pid);
+int truncate(const char* path, off_t new_size);
+int ftruncate(int fd, off_t new_size);
+#endif
diff --git a/nacl/utime.h b/nacl/utime.h
new file mode 100644
index 0000000..9691005
--- /dev/null
+++ b/nacl/utime.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ * Author: yugui@google.com (Yugui Sonoda)
+ */
+
+#ifndef RUBY_NACL_UTIME_H
+#define RUBY_NACL_UTIME_H
+#include <utime.h>
+int utime(const char *filename, const struct utimbuf *times);
+int utimes(const char *filename, const struct timeval times[2]);
+#endif
diff --git a/process.c b/process.c
index c5e6eb7..cf9144f 100644
--- a/process.c
+++ b/process.c
@@ -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,
diff --git a/signal.c b/signal.c
index e1b757e..e946337 100644
--- a/signal.c
+++ b/signal.c
@@ -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)
 {
diff --git a/thread.c b/thread.c
index 27504e5..ccf350e 100644
--- a/thread.c
+++ b/thread.c
@@ -2420,6 +2420,11 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
     memcpy(dst->fdset, src->fdset, size);
 }
 
+#ifdef __native_client__
+int select(int nfds, fd_set *readfds, fd_set *writefds,
+           fd_set *exceptfds, struct timeval *timeout);
+#endif
+
 int
 rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
 {
@@ -3298,7 +3303,9 @@ mutex_free(void *ptr)
 	    if (err) rb_bug("%s", err);
 	}
 	native_mutex_destroy(&mutex->lock);
+#ifdef HAVE_PTHREAD_COND_INITIALIZE
 	native_cond_destroy(&mutex->cond);
+#endif
     }
     ruby_xfree(ptr);
 }
@@ -3333,7 +3340,9 @@ mutex_alloc(VALUE klass)
 
     obj = TypedData_Make_Struct(klass, rb_mutex_t, &mutex_data_type, mutex);
     native_mutex_initialize(&mutex->lock);
+#ifdef HAVE_PTHREAD_COND_INITIALIZE
     native_cond_initialize(&mutex->cond, RB_CONDATTR_CLOCK_MONOTONIC);
+#endif
     return obj;
 }
 
@@ -4781,4 +4790,3 @@ rb_reset_coverages(void)
     GET_VM()->coverages = Qfalse;
     rb_remove_event_hook(update_coverage);
 }
-
diff --git a/thread_pthread.c b/thread_pthread.c
index de8325e..61b93e1 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -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)
 {
+#ifndef __native_client__
     if (!timer_thread_id) {
 	pthread_attr_t attr;
 	int err;
@@ -1256,6 +1274,7 @@ rb_thread_create_timer_thread(void)
 	}
 	pthread_attr_destroy(&attr);
     }
+#endif
 }
 
 static int
diff --git a/util.c b/util.c
index fca9e67..733045d 100644
--- a/util.c
+++ b/util.c
@@ -462,6 +462,15 @@ ruby_strdup(const char *str)
     return tmp;
 }
 
+#ifdef __native_client__
+char *
+ruby_getcwd(void)
+{
+    char *buf = xmalloc(2);
+    strcpy(buf, ".");
+    return buf;
+}
+#else
 char *
 ruby_getcwd(void)
 {
@@ -490,6 +499,7 @@ ruby_getcwd(void)
 #endif
     return buf;
 }
+#endif
 
 /****************************************************************
  *
diff --git a/vm_core.h b/vm_core.h
index 5a9e6bc..e87d5ae 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -92,6 +92,10 @@
 #endif
 #endif
 
+#ifdef __native_client__
+#undef OPT_DIRECT_THREADED_CODE
+#endif
+
 /* call threaded code */
 #if    OPT_CALL_THREADED_CODE
 #if    OPT_DIRECT_THREADED_CODE
static-build.20120515.diff (33.8 KB, text/x-diff)
diff --git a/Makefile.in b/Makefile.in
index bae0ce1..721dc2a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -16,6 +16,7 @@ PLATFORM_DIR = @PLATFORM_DIR@
 
 CC = @CC@
 CPP = @CPP@
+LD = @LD@
 YACC = bison
 PURIFY =
 AUTOCONF = autoconf
@@ -70,6 +71,8 @@ DLDFLAGS = @LIBRUBY_DLDFLAGS@ $(XLDFLAGS) $(ARCH_FLAG)
 SOLIBS = @SOLIBS@
 MAINLIBS = @MAINLIBS@
 ARCHMINIOBJS = @MINIOBJS@
+ENCOBJS = @ENCOBJS@
+EXTOBJS = @EXTOBJS@
 BUILTIN_ENCOBJS = @BUILTIN_ENCOBJS@
 BUILTIN_TRANSSRCS = @BUILTIN_TRANSSRCS@
 BUILTIN_TRANSOBJS = @BUILTIN_TRANSOBJS@
@@ -175,7 +178,7 @@ miniruby$(EXEEXT):
 $(PROGRAM):
 		@$(RM) $@
 		$(ECHO) linking $@
-		$(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINLIBS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) $(OUTFLAG)$@
+		$(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINLIBS) $(MAINOBJ) $(EXTOBJS) $(ENCOBJS) $(LIBRUBYARG) $(LIBS) $(EXTLIBS) $(OUTFLAG)$@
 		$(Q) $(POSTLINK)
 
 # We must `rm' the library each time this rule is invoked because "updating" a
@@ -186,8 +189,9 @@ $(LIBRUBY_A):
 		$(ECHO) linking static-library $@
 		$(Q) $(AR) $(ARFLAGS) $@ $(OBJS) $(DMYEXT)
 		@-$(RANLIB) $@ 2> /dev/null || true
+		$(ECHO) verifying static-library $@
 		@$(PURIFY) $(CC) $(XLDFLAGS) $(MAINOBJ) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)conftest$(EXEEXT) $(LDFLAGS)
-		@$(RM) conftest$(EXEEXT)
+		@$(RM) conftset$(EXEEXT) conftest.c
 
 $(LIBRUBY_SO):
 		@-$(PRE_LIBRUBY_UPDATE)
@@ -311,7 +315,8 @@ enc/unicode/name2ctype.h: enc/unicode/name2ctype.kwd
 	$(Q) $(CPP) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -E $< > $@
 
 clean-local::
-	$(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output
+	$(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output \
+		enc/encinit.c enc/encinit.$(OBJEXT)
 	-$(Q)$(RM) $(pkgconfig_DATA)
 
 distclean-local::
@@ -335,8 +340,10 @@ clean-ext distclean-ext realclean-ext::
 		$(RMDIRS) "ext/$$dir" 2> /dev/null || true;; \
 	    esac; \
 	done
+	-$(Q)$(RM) ext/extinit.$(OBJEXT)
 
 distclean-ext realclean-ext::
+	-$(Q)$(RM) ext/extinit.c
 	-$(Q)$(RMDIR) ext 2> /dev/null || true
 
 clean-extout:
@@ -362,6 +369,8 @@ ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP)
 	$(ECHO) compiling $@
 	$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c ext/extinit.c
 
+enc/encinit.$(OBJEXT): enc/encinit.c $(SETUP)
+
 up::
 	@$(CHDIR) "$(srcdir)" && LC_TIME=C exec $(VCSUP)
 
diff --git a/common.mk b/common.mk
index 88de9e9..2881329 100644
--- a/common.mk
+++ b/common.mk
@@ -32,7 +32,6 @@ ID_H_TARGET   = -id.h-
 DMYEXT	      = dmyext.$(OBJEXT)
 NORMALMAINOBJ = main.$(OBJEXT)
 MAINOBJ       = $(NORMALMAINOBJ)
-EXTOBJS	      = 
 DLDOBJS	      = $(DMYEXT)
 MINIOBJS      = $(ARCHMINIOBJS) dmyencoding.$(OBJEXT) dmyversion.$(OBJEXT) miniprelude.$(OBJEXT)
 ENC_MK        = enc.mk
@@ -141,7 +140,7 @@ COMPILE_PRELUDE = $(MINIRUBY) -I$(srcdir) $(srcdir)/tool/compile_prelude.rb
 
 all: showflags main docs
 
-main: showflags encs exts
+main: showflags $(EXTSTATIC:static=lib)encs exts
 	@$(NULLCMD)
 
 .PHONY: showflags
@@ -168,12 +167,13 @@ exts: build-ext
 
 EXTS_MK = exts.mk
 $(EXTS_MK): $(MKFILES) incs $(PREP) $(RBCONFIG) $(LIBRUBY)
+	$(ECHO) generating makefile $@
 	$(Q)$(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --command-output=$(EXTS_MK) $(EXTMK_ARGS) configure
 
 configure-ext: $(EXTS_MK)
 
 build-ext: $(EXTS_MK)
-	$(Q)$(MAKE) -f $(EXTS_MK) $(MFLAGS)
+	$(Q)$(MAKE) -f $(EXTS_MK) $(MFLAGS) $(EXTSTATIC)
 
 $(MKMAIN_CMD): $(MKFILES) incs $(PREP) $(RBCONFIG) $(LIBRUBY)
 	$(Q)$(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --command-output=$@ $(EXTMK_ARGS)
@@ -206,7 +206,7 @@ Doxyfile: $(srcdir)/template/Doxyfile.tmpl $(PREP) $(srcdir)/tool/generic_erb.rb
 program: showflags $(PROGRAM)
 wprogram: showflags $(WPROGRAM)
 
-$(PROGRAM): $(LIBRUBY) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(SETUP) $(PREP)
+$(PROGRAM): $(LIBRUBY) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(ENCOBJS) $(SETUP) $(PREP)
 
 $(LIBRUBY_A):	$(OBJS) $(MAINOBJ) $(DMYEXT) $(ARCHFILE)
 
@@ -519,17 +519,21 @@ test-rubyspec: test-rubyspec-precheck
 	$(RUNRUBY) $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec $(MSPECOPT)
 
 encs: enc trans
-encs enc trans: showflags $(ENC_MK) $(LIBRUBY) $(PREP)
+libencs: libenc libtrans
+encs enc trans libencs libenc libtrans: showflags $(ENC_MK) $(LIBRUBY) $(PREP)
 	$(ECHO) making $@
-	$(Q) $(MAKE) -f $(ENC_MK) RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" $(MFLAGS) $@
+	$(Q) $(MAKE) -f $(ENC_MK) V="$(V)" \
+		RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" \
+		$(MFLAGS) $@
 
-enc: {$(VPATH)}encdb.h
-trans: {$(VPATH)}transdb.h
+
+libenc enc: {$(VPATH)}encdb.h
+libtrans trans: {$(VPATH)}transdb.h
 
 $(ENC_MK): $(srcdir)/enc/make_encmake.rb $(srcdir)/enc/Makefile.in $(srcdir)/enc/depend \
-	$(srcdir)/lib/mkmf.rb $(RBCONFIG)
+	$(srcdir)/enc/encinit.c.erb $(srcdir)/lib/mkmf.rb $(RBCONFIG)
 	$(ECHO) generating $@
-	$(Q) $(MINIRUBY) $(srcdir)/enc/make_encmake.rb --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" $@ $(ENCS)
+	$(Q) $(MINIRUBY) $(srcdir)/enc/make_encmake.rb --builtin-encs="$(BUILTIN_ENCOBJS)" --builtin-transes="$(BUILTIN_TRANSOBJS)" --module$(EXTSTATIC) $@ $(ENCS)
 
 .PRECIOUS: $(MKFILES)
 
@@ -833,6 +837,8 @@ transdb.h: $(PREP) srcs-enc $(srcdir)/tool/generic_erb.rb $(srcdir)/template/tra
 	$(ECHO) generating $@
 	$(Q) $(MINIRUBY) $(srcdir)/tool/generic_erb.rb -c -o $@ $(srcdir)/template/transdb.h.tmpl $(srcdir)/enc/trans enc/trans
 
+enc/encinit.c: $(ENC_MK) $(srcdir)/enc/encinit.c.erb
+
 known_errors.inc: $(srcdir)/template/known_errors.inc.tmpl $(srcdir)/defs/known_errors.def
 	$(ECHO) generating $@
 	$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -c -o $@ $(srcdir)/template/known_errors.inc.tmpl $(srcdir)/defs/known_errors.def
diff --git a/configure.in b/configure.in
index d0043b8..ee7bb05 100644
--- a/configure.in
+++ b/configure.in
@@ -291,6 +291,8 @@ AC_PROG_CC
 AC_PROG_CXX
 AC_PROG_GCC_TRADITIONAL
 AC_SUBST(GCC)
+AC_CHECK_TOOL([LD], [ld], [ld])
+AC_SUBST(LD)
 if test "$GCC" = yes; then
     linker_flag=-Wl,
     : ${optflags=-O3}
@@ -2095,7 +2097,7 @@ if test "$with_dln_a_out" != yes; then
 
   AS_CASE(["$target_os"],
 	[hpux*], [	DLDFLAGS="$DLDFLAGS -E"
-			: ${LDSHARED='ld -b'}
+			: ${LDSHARED="$(LD) -b"}
 			XLDFLAGS="$XLDFLAGS -Wl,-E"
 			: ${LIBPATHENV=SHLIB_PATH}
 			if test "$rb_cv_prog_gnu_ld" = no; then
@@ -2114,17 +2116,17 @@ if test "$with_dln_a_out" != yes; then
 			    : ${LIBPATHENV=LD_LIBRARY_PATH_64}
 			fi
 			rb_cv_dlopen=yes],
-	[sunos*], [	: ${LDSHARED='ld -assert nodefinitions'}
+	[sunos*], [	: ${LDSHARED="$(LD) -assert nodefinitions"}
 			rb_cv_dlopen=yes],
-	[irix*], [	: ${LDSHARED='ld -shared'}
+	[irix*], [	: ${LDSHARED="$(LD) -shared"}
 			rb_cv_dlopen=yes],
-	[sysv4*], [	: ${LDSHARED='ld -G'}
+	[sysv4*], [	: ${LDSHARED="$(LD) -G"}
 			rb_cv_dlopen=yes],
 	[nto-qnx*], [	: ${LDSHARED='$(CC) -shared'}
 			rb_cv_dlopen=yes],
-	[esix*|uxpds*], [ : ${LDSHARED="ld -G"}
+	[esix*|uxpds*], [ : ${LDSHARED="$(LD) -G"}
 			rb_cv_dlopen=yes],
-	[osf*], [	: ${LDSHARED="ld -shared -expect_unresolved \"*\""}
+	[osf*], [	: ${LDSHARED="$(LD) -shared -expect_unresolved \"*\""}
 			rb_cv_dlopen=yes],
 	[bsdi3*], [	AS_CASE(["$CC"],
 			[*shlicc*], [	: ${LDSHARED='$(CC) -r'}
@@ -2145,7 +2147,7 @@ if test "$with_dln_a_out" != yes; then
 			    LDFLAGS="$LDFLAGS -rdynamic"
 			    DLDFLAGS="$DLDFLAGS "'-Wl,-soname,$(.TARGET)'
 			else
-			  test "$GCC" = yes && test "$rb_cv_prog_gnu_ld" = yes || LDSHARED="ld -Bshareable"
+			  test "$GCC" = yes && test "$rb_cv_prog_gnu_ld" = yes || LDSHARED="$(LD) -Bshareable"
 			fi
 			rb_cv_dlopen=yes],
 	[openbsd*], [	: ${LDSHARED='$(CC) -shared ${CCDLFLAGS}'}
@@ -2179,12 +2181,12 @@ if test "$with_dln_a_out" != yes; then
 			rb_cv_dlopen=yes],
 	[beos*], [	AS_CASE(["$target_cpu"],
 			  [powerpc*], [
-			    : ${LDSHARED="ld -xms"}
+			    : ${LDSHARED="$(LD) -xms"}
 			    DLDFLAGS="$DLDFLAGS "'-export Init_$(TARGET) -lbe -lroot glue-noinit.a init_term_dyn.o start_dyn.o'
 			    LDFLAGS="$LDFLAGS -L/boot/home/config/lib -lbe -lroot"
 			    ],
 			  [i586*], [
-			    : ${LDSHARED="ld -shared"}
+			    : ${LDSHARED="$(LD) -shared"}
 			    DLDFLAGS="$DLDFLAGS -L/boot/develop/lib/x86 -L/boot/home/config/lib \$(topdir)/_APP_ -lbe -lroot"
 			    LDFLAGS="$LDFLAGS -L/boot/develop/lib/x86 -L/boot/home/config/lib -lbe -lroot"
 			    ])
@@ -2192,17 +2194,17 @@ if test "$with_dln_a_out" != yes; then
 			rb_cv_dlopen=yes],
 	[haiku*], [	AS_CASE(["$target_cpu"],
 			  [powerpc*], [
-			    : ${LDSHARED="ld -xms"}
+			    : ${LDSHARED="$(LD) -xms"}
 			    DLDFLAGS="$DLDFLAGS "'-export Init_$(TARGET) -lbe -lroot glue-noinit.a init_term_dyn.o start_dyn.o'
                             ],
 			  [i586*], [
-			    : ${LDSHARED="ld -shared"}
+			    : ${LDSHARED="$(LD) -shared"}
 			    DLDFLAGS="$DLDFLAGS -L/boot/develop/lib/x86 -lbe -lroot"
 			    ])
 			: ${LIBPATHENV=LIBRARY_PATH}
 			rb_cv_dlopen=yes ],
 	[nto-qnx*], [	DLDFLAGS="$DLDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
-			: ${LDSHARED='ld -Bshareable -x'}
+			: ${LDSHARED="$(LD) -Bshareable -x"}
 			LDFLAGS="$LDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
 			rb_cv_dlopen=yes],
 	[cygwin*|mingw*], [
@@ -2211,12 +2213,12 @@ if test "$with_dln_a_out" != yes; then
 			DLDFLAGS="${DLDFLAGS} -Wl,--enable-auto-image-base,--enable-auto-import"
 			: ${LIBPATHENV=""}
 			rb_cv_dlopen=yes],
-	[hiuxmpp], [	: ${LDSHARED='ld -r'}],
+	[hiuxmpp], [	: ${LDSHARED="$(LD) -r"}],
 	[atheos*], [	: ${LDSHARED='$(CC) -shared'}
 			rb_cv_dlopen=yes],
 	[os2-emx*], [	LDFLAGS="$LDFLAGS -Zomf"
 			],
-	[ 	: ${LDSHARED='ld'}])
+	[ 	: ${LDSHARED="$(LD)"}])
   AC_MSG_RESULT($rb_cv_dlopen)
 fi
 if test "${LDSHAREDXX}" = ""; then
@@ -2324,6 +2326,13 @@ AC_SUBST(EXTSTATIC)dnl
 AC_ARG_WITH(static-linked-ext,
 	    AS_HELP_STRING([--with-static-linked-ext], [link external modules statically]),
             [AS_CASE([$withval],[yes],[STATIC=;EXTSTATIC=static])])
+if test x"$EXTSTATIC" = xstatic; then
+  ENCOBJS='enc/encinit.$(OBJEXT) enc/libenc.a enc/libtrans.a'
+  EXTOBJS='ext/extinit.$(OBJEXT) ext/libext.a'
+  AC_DEFINE_UNQUOTED(EXTSTATIC, 1)
+fi
+AC_SUBST(ENCOBJS)
+AC_SUBST(EXTOBJS)
 
 AS_CASE(["$target_os"],
   dnl OS/2 environment w/ Autoconf 2.1x for EMX
@@ -2507,7 +2516,7 @@ AS_CASE("$enable_shared", [yes], [
   LIBRUBYARG_SHARED=
 
   # enable PIE if possible
-  if test "$GCC" = yes; then
+  if test "$GCC" = yes and -z "$EXTSTATIC"; then
     RUBY_TRY_CFLAGS(-fPIE, [pie=yes], [pie=no])
     if test "$pie" = yes; then
       RUBY_APPEND_OPTION(XCFLAGS, -fPIE)
diff --git a/dmyext.c b/dmyext.c
index 4d273f7..741dc59 100644
--- a/dmyext.c
+++ b/dmyext.c
@@ -2,3 +2,8 @@ void
 Init_ext(void)
 {
 }
+
+void
+Init_enc()
+{
+}
diff --git a/enc/Makefile.in b/enc/Makefile.in
index 203a83d..44e714a 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -20,6 +20,7 @@ ENCSODIR = $(EXTOUT)/$(arch)/enc
 TRANSSODIR = $(ENCSODIR)/trans
 DLEXT = @DLEXT@
 OBJEXT = @OBJEXT@
+LIBEXT = @LIBEXT@
 
 BUILTIN_ENCS	= ascii.c us_ascii.c\
 		  unicode.c utf_8.c
@@ -32,7 +33,9 @@ LIBRUBYARG_SHARED = @LIBRUBYARG_SHARED@
 LIBRUBYARG_STATIC = $(LIBRUBYARG_SHARED)
 
 empty =
+AR = @AR@
 CC = @CC@
+RANLIB = @RANLIB@
 OUTFLAG = @OUTFLAG@$(empty)
 COUTFLAG = @COUTFLAG@$(empty)
 CFLAGS = $(CCDLFLAGS) @CFLAGS@ @ARCH_FLAG@
diff --git a/enc/depend b/enc/depend
index ca6085d..d600616 100644
--- a/enc/depend
+++ b/enc/depend
@@ -5,34 +5,7 @@
 % dldflags = $2
 % enable_shared = CONFIG['ENABLE_SHARED'] == 'yes'
 % deffile = (true if /\$\(DEFFILE\)/ =~ CONFIG["LINK_SO"])
-% encs = Dir.open($srcdir) {|d| d.grep(/.+\.c\z/)} - BUILTIN_ENCS - ["mktable.c"]
-% encs.each {|e| e.chomp!(".c")}
-% encs.reject! {|e| !ENC_PATTERNS.any? {|p| File.fnmatch?(p, e)}} if !ENC_PATTERNS.empty?
-% encs.reject! {|e| NOENC_PATTERNS.any? {|p| File.fnmatch?(p, e)}}
-% alphanumeric_order = proc {|e| e.scan(/(\d+)|(\D+)/).map {|n,a| a||[n.size,n.to_i]}.flatten}
-% encs = encs.sort_by(&alphanumeric_order)
-% encs.unshift(encs.delete("encdb"))
-% atrans = []
-% trans = Dir.open($srcdir+"/trans") {|d|
-%   d.select {|e|
-%     if e.chomp!('.trans')
-%       atrans << e
-%       true
-%     elsif e.chomp!('.c')
-%       true
-%     end
-%   }
-% }
-% trans -= BUILTIN_TRANSES
-% atrans -= BUILTIN_TRANSES
-% trans.uniq!
-% atrans = atrans.sort_by(&alphanumeric_order)
-% trans = trans.sort_by(&alphanumeric_order)
-% trans.unshift(trans.delete("transdb"))
-% trans.compact!
-% trans |= atrans
-% trans.map! {|e| "trans/#{e}"}
-% dependencies = encs + trans
+% dependencies = ENCS + TRANS
 % cleanlibs = Shellwords.shellwords(CONFIG["cleanlibs"] || "")
 % cleanobjs = Shellwords.shellwords(CONFIG["cleanobjs"] || "")
 % cleanobjs << "$*.def" if deffile
@@ -54,31 +27,51 @@ else
   ''
 end %> <%=CONFIG['LIBS']%> $(EXTLIBS)
 
-ENCOBJS = <%=encs.map {|e|"enc/#{e}.$(OBJEXT)"}.join(" \\\n\t  ")%><%="\n" if encs.size>1%>
-ENCSOS = <%=encs.map {|e|"$(ENCSODIR)/#{e}.$(DLEXT)"}.join(" \\\n\t ")%><%="\n" if encs.size>1%>
+ENCOBJS = <%=ENCS.map {|e|"enc/#{e}.$(OBJEXT)"}.join(" \\\n\t  ")%><%="\n" if ENCS.size>1%>
+ENCSOS = <%=ENCS.map {|e|"$(ENCSODIR)/#{e}.$(DLEXT)"}.join(" \\\n\t ")%><%="\n" if ENCS.size>1%>
 ENCCLEANLIBS = <%=cleanlibs.map {|clean|
   clean.gsub(/\$\*(\.\w+)?/) {"$(ENCOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
 }.join(" ")%>
 ENCCLEANOBJS = <%=cleanobjs.map {|clean|
   clean.gsub(/\$\*(\.\w+)?/) {"$(ENCOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
 }.join(" ")%>
+LIBENC=enc/libenc.$(LIBEXT)
 
 TRANSVPATH = $(srcdir)/enc/trans
 
-TRANSCSRCS = <%=atrans.map {|e| transvpath % "#{e}.c"}.join(" \\\n\t     ")%><%="\n" if trans.size>1%>
-TRANSOBJS = <%=trans.map {|e|"enc/#{e}.$(OBJEXT)"}.join(" \\\n\t    ")%><%="\n" if trans.size>1%>
-TRANSSOS = <%=trans.map {|e|"$(ENCSODIR)/#{e}.$(DLEXT)"}.join(" \\\n\t   ")%><%="\n" if trans.size>1%>
+TRANSCSRCS = <%=ATRANS.map {|e| transvpath % "#{e}.c"}.join(" \\\n\t     ")%><%="\n" if TRANS.size>1%>
+TRANSOBJS = <%=TRANS.map {|e|"enc/#{e}.$(OBJEXT)"}.join(" \\\n\t    ")%><%="\n" if TRANS.size>1%>
+TRANSSOS = <%=TRANS.map {|e|"$(ENCSODIR)/#{e}.$(DLEXT)"}.join(" \\\n\t   ")%><%="\n" if TRANS.size>1%>
 TRANSCLEANLIBS = <%=cleanlibs.map {|clean|
   clean.gsub(/\$\*(\.\w+)?/) {"$(TRANSOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
 }.join(" ")%>
 TRANSCLEANOBJS = <%=cleanobjs.map {|clean|
   clean.gsub(/\$\*(\.\w+)?/) {"$(TRANSOBJS#{$1 ? ":.#{CONFIG["OBJEXT"]}=#{$1}" : ""})"}
 }.join(" ")%>
+LIBTRANS=enc/libtrans.$(LIBEXT)
 
 encs: all
+% if MODULE_TYPE == :static
+all: libenc libtrans
+% else
 all: enc trans
+%end
+libencs: libenc libtrans
 enc: $(ENCSOS)
+libenc: $(LIBENC)
 trans: $(TRANSSOS)
+libtrans: $(LIBTRANS)
+
+$(LIBENC): $(ENCOBJS)
+	@$(RM) $@
+	$(ECHO) linking statically-linked encoding library $@
+	$(Q) $(AR) <%=CONFIG['ARFLAGS'] || 'rcu' %> $@ $(ENCOBJS)
+	@-$(RANLIB) $@ 2> /dev/null || true
+$(LIBTRANS): $(TRANSOBJS)
+	@$(RM) $@
+	$(ECHO) linking statically-linked transcoder library $@
+	$(Q) $(AR) <%=CONFIG['ARFLAGS'] || 'rcu' %> $@ $(TRANSOBJS)
+	@-$(RANLIB) $@ 2> /dev/null || true
 
 srcs: $(TRANSCSRCS)
 
@@ -86,15 +79,15 @@ srcs: $(TRANSCSRCS)
 	$(ECHO) generating table from $@
 	$(Q)$(MINIRUBY) "$(srcdir)/tool/transcode-tblgen.rb" -vo "$@" "$<"
 
-% unless encs.empty? or trans.empty?
+% unless ENCS.empty? or TRANS.empty?
 
-%   unless encs.empty?
+%   unless ENCS.empty?
 $(ENCOBJS): regenc.h oniguruma.h config.h defines.h
 %   end
-%   unless trans.empty?
+%   unless TRANS.empty?
 $(TRANSOBJS): ruby.h intern.h config.h defines.h missing.h encoding.h oniguruma.h st.h transcode_data.h
 %   end
-%   atrans.each do |e|
+%   ATRANS.each do |e|
 %     src = "#{e}.trans"
 
 <%=transvpath % "#{e}.c"%>: <%= transvpath % "#{e}.trans"%>
@@ -142,7 +135,7 @@ enc/encdb.$(OBJEXT): encdb.h
 enc/trans/transdb.$(OBJEXT): transdb.h
 
 clean:
-% %w[$(ENCSOS) $(ENCOBJS) $(ENCCLEANOBJS) $(ENCCLEANLIBS) $(TRANSSOS) $(TRANSOBJS) $(TRANSCLEANOBJS) $(TRANSCLEANLIBS)].each do |clean|
+% %w[$(ENCSOS) $(LIBENC) $(ENCOBJS) $(ENCCLEANOBJS) $(ENCCLEANLIBS) $(TRANSSOS) $(LIBTRANS) $(TRANSOBJS) $(TRANSCLEANOBJS) $(TRANSCLEANLIBS)].each do |clean|
 	$(Q)$(RM) <%=pathrep[clean]%>
 % end
 % @ignore_error = $nmake ? '' : ' 2> /dev/null || true'
@@ -160,3 +153,5 @@ clean-srcs:
 	$(Q)$(RM) enc/unicode/name2ctype.h
 	-$(Q)$(RMDIR) <%=pathrep['enc/unicode']%><%=@ignore_error%>
 	-$(Q)$(RMDIR) <%=pathrep['enc']%><%=@ignore_error%>
+
+<%# vim: set ft=eruby noexpandtab ts=8 sw=2 : -%>
diff --git a/enc/encinit.c.erb b/enc/encinit.c.erb
new file mode 100644
index 0000000..17b9402
--- /dev/null
+++ b/enc/encinit.c.erb
@@ -0,0 +1,26 @@
+<%# -*- encoding: UTF-8 -*-%>
+/* Copyright 2012 Google Inc. Some Rights Reserved.
+ * Author: yugui@google.com (Yugui Sonoda)
+ */
+#include <stdio.h>
+
+#define init(func, name) {	\
+    extern void func(void);	\
+    ruby_init_ext(name, func);	\
+}
+
+void ruby_init_ext(const char *name, void (*init)(void));
+
+void Init_enc()
+{
+  <% ENCS.each do |enc| -%>
+    init(Init_<%= enc %>, "enc/<%= enc %>.so");
+  <% end -%>
+
+  init(Init_transdb, "enc/trans/transdb.so");
+  <% TRANS.each do |trans| -%>
+    <% next if trans == 'trans/transdb' -%>
+    init(Init_trans_<%= File.basename trans %>, "enc/<%= trans %>.so");
+  <% end -%>
+}
+<%# vim: set fenc=utf-8 ft=eruby sw=2 : -%>
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index ed36803..291774e 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -15,6 +15,7 @@ BUILTIN_ENCS = []
 BUILTIN_TRANSES = []
 ENC_PATTERNS = []
 NOENC_PATTERNS = []
+module_type = :dynamic
 
 until ARGV.empty?
   case ARGV[0]
@@ -30,11 +31,57 @@ until ARGV.empty?
   when /\A--no-encs=/
     NOENC_PATTERNS.concat $'.split
     ARGV.shift
+  when /\A--module$/
+    ARGV.shift
+  when /\A--modulestatic$/
+    module_type = :static
+    ARGV.shift
   else
     break
   end
 end
 
+ALPHANUMERIC_ORDER = proc {|e| e.scan(/(\d+)|(\D+)/).map {|n,a| a||[n.size,n.to_i]}.flatten}
+def target_encodings
+  encs = Dir.open($srcdir) {|d| d.grep(/.+\.c\z/)} - BUILTIN_ENCS - ["mktable.c"]
+  encs.each {|e| e.chomp!(".c")}
+  encs.reject! {|e| !ENC_PATTERNS.any? {|p| File.fnmatch?(p, e)}} if !ENC_PATTERNS.empty?
+  encs.reject! {|e| NOENC_PATTERNS.any? {|p| File.fnmatch?(p, e)}}
+  encs = encs.sort_by(&ALPHANUMERIC_ORDER)
+  encs.unshift(encs.delete("encdb"))
+  return encs
+end
+
+def target_transcoders
+  atrans = []
+  trans = Dir.open($srcdir+"/trans") {|d|
+    d.select {|e|
+      if e.chomp!('.trans')
+        atrans << e
+        true
+      elsif e.chomp!('.c')
+        true
+      end
+    }
+  }
+  trans -= BUILTIN_TRANSES
+  atrans -= BUILTIN_TRANSES
+  trans.uniq!
+  atrans = atrans.sort_by(&ALPHANUMERIC_ORDER)
+  trans = trans.sort_by(&ALPHANUMERIC_ORDER)
+  trans.unshift(trans.delete("transdb"))
+  trans.compact!
+  trans |= atrans
+  trans.map! {|e| "trans/#{e}"}
+
+  return atrans, trans
+end
+
+# Constants that "depend" needs.
+MODULE_TYPE = module_type
+ENCS = target_encodings
+ATRANS, TRANS = target_transcoders
+
 if File.exist?(depend = File.join($srcdir, "depend"))
   erb = ERB.new(File.read(depend), nil, '%')
   erb.filename = depend
@@ -48,3 +95,18 @@ mkin.gsub!(/@(#{CONFIG.keys.join('|')})@/) {CONFIG[$1]}
 open(ARGV[0], 'wb') {|f|
   f.puts mkin, dep
 }
+if MODULE_TYPE == :static
+  erb = ERB.new(File.read(File.join($srcdir, "encinit.c.erb")), nil, '%-')
+  erb.filename = "enc/encinit.c.cerb"
+  tmp = erb.result(binding)
+  begin
+    Dir.mkdir 'enc'
+  rescue Errno::EEXIST
+  end
+  File.open("enc/encinit.c", "w") {|f|
+    f.puts "/* Automatically generated from enc/encinit.c.erb"
+    f.puts " * Do not edit."
+    f.puts " */"
+    f.puts tmp
+  }
+end
diff --git a/enc/trans/big5.trans b/enc/trans/big5.trans
index 9dacfd1..c85ada3 100644
--- a/enc/trans/big5.trans
+++ b/enc/trans/big5.trans
@@ -26,8 +26,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_big5(void)
+TRANS_INIT(big5)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/chinese.trans b/enc/trans/chinese.trans
index 0662a3b..282c91a 100644
--- a/enc/trans/chinese.trans
+++ b/enc/trans/chinese.trans
@@ -25,8 +25,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_chinese(void)
+TRANS_INIT(chinese)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/emoji.trans b/enc/trans/emoji.trans
index 1cf1374..25d9fdd 100644
--- a/enc/trans/emoji.trans
+++ b/enc/trans/emoji.trans
@@ -30,8 +30,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_emoji(void)
+TRANS_INIT(emoji)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/emoji_iso2022_kddi.trans b/enc/trans/emoji_iso2022_kddi.trans
index ccf3139..ea180c1 100644
--- a/enc/trans/emoji_iso2022_kddi.trans
+++ b/enc/trans/emoji_iso2022_kddi.trans
@@ -208,8 +208,7 @@ rb_iso2022jp_kddi_encoder = {
     iso2022jp_kddi_encoder_reset_sequence_size, finish_iso2022jp_kddi_encoder
 };
 
-void
-Init_emoji_iso2022_kddi(void)
+TRANS_INIT(emoji_iso2022_kddi)
 {
     rb_register_transcoder(&rb_iso2022jp_kddi_decoder);
     rb_register_transcoder(&rb_iso2022jp_kddi_encoder);
diff --git a/enc/trans/emoji_sjis_docomo.trans b/enc/trans/emoji_sjis_docomo.trans
index 36e6e20..5dd9c7a 100644
--- a/enc/trans/emoji_sjis_docomo.trans
+++ b/enc/trans/emoji_sjis_docomo.trans
@@ -26,8 +26,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_emoji_sjis_docomo(void)
+TRANS_INIT(emoji_sjis_docomo)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/emoji_sjis_kddi.trans b/enc/trans/emoji_sjis_kddi.trans
index 654bed1..6df62f1 100644
--- a/enc/trans/emoji_sjis_kddi.trans
+++ b/enc/trans/emoji_sjis_kddi.trans
@@ -27,8 +27,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_emoji_sjis_kddi(void)
+TRANS_INIT(emoji_sjis_kddi)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/emoji_sjis_softbank.trans b/enc/trans/emoji_sjis_softbank.trans
index c152d99..c520035 100644
--- a/enc/trans/emoji_sjis_softbank.trans
+++ b/enc/trans/emoji_sjis_softbank.trans
@@ -26,8 +26,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_emoji_sjis_softbank(void)
+TRANS_INIT(emoji_sjis_softbank)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/escape.trans b/enc/trans/escape.trans
index a2fbeba..550e4ac 100644
--- a/enc/trans/escape.trans
+++ b/enc/trans/escape.trans
@@ -85,8 +85,7 @@ rb_escape_xml_attr_quote = {
     escape_xml_attr_quote_finish
 };
 
-void
-Init_escape(void)
+TRANS_INIT(escape)
 {
 <%= transcode_register_code %>
     rb_register_transcoder(&rb_escape_xml_attr_quote);
diff --git a/enc/trans/gb18030.trans b/enc/trans/gb18030.trans
index c7da4c6..94c866e 100644
--- a/enc/trans/gb18030.trans
+++ b/enc/trans/gb18030.trans
@@ -176,8 +176,7 @@ rb_to_GB18030 = {
 };
 
 
-void
-Init_gb18030(void)
+TRANS_INIT(gb18030)
 {
     rb_register_transcoder(&rb_from_GB18030);
     rb_register_transcoder(&rb_to_GB18030);
diff --git a/enc/trans/gbk.trans b/enc/trans/gbk.trans
index 0df1481..3c2cf0c 100644
--- a/enc/trans/gbk.trans
+++ b/enc/trans/gbk.trans
@@ -9,8 +9,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_gbk(void)
+TRANS_INIT(gbk)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/iso2022.trans b/enc/trans/iso2022.trans
index 3f40cce..a441f15 100644
--- a/enc/trans/iso2022.trans
+++ b/enc/trans/iso2022.trans
@@ -553,8 +553,7 @@ rb_cp50220_encoder = {
     iso2022jp_encoder_reset_sequence_size, finish_cp50220_encoder
 };
 
-void
-Init_iso2022(void)
+TRANS_INIT(iso2022)
 {
     rb_register_transcoder(&rb_iso2022jp_decoder);
     rb_register_transcoder(&rb_iso2022jp_encoder);
diff --git a/enc/trans/japanese.trans b/enc/trans/japanese.trans
index ce5d0bb..7ff024f 100644
--- a/enc/trans/japanese.trans
+++ b/enc/trans/japanese.trans
@@ -90,8 +90,7 @@ rb_sjis2eucjp = {
     NULL, NULL, NULL, fun_so_sjis2eucjp
 };
 
-void
-Init_japanese(void)
+TRANS_INIT(japanese)
 {
     rb_register_transcoder(&rb_eucjp2sjis);
     rb_register_transcoder(&rb_sjis2eucjp);
diff --git a/enc/trans/japanese_euc.trans b/enc/trans/japanese_euc.trans
index ff976e4..0f63272 100644
--- a/enc/trans/japanese_euc.trans
+++ b/enc/trans/japanese_euc.trans
@@ -51,8 +51,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_japanese_euc(void)
+TRANS_INIT(japanese_euc)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/japanese_sjis.trans b/enc/trans/japanese_sjis.trans
index 00eace2..8e1e130 100644
--- a/enc/trans/japanese_sjis.trans
+++ b/enc/trans/japanese_sjis.trans
@@ -27,8 +27,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_japanese_sjis(void)
+TRANS_INIT(japanese_sjis)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/korean.trans b/enc/trans/korean.trans
index ef1cdfc..fda1a3e 100644
--- a/enc/trans/korean.trans
+++ b/enc/trans/korean.trans
@@ -12,8 +12,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_korean(void)
+TRANS_INIT(korean)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/single_byte.trans b/enc/trans/single_byte.trans
index 2293bf6..50f568c 100644
--- a/enc/trans/single_byte.trans
+++ b/enc/trans/single_byte.trans
@@ -84,8 +84,7 @@
 
 <%= transcode_generated_code %>
 
-void
-Init_single_byte(void)
+TRANS_INIT(single_byte)
 {
 <%= transcode_register_code %>
 }
diff --git a/enc/trans/utf8_mac.trans b/enc/trans/utf8_mac.trans
index 8ea0afd..11ce35e 100644
--- a/enc/trans/utf8_mac.trans
+++ b/enc/trans/utf8_mac.trans
@@ -233,8 +233,7 @@ rb_from_UTF8_MAC = {
     from_utf8_mac_finish
 };
 
-void
-Init_utf8_mac(void)
+TRANS_INIT(utf8_mac)
 {
 <%= transcode_register_code %>
     rb_register_transcoder(&rb_from_UTF8_MAC);
diff --git a/enc/trans/utf_16_32.trans b/enc/trans/utf_16_32.trans
index c841df0..632c880 100644
--- a/enc/trans/utf_16_32.trans
+++ b/enc/trans/utf_16_32.trans
@@ -539,8 +539,7 @@ rb_to_UTF_32 = {
     NULL, NULL, NULL, fun_so_to_utf_32
 };
 
-void
-Init_utf_16_32(void)
+TRANS_INIT(utf_16_32)
 {
     rb_register_transcoder(&rb_from_UTF_16BE);
     rb_register_transcoder(&rb_to_UTF_16BE);
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 3b2c0c6..c8ff664 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -10,7 +10,7 @@ $dryrun = false
 $clean = nil
 $nodynamic = nil
 $extinit = nil
-$extobjs = nil
+$extobjs = []
 $extflags = ""
 $extlibs = nil
 $extpath = nil
@@ -26,6 +26,8 @@ alias $0 $progname
 $extlist = []
 $compiled = {}
 
+DUMMY_SIGNATURE = "***DUMMY MAKEFILE***"
+
 srcdir = File.dirname(File.dirname(__FILE__))
 unless defined?(CROSS_COMPILING) and CROSS_COMPILING
   $:.replace([File.expand_path("lib", srcdir), Dir.pwd])
@@ -46,7 +48,18 @@ def sysquote(x)
 end
 
 def verbose?
-  $mflags.defined?("Q") != "@"
+  $mflags.defined?("V") == "1"
+end
+
+def system(*args)
+  if verbose?
+    if args.size == 1
+      puts args
+    else
+      puts args.map{|arg| Shellwords.escape(arg)}.join(' ')
+    end
+  end
+  super
 end
 
 def extract_makefile(makefile, keep = true)
@@ -100,7 +113,7 @@ def extract_makefile(makefile, keep = true)
 end
 
 def extmake(target)
-  unless $configure_only
+  unless $configure_only || verbose?
     print "#{$message} #{target}\n"
     $stdout.flush
   end
@@ -163,7 +176,11 @@ def extmake(target)
         then
 	  ok = false
           if $configure_only
-            print "#{$message} #{target}\n"
+            if verbose?
+              print "#{conf}\n" if conf
+            else
+              print "#{$message} #{target}\n"
+            end
             $stdout.flush
           end
           init_mkmf
@@ -193,9 +210,11 @@ def extmake(target)
 	$0 = $PROGRAM_NAME
       end
     end
+    ok &&= File.open(makefile){|f| !f.gets[DUMMY_SIGNATURE]}
     ok = yield(ok) if block_given?
     unless ok
       open(makefile, "w") do |f|
+        f.puts "# " + DUMMY_SIGNATURE
 	f.print(*dummy_makefile(CONFIG["srcdir"]))
       end
       print "Failed to configure #{target}. It will not be installed.\n"
@@ -205,9 +224,9 @@ def extmake(target)
     unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR")
       args += [sysquote("DESTDIR=" + relative_from($destdir, "../"+prefix))]
     end
-    if $static
+    if $static and %r(\A-test-) !~ target and target != 'json' and ok
       args += ["static"] unless $clean
-      $extlist.push [$static, $target, File.basename($target), $preload]
+      $extlist.push [$static, target, $target, $preload]
     end
     FileUtils.rm_f(old_cleanfiles - $distcleanfiles)
     FileUtils.rm_f(old_objs - $objs)
@@ -466,7 +485,7 @@ Dir::chdir('ext')
 hdrdir = $hdrdir
 $hdrdir = ($top_srcdir = relative_from(srcdir, $topdir = "..")) + "/include"
 exts.each do |d|
-  $static = $force_static ? $static_ext[target] : nil
+  $static = $force_static ? true : $static_ext[target]
 
   if $ignore or !$nodynamic or $static
     extmake(d) or abort
@@ -502,7 +521,7 @@ if $ignore
 end
 
 $extinit ||= ""
-$extobjs ||= ""
+$extobjs ||= []
 $extpath ||= []
 $extflags ||= ""
 $extlibs ||= []
@@ -519,31 +538,27 @@ unless $extlist.empty?
       end
       next
     end
-    f = format("%s/%s.%s", t, i, $LIBEXT)
-    if File.exist?(f)
-      $extinit << "    init(Init_#{i}, \"#{t}.so\");\n"
-      $extobjs << "ext/#{f} "
-      built << t
-    end
+    $extinit << "    init(Init_#{File.basename i}, \"#{i}.so\");\n"
+    $extobjs << format("ext/%s/%s.%s", t, File.basename(i), $LIBEXT)
+    built << t
   end
 
   src = %{\
-#include "ruby.h"
+#include "ruby/ruby.h"
 
 #define init(func, name) {	\\
-    extern void func _((void));	\\
+    extern void func(void);	\\
     ruby_init_ext(name, func);	\\
 }
 
-void ruby_init_ext _((const char *name, void (*init)(void)));
+void ruby_init_ext(const char *name, void (*init)(void));
 
-void Init_ext _((void))\n{\n#$extinit}
+void Init_ext(void)\n{\n#$extinit}
 }
   if !modified?(extinit.c, MTIMES) || IO.read(extinit.c) != src
     open(extinit.c, "w") {|fe| fe.print src}
   end
 
-  $extobjs = "ext/#{extinit.o} #{$extobjs}"
   if RUBY_PLATFORM =~ /beos/
     $extflags.delete("-L/usr/local/lib")
   end
@@ -552,7 +567,6 @@ void Init_ext _((void))\n{\n#$extinit}
   conf = [
     ['LIBRUBY_SO_UPDATE', '$(LIBRUBY_EXTS)'],
     ['SETUP', $setup],
-    [enable_config("shared", $enable_shared) ? 'DLDOBJS' : 'EXTOBJS', $extobjs],
     ['EXTLIBS', $extlibs.join(' ')], ['EXTLDFLAGS', $extflags]
   ].map {|n, v|
     "#{n}=#{v}" if v and !(v = v.strip).empty?
@@ -615,21 +629,34 @@ if $configure_only and $command_output
       w += d.size + 1
     end
     mf.puts
+    if !$extlist.empty?
+      mf.puts "EXTOBJS = ext/extinit.#{$OBJEXT} #{$extobjs.join(' ')}"
+      mf.puts "EXTLIBS = #{$extlibs.join(' ')}"
+    else
+      mf.puts "EXTOBJS = "
+      mf.puts "EXTLIBS = "
+    end
+    mf.puts
     targets = %w[all install static install-so install-rb clean distclean realclean]
     targets.each do |tgt|
       mf.puts "#{tgt}: $(extensions:/.=/#{tgt})"
     end
     mf.puts
     mf.puts "all: #{rubies.join(' ')}"
-    mf.puts "#{rubies.join(' ')}: $(extensions:/.=/all)"
+    mf.puts "static: ext/extinit.#{$OBJEXT} #{rubies.join(' ')}"
+    mf.puts "clean:\n\t-$(Q)$(RM) ext/extinit.#{$OBJEXT}"
+    mf.puts "distclean:\n\t-$(Q)$(RM) ext/extinit.c"
+    mf.puts
+    mf.puts "#{rubies.join(' ')}: $(extensions:/.=/#{$force_static ? 'static' : 'all'})"
     rubies.each do |tgt|
-      mf.puts "#{tgt}:\n\t$(Q)$(MAKE) $(MFLAGS) $@"
+      mf.puts "#{tgt}:\n\t$(Q)$(MAKE) $(MFLAGS) EXTOBJS='$(EXTOBJS)' EXTLIBS='$(EXTLIBS)' $@"
     end
+    mf.puts "ext/extinit.#{$OBJEXT}:\n\t$(Q)$(MAKE) $(MFLAGS) V=$(V) $@"
     mf.puts
     exec = config_string("exec") {|str| str + " "}
     targets.each do |tgt|
       exts.each do |d|
-        mf.puts "#{d[0..-2]}#{tgt}:\n\t$(Q)cd $(@D) && #{exec}$(MAKE) $(MFLAGS) $(@F)"
+        mf.puts "#{d[0..-2]}#{tgt}:\n\t$(Q)cd $(@D) && #{exec}$(MAKE) $(MFLAGS) V=$(V) $(@F)"
       end
     end
   end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 78841e2..05c3bd8 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -1835,7 +1835,7 @@ DISTCLEANFILES = #{$distcleanfiles.join(' ')}
 
 all install static install-so install-rb: Makefile
 .PHONY: all install static install-so install-rb
-.PHONY: clean clean-so clean-rb
+.PHONY: clean clean-so clean-static clean-rb
 
 RULES
   end
@@ -2064,7 +2064,7 @@ CLEANOBJS     = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$
 all:    #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
 static: $(STATIC_LIB)#{$extout ? " install-rb" : ""}
 .PHONY: all install static install-so install-rb
-.PHONY: clean clean-so clean-rb
+.PHONY: clean clean-so clean-static clean-rb
 "
     mfile.print CLEANINGS
     fsep = config_string('BUILD_FILE_SEPARATOR') {|s| s unless s == "/"}
@@ -2100,6 +2100,8 @@ static: $(STATIC_LIB)#{$extout ? " install-rb" : ""}
           mfile.print "\t@echo #{dir}/#{File.basename(f)}>>$(INSTALLED_LIST)\n"
         end
       end
+      mfile.print "clean-static::\n"
+      mfile.print "\t-$(Q)$(RM) $(STATIC_LIB)\n"
     else
       mfile.puts "Makefile"
     end
@@ -2372,13 +2374,14 @@ MESSAGE
 clean-rb-default::
 clean-rb::
 clean-so::
-clean: clean-so clean-rb-default clean-rb
+clean: clean-so clean-static clean-rb-default clean-rb
 \t\t-$(Q)$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep})
 
 distclean-rb-default::
 distclean-rb::
 distclean-so::
-distclean: clean distclean-so distclean-rb-default distclean-rb
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
 \t\t-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
 \t\t-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep})
 \t\t-$(Q)$(RMDIRS) $(DISTCLEANDIRS#{sep})#{$ignore_error}
diff --git a/ruby.c b/ruby.c
index 93779a7..4fe0464 100644
--- a/ruby.c
+++ b/ruby.c
@@ -1225,6 +1225,9 @@ rb_f_chomp(argc, argv)
     return str;
 }
 
+/* blank function in dmyencoding.c or generated by enc/make_encmake.rb */
+extern void Init_enc(void);
+
 static VALUE
 process_options(int argc, char **argv, struct cmdline_options *opt)
 {
@@ -1311,6 +1314,7 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
 #endif
 
     ruby_init_loadpath_safe(opt->safe_level);
+    Init_enc();
     rb_enc_find_index("encdb");
     lenc = rb_locale_encoding();
     rb_enc_associate(rb_progname, lenc);
diff --git a/transcode_data.h b/transcode_data.h
index 8e85ce6..dcaee61 100644
--- a/transcode_data.h
+++ b/transcode_data.h
@@ -110,6 +110,16 @@ struct rb_transcoder {
 void rb_declare_transcoder(const char *enc1, const char *enc2, const char *lib);
 void rb_register_transcoder(const rb_transcoder *);
 
+/*
+ * To get rid of collision of initializer symbols in statically-linked encodings
+ * and transcoders
+ */
+#if defined(EXTSTATIC) && EXTSTATIC
+# define TRANS_INIT(name) void Init_trans_ ## name(void)
+#else
+# define TRANS_INIT(name) void Init_ ## name(void)
+#endif
+
 #if defined __GNUC__ && __GNUC__ >= 4
 #pragma GCC visibility pop
 #endif

In This Thread

Prev Next