Quellcode durchsuchen

Merge remote-tracking branch 'origin/master' into php7

Conflicts:
      config.w32
      package.xml
      php_v8js_macros.h
      v8js.cc
      v8js_array_access.cc
      v8js_class.cc
      v8js_convert.cc
      v8js_exceptions.cc
      v8js_object_export.cc
      v8js_timer.cc
      v8js_v8.cc
      v8js_v8object_class.cc
Stefan Siegl vor 9 Jahren
Ursprung
Commit
e9e90bac65
48 geänderte Dateien mit 586 neuen und 2080 gelöschten Zeilen
  1. 0 11
      Makefile.frag
  2. 7 2
      README.Linux.md
  3. 10 1
      README.md
  4. 0 6
      TODO
  5. 105 118
      config.m4
  6. 4 5
      config.w32
  7. 0 839
      js/json-template.js
  8. 0 339
      js/jstparser.js
  9. 105 47
      package.xml
  10. 0 5
      php_v8js_macros.h
  11. 0 15
      samples/dlopen.supp
  12. 0 75
      samples/test_call.php
  13. 0 32
      samples/test_callback.php
  14. 0 11
      samples/test_closure.php
  15. 0 5
      samples/test_crash.php
  16. 0 9
      samples/test_date.php
  17. 0 59
      samples/test_dumper.php
  18. 0 26
      samples/test_exception.php
  19. 0 34
      samples/test_exception2.php
  20. 0 23
      samples/test_extend.php
  21. 0 9
      samples/test_extension.php
  22. 0 48
      samples/test_method.php
  23. 0 173
      test.php
  24. 7 1
      tests/memory_limit.phpt
  25. 20 0
      tests/object_passback_002.phpt
  26. 44 0
      tests/return_this_001.phpt
  27. 50 0
      tests/return_this_basic.phpt
  28. 16 0
      tests/set_average_object_size_basic.phpt
  29. 7 1
      tests/set_memory_limit_001.phpt
  30. 7 1
      tests/set_memory_limit_003.phpt
  31. 7 1
      tests/set_memory_limit_basic.phpt
  32. 7 1
      tests/set_time_limit_001.phpt
  33. 7 1
      tests/set_time_limit_002.phpt
  34. 7 1
      tests/set_time_limit_003.phpt
  35. 7 1
      tests/set_time_limit_004.phpt
  36. 7 1
      tests/set_time_limit_basic.phpt
  37. 7 1
      tests/time_limit.phpt
  38. 0 2
      v8js.cc
  39. 7 13
      v8js_array_access.cc
  40. 28 26
      v8js_class.cc
  41. 1 2
      v8js_class.h
  42. 7 5
      v8js_convert.cc
  43. 10 16
      v8js_exceptions.cc
  44. 20 19
      v8js_methods.cc
  45. 23 26
      v8js_object_export.cc
  46. 31 15
      v8js_timer.cc
  47. 17 52
      v8js_v8.cc
  48. 11 2
      v8js_v8object_class.cc

+ 0 - 11
Makefile.frag

@@ -7,14 +7,3 @@ endif
 ifneq (,$(realpath $(EXTENSION_DIR)/pthreads.so))
 PHP_TEST_SHARED_EXTENSIONS+=-d extension=$(EXTENSION_DIR)/pthreads.so
 endif
-
-testv8: all
-	$(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php
-
-debugv8: all
-	gdb --arg $(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php
-
-valgrindv8: all
-	USE_ZEND_ALLOC=0 valgrind --leak-check=full --show-reachable=yes --track-origins=yes $(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php 2> valgrind.dump
-
-

+ 7 - 2
README.Linux.md

@@ -63,11 +63,14 @@ make native library=shared snapshot=on -j8
 sudo mkdir -p /usr/lib /usr/include
 sudo cp out/native/lib.target/lib*.so /usr/lib/
 sudo cp -R include/* /usr/include
+
+# Install libv8_libplatform.a (V8 >= 5.2.51)
+echo -e "create /usr/lib/libv8_libplatform.a\naddlib out/native/obj.target/src/libv8_libplatform.a\nsave\nend" | sudo ar -M
+
+# ... same for V8 < 5.2.51, libv8_libplatform.a is built in tools/gyp directory
 echo -e "create /usr/lib/libv8_libplatform.a\naddlib out/native/obj.target/tools/gyp/libv8_libplatform.a\nsave\nend" | sudo ar -M
 ```
 
-Then add `extension=v8js.so` to your php.ini file. If you have a separate configuration for CLI, add it there also.
-
 * If you don't want to overwrite the system copy of v8, replace `/usr` in
   the above commands with some other path like `/opt/v8` and then add
   `--with-v8js=/opt/v8` to the php-v8js `./configure` command below.
@@ -91,3 +94,5 @@ make
 make test
 sudo make install
 ```
+
+Then add `extension=v8js.so` to your php.ini file. If you have a separate configuration for CLI, add it there also.

+ 10 - 1
README.md

@@ -23,7 +23,8 @@ Minimum requirements
 	V8 is Google's open source Javascript engine.
 	V8 is written in C++ and is used in Google Chrome, the open source browser from Google.
 	V8 implements ECMAScript as specified in ECMA-262, 5th edition.
-    This extension makes use of V8 isolates to ensure separation between multiple V8Js instances and uses the new isolate-based mechanism to throw exceptions, hence the need for 3.24.6 or above.
+
+	This extension requires V8 4.6.76 or higher.
 
     V8 releases are published rather quickly and the V8 team usually provides security support
     for the version line shipped with the Chrome browser (stable channel) and newer (only).
@@ -163,6 +164,14 @@ class V8Js
     public function setMemoryLimit($limit)
     {}
 
+    /**
+     * Set the average object size (in bytes) for this V8Js object.
+     * V8's "amount of external memory" is adjusted by this value for every exported object.  V8 triggers a garbage collection once this totals to 192 MB.
+     * @param int $average_object_size
+     */
+    public function setAverageObjectSize($average_object_size)
+    {}
+
     /**
      * Returns uncaught pending exception or null if there is no pending exception.
      * @return V8JsScriptException|null

+ 0 - 6
TODO

@@ -1,6 +0,0 @@
-- Feature: Extension registering from php.ini
-- Feature: Thread safety
-- Missing: Indexed property handlers
-- Missing: static properties of PHP objects (on instance.constructor?)
-- Bug: exception propagation fails when property getter is set
-- Bug: method_exists() leaks when used with V8 objects

+ 105 - 118
config.m4

@@ -121,8 +121,8 @@ int main ()
     set $ac_cv_v8_version
     IFS=$ac_IFS
     V8_API_VERSION=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3`
-    if test "$V8_API_VERSION" -lt 3024006 ; then
-       AC_MSG_ERROR([libv8 must be version 3.24.6 or greater])
+    if test "$V8_API_VERSION" -lt 4006076 ; then
+       AC_MSG_ERROR([libv8 must be version 4.6.76 or greater])
     fi
     AC_DEFINE_UNQUOTED([PHP_V8_API_VERSION], $V8_API_VERSION, [ ])
     AC_DEFINE_UNQUOTED([PHP_V8_VERSION], "$ac_cv_v8_version", [ ])
@@ -130,128 +130,115 @@ int main ()
     AC_MSG_ERROR([could not determine libv8 version])
   fi
 
-  if test "$V8_API_VERSION" -ge 3029036 ; then
-    dnl building for v8 3.29.36 or later, which requires us to
-    dnl initialize and provide a platform; hence we need to
-    dnl link in libplatform to make our life easier.
-    PHP_ADD_INCLUDE($V8_DIR)
+  PHP_ADD_INCLUDE($V8_DIR)
 
-    case $host_os in
-      darwin* )
-        static_link_extra="libv8_libplatform.a libv8_libbase.a"
-        ;;
-      * )
-        static_link_extra="libv8_libplatform.a"
-        ;;
-    esac
-
-    for static_link_extra_file in $static_link_extra; do
-      AC_MSG_CHECKING([for $static_link_extra_file])
-
-      for i in $PHP_V8JS $SEARCH_PATH ; do
-        if test -r $i/lib64/$static_link_extra_file; then
-          static_link_dir=$i/lib64
-          AC_MSG_RESULT(found in $i)
-        fi
-        if test -r $i/lib/$static_link_extra_file; then
-          static_link_dir=$i/lib
-          AC_MSG_RESULT(found in $i)
-        fi
-      done
-
-      if test -z "$static_link_dir"; then
-        AC_MSG_RESULT([not found])
-        AC_MSG_ERROR([Please provide $static_link_extra_file next to the libv8.so, see README.md for details])
-      fi
-
-      LDFLAGS_libplatform="$static_link_dir/$static_link_extra_file"
-    done
+  case $host_os in
+	darwin* )
+	  static_link_extra="libv8_libplatform.a libv8_libbase.a"
+	  ;;
+	* )
+	  static_link_extra="libv8_libplatform.a"
+	  ;;
+  esac
 
-    # modify flags for (possibly) succeeding V8 startup check
-    CPPFLAGS="$CPPFLAGS -I$V8_DIR"
-    LIBS="$LIBS $LDFLAGS_libplatform"
-  fi
+  LDFLAGS_libplatform=""
+  for static_link_extra_file in $static_link_extra; do
+	AC_MSG_CHECKING([for $static_link_extra_file])
+
+	if test -r $V8_DIR/lib64/$static_link_extra_file; then
+	  static_link_dir=$V8_DIR/lib64
+	  AC_MSG_RESULT(found in $V8_DIR/lib64)
+	fi
+
+	if test -r $V8_DIR/lib/$static_link_extra_file; then
+	  static_link_dir=$V8_DIR/lib
+	  AC_MSG_RESULT(found in $V8_DIR/lib)
+	fi
+
+	if test -z "$static_link_dir"; then
+	  AC_MSG_RESULT([not found])
+	  AC_MSG_ERROR([Please provide $static_link_extra_file next to the libv8.so, see README.md for details])
+	fi
+
+	LDFLAGS_libplatform="$LDFLAGS_libplatform $static_link_dir/$static_link_extra_file"
+  done
+
+  # modify flags for (possibly) succeeding V8 startup check
+  CPPFLAGS="$CPPFLAGS -I$V8_DIR"
+  LIBS="$LIBS $LDFLAGS_libplatform"
+
+  dnl building for v8 4.4.10 or later, which requires us to
+  dnl provide startup data, if V8 wasn't compiled with snapshot=off.
+  AC_MSG_CHECKING([whether V8 requires startup data])
+  AC_TRY_RUN([
+	  #include <v8.h>
+	  #include <libplatform/libplatform.h>
+	  #include <stdlib.h>
+	  #include <string.h>
 
-  if test "$V8_API_VERSION" -ge 4004010 ; then
-    dnl building for v8 4.4.10 or later, which requires us to
-    dnl provide startup data, if V8 wasn't compiled with snapshot=off.
-    AC_MSG_CHECKING([whether V8 requires startup data])
-    AC_TRY_RUN([
-        #include <v8.h>
-        #include <libplatform/libplatform.h>
-        #include <stdlib.h>
-        #include <string.h>
-
-#if PHP_V8_API_VERSION >= 4004010
 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
 public:
-	virtual void* Allocate(size_t length) {
-		void* data = AllocateUninitialized(length);
-		return data == NULL ? data : memset(data, 0, length);
-	}
-	virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
-	virtual void Free(void* data, size_t) { free(data); }
+  virtual void* Allocate(size_t length) {
+	  void* data = AllocateUninitialized(length);
+	  return data == NULL ? data : memset(data, 0, length);
+  }
+  virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
+  virtual void Free(void* data, size_t) { free(data); }
 };
-#endif
-
-        int main ()
-        {
-            v8::Platform *v8_platform = v8::platform::CreateDefaultPlatform();
-            v8::V8::InitializePlatform(v8_platform);
-            v8::V8::Initialize();
-
-#if PHP_V8_API_VERSION >= 4004044
-            static ArrayBufferAllocator array_buffer_allocator;
-            v8::Isolate::CreateParams create_params;
-            create_params.array_buffer_allocator = &array_buffer_allocator;
-
-            v8::Isolate::New(create_params);
-#else /* PHP_V8_API_VERSION < 4004044 */
-            v8::Isolate::New();
-#endif
-            return 0;
-        }
-    ], [
-        AC_MSG_RESULT([no])
-    ], [
-        AC_MSG_RESULT([yes])
-        AC_DEFINE([PHP_V8_USE_EXTERNAL_STARTUP_DATA], [1], [Whether V8 requires (and can be provided with custom versions of) external startup data])
-
-        SEARCH_PATH="$V8_DIR/lib $V8_DIR/share/v8"
-
-        AC_MSG_CHECKING([for natives_blob.bin])
-        SEARCH_FOR="natives_blob.bin"
-
-        for i in $SEARCH_PATH ; do
-          if test -r $i/$SEARCH_FOR; then
-            AC_MSG_RESULT([found ($i/$SEARCH_FOR)])
-            AC_DEFINE_UNQUOTED([PHP_V8_NATIVES_BLOB_PATH], "$i/$SEARCH_FOR", [Full path to natives_blob.bin file])
-            native_blob_found=1
-          fi
-        done
-
-        if test -z "$native_blob_found"; then
-          AC_MSG_RESULT([not found])
-          AC_MSG_ERROR([Please provide V8 native blob as needed])
-        fi
-
-        AC_MSG_CHECKING([for snapshot_blob.bin])
-        SEARCH_FOR="snapshot_blob.bin"
-
-        for i in $SEARCH_PATH ; do
-          if test -r $i/$SEARCH_FOR; then
-            AC_MSG_RESULT([found ($i/$SEARCH_FOR)])
-            AC_DEFINE_UNQUOTED([PHP_V8_SNAPSHOT_BLOB_PATH], "$i/$SEARCH_FOR", [Full path to snapshot_blob.bin file])
-            snapshot_blob_found=1
-          fi
-        done
-
-        if test -z "$snapshot_blob_found"; then
-          AC_MSG_RESULT([not found])
-          AC_MSG_ERROR([Please provide V8 snapshot blob as needed])
-        fi
-    ])
-  fi
+
+	  int main ()
+	  {
+		  v8::Platform *v8_platform = v8::platform::CreateDefaultPlatform();
+		  v8::V8::InitializePlatform(v8_platform);
+		  v8::V8::Initialize();
+
+		  static ArrayBufferAllocator array_buffer_allocator;
+		  v8::Isolate::CreateParams create_params;
+		  create_params.array_buffer_allocator = &array_buffer_allocator;
+
+		  v8::Isolate::New(create_params);
+		  return 0;
+	  }
+  ], [
+	  AC_MSG_RESULT([no])
+  ], [
+	  AC_MSG_RESULT([yes])
+	  AC_DEFINE([PHP_V8_USE_EXTERNAL_STARTUP_DATA], [1], [Whether V8 requires (and can be provided with custom versions of) external startup data])
+
+	  SEARCH_PATH="$V8_DIR/lib $V8_DIR/share/v8"
+
+	  AC_MSG_CHECKING([for natives_blob.bin])
+	  SEARCH_FOR="natives_blob.bin"
+
+	  for i in $SEARCH_PATH ; do
+		if test -r $i/$SEARCH_FOR; then
+		  AC_MSG_RESULT([found ($i/$SEARCH_FOR)])
+		  AC_DEFINE_UNQUOTED([PHP_V8_NATIVES_BLOB_PATH], "$i/$SEARCH_FOR", [Full path to natives_blob.bin file])
+		  native_blob_found=1
+		fi
+	  done
+
+	  if test -z "$native_blob_found"; then
+		AC_MSG_RESULT([not found])
+		AC_MSG_ERROR([Please provide V8 native blob as needed])
+	  fi
+
+	  AC_MSG_CHECKING([for snapshot_blob.bin])
+	  SEARCH_FOR="snapshot_blob.bin"
+
+	  for i in $SEARCH_PATH ; do
+		if test -r $i/$SEARCH_FOR; then
+		  AC_MSG_RESULT([found ($i/$SEARCH_FOR)])
+		  AC_DEFINE_UNQUOTED([PHP_V8_SNAPSHOT_BLOB_PATH], "$i/$SEARCH_FOR", [Full path to snapshot_blob.bin file])
+		  snapshot_blob_found=1
+		fi
+	  done
+
+	  if test -z "$snapshot_blob_found"; then
+		AC_MSG_RESULT([not found])
+		AC_MSG_ERROR([Please provide V8 snapshot blob as needed])
+	  fi
+  ])
 
   AC_LANG_RESTORE
   LIBS=$old_LIBS

