mirror of
https://github.com/phpv8/v8js.git
synced 2024-12-22 10:31:53 +00:00
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
This commit is contained in:
commit
e9e90bac65
@ -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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
11
README.md
11
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
|
||||
|
6
TODO
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
|
35
config.m4
35
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,10 +130,6 @@ 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)
|
||||
|
||||
case $host_os in
|
||||
@ -145,34 +141,32 @@ int main ()
|
||||
;;
|
||||
esac
|
||||
|
||||
LDFLAGS_libplatform=""
|
||||
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)
|
||||
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 $i/lib/$static_link_extra_file; then
|
||||
static_link_dir=$i/lib
|
||||
AC_MSG_RESULT(found in $i)
|
||||
|
||||
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
|
||||
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"
|
||||
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"
|
||||
fi
|
||||
|
||||
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])
|
||||
@ -182,7 +176,6 @@ int main ()
|
||||
#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) {
|
||||
@ -192,7 +185,6 @@ public:
|
||||
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
|
||||
virtual void Free(void* data, size_t) { free(data); }
|
||||
};
|
||||
#endif
|
||||
|
||||
int main ()
|
||||
{
|
||||
@ -200,15 +192,11 @@ public:
|
||||
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;
|
||||
}
|
||||
], [
|
||||
@ -251,7 +239,6 @@ public:
|
||||
AC_MSG_ERROR([Please provide V8 snapshot blob as needed])
|
||||
fi
|
||||
])
|
||||
fi
|
||||
|
||||
AC_LANG_RESTORE
|
||||
LIBS=$old_LIBS
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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,'&').
|
||||
replace(/>/g,'>').
|
||||
replace(/</g,'<');
|
||||
}
|
||||
|
||||
function HtmlTagEscape(s) {
|
||||
return s.replace(/&/g,'&').
|
||||
replace(/>/g,'>').
|
||||
replace(/</g,'<').
|
||||
replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
}();
|
339
js/jstparser.js
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('&', '&');
|
||||
s = s.replaceAll('"', '"');
|
||||
s = s.replaceAll('<', '<');
|
||||
s = s.replaceAll('>', '>');
|
||||
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;
|
||||
}();
|
152
package.xml
152
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>stesie@php.net</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 "amount of external memory" 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't re-wrap
|
||||
- Retain object identity on 'return $this'
|
||||
- Retain object identity on JS-side 'return this'
|
||||
</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 & GetHiddenValue et al)
|
||||
- Improve/Clarify README
|
||||
|
||||
Code Cleanup
|
||||
- Removed support for "old-age" V8 versions (i.e. V8 < 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
|
||||
|
@ -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;
|
||||
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
Ignore dlopen bug #1.
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:_dl_open
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
Ignore dlopen bug #2.
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:_dl_close
|
||||
...
|
||||
}
|
@ -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";
|
||||
}
|
@ -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"; } });');
|
||||
|
@ -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";
|
||||
}
|
@ -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 %>", {});'));
|
||||
}
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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'));
|
||||
|
@ -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);
|
||||
}
|
173
test.php
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 & 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());
|
||||
}
|
@ -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
tests/object_passback_002.phpt
Normal file
20
tests/object_passback_002.phpt
Normal file
@ -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
tests/return_this_001.phpt
Normal file
44
tests/return_this_001.phpt
Normal file
@ -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
tests/return_this_basic.phpt
Normal file
50
tests/return_this_basic.phpt
Normal file
@ -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
tests/set_average_object_size_basic.phpt
Normal file
16
tests/set_average_object_size_basic.phpt
Normal file
@ -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===
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
2
v8js.cc
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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
} /* }}} */
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 <jani.taskinen@iki.fi> |
|
||||
| Author: Patrick Reilly <preilly@php.net> |
|
||||
| Author: Stefan Siegl <stesie@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
|
@ -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,12 +81,8 @@ 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(!php_ref.IsEmpty()) {
|
||||
assert(php_ref->IsExternal());
|
||||
zend_object *php_exception = reinterpret_cast<zend_object *>(v8::External::Cast(*php_ref)->Value());
|
||||
if(try_catch->Exception()->IsObject() && try_catch->Exception()->ToObject()->InternalFieldCount()) {
|
||||
zend_object *php_exception = reinterpret_cast<zend_object *>(try_catch->Exception()->ToObject()->GetAlignedPointerFromInternalField(1));
|
||||
|
||||
zend_class_entry *exception_ce = zend_exception_get_default(TSRMLS_C);
|
||||
if (instanceof_function(php_exception->ce, exception_ce TSRMLS_CC)) {
|
||||
@ -96,8 +92,6 @@ void v8js_create_script_exception(zval *return_value, v8::Isolate *isolate, v8::
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PHPV8_EXPROP(_string, message, message_string);
|
||||
|
||||
efree(message_string);
|
||||
|
@ -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);
|
||||
|
@ -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 <jani.taskinen@iki.fi> |
|
||||
| Author: Patrick Reilly <preilly@php.net> |
|
||||
| Author: Stefan Siegl <stesie@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
@ -42,6 +42,15 @@ static void v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /*
|
||||
|
||||
v8::Locker locker(isolate);
|
||||
v8::HeapStatistics hs;
|
||||
bool send_notification = false;
|
||||
bool has_sent_notification = false;
|
||||
|
||||
do {
|
||||
if (send_notification) {
|
||||
isolate->LowMemoryNotification();
|
||||
has_sent_notification = true;
|
||||
}
|
||||
|
||||
isolate->GetHeapStatistics(&hs);
|
||||
|
||||
globals->timer_mutex.lock();
|
||||
@ -56,13 +65,20 @@ static void v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /*
|
||||
}
|
||||
|
||||
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();
|
||||
} while(send_notification != has_sent_notification);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
69
v8js_v8.cc
69
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user