+ 4 - 5
config.w32

@@ -42,13 +42,12 @@ if (PHP_V8JS != "no") {
 		} else {
 			WARNING("Could not parse v8-version.h");
 		}
-		var v8api = v8major + v8js_zeroPad(v8minor,2) + v8js_zeroPad(v8build,2) + v8js_zeroPad(v8patch,2);
-		var v8ver = v8major+"."+v8minor+"."+v8build+"."+v8patch;
-		//WARNING("v8api = " + v8api + ", v8ver = " + v8ver);
+
+		var v8api = v8major + v8js_zeroPad(v8minor, 3) + v8js_zeroPad(v8build, 3);
+		var v8ver = v8major + "." + v8minor + "." + v8build + "." + v8patch;
+
 		AC_DEFINE("PHP_V8_API_VERSION", v8api, "", false);
 		AC_DEFINE("PHP_V8_VERSION", v8ver, "", true);
-		//AC_DEFINE("PHP_V8_API_VERSION", "4007075", "", false);
-		//AC_DEFINE("PHP_V8_VERSION", "4.7.75", "", true);
 
 		EXTENSION("v8js", "v8js_array_access.cc v8js.cc v8js_class.cc v8js_commonjs.cc v8js_convert.cc v8js_exceptions.cc v8js_generator_export.cc v8js_methods.cc v8js_object_export.cc v8js_timer.cc v8js_v8.cc v8js_v8object_class.cc v8js_variables.cc", "yes");
 

+ 0 - 839
js/json-template.js

@@ -1,839 +0,0 @@
-// Copyright (C) 2009 Andy Chu
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// $Id$
-
-//
-// JavaScript implementation of json-template.
-//
-
-// This is predefined in tests, shouldn't be defined anywhere else.  TODO: Do
-// something nicer.
-var log = log || function() {};
-var repr = repr || function() {};
-
-
-// The "module" exported by this script is called "jsontemplate":
-
-var jsontemplate = function() {
-
-
-// Regex escaping for metacharacters
-function EscapeMeta(meta) {
-  return meta.replace(/([\{\}\(\)\[\]\|\^\$\-\+\?])/g, '\\$1');
-}
-
-var token_re_cache = {};
-
-function _MakeTokenRegex(meta_left, meta_right) {
-  var key = meta_left + meta_right;
-  var regex = token_re_cache[key];
-  if (regex === undefined) {
-    var str = '(' + EscapeMeta(meta_left) + '.*?' + EscapeMeta(meta_right) +
-              '\n?)';
-    regex = new RegExp(str, 'g');
-  }
-  return regex;
-}
-
-//
-// Formatters
-//
-
-function HtmlEscape(s) {
-  return s.replace(/&/g,'&amp;').
-           replace(/>/g,'&gt;').
-           replace(/</g,'&lt;');
-}
-
-function HtmlTagEscape(s) {
-  return s.replace(/&/g,'&amp;').
-           replace(/>/g,'&gt;').
-           replace(/</g,'&lt;').
-           replace(/"/g,'&quot;');
-}
-
-// Default ToString can be changed
-function ToString(s) {
-  if (s === null) {
-    return 'null';
-  }
-  return s.toString();
-}
-
-// Formatter to pluralize words
-function _Pluralize(value, unused_context, args) {
-  var s, p;
-  switch (args.length) {
-    case 0:
-      s = ''; p = 's';
-      break;
-    case 1:
-      s = ''; p = args[0];
-      break;
-    case 2:
-      s = args[0]; p = args[1];
-      break;
-    default:
-      // Should have been checked at compile time
-      throw {
-        name: 'EvaluationError', message: 'pluralize got too many args'
-      };
-  }
-  return (value > 1) ? p : s;
-}
-
-function _Cycle(value, unused_context, args) {
-  // Cycle between various values on consecutive integers.
-  // @index starts from 1, so use 1-based indexing.
-  return args[(value - 1) % args.length];
-}
-
-var DEFAULT_FORMATTERS = {
-  'html': HtmlEscape,
-  'htmltag': HtmlTagEscape,
-  'html-attr-value': HtmlTagEscape,
-  'str': ToString,
-  'raw': function(x) { return x; },
-  'AbsUrl': function(value, context) {
-    // TODO: Normalize leading/trailing slashes
-    return context.get('base-url') + '/' + value;
-  }
-};
-
-var DEFAULT_PREDICATES = {
-  'singular?': function(x) { return  x == 1; },
-  'plural?': function(x) { return x > 1; },
-  'Debug?': function(unused, context) {
-    try {
-      return context.get('debug');
-    } catch(err) {
-      if (err.name == 'UndefinedVariable') {
-        return false;
-      } else {
-        throw err;
-      }
-    }
-  }
-};
-
-var FunctionRegistry = function() {
-  return {
-    lookup: function(user_str) {
-      return [null, null];
-    }
-  };
-};
-
-var SimpleRegistry = function(obj) {
-  return {
-    lookup: function(user_str) {
-      var func = obj[user_str] || null;
-      return [func, null];
-    }
-  };
-};
-
-var CallableRegistry = function(callable) {
-  return {
-    lookup: function(user_str) {
-      var func = callable(user_str);
-      return [func, null];
-    }
-  };
-};
-
-// Default formatters which can't be expressed in DEFAULT_FORMATTERS
-var PrefixRegistry = function(functions) {
-  return {
-    lookup: function(user_str) {
-      for (var i = 0; i < functions.length; i++) {
-        var name = functions[i].name, func = functions[i].func;
-        if (user_str.slice(0, name.length) == name) {
-          // Delimiter is usually a space, but could be something else
-          var args;
-          var splitchar = user_str.charAt(name.length);
-          if (splitchar === '') {
-            args = [];  // No arguments
-          } else {
-            args = user_str.split(splitchar).slice(1);
-          }
-          return [func, args];
-        } 
-      }
-      return [null, null];  // No formatter
-    }
-  };
-};
-
-var ChainedRegistry = function(registries) {
-  return {
-    lookup: function(user_str) {
-      for (var i=0; i<registries.length; i++) {
-        var result = registries[i].lookup(user_str);
-        if (result[0]) {
-          return result;
-        }
-      }
-      return [null, null];  // Nothing found
-    }
-  };
-};
-
-//
-// Template implementation
-//
-
-function _ScopedContext(context, undefined_str) {
-  // The stack contains:
-  //   The current context (an object).
-  //   An iteration index.  -1 means we're NOT iterating.
-  var stack = [{context: context, index: -1}];
-
-  return {
-    PushSection: function(name) {
-      if (name === undefined || name === null) {
-        return null;
-      }
-      var new_context;
-      if (name == '@') {
-        new_context = stack[stack.length-1].context;
-      } else {
-        new_context = stack[stack.length-1].context[name] || null;
-      }
-      stack.push({context: new_context, index: -1});
-      return new_context;
-    },
-
-    Pop: function() {
-      stack.pop();
-    },
-
-    next: function() {
-      var stacktop = stack[stack.length-1];
-
-      // Now we're iterating -- push a new mutable object onto the stack
-      if (stacktop.index == -1) {
-        stacktop = {context: null, index: 0};
-        stack.push(stacktop);
-      }
-
-      // The thing we're iterating over
-      var context_array = stack[stack.length-2].context;
-
-      // We're already done
-      if (stacktop.index == context_array.length) {
-        stack.pop();
-        return undefined;  // sentinel to say that we're done
-      }
-
-      stacktop.context = context_array[stacktop.index++];
-      return true;  // OK, we mutated the stack
-    },
-
-    _Undefined: function(name) {
-      if (undefined_str === undefined) {
-        throw {
-          name: 'UndefinedVariable', message: name + ' is not defined'
-        };
-      } else {
-        return undefined_str;
-      }
-    },
-
-    _LookUpStack: function(name) {
-      var i = stack.length - 1;
-      while (true) {
-        var frame = stack[i];
-        if (name == '@index') {
-          if (frame.index != -1) {  // -1 is undefined
-            return frame.index;
-          }
-        } else {
-          var context = frame.context;
-          if (typeof context === 'object') {
-            var value = context[name];
-            if (value !== undefined) {
-              return value;
-            }
-          }
-        }
-        i--;
-        if (i <= -1) {
-          return this._Undefined(name);
-        }
-      }
-    },
-
-    get: function(name) {
-      if (name == '@') {
-        return stack[stack.length-1].context;
-      }
-      var parts = name.split('.');
-      var value = this._LookUpStack(parts[0]);
-      if (parts.length > 1) {
-        for (var i=1; i<parts.length; i++) {
-          value = value[parts[i]];
-          if (value === undefined) {
-            return this._Undefined(parts[i]);
-          }
-        }
-      }
-      return value;
-    }
-
-  };
-}
-
-
-// Crockford's "functional inheritance" pattern
-
-var _AbstractSection = function(spec) {
-  var that = {};
-  that.current_clause = [];
-
-  that.Append = function(statement) {
-    that.current_clause.push(statement);
-  };
-
-  that.AlternatesWith = function() {
-    throw {
-      name: 'TemplateSyntaxError',
-      message:
-          '{.alternates with} can only appear with in {.repeated section ...}'
-    };
-  };
-
-  that.NewOrClause = function(pred) {
-    throw { name: 'NotImplemented' };  // "Abstract"
-  };
-
-  return that;
-};
-
-var _Section = function(spec) {
-  var that = _AbstractSection(spec);
-  that.statements = {'default': that.current_clause};
-
-  that.section_name = spec.section_name;
-
-  that.Statements = function(clause) {
-    clause = clause || 'default';
-    return that.statements[clause] || [];
-  };
-
-  that.NewOrClause = function(pred) {
-    if (pred) {
-      throw {
-        name: 'TemplateSyntaxError',
-        message: '{.or} clause only takes a predicate inside predicate blocks'
-      };
-    }
-    that.current_clause = [];
-    that.statements['or'] = that.current_clause;
-  };
-
-  return that;
-};
-
-// Repeated section is like section, but it supports {.alternates with}
-var _RepeatedSection = function(spec) {
-  var that = _Section(spec);
-
-  that.AlternatesWith = function() {
-    that.current_clause = [];
-    that.statements['alternate'] = that.current_clause;
-  };
-
-  return that;
-};
-
-// Represents a sequence of predicate clauses.
-var _PredicateSection = function(spec) {
-  var that = _AbstractSection(spec);
-  // Array of func, statements
-  that.clauses = [];
-
-  that.NewOrClause = function(pred) {
-    // {.or} always executes if reached, so use identity func with no args
-    pred = pred || [function(x) { return true; }, null];
-    that.current_clause = [];
-    that.clauses.push([pred, that.current_clause]);
-  };
-
-  return that;
-};
-
-
-function _Execute(statements, context, callback) {
-  for (var i=0; i<statements.length; i++) {
-    var statement = statements[i];
-
-    if (typeof(statement) == 'string') {
-      callback(statement);
-    } else {
-      var func = statement[0];
-      var args = statement[1];
-      func(args, context, callback);
-    }
-  }
-}
-
-function _DoSubstitute(statement, context, callback) {
-  var value;
-  value = context.get(statement.name);
-
-  // Format values
-  for (var i=0; i<statement.formatters.length; i++) {
-    var pair = statement.formatters[i];
-    var formatter = pair[0];
-    var args = pair[1];
-    value = formatter(value, context, args);
-  }
-
-  callback(value);
-}
-
-// for [section foo]
-function _DoSection(args, context, callback) {
-
-  var block = args;
-  var value = context.PushSection(block.section_name);
-  var do_section = false;
-
-  // "truthy" values should have their sections executed.
-  if (value) {
-    do_section = true;
-  }
-  // Except: if the value is a zero-length array (which is "truthy")
-  if (value && value.length === 0) {
-    do_section = false;
-  }
-
-  if (do_section) {
-    _Execute(block.Statements(), context, callback);
-    context.Pop();
-  } else {  // Empty list, None, False, etc.
-    context.Pop();
-    _Execute(block.Statements('or'), context, callback);
-  }
-}
-
-// {.pred1?} A {.or pred2?} B ... {.or} Z {.end}
-function _DoPredicates(args, context, callback) {
-  // Here we execute the first clause that evaluates to true, and then stop.
-  var block = args;
-  var value = context.get('@');
-  for (var i=0; i<block.clauses.length; i++) {
-    var clause = block.clauses[i];
-    var predicate = clause[0][0];
-    var pred_args = clause[0][1];
-    var statements = clause[1];
-
-    var do_clause = predicate(value, context, pred_args);
-    if (do_clause) {
-      _Execute(statements, context, callback);
-      break;
-    }
-  }
-}
-
-
-function _DoRepeatedSection(args, context, callback) {
-  var block = args;
-
-  items = context.PushSection(block.section_name);
-  pushed = true;
-
-  if (items && items.length > 0) {
-    // TODO: check that items is an array; apparently this is hard in JavaScript
-    //if type(items) is not list:
-    //  raise EvaluationError('Expected a list; got %s' % type(items))
-
-    // Execute the statements in the block for every item in the list.
-    // Execute the alternate block on every iteration except the last.  Each
-    // item could be an atom (string, integer, etc.) or a dictionary.
-    
-    var last_index = items.length - 1;
-    var statements = block.Statements();
-    var alt_statements = block.Statements('alternate');
-
-    for (var i=0; context.next() !== undefined; i++) {
-      _Execute(statements, context, callback);
-      if (i != last_index) {
-        _Execute(alt_statements, context, callback);
-      }
-    }
-  } else {
-    _Execute(block.Statements('or'), context, callback);
-  }
-
-  context.Pop();
-}
-
-
-var _SECTION_RE = /(repeated)?\s*(section)\s+(\S+)?/;
-var _OR_RE = /or(?:\s+(.+))?/;
-var _IF_RE = /if(?:\s+(.+))?/;
-
-
-// Turn a object literal, function, or Registry into a Registry
-function MakeRegistry(obj) {
-  if (!obj) {
-    // if null/undefined, use a totally empty FunctionRegistry
-    return new FunctionRegistry();
-  } else if (typeof obj === 'function') {
-    return new CallableRegistry(obj);
-  } else if (obj.lookup !== undefined) {
-    // TODO: Is this a good pattern?  There is a namespace conflict where get
-    // could be either a formatter or a method on a FunctionRegistry.
-    // instanceof might be more robust.
-    return obj;
-  } else if (typeof obj === 'object') {
-    return new SimpleRegistry(obj);
-  }
-}
-
-// TODO: The compile function could be in a different module, in case we want to
-// compile on the server side.
-function _Compile(template_str, options) {
-  var more_formatters = MakeRegistry(options.more_formatters);
-
-  // default formatters with arguments
-  var default_formatters = PrefixRegistry([
-      {name: 'pluralize', func: _Pluralize},
-      {name: 'cycle', func: _Cycle}
-      ]);
-  var all_formatters = new ChainedRegistry([
-      more_formatters,
-      SimpleRegistry(DEFAULT_FORMATTERS),
-      default_formatters
-      ]);
-
-  var more_predicates = MakeRegistry(options.more_predicates);
-
-  // TODO: Add defaults
-  var all_predicates = new ChainedRegistry([
-      more_predicates, SimpleRegistry(DEFAULT_PREDICATES)
-      ]);
-
-  // We want to allow an explicit null value for default_formatter, which means
-  // that an error is raised if no formatter is specified.
-  var default_formatter;
-  if (options.default_formatter === undefined) {
-    default_formatter = 'str';
-  } else {
-    default_formatter = options.default_formatter;
-  }
-
-  function GetFormatter(format_str) {
-    var pair = all_formatters.lookup(format_str);
-    if (!pair[0]) {
-      throw {
-        name: 'BadFormatter',
-        message: format_str + ' is not a valid formatter'
-      };
-    }
-    return pair;
-  }
-
-  function GetPredicate(pred_str) {
-    var pair = all_predicates.lookup(pred_str);
-    if (!pair[0]) {
-      throw {
-        name: 'BadPredicate',
-        message: pred_str + ' is not a valid predicate'
-      };
-    }
-    return pair;
-  }
-
-  var format_char = options.format_char || '|';
-  if (format_char != ':' && format_char != '|') {
-    throw {
-      name: 'ConfigurationError',
-      message: 'Only format characters : and | are accepted'
-    };
-  }
-
-  var meta = options.meta || '{}';
-  var n = meta.length;
-  if (n % 2 == 1) {
-    throw {
-      name: 'ConfigurationError',
-      message: meta + ' has an odd number of metacharacters'
-    };
-  }
-  var meta_left = meta.substring(0, n/2);
-  var meta_right = meta.substring(n/2, n);
-
-  var token_re = _MakeTokenRegex(meta_left, meta_right);
-  var current_block = _Section({});
-  var stack = [current_block];
-
-  var strip_num = meta_left.length;  // assume they're the same length
-
-  var token_match;
-  var last_index = 0;
-
-  while (true) {
-    token_match = token_re.exec(template_str);
-    if (token_match === null) {
-      break;
-    } else {
-      var token = token_match[0];
-    }
-
-    // Add the previous literal to the program
-    if (token_match.index > last_index) {
-      var tok = template_str.slice(last_index, token_match.index);
-      current_block.Append(tok);
-    }
-    last_index = token_re.lastIndex;
-
-    var had_newline = false;
-    if (token.slice(-1) == '\n') {
-      token = token.slice(null, -1);
-      had_newline = true;
-    }
-
-    token = token.slice(strip_num, -strip_num);
-
-    if (token.charAt(0) == '#') {
-      continue;  // comment
-    }
-
-    if (token.charAt(0) == '.') {  // Keyword
-      token = token.substring(1, token.length);
-
-      var literal = {
-          'meta-left': meta_left,
-          'meta-right': meta_right,
-          'space': ' ',
-          'tab': '\t',
-          'newline': '\n'
-          }[token];
-
-      if (literal !== undefined) {
-        current_block.Append(literal);
-        continue;
-      }
-
-      var new_block, func;
-
-      var section_match = token.match(_SECTION_RE);
-      if (section_match) {
-        var repeated = section_match[1];
-        var section_name = section_match[3];
-
-        if (repeated) {
-          func = _DoRepeatedSection;
-          new_block = _RepeatedSection({section_name: section_name});
-        } else {
-          func = _DoSection;
-          new_block = _Section({section_name: section_name});
-        }
-        current_block.Append([func, new_block]);
-        stack.push(new_block);
-        current_block = new_block;
-        continue;
-      }
-
-      var pred_str, pred;
-
-      // Check {.or pred?} before {.pred?}
-      var or_match = token.match(_OR_RE);
-      if (or_match) {
-        pred_str = or_match[1];
-        pred = pred_str ? GetPredicate(pred_str) : null;
-        current_block.NewOrClause(pred);
-        continue;
-      }
-
-      // Match either {.pred?} or {.if pred?}
-      var matched = false;
-
-      var if_match = token.match(_IF_RE);
-      if (if_match) {
-        pred_str = if_match[1];
-        matched = true;
-      } else if (token.charAt(token.length-1) == '?') {
-        pred_str = token;
-        matched = true;
-      }
-      if (matched) {
-        pred = pred_str ? GetPredicate(pred_str) : null;
-        new_block = _PredicateSection();
-        new_block.NewOrClause(pred);
-        current_block.Append([_DoPredicates, new_block]);
-        stack.push(new_block);
-        current_block = new_block;
-        continue;
-      }
-
-      if (token == 'alternates with') {
-        current_block.AlternatesWith();
-        continue;
-      }
-
-      if (token == 'end') {
-        // End the block
-        stack.pop();
-        if (stack.length > 0) {
-          current_block = stack[stack.length-1];
-        } else {
-          throw {
-            name: 'TemplateSyntaxError',
-            message: 'Got too many {end} statements'
-          };
-        }
-        continue;
-      }
-    }
-
-    // A variable substitution
-    var parts = token.split(format_char);
-    var formatters;
-    var name;
-    if (parts.length == 1) {
-      if (default_formatter === null) {
-          throw {
-            name: 'MissingFormatter',
-            message: 'This template requires explicit formatters.'
-          };
-      }
-      // If no formatter is specified, use the default.
-      formatters = [GetFormatter(default_formatter)];
-      name = token;
-    } else {
-      formatters = [];
-      for (var j=1; j<parts.length; j++) {
-        formatters.push(GetFormatter(parts[j]));
-      }
-      name = parts[0];
-    }
-    current_block.Append([_DoSubstitute, {name: name, formatters: formatters}]);
-    if (had_newline) {
-      current_block.Append('\n');
-    }
-  }
-
-  // Add the trailing literal
-  current_block.Append(template_str.slice(last_index));
-
-  if (stack.length !== 1) {
-    throw {
-      name: 'TemplateSyntaxError',
-      message: 'Got too few {end} statements'
-    };
-  }
-  return current_block;
-}
-
-// The Template class is defined in the traditional style so that users can add
-// methods by mutating the prototype attribute.  TODO: Need a good idiom for
-// inheritance without mutating globals.
-
-function Template(template_str, options) {
-
-  // Add 'new' if we were not called with 'new', so prototyping works.
-  if(!(this instanceof Template)) {
-    return new Template(template_str, options);
-  }
-
-  this._options = options || {};
-  this._program = _Compile(template_str, this._options);
-}
-
-Template.prototype.render = function(data_dict, callback) {
-  // options.undefined_str can either be a string or undefined
-  var context = _ScopedContext(data_dict, this._options.undefined_str);
-  _Execute(this._program.Statements(), context, callback);
-};
-
-Template.prototype.expand = function(data_dict) {
-  var tokens = [];
-  this.render(data_dict, function(x) { tokens.push(x); });
-  return tokens.join('');
-};
-
-// fromString is a construction method that allows metadata to be written at the
-// beginning of the template string.  See Python's FromFile for a detailed
-// description of the format.
-//
-// The argument 'options' takes precedence over the options in the template, and
-// can be used for non-serializable options like template formatters.
-
-var OPTION_RE = /^([a-zA-Z\-]+):\s*(.*)/;
-var OPTION_NAMES = [
-    'meta', 'format-char', 'default-formatter', 'undefined-str'];
-// Use this "linear search" instead of Array.indexOf, which is nonstandard
-var OPTION_NAMES_RE = new RegExp(OPTION_NAMES.join('|'));
-
-function fromString(s, options) {
-  var parsed = {};
-  var begin = 0, end = 0;
-
-  while (true) {
-    var parsedOption = false;
-    end = s.indexOf('\n', begin);
-    if (end == -1) {
-      break;
-    }
-    var line = s.slice(begin, end);
-    begin = end+1;
-    var match = line.match(OPTION_RE);
-    if (match !== null) {
-      var name = match[1].toLowerCase(), value = match[2];
-      if (name.match(OPTION_NAMES_RE)) {
-        name = name.replace('-', '_');
-        value = value.replace(/^\s+/, '').replace(/\s+$/, '');
-        if (name == 'default_formatter' && value.toLowerCase() == 'none') {
-          value = null;
-        }
-        parsed[name] = value;
-        parsedOption = true;
-      }
-    }
-    if (!parsedOption) {
-      break;
-    }
-  }
-  // TODO: This doesn't enforce the blank line between options and template, but
-  // that might be more trouble than it's worth
-  if (parsed !== {}) {
-    body = s.slice(begin);
-  } else {
-    body = s;
-  }
-  for (var o in options) {
-    parsed[o] = options[o];
-  }
-  return Template(body, parsed);
-}
-
-
-// We just export one name for now, the Template "class".
-// We need HtmlEscape in the browser tests, so might as well export it.
-
-return {
-    Template: Template, HtmlEscape: HtmlEscape,
-    FunctionRegistry: FunctionRegistry, SimpleRegistry: SimpleRegistry,
-    CallableRegistry: CallableRegistry, ChainedRegistry: ChainedRegistry,
-    fromString: fromString,
-    // Private but exposed for testing
-    _Section: _Section
-    };
-
-}();

+ 0 - 339
js/jstparser.js

@@ -1,339 +0,0 @@
-/*
-Copyright 2008, mark turansky (www.markturansky.com)
-Copyright 2010, Andrew Kelley (superjoesoftware.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-The Software shall be used for Good, not Evil.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE. 
-
-(This is the license from www.json.org and I think it's awesome)
-*/
-
-String.prototype.endsWith = function endsWith(c) {
-    if (this.charAt(this.length - 1) == c) {
-        return true;
-    } else {
-        return false;
-    }
-};
-
-String.prototype.startsWith = function startsWith(c) {
-    if (this.charAt(0) == c) {
-        return true;
-    } else {
-        return false;
-    }
-};
-
-RegExp.quote = function(str) {
-    return str.replace(/([.?*+\^$\[\]\\(){}\-])/g, "\\$1");
-};
-
-String.prototype.replaceAll = function replaceAll(a, b) {
-    return this.replace(new RegExp(RegExp.quote(a), 'g'), b);
-};
-
-var Jst = function () {
-    // private variables:
-    var that; // reference to the public object
-    
-    // all write and writeln functions eval'd by Jst
-    // concatenate to this internal variable
-    var html = "";
-
-    // private functions
-    function CharacterStack(str) {
-        var i;
-
-        this.characters = [];
-        this.peek = function peek() {
-            return this.characters[this.characters.length - 1];
-        };
-        this.pop = function pop() {
-            return this.characters.pop();
-        };
-        this.push = function push(c) {
-            this.characters.push(c);
-        };
-        this.hasMore =  function hasMore() {
-            if (this.characters.length > 0) {
-                return true;
-            } else {
-                return false;
-            }
-        };
-
-        for (i = str.length; i >= 0; i -= 1) {
-            this.characters.push(str.charAt(i));
-        }
-    }
-
-    function StringWriter() {
-        this.str = "";
-        this.write = function write(s) {
-            this.str += s;
-        };
-        this.toString = function toString() {
-            return this.str;
-        };
-    }
-
-    function parseScriptlet(stack) {
-        var fragment = new StringWriter();
-        var c; // character
-
-        while (stack.hasMore()) {
-            if (stack.peek() == '%') { //possible end delimiter
-                c = stack.pop();
-                if (stack.peek() == '>') { //end delimiter
-                    // pop > so that it is not available to main parse loop
-                    stack.pop();
-                    if (stack.peek() == '\n') {
-                        fragment.write(stack.pop());
-                    }
-                    break;
-                } else {
-                    fragment.write(c);
-                }
-            } else {
-                fragment.write(stack.pop());
-            }
-        }
-        return fragment.toString();
-    }
-
-    function isOpeningDelimiter(c) {
-        if (c == "<" || c == "%lt;") {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    function isClosingDelimiter(c) {
-        if (c == ">" || c == "%gt;") {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    function appendExpressionFragment(writer, fragment, jstWriter) {
-        var i,j;
-        var c;
-
-        // check to be sure quotes are on both ends of a string literal
-        if (fragment.startsWith("\"") && !fragment.endsWith("\"")) {
-            //some scriptlets end with \n, especially if the script ends the file
-            if (fragment.endsWith("\n") && fragment.charAt(fragment.length - 2) == '"') {
-                //we're ok...
-            } else {
-                throw { "message":"'" + fragment + "' is not properly quoted"};
-            }
-        }
-
-        if (!fragment.startsWith("\"") && fragment.endsWith("\"")) {
-            throw { "message":"'" + fragment + "' is not properly quoted"};
-        }
-
-        // print or println?
-        if (fragment.endsWith("\n")) {
-            writer.write(jstWriter + "ln(");
-            //strip the newline
-            fragment = fragment.substring(0, fragment.length - 1);
-        } else {
-            writer.write(jstWriter + "(");
-        }
-
-        if (fragment.startsWith("\"") && fragment.endsWith("\"")) {
-            //strip the quotes
-            fragment = fragment.substring(1, fragment.length - 1);
-            writer.write("\"");
-            for (i = 0; i < fragment.length; i += 1) {
-                c = fragment.charAt(i);
-                if (c == '"') {
-                    writer.write("\\");
-                    writer.write(c);
-                }
-            }
-            writer.write("\"");
-        } else {
-            for (j = 0; j < fragment.length; j += 1) {
-                writer.write(fragment.charAt(j));
-            }
-        }
-
-        writer.write(");");
-    }
-
-    function appendTextFragment(writer, fragment) {
-        var i;
-        var c;
-
-        if (fragment.endsWith("\n")) {
-            writer.write("writeln(\"");
-        } else {
-            writer.write("write(\"");
-        }
-
-        for (i = 0; i < fragment.length; i += 1) {
-            c = fragment.charAt(i);
-            if (c == '"') {
-                writer.write("\\");
-            }
-            // we took care of the line break with print vs. println
-            if (c != '\n' && c != '\r') {
-                writer.write(c);
-            }
-        }
-
-        writer.write("\");");
-    }
-
-    function parseExpression(stack) {
-        var fragment = new StringWriter();
-        var c;
-
-        while (stack.hasMore()) {
-            if (stack.peek() == '%') { //possible end delimiter
-                c = stack.pop();
-                if (isClosingDelimiter(stack.peek())) { //end delimiter
-                    //pop > so that it is not available to main parse loop
-                    stack.pop();
-                    if (stack.peek() == '\n') {
-                        fragment.write(stack.pop());
-                    }
-                    break;
-                } else {
-                    fragment.write("%");
-                }
-            } else {
-                fragment.write(stack.pop());
-            }
-        }
-
-        return fragment.toString();
-    }
-
-    function parseText(stack) {
-        var fragment = new StringWriter();
-        var c,d;
-
-        while (stack.hasMore()) {
-            if (isOpeningDelimiter(stack.peek())) { //possible delimiter
-                c = stack.pop();
-                if (stack.peek() == '%') { // delimiter!
-                    // push c onto the stack to be used in main parse loop
-                    stack.push(c);
-                    break;
-                } else {
-                    fragment.write(c);
-                }
-            } else {
-                d = stack.pop();
-                fragment.write(d);
-                if (d == '\n') { //done with this fragment.  println it.
-                    break;
-                }
-            }
-        }
-        return fragment.toString();
-    }
-
-    function safeWrite(s) {
-        s = s.toString();
-        s = s.replaceAll('&', '&amp;');
-        s = s.replaceAll('"', '&quot;');
-        s = s.replaceAll('<', '&lt;');
-        s = s.replaceAll('>', '&gt;');
-        html += s;
-    }
-
-    function safeWriteln(s) {
-        safeWrite(s + "\n");
-    }
-
-    function write(s) {
-        html += s;
-    }
-
-    function writeln(s) {
-        write(s + "\n");
-    }
-
-    that = {
-        // public methods:
-        // pre-compile a template for quicker rendering. save the return value and 
-        // pass it to evaluate.
-        compile: function (src) {
-            var stack = new CharacterStack(src);
-            var writer = new StringWriter();
-
-            var c;
-            var fragment;
-            while (stack.hasMore()) {
-                if (isOpeningDelimiter(stack.peek())) { //possible delimiter
-                    c = stack.pop();
-                    if (stack.peek() == '%') { //delimiter!
-                        c = stack.pop();
-                        if (stack.peek() == "=") {
-                            // expression, escape all html
-                            stack.pop();
-                            fragment = parseExpression(stack);
-                            appendExpressionFragment(writer, fragment,
-                                "safeWrite");
-                        } else if (stack.peek() == "+") {
-                            // expression, don't escape html
-                            stack.pop();
-                            fragment = parseExpression(stack);
-                            appendExpressionFragment(writer, fragment,
-                                "write");
-                        } else {
-                            fragment = parseScriptlet(stack);
-                            writer.write(fragment);
-                        }
-                    } else {  //not a delimiter
-                        stack.push(c);
-                        fragment = parseText(stack);
-                        appendTextFragment(writer, fragment);
-                    }
-                } else {
-                    fragment = parseText(stack);
-                    appendTextFragment(writer, fragment);
-                }
-            }
-            return writer.toString();
-        },
-
-        // evaluate a pre-compiled script. recommended approach
-        evaluate: function (script, args) {
-            with(args) {
-                html = "";
-                eval(script);
-                return html;
-            }
-        },
-
-        // if you're lazy, you can use this
-        evaluateSingleShot: function (src, args) {
-            return this.evaluate(this.compile(src), args);
-        }
-    };
-    return that;
-}();

+ 105 - 47
package.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.9.5" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+<package packagerversion="1.10.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  <name>v8js</name>
  <channel>pecl.php.net</channel>
  <summary>V8 Javascript Engine for PHP</summary>
@@ -16,8 +16,8 @@
   <email>[email protected]</email>
   <active>yes</active>
  </lead>
- <date>2016-03-05</date>
- <time>23:24:43</time>
+ <date>2016-05-22</date>
+ <time>21:16:17</time>
  <version>
   <release>1.2.0</release>
   <api>1.2.0</api>
@@ -39,20 +39,6 @@ Merge improvements from V8Js for PHP 5.x (version 0.5.0) to PHP 7.0 branch:
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
-   <file baseinstalldir="/" md5sum="9532b5359069afc8bdccb5e01c288493" name="js/json-template.js" role="data" />
-   <file baseinstalldir="/" md5sum="a490e5a575deeceab9872035fbe74eb1" name="js/jstparser.js" role="data" />
-   <file baseinstalldir="/" md5sum="ddbae43f7979c5fe082e08ff4f2e8765" name="samples/dlopen.supp" role="src" />
-   <file baseinstalldir="/" md5sum="045e094122747e8e71f02ffc1ba88d4a" name="samples/test_call.php" role="php" />
-   <file baseinstalldir="/" md5sum="87cb416c6bc17d657abbbf314a41ff85" name="samples/test_callback.php" role="php" />
-   <file baseinstalldir="/" md5sum="ce6958537730e437aecf4f99044a78ce" name="samples/test_closure.php" role="php" />
-   <file baseinstalldir="/" md5sum="07f6c82a56280bfe86dce8feecf52c45" name="samples/test_crash.php" role="php" />
-   <file baseinstalldir="/" md5sum="0e89021f07fd28bef6d8444b9fda3f9a" name="samples/test_date.php" role="php" />
-   <file baseinstalldir="/" md5sum="8e224fd19d8fda0c532d410153cc5b01" name="samples/test_dumper.php" role="php" />
-   <file baseinstalldir="/" md5sum="220347fdf7c1c14f2c346fd97b55a025" name="samples/test_exception.php" role="php" />
-   <file baseinstalldir="/" md5sum="6432eedc51c3dd34abd0b0986c6268da" name="samples/test_exception2.php" role="php" />
-   <file baseinstalldir="/" md5sum="194dab9bb37e8456afd7eb5dd9f7a81e" name="samples/test_extend.php" role="php" />
-   <file baseinstalldir="/" md5sum="f9af8067e0113ca3048573813eee7272" name="samples/test_extension.php" role="php" />
-   <file baseinstalldir="/" md5sum="b9491aff4c5647784f0c7ae9e0638c18" name="samples/test_method.php" role="php" />
    <file baseinstalldir="/" md5sum="30d379088150c4d4dd0cd97ea21d8156" name="tests/array_access.phpt" role="test" />
    <file baseinstalldir="/" md5sum="c91b16332cdc186ecf8a8dc5581f17c7" name="tests/array_access_001.phpt" role="test" />
    <file baseinstalldir="/" md5sum="2f1e1a8788439173b4629d63e7bbe083" name="tests/array_access_002.phpt" role="test" />
@@ -164,7 +150,7 @@ Merge improvements from V8Js for PHP 5.x (version 0.5.0) to PHP 7.0 branch:
    <file baseinstalldir="/" md5sum="46d8c3c3efb87cb310af0d9dbcd84431" name="tests/leak-php-object.phpt" role="test" />
    <file baseinstalldir="/" md5sum="23fd9824ef435408ca93c01a79247237" name="tests/long.phpt" role="test" />
    <file baseinstalldir="/" md5sum="8fb9e33bf07c2e4fdc7dee4f499fded7" name="tests/magic_func.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="4b4649efff8a97cb6ac60e78d70af3df" name="tests/memory_limit.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="8645a8f43663294192f48ed7639afd0f" name="tests/memory_limit.phpt" role="test" />
    <file baseinstalldir="/" md5sum="4ce74d22310618686e37829a24c7ce8d" name="tests/multi-object.phpt" role="test" />
    <file baseinstalldir="/" md5sum="34e4df80d655576e146732118d29880e" name="tests/multi.phpt" role="test" />
    <file baseinstalldir="/" md5sum="6b603cb54da83955ca6a309352858bce" name="tests/null_byte_string.phpt" role="test" />
@@ -172,6 +158,7 @@ Merge improvements from V8Js for PHP 5.x (version 0.5.0) to PHP 7.0 branch:
    <file baseinstalldir="/" md5sum="97ac85efe2aa7318c9a4daa49d544165" name="tests/object_dom.phpt" role="test" />
    <file baseinstalldir="/" md5sum="7c34aed266f6cdb2278a5561cd6de45b" name="tests/object_method_call.phpt" role="test" />
    <file baseinstalldir="/" md5sum="ed1d6d0aafe39f41545c375507507564" name="tests/object_passback.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="d16c0d23ff223b006a9c9c83df257708" name="tests/object_passback_002.phpt" role="test" />
    <file baseinstalldir="/" md5sum="95e8658755180e9dc7533c0ed4d61bb2" name="tests/object_prototype.phpt" role="test" />
    <file baseinstalldir="/" md5sum="732770da7b148dcb702894dcb9462674" name="tests/object_reuse.phpt" role="test" />
    <file baseinstalldir="/" md5sum="e49bb15778697eea7d717de4cfc0c385" name="tests/php_exceptions_001.phpt" role="test" />
@@ -191,61 +178,63 @@ Merge improvements from V8Js for PHP 5.x (version 0.5.0) to PHP 7.0 branch:
    <file baseinstalldir="/" md5sum="76bdcf96941321e372516413f65c8576" name="tests/property_visibility__set.phpt" role="test" />
    <file baseinstalldir="/" md5sum="6fffe3110dcad47ed41cb7d026b97e49" name="tests/pthreads_001.phpt" role="test" />
    <file baseinstalldir="/" md5sum="5a97aad8f07d8a9a67882b614e956c72" name="tests/regression_121.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="130c02c6d60a1a9943ec1a9c4d0f7085" name="tests/return_this_001.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="be5746f51f76ad249f6521dedd240462" name="tests/return_this_basic.phpt" role="test" />
    <file baseinstalldir="/" md5sum="3b256c85a054b728a37b3429d4157f02" name="tests/return_value.phpt" role="test" />
    <file baseinstalldir="/" md5sum="1a9c261661b08cfc6879f7f427b9f0de" name="tests/serialize_001.phpt" role="test" />
    <file baseinstalldir="/" md5sum="53d16ab569633b031059bfd92ad888da" name="tests/serialize_002.phpt" role="test" />
    <file baseinstalldir="/" md5sum="f13ed20a96e5547b3cc0541259fe51e5" name="tests/serialize_basic.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="c67118d201153666d586134c8f275434" name="tests/set_memory_limit_001.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="b4865b48112b4c224f1704a1c29bfc8e" name="tests/set_memory_limit_003.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="b66f99b6b019a3abb69dc77257d984df" name="tests/set_memory_limit_basic.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="352885da189cfd10573814bcd4a59eda" name="tests/set_time_limit_001.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="3a2ccc98e237ae29606d7079cb62c168" name="tests/set_time_limit_002.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="824c26f9f4cc9c469772fb6f0719c41c" name="tests/set_time_limit_003.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="c54ef9796fe5279ea421ba74c2c2b156" name="tests/set_time_limit_004.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="529ca5dc55aba00a1e72e7759d822868" name="tests/set_time_limit_basic.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="e610855ff600c2da79009389c3268aa2" name="tests/set_average_object_size_basic.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="1a5d2983c48712708826f746fa0c9d5f" name="tests/set_memory_limit_001.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="88f5a3ac11d1c3898a02639d5b0f538d" name="tests/set_memory_limit_003.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="9a29a6c28f37c2b7abf6d0e8b26702e9" name="tests/set_memory_limit_basic.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="a7fc4f90d65dadefe169c2736119d958" name="tests/set_time_limit_001.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="7943ab689a61df2ab062ab59b3c779a6" name="tests/set_time_limit_002.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="9ef513915ae9808383eda06ffd0ea0c5" name="tests/set_time_limit_003.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="cb8fe5f7776a58e6f37f7967cc2b3422" name="tests/set_time_limit_004.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="c7c54b949f2ba452bf9a6c3085aaf6bc" name="tests/set_time_limit_basic.phpt" role="test" />
    <file baseinstalldir="/" md5sum="4886fac4c06e560fd0fef88c81357870" name="tests/skipif.inc" role="test" />
    <file baseinstalldir="/" md5sum="bdf52e983e410ace13f78611e2684673" name="tests/timezones.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="ad1acf475092854b3fa1b5d061405f41" name="tests/time_limit.phpt" role="test" />
+   <file baseinstalldir="/" md5sum="90ccf12ee3ce42b555858aa71623f9c1" name="tests/time_limit.phpt" role="test" />
    <file baseinstalldir="/" md5sum="73075b58cfce4db091b7f6f8c480e0a4" name="tests/use_after_dispose.phpt" role="test" />
    <file baseinstalldir="/" md5sum="aabbf8a564c546eef38c1e9e3b02bb14" name="tests/v8_unset_property.phpt" role="test" />
    <file baseinstalldir="/" md5sum="35ce3816ae00e697fca26142c46e0c79" name="tests/v8_write_property.phpt" role="test" />
    <file baseinstalldir="/" md5sum="ae504a63e5ff800e3aa7d529835d0e8e" name="tests/variable_passing.phpt" role="test" />
    <file baseinstalldir="/" md5sum="1bd7738aeeb5cf80d80561554f59f2ed" name="tests/var_dump.phpt" role="test" />
    <file baseinstalldir="/" md5sum="19a662f86a1bed6c0a12a276cdabe7ae" name="appveyor.yml" role="data" />
-   <file baseinstalldir="/" md5sum="f6f34bf39ed7d66fd2271310de9569a9" name="config.m4" role="src" />
-   <file baseinstalldir="/" md5sum="9d75e21fff18328abe6636f9504d1e6a" name="config.w32" role="src" />
+   <file baseinstalldir="/" md5sum="28db201e7171789b1ade57210a6412d1" name="config.m4" role="src" />
+   <file baseinstalldir="/" md5sum="6e2812f6a18b57fad7a026656b423836" name="config.w32" role="src" />
    <file baseinstalldir="/" md5sum="cea72666538d5b0b80a64ccdbda24919" name="CREDITS" role="doc" />
    <file baseinstalldir="/" md5sum="9f5b5f41204bcde55d9df87d5a970b30" name="LICENSE" role="doc" />
-   <file baseinstalldir="/" md5sum="679b9046688ed6f60969415b182b1cac" name="Makefile.frag" role="src" />
+   <file baseinstalldir="/" md5sum="716540317726f97d61cbbdc155102e01" name="Makefile.frag" role="src" />
    <file baseinstalldir="/" md5sum="31e331386def7ce98943694151c0d5cb" name="Makefile.travis" role="src" />
    <file baseinstalldir="/" md5sum="0e23fa6446e52a3b1cff8b18a6e0bd79" name="php_v8js.h" role="src" />
-   <file baseinstalldir="/" md5sum="817677afde6e2feec6f2c35059b76c5a" name="php_v8js_macros.h" role="src" />
-   <file baseinstalldir="/" md5sum="f6f2c7689fd36998b2f2a20a5756ee20" name="README.Linux.md" role="doc" />
+   <file baseinstalldir="/" md5sum="5725a21a7063305213791962116c5f5c" name="php_v8js_macros.h" role="src" />
+   <file baseinstalldir="/" md5sum="8d51598bd192df6e257ab0374d7f47c9" name="README.Linux.md" role="doc" />
    <file baseinstalldir="/" md5sum="d686d8e52af92521d4b8b0e86d00c463" name="README.MacOS.md" role="doc" />
-   <file baseinstalldir="/" md5sum="10318f67634c0f80ff13f4ee7bccbfb0" name="README.md" role="doc" />
+   <file baseinstalldir="/" md5sum="774a66bf34ff0923e3a2e075c9a8ce8a" name="README.md" role="doc" />
    <file baseinstalldir="/" md5sum="e88cfe2d7e76c7be1db283766a10dd51" name="README.Win32.md" role="doc" />
-   <file baseinstalldir="/" md5sum="542f52c54898f33ac53b173970cba305" name="test.php" role="php" />
-   <file baseinstalldir="/" md5sum="65294fadb5ed766094b1f587fc20ad37" name="TODO" role="doc" />
-   <file baseinstalldir="/" md5sum="40a7872fac33eeefa3e31a80a391f347" name="v8js.cc" role="src" />
-   <file baseinstalldir="/" md5sum="e4271e069340cce3b3c039c2ae6bf19b" name="v8js_array_access.cc" role="src" />
+   <file baseinstalldir="/" md5sum="d4a8e3fd2e228c61db4e76e7da10fc61" name="v8js-0.6.3.tgz" role="data" />
+   <file baseinstalldir="/" md5sum="98c0f00bcb80411b72422232fcd575f3" name="v8js.cc" role="src" />
+   <file baseinstalldir="/" md5sum="33fca37296f54143e6d57ccd819b9c83" name="v8js_array_access.cc" role="src" />
    <file baseinstalldir="/" md5sum="7baf3fe5b77d1374b39a1d8332e05df4" name="v8js_array_access.h" role="src" />
-   <file baseinstalldir="/" md5sum="e65c88069439ec71ed5a65fa091a43d6" name="v8js_class.cc" role="src" />
-   <file baseinstalldir="/" md5sum="11d20c0e080ace815d4d1d4bfcff0559" name="v8js_class.h" role="src" />
+   <file baseinstalldir="/" md5sum="3e490a7a00f2faa190d3aaa3fd63b13c" name="v8js_class.cc" role="src" />
+   <file baseinstalldir="/" md5sum="a69966968a1e3610150ebbd752f39483" name="v8js_class.h" role="src" />
    <file baseinstalldir="/" md5sum="4fc9e39231e977ac0d415f1682fcb2bd" name="v8js_commonjs.cc" role="src" />
    <file baseinstalldir="/" md5sum="32a5d1a65f64ec37ec294f496fc11ff1" name="v8js_commonjs.h" role="src" />
-   <file baseinstalldir="/" md5sum="af6fb42ed4822ff6e2defd196b6546a8" name="v8js_convert.cc" role="src" />
-   <file baseinstalldir="/" md5sum="7a2a998e0e6cbca26c223875a529fca5" name="v8js_exceptions.cc" role="src" />
+   <file baseinstalldir="/" md5sum="52ec041be78a47269a39ee0cc66d8530" name="v8js_convert.cc" role="src" />
+   <file baseinstalldir="/" md5sum="d644e3090f34ea08d62effd2d105588a" name="v8js_exceptions.cc" role="src" />
    <file baseinstalldir="/" md5sum="9d13bf5f413c2d76664670e847e1a801" name="v8js_exceptions.h" role="src" />
    <file baseinstalldir="/" md5sum="accfcfab37ae520fbf01f70fc4210465" name="v8js_generator_export.cc" role="data" />
    <file baseinstalldir="/" md5sum="177f62d686bc4e3465d5599651496b93" name="v8js_generator_export.h" role="src" />
-   <file baseinstalldir="/" md5sum="8e714185feaea8242690989b056267d3" name="v8js_methods.cc" role="src" />
-   <file baseinstalldir="/" md5sum="2e1aecd759e0630831a9f537b1a6141a" name="v8js_object_export.cc" role="src" />
+   <file baseinstalldir="/" md5sum="03ef3415c1e0f0d470acd5163b7df4f1" name="v8js_methods.cc" role="src" />
+   <file baseinstalldir="/" md5sum="840e83cc053b2fb348a2d02702d1ab8d" name="v8js_object_export.cc" role="src" />
    <file baseinstalldir="/" md5sum="bf0141470862151449311b81c947ecb3" name="v8js_object_export.h" role="src" />
-   <file baseinstalldir="/" md5sum="7353fbef57b2efc610ac090bf79d6a26" name="v8js_timer.cc" role="src" />
+   <file baseinstalldir="/" md5sum="6e7e0dfeb40d8fe2f57d309f121b98f8" name="v8js_timer.cc" role="src" />
    <file baseinstalldir="/" md5sum="5935c66a0bd8e819d35cf05d7a9e3c89" name="v8js_timer.h" role="src" />
-   <file baseinstalldir="/" md5sum="7d7dcdb6117d362e59842295e219c2be" name="v8js_v8.cc" role="src" />
+   <file baseinstalldir="/" md5sum="ad9c1c6559eb3687127be2fde12e6068" name="v8js_v8.cc" role="src" />
    <file baseinstalldir="/" md5sum="e551ee5b243164a3806a5b4ec4b2bf30" name="v8js_v8.h" role="src" />
-   <file baseinstalldir="/" md5sum="e13b0e1e1bc7ebb701451e02d76c361d" name="v8js_v8object_class.cc" role="src" />
+   <file baseinstalldir="/" md5sum="48a90b7fa9e00c0cb59f51dc069dc547" name="v8js_v8object_class.cc" role="src" />
    <file baseinstalldir="/" md5sum="1b329fe614d75d56fd6f9fa4f1425f2a" name="v8js_v8object_class.h" role="src" />
    <file baseinstalldir="/" md5sum="86f950271e3c6d37457f20e034a576e5" name="v8js_variables.cc" role="src" />
   </dir>
@@ -584,6 +573,75 @@ This release also merges in new features from V8Js 0.4.0, namely
 - Support V8 5.1 well
    </notes>
   </release>
+  <release>
+   <version>
+    <release>0.6.0</release>
+    <api>0.6.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-03-08</date>
+   <license uri="http://www.php.net/license">The MIT License (MIT)</license>
+   <notes>
+- Allow to adjust the considered &quot;amount of external memory&quot; by objects exported to V8
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.6.1</release>
+    <api>0.6.1</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-03-24</date>
+   <license uri="http://www.php.net/license">The MIT License (MIT)</license>
+   <notes>
+- Fix configuration on MacOS platform
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.6.2</release>
+    <api>0.6.2</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-03-25</date>
+   <license uri="http://www.php.net/license">The MIT License (MIT)</license>
+   <notes>
+- Pass back V8Object instances, don&apos;t re-wrap
+- Retain object identity on &apos;return $this&apos;
+- Retain object identity on JS-side &apos;return this&apos;
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.6.3</release>
+    <api>0.6.3</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-05-22</date>
+   <license uri="http://www.php.net/license">The MIT License (MIT)</license>
+   <notes>
+Bug Fixes
+- Send LowMemoryNotification signals to V8 before imposing memory limit
+- Fix build against V8 version 5.2 (deprecated WeakCallbackInfo &amp; GetHiddenValue et al)
+- Improve/Clarify README
+
+Code Cleanup
+- Removed support for &quot;old-age&quot; V8 versions (i.e. V8 &lt; 4.6.76)
+- Removed old examples and pre-phpt test script
+   </notes>
+  </release>
   <release>
    <version>
     <release>1.2.0</release>
@@ -593,7 +651,7 @@ This release also merges in new features from V8Js 0.4.0, namely
     <release>stable</release>
     <api>stable</api>
    </stability>
-   <date>2016-03-05</date>
+   <date>2016-05-22</date>
    <license uri="http://www.php.net/license">The MIT License (MIT)</license>
    <notes>
 - allow to pass generators from PHP to JS and vice versa

+ 0 - 5
php_v8js_macros.h

@@ -62,9 +62,6 @@ extern "C" {
 /* V8Js Version */
 #define PHP_V8JS_VERSION "1.2.0"
 
-/* Hidden field name used to link JS wrappers with underlying PHP object */
-#define PHPJS_OBJECT_KEY "phpjs::object"
-
 /* Helper macros */
 #define V8JS_GET_CLASS_NAME(var, obj) \
 	v8::String::Utf8Value var(obj->GetConstructorName());
@@ -166,9 +163,7 @@ struct _v8js_process_globals {
 	/* V8 command line flags */
 	char *v8_flags;
 
-#if PHP_V8_API_VERSION >= 3029036
 	v8::Platform *v8_platform;
-#endif
 };
 
 extern struct _v8js_process_globals v8js_process_globals;

+ 0 - 15
samples/dlopen.supp

@@ -1,15 +0,0 @@
-{
-   Ignore dlopen bug #1.
-   Memcheck:Leak
-   ...
-   fun:_dl_open
-   ...
-}
-
-{
-   Ignore dlopen bug #2.
-   Memcheck:Leak
-   ...
-   fun:_dl_close
-   ...
-}

+ 0 - 75
samples/test_call.php

@@ -1,75 +0,0 @@
-<?php
-
-class Foo {
-	var $bar = 'foobar';
-
-	function MyOwnFunc() {}
-
-	function __Get($name) {
-		echo "Called __get(): ";
-		var_dump($name);
-		return "xyz";
-	}
-	function __Set($name, $args) {
-		echo "Called __set(): ";
-		var_dump($name, $args);
-	}
-	function __Isset($name) {
-		echo "Called __isset(): ";
-		var_dump($name);
-		return true;
-	}
-	function __call($name, $args) {
-		echo "Called __call(): ";
-		var_dump($name, $args);
-	}
-	function __Invoke($name, $arg1, $arg2) {
-		echo "Called __invoke(): ";
-		var_dump(func_get_args());
-		return 'foobar';
-	}
-	function __toString() {
-		echo "Called __tostring: ";
-		return $this->bar;
-	}
-}
-
-$blaa = new V8Js();
-$blaa->obj = new Foo;
-
-try {
-  echo "__invoke()\n";
-  $blaa->executeString("var_dump(PHP.obj('arg1','arg2','arg3'));", "invoke_test1 #1.js");
-  echo "------------\n";
-
-  echo " __invoke() with new\n";
-  $blaa->executeString("myobj = new PHP.obj('arg1','arg2','arg3'); var_dump(myobj);", "invoke_test2 #2.js");
-  echo "------------\n";
-
-  echo " __tostring()\n";
-  $blaa->executeString('print(PHP.obj + "\n");', "tostring_test #3.js");
-  echo "------------\n";
-
-  echo " __isset() not called with existing property\n";
-  $blaa->executeString('if ("bar" in PHP.obj) print("bar exists\n");', "isset_test1 #4.js");
-  echo "------------\n";
-
-  echo " __isset() with non-existing property\n";
-  $blaa->executeString('if (!("foobar" in PHP.obj)) print("foobar does not exist\n"); else print("We called __isset and it said yes!\n");', "isset_test2 #5.js");
-  echo "------------\n";
-
-  echo " __get() not called with existing property\n";
-  $blaa->executeString('var_dump(PHP.obj.bar);', "get_test1 #6.js");
-  echo "------------\n";
-
-  echo " __get() with non-existing property\n";
-  $blaa->executeString('var_dump(PHP.obj.foo);', "get_test2 #7.js");
-  echo "------------\n";
-
-  echo " __call()\n";
-  $blaa->executeString('PHP.obj.foo(1,2,3);', "call_test1 #8.js");
-  echo "------------\n";
-
-} catch (V8JsScriptException $e) {
-  echo $e->getMessage(), "\n";
-}

+ 0 - 32
samples/test_callback.php

@@ -1,32 +0,0 @@
-<?php
-	
-$a = new V8Js();
-
-// Should not work with closure
-$a->test = function ($params) { return (method_exists($params, 'cb1')) ? $params->cb1("hello") : false; };
-$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
-var_dump(__LINE__, $ret);
-
-// Test is_a()
-$a->test = function ($params) { return (is_a($params, 'V8Object')) ? $params->cb1("hello") : false; };
-$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
-var_dump(__LINE__, $ret);
-
-// Test is_a()
-$a->test = function ($params) { return (is_a($params, 'V8Function')) ? $params("hello") : false; };
-$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
-var_dump(__LINE__, $ret);
-
-// Should not work with object
-$a->test = function ($params) { return (is_a($params, 'Closure')) ? $params("hello") : false; };
-$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
-var_dump(__LINE__, $ret);
-
-$a->test = function ($params) { var_dump($params); return $params->cb1("hello"); };
-$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
-var_dump(__LINE__, $ret);
-
-// FIX! method_exists() Leaks!
-$a->test = function ($params) { var_dump($params, method_exists($params, 'cb1'), $params->cb1); };
-$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
-

+ 0 - 11
samples/test_closure.php

@@ -1,11 +0,0 @@
-<?php
-
-$a = new V8Js();
-$a->func = function ($a) { echo "Closure..\n"; };
-
-try {
-  $a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
-  $a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
-} catch (V8JsScriptException $e) {
-  echo $e->getMessage(), "\n";
-}

+ 0 - 5
samples/test_crash.php

@@ -1,5 +0,0 @@
-<?php {
-    $a = new V8Js();
-
-    var_dump($a->executeString('Jst.write = function(s) { html += "EI TOIMI"; };' ."\n" .' Jst.evaluate("lol testi <%= 1 %>", {});'));
-}

+ 0 - 9
samples/test_date.php

@@ -1,9 +0,0 @@
-<?php
-
-$a = new V8Js();
-
-try {
-	var_dump($a->executeString("date = new Date('September 8, 1975 09:00:00'); print(date + '\\n'); date;", "test.js"));
-} catch (V8JsScriptException $e) {
-	echo $e->getMessage(), "\n";
-}

+ 0 - 59
samples/test_dumper.php

@@ -1,59 +0,0 @@
-<?php
-
-class Foo {
-	var $foo = 'bar';
-	var $true = true;
-	var $false = false;
-	var $bar = array(1,2,3,1.23456789);
-	var $ass = array("life" => 42, "foo" => "bar");
-	function __set($name, $value)
-	{
-		echo "I'm setter!\n";
-		var_dump($name, $value);
-	}
-	function __get($name)
-	{
-		echo "I'm getter!\n";
-		var_dump($name);
-	}
-	function __call($name, $args)
-	{
-		echo "I'm caller!\n";
-		var_dump($name, $args);
-	}
-}
-
-$a = new V8Js();
-$obj = new Foo;
-$a->arr = array("foobar" => $obj);
-
-$JS = <<< 'EOF'
-  var example = new Object;
-  example.foo = function () {
-    print("this is foo");
-  }
-  example.bar = function () {
-    print("this is bar");
-  }
-  example.__noSuchMethod__ = function (id, args) {
-    print("tried to handle unknown method " + id);
-    if (args.length != 0)
-      print("it had arguments: " + args);
-  }
-  example.foo();        // alerts "this is foo"
-  example.bar();        // alerts "this is bar"
-  example.grill();      // alerts "tried to handle unknown method grill"
-  example.ding("dong"); // alerts "tried to handle unknown method ding"
-EOF;
-
-try {
-  $a->executeString("var myarr = new Array(); myarr[0] = 'foo'; myarr[1] = 'bar'; var_dump(myarr); var_dump(new Date('September 8, 1975 09:00:00'))", "call_test1.js");
-  $a->executeString("var_dump(PHP.arr.foobar.bar);", "call_test2.js");
-  $a->executeString("var_dump(PHP.arr.foobar.bar[0]);", "call_test3.js");
-  $a->executeString("var_dump(var_dump(PHP.arr));", "call_test4.js");
-  $a->executeString("var patt1=/[^a-h]/g; var_dump(patt1);", "call_test5.js");
-  $a->executeString("var_dump(Math.PI, Infinity, null, undefined);", "call_test6.js");
-//  $a->executeString($JS);
-} catch (V8JsScriptException $e) {
-  echo $e->getMessage(), "\n";
-}

+ 0 - 26
samples/test_exception.php

@@ -1,26 +0,0 @@
-<?php {
-    class Foo {
-      private $v8 = NULL;
-
-      public function __construct()
-      {
-		$this->v8 = new V8Js();
-		$this->v8->foo = $this;
-		var_dump($this->v8->executeString('throw 1; PHP.foo.bar();', 'trycatch1'));
-		var_dump($this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch2'));
-      }
-
-      public function bar()
-      {
-      	echo "To Bar!\n";
-      	var_dump($this->v8->executeString('throw new Error();', 'throw'));
-      }
-    }
-    
-    try {
-      $foo = new Foo();
-    } catch (V8JsScriptException $e) {
-      echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
-    }
-
-}

+ 0 - 34
samples/test_exception2.php

@@ -1,34 +0,0 @@
-<?php {
-    class Foo {
-      private $v8 = NULL;
-
-      public function __construct()
-      {
-		$this->v8 = new V8Js(null, array(), false);
-		$this->v8->foo = $this;
-//		$this->v8->executeString('asdasda< / sd', 'trycatch0');
-//		$this->v8->executeString('blahnothere', 'trycatch1');
-//	  	$this->v8->executeString('throw new SyntaxError();', 'throw');
-//		$this->v8->executeString('function foo() {throw new SyntaxError();}', 'trycatch2');
-//		$this->v8->executeString('try { foo(); } catch (e) { print(e + " catched by pure JS!\n"); }', 'trycatch3');
-//		$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " catched via PHP callback!\n"); }', 'trycatch4');
-//		$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch5');
-//		$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch5');
-		var_dump($this->v8->getPendingException());
-      }
-
-      public function bar()
-      {
-//		$this->v8->executeString('asdasda< / sd', 'trycatch0');
-//		$this->v8->executeString('blahnothere', 'trycatch1');
-      	$this->v8->executeString('throw new Error();', 'throw');
-      }
-    }
-    
-    try {
-      $foo = new Foo();
-    } catch (V8JsScriptException $e) {
-      echo "PHP Exception: ", $e->getMessage(), "\n";
-    }
-
-}

+ 0 - 23
samples/test_extend.php

@@ -1,23 +0,0 @@
-<?php
-
-// Test class
-class Testing extends V8Js
-{
-	public $foo = 'ORIGINAL';
-	private $my_private = 'arf'; // Should not show in JS side
-	protected $my_protected = 'argh'; // Should not show in JS side
-
-	public function mytest($a, $b, $c = NULL)
-	{
-		var_dump(func_get_args());
-	}
-}
-
-$a = new Testing();
-echo $a;
-
-try {
-	$a->executeString("PHP.mytest(PHP.foo, PHP.my_private, PHP.my_protected);", "test7.js");
-} catch (V8JsScriptException $e) {
-	var_dump($e);
-}

+ 0 - 9
samples/test_extension.php

@@ -1,9 +0,0 @@
-<?php
-
-V8Js::registerExtension('a', file_get_contents('js/json-template.js'), array('b'));
-V8Js::registerExtension('b', file_get_contents('js/jstparser.js'), array('a'));
-
-var_dump(V8JS::getExtensions());
-
-$a = new V8Js('myobj', array(), array('b'));
-

+ 0 - 48
samples/test_method.php

@@ -1,48 +0,0 @@
-<?php
-
-// Test class
-class Testing
-{
-	public $foo = 'ORIGINAL';
-	private $my_private = 'arf'; // Should not show in JS side
-	protected $my_protected = 'argh'; // Should not show in JS side
-
-	function mytest($a, $b, $c = NULL)
-	{
-		var_dump(func_get_args());
-	}
-}
-
-$a = new V8Js();
-$a->myobj = new Testing();
-
-$a->executeString("PHP.myobj.mytest('arg1', 'arg2');", "test1.js");
-$a->executeString("PHP.myobj.mytest(true, false, 1234567890);", "test2.js");
-$a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
-
-// Invalid parameters
-try {
-	$a->executeString("PHP.myobj.mytest();", "test4.js");
-} catch (V8JsScriptException $e) {
-	echo $e->getMessage(), "\n";
-}
-
-try {
-	$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
-} catch (V8JsScriptException $e) {
-	echo $e->getMessage(), "\n";
-}
-
-// Array / Object
-try {
-//	date_default_timezone_set("UTC");
-	$a->executeString("date = new Date('September 8, 1975 09:00:00'); PHP.print(date); PHP.myobj.mytest(date, PHP.myobj, new Array(1,2,3));", "test6.js");
-} catch (V8JsScriptException $e) {
-	var_dump($e);
-}
-
-try {
-	$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
-} catch (V8JsScriptException $e) {
-	var_dump($e);
-}

+ 0 - 173
test.php

@@ -1,173 +0,0 @@
-<?php
-/*
-$v8 = new V8Js;
-$v8->func = function ($a) { return var_export(func_get_args(), TRUE); };
-$v8->executeString("PHP.func();", "arg_test1.js");
-exit;
-*/
-
-var_dump(V8Js::registerExtension('myparser.js', 'function foo() { print("foobar!\n"}', array('jstparser.js', 'json-template.js'), false));
-var_dump(V8Js::registerExtension('myparser.js', 'function foo() { print("foobar!\n"}', array('jstparser.js', 'json-template.js'), false));
-var_dump(V8Js::registerExtension('jstparser.js', file_get_contents('js/jstparser.js'), array(), false));
-//V8Js::registerExtension('json-template.js', file_get_contents('js/json-template.js'), array(), false);
-
-var_dump(V8JS::getExtensions());
-
-$a = new V8Js('myobj', array(), array('jstparser.js'));
-
-$jstparser = <<< 'EOT'
-var template = 'Gold &amp; Hot Rod Red, as seen in the new <a href="http://blog.markturansky.com/archives/51">Iron Man trailer</a>!' + "\n" +
-'<table cellspacing="0" cellpadding="4">' + "\n" +
-'    <% for(var i = 0; i < 10; i++){ %> ' + "\n" +
-'        <tr>' + "\n" +
-'        <td style="background-color: <%= i % 2 == 0 ? \'red\' : \'gold\' %>">' + "\n" +
-'            Hi, <%=name%>! i is <%= i %>' + "\n" +
-'        </td>' + "\n" +
-'        </tr>' + "\n" +
-'    <% } %>' + "\n" +
-'</table>' + "\n" +
-'Note that name is HTML escaped by default. Here it is without escaping:'+
-'<%+ name %>';
-Jst.evaluateSingleShot(template, {"name":"foobar"});
-EOT;
-
-echo($a->executeString($jstparser, "ext_test1.js")), "\n";
-
-$a->_SERVER = $_SERVER;
-$a->func = function ($a) { echo "Closure..\n"; };
-
-$a->executeString("print(myobj._SERVER['HOSTNAME']);", "test1.js");
-$a->executeString("print(myobj.func); myobj.func(1);", "closure_test.js");
-
-$JS = <<<'EOT'
-function dump(a)
-{
-	for (var i in a) { 
-	  var val = a[i];
-	  print(i + ' => ' + val + "\n");
-	}
-}
-function foo()
-{
-  var bar = 'bar';
-  var foo = 'foo';
-  return foo + bar;
-}
-function test()
-{
-  var a = 'PHP version: ' + PHP.phpver;
-  phpver = 'changed in JS!';
-  return a;
-}
-function loop()
-{
-  var foo = 'foo';
-  while(true)
-  {
-    foo + 'bar';
-  }
-}
-function output()
-{
-	while(true)
-	{
-		print("output:foo\n");
-		sleep(5);
-		exit();
-	}
-};
-function simplearray()
-{
-	print(myarray.a + "\n");
-	print(myarray.b + "\n");
-	print(myarray.c + "\n");
-	print(myarray.d + "\n");
-}
-function bigarray()
-{
-	print(PHP.$_SERVER['HOSTNAME'] + "\n");
-	print(PHP.$_SERVER.argv + "\n");
-}
-EOT;
-
-$jsontemplate = <<< EOT
-var t = jsontemplate.Template("{# This is a comment and will be removed from the output.}{.section songs}<h2>Songs in '{playlist-name}'</h2><table width=\"100%\">{.repeated section @}<tr><td><a href=\"{url-base|htmltag}{url|htmltag}\">Play</a><td><i>{title}</i></td><td>{artist}</td></tr>{.end}</table>{.or}<p><em>(No page content matches)</em></p>{.end}");
-t.expand({
-"url-base": "http://example.com/music/", 
-"playlist-name": "Epic Playlist", 
-"songs": [
-{
-"url": "1.mp3", 
-"artist": "Grayceon", 
-"title": "Sounds Like Thunder"
-}, 
-{
-"url": "2.mp3", 
-"artist": "Thou", 
-"title": "Their Hooves Carve Craters in the Earth"
-}]});
-EOT;
-
-class tester
-{
-	public $foo = 'bar';
-	private $my_private = 'arf';
-	protected $my_protected = 'argh';
-
-	function mytest() { echo 'Here be monsters..', "\n"; }
-}
-
-$a = new V8Js();
-$a->obj = new tester();
-$a->phpver = phpversion();
-$a->argv = $_SERVER['argv'];
-$a->integer = 1;
-$a->float = 3.14;
-$a->{'$'._SERVER} = $_SERVER;
-$a->GLOBALS = $GLOBALS;
-$a->myarray = array(
-	'a' => 'Array value for key A',
-	'b' => 'Array value for key B',
-	'c' => 'Array value for key C',
-	'd' => 'Array value for key D',
-);
-$a->arr = array("first", "second", "third");
-
-$a->executeString($JS, "test1.js");
-$a->executeString("bigarray()", "test1.js");
-
-try {
-  echo($a->executeString($jstparser, "test2.js")), "\n";
-  var_dump($a->executeString($jsontemplate, "test1.js"));
-} catch (V8JsScriptException $e) {
-  echo $e->getMessage();
-}
-
-// Test for context handling
-
-$a->executeString($JS, "test1.js");
-$a->executeString("bigarray();");
-
-echo '$a->obj: ', "\n"; $a->executeString("dump(PHP.obj);");
-echo '$a->arr: ', "\n"; $a->executeString("dump(PHP.arr);");
-echo '$a->argv: ', "\n"; $a->executeString("dump(PHP.argv);");
-
-var_dump($a->argv);
-var_dump($a->executeString("test();"));
-var_dump($a->executeString("test();"));
-
-$b = new V8Js();
-
-var_dump($a->phpver, $a->executeString("test();"));
-
-$b->executeString($JS, "test2.js");
-var_dump($b->executeString("test();"));
-var_dump($b->executeString("print('foobar\\n');"));
-
-// Exception methods
-
-try {
-  $b->executeString("foobar; foo();", "extest.js");
-} catch (V8JsScriptException $e) {
-  var_dump($e, $e->getJsFileName(), $e->getJsLineNumber(), $e->getJsSourceLine(), $e->getJsTrace());
-}

+ 7 - 1
tests/memory_limit.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::executeString() : Memory limit
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 $JS = <<< EOT

+ 20 - 0
tests/object_passback_002.phpt

@@ -0,0 +1,20 @@
+--TEST--
+Test V8::executeString() : Object passing JS > PHP > JS
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$v8 = new V8Js();
+
+$v8->theApiCall = function() use ($v8) {
+	return $v8->executeString('({ foo: 23 })');
+};
+
+$v8->executeString('var_dump(PHP.theApiCall().constructor.name);');
+
+?>
+===EOF===
+--EXPECT--
+string(6) "Object"
+===EOF===

+ 44 - 0
tests/return_this_001.phpt

@@ -0,0 +1,44 @@
+--TEST--
+Test V8::executeString() : return this (aka fluent setters, JS-side)
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$js = <<<EOJS
+function Bar() {
+}
+
+Bar.prototype.setFoo = function(value) {
+	this.foo = value;
+	return this;
+}
+
+Bar.prototype.setBar = function(value) {
+	this.bar = value;
+	return this;
+}
+
+theBar = new Bar();
+(theBar);
+EOJS;
+
+$v8 = new V8Js();
+$bar = $v8->executeString($js);
+
+$ret = $bar->setFoo(23)->setBar(42);
+var_dump($bar === $ret);
+
+$v8->executeString('var_dump(theBar);');
+
+?>
+===EOF===
+--EXPECTF--
+bool(true)
+object(Bar)#%d (2) {
+  ["foo"] =>
+  int(23)
+  ["bar"] =>
+  int(42)
+}
+===EOF===

+ 50 - 0
tests/return_this_basic.phpt

@@ -0,0 +1,50 @@
+--TEST--
+Test V8::executeString() : return $this (aka fluent setters)
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+class Foo {
+	private $foo;
+	private $bar;
+
+	public function setFoo($value)
+	{
+		$this->foo = $value;
+		return $this;
+	}
+
+	public function setBar($value)
+	{
+		$this->bar = $value;
+		return $this;
+	}
+}
+
+$v8 = new V8Js();
+$v8->theFoo = new Foo();
+
+$v8->executeString(<<<EOJS
+	var a = PHP.theFoo.setFoo(23);
+	var b = a.setBar(42);
+
+	var_dump(PHP.theFoo === a);
+	var_dump(PHP.theFoo === b);
+EOJS
+);
+
+var_dump($v8->theFoo);
+
+?>
+===EOF===
+--EXPECTF--
+bool(true)
+bool(true)
+object(Foo)#%d (2) {
+  ["foo":"Foo":private]=>
+  int(23)
+  ["bar":"Foo":private]=>
+  int(42)
+}
+===EOF===

+ 16 - 0
tests/set_average_object_size_basic.phpt

@@ -0,0 +1,16 @@
+--TEST--
+Test V8::setAverageObjectSize() : Average object size can be set on V8Js object
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+$v8 = new V8Js();
+$v8->setAverageObjectSize(32768);
+
+// there's no API to query the currently announced external memory allocation,
+// hence not much we can do here...
+
+?>
+===EOF===
+--EXPECT--
+===EOF===

+ 7 - 1
tests/set_memory_limit_001.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setMemoryLimit() : Memory limit applied to V8Function calls
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_memory_limit_003.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setMemoryLimit() : Memory limit can be imposed later
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_memory_limit_basic.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setMemoryLimit() : Memory limit can be set on V8Js object
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 $JS = <<< EOT

+ 7 - 1
tests/set_time_limit_001.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setTimeLimit() : Time limit applied to V8Function calls
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_time_limit_002.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setTimeLimit() : Time limit can be changed
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_time_limit_003.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setTimeLimit() : Time limit can be imposed later on
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_time_limit_004.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setTimeLimit() : Time limit can be prolonged
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/set_time_limit_basic.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::setTimeLimit() : Time limit can be set on V8Js object
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 7 - 1
tests/time_limit.phpt

@@ -1,7 +1,13 @@
 --TEST--
 Test V8::executeString() : Time limit
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+if (getenv("SKIP_SLOW_TESTS")) {
+	die("skip slow test");
+}
+?>
 --FILE--
 <?php
 

+ 0 - 2
v8js.cc

@@ -153,11 +153,9 @@ static PHP_MSHUTDOWN_FUNCTION(v8js)
 
 	if(v8_initialized) {
 		v8::V8::Dispose();
-#if PHP_V8_API_VERSION >= 3029036
 		v8::V8::ShutdownPlatform();
 		// @fixme call virtual destructor somehow
 		//delete v8js_process_globals.v8_platform;
-#endif
 	}
 
 	if (v8js_process_globals.v8_flags) {

+ 7 - 13
v8js_array_access.cc

@@ -2,7 +2,7 @@
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2013 The PHP Group                                |
+  | Copyright (c) 1997-2016 The PHP Group                                |
   +----------------------------------------------------------------------+
   | http://www.opensource.org/licenses/mit-license.php  MIT License      |
   +----------------------------------------------------------------------+
@@ -63,8 +63,7 @@ void v8js_array_access_getter(uint32_t index, const v8::PropertyCallbackInfo<v8:
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	zval zvalue;
 	ZVAL_UNDEF(&zvalue);
@@ -85,8 +84,7 @@ void v8js_array_access_setter(uint32_t index, v8::Local<v8::Value> value,
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	zval zvalue;
 	ZVAL_UNDEF(&zvalue);
@@ -153,8 +151,7 @@ static void v8js_array_access_length(v8::Local<v8::String> property, const v8::P
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
 	info.GetReturnValue().Set(V8JS_INT(length));
@@ -168,8 +165,7 @@ void v8js_array_access_deleter(uint32_t index, const v8::PropertyCallbackInfo<v8
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	zval zvalue;
 	ZVAL_UNDEF(&zvalue);
@@ -188,8 +184,7 @@ void v8js_array_access_query(uint32_t index, const v8::PropertyCallbackInfo<v8::
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	/* If index is set, then return an integer encoding a v8::PropertyAttribute;
 	 * otherwise we're expected to return an empty handle. */
@@ -207,8 +202,7 @@ void v8js_array_access_enumerator(const v8::PropertyCallbackInfo<v8::Array>& inf
 
 	V8JS_TSRMLS_FETCH();
 
-	v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 
 	int length = v8js_array_access_get_count_result(object TSRMLS_CC);
 	v8::Local<v8::Array> result = v8::Array::New(isolate, length);

+ 28 - 26
v8js_class.cc

@@ -2,7 +2,7 @@
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2015 The PHP Group                                |
+  | Copyright (c) 1997-2016 The PHP Group                                |
   +----------------------------------------------------------------------+
   | http://www.opensource.org/licenses/mit-license.php  MIT License      |
   +----------------------------------------------------------------------+
@@ -71,7 +71,6 @@ struct v8js_jsext {
 };
 /* }}} */
 
-#if PHP_V8_API_VERSION >= 4004010
 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
 public:
 	virtual void* Allocate(size_t length) {
@@ -81,7 +80,6 @@ public:
 	virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
 	virtual void Free(void* data, size_t) { free(data); }
 };
-#endif
 
 
 static void v8js_free_storage(zend_object *object TSRMLS_DC) /* {{{ */
@@ -153,7 +151,7 @@ static void v8js_free_storage(zend_object *object TSRMLS_DC) /* {{{ */
 		zval value;
 		ZVAL_OBJ(&value, object);
 		zval_dtor(&value);
-		c->isolate->AdjustAmountOfExternalAllocatedMemory(-1024);
+		c->isolate->AdjustAmountOfExternalAllocatedMemory(-c->average_object_size);
 		it->second.Reset();
 	}
 	c->weak_objects.~map();
@@ -202,9 +200,7 @@ static void v8js_free_storage(zend_object *object TSRMLS_DC) /* {{{ */
 	c->modules_stack.~vector();
 	c->modules_base.~vector();
 
-#if PHP_V8_API_VERSION >= 4003007
 	zval_dtor(&c->zval_snapshot_blob);
-#endif
 }
 /* }}} */
 
@@ -243,6 +239,8 @@ static zend_object* v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */
 	v8js_object_handlers.offset = XtOffsetOf(struct v8js_ctx, std);
 	v8js_object_handlers.free_obj = v8js_free_storage;
 
+	c->average_object_size = 1024;
+
 	return &c->std;
 }
 /* }}} */
@@ -353,13 +351,10 @@ static PHP_METHOD(V8Js, __construct)
 	ZVAL_NULL(&c->pending_exception);
 	c->in_execution = 0;
 
-#if PHP_V8_API_VERSION >= 4003007
 	new (&c->create_params) v8::Isolate::CreateParams();
 
-#if PHP_V8_API_VERSION >= 4004044
 	static ArrayBufferAllocator array_buffer_allocator;
 	c->create_params.array_buffer_allocator = &array_buffer_allocator;
-#endif
 
 	new (&c->snapshot_blob) v8::StartupData();
 	if (snapshot_blob) {
@@ -375,10 +370,6 @@ static PHP_METHOD(V8Js, __construct)
 	}
 
 	c->isolate = v8::Isolate::New(c->create_params);
-#else /* PHP_V8_API_VERSION < 4003007 */
-	c->isolate = v8::Isolate::New();
-#endif
-
 	c->isolate->SetData(0, c);
 
 	c->time_limit = 0;
@@ -467,6 +458,7 @@ static PHP_METHOD(V8Js, __construct)
 	c->object_name.Reset(isolate, object_name_js);
 
 	/* Add the PHP object into global object */
+	php_obj_t->InstanceTemplate()->SetInternalFieldCount(2);
 	v8::Local<v8::Object> php_obj = php_obj_t->InstanceTemplate()->NewInstance();
 	V8JS_GLOBAL(isolate)->ForceSet(object_name_js, php_obj, v8::ReadOnly);
 
@@ -487,7 +479,7 @@ static PHP_METHOD(V8Js, __construct)
 	} ZEND_HASH_FOREACH_END();
 
 	/* Add pointer to zend object */
-	php_obj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), v8::External::New(isolate, Z_OBJ_P(getThis())));
+	php_obj->SetAlignedPointerInInternalField(1, Z_OBJ_P(getThis()));
 
 	/* Export public methods */
 	void *ptr;
@@ -883,6 +875,22 @@ static PHP_METHOD(V8Js, setMemoryLimit)
 }
 /* }}} */
 
+/* {{{ proto void V8Js::setAverageObjectSize(average_object_size)
+ */
+static PHP_METHOD(V8Js, setAverageObjectSize)
+{
+	v8js_ctx *c;
+	long average_object_size = 0;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &average_object_size) == FAILURE) {
+		return;
+	}
+
+	c = Z_V8JS_CTX_OBJ_P(getThis());
+	c->average_object_size = average_object_size;
+}
+/* }}} */
+
 static void v8js_persistent_zval_ctor(zval *p) /* {{{ */
 {
 	assert(Z_TYPE_P(p) == IS_STRING);
@@ -1051,7 +1059,7 @@ static PHP_METHOD(V8Js, getExtensions)
 }
 /* }}} */
 
-#if PHP_V8_API_VERSION >= 4003007
+
 /* {{{ proto string|bool V8Js::createSnapshot(string embed_source)
  */
 static PHP_METHOD(V8Js, createSnapshot)
@@ -1081,7 +1089,6 @@ static PHP_METHOD(V8Js, createSnapshot)
 	delete[] snapshot_blob.data;
 }
 /* }}} */
-#endif  /* PHP_V8_API_VERSION >= 4003007 */
 
 
 /* {{{ arginfo */
@@ -1138,6 +1145,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setmoduleloader, 0, 0, 1)
 	ZEND_ARG_INFO(0, callable)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setaverageobjectsize, 0, 0, 1)
+	ZEND_ARG_INFO(0, average_object_size)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_registerextension, 0, 0, 2)
 	ZEND_ARG_INFO(0, extension_name)
 	ZEND_ARG_INFO(0, script)
@@ -1148,11 +1159,9 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO(arginfo_v8js_getextensions, 0)
 ZEND_END_ARG_INFO()
 
-#if PHP_V8_API_VERSION >= 4003007
 ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_createsnapshot, 0, 0, 1)
 	ZEND_ARG_INFO(0, script)
 ZEND_END_ARG_INFO()
-#endif
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_settimelimit, 0, 0, 1)
 	ZEND_ARG_INFO(0, time_limit)
@@ -1177,12 +1186,10 @@ const zend_function_entry v8js_methods[] = { /* {{{ */
 	PHP_ME(V8Js,	setModuleLoader,		arginfo_v8js_setmoduleloader,		ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	setTimeLimit,			arginfo_v8js_settimelimit,			ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	setMemoryLimit,			arginfo_v8js_setmemorylimit,		ZEND_ACC_PUBLIC)
+	PHP_ME(V8Js,	setAverageObjectSize,	arginfo_v8js_setaverageobjectsize,	ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	registerExtension,		arginfo_v8js_registerextension,		ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 	PHP_ME(V8Js,	getExtensions,			arginfo_v8js_getextensions,			ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-
-#if PHP_V8_API_VERSION >= 4003007
 	PHP_ME(V8Js,	createSnapshot,			arginfo_v8js_createsnapshot,		ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-#endif
 	{NULL, NULL, NULL}
 };
 /* }}} */
@@ -1253,11 +1260,6 @@ PHP_MINIT_FUNCTION(v8js_class) /* {{{ */
 
 	le_v8js_script = zend_register_list_destructors_ex(v8js_script_dtor, NULL, PHP_V8JS_SCRIPT_RES_NAME, module_number);
 
-#if PHP_V8_API_VERSION >= 4004010 && PHP_V8_API_VERSION < 4004044
-	static ArrayBufferAllocator array_buffer_allocator;
-	v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
-#endif
-
 	return SUCCESS;
 } /* }}} */
 

+ 1 - 2
v8js_class.h

@@ -46,6 +46,7 @@ struct v8js_ctx {
   bool time_limit_hit;
   long memory_limit;
   bool memory_limit_hit;
+  long average_object_size;
 
   v8js_tmpl_t global_template;
   v8js_tmpl_t array_tmpl;
@@ -69,11 +70,9 @@ struct v8js_ctx {
   std::vector<struct _v8js_script *> script_objects;
   char *tz;
 
-#if PHP_V8_API_VERSION >= 4003007
   v8::Isolate::CreateParams create_params;
   zval zval_snapshot_blob;
   v8::StartupData snapshot_blob;
-#endif
 
 #ifdef ZTS
   void ***zts_ctx;

+ 7 - 5
v8js_convert.cc

@@ -2,12 +2,13 @@
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2013 The PHP Group                                |
+  | Copyright (c) 1997-2016 The PHP Group                                |
   +----------------------------------------------------------------------+
   | http://www.opensource.org/licenses/mit-license.php  MIT License      |
   +----------------------------------------------------------------------+
   | Author: Jani Taskinen <[email protected]>                         |
   | Author: Patrick Reilly <[email protected]>                             |
+  | Author: Stefan Siegl <[email protected]>                                |
   +----------------------------------------------------------------------+
 */
 
@@ -225,16 +226,17 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v
 	}
 	else if (jsValue->IsObject())
 	{
-		v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(jsValue);
+		v8::Local<v8::Object> self = jsValue->ToObject();
+
 		// if this is a wrapped PHP object, then just unwrap it.
-		v8::Local<v8::Value> php_object = self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-		if (!php_object.IsEmpty()) {
-			zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+		if (self->InternalFieldCount()) {
+			zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 			zval zval_object;
 			ZVAL_OBJ(&zval_object, object);
 			RETVAL_ZVAL(&zval_object, 1, 0);
 			return SUCCESS;
 		}
+
 		if ((flags & V8JS_FLAG_FORCE_ARRAY && !jsValue->IsFunction()) || jsValue->IsArray()) {
 			array_init(return_value);
 			return v8js_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags, isolate TSRMLS_CC);

+ 10 - 16
v8js_exceptions.cc

@@ -2,7 +2,7 @@
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2015 The PHP Group                                |
+  | Copyright (c) 1997-2016 The PHP Group                                |
   +----------------------------------------------------------------------+
   | http://www.opensource.org/licenses/mit-license.php  MIT License      |
   +----------------------------------------------------------------------+
@@ -44,7 +44,7 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 	v8::Handle<v8::Message> tc_message = try_catch->Message();
 	const char *filename_string, *sourceline_string;
 	char *message_string;
-	int linenum, start_col, end_col, message_len;
+	int linenum, start_col, end_col;
 
 	object_init_ex(return_value, php_ce_v8js_script_exception);
 
@@ -52,7 +52,7 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 	zend_update_property##type(php_ce_v8js_script_exception, return_value, #name, sizeof(#name) - 1, value TSRMLS_CC);
 
 	if (tc_message.IsEmpty()) {
-		message_len = spprintf(&message_string, 0, "%s", exception_string);
+		spprintf(&message_string, 0, "%s", exception_string);
 	}
 	else
 	{
@@ -73,7 +73,7 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 		end_col = tc_message->GetEndColumn();
 		PHPV8_EXPROP(_long, JsEndColumn, end_col);
 
-		message_len = spprintf(&message_string, 0, "%s:%d: %s", filename_string, linenum, exception_string);
+		spprintf(&message_string, 0, "%s:%d: %s", filename_string, linenum, exception_string);
 
 		v8::String::Utf8Value stacktrace(try_catch->StackTrace());
 		if (stacktrace.length() > 0) {
@@ -81,21 +81,15 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
 			PHPV8_EXPROP(_string, JsTrace, stacktrace_string);
 		}
 
-		if(try_catch->Exception()->IsObject()) {
-			v8::Local<v8::Value> php_ref = try_catch->Exception()->ToObject()->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
+		if(try_catch->Exception()->IsObject() && try_catch->Exception()->ToObject()->InternalFieldCount()) {
+			zend_object *php_exception = reinterpret_cast<zend_object *>(try_catch->Exception()->ToObject()->GetAlignedPointerFromInternalField(1));
 
-			if(!php_ref.IsEmpty()) {
-				assert(php_ref->IsExternal());
-				zend_object *php_exception = reinterpret_cast<zend_object *>(v8::External::Cast(*php_ref)->Value());
-
-				zend_class_entry *exception_ce = zend_exception_get_default(TSRMLS_C);
-				if (instanceof_function(php_exception->ce, exception_ce TSRMLS_CC)) {
-					++GC_REFCOUNT(php_exception);
-					zend_exception_set_previous(Z_OBJ_P(return_value), php_exception);
-				}
+			zend_class_entry *exception_ce = zend_exception_get_default(TSRMLS_C);
+			if (instanceof_function(php_exception->ce, exception_ce TSRMLS_CC)) {
+				++GC_REFCOUNT(php_exception);
+				zend_exception_set_previous(Z_OBJ_P(return_value), php_exception);
 			}
 		}
-
 	}
 
 	PHPV8_EXPROP(_string, message, message_string);

+ 20 - 19
v8js_methods.cc

@@ -112,7 +112,7 @@ static void v8js_dumper(v8::Isolate *isolate, v8::Local<v8::Value> var, int leve
 
 	if (var->IsString())
 	{
-		php_printf("string(%zu) \"", valstr_len, valstr);
+		php_printf("string(%zu) \"", valstr_len);
 		PHPWRITE(valstr, valstr_len);
 		php_printf("\"\n");
 	}
@@ -208,7 +208,7 @@ V8JS_METHOD(require)
 	V8JS_TSRMLS_FETCH();
 
 	// Get the extension context
-	v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(info.Data());
+	v8::Local<v8::External> data = v8::Local<v8::External>::Cast(info.Data());
 	v8js_ctx *c = static_cast<v8js_ctx*>(data->Value());
 
 	// Check that we have a module loader
@@ -403,25 +403,23 @@ V8JS_METHOD(require)
 	}
 
 	// Create a template for the global object and set the built-in global functions
-	v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-	global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(isolate, V8JS_MN(print)), v8::ReadOnly);
-	global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(isolate, V8JS_MN(var_dump)), v8::ReadOnly);
-	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(isolate, V8JS_MN(sleep)), v8::ReadOnly);
-	global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), v8::External::New(isolate, c)), v8::ReadOnly);
+	v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+	global_template->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(isolate, V8JS_MN(print)), v8::ReadOnly);
+	global_template->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(isolate, V8JS_MN(var_dump)), v8::ReadOnly);
+	global_template->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(isolate, V8JS_MN(sleep)), v8::ReadOnly);
+	global_template->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), v8::External::New(isolate, c)), v8::ReadOnly);
 
 	// Add the exports object in which the module can return its API
 	v8::Local<v8::ObjectTemplate> exports_template = v8::ObjectTemplate::New();
-	v8::Local<v8::Object> exports = exports_template->NewInstance();
-	global->Set(V8JS_SYM("exports"), exports);
+	global_template->Set(V8JS_SYM("exports"), exports_template);
 
 	// Add the module object in which the module can have more fine-grained control over what it can return
-	v8::Handle<v8::ObjectTemplate> module_template = v8::ObjectTemplate::New();
-	v8::Handle<v8::Object> module = module_template->NewInstance();
-	module->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id));
-	global->Set(V8JS_SYM("module"), module);
+	v8::Local<v8::ObjectTemplate> module_template = v8::ObjectTemplate::New();
+	module_template->Set(V8JS_SYM("id"), V8JS_STR(normalised_module_id));
+	global_template->Set(V8JS_SYM("module"), module_template);
 
 	// Each module gets its own context so different modules do not affect each other
-	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate, v8::Context::New(isolate, NULL, global));
+	v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate, v8::Context::New(isolate, NULL, global_template));
 
 	// Catch JS exceptions
 	v8::TryCatch try_catch;
@@ -456,7 +454,7 @@ V8JS_METHOD(require)
 	c->modules_base.push_back(normalised_path);
 
 	// Run script
-	v8::Local<v8::Value> result = script->Run();
+	script->Run();
 
 	// Remove this module and path from the stack
 	c->modules_stack.pop_back();
@@ -479,16 +477,19 @@ V8JS_METHOD(require)
 		return;
 	}
 
-	v8::Handle<v8::Object> newobj;
+	v8::Local<v8::Object> newobj;
 
 	// Cache the module so it doesn't need to be compiled and run again
 	// Ensure compatibility with CommonJS implementations such as NodeJS by playing nicely with module.exports and exports
-	if (module->Has(V8JS_SYM("exports")) && !module->Get(V8JS_SYM("exports"))->IsUndefined()) {
+	if (context->Global()->Has(V8JS_SYM("module"))
+		&& context->Global()->Get(V8JS_SYM("module"))->IsObject()
+		&& context->Global()->Get(V8JS_SYM("module"))->ToObject()->Has(V8JS_SYM("exports"))
+		&& context->Global()->Get(V8JS_SYM("module"))->ToObject()->Get(V8JS_SYM("exports"))->IsObject()) {
 		// If module.exports has been set then we cache this arbitrary value...
-		newobj = module->Get(V8JS_SYM("exports"))->ToObject();
+		newobj = context->Global()->Get(V8JS_SYM("module"))->ToObject()->Get(V8JS_SYM("exports"))->ToObject();
 	} else {
 		// ...otherwise we cache the exports object itself
-		newobj = exports;
+		newobj = context->Global()->Get(V8JS_SYM("exports"))->ToObject();
 	}
 
 	c->modules_loaded[normalised_module_id].Reset(isolate, newobj);

+ 23 - 26
v8js_object_export.cc

@@ -2,12 +2,13 @@
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2013 The PHP Group                                |
+  | Copyright (c) 1997-2016 The PHP Group                                |
   +----------------------------------------------------------------------+
   | http://www.opensource.org/licenses/mit-license.php  MIT License      |
   +----------------------------------------------------------------------+
   | Author: Jani Taskinen <[email protected]>                         |
   | Author: Patrick Reilly <[email protected]>                             |
+  | Author: Stefan Siegl <[email protected]>                                |
   +----------------------------------------------------------------------+
 */
 
@@ -30,7 +31,7 @@ extern "C" {
 #include "zend_generators.h"
 }
 
-static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zend_object> &data);
+static void v8js_weak_object_callback(const v8::WeakCallbackInfo<zend_object> &data);
 
 /* Callback for PHP methods and functions */
 static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v8::Isolate *isolate, const v8::FunctionCallbackInfo<v8::Value>& info TSRMLS_DC) /* {{{ */
@@ -88,13 +89,9 @@ static void v8js_call_php_func(zend_object *object, zend_function *method_ptr, v
 	if (argc) {
 		fci.params = (zval *) safe_emalloc(argc, sizeof(zval), 0);
 		for (i = 0; i < argc; i++) {
-			v8::Local<v8::Value> php_object;
-			if (info[i]->IsObject()) {
-				php_object = v8::Local<v8::Object>::Cast(info[i])->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-			}
-			if (!php_object.IsEmpty()) {
+			if (info[i]->IsObject() && info[i]->ToObject()->InternalFieldCount()) {
 				/* This is a PHP object, passed to JS and back. */
-				zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+				zend_object *object = reinterpret_cast<zend_object *>(info[i]->ToObject()->GetAlignedPointerFromInternalField(1));
 				ZVAL_OBJ(&fci.params[i], object);
 				Z_ADDREF_P(&fci.params[i]);
 			} else {
@@ -155,6 +152,9 @@ failure:
 		} else {
 			v8js_terminate_execution(isolate);
 		}
+	} else if (Z_TYPE(retval) == IS_OBJECT && Z_OBJ(retval) == object) {
+		// special case: "return $this"
+		return_value = info.Holder();
 	} else {
 		return_value = zval_to_v8js(&retval, isolate TSRMLS_CC);
 	}
@@ -173,7 +173,7 @@ void v8js_php_callback(const v8::FunctionCallbackInfo<v8::Value>& info) /* {{{ *
 	v8::Local<v8::Object> self = info.Holder();
 
 	V8JS_TSRMLS_FETCH();
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)))->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 	zend_function *method_ptr;
 
 	/* Set method_ptr from v8::External or fetch the closure invoker */
@@ -192,9 +192,7 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 	v8::Isolate *isolate = info.GetIsolate();
 	info.GetReturnValue().Set(V8JS_UNDEFINED);
 
-	// @todo assert constructor call
 	v8::Handle<v8::Object> newobj = info.This();
-	v8::Local<v8::External> php_object;
 	zval value;
 
 	if (!info.IsConstructCall()) {
@@ -209,7 +207,7 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 
 	if (info[0]->IsExternal()) {
 		// Object created by v8js in v8js_hash_to_jsobj, PHP object passed as v8::External.
-		php_object = v8::Local<v8::External>::Cast(info[0]);
+		v8::Local<v8::External> php_object = v8::Local<v8::External>::Cast(info[0]);
 		zend_object *object = reinterpret_cast<zend_object *>(php_object->Value());
 		ZVAL_OBJ(&value, object);
 
@@ -217,7 +215,7 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 			// We already exported this object, hence no need to add another
 			// ref, v8 won't give us a second weak-object callback anyways.
 			newobj->SetAlignedPointerInInternalField(0, ext_tmpl->Value());
-			newobj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), php_object);
+			newobj->SetAlignedPointerInInternalField(1, object);
 			return;
 		}
 
@@ -242,26 +240,25 @@ static void v8js_construct_callback(const v8::FunctionCallbackInfo<v8::Value>& i
 		if (ctor_ptr != NULL) {
 			v8js_call_php_func(Z_OBJ(value), ctor_ptr, isolate, info TSRMLS_CC);
 		}
-		php_object = v8::External::New(isolate, Z_OBJ(value));
 	}
 
 	newobj->SetAlignedPointerInInternalField(0, ext_tmpl->Value());
-	newobj->SetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY), php_object);
+	newobj->SetAlignedPointerInInternalField(1, Z_OBJ(value));
 
 	// Since we got to decrease the reference count again, in case v8 garbage collector
 	// decides to dispose the JS object, we add a weak persistent handle and register
 	// a callback function that removes the reference.
 	ctx->weak_objects[Z_OBJ(value)].Reset(isolate, newobj);
-	ctx->weak_objects[Z_OBJ(value)].SetWeak(Z_OBJ(value), v8js_weak_object_callback);
+	ctx->weak_objects[Z_OBJ(value)].SetWeak(Z_OBJ(value), v8js_weak_object_callback, v8::WeakCallbackType::kParameter);
 
 	// Just tell v8 that we're allocating some external memory
 	// (for the moment we just always tell 1k instead of trying to find out actual values)
-	isolate->AdjustAmountOfExternalAllocatedMemory(1024);
+	isolate->AdjustAmountOfExternalAllocatedMemory(ctx->average_object_size);
 }
 /* }}} */
 
 
-static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zend_object> &data) {
+static void v8js_weak_object_callback(const v8::WeakCallbackInfo<zend_object> &data) {
 	v8::Isolate *isolate = data.GetIsolate();
 
 	zend_object *object = data.GetParameter();
@@ -274,10 +271,10 @@ static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zen
 	ctx->weak_objects.at(object).Reset();
 	ctx->weak_objects.erase(object);
 
-	isolate->AdjustAmountOfExternalAllocatedMemory(-1024);
+	isolate->AdjustAmountOfExternalAllocatedMemory(-ctx->average_object_size);
 }
 
-static void v8js_weak_closure_callback(const v8::WeakCallbackData<v8::Object, v8js_tmpl_t> &data) {
+static void v8js_weak_closure_callback(const v8::WeakCallbackInfo<v8js_tmpl_t> &data) {
 	v8::Isolate *isolate = data.GetIsolate();
 
 	v8js_tmpl_t *persist_tpl_ = data.GetParameter();
@@ -314,7 +311,7 @@ static void v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8::Ar
 	zend_string *key;
 	ulong index;
 
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)))->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 	ce = object->ce;
 
 	/* enumerate all methods */
@@ -433,7 +430,7 @@ static void v8js_fake_call_impl(const v8::FunctionCallbackInfo<v8::Value>& info)
 
 	V8JS_TSRMLS_FETCH();
 	zend_class_entry *ce;
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)))->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 	ce = object->ce;
 
 	// first arg is method name, second arg is array of args.
@@ -528,7 +525,7 @@ v8::Local<v8::Value> v8js_named_property_callback(v8::Local<v8::String> property
 	zend_function *method_ptr = NULL;
 	zval php_value;
 
-	zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*self->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY)))->Value());
+	zend_object *object = reinterpret_cast<zend_object *>(self->GetAlignedPointerFromInternalField(1));
 	zval zobject;
 	ZVAL_OBJ(&zobject, object);
 
@@ -761,7 +758,7 @@ static v8::Handle<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_class_
 		new_tpl = v8::FunctionTemplate::New(isolate, 0);
 
 		new_tpl->SetClassName(V8JS_ZSTR(ce->name));
-		new_tpl->InstanceTemplate()->SetInternalFieldCount(1);
+		new_tpl->InstanceTemplate()->SetInternalFieldCount(2);
 
 		if (ce == zend_ce_closure) {
 			/* Got a closure, mustn't cache ... */
@@ -844,7 +841,7 @@ static v8::Handle<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_class_
 	if (ce == zend_ce_closure) {
 		// free uncached function template when object is freed
 		ctx->weak_closures[persist_tpl_].Reset(isolate, newobj);
-		ctx->weak_closures[persist_tpl_].SetWeak(persist_tpl_, v8js_weak_closure_callback);
+		ctx->weak_closures[persist_tpl_].SetWeak(persist_tpl_, v8js_weak_closure_callback, v8::WeakCallbackType::kParameter);
 	}
 
 	return newobj;
@@ -935,7 +932,7 @@ v8::Handle<v8::Value> v8js_hash_to_jsobj(zval *value, v8::Isolate *isolate TSRML
 	}
 
 	/* Special case, passing back object originating from JS to JS */
-	if (ce == php_ce_v8function
+	if (ce == php_ce_v8function || ce == php_ce_v8object
 #ifdef V8JS_V8GENERATOR_SUPPORT
 		|| ce == php_ce_v8generator
 #endif

+ 31 - 15
v8js_timer.cc

@@ -42,27 +42,43 @@ static void v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /*
 
 	v8::Locker locker(isolate);
 	v8::HeapStatistics hs;
-	isolate->GetHeapStatistics(&hs);
+	bool send_notification = false;
+	bool has_sent_notification = false;
 
-	globals->timer_mutex.lock();
+	do {
+		if (send_notification) {
+			isolate->LowMemoryNotification();
+			has_sent_notification = true;
+		}
 
-	for (std::deque< v8js_timer_ctx* >::iterator it = globals->timer_stack.begin();
-		 it != globals->timer_stack.end(); it ++) {
-		v8js_timer_ctx *timer_ctx = *it;
-		v8js_ctx *c = timer_ctx->ctx;
+		isolate->GetHeapStatistics(&hs);
 
-		if(c->isolate != isolate || timer_ctx->killed) {
-			continue;
-		}
+		globals->timer_mutex.lock();
+
+		for (std::deque< v8js_timer_ctx* >::iterator it = globals->timer_stack.begin();
+			 it != globals->timer_stack.end(); it ++) {
+			v8js_timer_ctx *timer_ctx = *it;
+			v8js_ctx *c = timer_ctx->ctx;
 
-		if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
-			timer_ctx->killed = true;
-			v8::V8::TerminateExecution(c->isolate);
-			c->memory_limit_hit = true;
+			if(c->isolate != isolate || timer_ctx->killed) {
+				continue;
+			}
+
+			if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
+				if (has_sent_notification) {
+					timer_ctx->killed = true;
+					v8::V8::TerminateExecution(c->isolate);
+					c->memory_limit_hit = true;
+				} else {
+					// force garbage collection, then check again
+					send_notification = true;
+					break;
+				}
+			}
 		}
-	}
 
-	globals->timer_mutex.unlock();
+		globals->timer_mutex.unlock();
+	} while(send_notification != has_sent_notification);
 }
 /* }}} */
 

+ 17 - 52
v8js_v8.cc

@@ -29,46 +29,7 @@ extern "C" {
 #include "zend_exceptions.h"
 }
 
-#if PHP_V8_API_VERSION >= 3029036
 #include <libplatform/libplatform.h>
-#endif
-
-
-#if defined(PHP_V8_USE_EXTERNAL_STARTUP_DATA) && PHP_V8_API_VERSION < 4006076
-/* Old V8 version, requires startup data but has no
- * (internal/API) means to let it be loaded. */
-static v8::StartupData natives_;
-static v8::StartupData snapshot_;
-
-static void v8js_v8_load_startup_data(const char* blob_file,
-									  v8::StartupData* startup_data,
-									  void (*setter_fn)(v8::StartupData*)) {
-	startup_data->data = NULL;
-	startup_data->raw_size = 0;
-
-	if (!blob_file) {
-		return;
-	}
-
-	FILE* file = fopen(blob_file, "rb");
-	if (!file) {
-		return;
-	}
-
-	fseek(file, 0, SEEK_END);
-	startup_data->raw_size = static_cast<int>(ftell(file));
-	rewind(file);
-
-	startup_data->data = new char[startup_data->raw_size];
-	int read_size = static_cast<int>(fread(const_cast<char*>(startup_data->data),
-										   1, startup_data->raw_size, file));
-	fclose(file);
-
-	if (startup_data->raw_size == read_size) {
-		(*setter_fn)(startup_data);
-	}
-}
-#endif
 
 
 void v8js_v8_init(TSRMLS_D) /* {{{ */
@@ -93,21 +54,14 @@ void v8js_v8_init(TSRMLS_D) /* {{{ */
 
 #ifdef PHP_V8_USE_EXTERNAL_STARTUP_DATA
 	/* V8 doesn't work without startup data, load it. */
-#if PHP_V8_API_VERSION >= 4006076
 	v8::V8::InitializeExternalStartupData(
 		PHP_V8_NATIVES_BLOB_PATH,
 		PHP_V8_SNAPSHOT_BLOB_PATH
 	);
-#else
-	v8js_v8_load_startup_data(PHP_V8_NATIVES_BLOB_PATH, &natives_, v8::V8::SetNativesDataBlob);
-	v8js_v8_load_startup_data(PHP_V8_SNAPSHOT_BLOB_PATH, &snapshot_, v8::V8::SetSnapshotDataBlob);
-#endif
 #endif
 
-#if PHP_V8_API_VERSION >= 3029036
 	v8js_process_globals.v8_platform = v8::platform::CreateDefaultPlatform();
 	v8::V8::InitializePlatform(v8js_process_globals.v8_platform);
-#endif
 
 	/* Set V8 command line flags (must be done before V8::Initialize()!) */
 	if (v8js_process_globals.v8_flags) {
@@ -206,6 +160,21 @@ void v8js_v8_call(v8js_ctx *c, zval **return_value,
 			return;
 		}
 
+		if (memory_limit && !c->memory_limit_hit) {
+			// Re-check memory limit (very short executions might never be hit by timer thread)
+			v8::HeapStatistics hs;
+			isolate->GetHeapStatistics(&hs);
+
+			if (hs.used_heap_size() > memory_limit) {
+				isolate->LowMemoryNotification();
+				isolate->GetHeapStatistics(&hs);
+
+				if (hs.used_heap_size() > memory_limit) {
+					c->memory_limit_hit = true;
+				}
+			}
+		}
+
 		if (c->memory_limit_hit) {
 			// Execution has been terminated due to memory limit
 			sprintf(exception_string, "Script memory limit of %lu bytes exceeded", memory_limit);
@@ -305,13 +274,9 @@ int v8js_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *retval, i
 			zval value;
 			ZVAL_UNDEF(&value);
 
-			v8::Local<v8::Value> php_object;
-			if (jsVal->IsObject()) {
-				php_object = v8::Local<v8::Object>::Cast(jsVal)->GetHiddenValue(V8JS_SYM(PHPJS_OBJECT_KEY));
-			}
-			if (!php_object.IsEmpty()) {
+			if (jsVal->IsObject() && jsVal->ToObject()->InternalFieldCount()) {
 				/* This is a PHP object, passed to JS and back. */
-				zend_object *object = reinterpret_cast<zend_object *>(v8::External::Cast(*php_object)->Value());
+				zend_object *object = reinterpret_cast<zend_object *>(jsVal->ToObject()->GetAlignedPointerFromInternalField(1));
 				ZVAL_OBJ(&value, object);
 				Z_ADDREF_P(&value);
 			}

+ 11 - 2
v8js_v8object_class.cc

@@ -292,7 +292,7 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I
 	/* std::function relies on its dtor to be executed, otherwise it leaks
 	 * some memory on bailout. */
 	{
-		std::function< v8::Local<v8::Value>(v8::Isolate *) > v8_call = [obj, method, argc, argv TSRMLS_CC](v8::Isolate *isolate) {
+		std::function< v8::Local<v8::Value>(v8::Isolate *) > v8_call = [obj, method, argc, argv, object, &return_value TSRMLS_CC](v8::Isolate *isolate) {
 			int i = 0;
 
 			v8::Local<v8::String> method_name = V8JS_ZSYM(method);
@@ -322,7 +322,16 @@ static int v8js_v8object_call_method(zend_string *method, zend_object *object, I
 				jsArgv[i] = v8::Local<v8::Value>::New(isolate, zval_to_v8js(&argv[i], isolate TSRMLS_CC));
 			}
 
-			return cb->Call(thisObj, argc, jsArgv);
+			v8::Local<v8::Value> result = cb->Call(thisObj, argc, jsArgv);
+
+			if (obj->std.ce == php_ce_v8object && result->StrictEquals(thisObj)) {
+				/* JS code did "return this", retain object identity */
+				ZVAL_OBJ(return_value, object);
+				zval_copy_ctor(return_value);
+				result.Clear();
+			}
+
+			return result;
 		};
 
 		v8js_v8_call(obj->ctx, &return_value, obj->flags, obj->ctx->time_limit, obj->ctx->memory_limit, v8_call TSRMLS_CC);