Browse Source

import full versions of dependency libraries!

v0.7.4-release
Ryan 16 years ago
parent
commit
40c0f755c9
  1. 18
      configure
  2. 5
      deps/libebb/.gitignore
  3. 21
      deps/libebb/LICENSE
  4. 7
      deps/libebb/README
  5. 38
      deps/libebb/config.mk
  6. BIN
      deps/libebb/doc/icon.png
  7. 240
      deps/libebb/doc/index.html
  8. 798
      deps/libebb/ebb.c
  9. 120
      deps/libebb/ebb.h
  10. 117
      deps/libebb/ebb_request_parser.h
  11. 413
      deps/libebb/ebb_request_parser.rl
  12. 12
      deps/libebb/examples/ca-cert.pem
  13. 15
      deps/libebb/examples/ca-key.pem
  14. 101
      deps/libebb/examples/hello_world.c
  15. 412
      deps/libebb/rbtree.c
  16. 54
      deps/libebb/rbtree.h
  17. 60
      deps/libebb/test_examples.rb
  18. 108
      deps/libebb/test_rbtree.c
  19. 746
      deps/libebb/test_request_parser.c
  20. 23
      deps/liboi/LICENSE
  21. 36
      deps/liboi/README
  22. 33
      deps/liboi/config.mk
  23. 8
      deps/liboi/oi.h
  24. 278
      deps/liboi/oi.pod
  25. 486
      deps/liboi/oi_async.c
  26. 218
      deps/liboi/oi_async.h
  27. 41
      deps/liboi/oi_buf.c
  28. 30
      deps/liboi/oi_buf.h
  29. 25
      deps/liboi/oi_error.h
  30. 391
      deps/liboi/oi_file.c
  31. 58
      deps/liboi/oi_file.h
  32. 69
      deps/liboi/oi_queue.h
  33. 941
      deps/liboi/oi_socket.c
  34. 98
      deps/liboi/oi_socket.h
  35. 110
      deps/liboi/test/common.c
  36. 159
      deps/liboi/test/connection_interruption.c
  37. 102
      deps/liboi/test/echo.c
  38. 223
      deps/liboi/test/fancy_copy.c
  39. 165
      deps/liboi/test/ping_pong.c
  40. 54
      deps/liboi/test/sleeping_tasks.c
  41. 64
      deps/liboi/test/stdout.c
  42. 96
      deps/liboi/test/timeout.rb
  43. 15
      deps/v8/AUTHORS
  44. 816
      deps/v8/ChangeLog
  45. 51
      deps/v8/LICENSE
  46. 775
      deps/v8/SConstruct
  47. 42
      deps/v8/benchmarks/README.txt
  48. 233
      deps/v8/benchmarks/base.js
  49. 1698
      deps/v8/benchmarks/crypto.js
  50. 880
      deps/v8/benchmarks/deltablue.js
  51. 4685
      deps/v8/benchmarks/earley-boyer.js
  52. 3434
      deps/v8/benchmarks/raytrace.js
  53. 1614
      deps/v8/benchmarks/regexp.js
  54. 62
      deps/v8/benchmarks/revisions.html
  55. 539
      deps/v8/benchmarks/richards.js
  56. 102
      deps/v8/benchmarks/run.html
  57. 60
      deps/v8/benchmarks/run.js
  58. 70
      deps/v8/benchmarks/style.css
  59. BIN
      deps/v8/benchmarks/v8-logo.png
  60. 179
      deps/v8/include/v8-debug.h
  61. 2538
      deps/v8/include/v8.h
  62. 38
      deps/v8/samples/SConscript
  63. 42
      deps/v8/samples/count-hosts.js
  64. 622
      deps/v8/samples/process.cc
  65. 281
      deps/v8/samples/shell.cc
  66. 25
      deps/v8/scons-LICENSE
  67. 204
      deps/v8/scons-README
  68. 1513
      deps/v8/scons-time.py
  69. 165
      deps/v8/scons.py
  70. 502
      deps/v8/sconsign.py
  71. 183
      deps/v8/src/SConscript
  72. 548
      deps/v8/src/accessors.cc
  73. 101
      deps/v8/src/accessors.h
  74. 197
      deps/v8/src/allocation.cc
  75. 168
      deps/v8/src/allocation.h
  76. 3348
      deps/v8/src/api.cc
  77. 454
      deps/v8/src/api.h
  78. 109
      deps/v8/src/apinatives.js
  79. 69
      deps/v8/src/apiutils.h
  80. 70
      deps/v8/src/arguments.h
  81. 938
      deps/v8/src/array.js
  82. 249
      deps/v8/src/assembler-arm-inl.h
  83. 1471
      deps/v8/src/assembler-arm.cc
  84. 789
      deps/v8/src/assembler-arm.h
  85. 302
      deps/v8/src/assembler-ia32-inl.h
  86. 2205
      deps/v8/src/assembler-ia32.cc
  87. 863
      deps/v8/src/assembler-ia32.h
  88. 585
      deps/v8/src/assembler.cc
  89. 456
      deps/v8/src/assembler.h
  90. 490
      deps/v8/src/ast.cc
  91. 1710
      deps/v8/src/ast.h
  92. 1528
      deps/v8/src/bootstrapper.cc
  93. 80
      deps/v8/src/bootstrapper.h
  94. 658
      deps/v8/src/builtins-arm.cc
  95. 756
      deps/v8/src/builtins-ia32.cc
  96. 744
      deps/v8/src/builtins.cc
  97. 217
      deps/v8/src/builtins.h
  98. 103
      deps/v8/src/bytecodes-irregexp.h
  99. 85
      deps/v8/src/char-predicates-inl.h
  100. 64
      deps/v8/src/char-predicates.h

18
configure

@ -4,19 +4,11 @@
# Fancy colors used to beautify the output a bit.
#
if [ "$NOCOLOR" ] ; then
NORMAL=""
BOLD=""
RED=""
YELLOW=""
GREEN=""
else
NORMAL='\\033[0m'
BOLD='\\033[01;1m'
RED='\\033[01;91m'
YELLOW='\\033[00;33m'
GREEN='\\033[01;92m'
fi
NORMAL=""
BOLD=""
RED=""
YELLOW=""
GREEN=""
EXIT_SUCCESS=0
EXIT_FAILURE=1

5
deps/libebb/.gitignore

@ -0,0 +1,5 @@
*.o
examples/hello_world
test_request_parser
ebb_request_parser.c
tags

21
deps/libebb/LICENSE

@ -0,0 +1,21 @@
Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
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 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.

7
deps/libebb/README

@ -0,0 +1,7 @@
see doc/index.html and examples/hello_world.c for explanation
webpage: http://tinyclouds.org/libebb/
git repository: http://github.com/ry/libebb/tree/master
To build libebb please edit config.mk to reflect your system's parameters.

38
deps/libebb/config.mk

@ -0,0 +1,38 @@
PREFIX = $(HOME)/local/libebb
# libev
EVINC = $(HOME)/local/libev/include
EVLIB = $(HOME)/local/libev/lib
EVLIBS = -L${EVLIB} -lev
# GnuTLS, comment if you don't want it (necessary for HTTPS)
GNUTLSLIB = /usr/lib
GNUTLSINC = /usr/include
GNUTLSLIBS = -L${GNUTLSLIB} -lgnutls
GNUTLSFLAGS = -DHAVE_GNUTLS
# includes and libs
INCS = -I${EVINC} -I${GNUTLSINC}
LIBS = ${EVLIBS} ${GNUTLSLIBS} -lefence
# flags
CPPFLAGS = -DVERSION=\"$(VERSION)\" ${GNUTLSFLAGS}
CFLAGS = -O2 -g -Wall ${INCS} ${CPPFLAGS} -fPIC
LDFLAGS = -s ${LIBS}
LDOPT = -shared
SUFFIX = so
SONAME = -Wl,-soname,$(OUTPUT_LIB)
# Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"$(VERSION)\" -fPIC
#LDFLAGS = ${LIBS}
#SONAME =
# Darwin
# LDOPT = -dynamiclib
# SUFFIX = dylib
# SONAME = -current_version $(VERSION) -compatibility_version $(VERSION)
# compiler and linker
CC = cc
RANLIB = ranlib

BIN
deps/libebb/doc/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

240
deps/libebb/doc/index.html

@ -0,0 +1,240 @@
<html>
<style>
body {
background: #fff;
color: #2e3436;
font-size: 12pt;
line-height: 16pt;
/* font-family: Palatino; */
margin: 3em 0 3em 3em;
}
code, pre {
}
#contents {
max-width: 40em;
}
ul {
padding-left: 0;
}
li {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
p {
text-align: left;
}
img {
float: left;
margin: 0 1em 1em 0;
}
p { clear: both; }
</style>
<body> <div id="contents">
<img src="icon.png"/>
<h1>libebb</h1>
<p>
libebb is a lightweight HTTP server library for C. It lays the
foundation for writing a web server by providing the socket juggling
and request parsing. By implementing the HTTP/1.1 grammar provided in
RFC2612, libebb understands most most valid HTTP/1.1 connections
(persistent, pipelined, and chunked requests included) and rejects
invalid or malicious requests. libebb supports SSL over HTTP.
</p>
<p>
The library embraces a minimalistic single-threaded evented design.
No control is removed from the user. For example, all allocations are
done through callbacks so that the user might implement in optimal
ways for their specific application. By design libebb is not
thread-safe and all provided callbacks must not block. libebb uses
the <a href="http://libev.schmorp.de/bench.html">high-performance</a>
libev event loop, but does not control it. The user of the library may
start and stop the loop at will, they may attach thier own watchers.
</p>
<p>
libebb depends on POSIX sockets, libev, and optionally GnuTLS.
</p>
<p>
libebb is in the early stages of development and probably contains
many bugs. The API is subject to radical changes. If you're
interested <a href="http://github.com/ry/libebb/tree/master">checkout
the source code</a> and <a
href="http://groups.google.com/group/ebbebb">join the mailing
list</a>. A release will be made when it proves stable.
</p>
<p>libebb is released under <a
href="http://www.gnu.org/licenses/license-list.html#X11License">the
X11 license</a>.</p>
<h2>Usage</h2>
<p>
libebb is a simple API, mostly it is providing callbacks. There are
two types of callbacks that one will work with:
</p>
<ul>
<li>callbacks to allocate and initialize data for libebb. These are
named <code>new_*</code> like <code>new_connection</code> and
<code>new_request</code></li>
<li>callbacks which happen on an event and might provide a pointer to
a chunk of data. These are named <code>on_*</code> like
<code>on_body</code> and <code>on_close</code>.</li>
</ul>
<p>
In libebb there are three important classes: <code>ebb_server</code>,
<code>ebb_connection</code>, and <code>ebb_request</code>.
Each server has many peer connections. Each peer connection may have many
requests.
There are two additional classes <code>ebb_buf</code> and <code>ebb_request_parser</code>
which may or may not be useful.
</p>
<h3><code>ebb_server</code></h3>
<p>
<code>ebb_server</code> represents a single web server listening on a
single port. The user must allocate the structure themselves, then
call <code>ebb_server_init()</code> and provide a libev event loop.
<code>ebb_server_set_secure()</code> will make the server understand
HTTPS connections.
</p>
<p>
After initialized the <code>ebb_server_listen_on_port()</code> can be
called to open the server up to new connections. libebb does not
control the event loop, it is the user's responsibility to start the
event loop (using <code>ev_loop()</code>) after
<code>ebb_server_listen_on_port()</code> is called.
</p>
<p>
To accept connections you must provide the new server with a callback
called <code>new_connection</code>. This callback must return an allocated
and initialized <code>ebb_connection</code> structure.
To set this callback do
</p>
<pre>my_server-&gt;new_connection = my_new_connection_callback;</pre>
<p>
Additional documentation can be found in <code>ebb.h</code>
</p>
<h3><code>ebb_connection</code></h3>
<p>
This structure contains information and callbacks for a single client
connection. It is allocated and initialized through the
<code>new_connection</code> callback in <code>ebb_server</code>.
To initialize a newly allocated <code>ebb_connection</code> use
<code>ebb_connection_init()</code>.
</p>
<p>
After <code>ebb_connection_init()</code> is called a number of
callbacks can be set: <code>new_request</code>, <code>new_buf</code>,
<code>on_timeout</code>, and <code>on_close</code>.
</p>
<p>
When an <code>ebb_connection</code> is returned to an
<code>ebb_server</code>, data is immediately data is read from the
socket. This data must be stored somewhere. Because libebb is
agnostic about allocation decisions, it passes this off to the user in
the form of a callback: <code>connection-&gt;new_buf</code>. This
callback returns a newly allocated and initialized
<code>ebb_buf</code> structure. How much libebb attempts to read from
the socket is determined by how large the returned
<code>ebb_buf</code> structure is. Using <code>new_buf</code> is
optional. By default libebb reads data into a static buffer
(allocated at compile time), writing over it on each read. In many
web server using the static buffer will be sufficent because callbacks
made during the parsing will buffer the data elsewhere. Providing a
<code>new_buf</code> callback is necessary only if you want to save
the raw data coming from the socket.
</p>
<p>
The <code>new_request</code> callback is called at the beginning of a
request. It must return a newly allocated and initialized
<code>ebb_request</code> structure. Because HTTP/1.1 supports <a
href="http://en.wikipedia.org/wiki/HTTP_persistent_connection">peristant</a>
connections, there may be many requests per connection.
</p>
<p>
You may access the file descriptor for the client socket inside the
<code>ebb_connection</code> structure. Writing the response, in valid
HTTP, is the user's responsibility. Remember, requests must be
returned to client in the same order that they were received.
</p>
<p>
A convience function, <coe>ebb_connection_write</code>, is provided
which will write a single string to the peer. You may use
this function or you may write to the file descriptor directly.
</p>
<p>
To close a peer connection use
<code>ebb_connnection_schedule_close()</code>. Because SSL may require
some additional communication to close the connection properly, the
file descriptor cannot be closed immediately. The
<code>on_close</code> callback will be made when the peer socket is
finally closed.
<em>Only once <code>on_close</code> is called may the
user free the <code>ebb_connection</code> structure.</em>
</p>
<h3><code>ebb_request</code></h3>
<p>
This structure provides information about a request. For example,
<code>request-&gt;method == EBB_POST</code> would mean the method of
the request is <code>POST</code>. There are also many callbacks
which can be set to handle data from a request as it is parsed.
</p>
<p>
The <code>on_uri</code> callback and all other
<code>ebb_element_cb</code> callbacks provide pointers to request
data. The annoying thing is they do not necessarily provide a
complete string. This is because the reads from the socket may not
contain an entire request and for efficency libebb does not attempt to
buffer the data. Theoretically, one might receive an
<code>on_uri</code> callback 10 times, each providing just a single
character of the request's URI. See <code>ebb_request_parser.h</code> for
a full list of callbacks that you may provide. (If you don't set them,
they are ignored.)
</p>
<p>
The <code>on_complete</code> callback is called at the end of
each request.
<em>Only once <code>on_complete</code> is called may the
user free the <code>ebb_request</code> structure.</em>
</p>
<h2>Example</h2>
<p>
A simple example is provided in <code>examples/hello_world.c</code>.
</p>
</div></body>
</html>

798
deps/libebb/ebb.c

@ -0,0 +1,798 @@
/* This file is part of libebb.
*
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
* All rights reserved.
*
* 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 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.
*/
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h> /* TCP_NODELAY */
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h> /* inet_ntoa */
#include <unistd.h>
#include <stdio.h> /* perror */
#include <errno.h> /* perror */
#include <stdlib.h> /* for the default methods */
#include <ev.h>
#include "ebb.h"
#include "ebb_request_parser.h"
#ifdef HAVE_GNUTLS
# include <gnutls/gnutls.h>
# include "rbtree.h" /* for session_cache */
#endif
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#ifndef MIN
# define MIN(a,b) (a < b ? a : b)
#endif
#define error(FORMAT, ...) fprintf(stderr, "error: " FORMAT "\n", ##__VA_ARGS__)
#define CONNECTION_HAS_SOMETHING_TO_WRITE (connection->to_write != NULL)
static void
set_nonblock (int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
assert(0 <= r && "Setting socket non-block failed!");
}
static ssize_t
nosigpipe_push(void *data, const void *buf, size_t len)
{
int fd = (int)data;
int flags = 0;
#ifdef MSG_NOSIGNAL
flags = MSG_NOSIGNAL;
#endif
return send(fd, buf, len, flags);
}
static void
close_connection(ebb_connection *connection)
{
#ifdef HAVE_GNUTLS
if(connection->server->secure)
ev_io_stop(connection->server->loop, &connection->handshake_watcher);
#endif
ev_io_stop(connection->server->loop, &connection->read_watcher);
ev_io_stop(connection->server->loop, &connection->write_watcher);
ev_timer_stop(connection->server->loop, &connection->timeout_watcher);
if(0 > close(connection->fd))
error("problem closing connection fd");
connection->open = FALSE;
if(connection->on_close)
connection->on_close(connection);
/* No access to the connection past this point!
* The user is allowed to free in the callback
*/
}
#ifdef HAVE_GNUTLS
#define GNUTLS_NEED_WRITE (gnutls_record_get_direction(connection->session) == 1)
#define GNUTLS_NEED_READ (gnutls_record_get_direction(connection->session) == 0)
#define EBB_MAX_SESSION_KEY 32
#define EBB_MAX_SESSION_VALUE 512
struct session_cache {
struct rbtree_node_t node;
gnutls_datum_t key;
gnutls_datum_t value;
char key_storage[EBB_MAX_SESSION_KEY];
char value_storage[EBB_MAX_SESSION_VALUE];
};
static int
session_cache_compare (void *left, void *right)
{
gnutls_datum_t *left_key = left;
gnutls_datum_t *right_key = right;
if(left_key->size < right_key->size)
return -1;
else if(left_key->size > right_key->size)
return 1;
else
return memcmp( left_key->data
, right_key->data
, MIN(left_key->size, right_key->size)
);
}
static int
session_cache_store(void *data, gnutls_datum_t key, gnutls_datum_t value)
{
rbtree tree = data;
if( tree == NULL
|| key.size > EBB_MAX_SESSION_KEY
|| value.size > EBB_MAX_SESSION_VALUE
) return -1;
struct session_cache *cache = gnutls_malloc(sizeof(struct session_cache));
memcpy (cache->key_storage, key.data, key.size);
cache->key.size = key.size;
cache->key.data = (void*)cache->key_storage;
memcpy (cache->value_storage, value.data, value.size);
cache->value.size = value.size;
cache->value.data = (void*)cache->value_storage;
cache->node.key = &cache->key;
cache->node.value = &cache;
rbtree_insert(tree, (rbtree_node)cache);
//printf("session_cache_store\n");
return 0;
}
static gnutls_datum_t
session_cache_retrieve (void *data, gnutls_datum_t key)
{
rbtree tree = data;
gnutls_datum_t res = { NULL, 0 };
struct session_cache *cache = rbtree_lookup(tree, &key);
if(cache == NULL)
return res;
res.size = cache->value.size;
res.data = gnutls_malloc (res.size);
if(res.data == NULL)
return res;
memcpy(res.data, cache->value.data, res.size);
//printf("session_cache_retrieve\n");
return res;
}
static int
session_cache_remove (void *data, gnutls_datum_t key)
{
rbtree tree = data;
if(tree == NULL)
return -1;
struct session_cache *cache = (struct session_cache *)rbtree_delete(tree, &key);
if(cache == NULL)
return -1;
gnutls_free(cache);
//printf("session_cache_remove\n");
return 0;
}
static void
on_handshake(struct ev_loop *loop ,ev_io *watcher, int revents)
{
ebb_connection *connection = watcher->data;
//printf("on_handshake\n");
assert(ev_is_active(&connection->timeout_watcher));
assert(!ev_is_active(&connection->read_watcher));
assert(!ev_is_active(&connection->write_watcher));
if(EV_ERROR & revents) {
error("on_handshake() got error event, closing connection.n");
goto error;
}
int r = gnutls_handshake(connection->session);
if(r < 0) {
if(gnutls_error_is_fatal(r)) goto error;
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
ev_io_set( watcher
, connection->fd
, (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ)
);
return;
}
ebb_connection_reset_timeout(connection);
ev_io_stop(loop, watcher);
ev_io_start(loop, &connection->read_watcher);
if(CONNECTION_HAS_SOMETHING_TO_WRITE)
ev_io_start(loop, &connection->write_watcher);
return;
error:
close_connection(connection);
}
#endif /* HAVE_GNUTLS */
/* Internal callback
* called by connection->timeout_watcher
*/
static void
on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
{
ebb_connection *connection = watcher->data;
assert(watcher == &connection->timeout_watcher);
//printf("on_timeout\n");
/* if on_timeout returns true, we don't time out */
if(connection->on_timeout) {
int r = connection->on_timeout(connection);
if(r == EBB_AGAIN) {
ebb_connection_reset_timeout(connection);
return;
}
}
ebb_connection_schedule_close(connection);
}
/* Internal callback
* called by connection->read_watcher
*/
static void
on_readable(struct ev_loop *loop, ev_io *watcher, int revents)
{
ebb_connection *connection = watcher->data;
char recv_buffer[TCP_MAXWIN];
ssize_t recved;
//printf("on_readable\n");
// TODO -- why is this broken?
//assert(ev_is_active(&connection->timeout_watcher));
assert(watcher == &connection->read_watcher);
if(EV_ERROR & revents) {
error("on_readable() got error event, closing connection.");
goto error;
}
#ifdef HAVE_GNUTLS
assert(!ev_is_active(&connection->handshake_watcher));
if(connection->server->secure) {
recved = gnutls_record_recv( connection->session
, recv_buffer
, TCP_MAXWIN
);
if(recved <= 0) {
if(gnutls_error_is_fatal(recved)) goto error;
if( (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN)
&& GNUTLS_NEED_WRITE
) ev_io_start(loop, &connection->write_watcher);
return;
}
} else {
#endif /* HAVE_GNUTLS */
recved = recv(connection->fd, recv_buffer, TCP_MAXWIN, 0);
if(recved <= 0) goto error;
#ifdef HAVE_GNUTLS
}
#endif /* HAVE_GNUTLS */
ebb_connection_reset_timeout(connection);
ebb_request_parser_execute(&connection->parser, recv_buffer, recved);
/* parse error? just drop the client. screw the 400 response */
if(ebb_request_parser_has_error(&connection->parser)) goto error;
return;
error:
ebb_connection_schedule_close(connection);
}
/* Internal callback
* called by connection->write_watcher
*/
static void
on_writable(struct ev_loop *loop, ev_io *watcher, int revents)
{
ebb_connection *connection = watcher->data;
ssize_t sent;
//printf("on_writable\n");
assert(CONNECTION_HAS_SOMETHING_TO_WRITE);
assert(connection->written <= connection->to_write_len);
// TODO -- why is this broken?
//assert(ev_is_active(&connection->timeout_watcher));
assert(watcher == &connection->write_watcher);
if(connection->to_write == 0)
goto stop_writing;
#ifdef HAVE_GNUTLS
assert(!ev_is_active(&connection->handshake_watcher));
if(connection->server->secure) {
sent = gnutls_record_send( connection->session
, connection->to_write + connection->written
, connection->to_write_len - connection->written
);
if(sent < 0) {
if(gnutls_error_is_fatal(sent)) goto error;
if( (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN)
&& GNUTLS_NEED_READ
) ev_io_stop(loop, watcher);
return;
}
} else {
#endif /* HAVE_GNUTLS */
sent = nosigpipe_push( (void*)connection->fd
, connection->to_write + connection->written
, connection->to_write_len - connection->written
);
if(sent < 0) goto error;
if(sent == 0) return;
#ifdef HAVE_GNUTLS
}
#endif /* HAVE_GNUTLS */
ebb_connection_reset_timeout(connection);
connection->written += sent;
if(connection->written == connection->to_write_len) {
goto stop_writing;
}
return;
stop_writing:
ev_io_stop(loop, watcher);
connection->to_write = NULL;
if(connection->after_write_cb)
connection->after_write_cb(connection);
return;
error:
error("close connection on write.");
ebb_connection_schedule_close(connection);
}
#ifdef HAVE_GNUTLS
static void
on_goodbye_tls(struct ev_loop *loop, ev_io *watcher, int revents)
{
ebb_connection *connection = watcher->data;
assert(watcher == &connection->goodbye_tls_watcher);
if(EV_ERROR & revents) {
error("on_goodbye() got error event, closing connection.");
goto die;
}
int r = gnutls_bye(connection->session, GNUTLS_SHUT_RDWR);
if(r < 0) {
if(gnutls_error_is_fatal(r)) goto die;
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
ev_io_set( watcher
, connection->fd
, (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ)
);
return;
}
die:
ev_io_stop(loop, watcher);
if(connection->session)
gnutls_deinit(connection->session);
close_connection(connection);
}
#endif /* HAVE_GNUTLS*/
static void
on_goodbye(struct ev_loop *loop, ev_timer *watcher, int revents)
{
ebb_connection *connection = watcher->data;
assert(watcher == &connection->goodbye_watcher);
close_connection(connection);
}
static ebb_request*
new_request_wrapper(void *data)
{
ebb_connection *connection = data;
if(connection->new_request)
return connection->new_request(connection);
return NULL;
}
/* Internal callback
* Called by server->connection_watcher.
*/
static void
on_connection(struct ev_loop *loop, ev_io *watcher, int revents)
{
ebb_server *server = watcher->data;
//printf("on connection!\n");
assert(server->listening);
assert(server->loop == loop);
assert(&server->connection_watcher == watcher);
if(EV_ERROR & revents) {
error("on_connection() got error event, closing server.");
ebb_server_unlisten(server);
return;
}
struct sockaddr_in addr; // connector's address information
socklen_t addr_len = sizeof(addr);
int fd = accept( server->fd
, (struct sockaddr*) & addr
, & addr_len
);
if(fd < 0) {
perror("accept()");
return;
}
ebb_connection *connection = NULL;
if(server->new_connection)
connection = server->new_connection(server, &addr);
if(connection == NULL) {
close(fd);
return;
}
set_nonblock(fd);
connection->fd = fd;
connection->open = TRUE;
connection->server = server;
memcpy(&connection->sockaddr, &addr, addr_len);
if(server->port[0] != '\0')
connection->ip = inet_ntoa(connection->sockaddr.sin_addr);
#ifdef SO_NOSIGPIPE
int arg = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &arg, sizeof(int));
#endif
#ifdef HAVE_GNUTLS
if(server->secure) {
gnutls_init(&connection->session, GNUTLS_SERVER);
gnutls_transport_set_lowat(connection->session, 0);
gnutls_set_default_priority(connection->session);
gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, connection->server->credentials);
gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr) fd);
gnutls_transport_set_push_function(connection->session, nosigpipe_push);
gnutls_db_set_ptr (connection->session, &server->session_cache);
gnutls_db_set_store_function (connection->session, session_cache_store);
gnutls_db_set_retrieve_function (connection->session, session_cache_retrieve);
gnutls_db_set_remove_function (connection->session, session_cache_remove);
}
ev_io_set(&connection->handshake_watcher, connection->fd, EV_READ | EV_WRITE);
#endif /* HAVE_GNUTLS */
/* Note: not starting the write watcher until there is data to be written */
ev_io_set(&connection->write_watcher, connection->fd, EV_WRITE);
ev_io_set(&connection->read_watcher, connection->fd, EV_READ);
/* XXX: seperate error watcher? */
ev_timer_again(loop, &connection->timeout_watcher);
#ifdef HAVE_GNUTLS
if(server->secure) {
ev_io_start(loop, &connection->handshake_watcher);
return;
}
#endif
ev_io_start(loop, &connection->read_watcher);
}
/**
* Begin the server listening on a file descriptor. This DOES NOT start the
* event loop. Start the event loop after making this call.
*/
int
ebb_server_listen_on_fd(ebb_server *server, const int fd)
{
assert(server->listening == FALSE);
if (listen(fd, EBB_MAX_CONNECTIONS) < 0) {
perror("listen()");
return -1;
}
set_nonblock(fd); /* XXX superfluous? */
server->fd = fd;
server->listening = TRUE;
ev_io_set (&server->connection_watcher, server->fd, EV_READ);
ev_io_start (server->loop, &server->connection_watcher);
return server->fd;
}
/**
* Begin the server listening on a file descriptor This DOES NOT start the
* event loop. Start the event loop after making this call.
*/
int
ebb_server_listen_on_port(ebb_server *server, const int port)
{
int fd = -1;
struct linger ling = {0, 0};
struct sockaddr_in addr;
int flags = 1;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket()");
goto error;
}
flags = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
/* XXX: Sending single byte chunks in a response body? Perhaps there is a
* need to enable the Nagel algorithm dynamically. For now disabling.
*/
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
/* the memset call clears nonstandard fields in some impementations that
* otherwise mess things up.
*/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind()");
goto error;
}
int ret = ebb_server_listen_on_fd(server, fd);
if (ret >= 0) {
sprintf(server->port, "%d", port);
}
return ret;
error:
if(fd > 0) close(fd);
return -1;
}
/**
* Stops the server. Will not accept new connections. Does not drop
* existing connections.
*/
void
ebb_server_unlisten(ebb_server *server)
{
if(server->listening) {
ev_io_stop(server->loop, &server->connection_watcher);
close(server->fd);
server->port[0] = '\0';
server->listening = FALSE;
}
}
/**
* Initialize an ebb_server structure. After calling ebb_server_init set
* the callback server->new_connection and, optionally, callback data
* server->data. The new connection MUST be initialized with
* ebb_connection_init before returning it to the server.
*
* @param server the server to initialize
* @param loop a libev loop
*/
void
ebb_server_init(ebb_server *server, struct ev_loop *loop)
{
server->loop = loop;
server->listening = FALSE;
server->port[0] = '\0';
server->fd = -1;
server->connection_watcher.data = server;
ev_init (&server->connection_watcher, on_connection);
server->secure = FALSE;
#ifdef HAVE_GNUTLS
rbtree_init(&server->session_cache, session_cache_compare);
server->credentials = NULL;
#endif
server->new_connection = NULL;
server->data = NULL;
}
#ifdef HAVE_GNUTLS
/* similar to server_init.
*
* the user of secure server might want to set additional callbacks from
* GNUTLS. In particular
* gnutls_global_set_mem_functions()
* gnutls_global_set_log_function()
* Also see the note above ebb_connection_init() about setting gnutls cache
* access functions
*
* cert_file: the filename of a PEM certificate file
*
* key_file: the filename of a private key. Currently only PKCS-1 encoded
* RSA and DSA private keys are accepted.
*/
int
ebb_server_set_secure (ebb_server *server, const char *cert_file, const char *key_file)
{
server->secure = TRUE;
gnutls_global_init();
gnutls_certificate_allocate_credentials(&server->credentials);
/* todo gnutls_certificate_free_credentials */
int r = gnutls_certificate_set_x509_key_file( server->credentials
, cert_file
, key_file
, GNUTLS_X509_FMT_PEM
);
if(r < 0) {
error("loading certificates");
return -1;
}
return 1;
}
#endif /* HAVE_GNUTLS */
/**
* Initialize an ebb_connection structure. After calling this function you
* must setup callbacks for the different actions the server can take. See
* server.h for which callbacks are availible.
*
* This should be called immediately after allocating space for a new
* ebb_connection structure. Most likely, this will only be called within
* the ebb_server->new_connection callback which you supply.
*
* If using SSL do consider setting
* gnutls_db_set_retrieve_function (connection->session, _);
* gnutls_db_set_remove_function (connection->session, _);
* gnutls_db_set_store_function (connection->session, _);
* gnutls_db_set_ptr (connection->session, _);
* To provide a better means of storing SSL session caches. libebb provides
* only a simple default implementation.
*
* @param connection the connection to initialize
* @param timeout the timeout in seconds
*/
void
ebb_connection_init(ebb_connection *connection)
{
connection->fd = -1;
connection->server = NULL;
connection->ip = NULL;
connection->open = FALSE;
ebb_request_parser_init( &connection->parser );
connection->parser.data = connection;
connection->parser.new_request = new_request_wrapper;
ev_init (&connection->write_watcher, on_writable);
connection->write_watcher.data = connection;
connection->to_write = NULL;
ev_init(&connection->read_watcher, on_readable);
connection->read_watcher.data = connection;
#ifdef HAVE_GNUTLS
connection->handshake_watcher.data = connection;
ev_init(&connection->handshake_watcher, on_handshake);
ev_init(&connection->goodbye_tls_watcher, on_goodbye_tls);
connection->goodbye_tls_watcher.data = connection;
connection->session = NULL;
#endif /* HAVE_GNUTLS */
ev_timer_init(&connection->goodbye_watcher, on_goodbye, 0., 0.);
connection->goodbye_watcher.data = connection;
ev_timer_init(&connection->timeout_watcher, on_timeout, 0., EBB_DEFAULT_TIMEOUT);
connection->timeout_watcher.data = connection;
connection->new_request = NULL;
connection->on_timeout = NULL;
connection->on_close = NULL;
connection->data = NULL;
}
void
ebb_connection_schedule_close (ebb_connection *connection)
{
#ifdef HAVE_GNUTLS
if(connection->server->secure) {
ev_io_set(&connection->goodbye_tls_watcher, connection->fd, EV_READ | EV_WRITE);
ev_io_start(connection->server->loop, &connection->goodbye_tls_watcher);
return;
}
#endif
ev_timer_start(connection->server->loop, &connection->goodbye_watcher);
}
/*
* Resets the timeout to stay alive for another connection->timeout seconds
*/
void
ebb_connection_reset_timeout(ebb_connection *connection)
{
ev_timer_again(connection->server->loop, &connection->timeout_watcher);
}
/**
* Writes a string to the socket. This is actually sets a watcher
* which may take multiple iterations to write the entire string.
*
* This can only be called once at a time. If you call it again
* while the connection is writing another buffer the ebb_connection_write
* will return FALSE and ignore the request.
*/
int
ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb cb)
{
if(ev_is_active(&connection->write_watcher))
return FALSE;
assert(!CONNECTION_HAS_SOMETHING_TO_WRITE);
connection->to_write = buf;
connection->to_write_len = len;
connection->written = 0;
connection->after_write_cb = cb;
ev_io_start(connection->server->loop, &connection->write_watcher);
return TRUE;
}

120
deps/libebb/ebb.h

@ -0,0 +1,120 @@
/* This file is part of libebb.
*
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
* All rights reserved.
*
* 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 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.
*/
#ifndef EBB_H
#define EBB_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <ev.h>
#ifdef HAVE_GNUTLS
# include <gnutls/gnutls.h>
# include "rbtree.h" /* for ebb_server.session_cache */
#endif
#include "ebb_request_parser.h"
#define EBB_MAX_CONNECTIONS 1024
#define EBB_DEFAULT_TIMEOUT 30.0
#define EBB_AGAIN 0
#define EBB_STOP 1
typedef struct ebb_server ebb_server;
typedef struct ebb_connection ebb_connection;
typedef void (*ebb_after_write_cb) (ebb_connection *connection);
typedef void (*ebb_connection_cb)(ebb_connection *connection, void *data);
struct ebb_server {
int fd; /* ro */
struct sockaddr_in sockaddr; /* ro */
socklen_t socklen; /* ro */
char port[6]; /* ro */
struct ev_loop *loop; /* ro */
unsigned listening:1; /* ro */
unsigned secure:1; /* ro */
#ifdef HAVE_GNUTLS
gnutls_certificate_credentials_t credentials; /* private */
struct rbtree_t session_cache; /* private */
#endif
ev_io connection_watcher; /* private */
/* Public */
/* Allocates and initializes an ebb_connection. NULL by default. */
ebb_connection* (*new_connection) (ebb_server*, struct sockaddr_in*);
void *data;
};
struct ebb_connection {
int fd; /* ro */
struct sockaddr_in sockaddr; /* ro */
socklen_t socklen; /* ro */
ebb_server *server; /* ro */
char *ip; /* ro */
unsigned open:1; /* ro */
const char *to_write; /* ro */
size_t to_write_len; /* ro */
size_t written; /* ro */
ebb_after_write_cb after_write_cb; /* ro */
ebb_request_parser parser; /* private */
ev_io write_watcher; /* private */
ev_io read_watcher; /* private */
ev_timer timeout_watcher; /* private */
ev_timer goodbye_watcher; /* private */
#ifdef HAVE_GNUTLS
ev_io handshake_watcher; /* private */
gnutls_session_t session; /* private */
ev_io goodbye_tls_watcher; /* private */
#endif
/* Public */
ebb_request* (*new_request) (ebb_connection*);
/* Returns EBB_STOP or EBB_AGAIN. NULL by default. */
int (*on_timeout) (ebb_connection*);
void (*on_close) (ebb_connection*);
void *data;
};
void ebb_server_init (ebb_server *server, struct ev_loop *loop);
#ifdef HAVE_GNUTLS
int ebb_server_set_secure (ebb_server *server, const char *cert_file,
const char *key_file);
#endif
int ebb_server_listen_on_port (ebb_server *server, const int port);
int ebb_server_listen_on_fd (ebb_server *server, const int sfd);
void ebb_server_unlisten (ebb_server *server);
void ebb_connection_init (ebb_connection *);
void ebb_connection_schedule_close (ebb_connection *);
void ebb_connection_reset_timeout (ebb_connection *);
int ebb_connection_write (ebb_connection *, const char *buf, size_t len, ebb_after_write_cb);
#endif

117
deps/libebb/ebb_request_parser.h

@ -0,0 +1,117 @@
/* This file is part of the libebb web server library
*
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
* All rights reserved.
*
* This parser is based on code from Zed Shaw's Mongrel.
* Copyright (c) 2005 Zed A. Shaw
*
* 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 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.
*/
#ifndef ebb_request_parser_h
#define ebb_request_parser_h
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
typedef struct ebb_request ebb_request;
typedef struct ebb_request_parser ebb_request_parser;
typedef void (*ebb_header_cb)(ebb_request*, const char *at, size_t length, int header_index);
typedef void (*ebb_element_cb)(ebb_request*, const char *at, size_t length);
#define EBB_MAX_MULTIPART_BOUNDARY_LEN 20
/* HTTP Methods */
#define EBB_COPY 0x00000001
#define EBB_DELETE 0x00000002
#define EBB_GET 0x00000004
#define EBB_HEAD 0x00000008
#define EBB_LOCK 0x00000010
#define EBB_MKCOL 0x00000020
#define EBB_MOVE 0x00000040
#define EBB_OPTIONS 0x00000080
#define EBB_POST 0x00000100
#define EBB_PROPFIND 0x00000200
#define EBB_PROPPATCH 0x00000400
#define EBB_PUT 0x00000800
#define EBB_TRACE 0x00001000
#define EBB_UNLOCK 0x00002000
/* Transfer Encodings */
#define EBB_IDENTITY 0x00000001
#define EBB_CHUNKED 0x00000002
struct ebb_request {
int method;
int transfer_encoding; /* ro */
int expect_continue; /* ro */
unsigned int version_major; /* ro */
unsigned int version_minor; /* ro */
int number_of_headers; /* ro */
int keep_alive; /* private - use ebb_request_should_keep_alive */
size_t content_length; /* ro - 0 if unknown */
size_t body_read; /* ro */
/* Public - ordered list of callbacks */
ebb_element_cb on_path;
ebb_element_cb on_query_string;
ebb_element_cb on_uri;
ebb_element_cb on_fragment;
ebb_header_cb on_header_field;
ebb_header_cb on_header_value;
void (*on_headers_complete)(ebb_request *);
ebb_element_cb on_body;
void (*on_complete)(ebb_request *);
void *data;
};
struct ebb_request_parser {
int cs; /* private */
size_t chunk_size; /* private */
unsigned eating:1; /* private */
ebb_request *current_request; /* ro */
const char *header_field_mark;
const char *header_value_mark;
const char *query_string_mark;
const char *path_mark;
const char *uri_mark;
const char *fragment_mark;
/* Public */
ebb_request* (*new_request)(void*);
void *data;
};
void ebb_request_parser_init(ebb_request_parser *parser);
size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *data, size_t len);
int ebb_request_parser_has_error(ebb_request_parser *parser);
int ebb_request_parser_is_finished(ebb_request_parser *parser);
void ebb_request_init(ebb_request *);
int ebb_request_should_keep_alive(ebb_request *request);
#define ebb_request_has_body(request) \
(request->transfer_encoding == EBB_CHUNKED || request->content_length > 0 )
#ifdef __cplusplus
}
#endif
#endif

413
deps/libebb/ebb_request_parser.rl

@ -0,0 +1,413 @@
/* This file is part of the libebb web server library
*
* Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
* All rights reserved.
*
* This parser is based on code from Zed Shaw's Mongrel.
* Copyright (c) 2005 Zed A. Shaw
*
* 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 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.
*/
#include "ebb_request_parser.h"
#include <stdio.h>
#include <assert.h>
static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
#define TRUE 1
#define FALSE 0
#define MIN(a,b) (a < b ? a : b)
#define REMAINING (pe - p)
#define CURRENT (parser->current_request)
#define CONTENT_LENGTH (parser->current_request->content_length)
#define CALLBACK(FOR) \
if(parser->FOR##_mark && CURRENT->on_##FOR) { \
CURRENT->on_##FOR( CURRENT \
, parser->FOR##_mark \
, p - parser->FOR##_mark \
); \
}
#define HEADER_CALLBACK(FOR) \
if(parser->FOR##_mark && CURRENT->on_##FOR) { \
CURRENT->on_##FOR( CURRENT \
, parser->FOR##_mark \
, p - parser->FOR##_mark \
, CURRENT->number_of_headers \
); \
}
#define END_REQUEST \
if(CURRENT->on_complete) \
CURRENT->on_complete(CURRENT); \
CURRENT = NULL;
%%{
machine ebb_request_parser;
action mark_header_field { parser->header_field_mark = p; }
action mark_header_value { parser->header_value_mark = p; }
action mark_fragment { parser->fragment_mark = p; }
action mark_query_string { parser->query_string_mark = p; }
action mark_request_path { parser->path_mark = p; }
action mark_request_uri { parser->uri_mark = p; }
action write_field {
HEADER_CALLBACK(header_field);
parser->header_field_mark = NULL;
}
action write_value {
HEADER_CALLBACK(header_value);
parser->header_value_mark = NULL;
}
action request_uri {
CALLBACK(uri);
parser->uri_mark = NULL;
}
action fragment {
CALLBACK(fragment);
parser->fragment_mark = NULL;
}
action query_string {
CALLBACK(query_string);
parser->query_string_mark = NULL;
}
action request_path {
CALLBACK(path);
parser->path_mark = NULL;
}
action content_length {
CURRENT->content_length *= 10;
CURRENT->content_length += *p - '0';
}
action use_identity_encoding { CURRENT->transfer_encoding = EBB_IDENTITY; }
action use_chunked_encoding { CURRENT->transfer_encoding = EBB_CHUNKED; }
action set_keep_alive { CURRENT->keep_alive = TRUE; }
action set_not_keep_alive { CURRENT->keep_alive = FALSE; }
action expect_continue {
CURRENT->expect_continue = TRUE;
}
action trailer {
/* not implemenetd yet. (do requests even have trailing headers?) */
}
action version_major {
CURRENT->version_major *= 10;
CURRENT->version_major += *p - '0';
}
action version_minor {
CURRENT->version_minor *= 10;
CURRENT->version_minor += *p - '0';
}
action end_header_line {
CURRENT->number_of_headers++;
}
action end_headers {
if(CURRENT->on_headers_complete)
CURRENT->on_headers_complete(CURRENT);
}
action add_to_chunk_size {
parser->chunk_size *= 16;
parser->chunk_size += unhex[(int)*p];
}
action skip_chunk_data {
skip_body(&p, parser, MIN(parser->chunk_size, REMAINING));
fhold;
if(parser->chunk_size > REMAINING) {
fbreak;
} else {
fgoto chunk_end;
}
}
action end_chunked_body {
END_REQUEST;
fnext main;
}
action start_req {
assert(CURRENT == NULL);
CURRENT = parser->new_request(parser->data);
}
action body_logic {
if(CURRENT->transfer_encoding == EBB_CHUNKED) {
fnext ChunkedBody;
} else {
/* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */
parser->chunk_size = CURRENT->content_length;
p += 1;
skip_body(&p, parser, MIN(REMAINING, CURRENT->content_length));
fhold;
if(parser->chunk_size > REMAINING) {
fbreak;
}
}
}
#
##
###
#### HTTP/1.1 STATE MACHINE
###
## RequestHeaders and character types are from
# Zed Shaw's beautiful Mongrel parser.
CRLF = "\r\n";
# character types
CTL = (cntrl | 127);
safe = ("$" | "-" | "_" | ".");
extra = ("!" | "*" | "'" | "(" | ")" | ",");
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
national = any -- (alpha | digit | reserved | extra | safe | unsafe);
unreserved = (alpha | digit | safe | extra | national);
escape = ("%" xdigit xdigit);
uchar = (unreserved | escape);
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
# elements
token = (ascii -- (CTL | tspecials));
quote = "\"";
# qdtext = token -- "\"";
# quoted_pair = "\" ascii;
# quoted_string = "\"" (qdtext | quoted_pair )* "\"";
# headers
Method = ( "COPY" %{ CURRENT->method = EBB_COPY; }
| "DELETE" %{ CURRENT->method = EBB_DELETE; }
| "GET" %{ CURRENT->method = EBB_GET; }
| "HEAD" %{ CURRENT->method = EBB_HEAD; }
| "LOCK" %{ CURRENT->method = EBB_LOCK; }
| "MKCOL" %{ CURRENT->method = EBB_MKCOL; }
| "MOVE" %{ CURRENT->method = EBB_MOVE; }
| "OPTIONS" %{ CURRENT->method = EBB_OPTIONS; }
| "POST" %{ CURRENT->method = EBB_POST; }
| "PROPFIND" %{ CURRENT->method = EBB_PROPFIND; }
| "PROPPATCH" %{ CURRENT->method = EBB_PROPPATCH; }
| "PUT" %{ CURRENT->method = EBB_PUT; }
| "TRACE" %{ CURRENT->method = EBB_TRACE; }
| "UNLOCK" %{ CURRENT->method = EBB_UNLOCK; }
); # Not allowing extension methods
HTTP_Version = "HTTP/" digit+ $version_major "." digit+ $version_minor;
scheme = ( alpha | digit | "+" | "-" | "." )* ;
absolute_uri = (scheme ":" (uchar | reserved )*);
path = ( pchar+ ( "/" pchar* )* ) ;
query = ( uchar | reserved )* >mark_query_string %query_string ;
param = ( pchar | "/" )* ;
params = ( param ( ";" param )* ) ;
rel_path = ( path? (";" params)? ) ;
absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?;
Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri;
Fragment = ( uchar | reserved )* >mark_fragment %fragment;
field_name = ( token -- ":" )+;
Field_Name = field_name >mark_header_field %write_field;
field_value = ((any - " ") any*)?;
Field_Value = field_value >mark_header_value %write_value;
hsep = ":" " "*;
header = (field_name hsep field_value) :> CRLF;
Header = ( ("Content-Length"i hsep digit+ $content_length)
| ("Connection"i hsep
( "Keep-Alive"i %set_keep_alive
| "close"i %set_not_keep_alive
)
)
| ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
# | ("Expect"i hsep "100-continue"i %expect_continue)
# | ("Trailer"i hsep field_value %trailer)
| (Field_Name hsep Field_Value)
) :> CRLF;
Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ;
RequestHeader = Request_Line (Header %end_header_line)* :> CRLF @end_headers;
# chunked message
trailing_headers = header*;
#chunk_ext_val = token | quoted_string;
chunk_ext_val = token*;
chunk_ext_name = token*;
chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
last_chunk = "0"+ chunk_extension CRLF;
chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
chunk_end = CRLF;
chunk_body = any >skip_chunk_data;
chunk_begin = chunk_size chunk_extension CRLF;
chunk = chunk_begin chunk_body chunk_end;
ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;
Request = RequestHeader >start_req @body_logic;
main := Request*; # sequence of requests (for keep-alive)
}%%
%% write data;
static void
skip_body(const char **p, ebb_request_parser *parser, size_t nskip) {
if(CURRENT->on_body && nskip > 0) {
CURRENT->on_body(CURRENT, *p, nskip);
}
CURRENT->body_read += nskip;
parser->chunk_size -= nskip;
*p += nskip;
if(0 == parser->chunk_size) {
parser->eating = FALSE;
if(CURRENT->transfer_encoding == EBB_IDENTITY) {
END_REQUEST;
}
} else {
parser->eating = TRUE;
}
}
void ebb_request_parser_init(ebb_request_parser *parser)
{
int cs = 0;
%% write init;
parser->cs = cs;
parser->chunk_size = 0;
parser->eating = 0;
parser->current_request = NULL;
parser->header_field_mark = parser->header_value_mark =
parser->query_string_mark = parser->path_mark =
parser->uri_mark = parser->fragment_mark = NULL;
parser->new_request = NULL;
}
/** exec **/
size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *buffer, size_t len)
{
const char *p, *pe;
int cs = parser->cs;
assert(parser->new_request && "undefined callback");
p = buffer;
pe = buffer+len;
if(0 < parser->chunk_size && parser->eating) {
/* eat body */
size_t eat = MIN(len, parser->chunk_size);
skip_body(&p, parser, eat);
}
if(parser->header_field_mark) parser->header_field_mark = buffer;
if(parser->header_value_mark) parser->header_value_mark = buffer;
if(parser->fragment_mark) parser->fragment_mark = buffer;
if(parser->query_string_mark) parser->query_string_mark = buffer;
if(parser->path_mark) parser->path_mark = buffer;
if(parser->uri_mark) parser->uri_mark = buffer;
%% write exec;
parser->cs = cs;
HEADER_CALLBACK(header_field);
HEADER_CALLBACK(header_value);
CALLBACK(fragment);
CALLBACK(query_string);
CALLBACK(path);
CALLBACK(uri);
assert(p <= pe && "buffer overflow after parsing execute");
return(p - buffer);
}
int ebb_request_parser_has_error(ebb_request_parser *parser)
{
return parser->cs == ebb_request_parser_error;
}
int ebb_request_parser_is_finished(ebb_request_parser *parser)
{
return parser->cs == ebb_request_parser_first_final;
}
void ebb_request_init(ebb_request *request)
{
request->expect_continue = FALSE;
request->body_read = 0;
request->content_length = 0;
request->version_major = 0;
request->version_minor = 0;
request->number_of_headers = 0;
request->transfer_encoding = EBB_IDENTITY;
request->keep_alive = -1;
request->on_complete = NULL;
request->on_headers_complete = NULL;
request->on_body = NULL;
request->on_header_field = NULL;
request->on_header_value = NULL;
request->on_uri = NULL;
request->on_fragment = NULL;
request->on_path = NULL;
request->on_query_string = NULL;
}
int ebb_request_should_keep_alive(ebb_request *request)
{
if(request->keep_alive == -1)
if(request->version_major == 1)
return (request->version_minor != 0);
else if(request->version_major == 0)
return FALSE;
else
return TRUE;
else
return request->keep_alive;
}

12
deps/libebb/examples/ca-cert.pem

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBzDCCATegAwIBAgIESIuNVTALBgkqhkiG9w0BAQUwADAeFw0wODA3MjYyMDQ3
MTlaFw0xMTA0MjIyMDQ3MjVaMAAwgZwwCwYJKoZIhvcNAQEBA4GMADCBiAKBgOm9
l/FoXbTIcEusk/QlS5YrlR04+oWIbSdZIf3GJBEWEUPljDxAX96qHsTcaVnGK+EP
keU4cIZvdY+hzbqa5cc1j2/9IeJNejL8gpQ/ocyMM69yq5Ib2F8K4mGWm1xr30hU
bYpY5D0MrZ1b0HtYFVc8KVAr0ADGG+pye0P9c3B/AgMBAAGjWjBYMAwGA1UdEwEB
/wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMB
MB0GA1UdDgQWBBTYgVB7kJnnm+jgX9DgrapzGfUmxjALBgkqhkiG9w0BAQUDgYEA
GkadA2H8CAzU3w4oCGZu9Ry9Tj/9Agw1XMFKvoJuG7VLPk7+B25JvNFVsmpROLxO
0TJ6mIU2hz5/rLvEfTBGQ+DYtbsjIxCz1fD7R5c1kKBtA0d0u8mY8pTlPNlxFPSW
3ymx5DB2zyDa/HuX6m6/VmzMYmA0vp7Dp1cl+pA9Nhs=
-----END CERTIFICATE-----

15
deps/libebb/examples/ca-key.pem

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDpvZfxaF20yHBLrJP0JUuWK5UdOPqFiG0nWSH9xiQRFhFD5Yw8
QF/eqh7E3GlZxivhD5HlOHCGb3WPoc26muXHNY9v/SHiTXoy/IKUP6HMjDOvcquS
G9hfCuJhlptca99IVG2KWOQ9DK2dW9B7WBVXPClQK9AAxhvqcntD/XNwfwIDAQAB
AoGAJqo3LTbfcV1KvinhG5zjwQaalwfq4RXtQHoNFmalZrIozvt01C6t7S5lApmX
T8NpVMR3lNxeOM7NOqJAXuLqqVVqk81YEYuMx6E4gB/Ifl7jVZk1jstmLILhh59D
pXrlpzvvm5X2hVsI7lp/YGAvtdLS1iVy37bGgmQWfCeeZiECQQDtZLfcJb4oE1yR
ZfLOcPDlBCw02wGMNFpAjwbspf/du3Yn3ONWHVfhSCCcCe262h9PLblL97LoB+gF
OHOlM9JvAkEA/A+U3/p9pwL4L742pxZP62/rmk6p5mZFIykk2jIUTpdilXBWBlcT
OjgjnpquZpwnaClmvgFpkzdhIPF7Nq4K8QJBAITVKagOmnOUOeTF1fI78h9DkXTV
4uzP0nxzS52ZWS16Gqg9ihuCecz97flB+Prn2EMWw6tFY58/5U0ehF85OxMCQQDF
08TYdVSg+6emcPeb89sNwW18UjjuZ13j1qrhxWRCunXZK62YlEa27tCl7mjqh6w2
CChm/9zIejJ1FJHLvJVBAkBj63ZbwggMYkxuj60jIBbNrEtDx9y7zM0sXkiJqcKp
5uGtJNafG+yZrLAHE6/b4aqUOtGsCGsiZpT9ms7CoaVr
-----END RSA PRIVATE KEY-----

101
deps/libebb/examples/hello_world.c

@ -0,0 +1,101 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <ev.h>
#include "ebb.h"
#define MSG ("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nhello world\n")
static int c = 0;
struct hello_connection {
unsigned int responses_to_write;
};
void on_close(ebb_connection *connection)
{
free(connection->data);
free(connection);
}
static void continue_responding(ebb_connection *connection)
{
int r;
struct hello_connection *connection_data = connection->data;
//printf("response complete \n");
if(--connection_data->responses_to_write > 0) {
/* write another response */
r = ebb_connection_write(connection, MSG, sizeof MSG, continue_responding);
assert(r);
} else {
ebb_connection_schedule_close(connection);
}
}
static void request_complete(ebb_request *request)
{
//printf("request complete \n");
ebb_connection *connection = request->data;
struct hello_connection *connection_data = connection->data;
if(ebb_request_should_keep_alive(request))
connection_data->responses_to_write++;
else
connection_data->responses_to_write = 1;
ebb_connection_write(connection, MSG, sizeof MSG, continue_responding);
free(request);
}
static ebb_request* new_request(ebb_connection *connection)
{
//printf("request %d\n", ++c);
ebb_request *request = malloc(sizeof(ebb_request));
ebb_request_init(request);
request->data = connection;
request->on_complete = request_complete;
return request;
}
ebb_connection* new_connection(ebb_server *server, struct sockaddr_in *addr)
{
struct hello_connection *connection_data = malloc(sizeof(struct hello_connection));
if(connection_data == NULL)
return NULL;
connection_data->responses_to_write = 0;
ebb_connection *connection = malloc(sizeof(ebb_connection));
if(connection == NULL) {
free(connection_data);
return NULL;
}
ebb_connection_init(connection);
connection->data = connection_data;
connection->new_request = new_request;
connection->on_close = on_close;
printf("connection: %d\n", c++);
return connection;
}
int main(int argc, char **_)
{
struct ev_loop *loop = ev_default_loop(0);
ebb_server server;
ebb_server_init(&server, loop);
if(argc > 1) {
printf("using SSL\n");
ebb_server_set_secure(&server, "ca-cert.pem", "ca-key.pem");
}
server.new_connection = new_connection;
printf("hello_world listening on port 5000\n");
ebb_server_listen_on_port(&server, 5000);
ev_loop(loop, 0);
return 0;
}

412
deps/libebb/rbtree.c

@ -0,0 +1,412 @@
/* Copyright (c) 2008 Derrick Coetzee
* http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
*
* 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 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.
*/
#include "rbtree.h"
#include <assert.h>
#ifndef NULL
# define NULL ((void*)0)
#endif
typedef rbtree_node node;
typedef enum rbtree_node_color color;
static node grandparent(node n);
static node sibling(node n);
static node uncle(node n);
static void verify_properties(rbtree t);
static void verify_property_1(node root);
/* static void verify_property_2(node root); */
static color node_color(node n);
static void verify_property_4(node root);
/* static void verify_property_5(node root); */
static void verify_property_5_helper(node n, int black_count, int* black_count_path);
static node lookup_node(rbtree t, void* key);
static void rotate_left(rbtree t, node n);
static void rotate_right(rbtree t, node n);
static void replace_node(rbtree t, node oldn, node newn);
static void insert_case1(rbtree t, node n);
static void insert_case2(rbtree t, node n);
static void insert_case3(rbtree t, node n);
static void insert_case4(rbtree t, node n);
static void insert_case5(rbtree t, node n);
static node maximum_node(node root);
static void delete_case1(rbtree t, node n);
static void delete_case2(rbtree t, node n);
static void delete_case3(rbtree t, node n);
static void delete_case4(rbtree t, node n);
static void delete_case5(rbtree t, node n);
static void delete_case6(rbtree t, node n);
node grandparent(node n) {
assert (n != NULL);
assert (n->parent != NULL); /* Not the root node */
assert (n->parent->parent != NULL); /* Not child of root */
return n->parent->parent;
}
node sibling(node n) {
assert (n != NULL);
assert (n->parent != NULL); /* Root node has no sibling */
if (n == n->parent->left)
return n->parent->right;
else
return n->parent->left;
}
node uncle(node n) {
assert (n != NULL);
assert (n->parent != NULL); /* Root node has no uncle */
assert (n->parent->parent != NULL); /* Children of root have no uncle */
return sibling(n->parent);
}
void verify_properties(rbtree t) {
#ifdef VERIFY_RBTREE
verify_property_1(t->root);
verify_property_2(t->root);
/* Property 3 is implicit */
verify_property_4(t->root);
verify_property_5(t->root);
#endif
}
void verify_property_1(node n) {
assert(node_color(n) == RED || node_color(n) == BLACK);
if (n == NULL) return;
verify_property_1(n->left);
verify_property_1(n->right);
}
/*
void verify_property_2(node root) {
assert(node_color(root) == BLACK);
}
*/
color node_color(node n) {
return n == NULL ? BLACK : n->color;
}
void verify_property_4(node n) {
if (node_color(n) == RED) {
assert (node_color(n->left) == BLACK);
assert (node_color(n->right) == BLACK);
assert (node_color(n->parent) == BLACK);
}
if (n == NULL) return;
verify_property_4(n->left);
verify_property_4(n->right);
}
/*
void verify_property_5(node root) {
int black_count_path = -1;
verify_property_5_helper(root, 0, &black_count_path);
}
*/
void verify_property_5_helper(node n, int black_count, int* path_black_count) {
if (node_color(n) == BLACK) {
black_count++;
}
if (n == NULL) {
if (*path_black_count == -1) {
*path_black_count = black_count;
} else {
assert (black_count == *path_black_count);
}
return;
}
verify_property_5_helper(n->left, black_count, path_black_count);
verify_property_5_helper(n->right, black_count, path_black_count);
}
void rbtree_init(rbtree t, rbtree_compare_func compare) {
t->root = NULL;
t->compare = compare;
verify_properties(t);
}
node lookup_node(rbtree t, void* key) {
node n = t->root;
while (n != NULL) {
int comp_result = t->compare(key, n->key);
if (comp_result == 0) {
return n;
} else if (comp_result < 0) {
n = n->left;
} else {
assert(comp_result > 0);
n = n->right;
}
}
return n;
}
void* rbtree_lookup(rbtree t, void* key) {
node n = lookup_node(t, key);
return n == NULL ? NULL : n->value;
}
void rotate_left(rbtree t, node n) {
node r = n->right;
replace_node(t, n, r);
n->right = r->left;
if (r->left != NULL) {
r->left->parent = n;
}
r->left = n;
n->parent = r;
}
void rotate_right(rbtree t, node n) {
node L = n->left;
replace_node(t, n, L);
n->left = L->right;
if (L->right != NULL) {
L->right->parent = n;
}
L->right = n;
n->parent = L;
}
void replace_node(rbtree t, node oldn, node newn) {
if (oldn->parent == NULL) {
t->root = newn;
} else {
if (oldn == oldn->parent->left)
oldn->parent->left = newn;
else
oldn->parent->right = newn;
}
if (newn != NULL) {
newn->parent = oldn->parent;
}
}
void rbtree_insert(rbtree t, rbtree_node inserted_node) {
inserted_node->color = RED;
inserted_node->left = NULL;
inserted_node->right = NULL;
inserted_node->parent = NULL;
if (t->root == NULL) {
t->root = inserted_node;
} else {
node n = t->root;
while (1) {
int comp_result = t->compare(inserted_node->key, n->key);
if (comp_result == 0) {
n->value = inserted_node->value;
return;
} else if (comp_result < 0) {
if (n->left == NULL) {
n->left = inserted_node;
break;
} else {
n = n->left;
}
} else {
assert (comp_result > 0);
if (n->right == NULL) {
n->right = inserted_node;
break;
} else {
n = n->right;
}
}
}
inserted_node->parent = n;
}
insert_case1(t, inserted_node);
verify_properties(t);
}
void insert_case1(rbtree t, node n) {
if (n->parent == NULL)
n->color = BLACK;
else
insert_case2(t, n);
}
void insert_case2(rbtree t, node n) {
if (node_color(n->parent) == BLACK)
return; /* Tree is still valid */
else
insert_case3(t, n);
}
void insert_case3(rbtree t, node n) {
if (node_color(uncle(n)) == RED) {
n->parent->color = BLACK;
uncle(n)->color = BLACK;
grandparent(n)->color = RED;
insert_case1(t, grandparent(n));
} else {
insert_case4(t, n);
}
}
void insert_case4(rbtree t, node n) {
if (n == n->parent->right && n->parent == grandparent(n)->left) {
rotate_left(t, n->parent);
n = n->left;
} else if (n == n->parent->left && n->parent == grandparent(n)->right) {
rotate_right(t, n->parent);
n = n->right;
}
insert_case5(t, n);
}
void insert_case5(rbtree t, node n) {
n->parent->color = BLACK;
grandparent(n)->color = RED;
if (n == n->parent->left && n->parent == grandparent(n)->left) {
rotate_right(t, grandparent(n));
} else {
assert (n == n->parent->right && n->parent == grandparent(n)->right);
rotate_left(t, grandparent(n));
}
}
rbtree_node rbtree_delete(rbtree t, void* key) {
node child;
node n = lookup_node(t, key);
if (n == NULL) return NULL; /* Key not found, do nothing */
if (n->left != NULL && n->right != NULL) {
/* Copy key/value from predecessor and then delete it instead */
node pred = maximum_node(n->left);
n->key = pred->key;
n->value = pred->value;
n = pred;
}
assert(n->left == NULL || n->right == NULL);
child = n->right == NULL ? n->left : n->right;
if (node_color(n) == BLACK) {
n->color = node_color(child);
delete_case1(t, n);
}
replace_node(t, n, child);
verify_properties(t);
return n;
}
static node maximum_node(node n) {
assert (n != NULL);
while (n->right != NULL) {
n = n->right;
}
return n;
}
void delete_case1(rbtree t, node n) {
if (n->parent == NULL)
return;
else
delete_case2(t, n);
}
void delete_case2(rbtree t, node n) {
if (node_color(sibling(n)) == RED) {
n->parent->color = RED;
sibling(n)->color = BLACK;
if (n == n->parent->left)
rotate_left(t, n->parent);
else
rotate_right(t, n->parent);
}
delete_case3(t, n);
}
void delete_case3(rbtree t, node n) {
if (node_color(n->parent) == BLACK &&
node_color(sibling(n)) == BLACK &&
node_color(sibling(n)->left) == BLACK &&
node_color(sibling(n)->right) == BLACK)
{
sibling(n)->color = RED;
delete_case1(t, n->parent);
}
else
delete_case4(t, n);
}
void delete_case4(rbtree t, node n) {
if (node_color(n->parent) == RED &&
node_color(sibling(n)) == BLACK &&
node_color(sibling(n)->left) == BLACK &&
node_color(sibling(n)->right) == BLACK)
{
sibling(n)->color = RED;
n->parent->color = BLACK;
}
else
delete_case5(t, n);
}
void delete_case5(rbtree t, node n) {
if (n == n->parent->left &&
node_color(sibling(n)) == BLACK &&
node_color(sibling(n)->left) == RED &&
node_color(sibling(n)->right) == BLACK)
{
sibling(n)->color = RED;
sibling(n)->left->color = BLACK;
rotate_right(t, sibling(n));
}
else if (n == n->parent->right &&
node_color(sibling(n)) == BLACK &&
node_color(sibling(n)->right) == RED &&
node_color(sibling(n)->left) == BLACK)
{
sibling(n)->color = RED;
sibling(n)->right->color = BLACK;
rotate_left(t, sibling(n));
}
delete_case6(t, n);
}
void delete_case6(rbtree t, node n) {
sibling(n)->color = node_color(n->parent);
n->parent->color = BLACK;
if (n == n->parent->left) {
assert (node_color(sibling(n)->right) == RED);
sibling(n)->right->color = BLACK;
rotate_left(t, n->parent);
}
else
{
assert (node_color(sibling(n)->left) == RED);
sibling(n)->left->color = BLACK;
rotate_right(t, n->parent);
}
}

54
deps/libebb/rbtree.h

@ -0,0 +1,54 @@
/* Copyright (c) 2008 Derrick Coetzee
* http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
* Small changes by Ryah Dahl
*
* 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 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.
*/
#ifndef _RBTREE_H_
#define _RBTREE_H_
enum rbtree_node_color { RED, BLACK };
typedef int (*rbtree_compare_func)(void* left_key, void* right_key);
typedef struct rbtree_node_t {
struct rbtree_node_t* left; /* private */
struct rbtree_node_t* right; /* private */
struct rbtree_node_t* parent; /* private */
enum rbtree_node_color color; /* private */
void* key; /* public */
void* value; /* public */
} *rbtree_node;
typedef struct rbtree_t {
rbtree_node root; /* private */
rbtree_compare_func compare; /* private */
} *rbtree;
void rbtree_init(rbtree t, rbtree_compare_func);
void* rbtree_lookup(rbtree t, void* key);
void rbtree_insert(rbtree t, rbtree_node);
/* you must free the returned node */
rbtree_node rbtree_delete(rbtree t, void* key);
#endif

60
deps/libebb/test_examples.rb

@ -0,0 +1,60 @@
require 'test/unit'
require 'socket'
REQ = "GET /hello/%d HTTP/1.1\r\n\r\n"
HOST = '0.0.0.0'
PORT = 5000
class TCPSocket
def full_send(string)
written = 0
while(written < string.length)
sent = write(string)
string = string.slice(sent, 10000)
end
written
end
def full_read
response = ""
while chunk = read(10000)
response += chunk
end
response
end
end
class EbbTest < Test::Unit::TestCase
def setup
@socket = TCPSocket.new(HOST, PORT)
end
def test_bad_req
@socket.full_send("hello")
assert_equal "", @socket.full_read
#assert @socket.closed?
end
def test_single
written = 0
req = REQ % 1
@socket.full_send(req)
response = @socket.full_read()
count = 0
response.scan("hello world") { count += 1 }
assert_equal 1, count
end
def test_pipeline
written = 0
req = (REQ % 1) + (REQ % 2) + (REQ % 3) + (REQ % 4)
@socket.full_send(req)
response = @socket.full_read()
count = 0
response.scan("hello world") { count += 1 }
assert_equal 4, count
end
end

108
deps/libebb/test_rbtree.c

@ -0,0 +1,108 @@
/* Copyright (c) 2008 Derrick Coetzee
* http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
*
* 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 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.
*/
#include "rbtree.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h> /* rand(), malloc(), free() */
static int compare_int(void* left, void* right);
static void print_tree(rbtree t);
static void print_tree_helper(rbtree_node n, int indent);
int compare_int(void* leftp, void* rightp) {
int left = (int)leftp;
int right = (int)rightp;
if (left < right)
return -1;
else if (left > right)
return 1;
else {
assert (left == right);
return 0;
}
}
#define INDENT_STEP 4
void print_tree_helper(rbtree_node n, int indent);
void print_tree(rbtree t) {
print_tree_helper(t->root, 0);
puts("");
}
void print_tree_helper(rbtree_node n, int indent) {
int i;
if (n == NULL) {
fputs("<empty tree>", stdout);
return;
}
if (n->right != NULL) {
print_tree_helper(n->right, indent + INDENT_STEP);
}
for(i=0; i<indent; i++)
fputs(" ", stdout);
if (n->color == BLACK)
printf("%d\n", (int)n->key);
else
printf("<%d>\n", (int)n->key);
if (n->left != NULL) {
print_tree_helper(n->left, indent + INDENT_STEP);
}
}
int main() {
int i;
struct rbtree_t t;
rbtree_init(&t, compare_int);
print_tree(&t);
for(i=0; i<5000; i++) {
int x = rand() % 10000;
int y = rand() % 10000;
#ifdef TRACE
print_tree(&t);
printf("Inserting %d -> %d\n\n", x, y);
#endif
rbtree_node node = (rbtree_node) malloc(sizeof(struct rbtree_node_t));
node->key = (void*)x;
node->value = (void*)y;
rbtree_insert(&t, node);
assert(rbtree_lookup(&t, (void*)x) == (void*)y);
}
for(i=0; i<60000; i++) {
int x = rand() % 10000;
#ifdef TRACE
print_tree(&t);
printf("Deleting key %d\n\n", x);
#endif
rbtree_node n = rbtree_delete(&t, (void*)x);
if(n != NULL) {
free(n);
}
}
printf("Okay\n");
return 0;
}

746
deps/libebb/test_request_parser.c

@ -0,0 +1,746 @@
/* unit tests for request parser
* Copyright 2008 ryah dahl, ry@ndahl.us
*
* This software may be distributed under the "MIT" license included in the
* README
*/
#include "ebb_request_parser.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define MAX_HEADERS 500
#define MAX_ELEMENT_SIZE 500
static ebb_request_parser parser;
struct request_data {
const char *raw;
int request_method;
char request_path[MAX_ELEMENT_SIZE];
char request_uri[MAX_ELEMENT_SIZE];
char fragment[MAX_ELEMENT_SIZE];
char query_string[MAX_ELEMENT_SIZE];
char body[MAX_ELEMENT_SIZE];
int num_headers;
char header_fields[MAX_HEADERS][MAX_ELEMENT_SIZE];
char header_values[MAX_HEADERS][MAX_ELEMENT_SIZE];
int should_keep_alive;
ebb_request request;
};
static struct request_data requests[5];
static int num_requests;
const struct request_data curl_get =
{ raw: "GET /test HTTP/1.1\r\n"
"User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
"Host: 0.0.0.0:5000\r\n"
"Accept: */*\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/test"
, request_uri: "/test"
, num_headers: 3
, header_fields: { "User-Agent", "Host", "Accept" }
, header_values: { "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1", "0.0.0.0:5000", "*/*" }
, body: ""
};
const struct request_data firefox_get =
{ raw: "GET /favicon.ico HTTP/1.1\r\n"
"Host: 0.0.0.0:5000\r\n"
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Language: en-us,en;q=0.5\r\n"
"Accept-Encoding: gzip,deflate\r\n"
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
"Keep-Alive: 300\r\n"
"Connection: keep-alive\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/favicon.ico"
, request_uri: "/favicon.ico"
, num_headers: 8
, header_fields:
{ "Host"
, "User-Agent"
, "Accept"
, "Accept-Language"
, "Accept-Encoding"
, "Accept-Charset"
, "Keep-Alive"
, "Connection"
}
, header_values:
{ "0.0.0.0:5000"
, "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0"
, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
, "en-us,en;q=0.5"
, "gzip,deflate"
, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
, "300"
, "keep-alive"
}
, body: ""
};
const struct request_data dumbfuck =
{ raw: "GET /dumbfuck HTTP/1.1\r\n"
"aaaaaaaaaaaaa:++++++++++\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/dumbfuck"
, request_uri: "/dumbfuck"
, num_headers: 1
, header_fields: { "aaaaaaaaaaaaa" }
, header_values: { "++++++++++" }
, body: ""
};
const struct request_data fragment_in_uri =
{ raw: "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: "page=1"
, fragment: "posts-17408"
, request_path: "/forums/1/topics/2375"
/* XXX request uri does not include fragment? */
, request_uri: "/forums/1/topics/2375?page=1"
, num_headers: 0
, body: ""
};
// get - no headers - no body
const struct request_data get_no_headers_no_body =
{ raw: "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/get_no_headers_no_body/world"
, request_uri: "/get_no_headers_no_body/world"
, num_headers: 0
, body: ""
};
// get - one header - no body
const struct request_data get_one_header_no_body =
{ raw: "GET /get_one_header_no_body HTTP/1.1\r\n"
"Accept: */*\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/get_one_header_no_body"
, request_uri: "/get_one_header_no_body"
, num_headers: 1
, header_fields: { "Accept" }
, header_values: { "*/*" }
, body: ""
};
// get - no headers - body "HELLO"
const struct request_data get_funky_content_length_body_hello =
{ raw: "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
"conTENT-Length: 5\r\n"
"\r\n"
"HELLO"
, should_keep_alive: FALSE
, request_method: EBB_GET
, query_string: ""
, fragment: ""
, request_path: "/get_funky_content_length_body_hello"
, request_uri: "/get_funky_content_length_body_hello"
, num_headers: 1
, header_fields: { "conTENT-Length" }
, header_values: { "5" }
, body: "HELLO"
};
// post - one header - body "World"
const struct request_data post_identity_body_world =
{ raw: "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
"Accept: */*\r\n"
"Transfer-Encoding: identity\r\n"
"Content-Length: 5\r\n"
"\r\n"
"World"
, should_keep_alive: TRUE
, request_method: EBB_POST
, query_string: "q=search"
, fragment: "hey"
, request_path: "/post_identity_body_world"
, request_uri: "/post_identity_body_world?q=search"
, num_headers: 3
, header_fields: { "Accept", "Transfer-Encoding", "Content-Length" }
, header_values: { "*/*", "identity", "5" }
, body: "World"
};
// post - no headers - chunked body "all your base are belong to us"
const struct request_data post_chunked_all_your_base =
{ raw: "POST /post_chunked_all_your_base HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1e\r\nall your base are belong to us\r\n"
"0\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_POST
, query_string: ""
, fragment: ""
, request_path: "/post_chunked_all_your_base"
, request_uri: "/post_chunked_all_your_base"
, num_headers: 1
, header_fields: { "Transfer-Encoding" }
, header_values: { "chunked" }
, body: "all your base are belong to us"
};
// two chunks ; triple zero ending
const struct request_data two_chunks_mult_zero_end =
{ raw: "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\nhello\r\n"
"6\r\n world\r\n"
"000\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_POST
, query_string: ""
, fragment: ""
, request_path: "/two_chunks_mult_zero_end"
, request_uri: "/two_chunks_mult_zero_end"
, num_headers: 1
, header_fields: { "Transfer-Encoding" }
, header_values: { "chunked" }
, body: "hello world"
};
// chunked with trailing headers. blech.
const struct request_data chunked_w_trailing_headers =
{ raw: "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\nhello\r\n"
"6\r\n world\r\n"
"0\r\n"
"Vary: *\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_POST
, query_string: ""
, fragment: ""
, request_path: "/chunked_w_trailing_headers"
, request_uri: "/chunked_w_trailing_headers"
, num_headers: 1
, header_fields: { "Transfer-Encoding" }
, header_values: { "chunked" }
, body: "hello world"
};
// with bullshit after the length
const struct request_data chunked_w_bullshit_after_length =
{ raw: "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
"6; blahblah; blah\r\n world\r\n"
"0\r\n"
"\r\n"
, should_keep_alive: TRUE
, request_method: EBB_POST
, query_string: ""
, fragment: ""
, request_path: "/chunked_w_bullshit_after_length"
, request_uri: "/chunked_w_bullshit_after_length"
, num_headers: 1
, header_fields: { "Transfer-Encoding" }
, header_values: { "chunked" }
, body: "hello world"
};
const struct request_data *fixtures[] =
{ &curl_get
, &firefox_get
, &dumbfuck
, &fragment_in_uri
, &get_no_headers_no_body
, &get_one_header_no_body
, &get_funky_content_length_body_hello
, &post_identity_body_world
, &post_chunked_all_your_base
, &two_chunks_mult_zero_end
, &chunked_w_trailing_headers
, &chunked_w_bullshit_after_length
, NULL
};
int request_data_eq
( struct request_data *r1
, const struct request_data *r2
)
{
if(ebb_request_should_keep_alive(&r1->request) != r2->should_keep_alive) {
printf("requests disagree on keep-alive");
return FALSE;
}
if(0 != strcmp(r1->body, r2->body)) {
printf("body '%s' != '%s'\n", r1->body, r2->body);
return FALSE;
}
if(0 != strcmp(r1->fragment, r2->fragment)) {
printf("fragment '%s' != '%s'\n", r1->fragment, r2->fragment);
return FALSE;
}
if(0 != strcmp(r1->query_string, r2->query_string)) {
printf("query_string '%s' != '%s'\n", r1->query_string, r2->query_string);
return FALSE;
}
if(r1->request.method != r2->request_method) {
printf("request_method '%d' != '%d'\n", r1->request.method, r2->request_method);
return FALSE;
}
if(0 != strcmp(r1->request_path, r2->request_path)) {
printf("request_path '%s' != '%s'\n", r1->request_path, r2->request_path);
return FALSE;
}
if(0 != strcmp(r1->request_uri, r2->request_uri)) {
printf("request_uri '%s' != '%s'\n", r1->request_uri, r2->request_uri);
return FALSE;
}
if(r1->num_headers != r2->num_headers) {
printf("num_headers '%d' != '%d'\n", r1->num_headers, r2->num_headers);
return FALSE;
}
int i;
for(i = 0; i < r1->num_headers; i++) {
if(0 != strcmp(r1->header_fields[i], r2->header_fields[i])) {
printf("header field '%s' != '%s'\n", r1->header_fields[i], r2->header_fields[i]);
return FALSE;
}
if(0 != strcmp(r1->header_values[i], r2->header_values[i])) {
printf("header field '%s' != '%s'\n", r1->header_values[i], r2->header_values[i]);
return FALSE;
}
}
return TRUE;
}
int request_eq
( int index
, const struct request_data *expected
)
{
return request_data_eq(&requests[index], expected);
}
void request_complete(ebb_request *info)
{
num_requests++;
}
void request_path_cb(ebb_request *request, const char *p, size_t len)
{
strncat(requests[num_requests].request_path, p, len);
}
void request_uri_cb(ebb_request *request, const char *p, size_t len)
{
strncat(requests[num_requests].request_uri, p, len);
}
void query_string_cb(ebb_request *request, const char *p, size_t len)
{
strncat(requests[num_requests].query_string, p, len);
}
void fragment_cb(ebb_request *request, const char *p, size_t len)
{
strncat(requests[num_requests].fragment, p, len);
}
void header_field_cb(ebb_request *request, const char *p, size_t len, int header_index)
{
strncat(requests[num_requests].header_fields[header_index], p, len);
}
void header_value_cb(ebb_request *request, const char *p, size_t len, int header_index)
{
strncat(requests[num_requests].header_values[header_index], p, len);
requests[num_requests].num_headers = header_index + 1;
}
void body_handler(ebb_request *request, const char *p, size_t len)
{
strncat(requests[num_requests].body, p, len);
// printf("body_handler: '%s'\n", requests[num_requests].body);
}
ebb_request* new_request ()
{
requests[num_requests].num_headers = 0;
requests[num_requests].request_method = -1;
requests[num_requests].request_path[0] = 0;
requests[num_requests].request_uri[0] = 0;
requests[num_requests].fragment[0] = 0;
requests[num_requests].query_string[0] = 0;
requests[num_requests].body[0] = 0;
int i;
for(i = 0; i < MAX_HEADERS; i++) {
requests[num_requests].header_fields[i][0] = 0;
requests[num_requests].header_values[i][0] = 0;
}
ebb_request *r = &requests[num_requests].request;
ebb_request_init(r);
r->on_complete = request_complete;
r->on_header_field = header_field_cb;
r->on_header_value = header_value_cb;
r->on_path = request_path_cb;
r->on_uri = request_uri_cb;
r->on_fragment = fragment_cb;
r->on_query_string = query_string_cb;
r->on_body = body_handler;
r->on_headers_complete = NULL;
r->data = &requests[num_requests];
// printf("new request %d\n", num_requests);
return r;
}
void parser_init()
{
num_requests = 0;
ebb_request_parser_init(&parser);
parser.new_request = new_request;
}
int test_request
( const struct request_data *request_data
)
{
size_t traversed = 0;
parser_init();
traversed = ebb_request_parser_execute( &parser
, request_data->raw
, strlen(request_data->raw)
);
if( ebb_request_parser_has_error(&parser) )
return FALSE;
if(! ebb_request_parser_is_finished(&parser) )
return FALSE;
if(num_requests != 1)
return FALSE;
return request_eq(0, request_data);
}
int test_error
( const char *buf
)
{
size_t traversed = 0;
parser_init();
traversed = ebb_request_parser_execute(&parser, buf, strlen(buf));
return ebb_request_parser_has_error(&parser);
}
int test_multiple3
( const struct request_data *r1
, const struct request_data *r2
, const struct request_data *r3
)
{
char total[80*1024] = "\0";
strcat(total, r1->raw);
strcat(total, r2->raw);
strcat(total, r3->raw);
size_t traversed = 0;
parser_init();
traversed = ebb_request_parser_execute(&parser, total, strlen(total));
if( ebb_request_parser_has_error(&parser) )
return FALSE;
if(! ebb_request_parser_is_finished(&parser) )
return FALSE;
if(num_requests != 3)
return FALSE;
if(!request_eq(0, r1)){
printf("request 1 error.\n");
return FALSE;
}
if(!request_eq(1, r2)){
printf("request 2 error.\n");
return FALSE;
}
if(!request_eq(2, r3)){
printf("request 3 error.\n");
return FALSE;
}
return TRUE;
}
/**
* SCAN through every possible breaking to make sure the
* parser can handle getting the content in any chunks that
* might come from the socket
*/
int test_scan2
( const struct request_data *r1
, const struct request_data *r2
, const struct request_data *r3
)
{
char total[80*1024] = "\0";
char buf1[80*1024] = "\0";
char buf2[80*1024] = "\0";
strcat(total, r1->raw);
strcat(total, r2->raw);
strcat(total, r3->raw);
int total_len = strlen(total);
//printf("total_len = %d\n", total_len);
int i;
for(i = 1; i < total_len - 1; i ++ ) {
parser_init();
int buf1_len = i;
strncpy(buf1, total, buf1_len);
buf1[buf1_len] = 0;
int buf2_len = total_len - i;
strncpy(buf2, total+i, buf2_len);
buf2[buf2_len] = 0;
ebb_request_parser_execute(&parser, buf1, buf1_len);
if( ebb_request_parser_has_error(&parser) ) {
return FALSE;
}
/*
if(ebb_request_parser_is_finished(&parser))
return FALSE;
*/
ebb_request_parser_execute(&parser, buf2, buf2_len);
if( ebb_request_parser_has_error(&parser))
return FALSE;
if(!ebb_request_parser_is_finished(&parser))
return FALSE;
if(3 != num_requests) {
printf("scan error: got %d requests in iteration %d\n", num_requests, i);
return FALSE;
}
if(!request_eq(0, r1)) {
printf("not maching r1\n");
return FALSE;
}
if(!request_eq(1, r2)) {
printf("not maching r2\n");
return FALSE;
}
if(!request_eq(2, r3)) {
printf("not maching r3\n");
return FALSE;
}
}
return TRUE;
}
int test_scan3
( const struct request_data *r1
, const struct request_data *r2
, const struct request_data *r3
)
{
char total[80*1024] = "\0";
char buf1[80*1024] = "\0";
char buf2[80*1024] = "\0";
char buf3[80*1024] = "\0";
strcat(total, r1->raw);
strcat(total, r2->raw);
strcat(total, r3->raw);
int total_len = strlen(total);
//printf("total_len = %d\n", total_len);
int i,j;
for(j = 2; j < total_len - 1; j ++ ) {
for(i = 1; i < j; i ++ ) {
parser_init();
int buf1_len = i;
strncpy(buf1, total, buf1_len);
buf1[buf1_len] = 0;
int buf2_len = j - i;
strncpy(buf2, total+i, buf2_len);
buf2[buf2_len] = 0;
int buf3_len = total_len - j;
strncpy(buf3, total+j, buf3_len);
buf3[buf3_len] = 0;
/*
printf("buf1: %s - %d\n", buf1, buf1_len);
printf("buf2: %s - %d \n", buf2, buf2_len );
printf("buf3: %s - %d\n\n", buf3, buf3_len);
*/
ebb_request_parser_execute(&parser, buf1, buf1_len);
if( ebb_request_parser_has_error(&parser) ) {
return FALSE;
}
ebb_request_parser_execute(&parser, buf2, buf2_len);
if( ebb_request_parser_has_error(&parser) ) {
return FALSE;
}
ebb_request_parser_execute(&parser, buf3, buf3_len);
if( ebb_request_parser_has_error(&parser))
return FALSE;
if(!ebb_request_parser_is_finished(&parser))
return FALSE;
if(3 != num_requests) {
printf("scan error: only got %d requests in iteration %d\n", num_requests, i);
return FALSE;
}
if(!request_eq(0, r1)) {
printf("not maching r1\n");
return FALSE;
}
if(!request_eq(1, r2)) {
printf("not maching r2\n");
return FALSE;
}
if(!request_eq(2, r3)) {
printf("not maching r3\n");
return FALSE;
}
}
}
return TRUE;
}
int main()
{
assert(test_error("hello world"));
assert(test_error("GET / HTP/1.1\r\n\r\n"));
assert(test_request(&curl_get));
assert(test_request(&firefox_get));
// Zed's header tests
assert(test_request(&dumbfuck));
const char *dumbfuck2 = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n";
assert(test_error(dumbfuck2));
assert(test_request(&fragment_in_uri));
/* TODO sending junk and large headers gets rejected */
/* check to make sure our predefined requests are okay */
assert(test_request(&get_no_headers_no_body));
assert(test_request(&get_one_header_no_body));
assert(test_request(&get_no_headers_no_body));
// no content-length
const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\nAccept: */*\r\nHELLO\r\n";
assert(test_error(bad_get_no_headers_no_body)); // error if there is a body without content length
assert(test_request(&get_funky_content_length_body_hello));
assert(test_request(&post_identity_body_world));
assert(test_request(&post_chunked_all_your_base));
assert(test_request(&two_chunks_mult_zero_end));
assert(test_request(&chunked_w_trailing_headers));
assert(test_request(&chunked_w_bullshit_after_length));
assert(1 == requests[0].request.version_major);
assert(1 == requests[0].request.version_minor);
// three requests - no bodies
assert( test_multiple3( &get_no_headers_no_body
, &get_one_header_no_body
, &get_no_headers_no_body
));
// three requests - one body
assert( test_multiple3(&get_no_headers_no_body, &get_funky_content_length_body_hello, &get_no_headers_no_body));
// three requests with bodies -- last is chunked
assert( test_multiple3(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base));
// three chunked requests
assert( test_multiple3(&two_chunks_mult_zero_end, &post_chunked_all_your_base, &chunked_w_trailing_headers));
assert(test_scan2(&get_no_headers_no_body, &get_one_header_no_body, &get_no_headers_no_body));
assert(test_scan2(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base));
assert(test_scan2(&two_chunks_mult_zero_end, &chunked_w_trailing_headers, &chunked_w_bullshit_after_length));
assert(test_scan3(&get_no_headers_no_body, &get_one_header_no_body, &get_no_headers_no_body));
assert(test_scan3(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base));
assert(test_scan3(&two_chunks_mult_zero_end, &chunked_w_trailing_headers, &chunked_w_bullshit_after_length));
printf("okay\n");
return 0;
}

23
deps/liboi/LICENSE

@ -0,0 +1,23 @@
liboi is Copyright (C) 2009 Ryan Dahl.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

36
deps/liboi/README

@ -0,0 +1,36 @@
liboi is a C library for doing evented I/O. It is intended for building
efficent internet programs.
liboi is released under the X11 license.
= Feature Summary
* The library has a minimalist design
- Does not make internal allocations
- Does not wrap functionality of GnuTLS or libev. The user must use those
libraries in conjuction with liboi.
* Supports both server and client sockets.
* Supports evented file I/O emulation through a thread pool.
* SSL support
* Sendfile (file to socket) with emulation on platforms that do not support
it.
= Building
1 Edit config.mk. You almost certainly will need to set the EVDIR and
GNUTLSDIR variables.
2 Run "make"
= Documentation
1 make doc
2 man ./oi.3
= Website
http://github.com/ry/liboi
= Author
Ryan Dahl (ry@tinyclouds.org)

33
deps/liboi/config.mk

@ -0,0 +1,33 @@
# Define EVDIR=/foo/bar if your libev header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
EVDIR=$(HOME)/local/libev
# Define GNUTLSDIR=/foo/bar if your gnutls header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
#GNUTLSDIR=/usr
#
#
# Define NO_PREAD if you have a problem with pread() system call (e.g.
# cygwin.dll before v1.5.22).
#
#
# Define NO_SENDFILE if you have a problem with the sendfile() system call
#
#
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
# CFLAGS and LDFLAGS are for the users to override from the command line.
CFLAGS = -g
LDFLAGS =
PREFIX = $(HOME)/local/liboi
CC = gcc
AR = ar
RM = rm -f
RANLIB = ranlib

8
deps/liboi/oi.h

@ -0,0 +1,8 @@
#ifndef oi_h
#define oi_h
#include <oi_socket.h>
#include <oi_async.h>
#include <oi_file.h>
#endif

278
deps/liboi/oi.pod

@ -0,0 +1,278 @@
=head1 NAME
liboi - a C library for doing evented I/O.
=head1 SYNOPSIS
#include <oi.h>
=head1 DESCRIPTION
liboi is an object oriented library for doing evented socket and file I/O.
The API is mostly about registering callbacks to be executed on certain
events.
Because most systems do not support asynchornous file I/O, the behavior is
emulated with an internal thread pool. The thread pool is accessed with the
C<oi_async> and C<oi_task> objects. Typically one will not need to use these
directly as C<oi_file> wraps that functionality.
=head2 CONVENTIONS
liboi's goal is to be very simple layer above the POSIX API. To that end it
avoids internal allocations as much as possible. Unless otherwise noted you
should assume all pointers passed into liboi will remain your responsibility
to maintain. That means you should not free the data passed into liboi
until the object in question has completed.
C<oi_socket> and C<oi_file> objects must be attached to an event loop. This
is completed with the C<*_attach> and C<*_detach> methods. When an object
is detached, other methods can be called - just the loop will not churn out
callbacks.
Both C<oi_socket> and C<oi_file> contain a number of callback pointers.
These are to be set manually after calling their initalization functions.
All classes include a C<void *data> member which is left for you to use.
=head1 ERROR HANDLING
=head1 Sockets
The C<oi_socket> structure represents a socket.
The callbacks inside C<oi_socket> are
void (*on_connect) (oi_socket *);
void (*on_read) (oi_socket *, const void *buf, size_t count);
void (*on_drain) (oi_socket *);
void (*on_error) (oi_socket *, struct oi_error e);
void (*on_close) (oi_socket *);
void (*on_timeout) (oi_socket *);
A the memory for a socket is released when the C<on_close()> callback is
made. That is, the user may free the memory for the socket with-in the
C<on_close()> callback.
=over 4
=item void oi_socket_init (oi_socket *, float timeout);
Initialize a socket. C<timeout> is the number of seconds of inactivity that
is allowed before the socket closes. The timeout only starts once the
socket is attached to a loop and open.
A C<timeout> argument of 0.0 signals that no timeout should be used.
After calling this function, register your callbacks manually. Thus your
code will probably look like this
oi_socket socket;
oi_socket_init(&socket, 60.0);
socket.on_connect = my_on_connect;
socket.on_read = my_on_read;
/* etc */
=item int oi_socket_connect (oi_socket *, struct addrinfo *addrinfo);
Open a client connect to the specified address. When the connection is made
C<socket.on_connect> will be called.
Here is an example of filling in C<addrinfo> for a local TCP connection on
port 5555:
struct addrinfo *servinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
r = getaddrinfo(NULL, "5555", &hints, &servinfo);
assert(r == 0);
oi_socket_connect(socket, servinfo);
=item void oi_socket_attach (oi_socket *, struct ev_loop *loop);
A socket must be attached to a loop in order before any callbacks will be
made.
=item void oi_socket_detach (oi_socket *);
Detaching a socket will not close the connection.
=item void oi_socket_read_start (oi_socket *);
This will make the socket start receiving data. When data is received the
C<socket.on_read> callback is made. The maximum amount of data that can be
receive at a time is controlled by C<socket.chunksize>.
The buffer returned by C<socket.on_read> is statically allocated exists only
for the length of the callback. That means if you need to save any of the
data coming down the line, you must copy it to a new buffer.
Ideally you will have a parser attached to the C<on_read> callback which can
be interrupted at any time.
C<socket.chunksize> can be changed at any time.
=item void oi_socket_read_stop (oi_socket *);
Stops receiving data. You may receive spurious C<on_read> attempts even
though the socket reading is stopped - be prepared to handle them.
=item void oi_socket_reset_timeout (oi_socket *);
Reset the timeout to allow the socket to exist for another few seconds
(however long you specified in the initialization function).
=item void oi_socket_write (oi_socket *socket, oi_buf *buf);
Write the I<buf> to the socket. Each socket has a queue of C<oi_buf> objects
to be written - this appends the specified buffer to the end of that queue.
You will be notified when the queue is empty with the C<socket.on_drain()>
callback. When the socket has written the buffer C<buf.release()> will be
called. The release callback does not imply that the buffer was successfully
written.
=item void oi_socket_write_simple (oi_socket *, const char *str, size_t len);
Sometimes you are just hacking around and need to quickly write a string to
the socket. This convenience function allocates an C<oi_buf> object, and
C<strdup>s the given string. The allocated buffer will be freed by liboi
internally.
Most production most applications will use their own memory pool and will
not need this function.
=item void oi_socket_write_eof (oi_socket *);
This closes the write end of the socket. Further writes are not allowed
after this.
=item void oi_socket_close (oi_socket *);
Attempts to close the socket.
If the socket is secure, an SSL bye message will be sent.
SSL recommends that you wait for a bye response from the peer however this
tends to be overkill for most people. By default liboi will not wait for
peer to send a matching bye message. If you require this then set
C<socket.wait_for_secure_hangup> to 1.
When the close is complete C<on_close()> is made. The C<on_close()>
callback is not made until the program returns to the event loop. This is
because C<on_close()> may free the socket memory and if C<on_close()> was
called from C<oi_socket_close()>, then the socket object might unexpectedly
be gone. To summarize: C<oi_socket_close()> does not call C<on_close()> and
the socket memory is still accessable immediately after making calling
C<oi_socket_close()>.
=item void oi_socket_set_secure_session (oi_socket *, gnutls_session_t);
This make a socket use SSL. You must create the GnuTLS session yourself and
assign its credentials.
=back
=head1 Servers
A server simply listens on an address for new connections. The connections
come in the form of C<oi_socket> objects. The key is to give a
C<server.on_connection()> callback which returns an initialized C<oi_socket>.
The callback looks like this
oi_socket* (*on_connection) (oi_server *, struct sockaddr *remote_addr, socklen_t remove_addr_len);
Returning NULL from C<on_connection()> will reject the connection.
=over 4
=item void oi_server_init (oi_server *, int backlog);
Initializes a server object. C<backlog> is the argument given
internally to C<listen()>. Set the C<server.on_connection()> callback
after calling this.
=item int oi_server_listen (oi_server *, struct addrinfo *addrinfo);
Listens on the specified address. The server will not accept connections
until it is attached to a loop, however.
=item void oi_server_attach (oi_server *, struct ev_loop *loop);
Attaches a server to a loop.
=item void oi_server_detach (oi_server *);
Detaches a server to a loop. Does not close the server.
=item void oi_server_close (oi_server *);
Stops the server from listening.
=back
=head1 Files
Files internally use a thread pool to operate without blocking.
The thread pool is started once a file is attached and it continues until
program termination.
The following callbacks are used inside of the file object
void (*on_open) (oi_file *);
void (*on_read) (oi_file *, size_t count);
void (*on_drain) (oi_file *);
void (*on_error) (oi_file *, struct oi_error);
void (*on_close) (oi_file *);
=over 4
=item int oi_file_init (oi_file *);
Initializes a file object.
=item void oi_file_attach (oi_file *, struct ev_loop *);
Attaches a file object to a loop. If the thread pool has not been started,
then it is started at this call.
=item void oi_file_detach (oi_file *);
Detaches a file object from the loop.
=item int oi_file_open_path (oi_file *, const char *path, int flags, mode_t mode);
Opens a file specified by the path. The C<flag> and C<mode> arguments are
the same as used by L<open.2>. The C<file.on_open> callback is triggered
when the file is opened. Returns 0 on success. Returns -1 if the given file
is already open.
WARNING: path argument must be valid until C<oi_file> object is closed and
the C<file.on_close()> callback is made. I.E., liboi does not strdup the path
pointer.
=item int oi_file_open_stdin (oi_file *);
=item int oi_file_open_stdout (oi_file *);
=item int oi_file_open_stderr (oi_file *);
=item void oi_file_read_start (oi_file *, void *buffer, size_t bufsize);
=item void oi_file_read_stop (oi_file *);
=item int oi_file_write (oi_file *, oi_buf *to_write);
=item int oi_file_write_simple (oi_file *, const char *, size_t);
=item int oi_file_send (oi_file *source, oi_socket *destination, off_t offset, size_t length);
=item void oi_file_close (oi_file *);
=over 4
=back
=head1 AUTHOR
Ryan Dahl <ry@tinyclouds.org>

486
deps/liboi/oi_async.c

@ -0,0 +1,486 @@
#include <stdlib.h> /* malloc() */
#include <stdio.h> /* perror() */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h> /* read(), write() */
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>
#if HAVE_SENDFILE
# if __linux
# include <sys/sendfile.h>
# elif __freebsd
# include <sys/socket.h>
# include <sys/uio.h>
# elif __hpux
# include <sys/socket.h>
# elif __solaris /* not yet */
# include <sys/sendfile.h>
# else
# error sendfile support requested but not available
# endif
#endif
#include <ev.h>
#include <oi.h>
#define NWORKERS 4
/* TODO make adjustable
* once it is fix sleeping_tasks
*/
static int active_watchers = 0;
static int active_workers = 0;
static int readiness_pipe[2] = {-1, -1};
static oi_queue waiting_tasks;
static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t finished_lock = PTHREAD_MUTEX_INITIALIZER;
struct worker {
oi_task *task;
pthread_t thread;
pthread_attr_t thread_attr;
};
/* Sendfile and pread emulation come from Marc Lehmann's libeio and are
* Copyright (C)2007,2008 Marc Alexander Lehmann.
* Many ideas of oi_async.* are taken from libeio and in fact, I plan to
* use libeio once it becomes usable for me. (The problem is issuing tasks
* from multiple threads.)
*/
#if !HAVE_PREADWRITE
/*
* make our pread/pwrite emulation safe against themselves, but not against
* normal read/write by using a mutex. slows down execution a lot,
* but that's your problem, not mine.
*/
static pthread_mutex_t preadwritelock = PTHREAD_MUTEX_INITIALIZER;
#endif
#if !HAVE_PREADWRITE
# undef pread
# undef pwrite
# define pread eio__pread
# define pwrite eio__pwrite
static ssize_t
eio__pread (int fd, void *buf, size_t count, off_t offset)
{
ssize_t res;
off_t ooffset;
pthread_mutex_lock(&preadwritelock);
ooffset = lseek (fd, 0, SEEK_CUR);
lseek (fd, offset, SEEK_SET);
res = read (fd, buf, count);
lseek (fd, ooffset, SEEK_SET);
pthread_mutex_unlock(&preadwritelock);
return res;
}
static ssize_t
eio__pwrite (int fd, void *buf, size_t count, off_t offset)
{
ssize_t res;
off_t ooffset;
pthread_mutex_lock(&preadwritelock);
ooffset = lseek (fd, 0, SEEK_CUR);
lseek (fd, offset, SEEK_SET);
res = write (fd, buf, count);
lseek (fd, offset, SEEK_SET);
pthread_mutex_unlock(&preadwritelock);
return res;
}
#endif
/* sendfile always needs emulation */
static ssize_t
eio__sendfile (int ofd, int ifd, off_t offset, size_t count)
{
ssize_t res;
if (!count)
return 0;
#if HAVE_SENDFILE
# if __linux
res = sendfile (ofd, ifd, &offset, count);
# elif __freebsd
/*
* Of course, the freebsd sendfile is a dire hack with no thoughts
* wasted on making it similar to other I/O functions.
*/
{
off_t sbytes;
res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0);
if (res < 0 && sbytes)
/* maybe only on EAGAIN: as usual, the manpage leaves you guessing */
res = sbytes;
}
# elif __hpux
res = sendfile (ofd, ifd, offset, count, 0, 0);
# elif __solaris
{
struct sendfilevec vec;
size_t sbytes;
vec.sfv_fd = ifd;
vec.sfv_flag = 0;
vec.sfv_off = offset;
vec.sfv_len = count;
res = sendfilev (ofd, &vec, 1, &sbytes);
if (res < 0 && sbytes)
res = sbytes;
}
# endif
#else
res = -1;
errno = ENOSYS;
#endif
if (res < 0
&& (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK
#if __solaris
|| errno == EAFNOSUPPORT || errno == EPROTOTYPE
#endif
)
)
{
/* emulate sendfile. this is a major pain in the ass */
/* buffer size for various temporary buffers */
#define EIO_BUFSIZE 65536
char *eio_buf = malloc (EIO_BUFSIZE);
errno = ENOMEM;
if (!eio_buf)
return -1;
res = 0;
while (count) {
ssize_t cnt;
cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset);
if (cnt <= 0) {
if (cnt && !res) res = -1;
break;
}
cnt = write (ofd, eio_buf, cnt);
if (cnt <= 0) {
if (cnt && !res) res = -1;
break;
}
offset += cnt;
res += cnt;
count -= cnt;
}
free(eio_buf);
}
return res;
}
static oi_task*
queue_shift(pthread_mutex_t *lock, oi_queue *queue)
{
oi_queue *last = NULL;
pthread_mutex_lock(lock);
if(!oi_queue_empty(queue)) {
last = oi_queue_last(queue);
oi_queue_remove(last);
}
pthread_mutex_unlock(lock);
if(last == NULL)
return NULL;
return oi_queue_data(last, oi_task, queue);
}
#define P1(name,a) { \
t->params.name.result = name( t->params.name.a ); \
break; \
}
#define P2(name,a,b) { \
t->params.name.result = name( t->params.name.a \
, t->params.name.b \
); \
break; \
}
#define P3(name,a,b,c) { \
t->params.name.result = name( t->params.name.a \
, t->params.name.b \
, t->params.name.c \
); \
break; \
}
#define P4(name,a,b,c,d) { \
t->params.name.result = name( t->params.name.a \
, t->params.name.b \
, t->params.name.c \
, t->params.name.d \
); \
break; \
}
static void
execute_task(oi_task *t)
{
errno = 0;
switch(t->type) {
case OI_TASK_OPEN: P3(open, pathname, flags, mode);
case OI_TASK_READ: P3(read, fd, buf, count);
case OI_TASK_WRITE: P3(write, fd, buf, count);
case OI_TASK_CLOSE: P1(close, fd);
case OI_TASK_SLEEP: P1(sleep, seconds);
case OI_TASK_SENDFILE: P4(eio__sendfile, out_fd, in_fd, offset, count);
case OI_TASK_GETADDRINFO: P4(getaddrinfo, nodename, servname, hints, res);
case OI_TASK_LSTAT: P2(lstat, path, buf);
default:
assert(0 && "unknown task type");
break;
}
t->errorno = errno;
}
static void
attempt_to_get_a_task(struct worker *worker)
{
char dummy;
assert(readiness_pipe[0] > 0);
int r = read(readiness_pipe[0], &dummy, 1);
if(r == -1 && (errno != EAGAIN || errno != EINTR)) {
perror("read(readiness_pipe[0])");
return;
}
// 1 pop task from queue
assert(worker->task == NULL);
oi_task *task = queue_shift(&queue_lock, &waiting_tasks);
if(task == NULL) return;
worker->task = task;
// 2 run task
execute_task(task);
// 3 notify complition
oi_async *async = task->async;
assert(async != NULL);
pthread_mutex_lock(&finished_lock);
oi_queue_insert_head(&async->finished_tasks, &task->queue);
pthread_mutex_unlock(&finished_lock);
ev_async_send(async->loop, &async->watcher);
worker->task = NULL;
/* attempt to pull another task */
return attempt_to_get_a_task(worker);
}
void *
worker_loop(void *data)
{
int r;
struct worker *worker = data;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(readiness_pipe[0], &readfds);
active_workers++;
assert(active_workers <= NWORKERS);
while(1) {
r = select(1+readiness_pipe[0], &readfds, 0, 0, 0);
if(r == -1) break;
attempt_to_get_a_task(worker);
}
active_workers--;
return NULL;
}
static struct worker*
worker_new()
{
int r;
struct worker *worker = calloc(sizeof(struct worker), 1);
if(worker == NULL ) { return NULL; }
worker->task = NULL;
pthread_attr_setdetachstate(&worker->thread_attr, PTHREAD_CREATE_DETACHED);
r = pthread_create( &worker->thread
, NULL // &worker->thread_attr
, worker_loop
, worker
);
if(r != 0) {
/* TODO: error checking */
perror("pthread_create");
goto error;
}
return worker;
error:
free(worker);
return NULL;
}
static void
start_workers()
{
assert(readiness_pipe[0] == -1);
assert(readiness_pipe[1] == -1);
assert(active_workers == 0);
int r = pipe(readiness_pipe);
if(r < 0) {
perror("pipe()");
assert(0 && "TODO HANDLE ME");
}
/* set the write end non-blocking */
int flags = fcntl(readiness_pipe[1], F_GETFL, 0);
r = fcntl(readiness_pipe[1], F_SETFL, flags | O_NONBLOCK);
if(r < 0) {
assert(0 && "error setting pipe to non-blocking?");
/* TODO error report */
}
oi_queue_init(&waiting_tasks);
int i;
for(i = 0; i < NWORKERS; i++)
worker_new();
}
/*
static void
stop_workers()
{
assert(0 && "TODO implement me");
}
*/
static void
on_completion(struct ev_loop *loop, ev_async *watcher, int revents)
{
oi_async *async = watcher->data;
oi_task *task;
while((task = queue_shift(&finished_lock, &async->finished_tasks))) {
assert(task->active);
task->active = 0;
errno = task->errorno;
# define done_cb(kind) { \
assert(task->params.kind.cb); \
task->params.kind.cb(task, task->params.kind.result); \
break; \
}
switch(task->type) {
case OI_TASK_OPEN: done_cb(open);
case OI_TASK_READ: done_cb(read);
case OI_TASK_WRITE: done_cb(write);
case OI_TASK_CLOSE: done_cb(close);
case OI_TASK_SLEEP: done_cb(sleep);
case OI_TASK_SENDFILE: done_cb(eio__sendfile);
case OI_TASK_GETADDRINFO: done_cb(getaddrinfo);
case OI_TASK_LSTAT: done_cb(lstat);
}
/* the task is possibly freed by callback. do not access it again. */
}
}
void
oi_async_init (oi_async *async)
{
ev_async_init(&async->watcher, on_completion);
oi_queue_init(&async->finished_tasks);
oi_queue_init(&async->new_tasks);
async->watcher.data = async;
}
static void
dispatch_tasks(oi_async *async)
{
while(!oi_queue_empty(&async->new_tasks)) {
oi_queue *last = oi_queue_last(&async->new_tasks);
oi_queue_remove(last);
oi_task *task = oi_queue_data(last, oi_task, queue);
// 1. add task to task queue.
pthread_mutex_lock(&queue_lock);
oi_queue_insert_head(&waiting_tasks, &task->queue);
pthread_mutex_unlock(&queue_lock);
// 2. write byte to pipe
char dummy;
int written = write(readiness_pipe[1], &dummy, 1);
// 3. TODO make sure byte is written
assert(written == 1);
}
}
void
oi_async_attach (struct ev_loop *loop, oi_async *async)
{
if(active_watchers == 0 && active_workers == 0)
start_workers();
active_watchers++;
ev_async_start(loop, &async->watcher);
async->loop = loop;
dispatch_tasks(async);
}
void
oi_async_detach (oi_async *async)
{
if(async->loop == NULL)
return;
ev_async_stop(async->loop, &async->watcher);
async->loop = NULL;
active_watchers--;
if(active_watchers == 0) {
//stop_workers();
}
}
void
oi_async_submit (oi_async *async, oi_task *task)
{
assert(!task->active);
assert(task->async == NULL);
task->async = async;
task->active = 1;
oi_queue_insert_head(&async->new_tasks, &task->queue);
if(ev_is_active(&async->watcher)) {
dispatch_tasks(async);
}
}

218
deps/liboi/oi_async.h

@ -0,0 +1,218 @@
#include <ev.h>
#include <pthread.h>
#include <netdb.h>
#include <oi.h>
#ifndef oi_async_h
#define oi_async_h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct oi_async oi_async;
typedef struct oi_task oi_task;
struct oi_async {
/* private */
ev_async watcher;
struct ev_loop *loop;
oi_queue finished_tasks;
oi_queue new_tasks;
/* public */
void *data;
};
typedef void (*oi_task_int_cb)(oi_task *, int result);
typedef void (*oi_task_uint_cb)(oi_task *, unsigned int result);
typedef void (*oi_task_ssize_cb)(oi_task *, ssize_t result);
struct oi_task {
/* private */
oi_async *async;
oi_queue queue;
int type;
union {
struct {
const char *pathname;
int flags;
mode_t mode;
oi_task_int_cb cb;
int result;
} open;
struct {
int fd;
void *buf;
size_t count;
oi_task_ssize_cb cb;
ssize_t result;
} read;
struct {
int fd;
const void *buf;
size_t count;
oi_task_ssize_cb cb;
ssize_t result;
} write;
struct {
int fd;
oi_task_int_cb cb;
int result;
} close;
struct {
unsigned int seconds;
oi_task_uint_cb cb;
unsigned int result;
} sleep;
struct {
int out_fd;
int in_fd;
off_t offset;
size_t count;
oi_task_ssize_cb cb;
ssize_t result;
} eio__sendfile;
struct {
const char *nodename; /* restrict ? */
const char *servname; /* restrict ? */
struct addrinfo *hints;
struct addrinfo **res; /* restrict ? */
oi_task_int_cb cb;
int result;
} getaddrinfo;
struct {
const char *path;
struct stat *buf;
oi_task_int_cb cb;
int result;
} lstat;
} params;
/* read-only */
volatile unsigned active:1;
int errorno;
/* public */
void *data;
};
void oi_async_init (oi_async *);
void oi_async_attach (struct ev_loop *loop, oi_async *);
void oi_async_detach (oi_async *);
void oi_async_submit (oi_async *, oi_task *);
/* To submit a task for async processing
* (0) allocate memory for your task
* (1) initialize the task with one of the functions below
* (2) optionally set the task->data pointer
* (3) oi_async_submit() the task
*/
enum { OI_TASK_OPEN
, OI_TASK_READ
, OI_TASK_WRITE
, OI_TASK_CLOSE
, OI_TASK_SLEEP
, OI_TASK_SENDFILE
, OI_TASK_GETADDRINFO
, OI_TASK_LSTAT
};
#define oi_task_init_common(task, _type) do {\
(task)->active = 0;\
(task)->async = NULL;\
(task)->type = _type;\
} while(0)
static inline void
oi_task_init_open(oi_task *t, oi_task_int_cb cb, const char *pathname, int flags, mode_t mode)
{
oi_task_init_common(t, OI_TASK_OPEN);
t->params.open.cb = cb;
t->params.open.pathname = pathname;
t->params.open.flags = flags;
t->params.open.mode = mode;
}
static inline void
oi_task_init_read(oi_task *t, oi_task_ssize_cb cb, int fd, void *buf, size_t count)
{
oi_task_init_common(t, OI_TASK_READ);
t->params.read.cb = cb;
t->params.read.fd = fd;
t->params.read.buf = buf;
t->params.read.count = count;
}
static inline void
oi_task_init_write(oi_task *t, oi_task_ssize_cb cb, int fd, const void *buf, size_t count)
{
oi_task_init_common(t, OI_TASK_WRITE);
t->params.write.cb = cb;
t->params.write.fd = fd;
t->params.write.buf = buf;
t->params.write.count = count;
}
static inline void
oi_task_init_close(oi_task *t, oi_task_int_cb cb, int fd)
{
oi_task_init_common(t, OI_TASK_CLOSE);
t->params.close.cb = cb;
t->params.close.fd = fd;
}
static inline void
oi_task_init_sleep(oi_task *t, oi_task_uint_cb cb, unsigned int seconds)
{
oi_task_init_common(t, OI_TASK_SLEEP);
t->params.sleep.cb = cb;
t->params.sleep.seconds = seconds;
}
static inline void
oi_task_init_sendfile(oi_task *t, oi_task_ssize_cb cb, int out_fd, int in_fd, off_t offset, size_t count)
{
oi_task_init_common(t, OI_TASK_SENDFILE);
t->params.eio__sendfile.cb = cb;
t->params.eio__sendfile.out_fd = out_fd;
t->params.eio__sendfile.in_fd = in_fd;
t->params.eio__sendfile.offset = offset;
t->params.eio__sendfile.count = count;
}
static inline void
oi_task_init_getaddrinfo(oi_task *t, oi_task_int_cb cb, const char *node,
const char *service, struct addrinfo *hints, struct addrinfo **res)
{
oi_task_init_common(t, OI_TASK_GETADDRINFO);
t->params.getaddrinfo.cb = cb;
t->params.getaddrinfo.nodename = node;
t->params.getaddrinfo.servname = service;
t->params.getaddrinfo.hints = hints;
t->params.getaddrinfo.res = res;
}
static inline void
oi_task_init_lstat(oi_task *t, oi_task_int_cb cb, const char *path, struct stat *buf)
{
oi_task_init_common(t, OI_TASK_LSTAT);
t->params.lstat.cb = cb;
t->params.lstat.path = path;
t->params.lstat.buf = buf;
}
#ifdef __cplusplus
}
#endif
#endif /* oi_async_h */

41
deps/liboi/oi_buf.c

@ -0,0 +1,41 @@
#include <oi_buf.h>
#include <stdlib.h>
#include <string.h>
void oi_buf_destroy
( oi_buf *buf
)
{
free(buf->base);
free(buf);
}
oi_buf * oi_buf_new2
( size_t len
)
{
oi_buf *buf = malloc(sizeof(oi_buf));
if(!buf)
return NULL;
buf->base = malloc(len);
if(!buf->base) {
free(buf);
return NULL;
}
buf->len = len;
buf->release = oi_buf_destroy;
return buf;
}
oi_buf * oi_buf_new
( const char *base
, size_t len
)
{
oi_buf *buf = oi_buf_new2(len);
if(!buf)
return NULL;
memcpy(buf->base, base, len);
return buf;
}

30
deps/liboi/oi_buf.h

@ -0,0 +1,30 @@
#include <oi_queue.h>
#ifndef oi_buf_h
#define oi_buf_h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct oi_buf oi_buf;
struct oi_buf {
/* public */
char *base;
size_t len;
void (*release) (oi_buf *); /* called when oi is done with the object */
void *data;
/* private */
size_t written;
oi_queue queue;
};
oi_buf * oi_buf_new (const char* base, size_t len);
oi_buf * oi_buf_new2 (size_t len);
void oi_buf_destroy (oi_buf *);
#ifdef __cplusplus
}
#endif
#endif // oi_buf_h

25
deps/liboi/oi_error.h

@ -0,0 +1,25 @@
#ifndef oi_error_h
#define oi_error_h
#ifdef __cplusplus
extern "C" {
#endif
struct oi_error {
enum { OI_ERROR_GNUTLS
, OI_ERROR_EV
, OI_ERROR_CLOSE
, OI_ERROR_SHUTDOWN
, OI_ERROR_OPEN
, OI_ERROR_SEND
, OI_ERROR_RECV
, OI_ERROR_WRITE
, OI_ERROR_READ
, OI_ERROR_SENDFILE
} domain;
int code; /* errno */
};
#ifdef __cplusplus
}
#endif
#endif // oi_error_h

391
deps/liboi/oi_file.c

@ -0,0 +1,391 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <ev.h>
#include <oi.h>
#define RELEASE_BUF(buf) if(buf->release) { buf->release(buf); }
#define DRAIN_CB(file) if(file->on_drain) { file->on_drain(file); }
#define RAISE_ERROR(s, _domain, _code) do { \
if(s->on_error) { \
struct oi_error __oi_error; \
__oi_error.domain = _domain; \
__oi_error.code = _code; \
s->on_error(s, __oi_error); \
} \
} while(0) \
/* forwards */
static void dispatch_write_buf (oi_file *file);
static void maybe_do_read (oi_file *file);
static void
after_read(oi_task *task, ssize_t recved)
{
oi_file *file = task->data;
if(recved == -1) {
RAISE_ERROR(file, OI_ERROR_READ, errno);
return;
}
if(recved == 0)
oi_file_read_stop(file);
if(file->on_read)
file->on_read(file, recved);
maybe_do_read(file);
}
static void
maybe_do_read(oi_file *file)
{
if ( file->read_buffer == NULL
|| file->write_buf != NULL
|| file->write_socket != NULL
|| !oi_queue_empty(&file->write_queue)
|| file->io_task.active
) return;
assert(file->fd > 0);
oi_task_init_read ( &file->io_task
, after_read
, file->fd
, file->read_buffer
, file->read_buffer_size
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
}
static void
submit_read (oi_file *file)
{
}
int
oi_file_init (oi_file *file)
{
oi_async_init(&file->async);
file->async.data = file;
oi_queue_init(&file->write_queue);
file->fd = -1;
file->loop = NULL;
file->write_buf = NULL;
file->read_buffer = NULL;
file->on_open = NULL;
file->on_read = NULL;
file->on_drain = NULL;
file->on_error = NULL;
file->on_close = NULL;
return 0;
}
void
oi_file_read_start (oi_file *file, void *buffer, size_t bufsize)
{
file->read_buffer = buffer;
file->read_buffer_size = bufsize;
maybe_do_read(file);
}
void
oi_file_read_stop (oi_file *file)
{
file->read_buffer = NULL;
}
void
oi_api_free_buf_with_heap_base(oi_buf *buf)
{
free(buf->base);
free(buf);
}
static void
after_open(oi_task *task, int result)
{
oi_file *file = task->data;
if(result == -1) {
RAISE_ERROR(file, OI_ERROR_OPEN, errno);
return;
}
file->fd = result;
if(file->on_open) {
file->on_open(file);
}
maybe_do_read(file);
}
int
oi_file_open_path (oi_file *file, const char *path, int flags, mode_t mode)
{
if(file->fd >= 0)
return -1;
oi_task_init_open( &file->io_task
, after_open
, path
, flags
, mode
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
return 0;
}
int
oi_file_open_stdin (oi_file *file)
{
if(file->fd >= 0)
return -1;
file->fd = STDIN_FILENO;
if(file->on_open)
file->on_open(file);
return 0;
}
int
oi_file_open_stdout (oi_file *file)
{
if(file->fd >= 0)
return -1;
file->fd = STDOUT_FILENO;
if(file->on_open)
file->on_open(file);
return 0;
}
int
oi_file_open_stderr (oi_file *file)
{
if(file->fd >= 0)
return -1;
file->fd = STDERR_FILENO;
if(file->on_open)
file->on_open(file);
return 0;
}
void
oi_file_attach (oi_file *file, struct ev_loop *loop)
{
oi_async_attach (loop, &file->async);
file->loop = loop;
}
void
oi_file_detach (oi_file *file)
{
oi_async_detach (&file->async);
file->loop = NULL;
}
static void
after_write(oi_task *task, ssize_t result)
{
oi_file *file = task->data;
if(result == -1) {
RAISE_ERROR(file, OI_ERROR_WRITE, errno);
return;
}
assert(file->write_buf != NULL);
oi_buf *buf = file->write_buf;
buf->written += result;
if(buf->written < buf->len) {
oi_task_init_write ( &file->io_task
, after_write
, file->fd
, buf->base + buf->written
, buf->len - buf->written
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
return;
}
assert(buf->written == buf->len);
RELEASE_BUF(file->write_buf);
file->write_buf = NULL;
if(oi_queue_empty(&file->write_queue)) {
DRAIN_CB(file);
maybe_do_read(file);
} else {
dispatch_write_buf(file);
}
return;
}
static void
dispatch_write_buf(oi_file *file)
{
if(file->write_buf != NULL)
return;
if(oi_queue_empty(&file->write_queue))
return;
oi_queue *q = oi_queue_last(&file->write_queue);
oi_queue_remove(q);
oi_buf *buf = file->write_buf = oi_queue_data(q, oi_buf, queue);
assert(!file->io_task.active);
oi_task_init_write ( &file->io_task
, after_write
, file->fd
, buf->base + buf->written
, buf->len - buf->written
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
}
int
oi_file_write (oi_file *file, oi_buf *buf)
{
if(file->fd < 0)
return -1;
if(file->read_buffer)
return -2;
/* TODO better business check*/
buf->written = 0;
oi_queue_insert_head(&file->write_queue, &buf->queue);
dispatch_write_buf(file);
return 0;
}
// Writes a string to the file.
// NOTE: Allocates memory. Avoid for performance applications.
int
oi_file_write_simple (oi_file *file, const char *str, size_t len)
{
if(file->fd < 0)
return -1;
if(file->read_buffer)
return -2;
/* TODO better business check*/
oi_buf *buf = malloc(sizeof(oi_buf));
buf->base = malloc(len);
memcpy(buf->base, str, len);
buf->len = len;
buf->release = oi_api_free_buf_with_heap_base;
oi_file_write(file, buf);
return 0;
}
static void
clear_write_queue(oi_file *file)
{
while(!oi_queue_empty(&file->write_queue)) {
oi_queue *q = oi_queue_last(&file->write_queue);
oi_queue_remove(q);
oi_buf *buf = oi_queue_data(q, oi_buf, queue);
RELEASE_BUF(buf);
}
}
static void
after_close(oi_task *task, int result)
{
oi_file *file = task->data;
assert(oi_queue_empty(&file->write_queue));
if(result == -1) {
RAISE_ERROR(file, OI_ERROR_CLOSE, errno);
return;
// TODO try to close again?
}
file->fd = -1;
// TODO deinit task_queue, detach thread_pool_result_watcher
if(file->on_close) {
file->on_close(file);
}
return;
}
void
oi_file_close (oi_file *file)
{
assert(file->fd >= 0 && "file not open!");
clear_write_queue(file);
oi_task_init_close ( &file->io_task
, after_close
, file->fd
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
}
static void
after_sendfile(oi_task *task, ssize_t sent)
{
oi_file *file = task->data;
oi_socket *socket = file->write_socket;
assert(socket != NULL);
file->write_socket = NULL;
if(sent == -1) {
RAISE_ERROR(file, OI_ERROR_SENDFILE, errno);
return;
}
if(socket->on_drain) {
socket->on_drain(socket);
}
maybe_do_read(file);
}
int
oi_file_send (oi_file *file, oi_socket *destination, off_t offset, size_t count)
{
if(file->fd < 0)
return -1;
if(file->read_buffer)
return -2;
/* TODO better business check*/
assert(file->write_socket == NULL);
// (1) make sure the write queue on the socket is cleared.
//
// (2)
//
file->write_socket = destination;
oi_task_init_sendfile ( &file->io_task
, after_sendfile
, destination->fd
, file->fd
, offset
, count
);
file->io_task.data = file;
oi_async_submit(&file->async, &file->io_task);
return 0;
}

58
deps/liboi/oi_file.h

@ -0,0 +1,58 @@
#include <oi.h>
#include <ev.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef oi_file_h
#define oi_file_h
typedef struct oi_file oi_file;
int oi_file_init (oi_file *);
void oi_file_attach (oi_file *, struct ev_loop *);
void oi_file_detach (oi_file *);
/* WARNING oi_file_open_path: path argument must be valid until oi_file
* object is closed and the on_close() callback is made. oi does not strdup
* the path pointer. */
int oi_file_open_path (oi_file *, const char *path, int flags, mode_t mode);
int oi_file_open_stdin (oi_file *);
int oi_file_open_stdout (oi_file *);
int oi_file_open_stderr (oi_file *);
void oi_file_read_start (oi_file *, void *buffer, size_t bufsize);
void oi_file_read_stop (oi_file *);
int oi_file_write (oi_file *, oi_buf *to_write);
int oi_file_write_simple (oi_file *, const char *, size_t);
int oi_file_send (oi_file *source, oi_socket *destination, off_t offset, size_t length);
void oi_file_close (oi_file *);
struct oi_file {
/* private */
oi_async async;
oi_task io_task;
struct ev_loop *loop;
oi_queue write_queue;
oi_buf *write_buf; /* TODO this pointer is unnecessary - remove and just look at first element of the queue */
oi_socket *write_socket;
void *read_buffer;
size_t read_buffer_size;
/* read-only */
int fd;
/* public */
void (*on_open) (oi_file *);
void (*on_read) (oi_file *, size_t count);
void (*on_drain) (oi_file *);
void (*on_error) (oi_file *, struct oi_error);
void (*on_close) (oi_file *);
void *data;
};
#ifdef __cplusplus
}
#endif
#endif /* oi_file_h */

69
deps/liboi/oi_queue.h

@ -0,0 +1,69 @@
/* Copyright (C) 2002-2009 Igor Sysoev
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef oi_queue_h
#define oi_queue_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* offsetof() */
typedef struct oi_queue oi_queue;
struct oi_queue {
oi_queue *prev;
oi_queue *next;
};
#define oi_queue_init(q) \
(q)->prev = q; \
(q)->next = q
#define oi_queue_empty(h) \
(h == (h)->prev)
#define oi_queue_insert_head(h, x) \
(x)->next = (h)->next; \
(x)->next->prev = x; \
(x)->prev = h; \
(h)->next = x
#define oi_queue_head(h) \
(h)->next
#define oi_queue_last(h) \
(h)->prev
#define oi_queue_remove(x) \
(x)->next->prev = (x)->prev; \
(x)->prev->next = (x)->next; \
(x)->prev = NULL; \
(x)->next = NULL
#define oi_queue_data(q, type, link) \
(type *) ((unsigned char *) q - offsetof(type, link))
#ifdef __cplusplus
}
#endif
#endif // oi_queue_h

941
deps/liboi/oi_socket.c

@ -0,0 +1,941 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h> /* close() */
#include <fcntl.h> /* fcntl() */
#include <errno.h> /* for the default methods */
#include <string.h> /* memset */
#include <netinet/tcp.h> /* TCP_NODELAY */
#include <ev.h>
#include <oi_socket.h>
#if HAVE_GNUTLS
# include <gnutls/gnutls.h>
# define GNUTLS_NEED_WRITE (gnutls_record_get_direction(socket->session) == 1)
# define GNUTLS_NEED_READ (gnutls_record_get_direction(socket->session) == 0)
#endif
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
#undef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define OKAY 0
#define AGAIN 1
#define ERROR 2
#define RAISE_ERROR(s, _domain, _code) do { \
if(s->on_error) { \
struct oi_error __oi_error; \
__oi_error.domain = _domain; \
__oi_error.code = _code; \
s->on_error(s, __oi_error); \
} \
} while(0) \
static int
full_close(oi_socket *socket)
{
if(-1 == close(socket->fd) && errno == EINTR) {
/* TODO fd still open. next loop call close again? */
assert(0 && "implement me");
return ERROR;
}
socket->read_action = NULL;
socket->write_action = NULL;
if(socket->loop) {
ev_feed_event(socket->loop, &socket->read_watcher, EV_READ);
}
return OKAY;
}
static int
half_close(oi_socket *socket)
{
int r = shutdown(socket->fd, SHUT_WR);
if(r == -1) {
RAISE_ERROR(socket, OI_ERROR_SHUTDOWN, errno);
return ERROR;
}
socket->write_action = NULL;
/* TODO set timer to zero so we get a callback soon */
return OKAY;
}
static void
update_write_buffer_after_send(oi_socket *socket, ssize_t sent)
{
oi_queue *q = oi_queue_last(&socket->out_stream);
oi_buf *to_write = oi_queue_data(q, oi_buf, queue);
to_write->written += sent;
socket->written += sent;
if(to_write->written == to_write->len) {
oi_queue_remove(q);
if(to_write->release) {
to_write->release(to_write);
}
if(oi_queue_empty(&socket->out_stream)) {
ev_io_stop(socket->loop, &socket->write_watcher);
if(socket->on_drain)
socket->on_drain(socket);
}
}
}
#if HAVE_GNUTLS
static int secure_socket_send(oi_socket *socket);
static int secure_socket_recv(oi_socket *socket);
/* TODO can this be done without ignoring SIGPIPE? */
static ssize_t
nosigpipe_push(gnutls_transport_ptr_t data, const void *buf, size_t len)
{
oi_socket *socket = (oi_socket*)data;
assert(socket->secure);
int flags = 0;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
#ifdef MSG_DONTWAIT
flags |= MSG_DONTWAIT;
#endif
int r = send(socket->fd, buf, len, flags);
if(r == -1) {
gnutls_transport_set_errno(socket->session, errno); /* necessary ? */
}
return r;
}
static int
secure_handshake(oi_socket *socket)
{
assert(socket->secure);
int r = gnutls_handshake(socket->session);
if(gnutls_error_is_fatal(r)) {
RAISE_ERROR(socket, OI_ERROR_GNUTLS, r);
return ERROR;
}
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
return AGAIN;
oi_socket_reset_timeout(socket);
if(!socket->connected) {
socket->connected = TRUE;
if(socket->on_connect)
socket->on_connect(socket);
}
if(socket->read_action)
socket->read_action = secure_socket_recv;
if(socket->write_action)
socket->write_action = secure_socket_send;
return OKAY;
}
static int
secure_socket_send(oi_socket *socket)
{
ssize_t sent;
if(oi_queue_empty(&socket->out_stream)) {
ev_io_stop(socket->loop, &socket->write_watcher);
return AGAIN;
}
oi_queue *q = oi_queue_last(&socket->out_stream);
oi_buf *to_write = oi_queue_data(q, oi_buf, queue);
assert(socket->secure);
sent = gnutls_record_send( socket->session
, to_write->base + to_write->written
, to_write->len - to_write->written
);
if(gnutls_error_is_fatal(sent)) {
RAISE_ERROR(socket, OI_ERROR_GNUTLS, sent);
return ERROR;
}
if(sent == 0)
return AGAIN;
oi_socket_reset_timeout(socket);
if(sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) {
if(GNUTLS_NEED_READ) {
if(socket->read_action) {
socket->read_action = secure_socket_send;
} else {
/* TODO GnuTLS needs read but already got EOF */
assert(0 && "needs read but already got EOF");
return ERROR;
}
}
return AGAIN;
}
if(sent > 0) {
/* make sure the callbacks are correct */
if(socket->read_action)
socket->read_action = secure_socket_recv;
update_write_buffer_after_send(socket, sent);
return OKAY;
}
assert(0 && "Unhandled return code from gnutls_record_send()!");
return ERROR;
}
static int
secure_socket_recv(oi_socket *socket)
{
char recv_buffer[TCP_MAXWIN];
size_t recv_buffer_size = MIN(TCP_MAXWIN, socket->chunksize);
ssize_t recved;
assert(socket->secure);
recved = gnutls_record_recv(socket->session, recv_buffer, recv_buffer_size);
//printf("secure socket recv %d %p\n", recved, socket->on_connect);
if(gnutls_error_is_fatal(recved)) {
RAISE_ERROR(socket, OI_ERROR_GNUTLS, recved);
return ERROR;
}
if(recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) {
if(GNUTLS_NEED_WRITE) {
if(socket->write_action) {
printf("need write\n");
socket->write_action = secure_socket_recv;
} else {
/* TODO GnuTLS needs send but already closed write end */
assert(0 && "needs read but cannot");
return ERROR;
}
}
return AGAIN;
}
oi_socket_reset_timeout(socket);
/* A server may also receive GNUTLS_E_REHANDSHAKE when a client has
* initiated a handshake. In that case the server can only initiate a
* handshake or terminate the connection. */
if(recved == GNUTLS_E_REHANDSHAKE) {
if(socket->write_action) {
socket->read_action = secure_handshake;
socket->write_action = secure_handshake;
return OKAY;
} else {
/* TODO */
assert(0 && "needs read but cannot");
return ERROR;
}
}
if(recved >= 0) {
/* Got EOF */
if(recved == 0)
socket->read_action = NULL;
if(socket->write_action)
socket->write_action = secure_socket_send;
if(socket->on_read) { socket->on_read(socket, recv_buffer, recved); }
return OKAY;
}
assert(0 && "Unhandled return code from gnutls_record_send()!");
return ERROR;
}
static int
secure_goodbye(oi_socket *socket, gnutls_close_request_t how)
{
assert(socket->secure);
int r = gnutls_bye(socket->session, how);
if(gnutls_error_is_fatal(r)) {
RAISE_ERROR(socket, OI_ERROR_GNUTLS, r);
return ERROR;
}
if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
return AGAIN;
return OKAY;
}
static int
secure_full_goodbye(oi_socket *socket)
{
int r = secure_goodbye(socket, GNUTLS_SHUT_RDWR);
if(OKAY == r) {
return full_close(socket);
}
return r;
}
static int
secure_half_goodbye(oi_socket *socket)
{
int r = secure_goodbye(socket, GNUTLS_SHUT_WR);
if(OKAY == r) {
return half_close(socket);
}
return r;
}
/* Tells the socket to use transport layer security (SSL). liboi does not
* want to make any decisions about security requirements, so the
* majoirty of GnuTLS configuration is left to the user. Only the transport
* layer of GnuTLS is controlled by liboi.
*
* That is, do not use gnutls_transport_* functions.
* Do use the rest of GnuTLS's API.
*/
void
oi_socket_set_secure_session (oi_socket *socket, gnutls_session_t session)
{
socket->session = session;
socket->secure = TRUE;
}
#endif /* HAVE GNUTLS */
static int
socket_send(oi_socket *socket)
{
ssize_t sent;
assert(socket->secure == FALSE);
if(oi_queue_empty(&socket->out_stream)) {
ev_io_stop(socket->loop, &socket->write_watcher);
return AGAIN;
}
oi_queue *q = oi_queue_last(&socket->out_stream);
oi_buf *to_write = oi_queue_data(q, oi_buf, queue);
int flags = 0;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
#ifdef MSG_DONTWAIT
flags |= MSG_DONTWAIT;
#endif
/* TODO use writev() here */
sent = send( socket->fd
, to_write->base + to_write->written
, to_write->len - to_write->written
, flags
);
if(sent < 0) {
switch(errno) {
case EAGAIN:
return AGAIN;
case ECONNREFUSED:
case ECONNRESET:
socket->write_action = NULL;
/* TODO maybe just clear write buffer instead of error?
* They should be able to read still from the socket.
*/
RAISE_ERROR(socket, OI_ERROR_SEND, errno);
return ERROR;
default:
perror("send()");
assert(0 && "oi shouldn't let this happen.");
return ERROR;
}
}
oi_socket_reset_timeout(socket);
if(!socket->connected) {
socket->connected = TRUE;
if(socket->on_connect) { socket->on_connect(socket); }
}
update_write_buffer_after_send(socket, sent);
return OKAY;
}
static int
socket_recv(oi_socket *socket)
{
char buf[TCP_MAXWIN];
size_t buf_size = TCP_MAXWIN;
ssize_t recved;
assert(socket->secure == FALSE);
if(!socket->connected) {
socket->connected = TRUE;
if(socket->on_connect) { socket->on_connect(socket); }
return OKAY;
}
recved = recv(socket->fd, buf, buf_size, 0);
if(recved < 0) {
switch(errno) {
case EAGAIN:
case EINTR:
return AGAIN;
/* A remote host refused to allow the network connection (typically
* because it is not running the requested service). */
case ECONNREFUSED:
RAISE_ERROR(socket, OI_ERROR_RECV, errno);
return ERROR;
case ECONNRESET:
RAISE_ERROR(socket, OI_ERROR_RECV, errno);
return ERROR;
default:
perror("recv()");
printf("unmatched errno %d\n", errno);
assert(0 && "recv returned error that oi should have caught before.");
return ERROR;
}
}
oi_socket_reset_timeout(socket);
if(recved == 0) {
oi_socket_read_stop(socket);
socket->read_action = NULL;
}
/* NOTE: EOF is signaled with recved == 0 on callback */
if(socket->on_read) { socket->on_read(socket, buf, recved); }
return OKAY;
}
static void
assign_file_descriptor(oi_socket *socket, int fd)
{
socket->fd = fd;
ev_io_set (&socket->read_watcher, fd, EV_READ);
ev_io_set (&socket->write_watcher, fd, EV_WRITE);
socket->read_action = socket_recv;
socket->write_action = socket_send;
#if HAVE_GNUTLS
if(socket->secure) {
gnutls_transport_set_lowat(socket->session, 0);
gnutls_transport_set_push_function(socket->session, nosigpipe_push);
gnutls_transport_set_ptr2 ( socket->session
/* recv */ , (gnutls_transport_ptr_t)fd
/* send */ , socket
);
socket->read_action = secure_handshake;
socket->write_action = secure_handshake;
}
#endif
}
/* Internal callback
* Called by server->connection_watcher.
*/
static void
on_connection(struct ev_loop *loop, ev_io *watcher, int revents)
{
oi_server *server = watcher->data;
// printf("on connection!\n");
assert(server->listening);
assert(server->loop == loop);
assert(&server->connection_watcher == watcher);
if(EV_ERROR & revents) {
oi_server_close(server);
return;
}
struct sockaddr address; /* connector's address information */
socklen_t addr_len = sizeof(address);
/* TODO accept all possible connections? currently: just one */
int fd = accept(server->fd, (struct sockaddr*)&address, &addr_len);
if(fd < 0) {
perror("accept()");
return;
}
oi_socket *socket = NULL;
if(server->on_connection)
socket = server->on_connection(server, (struct sockaddr*)&address, addr_len);
if(socket == NULL) {
close(fd);
return;
}
int flags = fcntl(fd, F_GETFL, 0);
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if(r < 0) {
/* TODO error report */
}
#ifdef SO_NOSIGPIPE
flags = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof(flags));
#endif
socket->server = server;
assign_file_descriptor(socket, fd);
oi_socket_attach(socket, loop);
}
int
oi_server_listen(oi_server *server, struct addrinfo *addrinfo)
{
int fd = -1;
struct linger ling = {0, 0};
assert(server->listening == FALSE);
fd = socket( addrinfo->ai_family
, addrinfo->ai_socktype
, addrinfo->ai_protocol
);
if(fd < 0) {
perror("socket()");
return -1;
}
int flags = fcntl(fd, F_GETFL, 0);
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if(r < 0) {
perror("fcntl()");
return -1;
}
flags = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
/* XXX: Sending single byte chunks in a response body? Perhaps there is a
* need to enable the Nagel algorithm dynamically. For now disabling.
*/
//setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0) {
perror("bind()");
close(fd);
return -1;
}
if (listen(fd, server->backlog) < 0) {
perror("listen()");
close(fd);
return -1;
}
server->fd = fd;
server->listening = TRUE;
ev_io_set (&server->connection_watcher, server->fd, EV_READ);
return 0;
}
/**
* Stops the server. Will not accept new connections. Does not drop
* existing connections.
*/
void
oi_server_close(oi_server *server)
{
if(server->listening) {
oi_server_detach(server);
close(server->fd);
/* TODO do this on the loop? check return value? */
server->listening = FALSE;
}
}
void
oi_server_attach (oi_server *server, struct ev_loop *loop)
{
ev_io_start (loop, &server->connection_watcher);
server->loop = loop;
}
void
oi_server_detach (oi_server *server)
{
ev_io_stop (server->loop, &server->connection_watcher);
server->loop = NULL;
}
void
oi_server_init(oi_server *server, int backlog)
{
server->backlog = backlog;
server->listening = FALSE;
server->fd = -1;
server->connection_watcher.data = server;
ev_init (&server->connection_watcher, on_connection);
server->on_connection = NULL;
server->on_error = NULL;
server->data = NULL;
}
/* Internal callback. called by socket->timeout_watcher */
static void
on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
{
oi_socket *socket = watcher->data;
assert(watcher == &socket->timeout_watcher);
// printf("on_timeout\n");
if(socket->on_timeout) { socket->on_timeout(socket); }
/* TODD set timer to zero */
full_close(socket);
}
static void
release_write_buffer(oi_socket *socket)
{
while(!oi_queue_empty(&socket->out_stream)) {
oi_queue *q = oi_queue_last(&socket->out_stream);
oi_buf *buf = oi_queue_data(q, oi_buf, queue);
oi_queue_remove(q);
if(buf->release) { buf->release(buf); }
}
}
/* Internal callback. called by socket->read_watcher */
static void
on_io_event(struct ev_loop *loop, ev_io *watcher, int revents)
{
oi_socket *socket = watcher->data;
if(revents & EV_ERROR) {
RAISE_ERROR(socket, OI_ERROR_EV, 0);
goto close;
}
int r;
int have_read_event = TRUE;
int have_write_event = TRUE;
while(have_read_event || have_write_event) {
if(socket->read_action) {
r = socket->read_action(socket);
if(r == ERROR) goto close;
if(r == AGAIN) have_read_event = FALSE;
} else {
have_read_event = FALSE;
}
if(socket->write_action) {
r = socket->write_action(socket);
if(r == ERROR) goto close;
if(r == AGAIN) have_write_event = FALSE;
} else {
have_write_event = FALSE;
}
if(socket->read_watcher.active == FALSE)
have_read_event = FALSE;
if(socket->write_watcher.active == FALSE)
have_write_event = FALSE;
}
if(socket->write_action == NULL && socket->read_action == NULL)
goto close;
return;
close:
release_write_buffer(socket);
ev_clear_pending (socket->loop, &socket->write_watcher);
ev_clear_pending (socket->loop, &socket->read_watcher);
ev_clear_pending (socket->loop, &socket->timeout_watcher);
oi_socket_detach(socket);
if(socket->on_close) { socket->on_close(socket); }
/* WARNING: user can free socket in on_close so no more
* access beyond this point. */
}
/**
* If using SSL do consider setting
* gnutls_db_set_retrieve_function (socket->session, _);
* gnutls_db_set_remove_function (socket->session, _);
* gnutls_db_set_store_function (socket->session, _);
* gnutls_db_set_ptr (socket->session, _);
*/
void
oi_socket_init(oi_socket *socket, float timeout)
{
socket->fd = -1;
socket->server = NULL;
socket->loop = NULL;
socket->connected = FALSE;
oi_queue_init(&socket->out_stream);
ev_init (&socket->write_watcher, on_io_event);
socket->write_watcher.data = socket;
ev_init(&socket->read_watcher, on_io_event);
socket->read_watcher.data = socket;
socket->secure = FALSE;
socket->wait_for_secure_hangup = FALSE;
#if HAVE_GNUTLS
socket->session = NULL;
#endif
/* TODO higher resolution timer */
ev_timer_init(&socket->timeout_watcher, on_timeout, 0., timeout);
socket->timeout_watcher.data = socket;
socket->read_action = NULL;
socket->write_action = NULL;
socket->chunksize = TCP_MAXWIN;
socket->on_connect = NULL;
socket->on_read = NULL;
socket->on_drain = NULL;
socket->on_error = NULL;
socket->on_timeout = NULL;
}
void
oi_socket_write_eof (oi_socket *socket)
{
#if HAVE_GNUTLS
/* try to hang up properly for secure connections */
if(socket->secure)
{
if( socket->connected /* completed handshake */
&& socket->write_action /* write end is open */
)
{
socket->write_action = secure_half_goodbye;
if(socket->loop)
ev_io_start(socket->loop, &socket->write_watcher);
return;
}
/* secure servers cannot handle half-closed connections? */
full_close(socket);
return;
}
#endif // HAVE_GNUTLS
if(socket->write_action)
half_close(socket);
else
full_close(socket);
}
void
oi_socket_close (oi_socket *socket)
{
#if HAVE_GNUTLS
/* try to hang up properly for secure connections */
if( socket->secure
&& socket->connected /* completed handshake */
&& socket->write_action /* write end is open */
)
{
if(socket->wait_for_secure_hangup && socket->read_action) {
socket->write_action = secure_full_goodbye;
socket->read_action = secure_full_goodbye;
} else {
socket->write_action = secure_half_goodbye;
socket->read_action = NULL;
}
if(socket->loop)
ev_io_start(socket->loop, &socket->write_watcher);
return;
}
#endif // HAVE_GNUTLS
full_close(socket);
}
/*
* Resets the timeout to stay alive for another socket->timeout seconds
*/
void
oi_socket_reset_timeout(oi_socket *socket)
{
ev_timer_again(socket->loop, &socket->timeout_watcher);
}
/**
* Writes a string to the socket. This is actually sets a watcher which may
* take multiple iterations to write the entire string.
*/
void
oi_socket_write(oi_socket *socket, oi_buf *buf)
{
if(socket->write_action == NULL)
return;
oi_queue_insert_head(&socket->out_stream, &buf->queue);
buf->written = 0;
ev_io_start(socket->loop, &socket->write_watcher);
}
static void
free_simple_buf ( oi_buf *buf )
{
free(buf->base);
free(buf);
}
/* Writes a string to the socket.
* NOTE: Allocates memory. Avoid for performance applications.
*/
void
oi_socket_write_simple(oi_socket *socket, const char *str, size_t len)
{
oi_buf *buf = malloc(sizeof(oi_buf));
buf->release = free_simple_buf;
buf->base = strdup(str);
buf->len = len;
oi_socket_write(socket, buf);
}
void
oi_socket_attach(oi_socket *socket, struct ev_loop *loop)
{
socket->loop = loop;
ev_timer_again(loop, &socket->timeout_watcher);
if(socket->read_action)
ev_io_start(loop, &socket->read_watcher);
if(socket->write_action)
ev_io_start(loop, &socket->write_watcher);
/* make sure the io_event happens soon in the case we're being reattached */
ev_feed_event(loop, &socket->read_watcher, EV_READ);
}
void
oi_socket_detach(oi_socket *socket)
{
if(socket->loop) {
ev_io_stop(socket->loop, &socket->write_watcher);
ev_io_stop(socket->loop, &socket->read_watcher);
ev_timer_stop(socket->loop, &socket->timeout_watcher);
socket->loop = NULL;
}
}
void
oi_socket_read_stop (oi_socket *socket)
{
ev_io_stop(socket->loop, &socket->read_watcher);
ev_clear_pending (socket->loop, &socket->read_watcher);
}
void
oi_socket_read_start (oi_socket *socket)
{
if(socket->read_action) {
ev_io_start(socket->loop, &socket->read_watcher);
/* XXX feed event? */
}
}
int
oi_socket_connect(oi_socket *s, struct addrinfo *addrinfo)
{
int fd = socket( addrinfo->ai_family
, addrinfo->ai_socktype
, addrinfo->ai_protocol
);
if(fd < 0) {
perror("socket()");
return -1;
}
int flags = fcntl(fd, F_GETFL, 0);
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if(r < 0) {
perror("fcntl()");
return -1;
}
#ifdef SO_NOSIGPIPE
flags = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof(flags));
#endif
r = connect( fd
, addrinfo->ai_addr
, addrinfo->ai_addrlen
);
if(r < 0 && errno != EINPROGRESS) {
perror("connect");
close(fd);
return -1;
}
assign_file_descriptor(s, fd);
return 0;
}

98
deps/liboi/oi_socket.h

@ -0,0 +1,98 @@
#include <netdb.h>
#include <ev.h>
#include <oi_queue.h>
#include <oi_error.h>
#include <oi_buf.h>
#ifndef oi_socket_h
#define oi_socket_h
#ifdef __cplusplus
extern "C" {
#endif
#ifndef HAVE_GNUTLS
# define HAVE_GNUTLS 0
#endif
#if HAVE_GNUTLS
# include <gnutls/gnutls.h>
#endif
typedef struct oi_server oi_server;
typedef struct oi_socket oi_socket;
void oi_server_init (oi_server *, int backlog);
int oi_server_listen (oi_server *, struct addrinfo *addrinfo);
void oi_server_attach (oi_server *, struct ev_loop *loop);
void oi_server_detach (oi_server *);
void oi_server_close (oi_server *);
void oi_socket_init (oi_socket *, float timeout);
int oi_socket_pair (oi_socket *a, oi_socket *b); /* TODO */
int oi_socket_connect (oi_socket *, struct addrinfo *addrinfo);
void oi_socket_attach (oi_socket *, struct ev_loop *loop);
void oi_socket_detach (oi_socket *);
void oi_socket_read_start (oi_socket *);
void oi_socket_read_stop (oi_socket *);
void oi_socket_reset_timeout (oi_socket *);
void oi_socket_write (oi_socket *, oi_buf *);
void oi_socket_write_simple (oi_socket *, const char *str, size_t len);
void oi_socket_write_eof (oi_socket *);
void oi_socket_close (oi_socket *);
#if HAVE_GNUTLS
void oi_socket_set_secure_session (oi_socket *, gnutls_session_t);
#endif
struct oi_server {
/* read only */
int fd;
int backlog;
struct ev_loop *loop;
unsigned listening:1;
/* private */
ev_io connection_watcher;
/* public */
oi_socket* (*on_connection) (oi_server *, struct sockaddr *remote_addr, socklen_t remove_addr_len);
void (*on_error) (oi_server *, struct oi_error e);
void *data;
};
struct oi_socket {
/* read only */
int fd;
struct ev_loop *loop;
oi_server *server;
oi_queue out_stream;
size_t written;
unsigned connected:1;
unsigned secure:1;
unsigned wait_for_secure_hangup:1;
/* if these are NULL then it means that end of the socket is closed. */
int (*read_action) (oi_socket *);
int (*write_action) (oi_socket *);
/* private */
ev_io write_watcher;
ev_io read_watcher;
ev_timer timeout_watcher;
#if HAVE_GNUTLS
gnutls_session_t session;
#endif
/* public */
size_t chunksize; /* the maximum chunk that on_read() will return */
void (*on_connect) (oi_socket *);
void (*on_read) (oi_socket *, const void *buf, size_t count);
void (*on_drain) (oi_socket *);
void (*on_error) (oi_socket *, struct oi_error e);
void (*on_close) (oi_socket *);
void (*on_timeout) (oi_socket *);
void *data;
};
#ifdef __cplusplus
}
#endif
#endif /* oi_socket_h */

110
deps/liboi/test/common.c

@ -0,0 +1,110 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <ev.h>
#include <oi.h>
#include <gnutls/gnutls.h>
#define HOST "127.0.0.1"
#define SOCKFILE "/tmp/oi.sock"
#define PORT "5000"
int nconnections;
static void
on_peer_close(oi_socket *socket)
{
//printf("server connection closed\n");
#if HAVE_GNUTLS
#if SECURE
gnutls_deinit(socket->session);
#endif
#endif
free(socket);
}
static void
on_client_timeout(oi_socket *socket)
{
printf("client connection timeout\n");
assert(0);
}
static void
on_peer_timeout(oi_socket *socket)
{
fprintf(stderr, "peer connection timeout\n");
assert(0);
}
static void
on_client_error(oi_socket *socket, struct oi_error e)
{
assert(0);
}
#if HAVE_GNUTLS
#if SECURE
#define DH_BITS 768
gnutls_anon_server_credentials_t server_credentials;
const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
static gnutls_dh_params_t dh_params;
void anon_tls_init()
{
gnutls_global_init();
gnutls_dh_params_init (&dh_params);
fprintf(stderr, "..");
fsync((int)stderr);
gnutls_dh_params_generate2 (dh_params, DH_BITS);
fprintf(stderr, ".");
gnutls_anon_allocate_server_credentials (&server_credentials);
gnutls_anon_set_server_dh_params (server_credentials, dh_params);
}
void anon_tls_server(oi_socket *socket)
{
gnutls_session_t session;
socket->data = session;
int r = gnutls_init(&session, GNUTLS_SERVER);
assert(r == 0);
gnutls_set_default_priority(session);
gnutls_kx_set_priority (session, kx_prio);
gnutls_credentials_set(session, GNUTLS_CRD_ANON, server_credentials);
gnutls_dh_set_prime_bits(session, DH_BITS);
oi_socket_set_secure_session(socket, session);
}
void anon_tls_client(oi_socket *socket)
{
gnutls_session_t client_session;
gnutls_anon_client_credentials_t client_credentials;
gnutls_anon_allocate_client_credentials (&client_credentials);
gnutls_init (&client_session, GNUTLS_CLIENT);
gnutls_set_default_priority(client_session);
gnutls_kx_set_priority(client_session, kx_prio);
/* Need to enable anonymous KX specifically. */
gnutls_credentials_set (client_session, GNUTLS_CRD_ANON, client_credentials);
oi_socket_set_secure_session(socket, client_session);
assert(socket->secure);
}
#endif // SECURE
#endif // HAVE_GNUTLS

159
deps/liboi/test/connection_interruption.c

@ -0,0 +1,159 @@
#include "test/common.c"
#define NCONN 100
#define TIMEOUT 1000.0
static oi_server server;
static void
on_peer_read(oi_socket *socket, const void *base, size_t len)
{
assert(len == 0);
oi_socket_write_simple(socket, "BYE", 3);
//printf("server wrote bye\n");
}
static void
on_peer_drain(oi_socket *socket)
{
oi_socket_close(socket);
}
static void
on_peer_error2(oi_socket *socket, struct oi_error e)
{
if(e.domain == OI_ERROR_GNUTLS) return;
assert(0);
}
static oi_socket*
on_server_connection(oi_server *server, struct sockaddr *addr, socklen_t len)
{
oi_socket *socket = malloc(sizeof(oi_socket));
oi_socket_init(socket, TIMEOUT);
socket->on_read = on_peer_read;
socket->on_drain = on_peer_drain;
socket->on_error = on_peer_error2;
socket->on_close = on_peer_close;
socket->on_timeout = on_peer_timeout;
#if HAVE_GNUTLS
# if SECURE
anon_tls_server(socket);
# endif
#endif
//printf("on server connection\n");
return socket;
}
static void
on_client_connect(oi_socket *socket)
{
//printf("on client connection\n");
oi_socket_write_eof(socket);
}
static void
on_client_close(oi_socket *socket)
{
oi_socket_close(socket); // already closed, but it shouldn't crash if we try to do it again
//printf("client connection closed\n");
if(++nconnections == NCONN) {
oi_server_detach(&server);
//printf("detaching server\n");
}
}
static void
on_client_read(oi_socket *socket, const void *base, size_t len)
{
char buf[200000];
strncpy(buf, base, len);
buf[len] = 0;
//printf("client got message: %s\n", buf);
if(strcmp(buf, "BYE") == 0) {
oi_socket_close(socket);
} else {
assert(0);
}
}
int
main(int argc, const char *argv[])
{
int r;
struct ev_loop *loop = ev_default_loop(0);
oi_server_init(&server, 1000);
server.on_connection = on_server_connection;
#if HAVE_GNUTLS
# if SECURE
anon_tls_init();
# endif
#endif
struct addrinfo *servinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
#if TCP
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
r = getaddrinfo(NULL, PORT, &hints, &servinfo);
assert(r == 0);
#else
struct stat tstat;
if (lstat(SOCKFILE, &tstat) == 0) {
if (S_ISSOCK(tstat.st_mode))
unlink(SOCKFILE);
}
servinfo = malloc(sizeof(struct addrinfo));
servinfo->ai_family = AF_UNIX;
servinfo->ai_socktype = SOCK_STREAM;
servinfo->ai_protocol = 0;
struct sockaddr_un *sockaddr = calloc(sizeof(struct sockaddr_un), 1);
sockaddr->sun_family = AF_UNIX;
strcpy(sockaddr->sun_path, SOCKFILE);
servinfo->ai_addr = (struct sockaddr*)sockaddr;
servinfo->ai_addrlen = sizeof(struct sockaddr_un);
#endif
oi_server_listen(&server, servinfo);
oi_server_attach(&server, loop);
int i;
for(i = 0; i < NCONN; i++) {
oi_socket *client = malloc(sizeof(oi_socket));
oi_socket_init(client, TIMEOUT);
client->on_read = on_client_read;
client->on_error = on_client_error;
client->on_connect = on_client_connect;
client->on_close = on_client_close;
client->on_timeout = on_client_timeout;
#if HAVE_GNUTLS
#if SECURE
anon_tls_client(client);
#endif
#endif
r = oi_socket_connect(client, servinfo);
assert(r == 0 && "problem connecting");
oi_socket_attach(client, loop);
}
ev_loop(loop, 0);
assert(nconnections == NCONN);
#if TCP
freeaddrinfo(servinfo);
#endif
return 0;
}

102
deps/liboi/test/echo.c

@ -0,0 +1,102 @@
#include "test/common.c"
int successful_ping_count;
static void
on_peer_read(oi_socket *socket, const void *base, size_t len)
{
if(len == 0)
return;
oi_socket_write_simple(socket, base, len);
}
static void
on_peer_error(oi_socket *socket, struct oi_error e)
{
assert(0);
}
static oi_socket*
on_server_connection(oi_server *server, struct sockaddr *addr, socklen_t len)
{
oi_socket *socket = malloc(sizeof(oi_socket));
oi_socket_init(socket, 5.0);
socket->on_read = on_peer_read;
socket->on_error = on_peer_error;
socket->on_close = on_peer_close;
socket->on_timeout = on_peer_timeout;
nconnections++;
#if HAVE_GNUTLS
# if SECURE
anon_tls_server(socket);
# endif
#endif
//printf("on server connection\n");
return socket;
}
int
main(int argc, const char *argv[])
{
int r;
struct ev_loop *loop = ev_default_loop(0);
oi_server server;
oi_socket client;
//printf("sizeof(oi_server): %d\n", sizeof(oi_server));
//printf("sizeof(oi_socket): %d\n", sizeof(oi_socket));
oi_server_init(&server, 10);
server.on_connection = on_server_connection;
#if HAVE_GNUTLS
# if SECURE
anon_tls_init();
# endif
#endif
struct addrinfo *servinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
#if TCP
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
r = getaddrinfo(NULL, PORT, &hints, &servinfo);
assert(r == 0);
#else
struct stat tstat;
if (lstat(SOCKFILE, &tstat) == 0) {
if (S_ISSOCK(tstat.st_mode))
unlink(SOCKFILE);
}
servinfo = malloc(sizeof(struct addrinfo));
servinfo->ai_family = AF_UNIX;
servinfo->ai_socktype = SOCK_STREAM;
servinfo->ai_protocol = 0;
struct sockaddr_un *sockaddr = calloc(sizeof(struct sockaddr_un), 1);
sockaddr->sun_family = AF_UNIX;
strcpy(sockaddr->sun_path, SOCKFILE);
servinfo->ai_addr = (struct sockaddr*)sockaddr;
servinfo->ai_addrlen = sizeof(struct sockaddr_un);
#endif
r = oi_server_listen(&server, servinfo);
assert(r == 0);
oi_server_attach(&server, loop);
ev_loop(loop, 0);
#if TCP
freeaddrinfo(servinfo);
#endif
return 0;
}

223
deps/liboi/test/fancy_copy.c

@ -0,0 +1,223 @@
/* This test uses most of oi's facilities. It starts by opening a new file
* and writing N characters to it. Once the first chunk is written, it opens
* that file with another handle
*
* /tmp/oi_test_src == /tmp/oi_test_dst
* | ^
* | |
* stream | | write
* | |
* V |
* client ----------> server/connection
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#include <oi.h>
#include <oi_file.h>
#define PORT "5555"
static struct ev_loop *loop;
static oi_file file_src;
static oi_file file_dst;
static oi_socket client;
static oi_server server;
static oi_socket connection;
static int got_connection = 0;
static char *src_filename;
static char *dst_filename;
static void
file_error(oi_file *_)
{
assert(0);
}
static void
on_file_dst_open (oi_file *_)
{
assert(connection.fd > 0);
oi_socket_read_start(&connection);
//printf("file_dst is open\n");
}
static void
on_file_dst_close (oi_file *_)
{
//printf("file dst closed\n");
oi_file_detach(&file_dst);
}
static void
on_connection_read (oi_socket *_, const void *buf, size_t count)
{
assert(file_dst.fd > 0);
if(count == 0) {
file_dst.on_drain = oi_file_close;
oi_socket_close(&connection);
} else {
oi_file_write_simple(&file_dst, buf, count);
}
}
static void
on_connection_connect (oi_socket *_)
{
oi_file_init(&file_dst);
file_dst.on_open = on_file_dst_open;
file_dst.on_close = on_file_dst_close;
oi_file_open_path(&file_dst, dst_filename, O_WRONLY | O_CREAT, 0644);
oi_file_attach(&file_dst, loop);
oi_socket_read_stop(&connection);
}
static void
on_connection_timeout (oi_socket *_)
{
assert(0);
}
static void
on_connection_error (oi_socket *_, struct oi_error e)
{
assert(0);
}
static void
on_connection_close (oi_socket *_)
{
oi_server_close(&server);
oi_server_detach(&server);
}
static oi_socket*
on_server_connection(oi_server *_, struct sockaddr *addr, socklen_t len)
{
assert(got_connection == FALSE);
oi_socket_init(&connection, 5.0);
connection.on_connect = on_connection_connect;
connection.on_read = on_connection_read;
connection.on_error = on_connection_error;
connection.on_close = on_connection_close;
connection.on_timeout = on_connection_timeout;
connection.on_drain = oi_socket_close;
got_connection = TRUE;
//printf("on server connection\n");
return &connection;
}
static void
on_client_read (oi_socket *_, const void *buf, size_t count)
{
assert(0);
}
static void
on_client_error (oi_socket *_, struct oi_error e)
{
assert(0);
}
static void
on_client_connect (oi_socket *_)
{
if(file_src.fd > 0) {
oi_file_send(&file_src, &client, 0, 50*1024);
}
}
static void
on_client_drain (oi_socket *_)
{
oi_socket_close(&client);
oi_file_close(&file_src);
}
static void
on_file_src_open (oi_file *_)
{
if(client.fd > 0) {
oi_file_send(&file_src, &client, 0, 50*1024);
}
}
static void
on_client_timeout (oi_socket *_)
{
assert(0);
}
int
main(int argc, char *argv[])
{
int r;
loop = ev_default_loop(0);
assert(argc == 3);
src_filename = argv[1];
dst_filename = argv[2];
assert(strlen(src_filename) > 0);
assert(strlen(dst_filename) > 0);
oi_server_init(&server, 10);
server.on_connection = on_server_connection;
struct addrinfo *servinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
r = getaddrinfo(NULL, PORT, &hints, &servinfo);
assert(r == 0);
r = oi_server_listen(&server, servinfo);
assert(r >= 0 && "problem listening");
oi_server_attach(&server, loop);
oi_socket_init(&client, 5.0);
client.on_read = on_client_read;
client.on_error = on_client_error;
client.on_connect = on_client_connect;
client.on_timeout = on_client_timeout;
//client.on_close = oi_socket_detach;
client.on_drain = on_client_drain;
r = oi_socket_connect(&client, servinfo);
assert(r == 0 && "problem connecting");
oi_socket_attach(&client, loop);
oi_file_init(&file_src);
file_src.on_open = on_file_src_open;
file_src.on_drain = file_error;
file_src.on_close = oi_file_detach;
oi_file_open_path(&file_src, src_filename, O_RDONLY, 0700);
oi_file_attach(&file_src, loop);
ev_loop(loop, 0);
printf("\noi_file: %d bytes\n", sizeof(oi_file));
printf("oi_socket: %d bytes\n", sizeof(oi_socket));
return 0;
}

165
deps/liboi/test/ping_pong.c

@ -0,0 +1,165 @@
#include "test/common.c"
#define PING "PING"
#define PONG "PONG"
#define EXCHANGES 100
int successful_ping_count;
static void
on_peer_read(oi_socket *socket, const void *base, size_t len)
{
if(len == 0)
return;
char buf[2000];
strncpy(buf, base, len);
buf[len] = 0;
//printf("server got message: %s\n", buf);
oi_socket_write_simple(socket, PONG, sizeof PONG);
}
static void
on_peer_error(oi_socket *socket, struct oi_error e)
{
assert(0);
}
static void
on_client_close(oi_socket *socket)
{
//printf("client connection closed\n");
ev_unloop(socket->loop, EVUNLOOP_ALL);
}
static oi_socket*
on_server_connection(oi_server *server, struct sockaddr *addr, socklen_t len)
{
oi_socket *socket = malloc(sizeof(oi_socket));
oi_socket_init(socket, 5.0);
socket->on_read = on_peer_read;
socket->on_error = on_peer_error;
socket->on_close = on_peer_close;
socket->on_timeout = on_peer_timeout;
nconnections++;
#if HAVE_GNUTLS
# if SECURE
anon_tls_server(socket);
# endif
#endif
//printf("on server connection\n");
return socket;
}
static void
on_client_connect(oi_socket *socket)
{
//printf("client connected. sending ping\n");
oi_socket_write_simple(socket, PING, sizeof PING);
}
static void
on_client_read(oi_socket *socket, const void *base, size_t len)
{
char buf[200000];
strncpy(buf, base, len);
buf[len] = 0;
//printf("client got message: %s\n", buf);
if(strcmp(buf, PONG) == 0) {
if(++successful_ping_count > EXCHANGES) {
oi_socket_close(socket);
return;
}
oi_socket_write_simple(socket, PING, sizeof PING);
} else {
assert(0);
}
}
int
main(int argc, const char *argv[])
{
int r;
struct ev_loop *loop = ev_default_loop(0);
oi_server server;
oi_socket client;
//printf("sizeof(oi_server): %d\n", sizeof(oi_server));
//printf("sizeof(oi_socket): %d\n", sizeof(oi_socket));
oi_server_init(&server, 10);
server.on_connection = on_server_connection;
#if HAVE_GNUTLS
# if SECURE
anon_tls_init();
# endif
#endif
struct addrinfo *servinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
#if TCP
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
r = getaddrinfo(NULL, PORT, &hints, &servinfo);
assert(r == 0);
#else
struct stat tstat;
if (lstat(SOCKFILE, &tstat) == 0) {
if (S_ISSOCK(tstat.st_mode))
unlink(SOCKFILE);
}
servinfo = malloc(sizeof(struct addrinfo));
servinfo->ai_family = AF_UNIX;
servinfo->ai_socktype = SOCK_STREAM;
servinfo->ai_protocol = 0;
struct sockaddr_un *sockaddr = calloc(sizeof(struct sockaddr_un), 1);
sockaddr->sun_family = AF_UNIX;
strcpy(sockaddr->sun_path, SOCKFILE);
servinfo->ai_addr = (struct sockaddr*)sockaddr;
servinfo->ai_addrlen = sizeof(struct sockaddr_un);
#endif
r = oi_server_listen(&server, servinfo);
assert(r == 0);
oi_server_attach(&server, loop);
oi_socket_init(&client, 5.0);
client.on_read = on_client_read;
client.on_error = on_client_error;
client.on_connect = on_client_connect;
client.on_close = on_client_close;
client.on_timeout = on_client_timeout;
#if HAVE_GNUTLS
# if SECURE
anon_tls_client(&client);
# endif
#endif
r = oi_socket_connect(&client, servinfo);
assert(r == 0 && "problem connecting");
oi_socket_attach(&client, loop);
ev_loop(loop, 0);
assert(successful_ping_count == EXCHANGES + 1);
assert(nconnections == 1);
#if TCP
freeaddrinfo(servinfo);
#endif
return 0;
}

54
deps/liboi/test/sleeping_tasks.c

@ -0,0 +1,54 @@
#include <unistd.h> /* sleep() */
#include <stdlib.h> /* malloc(), free() */
#include <assert.h>
#include <ev.h>
#include <oi_async.h>
#define SLEEPS 4
static int runs = 0;
static void
done (oi_task *task, unsigned int result)
{
assert(result == 0);
if(++runs == SLEEPS) {
ev_timer *timer = task->data;
ev_timer_stop(task->async->loop, timer);
oi_async_detach(task->async);
}
free(task);
}
static void
on_timeout(struct ev_loop *loop, ev_timer *w, int events)
{
assert(0 && "timeout before all sleeping tasks were complete!");
}
int
main()
{
struct ev_loop *loop = ev_default_loop(0);
oi_async async;
ev_timer timer;
int i;
oi_async_init(&async);
for(i = 0; i < SLEEPS; i++) {
oi_task *task = malloc(sizeof(oi_task));
oi_task_init_sleep(task, done, 1);
task->data = &timer;
oi_async_submit(&async, task);
}
oi_async_attach(loop, &async);
ev_timer_init (&timer, on_timeout, 1.2, 0.);
ev_timer_start (loop, &timer);
ev_loop(loop, 0);
assert(runs == SLEEPS);
return 0;
}

64
deps/liboi/test/stdout.c

@ -0,0 +1,64 @@
#include <unistd.h> /* sleep() */
#include <stdlib.h> /* malloc(), free() */
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ev.h>
#include <oi_file.h>
static oi_file file;
static oi_file out;
#define READ_BUFSIZE (5)
static char read_buf[READ_BUFSIZE];
# define SEP "~~~~~~~~~~~~~~~~~~~~~~\n"
static void
on_open(oi_file *f)
{
oi_file_write_simple(&out, "\n", 1);
oi_file_write_simple(&out, SEP, sizeof(SEP));
oi_file_read_start(f, read_buf, READ_BUFSIZE);
}
static void
on_close(oi_file *f)
{
oi_file_write_simple(&out, SEP, sizeof(SEP));
oi_file_detach(f);
out.on_drain = oi_file_detach;
}
static void
on_read(oi_file *f, size_t recved)
{
if(recved == 0) /* EOF */
oi_file_close(f);
else
oi_file_write_simple(&out, read_buf, recved);
}
int
main()
{
struct ev_loop *loop = ev_default_loop(0);
oi_file_init(&file);
file.on_open = on_open;
file.on_read = on_read;
file.on_close = on_close;
oi_file_open_path(&file, "LICENSE", O_RDONLY, 0);
oi_file_attach(&file, loop);
oi_file_init(&out);
oi_file_open_stdout(&out);
oi_file_attach(&out, loop);
ev_loop(loop, 0);
return 0;
}

96
deps/liboi/test/timeout.rb

@ -0,0 +1,96 @@
#!/usr/bin/env ruby
def test(description)
pid = fork do
exec(File.dirname(__FILE__) + "/echo")
end
begin
sleep 0.5 # give time for the server to start
yield(pid)
rescue
puts "\033[1;31mFAIL\033[m: #{description}"
raise $!
ensure
`kill -9 #{pid}`
end
puts "\033[1;32mPASS\033[m: #{description}"
end
test("make sure echo server works") do
socket = TCPSocket.open("localhost", 5000)
w = socket.write("hello");
raise "error" unless w == 5
got = socket.recv(5);
raise "error" unless got == "hello"
socket.close
end
test("doing nothing should not timeout the server") do |pid|
10.times do
print "."
STDOUT.flush
if Process.waitpid(pid, Process::WNOHANG)
raise "server died when it shouldn't have"
end
sleep 1
end
puts ""
end
test("connecting and doing nothing to should timeout in 5 seconds") do |pid|
socket = TCPSocket.open("localhost", 5000)
i = 0
10.times do
print "."
STDOUT.flush
break if Process.waitpid(pid, Process::WNOHANG)
sleep 1
i+=1
end
puts ""
raise "died too soon (after #{i} seconds)" if i < 5
raise "died too late (after #{i} seconds)" if i > 6
end
test("connecting and writing once to should timeout in 5 seconds") do |pid|
socket = TCPSocket.open("localhost", 5000)
w = socket.write("hello");
raise "error" unless w == 5
i = 0
10.times do
print "."
STDOUT.flush
break if Process.waitpid(pid, Process::WNOHANG)
sleep 1
i+=1
end
puts ""
raise "died too soon (after #{i} seconds)" if i < 5
raise "died too late (after #{i} seconds)" if i > 6
end
test("connecting waiting 3, writing once to should timeout in 8 seconds") do |pid|
socket = TCPSocket.open("localhost", 5000)
sleep 3
w = socket.write("hello");
raise "error" unless w == 5
i = 0
10.times do
print "."
STDOUT.flush
break if Process.waitpid(pid, Process::WNOHANG)
sleep 1
i+=1
end
puts ""
raise "died too soon (after #{i} seconds)" if i < 5
raise "died too late (after #{i} seconds)" if i > 6
end

15
deps/v8/AUTHORS

@ -0,0 +1,15 @@
# Below is a list of people and organizations that have contributed
# to the V8 project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google Inc.
Rene Rebe <rene@exactcode.de>
Rafal Krypa <rafal@krypa.net>
Jay Freeman <saurik@saurik.com>
Daniel James <dnljms@gmail.com>
Paolo Giarrusso <p.giarrusso@gmail.com>
Daniel Andersson <kodandersson@gmail.com>
Alexander Botero-Lowry <alexbl@FreeBSD.org>
Matt Hanselman <mjhanselman@gmail.com>

816
deps/v8/ChangeLog

@ -0,0 +1,816 @@
2009-04-15: Version 1.1.10
Fixed crash bug that occurred when loading a const variable in the
presence of eval.
Allowed using with and eval in registered extensions in debug mode
by fixing bogus assert.
Fixed the source position for function returns to enable the
debugger to break there.
2009-04-14: Version 1.1.9
Made the stack traversal code in the profiler robust by avoiding
to look into the heap.
Added name inferencing for anonymous functions to facilitate
debugging and profiling.
Re-enabled stats timers in the developer shell (d8).
Fixed issue 303 by avoiding to shortcut cons-symbols.
2009-04-11: Version 1.1.8
Changed test-debug/ThreadedDebugging to be non-flaky (issue 96).
Fixed step-in handling for Function.prototype.apply and call in
the debugger (issue 269).
Fixed v8::Object::DeleteHiddenValue to not bail out when there
are no hidden properties.
Added workaround for crash bug, where external symbol table
entries with deleted resources would lead to NPEs when looking
up in the symbol table.
2009-04-07: Version 1.1.7
Added support for easily importing additional environment
variables into the SCons build.
Optimized strict equality checks.
Fixed crash in indexed setters on objects without a corresponding
getter (issue 298).
Re-enabled script compilation cache.
2009-04-01: Version 1.1.6
Reverted an unsafe code generator change.
2009-04-01: Version 1.1.5
Fixed bug that caused function literals to not be optimized as
much as other functions.
Improved profiler support.
Fixed a crash bug in connection with debugger unloading.
Fixed a crash bug in the code generator caused by losing the
information that a frame element was copied.
Fixed an exception propagation bug that could cause non-null
return values when exceptions were thrown.
2009-03-30: Version 1.1.4
Optimized String.prototype.match.
Improved the stack information in profiles.
Fixed bug in ARM port making it possible to compile the runtime
system for thumb mode again.
Implemented a number of optimizations in the code generator.
Fixed a number of memory leaks in tests.
Fixed crash bug in connection with script source code and external
strings.
2009-03-24: Version 1.1.3
Fixed assertion failures in compilation of loop conditions.
Removed STL dependency from developer shell (d8).
Added infrastructure for protecting the V8 heap from corruption
caused by memory modifications from the outside.
2009-03-24: Version 1.1.2
Improved frame merge code generated by the code generator.
Optimized String.prototype.replace.
Implemented __defineGetter__ and __defineSetter__ for properties
with integer keys on non-array objects.
Improved debugger and profiler support.
Fixed a number of portability issues to allow compilation for
smaller ARM devices.
Exposed object cloning through the API.
Implemented hidden properties. This is used to expose an identity
hash for objects through the API.
Implemented restarting of regular expressions if their input
string changes representation during preemption.
Fixed a code generator bug that could cause assignments in loops
to be ignored if using continue to break out of the loop (issue
284).
2009-03-12: Version 1.1.1
Fixed an assertion in the new compiler to take stack overflow
exceptions into account.
Removed exception propagation code that could cause crashes.
Fixed minor bug in debugger line number computations.
8-byte align the C stack on Linux and Windows to speed up floating
point computations.
2009-03-12: Version 1.1.0
Improved code generation infrastructure by doing simple register
allocation and constant folding and propagation.
Optimized regular expression matching by avoiding to create
intermediate string arrays and by flattening nested array
representations of RegExp data.
Traverse a few stack frames when recording profiler samples to
include partial call graphs in the profiling output.
Added support for using OProfile to profile generated code.
Added remote debugging support to the D8 developer shell.
Optimized creation of nested literals like JSON objects.
Fixed a bug in garbage collecting unused maps and turned it on by
default (--collect-maps).
Added support for running tests under Valgrind.
2009-02-27: Version 1.0.3
Optimized double-to-integer conversions in bit operations by using
SSE3 instructions if available.
Optimized initialization sequences that store to multiple
properties of the same object.
Changed the D8 debugger frontend to use JSON messages.
Force garbage collections when disposing contexts.
Align code objects at 32-byte boundaries.
2009-02-25: Version 1.0.2
Improved profiling support by performing simple call stack
sampling for ticks and by fixing a bug in the logging of code
addresses.
Fixed a number of debugger issues.
Optimized code that uses eval.
Fixed a couple of bugs in the regular expression engine.
Reduced the size of generated code for certain regular expressions.
Removed JSCRE completely.
Fixed issue where test could not be run if there was a dot in the
checkout path.
2009-02-13: Version 1.0.1
Fixed two crash-bugs in irregexp (issue 231 and 233).
Fixed a number of minor bugs (issue 87, 227 and 228).
Added support for morphing strings to external strings on demand
to avoid having to create copies in the embedding code.
Removed experimental support for external symbol callbacks.
2009-02-09: Version 1.0.0
Fixed crash-bug in the code generation for case independent 16 bit
backreferences.
Made shells more robust in the presence of string conversion
failures (issue 224).
Fixed a potential infinite loop when attempting to resolve
eval (issue 221).
Miscellaneous fixes to the new regular expression engine.
Reduced binary by stripping unneeded text from JavaScript library and
minifying some JavaScript files.
2009-01-27: Version 0.4.9
Enabled new regular expression engine.
Made a number of changes to the debugger protocol.
Fixed a number of bugs in the preemption support.
Added -p option to the developer shell to run files in parallel
using preemption.
Fixed a number of minor bugs (including issues 176, 187, 189, 192,
193, 198 and 201).
Fixed a number of bugs in the serialization/deserialization
support for the ARM platform.
2009-01-19: Version 0.4.8.1
Minor patch to debugger support.
2009-01-16: Version 0.4.8
Fixed string length bug on ARM (issue 171).
Made most methods in the API const.
Optimized object literals by improving data locality.
Fixed bug that caused incomplete functions to be cached in case of
stack overflow exceptions.
Fixed bugs that caused catch variables and variables introduced by
eval to behave incorrectly when using accessors (issues 186, 190
and 191).
2009-01-06: Version 0.4.7
Minor bugfixes and optimizations.
Added command line debugger to D8 shell.
Fixed subtle bug that caused the wrong 'this' to be used when
calling a caught function in a catch clause.
Inline array loads within loops directly in the code instead of
always calling a stub.
2008-12-11: Version 0.4.6
Fixed exception reporting bug where certain exceptions were
incorrectly reported as uncaught.
Improved the memory allocation strategy used during compilation to
make running out of memory when compiling huge scripts less
likely.
Optimized String.replace by avoiding the construction of certain
sub strings.
Fixed bug in code generation for large switch statements on ARM.
Fixed bug that caused V8 to change the global object template
passed in by the user.
Changed the API for creating object groups used during garbage
collection. Entire object groups are now passed to V8 instead of
individual members of the groups.
2008-12-03: Version 0.4.5
Added experimental API support for allocating V8 symbols as
external strings.
Fixed bugs in debugging support on ARM.
Changed eval implementation to correctly detect whether or not a
call to eval is aliased.
Fixed bug caused by a combination of the compilation cache and
dictionary probing in native code. The bug caused us to sometimes
call functions that had not yet been compiled.
Added platform support for FreeBSD.
Added support for building V8 on Windows with either the shared or
static version of MSVCRT
Added the v8::jscre namespace around the jscre functions to avoid
link errors (duplicate symbols) when building Google Chrome.
Added support for calling a JavaScript function with the current
debugger execution context as its argument to the debugger
interface.
Changed the type of names of counters from wchar_t to char.
Changed the Windows system call used to compute daylight savings
time. The system call that we used to use became four times
slower on WinXP SP3.
Added support in the d8 developer shell for memory-mapped counters
and added a stats-viewer tool.
Fixed bug in upper/lower case mappings (issue 149).
2008-11-17: Version 0.4.4
Reduced code size by using shorter instruction encoding when
possible.
Added a --help option to the shell sample and to the d8 shell.
Added visual studio project files for building the ARM simulator.
Fixed a number of ARM simulator issues.
Fixed bug in out-of-memory handling on ARM.
Implemented shell support for passing arguments to a script from
the command line.
Fixed bug in date code that made certain date functions return -0
instead of 0 for dates before the epoch.
Restricted applications of eval so it can only be used in the
context of the associated global object.
Treat byte-order marks as whitespace characters.
2008-11-04: Version 0.4.3
Added support for API accessors that prohibit overwriting by
accessors defined in JavaScript code by using __defineGetter__ and
__defineSetter__.
Improved handling of conditionals in test status files.
Introduced access control in propertyIsEnumerable.
Improved performance of some string operations by caching
information about the type of the string between operations.
Fixed bug in fast-case code for switch statements that only have
integer labels.
2008-10-30: Version 0.4.2
Improved performance of Array.prototype.concat by moving the
implementation to C++ (issue 123).
Fixed heap growth policy to avoid growing old space to its maximum
capacity before doing a garbage collection and fixed issue that
would lead to artificial out of memory situations (issue 129).
Fixed Date.prototype.toLocaleDateString to return the date in the
same format as WebKit.
Added missing initialization checks to debugger API.
Added removing of unused maps during GC.
2008-10-28: Version 0.4.1
Added caching of RegExp data in compilation cache.
Added Visual Studio project file for d8 shell.
Fixed function call performance regression introduced in version
0.4.0 when splitting the global object in two parts (issue 120).
Fixed issue 131 by checking for empty handles before throwing and
reporting exceptions.
2008-10-23: Version 0.4.0
Split the global object into two parts: The state holding global
object and the global object proxy.
Fixed bug that affected the value of an assignment to an element
in certain cases (issue 116).
Added GetPropertyNames functionality (issue 33) and extra Date
functions (issue 77) to the API.
Changed WeakReferenceCallback to take a Persistent<Value> instead
of a Persistent<Object> (issue 101).
Fixed issues with message reporting for exceptions in try-finally
blocks (issues 73 and 75).
Optimized flattening of strings and string equality checking.
Improved Boyer-Moore implementation for faster indexOf operations.
Added development shell (d8) which includes counters and
completion support.
Fixed problem with the receiver passed to functions called from
eval (issue 124).
2008-10-16: Version 0.3.5
Improved string hash-code distribution by excluding bit-field bits
from the hash-code.
Changed string search algorithm used in indexOf from KMP to
Boyer-Moore.
Improved the generated code for the instanceof operator.
Improved performance of slow-case string equality checks by
specializing the code based on the string representation.
Improve the handling of out-of-memory situations (issue 70).
Improved performance of strict equality checks.
Improved profiler output to make it easier to see anonymous
functions.
Improved performance of slow-case keyed loads.
Improved property access performance by allocating a number of
properties in the front object.
Changed the toString behavior on the built-in object constructors
to print [native code] instead of the actual source. Some web
applications do not like constructors with complex toString
results.
2008-10-06: Version 0.3.4
Changed Array.prototype.sort to use quick sort.
Fixed code generation issue where leaving a finally block with
break or continue would accumulate elements on the expression
stack (issue 86).
Made sure that the name accessor on functions returns the expected
names for builtin JavaScript functions and C++ callback functions.
Added fast case code for extending the property storage array of
JavaScript objects.
Ported switch statement optimizations introduced in version 0.3.3
to the ARM code generator.
Allowed GCC to use strict-aliasing rules when compiling.
Improved performance of arguments object allocation by taking care
of arguments adaptor frames in the generated code.
Updated the V8 benchmark suite to version 2.
2008-09-25: Version 0.3.3
Improved handling of relocation information to enable more
peep-hole optimizations.
Optimized switch statements where all labels are constant small
integers.
Optimized String.prototype.indexOf for common cases.
Fixed more build issues (issue 80).
Fixed a couple of profiler issues.
Fixed bug where the body of a function created using the Function
constructor was not allowed to end with a single-line comment
(issue 85).
Improved handling of object literals by canonicalizing object
literal maps. This will allow JSON objects with the same set of
properties to share the same map making inline caching work better
for JSON objects.
2008-09-17: Version 0.3.2
Generalized the EvalCache into a CompilationCache and enabled it
for scripts too. The current strategy is to retire all entries
whenever a mark-sweep collection is started.
Fixed bug where switch statements containing only a default case
would lead to an unbalanced stack (issue 69).
Fixed bug that made access to the function in a named function
expression impossible in certain situations (issue 24).
Fixed even more build issues.
Optimized calling conventions on ARM. The conventions on ARM and
IA-32 now match.
Removed static initializers for flags and counters.
Improved inline caching behavior for uncommon cases where lazily
loading Date and RegExp code could force certain code paths go
megamorphic.
Removed arguments adaption for builtins written in C++. This
makes Array.prototype.push and Array.prototype.pop slightly
faster.
2008-09-11: Version 0.3.1
Fixed a number of build issues.
Fixed problem with missing I-cache flusing on ARM.
Changed space layout in memory management by splitting up
code space into old data space and code space.
Added utf-8 conversion support to the API (issue 57).
Optimized repeated calls to eval with the same strings. These
repeated calls are common in web applications.
Added Xcode project file.
Optimized a couple of Array operation.
Fixed parser bug by checking for end-of-string when parsing break
and continue (issue 35).
Fixed problem where asian characters were not categorized as
letters.
Fixed bug that disallowed calling functions fetched from an array
using a string as an array index (issue 32).
Fixed bug where the internal field count on object templates were
sometimes ignored (issue 54).
Added -f option to the shell sample for compatibility with other
engines (issue 18).
Added source info to TryCatches in the API.
Fixed problem where the seed for the random number generator was
clipped in a double to unsigned int conversion.
Fixed bug where cons string symbols were sometimes converted to
non-symbol flat strings during GC.
Fixed bug in error reporting when attempting to convert null to an
object.
2008-09-04: Version 0.3.0
Added support for running tests on the ARM simulator.
Fixed bug in the 'in' operator where negative indices were not
treated correctly.
Fixed build issues on gcc-4.3.1.
Changed Date.prototype.toLocaleTimeString to not print the
timezone part of the time.
Renamed debug.h to v8-debug.h to reduce the risk of name conflicts
with user code.
2008-09-02: Version 0.2.5
Renamed the top level directory 'public' to 'include'.
Added 'env' option to the SCons build scripts to support
overriding the ENV part of the build environment. This is mostly
to support Windows builds in cases where SCons cannot find the
correct paths to the Windows SDK, as these paths cannot be passed
through shell environment variables.
Enabled "Buffer Security Check" on for the Windows SCons build and
added the linker option /OPT:ICF as an optimization.
Added the V8 benchmark suite to the repository.
2008-09-01: Version 0.2.4
Included mjsunit JavaScript test suite and C++ unit tests.
Changed the shell sample to not print the result of executing a
script provided on the command line.
Fixed issue when building samples on Windows using a shared V8
library. Added visibility option on Linux build which makes the
generated library 18% smaller.
Changed build system to accept multiple build modes in one build
and generate separate objects, libraries and executables for each
mode.
Removed deferred negation optimization (a * -b => -(a * b)) since
this visibly changes operand conversion order.
Improved parsing performance by introducing stack guard in
preparsing. Without a stack guard preparsing always bails out
with stack overflow.
Changed shell sample to take flags directly from the command-line.
Added API call that implements this.
Added load, quit and version functions to the shell sample so it's
easier to run benchmarks and tests.
Fixed issue with building samples and cctests on 64-bit machines.
Fixed bug in the runtime system where the prototype chain was not
always searched for a setter when setting a property that does not
exist locally.
2008-08-14: Version 0.2.3
Improved performance of garbage collection by moving the
function that updates pointers during compacting collection
into the updating visitor. This gives the compiler a better
chance to inline and avoid a function call per (potential)
pointer.
Extended the shell sample with a --runtime-flags option.
Added Visual Studio project files for the shell.cc and
process.cc samples.
2008-08-13: Version 0.2.2
Improved performance of garbage collection by changing the way
we use the marking stack in the event of stack overflow during
full garbage collection and by changing the way we mark roots.
Cleaned up ARM version by removing top of stack caching and by
introducing push/pop elimination.
Cleaned up the way runtime functions are called to allow
runtime calls with no arguments.
Changed Windows build options to make sure that exceptions are
disabled and that optimization flags are enabled.
Added first version of Visual Studio project files.
2008-08-06: Version 0.2.1
Improved performance of unary addition by avoiding runtime calls.
Fixed the handling of '>' and '<=' to use right-to-left conversion
and left-to-right evaluation as specified by ECMA-262.
Fixed a branch elimination bug on the ARM platform where incorrect
code was generated because of overly aggressive branch
elimination.
Improved performance of code that repeatedly assigns the same
function to the same property of different objects with the same
map.
Untangled DEBUG and ENABLE_DISASSEMBLER defines. The disassembler
no longer expects DEBUG to be defined.
Added platform-nullos.cc to serve as the basis for new platform
implementations.
2008-07-30: Version 0.2.0
Changed all text files to have native svn:eol-style.
Added a few samples and support for building them. The samples
include a simple shell that can be used to benchmark and test V8.
Changed V8::GetVersion to return the version as a string.
Added source for lazily loaded scripts to snapshots and made
serialization non-destructive.
Improved ARM support by fixing the write barrier code to use
aligned loads and stores and by removing premature locals
optimization that relied on broken support for callee-saved
registers (removed).
Refactored the code for marking live objects during garbage
collection and the code for allocating objects in paged
spaces. Introduced an abstraction for the map word of a heap-
allocated object and changed the memory allocator to allocate
executable memory only for spaces that may contain code objects.
Moved StringBuilder to utils.h and ScopedLock to platform.h, where
they can be used by debugging and logging modules. Added
thread-safe message queues for dealing with debugger events.
Fixed the source code reported by toString for certain builtin
empty functions and made sure that the prototype property of a
function is enumerable.
Improved performance of converting values to condition flags in
generated code.
Merged disassembler-{arch} files.
2008-07-28: Version 0.1.4
Added support for storing JavaScript stack traces in a stack
allocated buffer to make it visible in shallow core dumps.
Controlled by the --preallocate-message-memory flag which is
disabled by default.
2008-07-25: Version 0.1.3
Fixed bug in JSObject::GetPropertyAttributePostInterceptor where
map transitions would count as properties.
Allowed aliased eval invocations by treating them as evals in the
global context. This may change in the future.
Added support for accessing the last entered context through the
API and renamed Context::Current to Context::GetCurrent and
Context::GetSecurityContext to Context::GetCurrentSecurityContext.
Fixed bug in the debugger that would cause the debugger scripts to
be recursively loaded and changed all disabling of interrupts to
be block-structured.
Made snapshot data read-only to allow it to be more easily shared
across multiple users of V8 when linked as a shared library.
2008-07-16: Version 0.1.2
Fixed building on Mac OS X by recognizing i386 and friends as
IA-32 platforms.
Added propagation of stack overflow exceptions that occur while
compiling nested functions.
Improved debugger with support for recursive break points and
handling of exceptions that occur in the debugger JavaScript code.
Renamed GetInternal to GetInternalField and SetInternal to
SetInternalField in the API and moved InternalFieldCount and
SetInternalFieldCount from FunctionTemplate to ObjectTemplate.
2008-07-09: Version 0.1.1
Fixed bug in stack overflow check code for IA-32 targets where a
non-tagged value in register eax was pushed to the stack.
Fixed potential quadratic behavior when converting strings to
numbers.
Fixed bug where the return value from Object::SetProperty could
end up being the property holder instead of the written value.
Improved debugger support by allowing nested break points and by
dealing with stack-overflows when compiling functions before
setting break points in them.
2008-07-03: Version 0.1.0
Initial export.

51
deps/v8/LICENSE

@ -0,0 +1,51 @@
This license applies to all parts of V8 that are not externally
maintained libraries. The externally maintained libraries used by V8
are:
- PCRE test suite, located in test/mjsunit/regexp-pcre.js. This is
based on the test suite from PCRE-7.3, which is copyrighted by the
University of Cambridge and Google, Inc. The copyright notice and
license are embedded in regexp-pcre.js.
- Dtoa, located under third_party/dtoa. This code is copyrighted by
David M. Gay and released under an MIT license.
- Strongtalk assembler, the basis of the files assembler-arm-inl.h,
assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h.
This code is copyrighted by Sun Microsystems Inc. and released
under a 3-clause BSD license.
- JSMin JavaScript minifier, located at tools/jsmin.py. This code is
copyrighted by Douglas Crockford and Baruch Even and released under
an MIT license.
These libraries have their own licenses; we recommend you read them,
as their terms may differ from the terms below.
Copyright 2006-2009, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

775
deps/v8/SConstruct

@ -0,0 +1,775 @@
# Copyright 2008 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import platform
import re
import sys
import os
from os.path import join, dirname, abspath
from types import DictType, StringTypes
root_dir = dirname(File('SConstruct').rfile().abspath)
sys.path.append(join(root_dir, 'tools'))
import js2c, utils
# ANDROID_TOP is the top of the Android checkout, fetched from the environment
# variable 'TOP'. You will also need to set the CXX, CC, AR and RANLIB
# environment variables to the cross-compiling tools.
ANDROID_TOP = os.environ.get('TOP')
if ANDROID_TOP is None:
ANDROID_TOP=""
ANDROID_FLAGS = ['-march=armv5te',
'-mtune=xscale',
'-msoft-float',
'-fpic',
'-mthumb-interwork',
'-funwind-tables',
'-fstack-protector',
'-fno-short-enums',
'-fmessage-length=0',
'-finline-functions',
'-fno-inline-functions-called-once',
'-fgcse-after-reload',
'-frerun-cse-after-loop',
'-frename-registers',
'-fomit-frame-pointer',
'-fno-strict-aliasing',
'-finline-limit=64',
'-MD']
ANDROID_INCLUDES = [ANDROID_TOP + '/bionic/libc/arch-arm/include',
ANDROID_TOP + '/bionic/libc/include',
ANDROID_TOP + '/bionic/libstdc++/include',
ANDROID_TOP + '/bionic/libc/kernel/common',
ANDROID_TOP + '/bionic/libc/kernel/arch-arm',
ANDROID_TOP + '/bionic/libm/include',
ANDROID_TOP + '/bionic/libm/include/arch/arm',
ANDROID_TOP + '/bionic/libthread_db/include']
ANDROID_LINKFLAGS = ['-nostdlib',
'-Bdynamic',
'-Wl,-T,' + ANDROID_TOP + '/build/core/armelf.x',
'-Wl,-dynamic-linker,/system/bin/linker',
'-Wl,--gc-sections',
'-Wl,-z,nocopyreloc',
'-Wl,-rpath-link=' + ANDROID_TOP + '/out/target/product/generic/obj/lib',
ANDROID_TOP + '/out/target/product/generic/obj/lib/crtbegin_dynamic.o',
ANDROID_TOP + '/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a',
ANDROID_TOP + '/out/target/product/generic/obj/lib/crtend_android.o'];
LIBRARY_FLAGS = {
'all': {
'CPPDEFINES': ['ENABLE_LOGGING_AND_PROFILING']
},
'gcc': {
'all': {
'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'],
'CXXFLAGS': ['$CCFLAGS', '-fno-rtti', '-fno-exceptions'],
},
'mode:debug': {
'CCFLAGS': ['-g', '-O0'],
'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG'],
'os:android': {
'CCFLAGS': ['-mthumb']
}
},
'mode:release': {
'CCFLAGS': ['-O3', '-fomit-frame-pointer', '-fdata-sections',
'-ffunction-sections'],
'os:android': {
'CCFLAGS': ['-mthumb', '-Os'],
'CPPDEFINES': ['SK_RELEASE', 'NDEBUG']
}
},
'os:linux': {
'CCFLAGS': ['-ansi'],
'library:shared': {
'LIBS': ['pthread', 'rt']
}
},
'os:macos': {
'CCFLAGS': ['-ansi'],
},
'os:freebsd': {
'CCFLAGS': ['-ansi'],
},
'os:win32': {
'CCFLAGS': ['-DWIN32'],
'CXXFLAGS': ['-DWIN32'],
},
'os:android': {
'CPPDEFINES': ['ANDROID', '__ARM_ARCH_5__', '__ARM_ARCH_5T__',
'__ARM_ARCH_5E__', '__ARM_ARCH_5TE__'],
'CCFLAGS': ANDROID_FLAGS,
'WARNINGFLAGS': ['-Wall', '-Wno-unused', '-Werror=return-type',
'-Wstrict-aliasing=2'],
'CPPPATH': ANDROID_INCLUDES,
},
'wordsize:64': {
'CCFLAGS': ['-m32'],
'LINKFLAGS': ['-m32']
},
'prof:oprofile': {
'CPPDEFINES': ['ENABLE_OPROFILE_AGENT']
}
},
'msvc': {
'all': {
'DIALECTFLAGS': ['/nologo'],
'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'],
'CXXFLAGS': ['$CCFLAGS', '/GR-', '/Gy'],
'CPPDEFINES': ['WIN32', '_USE_32BIT_TIME_T'],
'LINKFLAGS': ['/NOLOGO', '/MACHINE:X86', '/INCREMENTAL:NO',
'/NXCOMPAT', '/IGNORE:4221'],
'ARFLAGS': ['/NOLOGO'],
'CCPDBFLAGS': ['/Zi']
},
'mode:debug': {
'CCFLAGS': ['/Od', '/Gm'],
'CPPDEFINES': ['_DEBUG', 'ENABLE_DISASSEMBLER', 'DEBUG'],
'LINKFLAGS': ['/DEBUG'],
'msvcrt:static': {
'CCFLAGS': ['/MTd']
},
'msvcrt:shared': {
'CCFLAGS': ['/MDd']
}
},
'mode:release': {
'CCFLAGS': ['/O2', '/GL'],
'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG'],
'ARFLAGS': ['/LTCG'],
'msvcrt:static': {
'CCFLAGS': ['/MT']
},
'msvcrt:shared': {
'CCFLAGS': ['/MD']
}
},
}
}
V8_EXTRA_FLAGS = {
'gcc': {
'all': {
'CXXFLAGS': [], #['-fvisibility=hidden'],
'WARNINGFLAGS': ['-Wall', '-Werror', '-W',
'-Wno-unused-parameter']
},
'arch:arm': {
'CPPDEFINES': ['ARM']
},
'arch:android': {
'CPPDEFINES': ['ARM']
},
'os:win32': {
'WARNINGFLAGS': ['-pedantic', '-Wno-long-long']
},
'os:linux': {
'WARNINGFLAGS': ['-pedantic']
},
'os:macos': {
'WARNINGFLAGS': ['-pedantic']
},
'disassembler:on': {
'CPPDEFINES': ['ENABLE_DISASSEMBLER']
}
},
'msvc': {
'all': {
'WARNINGFLAGS': ['/W3', '/WX', '/wd4355', '/wd4800']
},
'library:shared': {
'CPPDEFINES': ['BUILDING_V8_SHARED'],
'LIBS': ['winmm', 'ws2_32']
},
'arch:arm': {
'CPPDEFINES': ['ARM'],
# /wd4996 is to silence the warning about sscanf
# used by the arm simulator.
'WARNINGFLAGS': ['/wd4996']
},
'disassembler:on': {
'CPPDEFINES': ['ENABLE_DISASSEMBLER']
}
}
}
MKSNAPSHOT_EXTRA_FLAGS = {
'gcc': {
'os:linux': {
'LIBS': ['pthread', 'rt'],
},
'os:macos': {
'LIBS': ['pthread'],
},
'os:freebsd': {
'LIBS': ['pthread'],
},
'os:win32': {
'LIBS': ['winmm', 'ws2_32'],
},
},
'msvc': {
'all': {
'LIBS': ['winmm', 'ws2_32']
}
}
}
DTOA_EXTRA_FLAGS = {
'gcc': {
'all': {
'WARNINGFLAGS': ['-Werror', '-Wno-uninitialized']
}
},
'msvc': {
'all': {
'WARNINGFLAGS': ['/WX', '/wd4018', '/wd4244']
}
}
}
CCTEST_EXTRA_FLAGS = {
'all': {
'CPPPATH': [join(root_dir, 'src')],
'LIBS': ['$LIBRARY']
},
'gcc': {
'all': {
'LIBPATH': [abspath('.')]
},
'os:linux': {
'LIBS': ['pthread', 'rt'],
},
'os:macos': {
'LIBS': ['pthread'],
},
'os:freebsd': {
'LIBS': ['execinfo', 'pthread']
},
'os:win32': {
'LIBS': ['winmm', 'ws2_32']
},
'wordsize:64': {
'CCFLAGS': ['-m32'],
'LINKFLAGS': ['-m32']
},
},
'msvc': {
'all': {
'CPPDEFINES': ['_HAS_EXCEPTIONS=0'],
'LIBS': ['winmm', 'ws2_32']
},
'library:shared': {
'CPPDEFINES': ['USING_V8_SHARED']
}
}
}
SAMPLE_FLAGS = {
'all': {
'CPPPATH': [join(abspath('.'), 'include')],
'LIBS': ['$LIBRARY'],
},
'gcc': {
'all': {
'LIBPATH': ['.'],
'CCFLAGS': ['-fno-rtti', '-fno-exceptions']
},
'os:linux': {
'LIBS': ['pthread', 'rt'],
},
'os:macos': {
'LIBS': ['pthread'],
},
'os:freebsd': {
'LIBS': ['execinfo', 'pthread']
},
'os:win32': {
'LIBS': ['winmm', 'ws2_32']
},
'os:android': {
'CPPDEFINES': ['ANDROID', '__ARM_ARCH_5__', '__ARM_ARCH_5T__',
'__ARM_ARCH_5E__', '__ARM_ARCH_5TE__'],
'CCFLAGS': ANDROID_FLAGS,
'CPPPATH': ANDROID_INCLUDES,
'LIBPATH': [ANDROID_TOP + '/out/target/product/generic/obj/lib'],
'LINKFLAGS': ANDROID_LINKFLAGS,
'LIBS': ['c', 'stdc++', 'm'],
'mode:release': {
'CPPDEFINES': ['SK_RELEASE', 'NDEBUG']
}
},
'wordsize:64': {
'CCFLAGS': ['-m32'],
'LINKFLAGS': ['-m32']
},
'mode:release': {
'CCFLAGS': ['-O2']
},
'mode:debug': {
'CCFLAGS': ['-g', '-O0']
},
'prof:oprofile': {
'LIBPATH': ['/usr/lib32', '/usr/lib32/oprofile'],
'LIBS': ['opagent']
}
},
'msvc': {
'all': {
'CCFLAGS': ['/nologo'],
'LINKFLAGS': ['/nologo'],
'LIBS': ['winmm', 'ws2_32']
},
'library:shared': {
'CPPDEFINES': ['USING_V8_SHARED']
},
'prof:on': {
'LINKFLAGS': ['/MAP']
},
'mode:release': {
'CCFLAGS': ['/O2'],
'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG'],
'msvcrt:static': {
'CCFLAGS': ['/MT']
},
'msvcrt:shared': {
'CCFLAGS': ['/MD']
}
},
'mode:debug': {
'CCFLAGS': ['/Od'],
'LINKFLAGS': ['/DEBUG'],
'msvcrt:static': {
'CCFLAGS': ['/MTd']
},
'msvcrt:shared': {
'CCFLAGS': ['/MDd']
}
}
}
}
D8_FLAGS = {
'gcc': {
'console:readline': {
'LIBS': ['readline']
},
'os:linux': {
'LIBS': ['pthread', 'rt'],
},
'os:macos': {
'LIBS': ['pthread'],
},
'os:freebsd': {
'LIBS': ['pthread'],
},
'os:android': {
'LIBPATH': [ANDROID_TOP + '/out/target/product/generic/obj/lib'],
'LINKFLAGS': ANDROID_LINKFLAGS,
'LIBS': ['c', 'stdc++', 'm'],
},
'os:win32': {
'LIBS': ['winmm', 'ws2_32'],
},
},
'msvc': {
'all': {
'LIBS': ['winmm', 'ws2_32']
}
}
}
SUFFIXES = {
'release': '',
'debug': '_g'
}
def Abort(message):
print message
sys.exit(1)
def GuessToolchain(os):
tools = Environment()['TOOLS']
if 'gcc' in tools:
return 'gcc'
elif 'msvc' in tools:
return 'msvc'
else:
return None
OS_GUESS = utils.GuessOS()
TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS)
ARCH_GUESS = utils.GuessArchitecture()
WORDSIZE_GUESS = utils.GuessWordsize()
SIMPLE_OPTIONS = {
'toolchain': {
'values': ['gcc', 'msvc'],
'default': TOOLCHAIN_GUESS,
'help': 'the toolchain to use'
},
'os': {
'values': ['freebsd', 'linux', 'macos', 'win32', 'android'],
'default': OS_GUESS,
'help': 'the os to build for'
},
'arch': {
'values':['arm', 'ia32'],
'default': ARCH_GUESS,
'help': 'the architecture to build for'
},
'snapshot': {
'values': ['on', 'off', 'nobuild'],
'default': 'off',
'help': 'build using snapshots for faster start-up'
},
'prof': {
'values': ['on', 'off', 'oprofile'],
'default': 'off',
'help': 'enable profiling of build target'
},
'library': {
'values': ['static', 'shared'],
'default': 'static',
'help': 'the type of library to produce'
},
'msvcrt': {
'values': ['static', 'shared'],
'default': 'static',
'help': 'the type of MSVCRT library to use'
},
'wordsize': {
'values': ['64', '32'],
'default': WORDSIZE_GUESS,
'help': 'the word size'
},
'simulator': {
'values': ['arm', 'none'],
'default': 'none',
'help': 'build with simulator'
},
'disassembler': {
'values': ['on', 'off'],
'default': 'off',
'help': 'enable the disassembler to inspect generated code'
},
'sourcesignatures': {
'values': ['MD5', 'timestamp'],
'default': 'MD5',
'help': 'set how the build system detects file changes'
},
'console': {
'values': ['dumb', 'readline'],
'default': 'dumb',
'help': 'the console to use for the d8 shell'
}
}
def GetOptions():
result = Options()
result.Add('mode', 'compilation mode (debug, release)', 'release')
result.Add('sample', 'build sample (shell, process)', '')
result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '')
result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '')
for (name, option) in SIMPLE_OPTIONS.iteritems():
help = '%s (%s)' % (name, ", ".join(option['values']))
result.Add(name, help, option.get('default'))
return result
def SplitList(str):
return [ s for s in str.split(",") if len(s) > 0 ]
def IsLegal(env, option, values):
str = env[option]
for s in SplitList(str):
if not s in values:
Abort("Illegal value for option %s '%s'." % (option, s))
return False
return True
def VerifyOptions(env):
if not IsLegal(env, 'mode', ['debug', 'release']):
return False
if not IsLegal(env, 'sample', ["shell", "process"]):
return False
if env['os'] == 'win32' and env['library'] == 'shared' and env['prof'] == 'on':
Abort("Profiling on windows only supported for static library.")
if env['prof'] == 'oprofile' and env['os'] != 'linux':
Abort("OProfile is only supported on Linux.")
for (name, option) in SIMPLE_OPTIONS.iteritems():
if (not option.get('default')) and (name not in ARGUMENTS):
message = ("A value for option %s must be specified (%s)." %
(name, ", ".join(option['values'])))
Abort(message)
if not env[name] in option['values']:
message = ("Unknown %s value '%s'. Possible values are (%s)." %
(name, env[name], ", ".join(option['values'])))
Abort(message)
class BuildContext(object):
def __init__(self, options, env_overrides, samples):
self.library_targets = []
self.mksnapshot_targets = []
self.cctest_targets = []
self.sample_targets = []
self.d8_targets = []
self.options = options
self.env_overrides = env_overrides
self.samples = samples
self.use_snapshot = (options['snapshot'] != 'off')
self.build_snapshot = (options['snapshot'] == 'on')
self.flags = None
def AddRelevantFlags(self, initial, flags):
result = initial.copy()
self.AppendFlags(result, flags.get('all'))
toolchain = self.options['toolchain']
if toolchain in flags:
self.AppendFlags(result, flags[toolchain].get('all'))
for option in sorted(self.options.keys()):
value = self.options[option]
self.AppendFlags(result, flags[toolchain].get(option + ':' + value))
return result
def AddRelevantSubFlags(self, options, flags):
self.AppendFlags(options, flags.get('all'))
for option in sorted(self.options.keys()):
value = self.options[option]
self.AppendFlags(options, flags.get(option + ':' + value))
def GetRelevantSources(self, source):
result = []
result += source.get('all', [])
for (name, value) in self.options.iteritems():
result += source.get(name + ':' + value, [])
return sorted(result)
def AppendFlags(self, options, added):
if not added:
return
for (key, value) in added.iteritems():
if key.find(':') != -1:
self.AddRelevantSubFlags(options, { key: value })
else:
if not key in options:
options[key] = value
else:
prefix = options[key]
if isinstance(prefix, StringTypes): prefix = prefix.split()
options[key] = prefix + value
def ConfigureObject(self, env, input, **kw):
if (kw.has_key('CPPPATH') and env.has_key('CPPPATH')):
kw['CPPPATH'] += env['CPPPATH']
if self.options['library'] == 'static':
return env.StaticObject(input, **kw)
else:
return env.SharedObject(input, **kw)
def ApplyEnvOverrides(self, env):
if not self.env_overrides:
return
if type(env['ENV']) == DictType:
env['ENV'].update(**self.env_overrides)
else:
env['ENV'] = self.env_overrides
def PostprocessOptions(options):
# Adjust architecture if the simulator option has been set
if (options['simulator'] != 'none') and (options['arch'] != options['simulator']):
if 'arch' in ARGUMENTS:
# Print a warning if arch has explicitly been set
print "Warning: forcing architecture to match simulator (%s)" % options['simulator']
options['arch'] = options['simulator']
def ParseEnvOverrides(arg, imports):
# The environment overrides are in the format NAME0:value0,NAME1:value1,...
# The environment imports are in the format NAME0,NAME1,...
overrides = {}
for var in imports.split(','):
if var in os.environ:
overrides[var] = os.environ[var]
for override in arg.split(','):
pos = override.find(':')
if pos == -1:
continue
overrides[override[:pos].strip()] = override[pos+1:].strip()
return overrides
def BuildSpecific(env, mode, env_overrides):
options = {'mode': mode}
for option in SIMPLE_OPTIONS:
options[option] = env[option]
PostprocessOptions(options)
context = BuildContext(options, env_overrides, samples=SplitList(env['sample']))
library_flags = context.AddRelevantFlags(os.environ, LIBRARY_FLAGS)
v8_flags = context.AddRelevantFlags(library_flags, V8_EXTRA_FLAGS)
mksnapshot_flags = context.AddRelevantFlags(library_flags, MKSNAPSHOT_EXTRA_FLAGS)
dtoa_flags = context.AddRelevantFlags(library_flags, DTOA_EXTRA_FLAGS)
cctest_flags = context.AddRelevantFlags(v8_flags, CCTEST_EXTRA_FLAGS)
sample_flags = context.AddRelevantFlags(os.environ, SAMPLE_FLAGS)
d8_flags = context.AddRelevantFlags(library_flags, D8_FLAGS)
context.flags = {
'v8': v8_flags,
'mksnapshot': mksnapshot_flags,
'dtoa': dtoa_flags,
'cctest': cctest_flags,
'sample': sample_flags,
'd8': d8_flags
}
target_id = mode
suffix = SUFFIXES[target_id]
library_name = 'v8' + suffix
env['LIBRARY'] = library_name
# Build the object files by invoking SCons recursively.
(object_files, shell_files, mksnapshot) = env.SConscript(
join('src', 'SConscript'),
build_dir=join('obj', target_id),
exports='context',
duplicate=False
)
context.mksnapshot_targets.append(mksnapshot)
# Link the object files into a library.
env.Replace(**context.flags['v8'])
context.ApplyEnvOverrides(env)
if context.options['library'] == 'static':
library = env.StaticLibrary(library_name, object_files)
else:
# There seems to be a glitch in the way scons decides where to put
# PDB files when compiling using MSVC so we specify it manually.
# This should not affect any other platforms.
pdb_name = library_name + '.dll.pdb'
library = env.SharedLibrary(library_name, object_files, PDB=pdb_name)
context.library_targets.append(library)
d8_env = Environment()
d8_env.Replace(**context.flags['d8'])
shell = d8_env.Program('d8' + suffix, object_files + shell_files)
context.d8_targets.append(shell)
for sample in context.samples:
sample_env = Environment(LIBRARY=library_name)
sample_env.Replace(**context.flags['sample'])
context.ApplyEnvOverrides(sample_env)
sample_object = sample_env.SConscript(
join('samples', 'SConscript'),
build_dir=join('obj', 'sample', sample, target_id),
exports='sample context',
duplicate=False
)
sample_name = sample + suffix
sample_program = sample_env.Program(sample_name, sample_object)
sample_env.Depends(sample_program, library)
context.sample_targets.append(sample_program)
cctest_program = env.SConscript(
join('test', 'cctest', 'SConscript'),
build_dir=join('obj', 'test', target_id),
exports='context object_files',
duplicate=False
)
context.cctest_targets.append(cctest_program)
return context
def Build():
opts = GetOptions()
env = Environment(options=opts)
Help(opts.GenerateHelpText(env))
VerifyOptions(env)
env_overrides = ParseEnvOverrides(env['env'], env['importenv'])
SourceSignatures(env['sourcesignatures'])
libraries = []
mksnapshots = []
cctests = []
samples = []
d8s = []
modes = SplitList(env['mode'])
for mode in modes:
context = BuildSpecific(env.Copy(), mode, env_overrides)
libraries += context.library_targets
mksnapshots += context.mksnapshot_targets
cctests += context.cctest_targets
samples += context.sample_targets
d8s += context.d8_targets
env.Alias('library', libraries)
env.Alias('mksnapshot', mksnapshots)
env.Alias('cctests', cctests)
env.Alias('sample', samples)
env.Alias('d8', d8s)
if env['sample']:
env.Default('sample')
else:
env.Default('library')
# We disable deprecation warnings because we need to be able to use
# env.Copy without getting warnings for compatibility with older
# version of scons. Also, there's a bug in some revisions that
# doesn't allow this flag to be set, so we swallow any exceptions.
# Lovely.
try:
SetOption('warn', 'no-deprecated')
except:
pass
Build()

42
deps/v8/benchmarks/README.txt

@ -0,0 +1,42 @@
V8 Benchmark Suite
==================
This is the V8 benchmark suite: A collection of pure JavaScript
benchmarks that we have used to tune V8. The licenses for the
individual benchmarks are included in the JavaScript files.
In addition to the benchmarks, the suite consists of the benchmark
framework (base.js), which must be loaded before any of the individual
benchmark files, and two benchmark runners: An HTML version (run.html)
and a standalone JavaScript version (run.js).
Changes From Version 1 To Version 2
===================================
For version 2 the crypto benchmark was fixed. Previously, the
decryption stage was given plaintext as input, which resulted in an
error. Now, the decryption stage is given the output of the
encryption stage as input. The result is checked against the original
plaintext. For this to give the correct results the crypto objects
are reset for each iteration of the benchmark. In addition, the size
of the plain text has been increased a little and the use of
Math.random() and new Date() to build an RNG pool has been removed.
Other benchmarks were fixed to do elementary verification of the
results of their calculations. This is to avoid accidentally
obtaining scores that are the result of an incorrect JavaScript engine
optimization.
Changes From Version 2 To Version 3
===================================
Version 3 adds a new benchmark, RegExp. The RegExp benchmark is
generated by loading 50 of the most popular pages on the web and
logging all regexp operations performed. Each operation is given a
weight that is calculated from an estimate of the popularity of the
pages where it occurs and the number of times it is executed while
loading each page. Finally the literal letters in the data are
encoded using ROT13 in a way that does not affect how the regexps
match their input.

233
deps/v8/benchmarks/base.js

@ -0,0 +1,233 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Simple framework for running the benchmark suites and
// computing a score based on the timing measurements.
// A benchmark has a name (string) and a function that will be run to
// do the performance measurement.
function Benchmark(name, run) {
this.name = name;
this.run = run;
}
// Benchmark results hold the benchmark and the measured time used to
// run the benchmark. The benchmark score is computed later once a
// full benchmark suite has run to completion.
function BenchmarkResult(benchmark, time) {
this.benchmark = benchmark;
this.time = time;
}
// Automatically convert results to numbers. Used by the geometric
// mean computation.
BenchmarkResult.prototype.valueOf = function() {
return this.time;
}
// Suites of benchmarks consist of a name and the set of benchmarks in
// addition to the reference timing that the final score will be based
// on. This way, all scores are relative to a reference run and higher
// scores implies better performance.
function BenchmarkSuite(name, reference, benchmarks) {
this.name = name;
this.reference = reference;
this.benchmarks = benchmarks;
BenchmarkSuite.suites.push(this);
}
// Keep track of all declared benchmark suites.
BenchmarkSuite.suites = [];
// Scores are not comparable across versions. Bump the version if
// you're making changes that will affect that scores, e.g. if you add
// a new benchmark or change an existing one.
BenchmarkSuite.version = '3';
// To make the benchmark results predictable, we replace Math.random
// with a 100% deterministic alternative.
Math.random = (function() {
var seed = 49734321;
return function() {
// Robert Jenkins' 32 bit integer hash function.
seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
return (seed & 0xfffffff) / 0x10000000;
};
})();
// Runs all registered benchmark suites and optionally yields between
// each individual benchmark to avoid running for too long in the
// context of browsers. Once done, the final score is reported to the
// runner.
BenchmarkSuite.RunSuites = function(runner) {
var continuation = null;
var suites = BenchmarkSuite.suites;
var length = suites.length;
BenchmarkSuite.scores = [];
var index = 0;
function RunStep() {
while (continuation || index < length) {
if (continuation) {
continuation = continuation();
} else {
var suite = suites[index++];
if (runner.NotifyStart) runner.NotifyStart(suite.name);
continuation = suite.RunStep(runner);
}
if (continuation && typeof window != 'undefined' && window.setTimeout) {
window.setTimeout(RunStep, 100);
return;
}
}
if (runner.NotifyScore) {
var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
var formatted = BenchmarkSuite.FormatScore(100 * score);
runner.NotifyScore(formatted);
}
}
RunStep();
}
// Counts the total number of registered benchmarks. Useful for
// showing progress as a percentage.
BenchmarkSuite.CountBenchmarks = function() {
var result = 0;
var suites = BenchmarkSuite.suites;
for (var i = 0; i < suites.length; i++) {
result += suites[i].benchmarks.length;
}
return result;
}
// Computes the geometric mean of a set of numbers.
BenchmarkSuite.GeometricMean = function(numbers) {
var log = 0;
for (var i = 0; i < numbers.length; i++) {
log += Math.log(numbers[i]);
}
return Math.pow(Math.E, log / numbers.length);
}
// Converts a score value to a string with at least three significant
// digits.
BenchmarkSuite.FormatScore = function(value) {
if (value > 100) {
return value.toFixed(0);
} else {
return value.toPrecision(3);
}
}
// Notifies the runner that we're done running a single benchmark in
// the benchmark suite. This can be useful to report progress.
BenchmarkSuite.prototype.NotifyStep = function(result) {
this.results.push(result);
if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);
}
// Notifies the runner that we're done with running a suite and that
// we have a result which can be reported to the user if needed.
BenchmarkSuite.prototype.NotifyResult = function() {
var mean = BenchmarkSuite.GeometricMean(this.results);
var score = this.reference / mean;
BenchmarkSuite.scores.push(score);
if (this.runner.NotifyResult) {
var formatted = BenchmarkSuite.FormatScore(100 * score);
this.runner.NotifyResult(this.name, formatted);
}
}
// Notifies the runner that running a benchmark resulted in an error.
BenchmarkSuite.prototype.NotifyError = function(error) {
if (this.runner.NotifyError) {
this.runner.NotifyError(this.name, error);
}
if (this.runner.NotifyStep) {
this.runner.NotifyStep(this.name);
}
}
// Runs a single benchmark for at least a second and computes the
// average time it takes to run a single iteration.
BenchmarkSuite.prototype.RunSingle = function(benchmark) {
var elapsed = 0;
var start = new Date();
for (var n = 0; elapsed < 1000; n++) {
benchmark.run();
elapsed = new Date() - start;
}
var usec = (elapsed * 1000) / n;
this.NotifyStep(new BenchmarkResult(benchmark, usec));
}
// This function starts running a suite, but stops between each
// individual benchmark in the suite and returns a continuation
// function which can be invoked to run the next benchmark. Once the
// last benchmark has been executed, null is returned.
BenchmarkSuite.prototype.RunStep = function(runner) {
this.results = [];
this.runner = runner;
var length = this.benchmarks.length;
var index = 0;
var suite = this;
function RunNext() {
if (index < length) {
try {
suite.RunSingle(suite.benchmarks[index++]);
} catch (e) {
suite.NotifyError(e);
return null;
}
return RunNext;
}
suite.NotifyResult();
return null;
}
return RunNext();
}

1698
deps/v8/benchmarks/crypto.js

File diff suppressed because it is too large

880
deps/v8/benchmarks/deltablue.js

@ -0,0 +1,880 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Copyright 1996 John Maloney and Mario Wolczko.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// This implementation of the DeltaBlue benchmark is derived
// from the Smalltalk implementation by John Maloney and Mario
// Wolczko. Some parts have been translated directly, whereas
// others have been modified more aggresively to make it feel
// more like a JavaScript program.
var DeltaBlue = new BenchmarkSuite('DeltaBlue', 71104, [
new Benchmark('DeltaBlue', deltaBlue)
]);
/**
* A JavaScript implementation of the DeltaBlue constrain-solving
* algorithm, as described in:
*
* "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
* Bjorn N. Freeman-Benson and John Maloney
* January 1990 Communications of the ACM,
* also available as University of Washington TR 89-08-06.
*
* Beware: this benchmark is written in a grotesque style where
* the constraint model is built by side-effects from constructors.
* I've kept it this way to avoid deviating too much from the original
* implementation.
*/
/* --- O b j e c t M o d e l --- */
Object.prototype.inherits = function (shuper) {
function Inheriter() { }
Inheriter.prototype = shuper.prototype;
this.prototype = new Inheriter();
this.superConstructor = shuper;
}
function OrderedCollection() {
this.elms = new Array();
}
OrderedCollection.prototype.add = function (elm) {
this.elms.push(elm);
}
OrderedCollection.prototype.at = function (index) {
return this.elms[index];
}
OrderedCollection.prototype.size = function () {
return this.elms.length;
}
OrderedCollection.prototype.removeFirst = function () {
return this.elms.pop();
}
OrderedCollection.prototype.remove = function (elm) {
var index = 0, skipped = 0;
for (var i = 0; i < this.elms.length; i++) {
var value = this.elms[i];
if (value != elm) {
this.elms[index] = value;
index++;
} else {
skipped++;
}
}
for (var i = 0; i < skipped; i++)
this.elms.pop();
}
/* --- *
* S t r e n g t h
* --- */
/**
* Strengths are used to measure the relative importance of constraints.
* New strengths may be inserted in the strength hierarchy without
* disrupting current constraints. Strengths cannot be created outside
* this class, so pointer comparison can be used for value comparison.
*/
function Strength(strengthValue, name) {
this.strengthValue = strengthValue;
this.name = name;
}
Strength.stronger = function (s1, s2) {
return s1.strengthValue < s2.strengthValue;
}
Strength.weaker = function (s1, s2) {
return s1.strengthValue > s2.strengthValue;
}
Strength.weakestOf = function (s1, s2) {
return this.weaker(s1, s2) ? s1 : s2;
}
Strength.strongest = function (s1, s2) {
return this.stronger(s1, s2) ? s1 : s2;
}
Strength.prototype.nextWeaker = function () {
switch (this.strengthValue) {
case 0: return Strength.WEAKEST;
case 1: return Strength.WEAK_DEFAULT;
case 2: return Strength.NORMAL;
case 3: return Strength.STRONG_DEFAULT;
case 4: return Strength.PREFERRED;
case 5: return Strength.REQUIRED;
}
}
// Strength constants.
Strength.REQUIRED = new Strength(0, "required");
Strength.STONG_PREFERRED = new Strength(1, "strongPreferred");
Strength.PREFERRED = new Strength(2, "preferred");
Strength.STRONG_DEFAULT = new Strength(3, "strongDefault");
Strength.NORMAL = new Strength(4, "normal");
Strength.WEAK_DEFAULT = new Strength(5, "weakDefault");
Strength.WEAKEST = new Strength(6, "weakest");
/* --- *
* C o n s t r a i n t
* --- */
/**
* An abstract class representing a system-maintainable relationship
* (or "constraint") between a set of variables. A constraint supplies
* a strength instance variable; concrete subclasses provide a means
* of storing the constrained variables and other information required
* to represent a constraint.
*/
function Constraint(strength) {
this.strength = strength;
}
/**
* Activate this constraint and attempt to satisfy it.
*/
Constraint.prototype.addConstraint = function () {
this.addToGraph();
planner.incrementalAdd(this);
}
/**
* Attempt to find a way to enforce this constraint. If successful,
* record the solution, perhaps modifying the current dataflow
* graph. Answer the constraint that this constraint overrides, if
* there is one, or nil, if there isn't.
* Assume: I am not already satisfied.
*/
Constraint.prototype.satisfy = function (mark) {
this.chooseMethod(mark);
if (!this.isSatisfied()) {
if (this.strength == Strength.REQUIRED)
alert("Could not satisfy a required constraint!");
return null;
}
this.markInputs(mark);
var out = this.output();
var overridden = out.determinedBy;
if (overridden != null) overridden.markUnsatisfied();
out.determinedBy = this;
if (!planner.addPropagate(this, mark))
alert("Cycle encountered");
out.mark = mark;
return overridden;
}
Constraint.prototype.destroyConstraint = function () {
if (this.isSatisfied()) planner.incrementalRemove(this);
else this.removeFromGraph();
}
/**
* Normal constraints are not input constraints. An input constraint
* is one that depends on external state, such as the mouse, the
* keybord, a clock, or some arbitraty piece of imperative code.
*/
Constraint.prototype.isInput = function () {
return false;
}
/* --- *
* U n a r y C o n s t r a i n t
* --- */
/**
* Abstract superclass for constraints having a single possible output
* variable.
*/
function UnaryConstraint(v, strength) {
UnaryConstraint.superConstructor.call(this, strength);
this.myOutput = v;
this.satisfied = false;
this.addConstraint();
}
UnaryConstraint.inherits(Constraint);
/**
* Adds this constraint to the constraint graph
*/
UnaryConstraint.prototype.addToGraph = function () {
this.myOutput.addConstraint(this);
this.satisfied = false;
}
/**
* Decides if this constraint can be satisfied and records that
* decision.
*/
UnaryConstraint.prototype.chooseMethod = function (mark) {
this.satisfied = (this.myOutput.mark != mark)
&& Strength.stronger(this.strength, this.myOutput.walkStrength);
}
/**
* Returns true if this constraint is satisfied in the current solution.
*/
UnaryConstraint.prototype.isSatisfied = function () {
return this.satisfied;
}
UnaryConstraint.prototype.markInputs = function (mark) {
// has no inputs
}
/**
* Returns the current output variable.
*/
UnaryConstraint.prototype.output = function () {
return this.myOutput;
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this constraint. Assume
* this constraint is satisfied.
*/
UnaryConstraint.prototype.recalculate = function () {
this.myOutput.walkStrength = this.strength;
this.myOutput.stay = !this.isInput();
if (this.myOutput.stay) this.execute(); // Stay optimization
}
/**
* Records that this constraint is unsatisfied
*/
UnaryConstraint.prototype.markUnsatisfied = function () {
this.satisfied = false;
}
UnaryConstraint.prototype.inputsKnown = function () {
return true;
}
UnaryConstraint.prototype.removeFromGraph = function () {
if (this.myOutput != null) this.myOutput.removeConstraint(this);
this.satisfied = false;
}
/* --- *
* S t a y C o n s t r a i n t
* --- */
/**
* Variables that should, with some level of preference, stay the same.
* Planners may exploit the fact that instances, if satisfied, will not
* change their output during plan execution. This is called "stay
* optimization".
*/
function StayConstraint(v, str) {
StayConstraint.superConstructor.call(this, v, str);
}
StayConstraint.inherits(UnaryConstraint);
StayConstraint.prototype.execute = function () {
// Stay constraints do nothing
}
/* --- *
* E d i t C o n s t r a i n t
* --- */
/**
* A unary input constraint used to mark a variable that the client
* wishes to change.
*/
function EditConstraint(v, str) {
EditConstraint.superConstructor.call(this, v, str);
}
EditConstraint.inherits(UnaryConstraint);
/**
* Edits indicate that a variable is to be changed by imperative code.
*/
EditConstraint.prototype.isInput = function () {
return true;
}
EditConstraint.prototype.execute = function () {
// Edit constraints do nothing
}
/* --- *
* B i n a r y C o n s t r a i n t
* --- */
var Direction = new Object();
Direction.NONE = 0;
Direction.FORWARD = 1;
Direction.BACKWARD = -1;
/**
* Abstract superclass for constraints having two possible output
* variables.
*/
function BinaryConstraint(var1, var2, strength) {
BinaryConstraint.superConstructor.call(this, strength);
this.v1 = var1;
this.v2 = var2;
this.direction = Direction.NONE;
this.addConstraint();
}
BinaryConstraint.inherits(Constraint);
/**
* Decides if this constratint can be satisfied and which way it
* should flow based on the relative strength of the variables related,
* and record that decision.
*/
BinaryConstraint.prototype.chooseMethod = function (mark) {
if (this.v1.mark == mark) {
this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
? Direction.FORWARD
: Direction.NONE;
}
if (this.v2.mark == mark) {
this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
? Direction.BACKWARD
: Direction.NONE;
}
if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
? Direction.BACKWARD
: Direction.NONE;
} else {
this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
? Direction.FORWARD
: Direction.BACKWARD
}
}
/**
* Add this constraint to the constraint graph
*/
BinaryConstraint.prototype.addToGraph = function () {
this.v1.addConstraint(this);
this.v2.addConstraint(this);
this.direction = Direction.NONE;
}
/**
* Answer true if this constraint is satisfied in the current solution.
*/
BinaryConstraint.prototype.isSatisfied = function () {
return this.direction != Direction.NONE;
}
/**
* Mark the input variable with the given mark.
*/
BinaryConstraint.prototype.markInputs = function (mark) {
this.input().mark = mark;
}
/**
* Returns the current input variable
*/
BinaryConstraint.prototype.input = function () {
return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
}
/**
* Returns the current output variable
*/
BinaryConstraint.prototype.output = function () {
return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this
* constraint. Assume this constraint is satisfied.
*/
BinaryConstraint.prototype.recalculate = function () {
var ihn = this.input(), out = this.output();
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
out.stay = ihn.stay;
if (out.stay) this.execute();
}
/**
* Record the fact that this constraint is unsatisfied.
*/
BinaryConstraint.prototype.markUnsatisfied = function () {
this.direction = Direction.NONE;
}
BinaryConstraint.prototype.inputsKnown = function (mark) {
var i = this.input();
return i.mark == mark || i.stay || i.determinedBy == null;
}
BinaryConstraint.prototype.removeFromGraph = function () {
if (this.v1 != null) this.v1.removeConstraint(this);
if (this.v2 != null) this.v2.removeConstraint(this);
this.direction = Direction.NONE;
}
/* --- *
* S c a l e C o n s t r a i n t
* --- */
/**
* Relates two variables by the linear scaling relationship: "v2 =
* (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
* this relationship but the scale factor and offset are considered
* read-only.
*/
function ScaleConstraint(src, scale, offset, dest, strength) {
this.direction = Direction.NONE;
this.scale = scale;
this.offset = offset;
ScaleConstraint.superConstructor.call(this, src, dest, strength);
}
ScaleConstraint.inherits(BinaryConstraint);
/**
* Adds this constraint to the constraint graph.
*/
ScaleConstraint.prototype.addToGraph = function () {
ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
this.scale.addConstraint(this);
this.offset.addConstraint(this);
}
ScaleConstraint.prototype.removeFromGraph = function () {
ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
if (this.scale != null) this.scale.removeConstraint(this);
if (this.offset != null) this.offset.removeConstraint(this);
}
ScaleConstraint.prototype.markInputs = function (mark) {
ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
this.scale.mark = this.offset.mark = mark;
}
/**
* Enforce this constraint. Assume that it is satisfied.
*/
ScaleConstraint.prototype.execute = function () {
if (this.direction == Direction.FORWARD) {
this.v2.value = this.v1.value * this.scale.value + this.offset.value;
} else {
this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
}
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this constraint. Assume
* this constraint is satisfied.
*/
ScaleConstraint.prototype.recalculate = function () {
var ihn = this.input(), out = this.output();
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
out.stay = ihn.stay && this.scale.stay && this.offset.stay;
if (out.stay) this.execute();
}
/* --- *
* E q u a l i t y C o n s t r a i n t
* --- */
/**
* Constrains two variables to have the same value.
*/
function EqualityConstraint(var1, var2, strength) {
EqualityConstraint.superConstructor.call(this, var1, var2, strength);
}
EqualityConstraint.inherits(BinaryConstraint);
/**
* Enforce this constraint. Assume that it is satisfied.
*/
EqualityConstraint.prototype.execute = function () {
this.output().value = this.input().value;
}
/* --- *
* V a r i a b l e
* --- */
/**
* A constrained variable. In addition to its value, it maintain the
* structure of the constraint graph, the current dataflow graph, and
* various parameters of interest to the DeltaBlue incremental
* constraint solver.
**/
function Variable(name, initialValue) {
this.value = initialValue || 0;
this.constraints = new OrderedCollection();
this.determinedBy = null;
this.mark = 0;
this.walkStrength = Strength.WEAKEST;
this.stay = true;
this.name = name;
}
/**
* Add the given constraint to the set of all constraints that refer
* this variable.
*/
Variable.prototype.addConstraint = function (c) {
this.constraints.add(c);
}
/**
* Removes all traces of c from this variable.
*/
Variable.prototype.removeConstraint = function (c) {
this.constraints.remove(c);
if (this.determinedBy == c) this.determinedBy = null;
}
/* --- *
* P l a n n e r
* --- */
/**
* The DeltaBlue planner
*/
function Planner() {
this.currentMark = 0;
}
/**
* Attempt to satisfy the given constraint and, if successful,
* incrementally update the dataflow graph. Details: If satifying
* the constraint is successful, it may override a weaker constraint
* on its output. The algorithm attempts to resatisfy that
* constraint using some other method. This process is repeated
* until either a) it reaches a variable that was not previously
* determined by any constraint or b) it reaches a constraint that
* is too weak to be satisfied using any of its methods. The
* variables of constraints that have been processed are marked with
* a unique mark value so that we know where we've been. This allows
* the algorithm to avoid getting into an infinite loop even if the
* constraint graph has an inadvertent cycle.
*/
Planner.prototype.incrementalAdd = function (c) {
var mark = this.newMark();
var overridden = c.satisfy(mark);
while (overridden != null)
overridden = overridden.satisfy(mark);
}
/**
* Entry point for retracting a constraint. Remove the given
* constraint and incrementally update the dataflow graph.
* Details: Retracting the given constraint may allow some currently
* unsatisfiable downstream constraint to be satisfied. We therefore collect
* a list of unsatisfied downstream constraints and attempt to
* satisfy each one in turn. This list is traversed by constraint
* strength, strongest first, as a heuristic for avoiding
* unnecessarily adding and then overriding weak constraints.
* Assume: c is satisfied.
*/
Planner.prototype.incrementalRemove = function (c) {
var out = c.output();
c.markUnsatisfied();
c.removeFromGraph();
var unsatisfied = this.removePropagateFrom(out);
var strength = Strength.REQUIRED;
do {
for (var i = 0; i < unsatisfied.size(); i++) {
var u = unsatisfied.at(i);
if (u.strength == strength)
this.incrementalAdd(u);
}
strength = strength.nextWeaker();
} while (strength != Strength.WEAKEST);
}
/**
* Select a previously unused mark value.
*/
Planner.prototype.newMark = function () {
return ++this.currentMark;
}
/**
* Extract a plan for resatisfaction starting from the given source
* constraints, usually a set of input constraints. This method
* assumes that stay optimization is desired; the plan will contain
* only constraints whose output variables are not stay. Constraints
* that do no computation, such as stay and edit constraints, are
* not included in the plan.
* Details: The outputs of a constraint are marked when it is added
* to the plan under construction. A constraint may be appended to
* the plan when all its input variables are known. A variable is
* known if either a) the variable is marked (indicating that has
* been computed by a constraint appearing earlier in the plan), b)
* the variable is 'stay' (i.e. it is a constant at plan execution
* time), or c) the variable is not determined by any
* constraint. The last provision is for past states of history
* variables, which are not stay but which are also not computed by
* any constraint.
* Assume: sources are all satisfied.
*/
Planner.prototype.makePlan = function (sources) {
var mark = this.newMark();
var plan = new Plan();
var todo = sources;
while (todo.size() > 0) {
var c = todo.removeFirst();
if (c.output().mark != mark && c.inputsKnown(mark)) {
plan.addConstraint(c);
c.output().mark = mark;
this.addConstraintsConsumingTo(c.output(), todo);
}
}
return plan;
}
/**
* Extract a plan for resatisfying starting from the output of the
* given constraints, usually a set of input constraints.
*/
Planner.prototype.extractPlanFromConstraints = function (constraints) {
var sources = new OrderedCollection();
for (var i = 0; i < constraints.size(); i++) {
var c = constraints.at(i);
if (c.isInput() && c.isSatisfied())
// not in plan already and eligible for inclusion
sources.add(c);
}
return this.makePlan(sources);
}
/**
* Recompute the walkabout strengths and stay flags of all variables
* downstream of the given constraint and recompute the actual
* values of all variables whose stay flag is true. If a cycle is
* detected, remove the given constraint and answer
* false. Otherwise, answer true.
* Details: Cycles are detected when a marked variable is
* encountered downstream of the given constraint. The sender is
* assumed to have marked the inputs of the given constraint with
* the given mark. Thus, encountering a marked node downstream of
* the output constraint means that there is a path from the
* constraint's output to one of its inputs.
*/
Planner.prototype.addPropagate = function (c, mark) {
var todo = new OrderedCollection();
todo.add(c);
while (todo.size() > 0) {
var d = todo.removeFirst();
if (d.output().mark == mark) {
this.incrementalRemove(c);
return false;
}
d.recalculate();
this.addConstraintsConsumingTo(d.output(), todo);
}
return true;
}
/**
* Update the walkabout strengths and stay flags of all variables
* downstream of the given constraint. Answer a collection of
* unsatisfied constraints sorted in order of decreasing strength.
*/
Planner.prototype.removePropagateFrom = function (out) {
out.determinedBy = null;
out.walkStrength = Strength.WEAKEST;
out.stay = true;
var unsatisfied = new OrderedCollection();
var todo = new OrderedCollection();
todo.add(out);
while (todo.size() > 0) {
var v = todo.removeFirst();
for (var i = 0; i < v.constraints.size(); i++) {
var c = v.constraints.at(i);
if (!c.isSatisfied())
unsatisfied.add(c);
}
var determining = v.determinedBy;
for (var i = 0; i < v.constraints.size(); i++) {
var next = v.constraints.at(i);
if (next != determining && next.isSatisfied()) {
next.recalculate();
todo.add(next.output());
}
}
}
return unsatisfied;
}
Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
var determining = v.determinedBy;
var cc = v.constraints;
for (var i = 0; i < cc.size(); i++) {
var c = cc.at(i);
if (c != determining && c.isSatisfied())
coll.add(c);
}
}
/* --- *
* P l a n
* --- */
/**
* A Plan is an ordered list of constraints to be executed in sequence
* to resatisfy all currently satisfiable constraints in the face of
* one or more changing inputs.
*/
function Plan() {
this.v = new OrderedCollection();
}
Plan.prototype.addConstraint = function (c) {
this.v.add(c);
}
Plan.prototype.size = function () {
return this.v.size();
}
Plan.prototype.constraintAt = function (index) {
return this.v.at(index);
}
Plan.prototype.execute = function () {
for (var i = 0; i < this.size(); i++) {
var c = this.constraintAt(i);
c.execute();
}
}
/* --- *
* M a i n
* --- */
/**
* This is the standard DeltaBlue benchmark. A long chain of equality
* constraints is constructed with a stay constraint on one end. An
* edit constraint is then added to the opposite end and the time is
* measured for adding and removing this constraint, and extracting
* and executing a constraint satisfaction plan. There are two cases.
* In case 1, the added constraint is stronger than the stay
* constraint and values must propagate down the entire length of the
* chain. In case 2, the added constraint is weaker than the stay
* constraint so it cannot be accomodated. The cost in this case is,
* of course, very low. Typical situations lie somewhere between these
* two extremes.
*/
function chainTest(n) {
planner = new Planner();
var prev = null, first = null, last = null;
// Build chain of n equality constraints
for (var i = 0; i <= n; i++) {
var name = "v" + i;
var v = new Variable(name);
if (prev != null)
new EqualityConstraint(prev, v, Strength.REQUIRED);
if (i == 0) first = v;
if (i == n) last = v;
prev = v;
}
new StayConstraint(last, Strength.STRONG_DEFAULT);
var edit = new EditConstraint(first, Strength.PREFERRED);
var edits = new OrderedCollection();
edits.add(edit);
var plan = planner.extractPlanFromConstraints(edits);
for (var i = 0; i < 100; i++) {
first.value = i;
plan.execute();
if (last.value != i)
alert("Chain test failed.");
}
}
/**
* This test constructs a two sets of variables related to each
* other by a simple linear transformation (scale and offset). The
* time is measured to change a variable on either side of the
* mapping and to change the scale and offset factors.
*/
function projectionTest(n) {
planner = new Planner();
var scale = new Variable("scale", 10);
var offset = new Variable("offset", 1000);
var src = null, dst = null;
var dests = new OrderedCollection();
for (var i = 0; i < n; i++) {
src = new Variable("src" + i, i);
dst = new Variable("dst" + i, i);
dests.add(dst);
new StayConstraint(src, Strength.NORMAL);
new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
}
change(src, 17);
if (dst.value != 1170) alert("Projection 1 failed");
change(dst, 1050);
if (src.value != 5) alert("Projection 2 failed");
change(scale, 5);
for (var i = 0; i < n - 1; i++) {
if (dests.at(i).value != i * 5 + 1000)
alert("Projection 3 failed");
}
change(offset, 2000);
for (var i = 0; i < n - 1; i++) {
if (dests.at(i).value != i * 5 + 2000)
alert("Projection 4 failed");
}
}
function change(v, newValue) {
var edit = new EditConstraint(v, Strength.PREFERRED);
var edits = new OrderedCollection();
edits.add(edit);
var plan = planner.extractPlanFromConstraints(edits);
for (var i = 0; i < 10; i++) {
v.value = newValue;
plan.execute();
}
edit.destroyConstraint();
}
// Global variable holding the current planner.
var planner = null;
function deltaBlue() {
chainTest(100);
projectionTest(100);
}

4685
deps/v8/benchmarks/earley-boyer.js

File diff suppressed because one or more lines are too long

3434
deps/v8/benchmarks/raytrace.js

File diff suppressed because it is too large

1614
deps/v8/benchmarks/regexp.js

File diff suppressed because it is too large

62
deps/v8/benchmarks/revisions.html

@ -0,0 +1,62 @@
<html>
<head>
<title>V8 Benchmark Suite Revisions</title>
<link type="text/css" rel="stylesheet" href="style.css"></link>
</head>
<body>
<div>
<div class="title"><h1>V8 Benchmark Suite Revisions</h1></div>
<table>
<tr>
<td class="contents">
<p>
The V8 benchmark suite is changed from time to time as we fix bugs or
expand the scope of the benchmarks. Here is a list of revisions, with
a description of the changes made. Note that benchmark results are
not comparable unless both results are run with the same revision of
the benchmark suite.
</p>
<div class="subtitle"><h3>Version 3 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v3/run.html">link</a>)</h3></div>
<p>Version 3 adds a new benchmark, <i>RegExp</i>. The RegExp
benchmark is generated by loading 50 of the most popular pages on the
web and logging all regexp operations performed. Each operation is
given a weight that is calculated from an estimate of the popularity
of the pages where it occurs and the number of times it is executed
while loading each page. Finally the literal letters in the data are
encoded using ROT13 in a way that does not affect how the regexps
match their input.
</p>
<div class="subtitle"><h3>Version 2 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v2/run.html">link</a>)</h3></div>
<p>For version 2 the crypto benchmark was fixed. Previously, the
decryption stage was given plaintext as input, which resulted in an
error. Now, the decryption stage is given the output of the
encryption stage as input. The result is checked against the original
plaintext. For this to give the correct results the crypto objects
are reset for each iteration of the benchmark. In addition, the size
of the plain text has been increased a little and the use of
Math.random() and new Date() to build an RNG pool has been
removed. </p>
<p>Other benchmarks were fixed to do elementary verification of the
results of their calculations. This is to avoid accidentally
obtaining scores that are the result of an incorrect JavaScript engine
optimization.</p>
<div class="subtitle"><h3>Version 1 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v1/run.html">link</a>)</h3></div>
<p>Initial release.</p>
</td><td style="text-align: center">
</td></tr></table>
</div>
</body>
</html>

539
deps/v8/benchmarks/richards.js

@ -0,0 +1,539 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This is a JavaScript implementation of the Richards
// benchmark from:
//
// http://www.cl.cam.ac.uk/~mr10/Bench.html
//
// The benchmark was originally implemented in BCPL by
// Martin Richards.
var Richards = new BenchmarkSuite('Richards', 34886, [
new Benchmark("Richards", runRichards)
]);
/**
* The Richards benchmark simulates the task dispatcher of an
* operating system.
**/
function runRichards() {
var scheduler = new Scheduler();
scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
var queue = new Packet(null, ID_WORKER, KIND_WORK);
queue = new Packet(queue, ID_WORKER, KIND_WORK);
scheduler.addWorkerTask(ID_WORKER, 1000, queue);
queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
scheduler.schedule();
if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
scheduler.holdCount != EXPECTED_HOLD_COUNT) {
var msg =
"Error during execution: queueCount = " + scheduler.queueCount +
", holdCount = " + scheduler.holdCount + ".";
throw new Error(msg);
}
}
var COUNT = 1000;
/**
* These two constants specify how many times a packet is queued and
* how many times a task is put on hold in a correct run of richards.
* They don't have any meaning a such but are characteristic of a
* correct run so if the actual queue or hold count is different from
* the expected there must be a bug in the implementation.
**/
var EXPECTED_QUEUE_COUNT = 2322;
var EXPECTED_HOLD_COUNT = 928;
/**
* A scheduler can be used to schedule a set of tasks based on their relative
* priorities. Scheduling is done by maintaining a list of task control blocks
* which holds tasks and the data queue they are processing.
* @constructor
*/
function Scheduler() {
this.queueCount = 0;
this.holdCount = 0;
this.blocks = new Array(NUMBER_OF_IDS);
this.list = null;
this.currentTcb = null;
this.currentId = null;
}
var ID_IDLE = 0;
var ID_WORKER = 1;
var ID_HANDLER_A = 2;
var ID_HANDLER_B = 3;
var ID_DEVICE_A = 4;
var ID_DEVICE_B = 5;
var NUMBER_OF_IDS = 6;
var KIND_DEVICE = 0;
var KIND_WORK = 1;
/**
* Add an idle task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {int} count the number of times to schedule the task
*/
Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
};
/**
* Add a work task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
};
/**
* Add a handler task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new HandlerTask(this));
};
/**
* Add a handler task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new DeviceTask(this))
};
/**
* Add the specified task and mark it as running.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {Task} task the task to add
*/
Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
this.addTask(id, priority, queue, task);
this.currentTcb.setRunning();
};
/**
* Add the specified task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {Task} task the task to add
*/
Scheduler.prototype.addTask = function (id, priority, queue, task) {
this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
this.list = this.currentTcb;
this.blocks[id] = this.currentTcb;
};
/**
* Execute the tasks managed by this scheduler.
*/
Scheduler.prototype.schedule = function () {
this.currentTcb = this.list;
while (this.currentTcb != null) {
if (this.currentTcb.isHeldOrSuspended()) {
this.currentTcb = this.currentTcb.link;
} else {
this.currentId = this.currentTcb.id;
this.currentTcb = this.currentTcb.run();
}
}
};
/**
* Release a task that is currently blocked and return the next block to run.
* @param {int} id the id of the task to suspend
*/
Scheduler.prototype.release = function (id) {
var tcb = this.blocks[id];
if (tcb == null) return tcb;
tcb.markAsNotHeld();
if (tcb.priority > this.currentTcb.priority) {
return tcb;
} else {
return this.currentTcb;
}
};
/**
* Block the currently executing task and return the next task control block
* to run. The blocked task will not be made runnable until it is explicitly
* released, even if new work is added to it.
*/
Scheduler.prototype.holdCurrent = function () {
this.holdCount++;
this.currentTcb.markAsHeld();
return this.currentTcb.link;
};
/**
* Suspend the currently executing task and return the next task control block
* to run. If new work is added to the suspended task it will be made runnable.
*/
Scheduler.prototype.suspendCurrent = function () {
this.currentTcb.markAsSuspended();
return this.currentTcb;
};
/**
* Add the specified packet to the end of the worklist used by the task
* associated with the packet and make the task runnable if it is currently
* suspended.
* @param {Packet} packet the packet to add
*/
Scheduler.prototype.queue = function (packet) {
var t = this.blocks[packet.id];
if (t == null) return t;
this.queueCount++;
packet.link = null;
packet.id = this.currentId;
return t.checkPriorityAdd(this.currentTcb, packet);
};
/**
* A task control block manages a task and the queue of work packages associated
* with it.
* @param {TaskControlBlock} link the preceding block in the linked block list
* @param {int} id the id of this block
* @param {int} priority the priority of this block
* @param {Packet} queue the queue of packages to be processed by the task
* @param {Task} task the task
* @constructor
*/
function TaskControlBlock(link, id, priority, queue, task) {
this.link = link;
this.id = id;
this.priority = priority;
this.queue = queue;
this.task = task;
if (queue == null) {
this.state = STATE_SUSPENDED;
} else {
this.state = STATE_SUSPENDED_RUNNABLE;
}
}
/**
* The task is running and is currently scheduled.
*/
var STATE_RUNNING = 0;
/**
* The task has packets left to process.
*/
var STATE_RUNNABLE = 1;
/**
* The task is not currently running. The task is not blocked as such and may
* be started by the scheduler.
*/
var STATE_SUSPENDED = 2;
/**
* The task is blocked and cannot be run until it is explicitly released.
*/
var STATE_HELD = 4;
var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
var STATE_NOT_HELD = ~STATE_HELD;
TaskControlBlock.prototype.setRunning = function () {
this.state = STATE_RUNNING;
};
TaskControlBlock.prototype.markAsNotHeld = function () {
this.state = this.state & STATE_NOT_HELD;
};
TaskControlBlock.prototype.markAsHeld = function () {
this.state = this.state | STATE_HELD;
};
TaskControlBlock.prototype.isHeldOrSuspended = function () {
return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
};
TaskControlBlock.prototype.markAsSuspended = function () {
this.state = this.state | STATE_SUSPENDED;
};
TaskControlBlock.prototype.markAsRunnable = function () {
this.state = this.state | STATE_RUNNABLE;
};
/**
* Runs this task, if it is ready to be run, and returns the next task to run.
*/
TaskControlBlock.prototype.run = function () {
var packet;
if (this.state == STATE_SUSPENDED_RUNNABLE) {
packet = this.queue;
this.queue = packet.link;
if (this.queue == null) {
this.state = STATE_RUNNING;
} else {
this.state = STATE_RUNNABLE;
}
} else {
packet = null;
}
return this.task.run(packet);
};
/**
* Adds a packet to the worklist of this block's task, marks this as runnable if
* necessary, and returns the next runnable object to run (the one
* with the highest priority).
*/
TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
if (this.queue == null) {
this.queue = packet;
this.markAsRunnable();
if (this.priority > task.priority) return this;
} else {
this.queue = packet.addTo(this.queue);
}
return task;
};
TaskControlBlock.prototype.toString = function () {
return "tcb { " + this.task + "@" + this.state + " }";
};
/**
* An idle task doesn't do any work itself but cycles control between the two
* device tasks.
* @param {Scheduler} scheduler the scheduler that manages this task
* @param {int} v1 a seed value that controls how the device tasks are scheduled
* @param {int} count the number of times this task should be scheduled
* @constructor
*/
function IdleTask(scheduler, v1, count) {
this.scheduler = scheduler;
this.v1 = v1;
this.count = count;
}
IdleTask.prototype.run = function (packet) {
this.count--;
if (this.count == 0) return this.scheduler.holdCurrent();
if ((this.v1 & 1) == 0) {
this.v1 = this.v1 >> 1;
return this.scheduler.release(ID_DEVICE_A);
} else {
this.v1 = (this.v1 >> 1) ^ 0xD008;
return this.scheduler.release(ID_DEVICE_B);
}
};
IdleTask.prototype.toString = function () {
return "IdleTask"
};
/**
* A task that suspends itself after each time it has been run to simulate
* waiting for data from an external device.
* @param {Scheduler} scheduler the scheduler that manages this task
* @constructor
*/
function DeviceTask(scheduler) {
this.scheduler = scheduler;
this.v1 = null;
}
DeviceTask.prototype.run = function (packet) {
if (packet == null) {
if (this.v1 == null) return this.scheduler.suspendCurrent();
var v = this.v1;
this.v1 = null;
return this.scheduler.queue(v);
} else {
this.v1 = packet;
return this.scheduler.holdCurrent();
}
};
DeviceTask.prototype.toString = function () {
return "DeviceTask";
};
/**
* A task that manipulates work packets.
* @param {Scheduler} scheduler the scheduler that manages this task
* @param {int} v1 a seed used to specify how work packets are manipulated
* @param {int} v2 another seed used to specify how work packets are manipulated
* @constructor
*/
function WorkerTask(scheduler, v1, v2) {
this.scheduler = scheduler;
this.v1 = v1;
this.v2 = v2;
}
WorkerTask.prototype.run = function (packet) {
if (packet == null) {
return this.scheduler.suspendCurrent();
} else {
if (this.v1 == ID_HANDLER_A) {
this.v1 = ID_HANDLER_B;
} else {
this.v1 = ID_HANDLER_A;
}
packet.id = this.v1;
packet.a1 = 0;
for (var i = 0; i < DATA_SIZE; i++) {
this.v2++;
if (this.v2 > 26) this.v2 = 1;
packet.a2[i] = this.v2;
}
return this.scheduler.queue(packet);
}
};
WorkerTask.prototype.toString = function () {
return "WorkerTask";
};
/**
* A task that manipulates work packets and then suspends itself.
* @param {Scheduler} scheduler the scheduler that manages this task
* @constructor
*/
function HandlerTask(scheduler) {
this.scheduler = scheduler;
this.v1 = null;
this.v2 = null;
}
HandlerTask.prototype.run = function (packet) {
if (packet != null) {
if (packet.kind == KIND_WORK) {
this.v1 = packet.addTo(this.v1);
} else {
this.v2 = packet.addTo(this.v2);
}
}
if (this.v1 != null) {
var count = this.v1.a1;
var v;
if (count < DATA_SIZE) {
if (this.v2 != null) {
v = this.v2;
this.v2 = this.v2.link;
v.a1 = this.v1.a2[count];
this.v1.a1 = count + 1;
return this.scheduler.queue(v);
}
} else {
v = this.v1;
this.v1 = this.v1.link;
return this.scheduler.queue(v);
}
}
return this.scheduler.suspendCurrent();
};
HandlerTask.prototype.toString = function () {
return "HandlerTask";
};
/* --- *
* P a c k e t
* --- */
var DATA_SIZE = 4;
/**
* A simple package of data that is manipulated by the tasks. The exact layout
* of the payload data carried by a packet is not importaint, and neither is the
* nature of the work performed on packets by the tasks.
*
* Besides carrying data, packets form linked lists and are hence used both as
* data and worklists.
* @param {Packet} link the tail of the linked list of packets
* @param {int} id an ID for this packet
* @param {int} kind the type of this packet
* @constructor
*/
function Packet(link, id, kind) {
this.link = link;
this.id = id;
this.kind = kind;
this.a1 = 0;
this.a2 = new Array(DATA_SIZE);
}
/**
* Add this packet to the end of a worklist, and return the worklist.
* @param {Packet} queue the worklist to add this packet to
*/
Packet.prototype.addTo = function (queue) {
this.link = null;
if (queue == null) return this;
var peek, next = queue;
while ((peek = next.link) != null)
next = peek;
next.link = this;
return queue;
};
Packet.prototype.toString = function () {
return "Packet";
};

102
deps/v8/benchmarks/run.html

@ -0,0 +1,102 @@
<html>
<head>
<title>V8 Benchmark Suite</title>
<script type="text/javascript" src="base.js"></script>
<script type="text/javascript" src="richards.js"></script>
<script type="text/javascript" src="deltablue.js"></script>
<script type="text/javascript" src="crypto.js"></script>
<script type="text/javascript" src="raytrace.js"></script>
<script type="text/javascript" src="earley-boyer.js"></script>
<script type="text/javascript" src="regexp.js"></script>
<link type="text/css" rel="stylesheet" href="style.css"></link>
<script type="text/javascript">
var completed = 0;
var benchmarks = BenchmarkSuite.CountBenchmarks();
var success = true;
function ShowProgress(name) {
var status = document.getElementById("status");
var percentage = ((++completed) / benchmarks) * 100;
status.innerHTML = "Running: " + Math.round(percentage) + "% completed.";
}
function AddResult(name, result) {
var text = name + ': ' + result;
var results = document.getElementById("results");
results.innerHTML += (text + "<br/>");
}
function AddError(name, error) {
AddResult(name, '<b>error</b>');
success = false;
}
function AddScore(score) {
var status = document.getElementById("status");
if (success) {
status.innerHTML = "Score: " + score;
}
}
function Run() {
BenchmarkSuite.RunSuites({ NotifyStep: ShowProgress,
NotifyError: AddError,
NotifyResult: AddResult,
NotifyScore: AddScore });
}
function Load() {
var version = BenchmarkSuite.version;
document.getElementById("version").innerHTML = version;
window.setTimeout(Run, 200);
}
</script>
</head>
<body onLoad="Load()">
<div>
<div class="title"><h1>V8 Benchmark Suite - version <span id="version">?</span></h1></div>
<table>
<tr>
<td class="contents">
This page contains a suite of pure JavaScript benchmarks that we have
used to tune V8. The final score is computed as the geometric mean of
the individual results to make it independent of the running times of
the individual benchmarks and of a reference system (score
100). Scores are not comparable across benchmark suite versions and
higher scores means better performance: <em>Bigger is better!</em>
<ul>
<li><b>Richards</b><br/>OS kernel simulation benchmark, originally written in BCPL by Martin Richards (<i>539 lines</i>).</li>
<li><b>DeltaBlue</b><br/>One-way constraint solver, originally written in Smalltalk by John Maloney and Mario Wolczko (<i>880 lines</i>).</li>
<li><b>Crypto</b><br/>Encryption and decryption benchmark based on code by Tom Wu (<i>1689 lines</i>).</li>
<li><b>RayTrace</b><br/>Ray tracer benchmark based on code by <a href="http://flog.co.nz/">Adam Burmister</a> (<i>3418 lines</i>).</li>
<li><b>EarleyBoyer</b><br/>Classic Scheme benchmarks, translated to JavaScript by Florian Loitsch's Scheme2Js compiler (<i>4682 lines</i>).</li>
<li><b>RegExp</b><br/>Regular expression benchmark generated by extracting regular expression operations from 50 of the most popular web pages
(<i>4758 lines</i>).
</li>
</ul>
<p>
Note that benchmark results are not comparable unless both results are
run with the same revision of the benchmark suite. We will be making
revisions from time to time in order to fix bugs or expand the scope
of the benchmark suite. For previous revisions and the change log see
the <a href="http://v8.googlecode.com/svn/data/benchmarks/current/revisions.html">revisions</a> page.
</p>
</td><td style="text-align: center">
<div class="run">
<div id="status" style="text-align: center; margin-top: 60px; font-size: 120%; font-weight: bold;">Starting...</div>
<div style="text-align: left; margin: 30px 0 0 90px;" id="results">
<div>
</div>
</td></tr></table>
</div>
</body>
</html>

60
deps/v8/benchmarks/run.js

@ -0,0 +1,60 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load('base.js');
load('richards.js');
load('deltablue.js');
load('crypto.js');
load('raytrace.js');
load('earley-boyer.js');
load('regexp.js');
var success = true;
function PrintResult(name, result) {
print(name + ': ' + result);
}
function PrintError(name, error) {
PrintResult(name, error);
success = false;
}
function PrintScore(score) {
if (success) {
print('----');
print('Score (version ' + BenchmarkSuite.version + '): ' + score);
}
}
BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
NotifyError: PrintError,
NotifyScore: PrintScore });

70
deps/v8/benchmarks/style.css

@ -0,0 +1,70 @@
body {
font-family: sans-serif;
}
hr{
border: 1px solid;
border-color: #36C;
margin: 1em 0
}
h1, h2, h3, h4 {
margin: 0;
margin-bottom: 0;
}
h1 {
font-size: 190%;
height: 1.2em;
}
h2{
font-size: 140%;
height: 1.2em;
}
h3{
font-size: 100%;
}
li{
margin: .3em 0 1em 0;
}
body{
font-family: Helvetica,Arial,sans-serif;
font-size: small;
color: #000;
background-color: #fff;
}
div.title {
background-color: rgb(229, 236, 249);
border-top: 1px solid rgb(51, 102, 204);
text-align: center;
padding-top: 0.2em;
padding-bottom: 0.2em;
margin-bottom: 20px;
}
div.subtitle {
border-bottom: 1px solid rgb(51, 102, 204);
margin-top: 2em;
}
td.contents {
text-align: start;
}
div.run {
margin: 20px;
width: 300px;
height: 300px;
float: right;
background-color: rgb(229, 236, 249);
background-image: url(v8-logo.png);
background-position: center center;
background-repeat: no-repeat;
border: 1px solid rgb(51, 102, 204);
}

BIN
deps/v8/benchmarks/v8-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

179
deps/v8/include/v8-debug.h

@ -0,0 +1,179 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_DEBUG_H_
#define V8_DEBUG_H_
#include "v8.h"
#ifdef _WIN32
typedef int int32_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t; // NOLINT
typedef long long int64_t; // NOLINT
// Setup for Windows DLL export/import. See v8.h in this directory for
// information on how to build/use V8 as a DLL.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define EXPORT __declspec(dllimport)
#else
#define EXPORT
#endif
#else // _WIN32
// Setup for Linux shared library export. See v8.h in this directory for
// information on how to build/use V8 as shared library.
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
/**
* Debugger support for the V8 JavaScript engine.
*/
namespace v8 {
// Debug events which can occur in the V8 JavaScript engine.
enum DebugEvent {
Break = 1,
Exception = 2,
NewFunction = 3,
BeforeCompile = 4,
AfterCompile = 5
};
/**
* Debug event callback function.
*
* \param event the type of the debug event that triggered the callback
* (enum DebugEvent)
* \param exec_state execution state (JavaScript object)
* \param event_data event specific data (JavaScript object)
* \param data value passed by the user to SetDebugEventListener
*/
typedef void (*DebugEventCallback)(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
Handle<Value> data);
/**
* Debug message callback function.
*
* \param message the debug message
* \param length length of the message
* \param data the data value passed when registering the message handler
* A DebugMessageHandler does not take posession of the message string,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*DebugMessageHandler)(const uint16_t* message, int length,
void* data);
/**
* Debug host dispatch callback function.
*
* \param dispatch the dispatch value
* \param data the data value passed when registering the dispatch handler
*/
typedef void (*DebugHostDispatchHandler)(void* dispatch,
void* data);
class EXPORT Debug {
public:
// Set a C debug event listener.
static bool SetDebugEventListener(DebugEventCallback that,
Handle<Value> data = Handle<Value>());
// Set a JavaScript debug event listener.
static bool SetDebugEventListener(v8::Handle<v8::Object> that,
Handle<Value> data = Handle<Value>());
// Break execution of JavaScript.
static void DebugBreak();
// Message based interface. The message protocol is JSON.
static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL,
bool message_handler_thread = true);
static void SendCommand(const uint16_t* command, int length);
// Dispatch interface.
static void SetHostDispatchHandler(DebugHostDispatchHandler handler,
void* data = NULL);
static void SendHostDispatch(void* dispatch);
/**
* Run a JavaScript function in the debugger.
* \param fun the function to call
* \param data passed as second argument to the function
* With this call the debugger is entered and the function specified is called
* with the execution state as the first argument. This makes it possible to
* get access to information otherwise not available during normal JavaScript
* execution e.g. details on stack frames. The following example show a
* JavaScript function which when passed to v8::Debug::Call will return the
* current line of JavaScript execution.
*
* \code
* function frame_source_line(exec_state) {
* return exec_state.frame(0).sourceLine();
* }
* \endcode
*/
static Handle<Value> Call(v8::Handle<v8::Function> fun,
Handle<Value> data = Handle<Value>());
/**
* Enable the V8 builtin debug agent. The debugger agent will listen on the
* supplied TCP/IP port for remote debugger connection.
* \param name the name of the embedding application
* \param port the TCP/IP port to listen on
*/
static bool EnableAgent(const char* name, int port);
};
} // namespace v8
#undef EXPORT
#endif // V8_DEBUG_H_

2538
deps/v8/include/v8.h

File diff suppressed because it is too large

38
deps/v8/samples/SConscript

@ -0,0 +1,38 @@
# Copyright 2008 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from os.path import join
Import('sample context')
def ConfigureObjectFiles():
env = Environment()
env.Replace(**context.flags['sample'])
context.ApplyEnvOverrides(env)
return env.Object(sample + '.cc')
sample_object = ConfigureObjectFiles()
Return('sample_object')

42
deps/v8/samples/count-hosts.js

@ -0,0 +1,42 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function Initialize() { }
function Process(request) {
if (options.verbose) {
log("Processing " + request.host + request.path +
" from " + request.referrer + "@" + request.userAgent);
}
if (!output[request.host]) {
output[request.host] = 1;
} else {
output[request.host]++
}
}
Initialize();

622
deps/v8/samples/process.cc

@ -0,0 +1,622 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <v8.h>
#include <string>
#include <map>
using namespace std;
using namespace v8;
// These interfaces represent an existing request processing interface.
// The idea is to imagine a real application that uses these interfaces
// and then add scripting capabilities that allow you to interact with
// the objects through JavaScript.
/**
* A simplified http request.
*/
class HttpRequest {
public:
virtual ~HttpRequest() { }
virtual const string& Path() = 0;
virtual const string& Referrer() = 0;
virtual const string& Host() = 0;
virtual const string& UserAgent() = 0;
};
/**
* The abstract superclass of http request processors.
*/
class HttpRequestProcessor {
public:
virtual ~HttpRequestProcessor() { }
// Initialize this processor. The map contains options that control
// how requests should be processed.
virtual bool Initialize(map<string, string>* options,
map<string, string>* output) = 0;
// Process a single request.
virtual bool Process(HttpRequest* req) = 0;
static void Log(const char* event);
};
/**
* An http request processor that is scriptable using JavaScript.
*/
class JsHttpRequestProcessor : public HttpRequestProcessor {
public:
// Creates a new processor that processes requests by invoking the
// Process function of the JavaScript script given as an argument.
explicit JsHttpRequestProcessor(Handle<String> script) : script_(script) { }
virtual ~JsHttpRequestProcessor();
virtual bool Initialize(map<string, string>* opts,
map<string, string>* output);
virtual bool Process(HttpRequest* req);
private:
// Execute the script associated with this processor and extract the
// Process function. Returns true if this succeeded, otherwise false.
bool ExecuteScript(Handle<String> script);
// Wrap the options and output map in a JavaScript objects and
// install it in the global namespace as 'options' and 'output'.
bool InstallMaps(map<string, string>* opts, map<string, string>* output);
// Constructs the template that describes the JavaScript wrapper
// type for requests.
static Handle<ObjectTemplate> MakeRequestTemplate();
static Handle<ObjectTemplate> MakeMapTemplate();
// Callbacks that access the individual fields of request objects.
static Handle<Value> GetPath(Local<String> name, const AccessorInfo& info);
static Handle<Value> GetReferrer(Local<String> name,
const AccessorInfo& info);
static Handle<Value> GetHost(Local<String> name, const AccessorInfo& info);
static Handle<Value> GetUserAgent(Local<String> name,
const AccessorInfo& info);
// Callbacks that access maps
static Handle<Value> MapGet(Local<String> name, const AccessorInfo& info);
static Handle<Value> MapSet(Local<String> name,
Local<Value> value,
const AccessorInfo& info);
// Utility methods for wrapping C++ objects as JavaScript objects,
// and going back again.
static Handle<Object> WrapMap(map<string, string>* obj);
static map<string, string>* UnwrapMap(Handle<Object> obj);
static Handle<Object> WrapRequest(HttpRequest* obj);
static HttpRequest* UnwrapRequest(Handle<Object> obj);
Handle<String> script_;
Persistent<Context> context_;
Persistent<Function> process_;
static Persistent<ObjectTemplate> request_template_;
static Persistent<ObjectTemplate> map_template_;
};
// -------------------------
// --- P r o c e s s o r ---
// -------------------------
static Handle<Value> LogCallback(const Arguments& args) {
if (args.Length() < 1) return v8::Undefined();
HandleScope scope;
Handle<Value> arg = args[0];
String::Utf8Value value(arg);
HttpRequestProcessor::Log(*value);
return v8::Undefined();
}
// Execute the script and fetch the Process method.
bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
map<string, string>* output) {
// Create a handle scope to hold the temporary references.
HandleScope handle_scope;
// Create a template for the global object where we set the
// built-in global functions.
Handle<ObjectTemplate> global = ObjectTemplate::New();
global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
// Each processor gets its own context so different processors
// don't affect each other (ignore the first three lines).
Handle<Context> context = Context::New(NULL, global);
// Store the context in the processor object in a persistent handle,
// since we want the reference to remain after we return from this
// method.
context_ = Persistent<Context>::New(context);
// Enter the new context so all the following operations take place
// within it.
Context::Scope context_scope(context);
// Make the options mapping available within the context
if (!InstallMaps(opts, output))
return false;
// Compile and run the script
if (!ExecuteScript(script_))
return false;
// The script compiled and ran correctly. Now we fetch out the
// Process function from the global object.
Handle<String> process_name = String::New("Process");
Handle<Value> process_val = context->Global()->Get(process_name);
// If there is no Process function, or if it is not a function,
// bail out
if (!process_val->IsFunction()) return false;
// It is a function; cast it to a Function
Handle<Function> process_fun = Handle<Function>::Cast(process_val);
// Store the function in a Persistent handle, since we also want
// that to remain after this call returns
process_ = Persistent<Function>::New(process_fun);
// All done; all went well
return true;
}
bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {
HandleScope handle_scope;
// We're just about to compile the script; set up an error handler to
// catch any exceptions the script might throw.
TryCatch try_catch;
// Compile the script and check for errors.
Handle<Script> compiled_script = Script::Compile(script);
if (compiled_script.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
Log(*error);
// The script failed to compile; bail out.
return false;
}
// Run the script!
Handle<Value> result = compiled_script->Run();
if (result.IsEmpty()) {
// The TryCatch above is still in effect and will have caught the error.
String::Utf8Value error(try_catch.Exception());
Log(*error);
// Running the script failed; bail out.
return false;
}
return true;
}
bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
map<string, string>* output) {
HandleScope handle_scope;
// Wrap the map object in a JavaScript wrapper
Handle<Object> opts_obj = WrapMap(opts);
// Set the options object as a property on the global object.
context_->Global()->Set(String::New("options"), opts_obj);
Handle<Object> output_obj = WrapMap(output);
context_->Global()->Set(String::New("output"), output_obj);
return true;
}
bool JsHttpRequestProcessor::Process(HttpRequest* request) {
// Create a handle scope to keep the temporary object references.
HandleScope handle_scope;
// Enter this processor's context so all the remaining operations
// take place there
Context::Scope context_scope(context_);
// Wrap the C++ request object in a JavaScript wrapper
Handle<Object> request_obj = WrapRequest(request);
// Set up an exception handler before calling the Process function
TryCatch try_catch;
// Invoke the process function, giving the global object as 'this'
// and one argument, the request.
const int argc = 1;
Handle<Value> argv[argc] = { request_obj };
Handle<Value> result = process_->Call(context_->Global(), argc, argv);
if (result.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
Log(*error);
return false;
} else {
return true;
}
}
JsHttpRequestProcessor::~JsHttpRequestProcessor() {
// Dispose the persistent handles. When noone else has any
// references to the objects stored in the handles they will be
// automatically reclaimed.
context_.Dispose();
process_.Dispose();
}
Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;
Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;
// -----------------------------------
// --- A c c e s s i n g M a p s ---
// -----------------------------------
// Utility function that wraps a C++ http request object in a
// JavaScript object.
Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
// Handle scope for temporary handles.
HandleScope handle_scope;
// Fetch the template for creating JavaScript map wrappers.
// It only has to be created once, which we do on demand.
if (request_template_.IsEmpty()) {
Handle<ObjectTemplate> raw_template = MakeMapTemplate();
map_template_ = Persistent<ObjectTemplate>::New(raw_template);
}
Handle<ObjectTemplate> templ = map_template_;
// Create an empty map wrapper.
Handle<Object> result = templ->NewInstance();
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
Handle<External> map_ptr = External::New(obj);
// Store the map pointer in the JavaScript wrapper.
result->SetInternalField(0, map_ptr);
// Return the result through the current handle scope. Since each
// of these handles will go away when the handle scope is deleted
// we need to call Close to let one, the result, escape into the
// outer handle scope.
return handle_scope.Close(result);
}
// Utility function that extracts the C++ map pointer from a wrapper
// object.
map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
void* ptr = field->Value();
return static_cast<map<string, string>*>(ptr);
}
// Convert a JavaScript string to a std::string. To not bother too
// much with string encodings we just use ascii.
string ObjectToString(Local<Value> value) {
String::Utf8Value utf8_value(value);
return string(*utf8_value);
}
Handle<Value> JsHttpRequestProcessor::MapGet(Local<String> name,
const AccessorInfo& info) {
// Fetch the map wrapped by this object.
map<string, string>* obj = UnwrapMap(info.Holder());
// Convert the JavaScript string to a std::string.
string key = ObjectToString(name);
// Look up the value if it exists using the standard STL ideom.
map<string, string>::iterator iter = obj->find(key);
// If the key is not present return an empty handle as signal
if (iter == obj->end()) return Handle<Value>();
// Otherwise fetch the value and wrap it in a JavaScript string
const string& value = (*iter).second;
return String::New(value.c_str(), value.length());
}
Handle<Value> JsHttpRequestProcessor::MapSet(Local<String> name,
Local<Value> value_obj,
const AccessorInfo& info) {
// Fetch the map wrapped by this object.
map<string, string>* obj = UnwrapMap(info.Holder());
// Convert the key and value to std::strings.
string key = ObjectToString(name);
string value = ObjectToString(value_obj);
// Update the map.
(*obj)[key] = value;
// Return the value; any non-empty handle will work.
return value_obj;
}
Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate() {
HandleScope handle_scope;
Handle<ObjectTemplate> result = ObjectTemplate::New();
result->SetInternalFieldCount(1);
result->SetNamedPropertyHandler(MapGet, MapSet);
// Again, return the result through the current handle scope.
return handle_scope.Close(result);
}
// -------------------------------------------
// --- A c c e s s i n g R e q u e s t s ---
// -------------------------------------------
/**
* Utility function that wraps a C++ http request object in a
* JavaScript object.
*/
Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
// Handle scope for temporary handles.
HandleScope handle_scope;
// Fetch the template for creating JavaScript http request wrappers.
// It only has to be created once, which we do on demand.
if (request_template_.IsEmpty()) {
Handle<ObjectTemplate> raw_template = MakeRequestTemplate();
request_template_ = Persistent<ObjectTemplate>::New(raw_template);
}
Handle<ObjectTemplate> templ = request_template_;
// Create an empty http request wrapper.
Handle<Object> result = templ->NewInstance();
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
Handle<External> request_ptr = External::New(request);
// Store the request pointer in the JavaScript wrapper.
result->SetInternalField(0, request_ptr);
// Return the result through the current handle scope. Since each
// of these handles will go away when the handle scope is deleted
// we need to call Close to let one, the result, escape into the
// outer handle scope.
return handle_scope.Close(result);
}
/**
* Utility function that extracts the C++ http request object from a
* wrapper object.
*/
HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
void* ptr = field->Value();
return static_cast<HttpRequest*>(ptr);
}
Handle<Value> JsHttpRequestProcessor::GetPath(Local<String> name,
const AccessorInfo& info) {
// Extract the C++ request object from the JavaScript wrapper.
HttpRequest* request = UnwrapRequest(info.Holder());
// Fetch the path.
const string& path = request->Path();
// Wrap the result in a JavaScript string and return it.
return String::New(path.c_str(), path.length());
}
Handle<Value> JsHttpRequestProcessor::GetReferrer(Local<String> name,
const AccessorInfo& info) {
HttpRequest* request = UnwrapRequest(info.Holder());
const string& path = request->Referrer();
return String::New(path.c_str(), path.length());
}
Handle<Value> JsHttpRequestProcessor::GetHost(Local<String> name,
const AccessorInfo& info) {
HttpRequest* request = UnwrapRequest(info.Holder());
const string& path = request->Host();
return String::New(path.c_str(), path.length());
}
Handle<Value> JsHttpRequestProcessor::GetUserAgent(Local<String> name,
const AccessorInfo& info) {
HttpRequest* request = UnwrapRequest(info.Holder());
const string& path = request->UserAgent();
return String::New(path.c_str(), path.length());
}
Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate() {
HandleScope handle_scope;
Handle<ObjectTemplate> result = ObjectTemplate::New();
result->SetInternalFieldCount(1);
// Add accessors for each of the fields of the request.
result->SetAccessor(String::NewSymbol("path"), GetPath);
result->SetAccessor(String::NewSymbol("referrer"), GetReferrer);
result->SetAccessor(String::NewSymbol("host"), GetHost);
result->SetAccessor(String::NewSymbol("userAgent"), GetUserAgent);
// Again, return the result through the current handle scope.
return handle_scope.Close(result);
}
// --- Test ---
void HttpRequestProcessor::Log(const char* event) {
printf("Logged: %s\n", event);
}
/**
* A simplified http request.
*/
class StringHttpRequest : public HttpRequest {
public:
StringHttpRequest(const string& path,
const string& referrer,
const string& host,
const string& user_agent);
virtual const string& Path() { return path_; }
virtual const string& Referrer() { return referrer_; }
virtual const string& Host() { return host_; }
virtual const string& UserAgent() { return user_agent_; }
private:
string path_;
string referrer_;
string host_;
string user_agent_;
};
StringHttpRequest::StringHttpRequest(const string& path,
const string& referrer,
const string& host,
const string& user_agent)
: path_(path),
referrer_(referrer),
host_(host),
user_agent_(user_agent) { }
void ParseOptions(int argc,
char* argv[],
map<string, string>& options,
string* file) {
for (int i = 1; i < argc; i++) {
string arg = argv[i];
int index = arg.find('=', 0);
if (index == string::npos) {
*file = arg;
} else {
string key = arg.substr(0, index);
string value = arg.substr(index+1);
options[key] = value;
}
}
}
// Reads a file into a v8 string.
Handle<String> ReadFile(const string& name) {
FILE* file = fopen(name.c_str(), "rb");
if (file == NULL) return Handle<String>();
fseek(file, 0, SEEK_END);
int size = ftell(file);
rewind(file);
char* chars = new char[size + 1];
chars[size] = '\0';
for (int i = 0; i < size;) {
int read = fread(&chars[i], 1, size - i, file);
i += read;
}
fclose(file);
Handle<String> result = String::New(chars, size);
delete[] chars;
return result;
}
const int kSampleSize = 6;
StringHttpRequest kSampleRequests[kSampleSize] = {
StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
StringHttpRequest("/", "localhost", "google.net", "firefox"),
StringHttpRequest("/", "localhost", "google.org", "safari"),
StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
};
bool ProcessEntries(HttpRequestProcessor* processor, int count,
StringHttpRequest* reqs) {
for (int i = 0; i < count; i++) {
if (!processor->Process(&reqs[i]))
return false;
}
return true;
}
void PrintMap(map<string, string>* m) {
for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
pair<string, string> entry = *i;
printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
}
}
int main(int argc, char* argv[]) {
map<string, string> options;
string file;
ParseOptions(argc, argv, options, &file);
if (file.empty()) {
fprintf(stderr, "No script was specified.\n");
return 1;
}
HandleScope scope;
Handle<String> source = ReadFile(file);
if (source.IsEmpty()) {
fprintf(stderr, "Error reading '%s'.\n", file.c_str());
return 1;
}
JsHttpRequestProcessor processor(source);
map<string, string> output;
if (!processor.Initialize(&options, &output)) {
fprintf(stderr, "Error initializing processor.\n");
return 1;
}
if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
return 1;
PrintMap(&output);
}

281
deps/v8/samples/shell.cc

@ -0,0 +1,281 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <v8.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void RunShell(v8::Handle<v8::Context> context);
bool ExecuteString(v8::Handle<v8::String> source,
v8::Handle<v8::Value> name,
bool print_result,
bool report_exceptions);
v8::Handle<v8::Value> Print(const v8::Arguments& args);
v8::Handle<v8::Value> Load(const v8::Arguments& args);
v8::Handle<v8::Value> Quit(const v8::Arguments& args);
v8::Handle<v8::Value> Version(const v8::Arguments& args);
v8::Handle<v8::String> ReadFile(const char* name);
void ReportException(v8::TryCatch* handler);
int RunMain(int argc, char* argv[]) {
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
v8::HandleScope handle_scope;
// Create a template for the global object.
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
// Bind the global 'print' function to the C++ Print callback.
global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
// Bind the global 'load' function to the C++ Load callback.
global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
// Bind the 'quit' function
global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
// Bind the 'version' function
global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
// Create a new execution environment containing the built-in
// functions
v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
// Enter the newly created execution environment.
v8::Context::Scope context_scope(context);
bool run_shell = (argc == 1);
for (int i = 1; i < argc; i++) {
const char* str = argv[i];
if (strcmp(str, "--shell") == 0) {
run_shell = true;
} else if (strcmp(str, "-f") == 0) {
// Ignore any -f flags for compatibility with the other stand-
// alone JavaScript engines.
continue;
} else if (strncmp(str, "--", 2) == 0) {
printf("Warning: unknown flag %s.\nTry --help for options\n", str);
} else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
// Execute argument given to -e option directly
v8::HandleScope handle_scope;
v8::Handle<v8::String> file_name = v8::String::New("unnamed");
v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
if (!ExecuteString(source, file_name, false, true))
return 1;
i++;
} else {
// Use all other arguments as names of files to load and run.
v8::HandleScope handle_scope;
v8::Handle<v8::String> file_name = v8::String::New(str);
v8::Handle<v8::String> source = ReadFile(str);
if (source.IsEmpty()) {
printf("Error reading '%s'\n", str);
return 1;
}
if (!ExecuteString(source, file_name, false, true))
return 1;
}
}
if (run_shell) RunShell(context);
return 0;
}
int main(int argc, char* argv[]) {
int result = RunMain(argc, argv);
v8::V8::Dispose();
return result;
}
// Extracts a C string from a V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
}
// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called. Prints its arguments on stdout separated by
// spaces and ending with a newline.
v8::Handle<v8::Value> Print(const v8::Arguments& args) {
bool first = true;
for (int i = 0; i < args.Length(); i++) {
v8::HandleScope handle_scope;
if (first) {
first = false;
} else {
printf(" ");
}
v8::String::Utf8Value str(args[i]);
const char* cstr = ToCString(str);
printf("%s", cstr);
}
printf("\n");
fflush(stdout);
return v8::Undefined();
}
// The callback that is invoked by v8 whenever the JavaScript 'load'
// function is called. Loads, compiles and executes its argument
// JavaScript file.
v8::Handle<v8::Value> Load(const v8::Arguments& args) {
for (int i = 0; i < args.Length(); i++) {
v8::HandleScope handle_scope;
v8::String::Utf8Value file(args[i]);
if (*file == NULL) {
return v8::ThrowException(v8::String::New("Error loading file"));
}
v8::Handle<v8::String> source = ReadFile(*file);
if (source.IsEmpty()) {
return v8::ThrowException(v8::String::New("Error loading file"));
}
if (!ExecuteString(source, v8::String::New(*file), false, false)) {
return v8::ThrowException(v8::String::New("Error executing file"));
}
}
return v8::Undefined();
}
// The callback that is invoked by v8 whenever the JavaScript 'quit'
// function is called. Quits.
v8::Handle<v8::Value> Quit(const v8::Arguments& args) {
// If not arguments are given args[0] will yield undefined which
// converts to the integer value 0.
int exit_code = args[0]->Int32Value();
exit(exit_code);
return v8::Undefined();
}
v8::Handle<v8::Value> Version(const v8::Arguments& args) {
return v8::String::New(v8::V8::GetVersion());
}
// Reads a file into a v8 string.
v8::Handle<v8::String> ReadFile(const char* name) {
FILE* file = fopen(name, "rb");
if (file == NULL) return v8::Handle<v8::String>();
fseek(file, 0, SEEK_END);
int size = ftell(file);
rewind(file);
char* chars = new char[size + 1];
chars[size] = '\0';
for (int i = 0; i < size;) {
int read = fread(&chars[i], 1, size - i, file);
i += read;
}
fclose(file);
v8::Handle<v8::String> result = v8::String::New(chars, size);
delete[] chars;
return result;
}
// The read-eval-execute loop of the shell.
void RunShell(v8::Handle<v8::Context> context) {
printf("V8 version %s\n", v8::V8::GetVersion());
static const int kBufferSize = 256;
while (true) {
char buffer[kBufferSize];
printf("> ");
char* str = fgets(buffer, kBufferSize, stdin);
if (str == NULL) break;
v8::HandleScope handle_scope;
ExecuteString(v8::String::New(str),
v8::String::New("(shell)"),
true,
true);
}
printf("\n");
}
// Executes a string within the current v8 context.
bool ExecuteString(v8::Handle<v8::String> source,
v8::Handle<v8::Value> name,
bool print_result,
bool report_exceptions) {
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
if (script.IsEmpty()) {
// Print errors that happened during compilation.
if (report_exceptions)
ReportException(&try_catch);
return false;
} else {
v8::Handle<v8::Value> result = script->Run();
if (result.IsEmpty()) {
// Print errors that happened during execution.
if (report_exceptions)
ReportException(&try_catch);
return false;
} else {
if (print_result && !result->IsUndefined()) {
// If all went well and the result wasn't undefined then print
// the returned value.
v8::String::Utf8Value str(result);
const char* cstr = ToCString(str);
printf("%s\n", cstr);
}
return true;
}
}
}
void ReportException(v8::TryCatch* try_catch) {
v8::HandleScope handle_scope;
v8::String::Utf8Value exception(try_catch->Exception());
const char* exception_string = ToCString(exception);
v8::Handle<v8::Message> message = try_catch->Message();
if (message.IsEmpty()) {
// V8 didn't provide any extra information about this error; just
// print the exception.
printf("%s\n", exception_string);
} else {
// Print (filename):(line number): (message).
v8::String::Utf8Value filename(message->GetScriptResourceName());
const char* filename_string = ToCString(filename);
int linenum = message->GetLineNumber();
printf("%s:%i: %s\n", filename_string, linenum, exception_string);
// Print line of source code.
v8::String::Utf8Value sourceline(message->GetSourceLine());
const char* sourceline_string = ToCString(sourceline);
printf("%s\n", sourceline_string);
// Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn();
for (int i = 0; i < start; i++) {
printf(" ");
}
int end = message->GetEndColumn();
for (int i = start; i < end; i++) {
printf("^");
}
printf("\n");
}
}

25
deps/v8/scons-LICENSE

@ -0,0 +1,25 @@
Copyright and license for SCons - a software construction tool
This copyright and license do not apply to any other software
with which this software may have been included.
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
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 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.

204
deps/v8/scons-README

@ -0,0 +1,204 @@
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
SCons - a software construction tool
This is the scons-README file for a version of SCons packaged for local
execution--that is, execution out of a specific local directory, without
having to install SCons as a system-wide utility.
You are likely reading this file in one of the following two situations:
1) You have unpacked an scons-local-{version} package and are
examining the contents.
In this case, you are presumably interested in using this
package to include a local copy of SCons with some other
software that you package, so that you can use SCons to build
your software without forcing all of your users to have it fully
installed. Instructions for this can be found below.
If you are not looking to use SCons in this way, then please
use either the scons-{version} package to install SCons on your
system, or the scons-src-{version} package if you want the full
source to SCons, including its packaging code and underlying
tests and testing infrastructure.
2) This file was included in some other software package so that
the package could be built using SCons.
In this case, follow the instructions provided with the
rest of the software package for how to use SCons to build
and/or install the software. The file containing build and
installation instructions will typically be named README or
INSTALL.
LATEST VERSION
==============
Before going further, you can check for the latest version of the
scons-local package, or any SCons package, at the SCons download page:
http://www.scons.org/download.html
EXECUTION REQUIREMENTS
======================
Running SCons requires Python version 1.5.2 or later. There should be
no other dependencies or requirements to run SCons.
The default SCons configuration assumes use of the Microsoft Visual C++
compiler suite on WIN32 systems, and assumes a C compiler named 'cc',
a C++ compiler named 'c++', and a Fortran compiler named 'g77' (such
as found in the GNU C compiler suite) on any other type of system.
You may, of course, override these default values by appropriate
configuration of Environment construction variables.
INSTALLATION
============
Installation of this package should be as simple as unpacking the
archive (either .tar.gz or .zip) in any directory (top-level or a
subdirectory) within the software package with which you want to ship
SCons.
Once you have installed this package, you should write an SConstruct
file at the top level of your source tree to build your software as you
see fit.
Then modify the build/install instructions for your package to instruct
your users to execute SCons as follows (if you installed this package in
your top-level directory):
$ python scons.py
Or (if, for example, you installed this package in a subdirectory named
"scons"):
$ python scons/scons.py
That should be all you have to do. (If it isn't that simple, please let
us know!)
CONTENTS OF THIS PACKAGE
========================
This scons-local package consists of the following:
scons-LICENSE
A copy of the copyright and terms under which SCons is
distributed (the Open Source Initiative-approved MIT license).
A disclaimer has been added to the beginning to make clear that
this license applies only to SCons, and not to any separate
software you've written with which you're planning to package
SCons.
scons-README
What you're looking at right now.
scons-local-{version}/
The SCons build engine. This is structured as a Python
library.
scons.py
The SCons script itself. The script sets up the Python
sys.path variable to use the build engine found in the
scons-local-{version}/ directory in preference to any other
SCons build engine installed on your system.
DOCUMENTATION
=============
Because this package is intended to be included with other software by
experienced users, we have not included any SCons documentation in this
package (other than this scons-README file you're reading right now).
If, however, you need documentation about SCons, then consult any of the
following from the corresponding scons-{version} or scons-src-{version}
package:
The RELEASE.txt file (src/RELEASE.txt file in the
scons-src-{version} package), which contains notes about this
specific release, including known problems.
The CHANGES.txt file (src/CHANGES.txt file in the
scons-src-{version} package), which contains a list of changes
since the previous release.
The scons.1 man page (doc/man/scons.1 in the scons-src-{version}
package), which contains a section of small examples for getting
started using SCons.
Additional documentation for SCons is available at:
http://www.scons.org/doc.html
LICENSING
=========
SCons is distributed under the MIT license, a full copy of which is
available in the scons-LICENSE file in this package. The MIT license is
an approved Open Source license, which means:
This software is OSI Certified Open Source Software. OSI
Certified is a certification mark of the Open Source Initiative.
More information about OSI certifications and Open Source software is
available at:
http://www.opensource.org/
REPORTING BUGS
==============
You can report bugs either by following the "Tracker - Bugs" link
on the SCons project page:
http://sourceforge.net/projects/scons/
or by sending mail to the SCons developers mailing list:
scons-devel@lists.sourceforge.net
MAILING LISTS
=============
A mailing list for users of SCons is available. You may send questions
or comments to the list at:
scons-users@lists.sourceforge.net
You may subscribe to the scons-users mailing list at:
http://lists.sourceforge.net/lists/listinfo/scons-users
FOR MORE INFORMATION
====================
Check the SCons web site at:
http://www.scons.org/
AUTHOR INFO
===========
Steven Knight
knight at baldmt dot com
http://www.baldmt.com/~knight/
With plenty of help from the SCons Development team:
Chad Austin
Charles Crain
Steve Leblanc
Anthony Roach
Terrel Shumway

1513
deps/v8/scons-time.py

File diff suppressed because it is too large

165
deps/v8/scons.py

@ -0,0 +1,165 @@
#! /usr/bin/env python
#
# SCons - a Software Constructor
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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 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.
#
__revision__ = "src/script/scons.py 3842 2008/12/20 22:59:52 scons"
__version__ = "1.2.0"
__build__ = "r3842"
__buildsys__ = "scons-dev"
__date__ = "2008/12/20 22:59:52"
__developer__ = "scons"
import os
import os.path
import sys
##############################################################################
# BEGIN STANDARD SCons SCRIPT HEADER
#
# This is the cut-and-paste logic so that a self-contained script can
# interoperate correctly with different SCons versions and installation
# locations for the engine. If you modify anything in this section, you
# should also change other scripts that use this same header.
##############################################################################
# Strip the script directory from sys.path() so on case-insensitive
# (WIN32) systems Python doesn't think that the "scons" script is the
# "SCons" package. Replace it with our own library directories
# (version-specific first, in case they installed by hand there,
# followed by generic) so we pick up the right version of the build
# engine modules if they're in either directory.
script_dir = sys.path[0]
if script_dir in sys.path:
sys.path.remove(script_dir)
libs = []
if os.environ.has_key("SCONS_LIB_DIR"):
libs.append(os.environ["SCONS_LIB_DIR"])
local_version = 'scons-local-' + __version__
local = 'scons-local'
if script_dir:
local_version = os.path.join(script_dir, local_version)
local = os.path.join(script_dir, local)
libs.append(os.path.abspath(local_version))
libs.append(os.path.abspath(local))
scons_version = 'scons-%s' % __version__
prefs = []
if sys.platform == 'win32':
# sys.prefix is (likely) C:\Python*;
# check only C:\Python*.
prefs.append(sys.prefix)
prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages'))
else:
# On other (POSIX) platforms, things are more complicated due to
# the variety of path names and library locations. Try to be smart
# about it.
if script_dir == 'bin':
# script_dir is `pwd`/bin;
# check `pwd`/lib/scons*.
prefs.append(os.getcwd())
else:
if script_dir == '.' or script_dir == '':
script_dir = os.getcwd()
head, tail = os.path.split(script_dir)
if tail == "bin":
# script_dir is /foo/bin;
# check /foo/lib/scons*.
prefs.append(head)
head, tail = os.path.split(sys.prefix)
if tail == "usr":
# sys.prefix is /foo/usr;
# check /foo/usr/lib/scons* first,
# then /foo/usr/local/lib/scons*.
prefs.append(sys.prefix)
prefs.append(os.path.join(sys.prefix, "local"))
elif tail == "local":
h, t = os.path.split(head)
if t == "usr":
# sys.prefix is /foo/usr/local;
# check /foo/usr/local/lib/scons* first,
# then /foo/usr/lib/scons*.
prefs.append(sys.prefix)
prefs.append(head)
else:
# sys.prefix is /foo/local;
# check only /foo/local/lib/scons*.
prefs.append(sys.prefix)
else:
# sys.prefix is /foo (ends in neither /usr or /local);
# check only /foo/lib/scons*.
prefs.append(sys.prefix)
temp = map(lambda x: os.path.join(x, 'lib'), prefs)
temp.extend(map(lambda x: os.path.join(x,
'lib',
'python' + sys.version[:3],
'site-packages'),
prefs))
prefs = temp
# Add the parent directory of the current python's library to the
# preferences. On SuSE-91/AMD64, for example, this is /usr/lib64,
# not /usr/lib.
try:
libpath = os.__file__
except AttributeError:
pass
else:
# Split /usr/libfoo/python*/os.py to /usr/libfoo/python*.
libpath, tail = os.path.split(libpath)
# Split /usr/libfoo/python* to /usr/libfoo
libpath, tail = os.path.split(libpath)
# Check /usr/libfoo/scons*.
prefs.append(libpath)
# Look first for 'scons-__version__' in all of our preference libs,
# then for 'scons'.
libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
sys.path = libs + sys.path
##############################################################################
# END STANDARD SCons SCRIPT HEADER
##############################################################################
if __name__ == "__main__":
import SCons.Script
# this does all the work, and calls sys.exit
# with the proper exit status when done.
SCons.Script.main()

502
deps/v8/sconsign.py

@ -0,0 +1,502 @@
#! /usr/bin/env python
#
# SCons - a Software Constructor
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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 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.
#
__revision__ = "src/script/sconsign.py 3842 2008/12/20 22:59:52 scons"
__version__ = "1.2.0"
__build__ = "r3842"
__buildsys__ = "scons-dev"
__date__ = "2008/12/20 22:59:52"
__developer__ = "scons"
import os
import os.path
import sys
import time
##############################################################################
# BEGIN STANDARD SCons SCRIPT HEADER
#
# This is the cut-and-paste logic so that a self-contained script can
# interoperate correctly with different SCons versions and installation
# locations for the engine. If you modify anything in this section, you
# should also change other scripts that use this same header.
##############################################################################
# Strip the script directory from sys.path() so on case-insensitive
# (WIN32) systems Python doesn't think that the "scons" script is the
# "SCons" package. Replace it with our own library directories
# (version-specific first, in case they installed by hand there,
# followed by generic) so we pick up the right version of the build
# engine modules if they're in either directory.
script_dir = sys.path[0]
if script_dir in sys.path:
sys.path.remove(script_dir)
libs = []
if os.environ.has_key("SCONS_LIB_DIR"):
libs.append(os.environ["SCONS_LIB_DIR"])
local_version = 'scons-local-' + __version__
local = 'scons-local'
if script_dir:
local_version = os.path.join(script_dir, local_version)
local = os.path.join(script_dir, local)
libs.append(os.path.abspath(local_version))
libs.append(os.path.abspath(local))
scons_version = 'scons-%s' % __version__
prefs = []
if sys.platform == 'win32':
# sys.prefix is (likely) C:\Python*;
# check only C:\Python*.
prefs.append(sys.prefix)
prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages'))
else:
# On other (POSIX) platforms, things are more complicated due to
# the variety of path names and library locations. Try to be smart
# about it.
if script_dir == 'bin':
# script_dir is `pwd`/bin;
# check `pwd`/lib/scons*.
prefs.append(os.getcwd())
else:
if script_dir == '.' or script_dir == '':
script_dir = os.getcwd()
head, tail = os.path.split(script_dir)
if tail == "bin":
# script_dir is /foo/bin;
# check /foo/lib/scons*.
prefs.append(head)
head, tail = os.path.split(sys.prefix)
if tail == "usr":
# sys.prefix is /foo/usr;
# check /foo/usr/lib/scons* first,
# then /foo/usr/local/lib/scons*.
prefs.append(sys.prefix)
prefs.append(os.path.join(sys.prefix, "local"))
elif tail == "local":
h, t = os.path.split(head)
if t == "usr":
# sys.prefix is /foo/usr/local;
# check /foo/usr/local/lib/scons* first,
# then /foo/usr/lib/scons*.
prefs.append(sys.prefix)
prefs.append(head)
else:
# sys.prefix is /foo/local;
# check only /foo/local/lib/scons*.
prefs.append(sys.prefix)
else:
# sys.prefix is /foo (ends in neither /usr or /local);
# check only /foo/lib/scons*.
prefs.append(sys.prefix)
temp = map(lambda x: os.path.join(x, 'lib'), prefs)
temp.extend(map(lambda x: os.path.join(x,
'lib',
'python' + sys.version[:3],
'site-packages'),
prefs))
prefs = temp
# Add the parent directory of the current python's library to the
# preferences. On SuSE-91/AMD64, for example, this is /usr/lib64,
# not /usr/lib.
try:
libpath = os.__file__
except AttributeError:
pass
else:
# Split /usr/libfoo/python*/os.py to /usr/libfoo/python*.
libpath, tail = os.path.split(libpath)
# Split /usr/libfoo/python* to /usr/libfoo
libpath, tail = os.path.split(libpath)
# Check /usr/libfoo/scons*.
prefs.append(libpath)
# Look first for 'scons-__version__' in all of our preference libs,
# then for 'scons'.
libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
sys.path = libs + sys.path
##############################################################################
# END STANDARD SCons SCRIPT HEADER
##############################################################################
import cPickle
import imp
import string
import whichdb
import SCons.SConsign
def my_whichdb(filename):
if filename[-7:] == ".dblite":
return "SCons.dblite"
try:
f = open(filename + ".dblite", "rb")
f.close()
return "SCons.dblite"
except IOError:
pass
return _orig_whichdb(filename)
_orig_whichdb = whichdb.whichdb
whichdb.whichdb = my_whichdb
def my_import(mname):
if '.' in mname:
i = string.rfind(mname, '.')
parent = my_import(mname[:i])
fp, pathname, description = imp.find_module(mname[i+1:],
parent.__path__)
else:
fp, pathname, description = imp.find_module(mname)
return imp.load_module(mname, fp, pathname, description)
class Flagger:
default_value = 1
def __setitem__(self, item, value):
self.__dict__[item] = value
self.default_value = 0
def __getitem__(self, item):
return self.__dict__.get(item, self.default_value)
Do_Call = None
Print_Directories = []
Print_Entries = []
Print_Flags = Flagger()
Verbose = 0
Readable = 0
def default_mapper(entry, name):
try:
val = eval("entry."+name)
except:
val = None
return str(val)
def map_action(entry, name):
try:
bact = entry.bact
bactsig = entry.bactsig
except AttributeError:
return None
return '%s [%s]' % (bactsig, bact)
def map_timestamp(entry, name):
try:
timestamp = entry.timestamp
except AttributeError:
timestamp = None
if Readable and timestamp:
return "'" + time.ctime(timestamp) + "'"
else:
return str(timestamp)
def map_bkids(entry, name):
try:
bkids = entry.bsources + entry.bdepends + entry.bimplicit
bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs
except AttributeError:
return None
result = []
for i in xrange(len(bkids)):
result.append(nodeinfo_string(bkids[i], bkidsigs[i], " "))
if result == []:
return None
return string.join(result, "\n ")
map_field = {
'action' : map_action,
'timestamp' : map_timestamp,
'bkids' : map_bkids,
}
map_name = {
'implicit' : 'bkids',
}
def field(name, entry, verbose=Verbose):
if not Print_Flags[name]:
return None
fieldname = map_name.get(name, name)
mapper = map_field.get(fieldname, default_mapper)
val = mapper(entry, name)
if verbose:
val = name + ": " + val
return val
def nodeinfo_raw(name, ninfo, prefix=""):
# This just formats the dictionary, which we would normally use str()
# to do, except that we want the keys sorted for deterministic output.
d = ninfo.__dict__
try:
keys = ninfo.field_list + ['_version_id']
except AttributeError:
keys = d.keys()
keys.sort()
l = []
for k in keys:
l.append('%s: %s' % (repr(k), repr(d.get(k))))
if '\n' in name:
name = repr(name)
return name + ': {' + string.join(l, ', ') + '}'
def nodeinfo_cooked(name, ninfo, prefix=""):
try:
field_list = ninfo.field_list
except AttributeError:
field_list = []
f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v)
if '\n' in name:
name = repr(name)
outlist = [name+':'] + filter(None, map(f, field_list))
if Verbose:
sep = '\n ' + prefix
else:
sep = ' '
return string.join(outlist, sep)
nodeinfo_string = nodeinfo_cooked
def printfield(name, entry, prefix=""):
outlist = field("implicit", entry, 0)
if outlist:
if Verbose:
print " implicit:"
print " " + outlist
outact = field("action", entry, 0)
if outact:
if Verbose:
print " action: " + outact
else:
print " " + outact
def printentries(entries, location):
if Print_Entries:
for name in Print_Entries:
try:
entry = entries[name]
except KeyError:
sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location))
else:
try:
ninfo = entry.ninfo
except AttributeError:
print name + ":"
else:
print nodeinfo_string(name, entry.ninfo)
printfield(name, entry.binfo)
else:
names = entries.keys()
names.sort()
for name in names:
entry = entries[name]
try:
ninfo = entry.ninfo
except AttributeError:
print name + ":"
else:
print nodeinfo_string(name, entry.ninfo)
printfield(name, entry.binfo)
class Do_SConsignDB:
def __init__(self, dbm_name, dbm):
self.dbm_name = dbm_name
self.dbm = dbm
def __call__(self, fname):
# The *dbm modules stick their own file suffixes on the names
# that are passed in. This is causes us to jump through some
# hoops here to be able to allow the user
try:
# Try opening the specified file name. Example:
# SPECIFIED OPENED BY self.dbm.open()
# --------- -------------------------
# .sconsign => .sconsign.dblite
# .sconsign.dblite => .sconsign.dblite.dblite
db = self.dbm.open(fname, "r")
except (IOError, OSError), e:
print_e = e
try:
# That didn't work, so try opening the base name,
# so that if the actually passed in 'sconsign.dblite'
# (for example), the dbm module will put the suffix back
# on for us and open it anyway.
db = self.dbm.open(os.path.splitext(fname)[0], "r")
except (IOError, OSError):
# That didn't work either. See if the file name
# they specified just exists (independent of the dbm
# suffix-mangling).
try:
open(fname, "r")
except (IOError, OSError), e:
# Nope, that file doesn't even exist, so report that
# fact back.
print_e = e
sys.stderr.write("sconsign: %s\n" % (print_e))
return
except KeyboardInterrupt:
raise
except cPickle.UnpicklingError:
sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname))
return
except Exception, e:
sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e))
return
if Print_Directories:
for dir in Print_Directories:
try:
val = db[dir]
except KeyError:
sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0]))
else:
self.printentries(dir, val)
else:
keys = db.keys()
keys.sort()
for dir in keys:
self.printentries(dir, db[dir])
def printentries(self, dir, val):
print '=== ' + dir + ':'
printentries(cPickle.loads(val), dir)
def Do_SConsignDir(name):
try:
fp = open(name, 'rb')
except (IOError, OSError), e:
sys.stderr.write("sconsign: %s\n" % (e))
return
try:
sconsign = SCons.SConsign.Dir(fp)
except KeyboardInterrupt:
raise
except cPickle.UnpicklingError:
sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name))
return
except Exception, e:
sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e))
return
printentries(sconsign.entries, args[0])
##############################################################################
import getopt
helpstr = """\
Usage: sconsign [OPTIONS] FILE [...]
Options:
-a, --act, --action Print build action information.
-c, --csig Print content signature information.
-d DIR, --dir=DIR Print only info about DIR.
-e ENTRY, --entry=ENTRY Print only info about ENTRY.
-f FORMAT, --format=FORMAT FILE is in the specified FORMAT.
-h, --help Print this message and exit.
-i, --implicit Print implicit dependency information.
-r, --readable Print timestamps in human-readable form.
--raw Print raw Python object representations.
-s, --size Print file sizes.
-t, --timestamp Print timestamp information.
-v, --verbose Verbose, describe each field.
"""
opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv",
['act', 'action',
'csig', 'dir=', 'entry=',
'format=', 'help', 'implicit',
'raw', 'readable',
'size', 'timestamp', 'verbose'])
for o, a in opts:
if o in ('-a', '--act', '--action'):
Print_Flags['action'] = 1
elif o in ('-c', '--csig'):
Print_Flags['csig'] = 1
elif o in ('-d', '--dir'):
Print_Directories.append(a)
elif o in ('-e', '--entry'):
Print_Entries.append(a)
elif o in ('-f', '--format'):
Module_Map = {'dblite' : 'SCons.dblite',
'sconsign' : None}
dbm_name = Module_Map.get(a, a)
if dbm_name:
try:
dbm = my_import(dbm_name)
except:
sys.stderr.write("sconsign: illegal file format `%s'\n" % a)
print helpstr
sys.exit(2)
Do_Call = Do_SConsignDB(a, dbm)
else:
Do_Call = Do_SConsignDir
elif o in ('-h', '--help'):
print helpstr
sys.exit(0)
elif o in ('-i', '--implicit'):
Print_Flags['implicit'] = 1
elif o in ('--raw',):
nodeinfo_string = nodeinfo_raw
elif o in ('-r', '--readable'):
Readable = 1
elif o in ('-s', '--size'):
Print_Flags['size'] = 1
elif o in ('-t', '--timestamp'):
Print_Flags['timestamp'] = 1
elif o in ('-v', '--verbose'):
Verbose = 1
if Do_Call:
for a in args:
Do_Call(a)
else:
for a in args:
dbm_name = whichdb.whichdb(a)
if dbm_name:
Map_Module = {'SCons.dblite' : 'dblite'}
dbm = my_import(dbm_name)
Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a)
else:
Do_SConsignDir(a)
sys.exit(0)

183
deps/v8/src/SConscript

@ -0,0 +1,183 @@
# Copyright 2008 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
from os.path import join, dirname, abspath
root_dir = dirname(File('SConstruct').rfile().abspath)
sys.path.append(join(root_dir, 'tools'))
import js2c
Import('context')
SOURCES = {
'all': [
'accessors.cc', 'allocation.cc', 'api.cc', 'assembler.cc', 'ast.cc',
'bootstrapper.cc', 'builtins.cc', 'checks.cc', 'code-stubs.cc',
'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc',
'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc',
'debug-agent.cc', 'disassembler.cc', 'execution.cc', 'factory.cc',
'flags.cc', 'frames.cc', 'func-name-inferrer.cc',
'global-handles.cc', 'handles.cc', 'hashmap.cc',
'heap.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc',
'jump-target.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
'oprofile-agent.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc',
'regexp-macro-assembler-irregexp.cc', 'regexp-stack.cc',
'register-allocator.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc',
'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc',
'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc',
'v8.cc', 'v8threads.cc', 'variables.cc', 'virtual-frame.cc', 'zone.cc'
],
'arch:arm': [
'assembler-arm.cc', 'builtins-arm.cc', 'codegen-arm.cc', 'cpu-arm.cc',
'disasm-arm.cc', 'debug-arm.cc', 'frames-arm.cc', 'ic-arm.cc',
'jump-target-arm.cc', 'macro-assembler-arm.cc',
'regexp-macro-assembler-arm.cc', 'register-allocator-arm.cc',
'stub-cache-arm.cc', 'virtual-frame-arm.cc'
],
'arch:ia32': [
'assembler-ia32.cc', 'builtins-ia32.cc', 'codegen-ia32.cc',
'cpu-ia32.cc', 'disasm-ia32.cc', 'debug-ia32.cc', 'frames-ia32.cc',
'ic-ia32.cc', 'jump-target-ia32.cc', 'macro-assembler-ia32.cc',
'regexp-macro-assembler-ia32.cc', 'register-allocator-ia32.cc',
'stub-cache-ia32.cc', 'virtual-frame-ia32.cc'
],
'simulator:arm': ['simulator-arm.cc'],
'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'],
'os:linux': ['platform-linux.cc', 'platform-posix.cc'],
'os:android': ['platform-linux.cc', 'platform-posix.cc'],
'os:macos': ['platform-macos.cc', 'platform-posix.cc'],
'os:nullos': ['platform-nullos.cc'],
'os:win32': ['platform-win32.cc'],
'mode:release': [],
'mode:debug': [
'objects-debug.cc', 'prettyprinter.cc', 'regexp-macro-assembler-tracer.cc'
]
}
D8_FILES = {
'all': [
'd8.cc', 'd8-debug.cc'
],
'os:linux': [
'd8-posix.cc'
],
'os:macos': [
'd8-posix.cc'
],
'os:android': [
'd8-posix.cc'
],
'os:freebsd': [
'd8-posix.cc'
],
'os:win32': [
'd8-windows.cc'
],
'os:nullos': [
'd8-windows.cc' # Empty implementation at the moment.
],
'console:readline': [
'd8-readline.cc'
]
}
LIBRARY_FILES = '''
runtime.js
v8natives.js
array.js
string.js
uri.js
math.js
messages.js
apinatives.js
debug-delay.js
mirror-delay.js
date-delay.js
regexp-delay.js
'''.split()
def Abort(message):
print message
sys.exit(1)
def ConfigureObjectFiles():
env = Environment()
env.Replace(**context.flags['v8'])
context.ApplyEnvOverrides(env)
env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C)
env['BUILDERS']['Snapshot'] = Builder(action='$SOURCE $TARGET --logfile "$LOGFILE"')
# Build the standard platform-independent source files.
source_files = context.GetRelevantSources(SOURCES)
d8_files = context.GetRelevantSources(D8_FILES)
d8_js = env.JS2C('d8-js.cc', 'd8.js', TYPE='D8')
d8_js_obj = context.ConfigureObject(env, d8_js, CPPPATH=['.'])
d8_objs = [context.ConfigureObject(env, [d8_files]), d8_js_obj]
# Combine the JavaScript library files into a single C++ file and
# compile it.
library_files = [s for s in LIBRARY_FILES]
library_files.append('macros.py')
libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE')
libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.'])
# Build dtoa.
dtoa_env = env.Copy()
dtoa_env.Replace(**context.flags['dtoa'])
dtoa_files = ['dtoa-config.c']
dtoa_obj = context.ConfigureObject(dtoa_env, dtoa_files)
source_objs = context.ConfigureObject(env, source_files)
non_snapshot_files = [dtoa_obj, source_objs]
# Create snapshot if necessary.
empty_snapshot_obj = context.ConfigureObject(env, 'snapshot-empty.cc')
mksnapshot_env = env.Copy()
mksnapshot_env.Replace(**context.flags['mksnapshot'])
mksnapshot_src = 'mksnapshot.cc'
mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
if context.use_snapshot:
if context.build_snapshot:
snapshot_cc = env.Snapshot('snapshot.cc', mksnapshot, LOGFILE=File('snapshot.log').abspath)
else:
snapshot_cc = Command('snapshot.cc', [], [])
snapshot_obj = context.ConfigureObject(env, snapshot_cc, CPPPATH=['.'])
libraries_obj = context.ConfigureObject(env, libraries_empty_src, CPPPATH=['.'])
else:
snapshot_obj = empty_snapshot_obj
library_objs = [non_snapshot_files, libraries_obj, snapshot_obj]
return (library_objs, d8_objs, [mksnapshot])
(library_objs, d8_objs, mksnapshot) = ConfigureObjectFiles()
Return('library_objs d8_objs mksnapshot')

548
deps/v8/src/accessors.cc

@ -0,0 +1,548 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "accessors.h"
#include "execution.h"
#include "factory.h"
#include "scopeinfo.h"
#include "top.h"
#include "zone-inl.h"
namespace v8 { namespace internal {
template <class C>
static C* FindInPrototypeChain(Object* obj, bool* found_it) {
ASSERT(!*found_it);
while (!Is<C>(obj)) {
if (obj == Heap::null_value()) return NULL;
obj = obj->GetPrototype();
}
*found_it = true;
return C::cast(obj);
}
// Entry point that never should be called.
Object* Accessors::IllegalSetter(JSObject*, Object*, void*) {
UNREACHABLE();
return NULL;
}
Object* Accessors::IllegalGetAccessor(Object* object, void*) {
UNREACHABLE();
return object;
}
Object* Accessors::ReadOnlySetAccessor(JSObject*, Object* value, void*) {
// According to ECMA-262, section 8.6.2.2, page 28, setting
// read-only properties must be silently ignored.
return value;
}
//
// Accessors::ArrayLength
//
Object* Accessors::ArrayGetLength(Object* object, void*) {
// Traverse the prototype chain until we reach an array.
bool found_it = false;
JSArray* holder = FindInPrototypeChain<JSArray>(object, &found_it);
if (!found_it) return Smi::FromInt(0);
return holder->length();
}
// The helper function will 'flatten' Number objects.
Object* Accessors::FlattenNumber(Object* value) {
if (value->IsNumber() || !value->IsJSValue()) return value;
JSValue* wrapper = JSValue::cast(value);
ASSERT(
Top::context()->global_context()->number_function()->has_initial_map());
Map* number_map =
Top::context()->global_context()->number_function()->initial_map();
if (wrapper->map() == number_map) return wrapper->value();
return value;
}
Object* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
value = FlattenNumber(value);
// Need to call methods that may trigger GC.
HandleScope scope;
// Protect raw pointers.
Handle<JSObject> object_handle(object);
Handle<Object> value_handle(value);
bool has_exception;
Handle<Object> uint32_v = Execution::ToUint32(value_handle, &has_exception);
if (has_exception) return Failure::Exception();
Handle<Object> number_v = Execution::ToNumber(value_handle, &has_exception);
if (has_exception) return Failure::Exception();
// Restore raw pointers,
object = *object_handle;
value = *value_handle;
if (uint32_v->Number() == number_v->Number()) {
if (object->IsJSArray()) {
return JSArray::cast(object)->SetElementsLength(*uint32_v);
} else {
// This means one of the object's prototypes is a JSArray and
// the object does not have a 'length' property.
// Calling SetProperty causes an infinite loop.
return object->IgnoreAttributesAndSetLocalProperty(Heap::length_symbol(),
value, NONE);
}
}
return Top::Throw(*Factory::NewRangeError("invalid_array_length",
HandleVector<Object>(NULL, 0)));
}
const AccessorDescriptor Accessors::ArrayLength = {
ArrayGetLength,
ArraySetLength,
0
};
//
// Accessors::StringLength
//
Object* Accessors::StringGetLength(Object* object, void*) {
Object* value = object;
if (object->IsJSValue()) value = JSValue::cast(object)->value();
if (value->IsString()) return Smi::FromInt(String::cast(value)->length());
// If object is not a string we return 0 to be compatible with WebKit.
// Note: Firefox returns the length of ToString(object).
return Smi::FromInt(0);
}
const AccessorDescriptor Accessors::StringLength = {
StringGetLength,
IllegalSetter,
0
};
//
// Accessors::ScriptSource
//
Object* Accessors::ScriptGetSource(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->source();
}
const AccessorDescriptor Accessors::ScriptSource = {
ScriptGetSource,
IllegalSetter,
0
};
//
// Accessors::ScriptName
//
Object* Accessors::ScriptGetName(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->name();
}
const AccessorDescriptor Accessors::ScriptName = {
ScriptGetName,
IllegalSetter,
0
};
//
// Accessors::ScriptId
//
Object* Accessors::ScriptGetId(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->id();
}
const AccessorDescriptor Accessors::ScriptId = {
ScriptGetId,
IllegalSetter,
0
};
//
// Accessors::ScriptLineOffset
//
Object* Accessors::ScriptGetLineOffset(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->line_offset();
}
const AccessorDescriptor Accessors::ScriptLineOffset = {
ScriptGetLineOffset,
IllegalSetter,
0
};
//
// Accessors::ScriptColumnOffset
//
Object* Accessors::ScriptGetColumnOffset(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->column_offset();
}
const AccessorDescriptor Accessors::ScriptColumnOffset = {
ScriptGetColumnOffset,
IllegalSetter,
0
};
//
// Accessors::ScriptType
//
Object* Accessors::ScriptGetType(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
return Script::cast(script)->type();
}
const AccessorDescriptor Accessors::ScriptType = {
ScriptGetType,
IllegalSetter,
0
};
//
// Accessors::ScriptGetLineEnds
//
Object* Accessors::ScriptGetLineEnds(Object* object, void*) {
HandleScope scope;
Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
InitScriptLineEnds(script);
return script->line_ends();
}
const AccessorDescriptor Accessors::ScriptLineEnds = {
ScriptGetLineEnds,
IllegalSetter,
0
};
//
// Accessors::FunctionPrototype
//
Object* Accessors::FunctionGetPrototype(Object* object, void*) {
bool found_it = false;
JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value();
if (!function->has_prototype()) {
Object* prototype = Heap::AllocateFunctionPrototype(function);
if (prototype->IsFailure()) return prototype;
Object* result = function->SetPrototype(prototype);
if (result->IsFailure()) return result;
}
return function->prototype();
}
Object* Accessors::FunctionSetPrototype(JSObject* object,
Object* value,
void*) {
bool found_it = false;
JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value();
if (function->has_initial_map()) {
// If the function has allocated the initial map
// replace it with a copy containing the new prototype.
Object* new_map = function->initial_map()->CopyDropTransitions();
if (new_map->IsFailure()) return new_map;
function->set_initial_map(Map::cast(new_map));
}
Object* prototype = function->SetPrototype(value);
if (prototype->IsFailure()) return prototype;
ASSERT(function->prototype() == value);
return function;
}
const AccessorDescriptor Accessors::FunctionPrototype = {
FunctionGetPrototype,
FunctionSetPrototype,
0
};
//
// Accessors::FunctionLength
//
Object* Accessors::FunctionGetLength(Object* object, void*) {
bool found_it = false;
JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Smi::FromInt(0);
// Check if already compiled.
if (!function->is_compiled()) {
// If the function isn't compiled yet, the length is not computed
// correctly yet. Compile it now and return the right length.
HandleScope scope;
Handle<JSFunction> function_handle(function);
if (!CompileLazy(function_handle, KEEP_EXCEPTION)) {
return Failure::Exception();
}
return Smi::FromInt(function_handle->shared()->length());
} else {
return Smi::FromInt(function->shared()->length());
}
}
const AccessorDescriptor Accessors::FunctionLength = {
FunctionGetLength,
ReadOnlySetAccessor,
0
};
//
// Accessors::FunctionName
//
Object* Accessors::FunctionGetName(Object* object, void*) {
bool found_it = false;
JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value();
return holder->shared()->name();
}
const AccessorDescriptor Accessors::FunctionName = {
FunctionGetName,
ReadOnlySetAccessor,
0
};
//
// Accessors::FunctionArguments
//
Object* Accessors::FunctionGetArguments(Object* object, void*) {
HandleScope scope;
bool found_it = false;
JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value();
Handle<JSFunction> function(holder);
// Find the top invocation of the function by traversing frames.
for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
// Skip all frames that aren't invocations of the given function.
JavaScriptFrame* frame = it.frame();
if (frame->function() != *function) continue;
// If there is an arguments variable in the stack, we return that.
int index = ScopeInfo<>::StackSlotIndex(frame->code(),
Heap::arguments_symbol());
if (index >= 0) return frame->GetExpression(index);
// If there isn't an arguments variable in the stack, we need to
// find the frame that holds the actual arguments passed to the
// function on the stack.
it.AdvanceToArgumentsFrame();
frame = it.frame();
// Get the number of arguments and construct an arguments object
// mirror for the right frame.
const int length = frame->GetProvidedParametersCount();
Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
Handle<FixedArray> array = Factory::NewFixedArray(length);
// Copy the parameters to the arguments object.
ASSERT(array->length() == length);
for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i));
arguments->set_elements(*array);
// Return the freshly allocated arguments object.
return *arguments;
}
// No frame corresponding to the given function found. Return null.
return Heap::null_value();
}
const AccessorDescriptor Accessors::FunctionArguments = {
FunctionGetArguments,
ReadOnlySetAccessor,
0
};
//
// Accessors::FunctionCaller
//
Object* Accessors::FunctionGetCaller(Object* object, void*) {
HandleScope scope;
bool found_it = false;
JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value();
Handle<JSFunction> function(holder);
// Find the top invocation of the function by traversing frames.
for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
// Skip all frames that aren't invocations of the given function.
if (it.frame()->function() != *function) continue;
// Once we have found the frame, we need to go to the caller
// frame. This may require skipping through a number of top-level
// frames, e.g. frames for scripts not functions.
while (true) {
it.Advance();
if (it.done()) return Heap::null_value();
JSFunction* caller = JSFunction::cast(it.frame()->function());
if (!caller->shared()->is_toplevel()) return caller;
}
}
// No frame corresponding to the given function found. Return null.
return Heap::null_value();
}
const AccessorDescriptor Accessors::FunctionCaller = {
FunctionGetCaller,
ReadOnlySetAccessor,
0
};
//
// Accessors::ObjectPrototype
//
Object* Accessors::ObjectGetPrototype(Object* receiver, void*) {
Object* current = receiver->GetPrototype();
while (current->IsJSObject() &&
JSObject::cast(current)->map()->is_hidden_prototype()) {
current = current->GetPrototype();
}
return current;
}
Object* Accessors::ObjectSetPrototype(JSObject* receiver,
Object* value,
void*) {
// Before we can set the prototype we need to be sure
// prototype cycles are prevented.
// It is sufficient to validate that the receiver is not in the new prototype
// chain.
// Silently ignore the change if value is not a JSObject or null.
// SpiderMonkey behaves this way.
if (!value->IsJSObject() && !value->IsNull()) return value;
for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
if (JSObject::cast(pt) == receiver) {
// Cycle detected.
HandleScope scope;
return Top::Throw(*Factory::NewError("cyclic_proto",
HandleVector<Object>(NULL, 0)));
}
}
// Find the first object in the chain whose prototype object is not
// hidden and set the new prototype on that object.
JSObject* current = receiver;
Object* current_proto = receiver->GetPrototype();
while (current_proto->IsJSObject() &&
JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
current = JSObject::cast(current_proto);
current_proto = current_proto->GetPrototype();
}
// Set the new prototype of the object.
Object* new_map = current->map()->CopyDropTransitions();
if (new_map->IsFailure()) return new_map;
Map::cast(new_map)->set_prototype(value);
current->set_map(Map::cast(new_map));
// To be consistent with other Set functions, return the value.
return value;
}
const AccessorDescriptor Accessors::ObjectPrototype = {
ObjectGetPrototype,
ObjectSetPrototype,
0
};
} } // namespace v8::internal

101
deps/v8/src/accessors.h

@ -0,0 +1,101 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_ACCESSORS_H_
#define V8_ACCESSORS_H_
namespace v8 { namespace internal {
// The list of accessor descriptors. This is a second-order macro
// taking a macro to be applied to all accessor descriptor names.
#define ACCESSOR_DESCRIPTOR_LIST(V) \
V(FunctionPrototype) \
V(FunctionLength) \
V(FunctionName) \
V(FunctionArguments) \
V(FunctionCaller) \
V(ArrayLength) \
V(StringLength) \
V(ScriptSource) \
V(ScriptName) \
V(ScriptId) \
V(ScriptLineOffset) \
V(ScriptColumnOffset) \
V(ScriptType) \
V(ScriptLineEnds) \
V(ObjectPrototype)
// Accessors contains all predefined proxy accessors.
class Accessors : public AllStatic {
public:
// Accessor descriptors.
#define ACCESSOR_DESCRIPTOR_DECLARATION(name) \
static const AccessorDescriptor name;
ACCESSOR_DESCRIPTOR_LIST(ACCESSOR_DESCRIPTOR_DECLARATION)
#undef ACCESSOR_DESCRIPTOR_DECLARATION
enum DescriptorId {
#define ACCESSOR_DESCRIPTOR_DECLARATION(name) \
k##name,
ACCESSOR_DESCRIPTOR_LIST(ACCESSOR_DESCRIPTOR_DECLARATION)
#undef ACCESSOR_DESCRIPTOR_DECLARATION
descriptorCount
};
// Accessor functions called directly from the runtime system.
static Object* FunctionGetPrototype(Object* object, void*);
static Object* FunctionSetPrototype(JSObject* object, Object* value, void*);
private:
// Accessor functions only used through the descriptor.
static Object* FunctionGetLength(Object* object, void*);
static Object* FunctionGetName(Object* object, void*);
static Object* FunctionGetArguments(Object* object, void*);
static Object* FunctionGetCaller(Object* object, void*);
static Object* ArraySetLength(JSObject* object, Object* value, void*);
static Object* ArrayGetLength(Object* object, void*);
static Object* StringGetLength(Object* object, void*);
static Object* ScriptGetName(Object* object, void*);
static Object* ScriptGetId(Object* object, void*);
static Object* ScriptGetSource(Object* object, void*);
static Object* ScriptGetLineOffset(Object* object, void*);
static Object* ScriptGetColumnOffset(Object* object, void*);
static Object* ScriptGetType(Object* object, void*);
static Object* ScriptGetLineEnds(Object* object, void*);
static Object* ObjectGetPrototype(Object* receiver, void*);
static Object* ObjectSetPrototype(JSObject* receiver, Object* value, void*);
// Helper functions.
static Object* FlattenNumber(Object* value);
static Object* IllegalSetter(JSObject*, Object*, void*);
static Object* IllegalGetAccessor(Object* object, void*);
static Object* ReadOnlySetAccessor(JSObject*, Object* value, void*);
};
} } // namespace v8::internal
#endif // V8_ACCESSORS_H_

197
deps/v8/src/allocation.cc

@ -0,0 +1,197 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include "v8.h"
namespace v8 { namespace internal {
void* Malloced::New(size_t size) {
ASSERT(NativeAllocationChecker::allocation_allowed());
void* result = malloc(size);
if (result == NULL) V8::FatalProcessOutOfMemory("Malloced operator new");
return result;
}
void Malloced::Delete(void* p) {
free(p);
}
void Malloced::FatalProcessOutOfMemory() {
V8::FatalProcessOutOfMemory("Out of memory");
}
#ifdef DEBUG
static void* invalid = static_cast<void*>(NULL);
void* Embedded::operator new(size_t size) {
UNREACHABLE();
return invalid;
}
void Embedded::operator delete(void* p) {
UNREACHABLE();
}
void* AllStatic::operator new(size_t size) {
UNREACHABLE();
return invalid;
}
void AllStatic::operator delete(void* p) {
UNREACHABLE();
}
#endif
char* StrDup(const char* str) {
int length = strlen(str);
char* result = NewArray<char>(length + 1);
memcpy(result, str, length * kCharSize);
result[length] = '\0';
return result;
}
char* StrNDup(const char* str, size_t n) {
size_t length = strlen(str);
if (n < length) length = n;
char* result = NewArray<char>(length + 1);
memcpy(result, str, length * kCharSize);
result[length] = '\0';
return result;
}
int NativeAllocationChecker::allocation_disallowed_ = 0;
PreallocatedStorage PreallocatedStorage::in_use_list_(0);
PreallocatedStorage PreallocatedStorage::free_list_(0);
bool PreallocatedStorage::preallocated_ = false;
void PreallocatedStorage::Init(size_t size) {
ASSERT(free_list_.next_ == &free_list_);
ASSERT(free_list_.previous_ == &free_list_);
PreallocatedStorage* free_chunk =
reinterpret_cast<PreallocatedStorage*>(new char[size]);
free_list_.next_ = free_list_.previous_ = free_chunk;
free_chunk->next_ = free_chunk->previous_ = &free_list_;
free_chunk->size_ = size - sizeof(PreallocatedStorage);
preallocated_ = true;
}
void* PreallocatedStorage::New(size_t size) {
if (!preallocated_) {
return FreeStoreAllocationPolicy::New(size);
}
ASSERT(free_list_.next_ != &free_list_);
ASSERT(free_list_.previous_ != &free_list_);
size = (size + kPointerSize - 1) & ~(kPointerSize - 1);
// Search for exact fit.
for (PreallocatedStorage* storage = free_list_.next_;
storage != &free_list_;
storage = storage->next_) {
if (storage->size_ == size) {
storage->Unlink();
storage->LinkTo(&in_use_list_);
return reinterpret_cast<void*>(storage + 1);
}
}
// Search for first fit.
for (PreallocatedStorage* storage = free_list_.next_;
storage != &free_list_;
storage = storage->next_) {
if (storage->size_ >= size + sizeof(PreallocatedStorage)) {
storage->Unlink();
storage->LinkTo(&in_use_list_);
PreallocatedStorage* left_over =
reinterpret_cast<PreallocatedStorage*>(
reinterpret_cast<char*>(storage + 1) + size);
left_over->size_ = storage->size_ - size - sizeof(PreallocatedStorage);
ASSERT(size + left_over->size_ + sizeof(PreallocatedStorage) ==
storage->size_);
storage->size_ = size;
left_over->LinkTo(&free_list_);
return reinterpret_cast<void*>(storage + 1);
}
}
// Allocation failure.
ASSERT(false);
return NULL;
}
// We don't attempt to coalesce.
void PreallocatedStorage::Delete(void* p) {
if (p == NULL) {
return;
}
if (!preallocated_) {
FreeStoreAllocationPolicy::Delete(p);
return;
}
PreallocatedStorage* storage = reinterpret_cast<PreallocatedStorage*>(p) - 1;
ASSERT(storage->next_->previous_ == storage);
ASSERT(storage->previous_->next_ == storage);
storage->Unlink();
storage->LinkTo(&free_list_);
}
void PreallocatedStorage::LinkTo(PreallocatedStorage* other) {
next_ = other->next_;
other->next_->previous_ = this;
previous_ = other;
other->next_ = this;
}
void PreallocatedStorage::Unlink() {
next_->previous_ = previous_;
previous_->next_ = next_;
}
PreallocatedStorage::PreallocatedStorage(size_t size)
: size_(size) {
previous_ = next_ = this;
}
} } // namespace v8::internal

168
deps/v8/src/allocation.h

@ -0,0 +1,168 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_ALLOCATION_H_
#define V8_ALLOCATION_H_
namespace v8 { namespace internal {
// A class that controls whether allocation is allowed. This is for
// the C++ heap only!
class NativeAllocationChecker {
public:
typedef enum { ALLOW, DISALLOW } NativeAllocationAllowed;
explicit inline NativeAllocationChecker(NativeAllocationAllowed allowed)
: allowed_(allowed) {
#ifdef DEBUG
if (allowed == DISALLOW) {
allocation_disallowed_++;
}
#endif
}
~NativeAllocationChecker() {
#ifdef DEBUG
if (allowed_ == DISALLOW) {
allocation_disallowed_--;
}
#endif
ASSERT(allocation_disallowed_ >= 0);
}
static inline bool allocation_allowed() {
return allocation_disallowed_ == 0;
}
private:
// This static counter ensures that NativeAllocationCheckers can be nested.
static int allocation_disallowed_;
// This flag applies to this particular instance.
NativeAllocationAllowed allowed_;
};
// Superclass for classes managed with new & delete.
class Malloced {
public:
void* operator new(size_t size) { return New(size); }
void operator delete(void* p) { Delete(p); }
static void FatalProcessOutOfMemory();
static void* New(size_t size);
static void Delete(void* p);
};
// A macro is used for defining the base class used for embedded instances.
// The reason is some compilers allocate a minimum of one word for the
// superclass. The macro prevents the use of new & delete in debug mode.
// In release mode we are not willing to pay this overhead.
#ifdef DEBUG
// Superclass for classes with instances allocated inside stack
// activations or inside other objects.
class Embedded {
public:
void* operator new(size_t size);
void operator delete(void* p);
};
#define BASE_EMBEDDED : public Embedded
#else
#define BASE_EMBEDDED
#endif
// Superclass for classes only using statics.
class AllStatic {
#ifdef DEBUG
public:
void* operator new(size_t size);
void operator delete(void* p);
#endif
};
template <typename T>
static T* NewArray(int size) {
ASSERT(NativeAllocationChecker::allocation_allowed());
T* result = new T[size];
if (result == NULL) Malloced::FatalProcessOutOfMemory();
return result;
}
template <typename T>
static void DeleteArray(T* array) {
delete[] array;
}
// The normal strdup functions use malloc. These versions of StrDup
// and StrNDup uses new and calls the FatalProcessOutOfMemory handler
// if allocation fails.
char* StrDup(const char* str);
char* StrNDup(const char* str, size_t n);
// Allocation policy for allocating in the C free store using malloc
// and free. Used as the default policy for lists.
class FreeStoreAllocationPolicy {
public:
INLINE(static void* New(size_t size)) { return Malloced::New(size); }
INLINE(static void Delete(void* p)) { Malloced::Delete(p); }
};
// Allocation policy for allocating in preallocated space.
// Used as an allocation policy for ScopeInfo when generating
// stack traces.
class PreallocatedStorage : public AllStatic {
public:
explicit PreallocatedStorage(size_t size);
size_t size() { return size_; }
static void* New(size_t size);
static void Delete(void* p);
// Preallocate a set number of bytes.
static void Init(size_t size);
private:
size_t size_;
PreallocatedStorage* previous_;
PreallocatedStorage* next_;
static bool preallocated_;
static PreallocatedStorage in_use_list_;
static PreallocatedStorage free_list_;
void LinkTo(PreallocatedStorage* other);
void Unlink();
DISALLOW_IMPLICIT_CONSTRUCTORS(PreallocatedStorage);
};
} } // namespace v8::internal
#endif // V8_ALLOCATION_H_

3348
deps/v8/src/api.cc

File diff suppressed because it is too large

454
deps/v8/src/api.h

@ -0,0 +1,454 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_API_H_
#define V8_API_H_
#include "apiutils.h"
#include "factory.h"
namespace v8 {
// Constants used in the implementation of the API. The most natural thing
// would usually be to place these with the classes that use them, but
// we want to keep them out of v8.h because it is an externally
// visible file.
class Consts {
public:
enum TemplateType {
FUNCTION_TEMPLATE = 0,
OBJECT_TEMPLATE = 1
};
};
// Utilities for working with neander-objects, primitive
// env-independent JSObjects used by the api.
class NeanderObject {
public:
explicit NeanderObject(int size);
inline NeanderObject(v8::internal::Handle<v8::internal::Object> obj);
inline NeanderObject(v8::internal::Object* obj);
inline v8::internal::Object* get(int index);
inline void set(int index, v8::internal::Object* value);
inline v8::internal::Handle<v8::internal::JSObject> value() { return value_; }
int size();
private:
v8::internal::Handle<v8::internal::JSObject> value_;
};
// Utilities for working with neander-arrays, a simple extensible
// array abstraction built on neander-objects.
class NeanderArray {
public:
NeanderArray();
inline NeanderArray(v8::internal::Handle<v8::internal::Object> obj);
inline v8::internal::Handle<v8::internal::JSObject> value() {
return obj_.value();
}
void add(v8::internal::Handle<v8::internal::Object> value);
int length();
v8::internal::Object* get(int index);
// Change the value at an index to undefined value. If the index is
// out of bounds, the request is ignored. Returns the old value.
void set(int index, v8::internal::Object* value);
private:
NeanderObject obj_;
};
NeanderObject::NeanderObject(v8::internal::Handle<v8::internal::Object> obj)
: value_(v8::internal::Handle<v8::internal::JSObject>::cast(obj)) { }
NeanderObject::NeanderObject(v8::internal::Object* obj)
: value_(v8::internal::Handle<v8::internal::JSObject>(
v8::internal::JSObject::cast(obj))) { }
NeanderArray::NeanderArray(v8::internal::Handle<v8::internal::Object> obj)
: obj_(obj) { }
v8::internal::Object* NeanderObject::get(int offset) {
ASSERT(value()->HasFastElements());
return v8::internal::FixedArray::cast(value()->elements())->get(offset);
}
void NeanderObject::set(int offset, v8::internal::Object* value) {
ASSERT(value_->HasFastElements());
v8::internal::FixedArray::cast(value_->elements())->set(offset, value);
}
template <typename T> static inline T ToCData(v8::internal::Object* obj) {
STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
return reinterpret_cast<T>(
reinterpret_cast<intptr_t>(v8::internal::Proxy::cast(obj)->proxy()));
}
template <typename T>
static inline v8::internal::Handle<v8::internal::Object> FromCData(T obj) {
STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
return v8::internal::Factory::NewProxy(
reinterpret_cast<v8::internal::Address>(reinterpret_cast<intptr_t>(obj)));
}
v8::Arguments::Arguments(v8::Local<v8::Value> data,
v8::Local<v8::Object> holder,
v8::Local<v8::Function> callee,
bool is_construct_call,
void** values, int length)
: data_(data), holder_(holder), callee_(callee),
is_construct_call_(is_construct_call),
values_(values), length_(length) { }
enum ExtensionTraversalState {
UNVISITED, VISITED, INSTALLED
};
class RegisteredExtension {
public:
explicit RegisteredExtension(Extension* extension);
static void Register(RegisteredExtension* that);
Extension* extension() { return extension_; }
RegisteredExtension* next() { return next_; }
RegisteredExtension* next_auto() { return next_auto_; }
ExtensionTraversalState state() { return state_; }
void set_state(ExtensionTraversalState value) { state_ = value; }
static RegisteredExtension* first_extension() { return first_extension_; }
private:
Extension* extension_;
RegisteredExtension* next_;
RegisteredExtension* next_auto_;
ExtensionTraversalState state_;
static RegisteredExtension* first_extension_;
static RegisteredExtension* first_auto_extension_;
};
class Utils {
public:
static bool ReportApiFailure(const char* location, const char* message);
static Local<FunctionTemplate> ToFunctionTemplate(NeanderObject obj);
static Local<ObjectTemplate> ToObjectTemplate(NeanderObject obj);
static inline Local<Context> ToLocal(
v8::internal::Handle<v8::internal::Context> obj);
static inline Local<Value> ToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<Function> ToLocal(
v8::internal::Handle<v8::internal::JSFunction> obj);
static inline Local<String> ToLocal(
v8::internal::Handle<v8::internal::String> obj);
static inline Local<Object> ToLocal(
v8::internal::Handle<v8::internal::JSObject> obj);
static inline Local<Array> ToLocal(
v8::internal::Handle<v8::internal::JSArray> obj);
static inline Local<External> ToLocal(
v8::internal::Handle<v8::internal::Proxy> obj);
static inline Local<Message> MessageToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<Number> NumberToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<Integer> IntegerToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<Uint32> Uint32ToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<FunctionTemplate> ToLocal(
v8::internal::Handle<v8::internal::FunctionTemplateInfo> obj);
static inline Local<ObjectTemplate> ToLocal(
v8::internal::Handle<v8::internal::ObjectTemplateInfo> obj);
static inline Local<Signature> ToLocal(
v8::internal::Handle<v8::internal::SignatureInfo> obj);
static inline Local<TypeSwitch> ToLocal(
v8::internal::Handle<v8::internal::TypeSwitchInfo> obj);
static inline v8::internal::Handle<v8::internal::TemplateInfo>
OpenHandle(const Template* that);
static inline v8::internal::Handle<v8::internal::FunctionTemplateInfo>
OpenHandle(const FunctionTemplate* that);
static inline v8::internal::Handle<v8::internal::ObjectTemplateInfo>
OpenHandle(const ObjectTemplate* that);
static inline v8::internal::Handle<v8::internal::Object>
OpenHandle(const Data* data);
static inline v8::internal::Handle<v8::internal::JSObject>
OpenHandle(const v8::Object* data);
static inline v8::internal::Handle<v8::internal::JSArray>
OpenHandle(const v8::Array* data);
static inline v8::internal::Handle<v8::internal::String>
OpenHandle(const String* data);
static inline v8::internal::Handle<v8::internal::JSFunction>
OpenHandle(const Script* data);
static inline v8::internal::Handle<v8::internal::JSFunction>
OpenHandle(const Function* data);
static inline v8::internal::Handle<v8::internal::JSObject>
OpenHandle(const Message* message);
static inline v8::internal::Handle<v8::internal::Context>
OpenHandle(const v8::Context* context);
static inline v8::internal::Handle<v8::internal::SignatureInfo>
OpenHandle(const v8::Signature* sig);
static inline v8::internal::Handle<v8::internal::TypeSwitchInfo>
OpenHandle(const v8::TypeSwitch* that);
static inline v8::internal::Handle<v8::internal::Proxy>
OpenHandle(const v8::External* that);
};
template <class T>
static inline T* ToApi(v8::internal::Handle<v8::internal::Object> obj) {
return reinterpret_cast<T*>(obj.location());
}
template <class T>
v8::internal::Handle<T> v8::internal::Handle<T>::EscapeFrom(
v8::HandleScope* scope) {
return Utils::OpenHandle(*scope->Close(Utils::ToLocal(*this)));
}
// Implementations of ToLocal
#define MAKE_TO_LOCAL(Name, From, To) \
Local<v8::To> Utils::Name(v8::internal::Handle<v8::internal::From> obj) { \
return Local<To>(reinterpret_cast<To*>(obj.location())); \
}
MAKE_TO_LOCAL(ToLocal, Context, Context)
MAKE_TO_LOCAL(ToLocal, Object, Value)
MAKE_TO_LOCAL(ToLocal, JSFunction, Function)
MAKE_TO_LOCAL(ToLocal, String, String)
MAKE_TO_LOCAL(ToLocal, JSObject, Object)
MAKE_TO_LOCAL(ToLocal, JSArray, Array)
MAKE_TO_LOCAL(ToLocal, Proxy, External)
MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature)
MAKE_TO_LOCAL(ToLocal, TypeSwitchInfo, TypeSwitch)
MAKE_TO_LOCAL(MessageToLocal, Object, Message)
MAKE_TO_LOCAL(NumberToLocal, Object, Number)
MAKE_TO_LOCAL(IntegerToLocal, Object, Integer)
MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32)
#undef MAKE_TO_LOCAL
// Implementations of OpenHandle
#define MAKE_OPEN_HANDLE(From, To) \
v8::internal::Handle<v8::internal::To> Utils::OpenHandle(\
const v8::From* that) { \
return v8::internal::Handle<v8::internal::To>( \
reinterpret_cast<v8::internal::To**>(const_cast<v8::From*>(that))); \
}
MAKE_OPEN_HANDLE(Template, TemplateInfo)
MAKE_OPEN_HANDLE(FunctionTemplate, FunctionTemplateInfo)
MAKE_OPEN_HANDLE(ObjectTemplate, ObjectTemplateInfo)
MAKE_OPEN_HANDLE(Signature, SignatureInfo)
MAKE_OPEN_HANDLE(TypeSwitch, TypeSwitchInfo)
MAKE_OPEN_HANDLE(Data, Object)
MAKE_OPEN_HANDLE(Object, JSObject)
MAKE_OPEN_HANDLE(Array, JSArray)
MAKE_OPEN_HANDLE(String, String)
MAKE_OPEN_HANDLE(Script, JSFunction)
MAKE_OPEN_HANDLE(Function, JSFunction)
MAKE_OPEN_HANDLE(Message, JSObject)
MAKE_OPEN_HANDLE(Context, Context)
MAKE_OPEN_HANDLE(External, Proxy)
#undef MAKE_OPEN_HANDLE
namespace internal {
// This class is here in order to be able to declare it a friend of
// HandleScope. Moving these methods to be members of HandleScope would be
// neat in some ways, but it would expose external implementation details in
// our public header file, which is undesirable.
//
// There is a singleton instance of this class to hold the per-thread data.
// For multithreaded V8 programs this data is copied in and out of storage
// so that the currently executing thread always has its own copy of this
// data.
class HandleScopeImplementer {
public:
HandleScopeImplementer()
: blocks(0),
entered_contexts_(0),
saved_contexts_(0) {
Initialize();
}
void Initialize() {
blocks.Initialize(0);
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
spare = NULL;
ignore_out_of_memory = false;
call_depth = 0;
}
static HandleScopeImplementer* instance();
// Threading support for handle data.
static int ArchiveSpacePerThread();
static char* RestoreThread(char* from);
static char* ArchiveThread(char* to);
// Garbage collection support.
static void Iterate(v8::internal::ObjectVisitor* v);
static char* Iterate(v8::internal::ObjectVisitor* v, char* data);
inline void** GetSpareOrNewBlock();
inline void DeleteExtensions(int extensions);
inline void IncrementCallDepth() {call_depth++;}
inline void DecrementCallDepth() {call_depth--;}
inline bool CallDepthIsZero() { return call_depth == 0; }
inline void EnterContext(Handle<Object> context);
inline bool LeaveLastContext();
// Returns the last entered context or an empty handle if no
// contexts have been entered.
inline Handle<Object> LastEnteredContext();
inline void SaveContext(Handle<Object> context);
inline Handle<Object> RestoreContext();
inline bool HasSavedContexts();
inline List<void**>* Blocks() { return &blocks; }
inline bool IgnoreOutOfMemory() { return ignore_out_of_memory; }
inline void SetIgnoreOutOfMemory(bool value) { ignore_out_of_memory = value; }
private:
List<void**> blocks;
Object** spare;
int call_depth;
// Used as a stack to keep track of entered contexts.
List<Handle<Object> > entered_contexts_;
// Used as a stack to keep track of saved contexts.
List<Handle<Object> > saved_contexts_;
bool ignore_out_of_memory;
// This is only used for threading support.
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
static void Iterate(ObjectVisitor* v,
List<void**>* blocks,
v8::ImplementationUtilities::HandleScopeData* handle_data);
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
};
static const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page
void HandleScopeImplementer::SaveContext(Handle<Object> context) {
saved_contexts_.Add(context);
}
Handle<Object> HandleScopeImplementer::RestoreContext() {
return saved_contexts_.RemoveLast();
}
bool HandleScopeImplementer::HasSavedContexts() {
return !saved_contexts_.is_empty();
}
void HandleScopeImplementer::EnterContext(Handle<Object> context) {
entered_contexts_.Add(context);
}
bool HandleScopeImplementer::LeaveLastContext() {
if (entered_contexts_.is_empty()) return false;
entered_contexts_.RemoveLast();
return true;
}
Handle<Object> HandleScopeImplementer::LastEnteredContext() {
if (entered_contexts_.is_empty()) return Handle<Object>::null();
return entered_contexts_.last();
}
// If there's a spare block, use it for growing the current scope.
void** HandleScopeImplementer::GetSpareOrNewBlock() {
void** block = (spare != NULL) ?
reinterpret_cast<void**>(spare) :
NewArray<void*>(kHandleBlockSize);
spare = NULL;
return block;
}
void HandleScopeImplementer::DeleteExtensions(int extensions) {
if (spare != NULL) {
DeleteArray(spare);
spare = NULL;
}
for (int i = extensions; i > 1; --i) {
void** block = blocks.RemoveLast();
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(block,
&block[kHandleBlockSize]);
#endif
DeleteArray(block);
}
spare = reinterpret_cast<Object**>(blocks.RemoveLast());
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(
reinterpret_cast<void**>(spare),
reinterpret_cast<void**>(&spare[kHandleBlockSize]));
#endif
}
} } // namespace v8::internal
#endif // V8_API_H_

109
deps/v8/src/apinatives.js

@ -0,0 +1,109 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains infrastructure used by the API. See
// v8natives.js for an explanation of these files are processed and
// loaded.
function CreateDate(time) {
var date = new ORIGINAL_DATE();
date.setTime(time);
return date;
}
const kApiFunctionCache = {};
const functionCache = kApiFunctionCache;
function Instantiate(data, name) {
if (!%IsTemplate(data)) return data;
var tag = %GetTemplateField(data, kApiTagOffset);
switch (tag) {
case kFunctionTag:
return InstantiateFunction(data, name);
case kNewObjectTag:
var Constructor = %GetTemplateField(data, kApiConstructorOffset);
var result = Constructor ? new (Instantiate(Constructor))() : {};
ConfigureTemplateInstance(result, data);
return result;
default:
throw 'Unknown API tag <' + tag + '>';
}
}
function InstantiateFunction(data, name) {
// We need a reference to kApiFunctionCache in the stack frame
// if we need to bail out from a stack overflow.
var cache = kApiFunctionCache;
var serialNumber = %GetTemplateField(data, kApiSerialNumberOffset);
var isFunctionCached =
(serialNumber in cache) && (cache[serialNumber] != kUninitialized);
if (!isFunctionCached) {
try {
cache[serialNumber] = null;
var fun = %CreateApiFunction(data);
if (name) %FunctionSetName(fun, name);
cache[serialNumber] = fun;
var prototype = %GetTemplateField(data, kApiPrototypeTemplateOffset);
fun.prototype = prototype ? Instantiate(prototype) : {};
%SetProperty(fun.prototype, "constructor", fun, DONT_ENUM);
var parent = %GetTemplateField(data, kApiParentTemplateOffset);
if (parent) {
var parent_fun = Instantiate(parent);
fun.prototype.__proto__ = parent_fun.prototype;
}
ConfigureTemplateInstance(fun, data);
} catch (e) {
cache[serialNumber] = kUninitialized;
throw e;
}
}
return cache[serialNumber];
}
function ConfigureTemplateInstance(obj, data) {
var properties = %GetTemplateField(data, kApiPropertyListOffset);
if (properties) {
// Disable access checks while instantiating the object.
var requires_access_checks = %DisableAccessChecks(obj);
try {
for (var i = 0; i < properties[0]; i += 3) {
var name = properties[i + 1];
var prop_data = properties[i + 2];
var attributes = properties[i + 3];
var value = Instantiate(prop_data, name);
%SetProperty(obj, name, value, attributes);
}
} finally {
if (requires_access_checks) %EnableAccessChecks(obj);
}
}
}

69
deps/v8/src/apiutils.h

@ -0,0 +1,69 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_APIUTILS_H_
#define V8_APIUTILS_H_
namespace v8 {
class ImplementationUtilities {
public:
static v8::Handle<v8::Primitive> Undefined();
static v8::Handle<v8::Primitive> Null();
static v8::Handle<v8::Boolean> True();
static v8::Handle<v8::Boolean> False();
static int GetNameCount(ExtensionConfiguration* that) {
return that->name_count_;
}
static const char** GetNames(ExtensionConfiguration* that) {
return that->names_;
}
static v8::Arguments NewArguments(Local<Value> data,
Local<Object> holder,
Local<Function> callee,
bool is_construct_call,
void** argv, int argc) {
return v8::Arguments(data, holder, callee, is_construct_call, argv, argc);
}
// Introduce an alias for the handle scope data to allow non-friends
// to access the HandleScope data.
typedef v8::HandleScope::Data HandleScopeData;
static HandleScopeData* CurrentHandleScope();
#ifdef DEBUG
static void ZapHandleRange(void** begin, void** end);
#endif
};
} // namespace v8
#endif // V8_APIUTILS_H_

70
deps/v8/src/arguments.h

@ -0,0 +1,70 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_ARGUMENTS_H_
#define V8_ARGUMENTS_H_
namespace v8 { namespace internal {
// Arguments provides access to runtime call parameters.
//
// It uses the fact that the instance fields of Arguments
// (length_, arguments_) are "overlayed" with the parameters
// (no. of parameters, and the parameter pointer) passed so
// that inside the C++ function, the parameters passed can
// be accessed conveniently:
//
// Object* Runtime_function(Arguments args) {
// ... use args[i] here ...
// }
class Arguments BASE_EMBEDDED {
public:
Object*& operator[] (int index) {
ASSERT(0 <= index && index < length_);
return arguments_[-index];
}
template <class S> Handle<S> at(int index) {
Object** value = &((*this)[index]);
// This cast checks that the object we're accessing does indeed have the
// expected type.
S::cast(*value);
return Handle<S>(reinterpret_cast<S**>(value));
}
// Get the total number of arguments including the receiver.
int length() const { return length_; }
private:
int length_;
Object** arguments_;
};
} } // namespace v8::internal
#endif // V8_ARGUMENTS_H_

938
deps/v8/src/array.js

@ -0,0 +1,938 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file relies on the fact that the following declarations have been made
// in runtime.js:
// const $Array = global.Array;
// -------------------------------------------------------------------
// Global list of arrays visited during toString, toLocaleString and
// join invocations.
var visited_arrays = new $Array();
// Gets a sorted array of array keys. Useful for operations on sparse
// arrays. Dupes have not been removed.
function GetSortedArrayKeys(array, intervals) {
var length = intervals.length;
var keys = [];
for (var k = 0; k < length; k++) {
var key = intervals[k];
if (key < 0) {
var j = -1 - key;
var limit = j + intervals[++k];
for (; j < limit; j++) {
var e = array[j];
if (!IS_UNDEFINED(e) || j in array) {
keys.push(j);
}
}
} else {
// The case where key is undefined also ends here.
if (!IS_UNDEFINED(key)) {
var e = array[key];
if (!IS_UNDEFINED(e) || key in array) {
keys.push(key);
}
}
}
}
keys.sort(function(a, b) { return a - b; });
return keys;
}
// Optimized for sparse arrays if separator is ''.
function SparseJoin(array, len, convert) {
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
var builder = new StringBuilder();
var last_key = -1;
var keys_length = keys.length;
for (var i = 0; i < keys_length; i++) {
var key = keys[i];
if (key != last_key) {
var e = array[key];
builder.add(convert(e));
last_key = key;
}
}
return builder.generate();
}
function UseSparseVariant(object, length, is_array) {
return is_array &&
length > 1000 &&
(!%_IsSmi(length) ||
%EstimateNumberOfElements(object) < (length >> 2));
}
function Join(array, length, separator, convert) {
if (length == 0) return '';
var is_array = IS_ARRAY(array);
if (is_array) {
// If the array is cyclic, return the empty string for already
// visited arrays.
if (!%PushIfAbsent(visited_arrays, array)) return '';
}
// Attempt to convert the elements.
try {
if (UseSparseVariant(array, length, is_array) && separator === '') {
return SparseJoin(array, length, convert);
}
// Fast case for one-element arrays.
if (length == 1) {
var e = array[0];
if (!IS_UNDEFINED(e) || (0 in array)) {
return convert(e);
}
}
var builder = new StringBuilder();
for (var i = 0; i < length; i++) {
var e = array[i];
if (i != 0) builder.add(separator);
if (!IS_UNDEFINED(e) || (i in array)) {
builder.add(convert(e));
}
}
return builder.generate();
} finally {
// Make sure to pop the visited array no matter what happens.
if (is_array) visited_arrays.pop();
}
}
function ConvertToString(e) {
if (e == null) return '';
else return ToString(e);
}
function ConvertToLocaleString(e) {
if (e == null) return '';
else {
// e_obj's toLocaleString might be overwritten, check if it is a function.
// Call ToString if toLocaleString is not a function.
// See issue 877615.
var e_obj = ToObject(e);
if (IS_FUNCTION(e_obj.toLocaleString))
return e_obj.toLocaleString();
else
return ToString(e);
}
}
// This function implements the optimized splice implementation that can use
// special array operations to handle sparse arrays in a sensible fashion.
function SmartSlice(array, start_i, del_count, len, deleted_elements) {
// Move deleted elements to a new array (the return value from splice).
// Intervals array can contain keys and intervals. See comment in Concat.
var intervals = %GetArrayKeys(array, start_i + del_count);
var length = intervals.length;
for (var k = 0; k < length; k++) {
var key = intervals[k];
if (key < 0) {
var j = -1 - key;
var interval_limit = j + intervals[++k];
if (j < start_i) {
j = start_i;
}
for (; j < interval_limit; j++) {
// ECMA-262 15.4.4.12 line 10. The spec could also be
// interpreted such that %HasLocalProperty would be the
// appropriate test. We follow KJS in consulting the
// prototype.
var current = array[j];
if (!IS_UNDEFINED(current) || j in array) {
deleted_elements[j - start_i] = current;
}
}
} else {
if (!IS_UNDEFINED(key)) {
if (key >= start_i) {
// ECMA-262 15.4.4.12 line 10. The spec could also be
// interpreted such that %HasLocalProperty would be the
// appropriate test. We follow KJS in consulting the
// prototype.
var current = array[key];
if (!IS_UNDEFINED(current) || key in array) {
deleted_elements[key - start_i] = current;
}
}
}
}
}
}
// This function implements the optimized splice implementation that can use
// special array operations to handle sparse arrays in a sensible fashion.
function SmartMove(array, start_i, del_count, len, num_additional_args) {
// Move data to new array.
var new_array = new $Array(len - del_count + num_additional_args);
var intervals = %GetArrayKeys(array, len);
var length = intervals.length;
for (var k = 0; k < length; k++) {
var key = intervals[k];
if (key < 0) {
var j = -1 - key;
var interval_limit = j + intervals[++k];
while (j < start_i && j < interval_limit) {
// The spec could also be interpreted such that
// %HasLocalProperty would be the appropriate test. We follow
// KJS in consulting the prototype.
var current = array[j];
if (!IS_UNDEFINED(current) || j in array) {
new_array[j] = current;
}
j++;
}
j = start_i + del_count;
while (j < interval_limit) {
// ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
// interpreted such that %HasLocalProperty would be the
// appropriate test. We follow KJS in consulting the
// prototype.
var current = array[j];
if (!IS_UNDEFINED(current) || j in array) {
new_array[j - del_count + num_additional_args] = current;
}
j++;
}
} else {
if (!IS_UNDEFINED(key)) {
if (key < start_i) {
// The spec could also be interpreted such that
// %HasLocalProperty would be the appropriate test. We follow
// KJS in consulting the prototype.
var current = array[key];
if (!IS_UNDEFINED(current) || key in array) {
new_array[key] = current;
}
} else if (key >= start_i + del_count) {
// ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
// be interpreted such that %HasLocalProperty would be the
// appropriate test. We follow KJS in consulting the
// prototype.
var current = array[key];
if (!IS_UNDEFINED(current) || key in array) {
new_array[key - del_count + num_additional_args] = current;
}
}
}
}
}
// Move contents of new_array into this array
%MoveArrayContents(new_array, array);
}
// This is part of the old simple-minded splice. We are using it either
// because the receiver is not an array (so we have no choice) or because we
// know we are not deleting or moving a lot of elements.
function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
for (var i = 0; i < del_count; i++) {
var index = start_i + i;
// The spec could also be interpreted such that %HasLocalProperty
// would be the appropriate test. We follow KJS in consulting the
// prototype.
var current = array[index];
if (!IS_UNDEFINED(current) || index in array)
deleted_elements[i] = current;
}
}
function SimpleMove(array, start_i, del_count, len, num_additional_args) {
if (num_additional_args !== del_count) {
// Move the existing elements after the elements to be deleted
// to the right position in the resulting array.
if (num_additional_args > del_count) {
for (var i = len - del_count; i > start_i; i--) {
var from_index = i + del_count - 1;
var to_index = i + num_additional_args - 1;
// The spec could also be interpreted such that
// %HasLocalProperty would be the appropriate test. We follow
// KJS in consulting the prototype.
var current = array[from_index];
if (!IS_UNDEFINED(current) || from_index in array) {
array[to_index] = current;
} else {
delete array[to_index];
}
}
} else {
for (var i = start_i; i < len - del_count; i++) {
var from_index = i + del_count;
var to_index = i + num_additional_args;
// The spec could also be interpreted such that
// %HasLocalProperty would be the appropriate test. We follow
// KJS in consulting the prototype.
var current = array[from_index];
if (!IS_UNDEFINED(current) || from_index in array) {
array[to_index] = current;
} else {
delete array[to_index];
}
}
for (var i = len; i > len - del_count + num_additional_args; i--) {
delete array[i - 1];
}
}
}
}
// -------------------------------------------------------------------
function ArrayToString() {
if (!IS_ARRAY(this)) {
throw new $TypeError('Array.prototype.toString is not generic');
}
return Join(this, this.length, ',', ConvertToString);
}
function ArrayToLocaleString() {
if (!IS_ARRAY(this)) {
throw new $TypeError('Array.prototype.toString is not generic');
}
return Join(this, this.length, ',', ConvertToLocaleString);
}
function ArrayJoin(separator) {
if (IS_UNDEFINED(separator)) separator = ',';
else separator = ToString(separator);
return Join(this, ToUint32(this.length), separator, ConvertToString);
}
// Removes the last element from the array and returns it. See
// ECMA-262, section 15.4.4.6.
function ArrayPop() {
var n = ToUint32(this.length);
if (n == 0) {
this.length = n;
return;
}
n--;
var value = this[n];
this.length = n;
delete this[n];
return value;
}
// Appends the arguments to the end of the array and returns the new
// length of the array. See ECMA-262, section 15.4.4.7.
function ArrayPush() {
var n = ToUint32(this.length);
var m = %_ArgumentsLength();
for (var i = 0; i < m; i++) {
this[i+n] = %_Arguments(i);
}
this.length = n + m;
return this.length;
}
function ArrayConcat(arg1) { // length == 1
// TODO: can we just use arguments?
var arg_count = %_ArgumentsLength();
var arrays = new $Array(1 + arg_count);
arrays[0] = this;
for (var i = 0; i < arg_count; i++) {
arrays[i + 1] = %_Arguments(i);
}
return %ArrayConcat(arrays);
}
// For implementing reverse() on large, sparse arrays.
function SparseReverse(array, len) {
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
var high_counter = keys.length - 1;
var low_counter = 0;
while (low_counter <= high_counter) {
var i = keys[low_counter];
var j = keys[high_counter];
var j_complement = len - j - 1;
var low, high;
if (j_complement <= i) {
high = j;
while (keys[--high_counter] == j);
low = j_complement;
}
if (j_complement >= i) {
low = i;
while (keys[++low_counter] == i);
high = len - i - 1;
}
var current_i = array[low];
if (!IS_UNDEFINED(current_i) || low in array) {
var current_j = array[high];
if (!IS_UNDEFINED(current_j) || high in array) {
array[low] = current_j;
array[high] = current_i;
} else {
array[high] = current_i;
delete array[low];
}
} else {
var current_j = array[high];
if (!IS_UNDEFINED(current_j) || high in array) {
array[low] = current_j;
delete array[high];
}
}
}
}
function ArrayReverse() {
var j = ToUint32(this.length) - 1;
if (UseSparseVariant(this, j, IS_ARRAY(this))) {
SparseReverse(this, j+1);
return this;
}
for (var i = 0; i < j; i++, j--) {
var current_i = this[i];
if (!IS_UNDEFINED(current_i) || i in this) {
var current_j = this[j];
if (!IS_UNDEFINED(current_j) || j in this) {
this[i] = current_j;
this[j] = current_i;
} else {
this[j] = current_i;
delete this[i];
}
} else {
var current_j = this[j];
if (!IS_UNDEFINED(current_j) || j in this) {
this[i] = current_j;
delete this[j];
}
}
}
return this;
}
function ArrayShift() {
var len = ToUint32(this.length);
if (len === 0) {
this.length = 0;
return;
}
var first = this[0];
if (IS_ARRAY(this))
SmartMove(this, 0, 1, len, 0);
else
SimpleMove(this, 0, 1, len, 0);
this.length = len - 1;
return first;
}
function ArrayUnshift(arg1) { // length == 1
var len = ToUint32(this.length);
var num_arguments = %_ArgumentsLength();
if (IS_ARRAY(this))
SmartMove(this, 0, 0, len, num_arguments);
else
SimpleMove(this, 0, 0, len, num_arguments);
for (var i = 0; i < num_arguments; i++) {
this[i] = %_Arguments(i);
}
this.length = len + num_arguments;
return len + num_arguments;
}
function ArraySlice(start, end) {
var len = ToUint32(this.length);
var start_i = TO_INTEGER(start);
var end_i = len;
if (end !== void 0) end_i = TO_INTEGER(end);
if (start_i < 0) {
start_i += len;
if (start_i < 0) start_i = 0;
} else {
if (start_i > len) start_i = len;
}
if (end_i < 0) {
end_i += len;
if (end_i < 0) end_i = 0;
} else {
if (end_i > len) end_i = len;
}
var result = [];
if (end_i < start_i) return result;
if (IS_ARRAY(this)) {
SmartSlice(this, start_i, end_i - start_i, len, result);
} else {
SimpleSlice(this, start_i, end_i - start_i, len, result);
}
result.length = end_i - start_i;
return result;
}
function ArraySplice(start, delete_count) {
var num_arguments = %_ArgumentsLength();
// SpiderMonkey and KJS return undefined in the case where no
// arguments are given instead of using the implicit undefined
// arguments. This does not follow ECMA-262, but we do the same for
// compatibility.
if (num_arguments == 0) return;
var len = ToUint32(this.length);
var start_i = TO_INTEGER(start);
if (start_i < 0) {
start_i += len;
if (start_i < 0) start_i = 0;
} else {
if (start_i > len) start_i = len;
}
// SpiderMonkey and KJS treat the case where no delete count is
// given differently from when an undefined delete count is given.
// This does not follow ECMA-262, but we do the same for
// compatibility.
var del_count = 0;
if (num_arguments > 1) {
del_count = TO_INTEGER(delete_count);
if (del_count < 0) del_count = 0;
if (del_count > len - start_i) del_count = len - start_i;
} else {
del_count = len - start_i;
}
var deleted_elements = [];
deleted_elements.length = del_count;
// Number of elements to add.
var num_additional_args = 0;
if (num_arguments > 2) {
num_additional_args = num_arguments - 2;
}
var use_simple_splice = true;
if (IS_ARRAY(this) && num_additional_args !== del_count) {
// If we are only deleting/moving a few things near the end of the
// array then the simple version is going to be faster, because it
// doesn't touch most of the array.
var estimated_non_hole_elements = %EstimateNumberOfElements(this);
if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
use_simple_splice = false;
}
}
if (use_simple_splice) {
SimpleSlice(this, start_i, del_count, len, deleted_elements);
SimpleMove(this, start_i, del_count, len, num_additional_args);
} else {
SmartSlice(this, start_i, del_count, len, deleted_elements);
SmartMove(this, start_i, del_count, len, num_additional_args);
}
// Insert the arguments into the resulting array in
// place of the deleted elements.
var i = start_i;
var arguments_index = 2;
var arguments_length = %_ArgumentsLength();
while (arguments_index < arguments_length) {
this[i++] = %_Arguments(arguments_index++);
}
this.length = len - del_count + num_additional_args;
// Return the deleted elements.
return deleted_elements;
}
function ArraySort(comparefn) {
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
var custom_compare = IS_FUNCTION(comparefn);
function Compare(x,y) {
// Assume the comparefn, if any, is a consistent comparison function.
// If it isn't, we are allowed arbitrary behavior by ECMA 15.4.4.11.
if (x === y) return 0;
if (custom_compare) {
// Don't call directly to avoid exposing the builtin's global object.
return comparefn.call(null, x, y);
}
if (%_IsSmi(x) && %_IsSmi(y)) {
return %SmiLexicographicCompare(x, y);
}
x = ToString(x);
y = ToString(y);
if (x == y) return 0;
else return x < y ? -1 : 1;
};
function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
// Pre-convert the element to a string for comparison if we know
// it will happen on each compare anyway.
var key =
(custom_compare || %_IsSmi(element)) ? element : ToString(element);
// place element in a[from..i[
// binary search
var min = from;
var max = i;
// The search interval is a[min..max[
while (min < max) {
var mid = min + ((max - min) >> 1);
var order = Compare(a[mid], key);
if (order == 0) {
min = max = mid;
break;
}
if (order < 0) {
min = mid + 1;
} else {
max = mid;
}
}
// place element at position min==max.
for (var j = i; j > min; j--) {
a[j] = a[j - 1];
}
a[min] = element;
}
}
function QuickSort(a, from, to) {
// Insertion sort is faster for short arrays.
if (to - from <= 22) {
InsertionSort(a, from, to);
return;
}
var pivot_index = $floor($random() * (to - from)) + from;
var pivot = a[pivot_index];
// Pre-convert the element to a string for comparison if we know
// it will happen on each compare anyway.
var pivot_key =
(custom_compare || %_IsSmi(pivot)) ? pivot : ToString(pivot);
// Issue 95: Keep the pivot element out of the comparisons to avoid
// infinite recursion if comparefn(pivot, pivot) != 0.
a[pivot_index] = a[from];
a[from] = pivot;
var low_end = from; // Upper bound of the elements lower than pivot.
var high_start = to; // Lower bound of the elements greater than pivot.
// From low_end to i are elements equal to pivot.
// From i to high_start are elements that haven't been compared yet.
for (var i = from + 1; i < high_start; ) {
var element = a[i];
var order = Compare(element, pivot_key);
if (order < 0) {
a[i] = a[low_end];
a[low_end] = element;
i++;
low_end++;
} else if (order > 0) {
high_start--;
a[i] = a[high_start];
a[high_start] = element;
} else { // order == 0
i++;
}
}
QuickSort(a, from, low_end);
QuickSort(a, high_start, to);
}
var old_length = ToUint32(this.length);
if (old_length < 2) return this;
%RemoveArrayHoles(this);
var length = ToUint32(this.length);
// Move undefined elements to the end of the array.
for (var i = 0; i < length; ) {
if (IS_UNDEFINED(this[i])) {
length--;
this[i] = this[length];
this[length] = void 0;
} else {
i++;
}
}
QuickSort(this, 0, length);
// We only changed the length of the this object (in
// RemoveArrayHoles) if it was an array. We are not allowed to set
// the length of the this object if it is not an array because this
// might introduce a new length property.
if (IS_ARRAY(this)) {
this.length = old_length;
}
return this;
}
// The following functions cannot be made efficient on sparse arrays while
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
function ArrayFilter(f, receiver) {
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
// Pull out the length so that modifications to the length in the
// loop will not affect the looping.
var length = this.length;
var result = [];
var result_length = 0;
for (var i = 0; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
if (f.call(receiver, current, i, this)) result[result_length++] = current;
}
}
return result;
}
function ArrayForEach(f, receiver) {
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
// Pull out the length so that modifications to the length in the
// loop will not affect the looping.
var length = this.length;
for (var i = 0; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
f.call(receiver, current, i, this);
}
}
}
// Executes the function once for each element present in the
// array until it finds one where callback returns true.
function ArraySome(f, receiver) {
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
// Pull out the length so that modifications to the length in the
// loop will not affect the looping.
var length = this.length;
for (var i = 0; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
if (f.call(receiver, current, i, this)) return true;
}
}
return false;
}
function ArrayEvery(f, receiver) {
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
// Pull out the length so that modifications to the length in the
// loop will not affect the looping.
var length = this.length;
for (var i = 0; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
if (!f.call(receiver, current, i, this)) return false;
}
}
return true;
}
function ArrayMap(f, receiver) {
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
// Pull out the length so that modifications to the length in the
// loop will not affect the looping.
var length = this.length;
var result = new $Array(length);
for (var i = 0; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
result[i] = f.call(receiver, current, i, this);
}
}
return result;
}
function ArrayIndexOf(element, index) {
var length = this.length;
if (index == null) {
index = 0;
} else {
index = TO_INTEGER(index);
// If index is negative, index from the end of the array.
if (index < 0) index = length + index;
// If index is still negative, search the entire array.
if (index < 0) index = 0;
}
// Lookup through the array.
for (var i = index; i < length; i++) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
if (current === element) return i;
}
}
return -1;
}
function ArrayLastIndexOf(element, index) {
var length = this.length;
if (index == null) {
index = length - 1;
} else {
index = TO_INTEGER(index);
// If index is negative, index from end of the array.
if (index < 0) index = length + index;
// If index is still negative, do not search the array.
if (index < 0) index = -1;
else if (index >= length) index = length - 1;
}
// Lookup through the array.
for (var i = index; i >= 0; i--) {
var current = this[i];
if (!IS_UNDEFINED(current) || i in this) {
if (current === element) return i;
}
}
return -1;
}
// -------------------------------------------------------------------
function UpdateFunctionLengths(lengths) {
for (var key in lengths) {
%FunctionSetLength(this[key], lengths[key]);
}
}
// -------------------------------------------------------------------
function SetupArray() {
// Setup non-enumerable constructor property on the Array.prototype
// object.
%SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
// Setup non-enumerable functions of the Array.prototype object and
// set their names.
InstallFunctions($Array.prototype, DONT_ENUM, $Array(
"toString", ArrayToString,
"toLocaleString", ArrayToLocaleString,
"join", ArrayJoin,
"pop", ArrayPop,
"push", ArrayPush,
"concat", ArrayConcat,
"reverse", ArrayReverse,
"shift", ArrayShift,
"unshift", ArrayUnshift,
"slice", ArraySlice,
"splice", ArraySplice,
"sort", ArraySort,
"filter", ArrayFilter,
"forEach", ArrayForEach,
"some", ArraySome,
"every", ArrayEvery,
"map", ArrayMap,
"indexOf", ArrayIndexOf,
"lastIndexOf", ArrayLastIndexOf
));
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
UpdateFunctionLengths({
ArrayFilter: 1,
ArrayForEach: 1,
ArraySome: 1,
ArrayEvery: 1,
ArrayMap: 1,
ArrayIndexOf: 1,
ArrayLastIndexOf: 1,
ArrayPush: 1
});
}
SetupArray();

249
deps/v8/src/assembler-arm-inl.h

@ -0,0 +1,249 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been modified
// significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
#ifndef V8_ASSEMBLER_ARM_INL_H_
#define V8_ASSEMBLER_ARM_INL_H_
#include "assembler-arm.h"
#include "cpu.h"
namespace v8 { namespace internal {
Condition NegateCondition(Condition cc) {
ASSERT(cc != al);
return static_cast<Condition>(cc ^ ne);
}
void RelocInfo::apply(int delta) {
if (RelocInfo::IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object.
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p += delta; // relocate entry
}
// We do not use pc relative addressing on ARM, so there is
// nothing else to do.
}
Address RelocInfo::target_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
return Assembler::target_address_at(pc_);
}
Address RelocInfo::target_address_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
return reinterpret_cast<Address>(Assembler::target_address_address_at(pc_));
}
void RelocInfo::set_target_address(Address target) {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
Assembler::set_target_address_at(pc_, target);
}
Object* RelocInfo::target_object() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
return reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
}
Object** RelocInfo::target_object_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
return reinterpret_cast<Object**>(Assembler::target_address_address_at(pc_));
}
void RelocInfo::set_target_object(Object* target) {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target));
}
Address* RelocInfo::target_reference_address() {
ASSERT(rmode_ == EXTERNAL_REFERENCE);
return reinterpret_cast<Address*>(Assembler::target_address_address_at(pc_));
}
Address RelocInfo::call_address() {
ASSERT(IsCallInstruction());
UNIMPLEMENTED();
return NULL;
}
void RelocInfo::set_call_address(Address target) {
ASSERT(IsCallInstruction());
UNIMPLEMENTED();
}
Object* RelocInfo::call_object() {
ASSERT(IsCallInstruction());
UNIMPLEMENTED();
return NULL;
}
Object** RelocInfo::call_object_address() {
ASSERT(IsCallInstruction());
UNIMPLEMENTED();
return NULL;
}
void RelocInfo::set_call_object(Object* target) {
ASSERT(IsCallInstruction());
UNIMPLEMENTED();
}
bool RelocInfo::IsCallInstruction() {
UNIMPLEMENTED();
return false;
}
Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) {
rm_ = no_reg;
imm32_ = immediate;
rmode_ = rmode;
}
Operand::Operand(const char* s) {
rm_ = no_reg;
imm32_ = reinterpret_cast<int32_t>(s);
rmode_ = RelocInfo::EMBEDDED_STRING;
}
Operand::Operand(const ExternalReference& f) {
rm_ = no_reg;
imm32_ = reinterpret_cast<int32_t>(f.address());
rmode_ = RelocInfo::EXTERNAL_REFERENCE;
}
Operand::Operand(Object** opp) {
rm_ = no_reg;
imm32_ = reinterpret_cast<int32_t>(opp);
rmode_ = RelocInfo::NONE;
}
Operand::Operand(Context** cpp) {
rm_ = no_reg;
imm32_ = reinterpret_cast<int32_t>(cpp);
rmode_ = RelocInfo::NONE;
}
Operand::Operand(Smi* value) {
rm_ = no_reg;
imm32_ = reinterpret_cast<intptr_t>(value);
rmode_ = RelocInfo::NONE;
}
Operand::Operand(Register rm) {
rm_ = rm;
rs_ = no_reg;
shift_op_ = LSL;
shift_imm_ = 0;
}
bool Operand::is_reg() const {
return rm_.is_valid() &&
rs_.is(no_reg) &&
shift_op_ == LSL &&
shift_imm_ == 0;
}
void Assembler::CheckBuffer() {
if (buffer_space() <= kGap) {
GrowBuffer();
}
if (pc_offset() > next_buffer_check_) {
CheckConstPool(false, true);
}
}
void Assembler::emit(Instr x) {
CheckBuffer();
*reinterpret_cast<Instr*>(pc_) = x;
pc_ += kInstrSize;
}
Address Assembler::target_address_address_at(Address pc) {
Instr instr = Memory::int32_at(pc);
// Verify that the instruction at pc is a ldr<cond> <Rd>, [pc +/- offset_12].
ASSERT((instr & 0x0f7f0000) == 0x051f0000);
int offset = instr & 0xfff; // offset_12 is unsigned
if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign
// Verify that the constant pool comes after the instruction referencing it.
ASSERT(offset >= -4);
return pc + offset + 8;
}
Address Assembler::target_address_at(Address pc) {
return Memory::Address_at(target_address_address_at(pc));
}
void Assembler::set_target_address_at(Address pc, Address target) {
Memory::Address_at(target_address_address_at(pc)) = target;
// Intuitively, we would think it is necessary to flush the instruction cache
// after patching a target address in the code as follows:
// CPU::FlushICache(pc, sizeof(target));
// However, on ARM, no instruction was actually patched by the assignment
// above; the target address is not part of an instruction, it is patched in
// the constant pool and is read via a data access; the instruction accessing
// this address in the constant pool remains unchanged.
}
} } // namespace v8::internal
#endif // V8_ASSEMBLER_ARM_INL_H_

1471
deps/v8/src/assembler-arm.cc

File diff suppressed because it is too large

789
deps/v8/src/assembler-arm.h

@ -0,0 +1,789 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been modified
// significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// A light-weight ARM Assembler
// Generates user mode instructions for the ARM architecture up to version 5
#ifndef V8_ASSEMBLER_ARM_H_
#define V8_ASSEMBLER_ARM_H_
#include "assembler.h"
namespace v8 { namespace internal {
// CPU Registers.
//
// 1) We would prefer to use an enum, but enum values are assignment-
// compatible with int, which has caused code-generation bugs.
//
// 2) We would prefer to use a class instead of a struct but we don't like
// the register initialization to depend on the particular initialization
// order (which appears to be different on OS X, Linux, and Windows for the
// installed versions of C++ we tried). Using a struct permits C-style
// "initialization". Also, the Register objects cannot be const as this
// forces initialization stubs in MSVC, making us dependent on initialization
// order.
//
// 3) By not using an enum, we are possibly preventing the compiler from
// doing certain constant folds, which may significantly reduce the
// code generated for some assembly instructions (because they boil down
// to a few constants). If this is a problem, we could change the code
// such that we use an enum in optimized mode, and the struct in debug
// mode. This way we get the compile-time error checking in debug mode
// and best performance in optimized code.
//
// Core register
struct Register {
bool is_valid() const { return 0 <= code_ && code_ < 16; }
bool is(Register reg) const { return code_ == reg.code_; }
int code() const {
ASSERT(is_valid());
return code_;
}
int bit() const {
ASSERT(is_valid());
return 1 << code_;
}
// (unfortunately we can't make this private in a struct)
int code_;
};
const int kNumRegisters = 16;
extern Register no_reg;
extern Register r0;
extern Register r1;
extern Register r2;
extern Register r3;
extern Register r4;
extern Register r5;
extern Register r6;
extern Register r7;
extern Register r8;
extern Register r9;
extern Register r10;
extern Register fp;
extern Register ip;
extern Register sp;
extern Register lr;
extern Register pc;
// Coprocessor register
struct CRegister {
bool is_valid() const { return 0 <= code_ && code_ < 16; }
bool is(CRegister creg) const { return code_ == creg.code_; }
int code() const {
ASSERT(is_valid());
return code_;
}
int bit() const {
ASSERT(is_valid());
return 1 << code_;
}
// (unfortunately we can't make this private in a struct)
int code_;
};
extern CRegister no_creg;
extern CRegister cr0;
extern CRegister cr1;
extern CRegister cr2;
extern CRegister cr3;
extern CRegister cr4;
extern CRegister cr5;
extern CRegister cr6;
extern CRegister cr7;
extern CRegister cr8;
extern CRegister cr9;
extern CRegister cr10;
extern CRegister cr11;
extern CRegister cr12;
extern CRegister cr13;
extern CRegister cr14;
extern CRegister cr15;
// Coprocessor number
enum Coprocessor {
p0 = 0,
p1 = 1,
p2 = 2,
p3 = 3,
p4 = 4,
p5 = 5,
p6 = 6,
p7 = 7,
p8 = 8,
p9 = 9,
p10 = 10,
p11 = 11,
p12 = 12,
p13 = 13,
p14 = 14,
p15 = 15
};
// Condition field in instructions
enum Condition {
eq = 0 << 28,
ne = 1 << 28,
cs = 2 << 28,
hs = 2 << 28,
cc = 3 << 28,
lo = 3 << 28,
mi = 4 << 28,
pl = 5 << 28,
vs = 6 << 28,
vc = 7 << 28,
hi = 8 << 28,
ls = 9 << 28,
ge = 10 << 28,
lt = 11 << 28,
gt = 12 << 28,
le = 13 << 28,
al = 14 << 28
};
// Returns the equivalent of !cc.
INLINE(Condition NegateCondition(Condition cc));
// Corresponds to transposing the operands of a comparison.
inline Condition ReverseCondition(Condition cc) {
switch (cc) {
case lo:
return hi;
case hi:
return lo;
case hs:
return ls;
case ls:
return hs;
case lt:
return gt;
case gt:
return lt;
case ge:
return le;
case le:
return ge;
default:
return cc;
};
}
// Branch hints are not used on the ARM. They are defined so that they can
// appear in shared function signatures, but will be ignored in ARM
// implementations.
enum Hint { no_hint };
// Hints are not used on the arm. Negating is trivial.
inline Hint NegateHint(Hint ignored) { return no_hint; }
// -----------------------------------------------------------------------------
// Addressing modes and instruction variants
// Shifter operand shift operation
enum ShiftOp {
LSL = 0 << 5,
LSR = 1 << 5,
ASR = 2 << 5,
ROR = 3 << 5,
RRX = -1
};
// Condition code updating mode
enum SBit {
SetCC = 1 << 20, // set condition code
LeaveCC = 0 << 20 // leave condition code unchanged
};
// Status register selection
enum SRegister {
CPSR = 0 << 22,
SPSR = 1 << 22
};
// Status register fields
enum SRegisterField {
CPSR_c = CPSR | 1 << 16,
CPSR_x = CPSR | 1 << 17,
CPSR_s = CPSR | 1 << 18,
CPSR_f = CPSR | 1 << 19,
SPSR_c = SPSR | 1 << 16,
SPSR_x = SPSR | 1 << 17,
SPSR_s = SPSR | 1 << 18,
SPSR_f = SPSR | 1 << 19
};
// Status register field mask (or'ed SRegisterField enum values)
typedef uint32_t SRegisterFieldMask;
// Memory operand addressing mode
enum AddrMode {
// bit encoding P U W
Offset = (8|4|0) << 21, // offset (without writeback to base)
PreIndex = (8|4|1) << 21, // pre-indexed addressing with writeback
PostIndex = (0|4|0) << 21, // post-indexed addressing with writeback
NegOffset = (8|0|0) << 21, // negative offset (without writeback to base)
NegPreIndex = (8|0|1) << 21, // negative pre-indexed with writeback
NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback
};
// Load/store multiple addressing mode
enum BlockAddrMode {
// bit encoding P U W
da = (0|0|0) << 21, // decrement after
ia = (0|4|0) << 21, // increment after
db = (8|0|0) << 21, // decrement before
ib = (8|4|0) << 21, // increment before
da_w = (0|0|1) << 21, // decrement after with writeback to base
ia_w = (0|4|1) << 21, // increment after with writeback to base
db_w = (8|0|1) << 21, // decrement before with writeback to base
ib_w = (8|4|1) << 21 // increment before with writeback to base
};
// Coprocessor load/store operand size
enum LFlag {
Long = 1 << 22, // long load/store coprocessor
Short = 0 << 22 // short load/store coprocessor
};
// -----------------------------------------------------------------------------
// Machine instruction Operands
// Class Operand represents a shifter operand in data processing instructions
class Operand BASE_EMBEDDED {
public:
// immediate
INLINE(explicit Operand(int32_t immediate,
RelocInfo::Mode rmode = RelocInfo::NONE));
INLINE(explicit Operand(const ExternalReference& f));
INLINE(explicit Operand(const char* s));
INLINE(explicit Operand(Object** opp));
INLINE(explicit Operand(Context** cpp));
explicit Operand(Handle<Object> handle);
INLINE(explicit Operand(Smi* value));
// rm
INLINE(explicit Operand(Register rm));
// rm <shift_op> shift_imm
explicit Operand(Register rm, ShiftOp shift_op, int shift_imm);
// rm <shift_op> rs
explicit Operand(Register rm, ShiftOp shift_op, Register rs);
// Return true if this is a register operand.
INLINE(bool is_reg() const);
Register rm() const { return rm_; }
private:
Register rm_;
Register rs_;
ShiftOp shift_op_;
int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg
int32_t imm32_; // valid if rm_ == no_reg
RelocInfo::Mode rmode_;
friend class Assembler;
};
// Class MemOperand represents a memory operand in load and store instructions
class MemOperand BASE_EMBEDDED {
public:
// [rn +/- offset] Offset/NegOffset
// [rn +/- offset]! PreIndex/NegPreIndex
// [rn], +/- offset PostIndex/NegPostIndex
// offset is any signed 32-bit value; offset is first loaded to register ip if
// it does not fit the addressing mode (12-bit unsigned and sign bit)
explicit MemOperand(Register rn, int32_t offset = 0, AddrMode am = Offset);
// [rn +/- rm] Offset/NegOffset
// [rn +/- rm]! PreIndex/NegPreIndex
// [rn], +/- rm PostIndex/NegPostIndex
explicit MemOperand(Register rn, Register rm, AddrMode am = Offset);
// [rn +/- rm <shift_op> shift_imm] Offset/NegOffset
// [rn +/- rm <shift_op> shift_imm]! PreIndex/NegPreIndex
// [rn], +/- rm <shift_op> shift_imm PostIndex/NegPostIndex
explicit MemOperand(Register rn, Register rm,
ShiftOp shift_op, int shift_imm, AddrMode am = Offset);
private:
Register rn_; // base
Register rm_; // register offset
int32_t offset_; // valid if rm_ == no_reg
ShiftOp shift_op_;
int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg
AddrMode am_; // bits P, U, and W
friend class Assembler;
};
typedef int32_t Instr;
class Assembler : public Malloced {
public:
// Create an assembler. Instructions and relocation information are emitted
// into a buffer, with the instructions starting from the beginning and the
// relocation information starting from the end of the buffer. See CodeDesc
// for a detailed comment on the layout (globals.h).
//
// If the provided buffer is NULL, the assembler allocates and grows its own
// buffer, and buffer_size determines the initial buffer size. The buffer is
// owned by the assembler and deallocated upon destruction of the assembler.
//
// If the provided buffer is not NULL, the assembler uses the provided buffer
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
Assembler(void* buffer, int buffer_size);
~Assembler();
// GetCode emits any pending (non-emitted) code and fills the descriptor
// desc. GetCode() is idempotent; it returns the same result if no other
// Assembler functions are invoked in between GetCode() calls.
void GetCode(CodeDesc* desc);
// Label operations & relative jumps (PPUM Appendix D)
//
// Takes a branch opcode (cc) and a label (L) and generates
// either a backward branch or a forward branch and links it
// to the label fixup chain. Usage:
//
// Label L; // unbound label
// j(cc, &L); // forward branch to unbound label
// bind(&L); // bind label to the current pc
// j(cc, &L); // backward branch to bound label
// bind(&L); // illegal: a label may be bound only once
//
// Note: The same Label can be used for forward and backward branches
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
// Returns the branch offset to the given label from the current code position
// Links the label to the current position if it is still unbound
// Manages the jump elimination optimization if the second parameter is true.
int branch_offset(Label* L, bool jump_elimination_allowed);
// Return the address in the constant pool of the code target address used by
// the branch/call instruction at pc.
INLINE(static Address target_address_address_at(Address pc));
// Read/Modify the code target address in the branch/call instruction at pc.
INLINE(static Address target_address_at(Address pc));
INLINE(static void set_target_address_at(Address pc, Address target));
// Distance between the instruction referring to the address of the call
// target (ldr pc, [target addr in const pool]) and the return address
static const int kTargetAddrToReturnAddrDist = sizeof(Instr);
// ---------------------------------------------------------------------------
// Code generation
// Insert the smallest number of nop instructions
// possible to align the pc offset to a multiple
// of m. m must be a power of 2 (>= 4).
void Align(int m);
// Branch instructions
void b(int branch_offset, Condition cond = al);
void bl(int branch_offset, Condition cond = al);
void blx(int branch_offset); // v5 and above
void blx(Register target, Condition cond = al); // v5 and above
void bx(Register target, Condition cond = al); // v5 and above, plus v4t
// Convenience branch instructions using labels
void b(Label* L, Condition cond = al) {
b(branch_offset(L, cond == al), cond);
}
void b(Condition cond, Label* L) { b(branch_offset(L, cond == al), cond); }
void bl(Label* L, Condition cond = al) { bl(branch_offset(L, false), cond); }
void bl(Condition cond, Label* L) { bl(branch_offset(L, false), cond); }
void blx(Label* L) { blx(branch_offset(L, false)); } // v5 and above
// Data-processing instructions
void and_(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void eor(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void sub(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void sub(Register dst, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al) {
sub(dst, src1, Operand(src2), s, cond);
}
void rsb(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void add(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void adc(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void sbc(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void rsc(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void tst(Register src1, const Operand& src2, Condition cond = al);
void tst(Register src1, Register src2, Condition cond = al) {
tst(src1, Operand(src2), cond);
}
void teq(Register src1, const Operand& src2, Condition cond = al);
void cmp(Register src1, const Operand& src2, Condition cond = al);
void cmp(Register src1, Register src2, Condition cond = al) {
cmp(src1, Operand(src2), cond);
}
void cmn(Register src1, const Operand& src2, Condition cond = al);
void orr(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void orr(Register dst, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al) {
orr(dst, src1, Operand(src2), s, cond);
}
void mov(Register dst, const Operand& src,
SBit s = LeaveCC, Condition cond = al);
void mov(Register dst, Register src, SBit s = LeaveCC, Condition cond = al) {
mov(dst, Operand(src), s, cond);
}
void bic(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
void mvn(Register dst, const Operand& src,
SBit s = LeaveCC, Condition cond = al);
// Multiply instructions
void mla(Register dst, Register src1, Register src2, Register srcA,
SBit s = LeaveCC, Condition cond = al);
void mul(Register dst, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
void smlal(Register dstL, Register dstH, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
void smull(Register dstL, Register dstH, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
void umlal(Register dstL, Register dstH, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
void umull(Register dstL, Register dstH, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
// Miscellaneous arithmetic instructions
void clz(Register dst, Register src, Condition cond = al); // v5 and above
// Status register access instructions
void mrs(Register dst, SRegister s, Condition cond = al);
void msr(SRegisterFieldMask fields, const Operand& src, Condition cond = al);
// Load/Store instructions
void ldr(Register dst, const MemOperand& src, Condition cond = al);
void str(Register src, const MemOperand& dst, Condition cond = al);
void ldrb(Register dst, const MemOperand& src, Condition cond = al);
void strb(Register src, const MemOperand& dst, Condition cond = al);
void ldrh(Register dst, const MemOperand& src, Condition cond = al);
void strh(Register src, const MemOperand& dst, Condition cond = al);
void ldrsb(Register dst, const MemOperand& src, Condition cond = al);
void ldrsh(Register dst, const MemOperand& src, Condition cond = al);
// Load/Store multiple instructions
void ldm(BlockAddrMode am, Register base, RegList dst, Condition cond = al);
void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al);
// Semaphore instructions
void swp(Register dst, Register src, Register base, Condition cond = al);
void swpb(Register dst, Register src, Register base, Condition cond = al);
// Exception-generating instructions and debugging support
void stop(const char* msg);
void bkpt(uint32_t imm16); // v5 and above
void swi(uint32_t imm24, Condition cond = al);
// Coprocessor instructions
void cdp(Coprocessor coproc, int opcode_1,
CRegister crd, CRegister crn, CRegister crm,
int opcode_2, Condition cond = al);
void cdp2(Coprocessor coproc, int opcode_1,
CRegister crd, CRegister crn, CRegister crm,
int opcode_2); // v5 and above
void mcr(Coprocessor coproc, int opcode_1,
Register rd, CRegister crn, CRegister crm,
int opcode_2 = 0, Condition cond = al);
void mcr2(Coprocessor coproc, int opcode_1,
Register rd, CRegister crn, CRegister crm,
int opcode_2 = 0); // v5 and above
void mrc(Coprocessor coproc, int opcode_1,
Register rd, CRegister crn, CRegister crm,
int opcode_2 = 0, Condition cond = al);
void mrc2(Coprocessor coproc, int opcode_1,
Register rd, CRegister crn, CRegister crm,
int opcode_2 = 0); // v5 and above
void ldc(Coprocessor coproc, CRegister crd, const MemOperand& src,
LFlag l = Short, Condition cond = al);
void ldc(Coprocessor coproc, CRegister crd, Register base, int option,
LFlag l = Short, Condition cond = al);
void ldc2(Coprocessor coproc, CRegister crd, const MemOperand& src,
LFlag l = Short); // v5 and above
void ldc2(Coprocessor coproc, CRegister crd, Register base, int option,
LFlag l = Short); // v5 and above
void stc(Coprocessor coproc, CRegister crd, const MemOperand& dst,
LFlag l = Short, Condition cond = al);
void stc(Coprocessor coproc, CRegister crd, Register base, int option,
LFlag l = Short, Condition cond = al);
void stc2(Coprocessor coproc, CRegister crd, const MemOperand& dst,
LFlag l = Short); // v5 and above
void stc2(Coprocessor coproc, CRegister crd, Register base, int option,
LFlag l = Short); // v5 and above
// Pseudo instructions
void nop() { mov(r0, Operand(r0)); }
void push(Register src) {
str(src, MemOperand(sp, 4, NegPreIndex), al);
}
void pop(Register dst) {
ldr(dst, MemOperand(sp, 4, PostIndex), al);
}
void pop() {
add(sp, sp, Operand(kPointerSize));
}
// Load effective address of memory operand x into register dst
void lea(Register dst, const MemOperand& x,
SBit s = LeaveCC, Condition cond = al);
// Jump unconditionally to given label.
void jmp(Label* L) { b(L, al); }
// Debugging
// Record a comment relocation entry that can be used by a disassembler.
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
void WriteRecordedPositions();
int pc_offset() const { return pc_ - buffer_; }
int current_position() const { return current_position_; }
int current_statement_position() const { return current_position_; }
protected:
int buffer_space() const { return reloc_info_writer.pos() - pc_; }
// Read/patch instructions
Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); }
void instr_at_put(byte* pc, Instr instr) {
*reinterpret_cast<Instr*>(pc) = instr;
}
Instr instr_at(int pos) { return *reinterpret_cast<Instr*>(buffer_ + pos); }
void instr_at_put(int pos, Instr instr) {
*reinterpret_cast<Instr*>(buffer_ + pos) = instr;
}
// Decode branch instruction at pos and return branch target pos
int target_at(int pos);
// Patch branch instruction at pos to branch to given branch target pos
void target_at_put(int pos, int target_pos);
// Check if is time to emit a constant pool for pending reloc info entries
void CheckConstPool(bool force_emit, bool require_jump);
// Block the emission of the constant pool before pc_offset
void BlockConstPoolBefore(int pc_offset) {
if (no_const_pool_before_ < pc_offset) no_const_pool_before_ = pc_offset;
}
private:
// Code buffer:
// The buffer into which code and relocation info are generated.
byte* buffer_;
int buffer_size_;
// True if the assembler owns the buffer, false if buffer is external.
bool own_buffer_;
// Buffer size and constant pool distance are checked together at regular
// intervals of kBufferCheckInterval emitted bytes
static const int kBufferCheckInterval = 1*KB/2;
int next_buffer_check_; // pc offset of next buffer check
// Code generation
static const int kInstrSize = sizeof(Instr); // signed size
// The relocation writer's position is at least kGap bytes below the end of
// the generated instructions. This is so that multi-instruction sequences do
// not have to check for overflow. The same is true for writes of large
// relocation info entries.
static const int kGap = 32;
byte* pc_; // the program counter; moves forward
// Constant pool generation
// Pools are emitted in the instruction stream, preferably after unconditional
// jumps or after returns from functions (in dead code locations).
// If a long code sequence does not contain unconditional jumps, it is
// necessary to emit the constant pool before the pool gets too far from the
// location it is accessed from. In this case, we emit a jump over the emitted
// constant pool.
// Constants in the pool may be addresses of functions that gets relocated;
// if so, a relocation info entry is associated to the constant pool entry.
// Repeated checking whether the constant pool should be emitted is rather
// expensive. By default we only check again once a number of instructions
// has been generated. That also means that the sizing of the buffers is not
// an exact science, and that we rely on some slop to not overrun buffers.
static const int kCheckConstIntervalInst = 32;
static const int kCheckConstInterval = kCheckConstIntervalInst * kInstrSize;
// Pools are emitted after function return and in dead code at (more or less)
// regular intervals of kDistBetweenPools bytes
static const int kDistBetweenPools = 1*KB;
// Constants in pools are accessed via pc relative addressing, which can
// reach +/-4KB thereby defining a maximum distance between the instruction
// and the accessed constant. We satisfy this constraint by limiting the
// distance between pools.
static const int kMaxDistBetweenPools = 4*KB - 2*kBufferCheckInterval;
// Emission of the constant pool may be blocked in some code sequences
int no_const_pool_before_; // block emission before this pc offset
// Keep track of the last emitted pool to guarantee a maximal distance
int last_const_pool_end_; // pc offset following the last constant pool
// Relocation info generation
// Each relocation is encoded as a variable size value
static const int kMaxRelocSize = RelocInfoWriter::kMaxSize;
RelocInfoWriter reloc_info_writer;
// Relocation info records are also used during code generation as temporary
// containers for constants and code target addresses until they are emitted
// to the constant pool. These pending relocation info records are temporarily
// stored in a separate buffer until a constant pool is emitted.
// If every instruction in a long sequence is accessing the pool, we need one
// pending relocation entry per instruction.
static const int kMaxNumPRInfo = kMaxDistBetweenPools/kInstrSize;
RelocInfo prinfo_[kMaxNumPRInfo]; // the buffer of pending relocation info
int num_prinfo_; // number of pending reloc info entries in the buffer
// The bound position, before this we cannot do instruction elimination.
int last_bound_pos_;
// source position information
int current_position_;
int current_statement_position_;
int written_position_;
int written_statement_position_;
// Code emission
inline void CheckBuffer();
void GrowBuffer();
inline void emit(Instr x);
// Instruction generation
void addrmod1(Instr instr, Register rn, Register rd, const Operand& x);
void addrmod2(Instr instr, Register rd, const MemOperand& x);
void addrmod3(Instr instr, Register rd, const MemOperand& x);
void addrmod4(Instr instr, Register rn, RegList rl);
void addrmod5(Instr instr, CRegister crd, const MemOperand& x);
// Labels
void print(Label* L);
void bind_to(Label* L, int pos);
void link_to(Label* L, Label* appendix);
void next(Label* L);
// Record reloc info for current pc_
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
};
} } // namespace v8::internal
#endif // V8_ASSEMBLER_ARM_H_

302
deps/v8/src/assembler-ia32-inl.h

@ -0,0 +1,302 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// A light-weight IA32 Assembler.
#ifndef V8_ASSEMBLER_IA32_INL_H_
#define V8_ASSEMBLER_IA32_INL_H_
#include "cpu.h"
namespace v8 { namespace internal {
Condition NegateCondition(Condition cc) {
return static_cast<Condition>(cc ^ 1);
}
// The modes possibly affected by apply must be in kApplyMask.
void RelocInfo::apply(int delta) {
if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) {
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p -= delta; // relocate entry
} else if (rmode_ == JS_RETURN && IsCallInstruction()) {
// Special handling of js_return when a break point is set (call
// instruction has been inserted).
int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
*p -= delta; // relocate entry
} else if (IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object.
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p += delta; // relocate entry
}
}
Address RelocInfo::target_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
return Assembler::target_address_at(pc_);
}
Address RelocInfo::target_address_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
return reinterpret_cast<Address>(pc_);
}
void RelocInfo::set_target_address(Address target) {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
Assembler::set_target_address_at(pc_, target);
}
Object* RelocInfo::target_object() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
return *reinterpret_cast<Object**>(pc_);
}
Object** RelocInfo::target_object_address() {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
return reinterpret_cast<Object**>(pc_);
}
void RelocInfo::set_target_object(Object* target) {
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
*reinterpret_cast<Object**>(pc_) = target;
}
Address* RelocInfo::target_reference_address() {
ASSERT(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
return reinterpret_cast<Address*>(pc_);
}
Address RelocInfo::call_address() {
ASSERT(IsCallInstruction());
return Assembler::target_address_at(pc_ + 1);
}
void RelocInfo::set_call_address(Address target) {
ASSERT(IsCallInstruction());
Assembler::set_target_address_at(pc_ + 1, target);
}
Object* RelocInfo::call_object() {
ASSERT(IsCallInstruction());
return *call_object_address();
}
Object** RelocInfo::call_object_address() {
ASSERT(IsCallInstruction());
return reinterpret_cast<Object**>(pc_ + 1);
}
void RelocInfo::set_call_object(Object* target) {
ASSERT(IsCallInstruction());
*call_object_address() = target;
}
bool RelocInfo::IsCallInstruction() {
return *pc_ == 0xE8;
}
Immediate::Immediate(int x) {
x_ = x;
rmode_ = RelocInfo::NONE;
}
Immediate::Immediate(const ExternalReference& ext) {
x_ = reinterpret_cast<int32_t>(ext.address());
rmode_ = RelocInfo::EXTERNAL_REFERENCE;
}
Immediate::Immediate(const char* s) {
x_ = reinterpret_cast<int32_t>(s);
rmode_ = RelocInfo::EMBEDDED_STRING;
}
Immediate::Immediate(Label *internal_offset) {
x_ = reinterpret_cast<int32_t>(internal_offset);
rmode_ = RelocInfo::INTERNAL_REFERENCE;
}
Immediate::Immediate(Handle<Object> handle) {
// Verify all Objects referred by code are NOT in new space.
Object* obj = *handle;
ASSERT(!Heap::InNewSpace(obj));
if (obj->IsHeapObject()) {
x_ = reinterpret_cast<intptr_t>(handle.location());
rmode_ = RelocInfo::EMBEDDED_OBJECT;
} else {
// no relocation needed
x_ = reinterpret_cast<intptr_t>(obj);
rmode_ = RelocInfo::NONE;
}
}
Immediate::Immediate(Smi* value) {
x_ = reinterpret_cast<intptr_t>(value);
rmode_ = RelocInfo::NONE;
}
void Assembler::emit(uint32_t x) {
*reinterpret_cast<uint32_t*>(pc_) = x;
pc_ += sizeof(uint32_t);
}
void Assembler::emit(Handle<Object> handle) {
// Verify all Objects referred by code are NOT in new space.
Object* obj = *handle;
ASSERT(!Heap::InNewSpace(obj));
if (obj->IsHeapObject()) {
emit(reinterpret_cast<intptr_t>(handle.location()),
RelocInfo::EMBEDDED_OBJECT);
} else {
// no relocation needed
emit(reinterpret_cast<intptr_t>(obj));
}
}
void Assembler::emit(uint32_t x, RelocInfo::Mode rmode) {
if (rmode != RelocInfo::NONE) RecordRelocInfo(rmode);
emit(x);
}
void Assembler::emit(const Immediate& x) {
if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) {
Label* label = reinterpret_cast<Label*>(x.x_);
emit_code_relative_offset(label);
return;
}
if (x.rmode_ != RelocInfo::NONE) RecordRelocInfo(x.rmode_);
emit(x.x_);
}
void Assembler::emit_code_relative_offset(Label* label) {
if (label->is_bound()) {
int32_t pos;
pos = label->pos() + Code::kHeaderSize - kHeapObjectTag;
emit(pos);
} else {
emit_disp(label, Displacement::CODE_RELATIVE);
}
}
void Assembler::emit_w(const Immediate& x) {
ASSERT(x.rmode_ == RelocInfo::NONE);
uint16_t value = static_cast<uint16_t>(x.x_);
reinterpret_cast<uint16_t*>(pc_)[0] = value;
pc_ += sizeof(uint16_t);
}
Address Assembler::target_address_at(Address pc) {
return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc);
}
void Assembler::set_target_address_at(Address pc, Address target) {
int32_t* p = reinterpret_cast<int32_t*>(pc);
*p = target - (pc + sizeof(int32_t));
CPU::FlushICache(p, sizeof(int32_t));
}
Displacement Assembler::disp_at(Label* L) {
return Displacement(long_at(L->pos()));
}
void Assembler::disp_at_put(Label* L, Displacement disp) {
long_at_put(L->pos(), disp.data());
}
void Assembler::emit_disp(Label* L, Displacement::Type type) {
Displacement disp(L, type);
L->link_to(pc_offset());
emit(static_cast<int>(disp.data()));
}
void Operand::set_modrm(int mod, Register rm) {
ASSERT((mod & -4) == 0);
buf_[0] = mod << 6 | rm.code();
len_ = 1;
}
void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) {
ASSERT(len_ == 1 || len_ == 2);
int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]);
*p = disp;
len_ += sizeof(int32_t);
rmode_ = rmode;
}
Operand::Operand(Register reg) {
// reg
set_modrm(3, reg);
}
Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
// [disp/r]
set_modrm(0, ebp);
set_dispr(disp, rmode);
}
} } // namespace v8::internal
#endif // V8_ASSEMBLER_IA32_INL_H_

2205
deps/v8/src/assembler-ia32.cc

File diff suppressed because it is too large

863
deps/v8/src/assembler-ia32.h

@ -0,0 +1,863 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// A light-weight IA32 Assembler.
#ifndef V8_ASSEMBLER_IA32_H_
#define V8_ASSEMBLER_IA32_H_
namespace v8 { namespace internal {
// CPU Registers.
//
// 1) We would prefer to use an enum, but enum values are assignment-
// compatible with int, which has caused code-generation bugs.
//
// 2) We would prefer to use a class instead of a struct but we don't like
// the register initialization to depend on the particular initialization
// order (which appears to be different on OS X, Linux, and Windows for the
// installed versions of C++ we tried). Using a struct permits C-style
// "initialization". Also, the Register objects cannot be const as this
// forces initialization stubs in MSVC, making us dependent on initialization
// order.
//
// 3) By not using an enum, we are possibly preventing the compiler from
// doing certain constant folds, which may significantly reduce the
// code generated for some assembly instructions (because they boil down
// to a few constants). If this is a problem, we could change the code
// such that we use an enum in optimized mode, and the struct in debug
// mode. This way we get the compile-time error checking in debug mode
// and best performance in optimized code.
//
struct Register {
bool is_valid() const { return 0 <= code_ && code_ < 8; }
bool is(Register reg) const { return code_ == reg.code_; }
// eax, ebx, ecx and edx are byte registers, the rest are not.
bool is_byte_register() const { return code_ <= 3; }
int code() const {
ASSERT(is_valid());
return code_;
}
int bit() const {
ASSERT(is_valid());
return 1 << code_;
}
// (unfortunately we can't make this private in a struct)
int code_;
};
const int kNumRegisters = 8;
extern Register eax;
extern Register ecx;
extern Register edx;
extern Register ebx;
extern Register esp;
extern Register ebp;
extern Register esi;
extern Register edi;
extern Register no_reg;
struct XMMRegister {
bool is_valid() const { return 0 <= code_ && code_ < 2; } // currently
int code() const {
ASSERT(is_valid());
return code_;
}
int code_;
};
extern XMMRegister xmm0;
extern XMMRegister xmm1;
extern XMMRegister xmm2;
extern XMMRegister xmm3;
extern XMMRegister xmm4;
extern XMMRegister xmm5;
extern XMMRegister xmm6;
extern XMMRegister xmm7;
enum Condition {
// any value < 0 is considered no_condition
no_condition = -1,
overflow = 0,
no_overflow = 1,
below = 2,
above_equal = 3,
equal = 4,
not_equal = 5,
below_equal = 6,
above = 7,
negative = 8,
positive = 9,
parity_even = 10,
parity_odd = 11,
less = 12,
greater_equal = 13,
less_equal = 14,
greater = 15,
// aliases
carry = below,
not_carry = above_equal,
zero = equal,
not_zero = not_equal,
sign = negative,
not_sign = positive
};
// Returns the equivalent of !cc.
// Negation of the default no_condition (-1) results in a non-default
// no_condition value (-2). As long as tests for no_condition check
// for condition < 0, this will work as expected.
inline Condition NegateCondition(Condition cc);
// Corresponds to transposing the operands of a comparison.
inline Condition ReverseCondition(Condition cc) {
switch (cc) {
case below:
return above;
case above:
return below;
case above_equal:
return below_equal;
case below_equal:
return above_equal;
case less:
return greater;
case greater:
return less;
case greater_equal:
return less_equal;
case less_equal:
return greater_equal;
default:
return cc;
};
}
enum Hint {
no_hint = 0,
not_taken = 0x2e,
taken = 0x3e
};
// The result of negating a hint is as if the corresponding condition
// were negated by NegateCondition. That is, no_hint is mapped to
// itself and not_taken and taken are mapped to each other.
inline Hint NegateHint(Hint hint) {
return (hint == no_hint)
? no_hint
: ((hint == not_taken) ? taken : not_taken);
}
// -----------------------------------------------------------------------------
// Machine instruction Immediates
class Immediate BASE_EMBEDDED {
public:
inline explicit Immediate(int x);
inline explicit Immediate(const char* s);
inline explicit Immediate(const ExternalReference& ext);
inline explicit Immediate(Handle<Object> handle);
inline explicit Immediate(Smi* value);
static Immediate CodeRelativeOffset(Label* label) {
return Immediate(label);
}
bool is_zero() const { return x_ == 0 && rmode_ == RelocInfo::NONE; }
bool is_int8() const {
return -128 <= x_ && x_ < 128 && rmode_ == RelocInfo::NONE;
}
bool is_int16() const {
return -32768 <= x_ && x_ < 32768 && rmode_ == RelocInfo::NONE;
}
private:
inline explicit Immediate(Label* value);
int x_;
RelocInfo::Mode rmode_;
friend class Assembler;
};
// -----------------------------------------------------------------------------
// Machine instruction Operands
enum ScaleFactor {
times_1 = 0,
times_2 = 1,
times_4 = 2,
times_8 = 3
};
class Operand BASE_EMBEDDED {
public:
// reg
INLINE(explicit Operand(Register reg));
// [disp/r]
INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode));
// disp only must always be relocated
// [base + disp/r]
explicit Operand(Register base, int32_t disp,
RelocInfo::Mode rmode = RelocInfo::NONE);
// [base + index*scale + disp/r]
explicit Operand(Register base,
Register index,
ScaleFactor scale,
int32_t disp,
RelocInfo::Mode rmode = RelocInfo::NONE);
// [index*scale + disp/r]
explicit Operand(Register index,
ScaleFactor scale,
int32_t disp,
RelocInfo::Mode rmode = RelocInfo::NONE);
static Operand StaticVariable(const ExternalReference& ext) {
return Operand(reinterpret_cast<int32_t>(ext.address()),
RelocInfo::EXTERNAL_REFERENCE);
}
static Operand StaticArray(Register index,
ScaleFactor scale,
const ExternalReference& arr) {
return Operand(index, scale, reinterpret_cast<int32_t>(arr.address()),
RelocInfo::EXTERNAL_REFERENCE);
}
// Returns true if this Operand is a wrapper for the specified register.
bool is_reg(Register reg) const;
private:
byte buf_[6];
// The number of bytes in buf_.
unsigned int len_;
// Only valid if len_ > 4.
RelocInfo::Mode rmode_;
// Set the ModRM byte without an encoded 'reg' register. The
// register is encoded later as part of the emit_operand operation.
inline void set_modrm(int mod, Register rm);
inline void set_sib(ScaleFactor scale, Register index, Register base);
inline void set_disp8(int8_t disp);
inline void set_dispr(int32_t disp, RelocInfo::Mode rmode);
friend class Assembler;
};
// -----------------------------------------------------------------------------
// A Displacement describes the 32bit immediate field of an instruction which
// may be used together with a Label in order to refer to a yet unknown code
// position. Displacements stored in the instruction stream are used to describe
// the instruction and to chain a list of instructions using the same Label.
// A Displacement contains 2 different fields:
//
// next field: position of next displacement in the chain (0 = end of list)
// type field: instruction type
//
// A next value of null (0) indicates the end of a chain (note that there can
// be no displacement at position zero, because there is always at least one
// instruction byte before the displacement).
//
// Displacement _data field layout
//
// |31.....2|1......0|
// [ next | type |
class Displacement BASE_EMBEDDED {
public:
enum Type {
UNCONDITIONAL_JUMP,
CODE_RELATIVE,
OTHER
};
int data() const { return data_; }
Type type() const { return TypeField::decode(data_); }
void next(Label* L) const {
int n = NextField::decode(data_);
n > 0 ? L->link_to(n) : L->Unuse();
}
void link_to(Label* L) { init(L, type()); }
explicit Displacement(int data) { data_ = data; }
Displacement(Label* L, Type type) { init(L, type); }
void print() {
PrintF("%s (%x) ", (type() == UNCONDITIONAL_JUMP ? "jmp" : "[other]"),
NextField::decode(data_));
}
private:
int data_;
class TypeField: public BitField<Type, 0, 2> {};
class NextField: public BitField<int, 2, 32-2> {};
void init(Label* L, Type type);
};
// CpuFeatures keeps track of which features are supported by the target CPU.
// Supported features must be enabled by a Scope before use.
// Example:
// if (CpuFeatures::IsSupported(SSE2)) {
// CpuFeatures::Scope fscope(SSE2);
// // Generate SSE2 floating point code.
// } else {
// // Generate standard x87 floating point code.
// }
class CpuFeatures : public AllStatic {
public:
// Feature flags bit positions. They are mostly based on the CPUID spec.
// (We assign CPUID itself to one of the currently reserved bits --
// feel free to change this if needed.)
enum Feature { SSE3 = 32, SSE2 = 26, CMOV = 15, RDTSC = 4, CPUID = 10 };
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
static void Probe();
// Check whether a feature is supported by the target CPU.
static bool IsSupported(Feature f) {
return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
}
// Check whether a feature is currently enabled.
static bool IsEnabled(Feature f) {
return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0;
}
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
explicit Scope(Feature f) {
ASSERT(CpuFeatures::IsSupported(f));
old_enabled_ = CpuFeatures::enabled_;
CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f);
}
~Scope() { CpuFeatures::enabled_ = old_enabled_; }
private:
uint64_t old_enabled_;
#else
public:
explicit Scope(Feature f) {}
#endif
};
private:
static uint64_t supported_;
static uint64_t enabled_;
};
class Assembler : public Malloced {
private:
// The relocation writer's position is kGap bytes below the end of
// the generated instructions. This leaves enough space for the
// longest possible ia32 instruction (17 bytes as of 9/26/06) and
// allows for a single, fast space check per instruction.
static const int kGap = 32;
public:
// Create an assembler. Instructions and relocation information are emitted
// into a buffer, with the instructions starting from the beginning and the
// relocation information starting from the end of the buffer. See CodeDesc
// for a detailed comment on the layout (globals.h).
//
// If the provided buffer is NULL, the assembler allocates and grows its own
// buffer, and buffer_size determines the initial buffer size. The buffer is
// owned by the assembler and deallocated upon destruction of the assembler.
//
// If the provided buffer is not NULL, the assembler uses the provided buffer
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
Assembler(void* buffer, int buffer_size);
~Assembler();
// GetCode emits any pending (non-emitted) code and fills the descriptor
// desc. GetCode() is idempotent; it returns the same result if no other
// Assembler functions are invoked in between GetCode() calls.
void GetCode(CodeDesc* desc);
// Read/Modify the code target in the branch/call instruction at pc.
inline static Address target_address_at(Address pc);
inline static void set_target_address_at(Address pc, Address target);
// Distance between the address of the code target in the call instruction
// and the return address
static const int kTargetAddrToReturnAddrDist = kPointerSize;
// ---------------------------------------------------------------------------
// Code generation
//
// - function names correspond one-to-one to ia32 instruction mnemonics
// - unless specified otherwise, instructions operate on 32bit operands
// - instructions on 8bit (byte) operands/registers have a trailing '_b'
// - instructions on 16bit (word) operands/registers have a trailing '_w'
// - naming conflicts with C++ keywords are resolved via a trailing '_'
// NOTE ON INTERFACE: Currently, the interface is not very consistent
// in the sense that some operations (e.g. mov()) can be called in more
// the one way to generate the same instruction: The Register argument
// can in some cases be replaced with an Operand(Register) argument.
// This should be cleaned up and made more orthogonal. The questions
// is: should we always use Operands instead of Registers where an
// Operand is possible, or should we have a Register (overloaded) form
// instead? We must be careful to make sure that the selected instruction
// is obvious from the parameters to avoid hard-to-find code generation
// bugs.
// Insert the smallest number of nop instructions
// possible to align the pc offset to a multiple
// of m. m must be a power of 2.
void Align(int m);
// Stack
void pushad();
void popad();
void pushfd();
void popfd();
void push(const Immediate& x);
void push(Register src);
void push(const Operand& src);
void push(Label* label, RelocInfo::Mode relocation_mode);
void pop(Register dst);
void pop(const Operand& dst);
void enter(const Immediate& size);
void leave();
// Moves
void mov_b(Register dst, const Operand& src);
void mov_b(const Operand& dst, int8_t imm8);
void mov_b(const Operand& dst, Register src);
void mov_w(Register dst, const Operand& src);
void mov_w(const Operand& dst, Register src);
void mov(Register dst, int32_t imm32);
void mov(Register dst, const Immediate& x);
void mov(Register dst, Handle<Object> handle);
void mov(Register dst, const Operand& src);
void mov(Register dst, Register src);
void mov(const Operand& dst, const Immediate& x);
void mov(const Operand& dst, Handle<Object> handle);
void mov(const Operand& dst, Register src);
void movsx_b(Register dst, const Operand& src);
void movsx_w(Register dst, const Operand& src);
void movzx_b(Register dst, const Operand& src);
void movzx_w(Register dst, const Operand& src);
// Conditional moves
void cmov(Condition cc, Register dst, int32_t imm32);
void cmov(Condition cc, Register dst, Handle<Object> handle);
void cmov(Condition cc, Register dst, const Operand& src);
// Exchange two registers
void xchg(Register dst, Register src);
// Arithmetics
void adc(Register dst, int32_t imm32);
void adc(Register dst, const Operand& src);
void add(Register dst, const Operand& src);
void add(const Operand& dst, const Immediate& x);
void and_(Register dst, int32_t imm32);
void and_(Register dst, const Operand& src);
void and_(const Operand& src, Register dst);
void and_(const Operand& dst, const Immediate& x);
void cmpb(const Operand& op, int8_t imm8);
void cmpb_al(const Operand& op);
void cmpw_ax(const Operand& op);
void cmpw(const Operand& op, Immediate imm16);
void cmp(Register reg, int32_t imm32);
void cmp(Register reg, Handle<Object> handle);
void cmp(Register reg, const Operand& op);
void cmp(const Operand& op, const Immediate& imm);
void dec_b(Register dst);
void dec(Register dst);
void dec(const Operand& dst);
void cdq();
void idiv(Register src);
void imul(Register dst, const Operand& src);
void imul(Register dst, Register src, int32_t imm32);
void inc(Register dst);
void inc(const Operand& dst);
void lea(Register dst, const Operand& src);
void mul(Register src);
void neg(Register dst);
void not_(Register dst);
void or_(Register dst, int32_t imm32);
void or_(Register dst, const Operand& src);
void or_(const Operand& dst, Register src);
void or_(const Operand& dst, const Immediate& x);
void rcl(Register dst, uint8_t imm8);
void sar(Register dst, uint8_t imm8);
void sar(Register dst);
void sbb(Register dst, const Operand& src);
void shld(Register dst, const Operand& src);
void shl(Register dst, uint8_t imm8);
void shl(Register dst);
void shrd(Register dst, const Operand& src);
void shr(Register dst, uint8_t imm8);
void shr(Register dst);
void shr_cl(Register dst);
void sub(const Operand& dst, const Immediate& x);
void sub(Register dst, const Operand& src);
void sub(const Operand& dst, Register src);
void test(Register reg, const Immediate& imm);
void test(Register reg, const Operand& op);
void test(const Operand& op, const Immediate& imm);
void xor_(Register dst, int32_t imm32);
void xor_(Register dst, const Operand& src);
void xor_(const Operand& src, Register dst);
void xor_(const Operand& dst, const Immediate& x);
// Bit operations.
void bt(const Operand& dst, Register src);
void bts(const Operand& dst, Register src);
// Miscellaneous
void hlt();
void int3();
void nop();
void rdtsc();
void ret(int imm16);
// Label operations & relative jumps (PPUM Appendix D)
//
// Takes a branch opcode (cc) and a label (L) and generates
// either a backward branch or a forward branch and links it
// to the label fixup chain. Usage:
//
// Label L; // unbound label
// j(cc, &L); // forward branch to unbound label
// bind(&L); // bind label to the current pc
// j(cc, &L); // backward branch to bound label
// bind(&L); // illegal: a label may be bound only once
//
// Note: The same Label can be used for forward and backward branches
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
// Calls
void call(Label* L);
void call(byte* entry, RelocInfo::Mode rmode);
void call(const Operand& adr);
void call(Handle<Code> code, RelocInfo::Mode rmode);
// Jumps
void jmp(Label* L); // unconditional jump to L
void jmp(byte* entry, RelocInfo::Mode rmode);
void jmp(const Operand& adr);
void jmp(Handle<Code> code, RelocInfo::Mode rmode);
// Conditional jumps
void j(Condition cc, Label* L, Hint hint = no_hint);
void j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint = no_hint);
void j(Condition cc, Handle<Code> code, Hint hint = no_hint);
// Floating-point operations
void fld(int i);
void fld1();
void fldz();
void fld_s(const Operand& adr);
void fld_d(const Operand& adr);
void fstp_s(const Operand& adr);
void fstp_d(const Operand& adr);
void fild_s(const Operand& adr);
void fild_d(const Operand& adr);
void fist_s(const Operand& adr);
void fistp_s(const Operand& adr);
void fistp_d(const Operand& adr);
void fisttp_s(const Operand& adr);
void fabs();
void fchs();
void fadd(int i);
void fsub(int i);
void fmul(int i);
void fdiv(int i);
void fisub_s(const Operand& adr);
void faddp(int i = 1);
void fsubp(int i = 1);
void fsubrp(int i = 1);
void fmulp(int i = 1);
void fdivp(int i = 1);
void fprem();
void fprem1();
void fxch(int i = 1);
void fincstp();
void ffree(int i = 0);
void ftst();
void fucomp(int i);
void fucompp();
void fcompp();
void fnstsw_ax();
void fwait();
void fnclex();
void frndint();
void sahf();
void setcc(Condition cc, Register reg);
void cpuid();
// SSE2 instructions
void cvttss2si(Register dst, const Operand& src);
void cvttsd2si(Register dst, const Operand& src);
void cvtsi2sd(XMMRegister dst, const Operand& src);
void addsd(XMMRegister dst, XMMRegister src);
void subsd(XMMRegister dst, XMMRegister src);
void mulsd(XMMRegister dst, XMMRegister src);
void divsd(XMMRegister dst, XMMRegister src);
// Use either movsd or movlpd.
void movdbl(XMMRegister dst, const Operand& src);
void movdbl(const Operand& dst, XMMRegister src);
// Debugging
void Print();
// Check the code size generated from label to here.
int SizeOfCodeGeneratedSince(Label* l) { return pc_offset() - l->pos(); }
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
// Record a comment relocation entry that can be used by a disassembler.
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
void WriteRecordedPositions();
// Writes a single word of data in the code stream.
// Used for inline tables, e.g., jump-tables.
void dd(uint32_t data, RelocInfo::Mode reloc_info);
// Writes the absolute address of a bound label at the given position in
// the generated code. That positions should have the relocation mode
// internal_reference!
void WriteInternalReference(int position, const Label& bound_label);
int pc_offset() const { return pc_ - buffer_; }
int current_statement_position() const { return current_statement_position_; }
int current_position() const { return current_position_; }
// Check if there is less than kGap bytes available in the buffer.
// If this is the case, we need to grow the buffer before emitting
// an instruction or relocation information.
inline bool overflow() const { return pc_ >= reloc_info_writer.pos() - kGap; }
// Get the number of bytes available in the buffer.
inline int available_space() const { return reloc_info_writer.pos() - pc_; }
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
protected:
void movsd(XMMRegister dst, const Operand& src);
void movsd(const Operand& dst, XMMRegister src);
void emit_sse_operand(XMMRegister reg, const Operand& adr);
void emit_sse_operand(XMMRegister dst, XMMRegister src);
private:
byte* addr_at(int pos) { return buffer_ + pos; }
byte byte_at(int pos) { return buffer_[pos]; }
uint32_t long_at(int pos) {
return *reinterpret_cast<uint32_t*>(addr_at(pos));
}
void long_at_put(int pos, uint32_t x) {
*reinterpret_cast<uint32_t*>(addr_at(pos)) = x;
}
// code emission
void GrowBuffer();
inline void emit(uint32_t x);
inline void emit(Handle<Object> handle);
inline void emit(uint32_t x, RelocInfo::Mode rmode);
inline void emit(const Immediate& x);
inline void emit_w(const Immediate& x);
// Emit the code-object-relative offset of the label's position
inline void emit_code_relative_offset(Label* label);
// instruction generation
void emit_arith_b(int op1, int op2, Register dst, int imm8);
// Emit a basic arithmetic instruction (i.e. first byte of the family is 0x81)
// with a given destination expression and an immediate operand. It attempts
// to use the shortest encoding possible.
// sel specifies the /n in the modrm byte (see the Intel PRM).
void emit_arith(int sel, Operand dst, const Immediate& x);
void emit_operand(Register reg, const Operand& adr);
void emit_farith(int b1, int b2, int i);
// labels
void print(Label* L);
void bind_to(Label* L, int pos);
void link_to(Label* L, Label* appendix);
// displacements
inline Displacement disp_at(Label* L);
inline void disp_at_put(Label* L, Displacement disp);
inline void emit_disp(Label* L, Displacement::Type type);
// record reloc info for current pc_
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
friend class CodePatcher;
friend class EnsureSpace;
// Code buffer:
// The buffer into which code and relocation info are generated.
byte* buffer_;
int buffer_size_;
// True if the assembler owns the buffer, false if buffer is external.
bool own_buffer_;
// code generation
byte* pc_; // the program counter; moves forward
RelocInfoWriter reloc_info_writer;
// push-pop elimination
byte* last_pc_;
// source position information
int current_statement_position_;
int current_position_;
int written_statement_position_;
int written_position_;
};
// Helper class that ensures that there is enough space for generating
// instructions and relocation information. The constructor makes
// sure that there is enough space and (in debug mode) the destructor
// checks that we did not generate too much.
class EnsureSpace BASE_EMBEDDED {
public:
explicit EnsureSpace(Assembler* assembler) : assembler_(assembler) {
if (assembler_->overflow()) assembler_->GrowBuffer();
#ifdef DEBUG
space_before_ = assembler_->available_space();
#endif
}
#ifdef DEBUG
~EnsureSpace() {
int bytes_generated = space_before_ - assembler_->available_space();
ASSERT(bytes_generated < assembler_->kGap);
}
#endif
private:
Assembler* assembler_;
#ifdef DEBUG
int space_before_;
#endif
};
} } // namespace v8::internal
#endif // V8_ASSEMBLER_IA32_H_

585
deps/v8/src/assembler.cc

@ -0,0 +1,585 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
#include "v8.h"
#include "arguments.h"
#include "execution.h"
#include "ic-inl.h"
#include "factory.h"
#include "runtime.h"
#include "serialize.h"
#include "stub-cache.h"
#include "regexp-stack.h"
namespace v8 { namespace internal {
// -----------------------------------------------------------------------------
// Implementation of Label
int Label::pos() const {
if (pos_ < 0) return -pos_ - 1;
if (pos_ > 0) return pos_ - 1;
UNREACHABLE();
return 0;
}
// -----------------------------------------------------------------------------
// Implementation of RelocInfoWriter and RelocIterator
//
// Encoding
//
// The most common modes are given single-byte encodings. Also, it is
// easy to identify the type of reloc info and skip unwanted modes in
// an iteration.
//
// The encoding relies on the fact that there are less than 14
// different relocation modes.
//
// embedded_object: [6 bits pc delta] 00
//
// code_taget: [6 bits pc delta] 01
//
// position: [6 bits pc delta] 10,
// [7 bits signed data delta] 0
//
// statement_position: [6 bits pc delta] 10,
// [7 bits signed data delta] 1
//
// any nondata mode: 00 [4 bits rmode] 11, // rmode: 0..13 only
// 00 [6 bits pc delta]
//
// pc-jump: 00 1111 11,
// 00 [6 bits pc delta]
//
// pc-jump: 01 1111 11,
// (variable length) 7 - 26 bit pc delta, written in chunks of 7
// bits, the lowest 7 bits written first.
//
// data-jump + pos: 00 1110 11,
// signed int, lowest byte written first
//
// data-jump + st.pos: 01 1110 11,
// signed int, lowest byte written first
//
// data-jump + comm.: 10 1110 11,
// signed int, lowest byte written first
//
const int kMaxRelocModes = 14;
const int kTagBits = 2;
const int kTagMask = (1 << kTagBits) - 1;
const int kExtraTagBits = 4;
const int kPositionTypeTagBits = 1;
const int kSmallDataBits = kBitsPerByte - kPositionTypeTagBits;
const int kEmbeddedObjectTag = 0;
const int kCodeTargetTag = 1;
const int kPositionTag = 2;
const int kDefaultTag = 3;
const int kPCJumpTag = (1 << kExtraTagBits) - 1;
const int kSmallPCDeltaBits = kBitsPerByte - kTagBits;
const int kSmallPCDeltaMask = (1 << kSmallPCDeltaBits) - 1;
const int kVariableLengthPCJumpTopTag = 1;
const int kChunkBits = 7;
const int kChunkMask = (1 << kChunkBits) - 1;
const int kLastChunkTagBits = 1;
const int kLastChunkTagMask = 1;
const int kLastChunkTag = 1;
const int kDataJumpTag = kPCJumpTag - 1;
const int kNonstatementPositionTag = 0;
const int kStatementPositionTag = 1;
const int kCommentTag = 2;
uint32_t RelocInfoWriter::WriteVariableLengthPCJump(uint32_t pc_delta) {
// Return if the pc_delta can fit in kSmallPCDeltaBits bits.
// Otherwise write a variable length PC jump for the bits that do
// not fit in the kSmallPCDeltaBits bits.
if (is_uintn(pc_delta, kSmallPCDeltaBits)) return pc_delta;
WriteExtraTag(kPCJumpTag, kVariableLengthPCJumpTopTag);
uint32_t pc_jump = pc_delta >> kSmallPCDeltaBits;
ASSERT(pc_jump > 0);
// Write kChunkBits size chunks of the pc_jump.
for (; pc_jump > 0; pc_jump = pc_jump >> kChunkBits) {
byte b = pc_jump & kChunkMask;
*--pos_ = b << kLastChunkTagBits;
}
// Tag the last chunk so it can be identified.
*pos_ = *pos_ | kLastChunkTag;
// Return the remaining kSmallPCDeltaBits of the pc_delta.
return pc_delta & kSmallPCDeltaMask;
}
void RelocInfoWriter::WriteTaggedPC(uint32_t pc_delta, int tag) {
// Write a byte of tagged pc-delta, possibly preceded by var. length pc-jump.
pc_delta = WriteVariableLengthPCJump(pc_delta);
*--pos_ = pc_delta << kTagBits | tag;
}
void RelocInfoWriter::WriteTaggedData(int32_t data_delta, int tag) {
*--pos_ = data_delta << kPositionTypeTagBits | tag;
}
void RelocInfoWriter::WriteExtraTag(int extra_tag, int top_tag) {
*--pos_ = top_tag << (kTagBits + kExtraTagBits) |
extra_tag << kTagBits |
kDefaultTag;
}
void RelocInfoWriter::WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag) {
// Write two-byte tagged pc-delta, possibly preceded by var. length pc-jump.
pc_delta = WriteVariableLengthPCJump(pc_delta);
WriteExtraTag(extra_tag, 0);
*--pos_ = pc_delta;
}
void RelocInfoWriter::WriteExtraTaggedData(int32_t data_delta, int top_tag) {
WriteExtraTag(kDataJumpTag, top_tag);
for (int i = 0; i < kIntSize; i++) {
*--pos_ = data_delta;
data_delta = ArithmeticShiftRight(data_delta, kBitsPerByte);
}
}
void RelocInfoWriter::Write(const RelocInfo* rinfo) {
#ifdef DEBUG
byte* begin_pos = pos_;
#endif
Counters::reloc_info_count.Increment();
ASSERT(rinfo->pc() - last_pc_ >= 0);
ASSERT(RelocInfo::NUMBER_OF_MODES < kMaxRelocModes);
// Use unsigned delta-encoding for pc.
uint32_t pc_delta = rinfo->pc() - last_pc_;
RelocInfo::Mode rmode = rinfo->rmode();
// The two most common modes are given small tags, and usually fit in a byte.
if (rmode == RelocInfo::EMBEDDED_OBJECT) {
WriteTaggedPC(pc_delta, kEmbeddedObjectTag);
} else if (rmode == RelocInfo::CODE_TARGET) {
WriteTaggedPC(pc_delta, kCodeTargetTag);
} else if (RelocInfo::IsPosition(rmode)) {
// Use signed delta-encoding for data.
int32_t data_delta = rinfo->data() - last_data_;
int pos_type_tag = rmode == RelocInfo::POSITION ? kNonstatementPositionTag
: kStatementPositionTag;
// Check if data is small enough to fit in a tagged byte.
if (is_intn(data_delta, kSmallDataBits)) {
WriteTaggedPC(pc_delta, kPositionTag);
WriteTaggedData(data_delta, pos_type_tag);
last_data_ = rinfo->data();
} else {
// Otherwise, use costly encoding.
WriteExtraTaggedPC(pc_delta, kPCJumpTag);
WriteExtraTaggedData(data_delta, pos_type_tag);
last_data_ = rinfo->data();
}
} else if (RelocInfo::IsComment(rmode)) {
// Comments are normally not generated, so we use the costly encoding.
WriteExtraTaggedPC(pc_delta, kPCJumpTag);
WriteExtraTaggedData(rinfo->data() - last_data_, kCommentTag);
last_data_ = rinfo->data();
} else {
// For all other modes we simply use the mode as the extra tag.
// None of these modes need a data component.
ASSERT(rmode < kPCJumpTag && rmode < kDataJumpTag);
WriteExtraTaggedPC(pc_delta, rmode);
}
last_pc_ = rinfo->pc();
#ifdef DEBUG
ASSERT(begin_pos - pos_ <= kMaxSize);
#endif
}
inline int RelocIterator::AdvanceGetTag() {
return *--pos_ & kTagMask;
}
inline int RelocIterator::GetExtraTag() {
return (*pos_ >> kTagBits) & ((1 << kExtraTagBits) - 1);
}
inline int RelocIterator::GetTopTag() {
return *pos_ >> (kTagBits + kExtraTagBits);
}
inline void RelocIterator::ReadTaggedPC() {
rinfo_.pc_ += *pos_ >> kTagBits;
}
inline void RelocIterator::AdvanceReadPC() {
rinfo_.pc_ += *--pos_;
}
void RelocIterator::AdvanceReadData() {
int32_t x = 0;
for (int i = 0; i < kIntSize; i++) {
x |= *--pos_ << i * kBitsPerByte;
}
rinfo_.data_ += x;
}
void RelocIterator::AdvanceReadVariableLengthPCJump() {
// Read the 32-kSmallPCDeltaBits most significant bits of the
// pc jump in kChunkBits bit chunks and shift them into place.
// Stop when the last chunk is encountered.
uint32_t pc_jump = 0;
for (int i = 0; i < kIntSize; i++) {
byte pc_jump_part = *--pos_;
pc_jump |= (pc_jump_part >> kLastChunkTagBits) << i * kChunkBits;
if ((pc_jump_part & kLastChunkTagMask) == 1) break;
}
// The least significant kSmallPCDeltaBits bits will be added
// later.
rinfo_.pc_ += pc_jump << kSmallPCDeltaBits;
}
inline int RelocIterator::GetPositionTypeTag() {
return *pos_ & ((1 << kPositionTypeTagBits) - 1);
}
inline void RelocIterator::ReadTaggedData() {
int8_t signed_b = *pos_;
rinfo_.data_ += ArithmeticShiftRight(signed_b, kPositionTypeTagBits);
}
inline RelocInfo::Mode RelocIterator::DebugInfoModeFromTag(int tag) {
if (tag == kStatementPositionTag) {
return RelocInfo::STATEMENT_POSITION;
} else if (tag == kNonstatementPositionTag) {
return RelocInfo::POSITION;
} else {
ASSERT(tag == kCommentTag);
return RelocInfo::COMMENT;
}
}
void RelocIterator::next() {
ASSERT(!done());
// Basically, do the opposite of RelocInfoWriter::Write.
// Reading of data is as far as possible avoided for unwanted modes,
// but we must always update the pc.
//
// We exit this loop by returning when we find a mode we want.
while (pos_ > end_) {
int tag = AdvanceGetTag();
if (tag == kEmbeddedObjectTag) {
ReadTaggedPC();
if (SetMode(RelocInfo::EMBEDDED_OBJECT)) return;
} else if (tag == kCodeTargetTag) {
ReadTaggedPC();
if (*(reinterpret_cast<int*>(rinfo_.pc())) == 0x61) {
tag = 0;
}
if (SetMode(RelocInfo::CODE_TARGET)) return;
} else if (tag == kPositionTag) {
ReadTaggedPC();
Advance();
// Check if we want source positions.
if (mode_mask_ & RelocInfo::kPositionMask) {
// Check if we want this type of source position.
if (SetMode(DebugInfoModeFromTag(GetPositionTypeTag()))) {
// Finally read the data before returning.
ReadTaggedData();
return;
}
}
} else {
ASSERT(tag == kDefaultTag);
int extra_tag = GetExtraTag();
if (extra_tag == kPCJumpTag) {
int top_tag = GetTopTag();
if (top_tag == kVariableLengthPCJumpTopTag) {
AdvanceReadVariableLengthPCJump();
} else {
AdvanceReadPC();
}
} else if (extra_tag == kDataJumpTag) {
// Check if we want debug modes (the only ones with data).
if (mode_mask_ & RelocInfo::kDebugMask) {
int top_tag = GetTopTag();
AdvanceReadData();
if (SetMode(DebugInfoModeFromTag(top_tag))) return;
} else {
// Otherwise, just skip over the data.
Advance(kIntSize);
}
} else {
AdvanceReadPC();
if (SetMode(static_cast<RelocInfo::Mode>(extra_tag))) return;
}
}
}
done_ = true;
}
RelocIterator::RelocIterator(Code* code, int mode_mask) {
rinfo_.pc_ = code->instruction_start();
rinfo_.data_ = 0;
// relocation info is read backwards
pos_ = code->relocation_start() + code->relocation_size();
end_ = code->relocation_start();
done_ = false;
mode_mask_ = mode_mask;
if (mode_mask_ == 0) pos_ = end_;
next();
}
RelocIterator::RelocIterator(const CodeDesc& desc, int mode_mask) {
rinfo_.pc_ = desc.buffer;
rinfo_.data_ = 0;
// relocation info is read backwards
pos_ = desc.buffer + desc.buffer_size;
end_ = pos_ - desc.reloc_size;
done_ = false;
mode_mask_ = mode_mask;
if (mode_mask_ == 0) pos_ = end_;
next();
}
// -----------------------------------------------------------------------------
// Implementation of RelocInfo
#ifdef ENABLE_DISASSEMBLER
const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
switch (rmode) {
case RelocInfo::NONE:
return "no reloc";
case RelocInfo::EMBEDDED_OBJECT:
return "embedded object";
case RelocInfo::EMBEDDED_STRING:
return "embedded string";
case RelocInfo::CONSTRUCT_CALL:
return "code target (js construct call)";
case RelocInfo::CODE_TARGET_CONTEXT:
return "code target (context)";
case RelocInfo::CODE_TARGET:
return "code target";
case RelocInfo::RUNTIME_ENTRY:
return "runtime entry";
case RelocInfo::JS_RETURN:
return "js return";
case RelocInfo::COMMENT:
return "comment";
case RelocInfo::POSITION:
return "position";
case RelocInfo::STATEMENT_POSITION:
return "statement position";
case RelocInfo::EXTERNAL_REFERENCE:
return "external reference";
case RelocInfo::INTERNAL_REFERENCE:
return "internal reference";
case RelocInfo::NUMBER_OF_MODES:
UNREACHABLE();
return "number_of_modes";
}
return "unknown relocation type";
}
void RelocInfo::Print() {
PrintF("%p %s", pc_, RelocModeName(rmode_));
if (IsComment(rmode_)) {
PrintF(" (%s)", data_);
} else if (rmode_ == EMBEDDED_OBJECT) {
PrintF(" (");
target_object()->ShortPrint();
PrintF(")");
} else if (rmode_ == EXTERNAL_REFERENCE) {
ExternalReferenceEncoder ref_encoder;
PrintF(" (%s) (%p)",
ref_encoder.NameOfAddress(*target_reference_address()),
*target_reference_address());
} else if (IsCodeTarget(rmode_)) {
Code* code = Code::GetCodeFromTargetAddress(target_address());
PrintF(" (%s) (%p)", Code::Kind2String(code->kind()), target_address());
} else if (IsPosition(rmode_)) {
PrintF(" (%d)", data());
}
PrintF("\n");
}
#endif // ENABLE_DISASSEMBLER
#ifdef DEBUG
void RelocInfo::Verify() {
switch (rmode_) {
case EMBEDDED_OBJECT:
Object::VerifyPointer(target_object());
break;
case CONSTRUCT_CALL:
case CODE_TARGET_CONTEXT:
case CODE_TARGET: {
// convert inline target address to code object
Address addr = target_address();
ASSERT(addr != NULL);
// Check that we can find the right code object.
HeapObject* code = HeapObject::FromAddress(addr - Code::kHeaderSize);
Object* found = Heap::FindCodeObject(addr);
ASSERT(found->IsCode());
ASSERT(code->address() == HeapObject::cast(found)->address());
break;
}
case RelocInfo::EMBEDDED_STRING:
case RUNTIME_ENTRY:
case JS_RETURN:
case COMMENT:
case POSITION:
case STATEMENT_POSITION:
case EXTERNAL_REFERENCE:
case INTERNAL_REFERENCE:
case NONE:
break;
case NUMBER_OF_MODES:
UNREACHABLE();
break;
}
}
#endif // DEBUG
// -----------------------------------------------------------------------------
// Implementation of ExternalReference
ExternalReference::ExternalReference(Builtins::CFunctionId id)
: address_(Builtins::c_function_address(id)) {}
ExternalReference::ExternalReference(Builtins::Name name)
: address_(Builtins::builtin_address(name)) {}
ExternalReference::ExternalReference(Runtime::FunctionId id)
: address_(Runtime::FunctionForId(id)->entry) {}
ExternalReference::ExternalReference(Runtime::Function* f)
: address_(f->entry) {}
ExternalReference::ExternalReference(const IC_Utility& ic_utility)
: address_(ic_utility.address()) {}
ExternalReference::ExternalReference(const Debug_Address& debug_address)
: address_(debug_address.address()) {}
ExternalReference::ExternalReference(StatsCounter* counter)
: address_(reinterpret_cast<Address>(counter->GetInternalPointer())) {}
ExternalReference::ExternalReference(Top::AddressId id)
: address_(Top::get_address_from_id(id)) {}
ExternalReference::ExternalReference(const SCTableReference& table_ref)
: address_(table_ref.address()) {}
ExternalReference ExternalReference::builtin_passed_function() {
return ExternalReference(&Builtins::builtin_passed_function);
}
ExternalReference ExternalReference::the_hole_value_location() {
return ExternalReference(Factory::the_hole_value().location());
}
ExternalReference ExternalReference::address_of_stack_guard_limit() {
return ExternalReference(StackGuard::address_of_jslimit());
}
ExternalReference ExternalReference::address_of_regexp_stack_limit() {
return ExternalReference(RegExpStack::limit_address());
}
ExternalReference ExternalReference::debug_break() {
return ExternalReference(FUNCTION_ADDR(Debug::Break));
}
ExternalReference ExternalReference::new_space_start() {
return ExternalReference(Heap::NewSpaceStart());
}
ExternalReference ExternalReference::new_space_allocation_top_address() {
return ExternalReference(Heap::NewSpaceAllocationTopAddress());
}
ExternalReference ExternalReference::heap_always_allocate_scope_depth() {
return ExternalReference(Heap::always_allocate_scope_depth_address());
}
ExternalReference ExternalReference::new_space_allocation_limit_address() {
return ExternalReference(Heap::NewSpaceAllocationLimitAddress());
}
ExternalReference ExternalReference::debug_step_in_fp_address() {
return ExternalReference(Debug::step_in_fp_addr());
}
} } // namespace v8::internal

456
deps/v8/src/assembler.h

@ -0,0 +1,456 @@
// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
#ifndef V8_ASSEMBLER_H_
#define V8_ASSEMBLER_H_
#include "runtime.h"
#include "top.h"
#include "zone-inl.h"
namespace v8 { namespace internal {
// -----------------------------------------------------------------------------
// Labels represent pc locations; they are typically jump or call targets.
// After declaration, a label can be freely used to denote known or (yet)
// unknown pc location. Assembler::bind() is used to bind a label to the
// current pc. A label can be bound only once.
class Label BASE_EMBEDDED {
public:
INLINE(Label()) { Unuse(); }
INLINE(~Label()) { ASSERT(!is_linked()); }
INLINE(void Unuse()) { pos_ = 0; }
INLINE(bool is_bound() const) { return pos_ < 0; }
INLINE(bool is_unused() const) { return pos_ == 0; }
INLINE(bool is_linked() const) { return pos_ > 0; }
// Returns the position of bound or linked labels. Cannot be used
// for unused labels.
int pos() const;
private:
// pos_ encodes both the binding state (via its sign)
// and the binding position (via its value) of a label.
//
// pos_ < 0 bound label, pos() returns the jump target position
// pos_ == 0 unused label
// pos_ > 0 linked label, pos() returns the last reference position
int pos_;
void bind_to(int pos) {
pos_ = -pos - 1;
ASSERT(is_bound());
}
void link_to(int pos) {
pos_ = pos + 1;
ASSERT(is_linked());
}
friend class Assembler;
friend class RegexpAssembler;
friend class Displacement;
friend class ShadowTarget;
friend class RegExpMacroAssemblerIrregexp;
};
// -----------------------------------------------------------------------------
// Relocation information
// Relocation information consists of the address (pc) of the datum
// to which the relocation information applies, the relocation mode
// (rmode), and an optional data field. The relocation mode may be
// "descriptive" and not indicate a need for relocation, but simply
// describe a property of the datum. Such rmodes are useful for GC
// and nice disassembly output.
class RelocInfo BASE_EMBEDDED {
public:
// The constant kNoPosition is used with the collecting of source positions
// in the relocation information. Two types of source positions are collected
// "position" (RelocMode position) and "statement position" (RelocMode
// statement_position). The "position" is collected at places in the source
// code which are of interest when making stack traces to pin-point the source
// location of a stack frame as close as possible. The "statement position" is
// collected at the beginning at each statement, and is used to indicate
// possible break locations. kNoPosition is used to indicate an
// invalid/uninitialized position value.
static const int kNoPosition = -1;
enum Mode {
// Please note the order is important (see IsCodeTarget, IsGCRelocMode).
CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.
CODE_TARGET_CONTEXT, // code target used for contextual loads.
CODE_TARGET, // code target which is not any of the above.
EMBEDDED_OBJECT,
EMBEDDED_STRING,
// Everything after runtime_entry (inclusive) is not GC'ed.
RUNTIME_ENTRY,
JS_RETURN, // Marks start of the ExitJSFrame code.
COMMENT,
POSITION, // See comment for kNoPosition above.
STATEMENT_POSITION, // See comment for kNoPosition above.
EXTERNAL_REFERENCE, // The address of an external C++ function.
INTERNAL_REFERENCE, // An address inside the same function.
// add more as needed
// Pseudo-types
NUMBER_OF_MODES, // must be no greater than 14 - see RelocInfoWriter
NONE, // never recorded
LAST_CODE_ENUM = CODE_TARGET,
LAST_GCED_ENUM = EMBEDDED_STRING
};
RelocInfo() {}
RelocInfo(byte* pc, Mode rmode, intptr_t data)
: pc_(pc), rmode_(rmode), data_(data) {
}
static inline bool IsConstructCall(Mode mode) {
return mode == CONSTRUCT_CALL;
}
static inline bool IsCodeTarget(Mode mode) {
return mode <= LAST_CODE_ENUM;
}
// Is the relocation mode affected by GC?
static inline bool IsGCRelocMode(Mode mode) {
return mode <= LAST_GCED_ENUM;
}
static inline bool IsJSReturn(Mode mode) {
return mode == JS_RETURN;
}
static inline bool IsComment(Mode mode) {
return mode == COMMENT;
}
static inline bool IsPosition(Mode mode) {
return mode == POSITION || mode == STATEMENT_POSITION;
}
static inline bool IsStatementPosition(Mode mode) {
return mode == STATEMENT_POSITION;
}
static inline bool IsExternalReference(Mode mode) {
return mode == EXTERNAL_REFERENCE;
}
static inline bool IsInternalReference(Mode mode) {
return mode == INTERNAL_REFERENCE;
}
static inline int ModeMask(Mode mode) { return 1 << mode; }
// Accessors
byte* pc() const { return pc_; }
void set_pc(byte* pc) { pc_ = pc; }
Mode rmode() const { return rmode_; }
intptr_t data() const { return data_; }
// Apply a relocation by delta bytes
INLINE(void apply(int delta));
// Read/modify the code target in the branch/call instruction
// this relocation applies to;
// can only be called if IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY
INLINE(Address target_address());
INLINE(void set_target_address(Address target));
INLINE(Object* target_object());
INLINE(Object** target_object_address());
INLINE(void set_target_object(Object* target));
// Read the address of the word containing the target_address. Can only
// be called if IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY.
INLINE(Address target_address_address());
// Read/modify the reference in the instruction this relocation
// applies to; can only be called if rmode_ is external_reference
INLINE(Address* target_reference_address());
// Read/modify the address of a call instruction. This is used to relocate
// the break points where straight-line code is patched with a call
// instruction.
INLINE(Address call_address());
INLINE(void set_call_address(Address target));
INLINE(Object* call_object());
INLINE(Object** call_object_address());
INLINE(void set_call_object(Object* target));
// Patch the code with some other code.
void PatchCode(byte* instructions, int instruction_count);
// Patch the code with a call.
void PatchCodeWithCall(Address target, int guard_bytes);
INLINE(bool IsCallInstruction());
#ifdef ENABLE_DISASSEMBLER
// Printing
static const char* RelocModeName(Mode rmode);
void Print();
#endif // ENABLE_DISASSEMBLER
#ifdef DEBUG
// Debugging
void Verify();
#endif
static const int kCodeTargetMask = (1 << (LAST_CODE_ENUM + 1)) - 1;
static const int kPositionMask = 1 << POSITION | 1 << STATEMENT_POSITION;
static const int kDebugMask = kPositionMask | 1 << COMMENT;
static const int kApplyMask; // Modes affected by apply. Depends on arch.
private:
// On ARM, note that pc_ is the address of the constant pool entry
// to be relocated and not the address of the instruction
// referencing the constant pool entry (except when rmode_ ==
// comment).
byte* pc_;
Mode rmode_;
intptr_t data_;
friend class RelocIterator;
};
// RelocInfoWriter serializes a stream of relocation info. It writes towards
// lower addresses.
class RelocInfoWriter BASE_EMBEDDED {
public:
RelocInfoWriter() : pos_(NULL), last_pc_(NULL), last_data_(0) {}
RelocInfoWriter(byte* pos, byte* pc) : pos_(pos), last_pc_(pc),
last_data_(0) {}
byte* pos() const { return pos_; }
byte* last_pc() const { return last_pc_; }
void Write(const RelocInfo* rinfo);
// Update the state of the stream after reloc info buffer
// and/or code is moved while the stream is active.
void Reposition(byte* pos, byte* pc) {
pos_ = pos;
last_pc_ = pc;
}
// Max size (bytes) of a written RelocInfo.
static const int kMaxSize = 12;
private:
inline uint32_t WriteVariableLengthPCJump(uint32_t pc_delta);
inline void WriteTaggedPC(uint32_t pc_delta, int tag);
inline void WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag);
inline void WriteExtraTaggedData(int32_t data_delta, int top_tag);
inline void WriteTaggedData(int32_t data_delta, int tag);
inline void WriteExtraTag(int extra_tag, int top_tag);
byte* pos_;
byte* last_pc_;
intptr_t last_data_;
DISALLOW_COPY_AND_ASSIGN(RelocInfoWriter);
};
// A RelocIterator iterates over relocation information.
// Typical use:
//
// for (RelocIterator it(code); !it.done(); it.next()) {
// // do something with it.rinfo() here
// }
//
// A mask can be specified to skip unwanted modes.
class RelocIterator: public Malloced {
public:
// Create a new iterator positioned at
// the beginning of the reloc info.
// Relocation information with mode k is included in the
// iteration iff bit k of mode_mask is set.
explicit RelocIterator(Code* code, int mode_mask = -1);
explicit RelocIterator(const CodeDesc& desc, int mode_mask = -1);
// Iteration
bool done() const { return done_; }
void next();
// Return pointer valid until next next().
RelocInfo* rinfo() {
ASSERT(!done());
return &rinfo_;
}
private:
// Advance* moves the position before/after reading.
// *Read* reads from current byte(s) into rinfo_.
// *Get* just reads and returns info on current byte.
void Advance(int bytes = 1) { pos_ -= bytes; }
int AdvanceGetTag();
int GetExtraTag();
int GetTopTag();
void ReadTaggedPC();
void AdvanceReadPC();
void AdvanceReadData();
void AdvanceReadVariableLengthPCJump();
int GetPositionTypeTag();
void ReadTaggedData();
static RelocInfo::Mode DebugInfoModeFromTag(int tag);
// If the given mode is wanted, set it in rinfo_ and return true.
// Else return false. Used for efficiently skipping unwanted modes.
bool SetMode(RelocInfo::Mode mode) {
return (mode_mask_ & 1 << mode) ? (rinfo_.rmode_ = mode, true) : false;
}
byte* pos_;
byte* end_;
RelocInfo rinfo_;
bool done_;
int mode_mask_;
DISALLOW_COPY_AND_ASSIGN(RelocIterator);
};
// A stack-allocated code region logs a name for the code generated
// while the region is in effect. This information is used by the
// profiler to categorize ticks within generated code.
class CodeRegion BASE_EMBEDDED {
public:
inline CodeRegion(Assembler* assm, const char *name) : assm_(assm) {
LOG(BeginCodeRegionEvent(this, assm, name));
}
inline ~CodeRegion() {
LOG(EndCodeRegionEvent(this, assm_));
}
private:
Assembler* assm_;
};
//------------------------------------------------------------------------------
// External function
//----------------------------------------------------------------------------
class IC_Utility;
class Debug_Address;
class SCTableReference;
// An ExternalReference represents a C++ address called from the generated
// code. All references to C++ functions and must be encapsulated in an
// ExternalReference instance. This is done in order to track the origin of
// all external references in the code.
class ExternalReference BASE_EMBEDDED {
public:
explicit ExternalReference(Builtins::CFunctionId id);
explicit ExternalReference(Builtins::Name name);
explicit ExternalReference(Runtime::FunctionId id);
explicit ExternalReference(Runtime::Function* f);
explicit ExternalReference(const IC_Utility& ic_utility);
explicit ExternalReference(const Debug_Address& debug_address);
explicit ExternalReference(StatsCounter* counter);
explicit ExternalReference(Top::AddressId id);
explicit ExternalReference(const SCTableReference& table_ref);
// One-of-a-kind references. These references are not part of a general
// pattern. This means that they have to be added to the
// ExternalReferenceTable in serialize.cc manually.
static ExternalReference builtin_passed_function();
// Static variable Factory::the_hole_value.location()
static ExternalReference the_hole_value_location();
// Static variable StackGuard::address_of_jslimit()
static ExternalReference address_of_stack_guard_limit();
// Static variable RegExpStack::limit_address()
static ExternalReference address_of_regexp_stack_limit();
// Function Debug::Break()
static ExternalReference debug_break();
// Static variable Heap::NewSpaceStart()
static ExternalReference new_space_start();
static ExternalReference heap_always_allocate_scope_depth();
// Used for fast allocation in generated code.
static ExternalReference new_space_allocation_top_address();
static ExternalReference new_space_allocation_limit_address();
// Used to check if single stepping is enabled in generated code.
static ExternalReference debug_step_in_fp_address();
Address address() const {return address_;}
private:
explicit ExternalReference(void* address)
: address_(reinterpret_cast<Address>(address)) {}
Address address_;
};
// -----------------------------------------------------------------------------
// Utility functions
// Move these into inline file?
static inline bool is_intn(int x, int n) {
return -(1 << (n-1)) <= x && x < (1 << (n-1));
}
static inline bool is_int24(int x) { return is_intn(x, 24); }
static inline bool is_int8(int x) { return is_intn(x, 8); }
static inline bool is_uintn(int x, int n) {
return (x & -(1 << n)) == 0;
}
static inline bool is_uint3(int x) { return is_uintn(x, 3); }
static inline bool is_uint4(int x) { return is_uintn(x, 4); }
static inline bool is_uint5(int x) { return is_uintn(x, 5); }
static inline bool is_uint8(int x) { return is_uintn(x, 8); }
static inline bool is_uint12(int x) { return is_uintn(x, 12); }
static inline bool is_uint16(int x) { return is_uintn(x, 16); }
static inline bool is_uint24(int x) { return is_uintn(x, 24); }
} } // namespace v8::internal
#endif // V8_ASSEMBLER_H_

490
deps/v8/src/ast.cc

@ -0,0 +1,490 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "ast.h"
#include "scopes.h"
#include "string-stream.h"
namespace v8 { namespace internal {
VariableProxySentinel VariableProxySentinel::this_proxy_(true);
VariableProxySentinel VariableProxySentinel::identifier_proxy_(false);
ValidLeftHandSideSentinel ValidLeftHandSideSentinel::instance_;
Property Property::this_property_(VariableProxySentinel::this_proxy(), NULL, 0);
Call Call::sentinel_(NULL, NULL, 0);
CallEval CallEval::sentinel_(NULL, NULL, 0);
// ----------------------------------------------------------------------------
// All the Accept member functions for each syntax tree node type.
#define DECL_ACCEPT(type) \
void type::Accept(AstVisitor* v) { \
if (v->CheckStackOverflow()) return; \
v->Visit##type(this); \
}
NODE_LIST(DECL_ACCEPT)
#undef DECL_ACCEPT
// ----------------------------------------------------------------------------
// Implementation of other node functionality.
VariableProxy::VariableProxy(Handle<String> name,
bool is_this,
bool inside_with)
: name_(name),
var_(NULL),
is_this_(is_this),
inside_with_(inside_with) {
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
// at least one access, otherwise no need for a VariableProxy
var_uses_.RecordAccess(1);
}
VariableProxy::VariableProxy(bool is_this)
: is_this_(is_this) {
}
void VariableProxy::BindTo(Variable* var) {
ASSERT(var_ == NULL); // must be bound only once
ASSERT(var != NULL); // must bind
ASSERT((is_this() && var->is_this()) || name_.is_identical_to(var->name()));
// Ideally CONST-ness should match. However, this is very hard to achieve
// because we don't know the exact semantics of conflicting (const and
// non-const) multiple variable declarations, const vars introduced via
// eval() etc. Const-ness and variable declarations are a complete mess
// in JS. Sigh...
var_ = var;
var->var_uses()->RecordUses(&var_uses_);
var->obj_uses()->RecordUses(&obj_uses_);
}
#ifdef DEBUG
const char* LoopStatement::OperatorString() const {
switch (type()) {
case DO_LOOP: return "DO";
case FOR_LOOP: return "FOR";
case WHILE_LOOP: return "WHILE";
}
return NULL;
}
#endif // DEBUG
Token::Value Assignment::binary_op() const {
switch (op_) {
case Token::ASSIGN_BIT_OR: return Token::BIT_OR;
case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR;
case Token::ASSIGN_BIT_AND: return Token::BIT_AND;
case Token::ASSIGN_SHL: return Token::SHL;
case Token::ASSIGN_SAR: return Token::SAR;
case Token::ASSIGN_SHR: return Token::SHR;
case Token::ASSIGN_ADD: return Token::ADD;
case Token::ASSIGN_SUB: return Token::SUB;
case Token::ASSIGN_MUL: return Token::MUL;
case Token::ASSIGN_DIV: return Token::DIV;
case Token::ASSIGN_MOD: return Token::MOD;
default: UNREACHABLE();
}
return Token::ILLEGAL;
}
bool FunctionLiteral::AllowsLazyCompilation() {
return scope()->AllowsLazyCompilation();
}
ObjectLiteral::Property::Property(Literal* key, Expression* value) {
key_ = key;
value_ = value;
Object* k = *key->handle();
if (k->IsSymbol() && Heap::Proto_symbol()->Equals(String::cast(k))) {
kind_ = PROTOTYPE;
} else if (value_->AsMaterializedLiteral() != NULL) {
kind_ = MATERIALIZED_LITERAL;
} else if (value_->AsLiteral() != NULL) {
kind_ = CONSTANT;
} else {
kind_ = COMPUTED;
}
}
ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) {
key_ = new Literal(value->name());
value_ = value;
kind_ = is_getter ? GETTER : SETTER;
}
void TargetCollector::AddTarget(BreakTarget* target) {
// Add the label to the collector, but discard duplicates.
int length = targets_->length();
for (int i = 0; i < length; i++) {
if (targets_->at(i) == target) return;
}
targets_->Add(target);
}
// ----------------------------------------------------------------------------
// Implementation of AstVisitor
void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
Visit(statements->at(i));
}
}
void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) {
for (int i = 0; i < expressions->length(); i++) {
// The variable statement visiting code may pass NULL expressions
// to this code. Maybe this should be handled by introducing an
// undefined expression or literal? Revisit this code if this
// changes
Expression* expression = expressions->at(i);
if (expression != NULL) Visit(expression);
}
}
// ----------------------------------------------------------------------------
// Regular expressions
#define MAKE_ACCEPT(Name) \
void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \
return visitor->Visit##Name(this, data); \
}
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT)
#undef MAKE_ACCEPT
#define MAKE_TYPE_CASE(Name) \
RegExp##Name* RegExpTree::As##Name() { \
return NULL; \
} \
bool RegExpTree::Is##Name() { return false; }
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
#undef MAKE_TYPE_CASE
#define MAKE_TYPE_CASE(Name) \
RegExp##Name* RegExp##Name::As##Name() { \
return this; \
} \
bool RegExp##Name::Is##Name() { return true; }
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
#undef MAKE_TYPE_CASE
RegExpEmpty RegExpEmpty::kInstance;
static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) {
Interval result = Interval::Empty();
for (int i = 0; i < children->length(); i++)
result = result.Union(children->at(i)->CaptureRegisters());
return result;
}
Interval RegExpAlternative::CaptureRegisters() {
return ListCaptureRegisters(nodes());
}
Interval RegExpDisjunction::CaptureRegisters() {
return ListCaptureRegisters(alternatives());
}
Interval RegExpLookahead::CaptureRegisters() {
return body()->CaptureRegisters();
}
Interval RegExpCapture::CaptureRegisters() {
Interval self(StartRegister(index()), EndRegister(index()));
return self.Union(body()->CaptureRegisters());
}
Interval RegExpQuantifier::CaptureRegisters() {
return body()->CaptureRegisters();
}
bool RegExpAssertion::IsAnchored() {
return type() == RegExpAssertion::START_OF_INPUT;
}
bool RegExpAlternative::IsAnchored() {
ZoneList<RegExpTree*>* nodes = this->nodes();
for (int i = 0; i < nodes->length(); i++) {
RegExpTree* node = nodes->at(i);
if (node->IsAnchored()) { return true; }
if (node->max_match() > 0) { return false; }
}
return false;
}
bool RegExpDisjunction::IsAnchored() {
ZoneList<RegExpTree*>* alternatives = this->alternatives();
for (int i = 0; i < alternatives->length(); i++) {
if (!alternatives->at(i)->IsAnchored())
return false;
}
return true;
}
bool RegExpLookahead::IsAnchored() {
return is_positive() && body()->IsAnchored();
}
bool RegExpCapture::IsAnchored() {
return body()->IsAnchored();
}
// Convert regular expression trees to a simple sexp representation.
// This representation should be different from the input grammar
// in as many cases as possible, to make it more difficult for incorrect
// parses to look as correct ones which is likely if the input and
// output formats are alike.
class RegExpUnparser: public RegExpVisitor {
public:
RegExpUnparser();
void VisitCharacterRange(CharacterRange that);
SmartPointer<const char> ToString() { return stream_.ToCString(); }
#define MAKE_CASE(Name) virtual void* Visit##Name(RegExp##Name*, void* data);
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
#undef MAKE_CASE
private:
StringStream* stream() { return &stream_; }
HeapStringAllocator alloc_;
StringStream stream_;
};
RegExpUnparser::RegExpUnparser() : stream_(&alloc_) {
}
void* RegExpUnparser::VisitDisjunction(RegExpDisjunction* that, void* data) {
stream()->Add("(|");
for (int i = 0; i < that->alternatives()->length(); i++) {
stream()->Add(" ");
that->alternatives()->at(i)->Accept(this, data);
}
stream()->Add(")");
return NULL;
}
void* RegExpUnparser::VisitAlternative(RegExpAlternative* that, void* data) {
stream()->Add("(:");
for (int i = 0; i < that->nodes()->length(); i++) {
stream()->Add(" ");
that->nodes()->at(i)->Accept(this, data);
}
stream()->Add(")");
return NULL;
}
void RegExpUnparser::VisitCharacterRange(CharacterRange that) {
stream()->Add("%k", that.from());
if (!that.IsSingleton()) {
stream()->Add("-%k", that.to());
}
}
void* RegExpUnparser::VisitCharacterClass(RegExpCharacterClass* that,
void* data) {
if (that->is_negated())
stream()->Add("^");
stream()->Add("[");
for (int i = 0; i < that->ranges()->length(); i++) {
if (i > 0) stream()->Add(" ");
VisitCharacterRange(that->ranges()->at(i));
}
stream()->Add("]");
return NULL;
}
void* RegExpUnparser::VisitAssertion(RegExpAssertion* that, void* data) {
switch (that->type()) {
case RegExpAssertion::START_OF_INPUT:
stream()->Add("@^i");
break;
case RegExpAssertion::END_OF_INPUT:
stream()->Add("@$i");
break;
case RegExpAssertion::START_OF_LINE:
stream()->Add("@^l");
break;
case RegExpAssertion::END_OF_LINE:
stream()->Add("@$l");
break;
case RegExpAssertion::BOUNDARY:
stream()->Add("@b");
break;
case RegExpAssertion::NON_BOUNDARY:
stream()->Add("@B");
break;
}
return NULL;
}
void* RegExpUnparser::VisitAtom(RegExpAtom* that, void* data) {
stream()->Add("'");
Vector<const uc16> chardata = that->data();
for (int i = 0; i < chardata.length(); i++) {
stream()->Add("%k", chardata[i]);
}
stream()->Add("'");
return NULL;
}
void* RegExpUnparser::VisitText(RegExpText* that, void* data) {
if (that->elements()->length() == 1) {
that->elements()->at(0).data.u_atom->Accept(this, data);
} else {
stream()->Add("(!");
for (int i = 0; i < that->elements()->length(); i++) {
stream()->Add(" ");
that->elements()->at(i).data.u_atom->Accept(this, data);
}
stream()->Add(")");
}
return NULL;
}
void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) {
stream()->Add("(# %i ", that->min());
if (that->max() == RegExpTree::kInfinity) {
stream()->Add("- ");
} else {
stream()->Add("%i ", that->max());
}
stream()->Add(that->is_greedy() ? "g " : "n ");
that->body()->Accept(this, data);
stream()->Add(")");
return NULL;
}
void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) {
stream()->Add("(^ ");
that->body()->Accept(this, data);
stream()->Add(")");
return NULL;
}
void* RegExpUnparser::VisitLookahead(RegExpLookahead* that, void* data) {
stream()->Add("(-> ");
stream()->Add(that->is_positive() ? "+ " : "- ");
that->body()->Accept(this, data);
stream()->Add(")");
return NULL;
}
void* RegExpUnparser::VisitBackReference(RegExpBackReference* that,
void* data) {
stream()->Add("(<- %i)", that->index());
return NULL;
}
void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) {
stream()->Put('%');
return NULL;
}
SmartPointer<const char> RegExpTree::ToString() {
RegExpUnparser unparser;
Accept(&unparser, NULL);
return unparser.ToString();
}
RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives)
: alternatives_(alternatives) {
ASSERT(alternatives->length() > 1);
RegExpTree* first_alternative = alternatives->at(0);
min_match_ = first_alternative->min_match();
max_match_ = first_alternative->max_match();
for (int i = 1; i < alternatives->length(); i++) {
RegExpTree* alternative = alternatives->at(i);
min_match_ = Min(min_match_, alternative->min_match());
max_match_ = Max(max_match_, alternative->max_match());
}
}
RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes)
: nodes_(nodes) {
ASSERT(nodes->length() > 1);
min_match_ = 0;
max_match_ = 0;
for (int i = 0; i < nodes->length(); i++) {
RegExpTree* node = nodes->at(i);
min_match_ += node->min_match();
int node_max_match = node->max_match();
if (kInfinity - max_match_ < node_max_match) {
max_match_ = kInfinity;
} else {
max_match_ += node->max_match();
}
}
}
} } // namespace v8::internal

1710
deps/v8/src/ast.h

File diff suppressed because it is too large

1528
deps/v8/src/bootstrapper.cc

File diff suppressed because it is too large

80
deps/v8/src/bootstrapper.h

@ -0,0 +1,80 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_BOOTSTRAPPER_H_
#define V8_BOOTSTRAPPER_H_
namespace v8 { namespace internal {
// The Boostrapper is the public interface for creating a JavaScript global
// context.
class Bootstrapper : public AllStatic {
public:
// Requires: Heap::Setup has been called.
static void Initialize(bool create_heap_objects);
static void TearDown();
// Creates a JavaScript Global Context with initial object graph.
// The returned value is a global handle casted to V8Environment*.
static Handle<Context> CreateEnvironment(
Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
// Detach the environment from its outer global object.
static void DetachGlobal(Handle<Context> env);
// Traverses the pointers for memory management.
static void Iterate(ObjectVisitor* v);
// Accessors for the native scripts cache. Used in lazy loading.
static Handle<String> NativesSourceLookup(int index);
static bool NativesCacheLookup(Vector<const char> name,
Handle<JSFunction>* handle);
static void NativesCacheAdd(Vector<const char> name, Handle<JSFunction> fun);
// Append code that needs fixup at the end of boot strapping.
static void AddFixup(Code* code, MacroAssembler* masm);
// Tells whether bootstrapping is active.
static bool IsActive();
// Encoding/decoding support for fixup flags.
class FixupFlagsIsPCRelative: public BitField<bool, 0, 1> {};
class FixupFlagsUseCodeObject: public BitField<bool, 1, 1> {};
class FixupFlagsArgumentsCount: public BitField<uint32_t, 2, 32-2> {};
// Support for thread preemption.
static int ArchiveSpacePerThread();
static char* ArchiveState(char* to);
static char* RestoreState(char* from);
};
}} // namespace v8::internal
#endif // V8_BOOTSTRAPPER_H_

658
deps/v8/src/builtins-arm.cc

@ -0,0 +1,658 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "codegen-inl.h"
#include "debug.h"
#include "runtime.h"
namespace v8 { namespace internal {
#define __ masm->
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
// TODO(1238487): Don't pass the function in a static variable.
__ mov(ip, Operand(ExternalReference::builtin_passed_function()));
__ str(r1, MemOperand(ip, 0));
// The actual argument count has already been loaded into register
// r0, but JumpToBuiltin expects r0 to contain the number of
// arguments including the receiver.
__ add(r0, r0, Operand(1));
__ JumpToBuiltin(ExternalReference(id));
}
void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : number of arguments
// -- r1 : constructor function
// -- lr : return address
// -- sp[...]: constructor arguments
// -----------------------------------
// Enter a construct frame.
__ EnterConstructFrame();
// Preserve the two incoming parameters
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
__ push(r0); // smi-tagged arguments count
__ push(r1); // constructor function
// Allocate the new receiver object.
__ push(r1); // argument for Runtime_NewObject
__ CallRuntime(Runtime::kNewObject, 1);
__ push(r0); // save the receiver
// Push the function and the allocated receiver from the stack.
// sp[0]: receiver (newly allocated object)
// sp[1]: constructor function
// sp[2]: number of arguments (smi-tagged)
__ ldr(r1, MemOperand(sp, kPointerSize));
__ push(r1); // function
__ push(r0); // receiver
// Reload the number of arguments from the stack.
// r1: constructor function
// sp[0]: receiver
// sp[1]: constructor function
// sp[2]: receiver
// sp[3]: constructor function
// sp[4]: number of arguments (smi-tagged)
__ ldr(r3, MemOperand(sp, 4 * kPointerSize));
// Setup pointer to last argument.
__ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
// Setup number of arguments for function call below
__ mov(r0, Operand(r3, LSR, kSmiTagSize));
// Copy arguments and receiver to the expression stack.
// r0: number of arguments
// r2: address of last argument (caller sp)
// r1: constructor function
// r3: number of arguments (smi-tagged)
// sp[0]: receiver
// sp[1]: constructor function
// sp[2]: receiver
// sp[3]: constructor function
// sp[4]: number of arguments (smi-tagged)
Label loop, entry;
__ b(&entry);
__ bind(&loop);
__ ldr(ip, MemOperand(r2, r3, LSL, kPointerSizeLog2 - 1));
__ push(ip);
__ bind(&entry);
__ sub(r3, r3, Operand(2), SetCC);
__ b(ge, &loop);
// Call the function.
// r0: number of arguments
// r1: constructor function
ParameterCount actual(r0);
__ InvokeFunction(r1, actual, CALL_FUNCTION);
// Pop the function from the stack.
// sp[0]: constructor function
// sp[2]: receiver
// sp[3]: constructor function
// sp[4]: number of arguments (smi-tagged)
__ pop();
// Restore context from the frame.
// r0: result
// sp[0]: receiver
// sp[1]: constructor function
// sp[2]: number of arguments (smi-tagged)
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
// If the result is a smi, it is *not* an object in the ECMA sense.
// r0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: constructor function
// sp[2]: number of arguments (smi-tagged)
__ tst(r0, Operand(kSmiTagMask));
__ b(eq, &use_receiver);
// If the type of the result (stored in its map) is less than
// FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
__ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
__ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE));
__ b(ge, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);
__ ldr(r0, MemOperand(sp));
// Remove receiver from the stack, remove caller arguments, and
// return.
__ bind(&exit);
// r0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: constructor function
// sp[2]: number of arguments (smi-tagged)
__ ldr(r1, MemOperand(sp, 2 * kPointerSize));
__ LeaveConstructFrame();
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
__ add(sp, sp, Operand(kPointerSize));
__ mov(pc, Operand(lr));
}
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Called from Generate_JS_Entry
// r0: code entry
// r1: function
// r2: receiver
// r3: argc
// r4: argv
// r5-r7, cp may be clobbered
// Clear the context before we push it when entering the JS frame.
__ mov(cp, Operand(0));
// Enter an internal frame.
__ EnterInternalFrame();
// Setup the context from the function argument.
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
// Push the function and the receiver onto the stack.
__ push(r1);
__ push(r2);
// Copy arguments to the stack in a loop.
// r1: function
// r3: argc
// r4: argv, i.e. points to first arg
Label loop, entry;
__ add(r2, r4, Operand(r3, LSL, kPointerSizeLog2));
// r2 points past last arg.
__ b(&entry);
__ bind(&loop);
__ ldr(r0, MemOperand(r4, kPointerSize, PostIndex)); // read next parameter
__ ldr(r0, MemOperand(r0)); // dereference handle
__ push(r0); // push parameter
__ bind(&entry);
__ cmp(r4, Operand(r2));
__ b(ne, &loop);
// Initialize all JavaScript callee-saved registers, since they will be seen
// by the garbage collector as part of handlers.
__ mov(r4, Operand(Factory::undefined_value()));
__ mov(r5, Operand(r4));
__ mov(r6, Operand(r4));
__ mov(r7, Operand(r4));
if (kR9Available == 1)
__ mov(r9, Operand(r4));
// Invoke the code and pass argc as r0.
__ mov(r0, Operand(r3));
if (is_construct) {
__ Call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(r0);
__ InvokeFunction(r1, actual, CALL_FUNCTION);
}
// Exit the JS frame and remove the parameters (except function), and return.
// Respect ABI stack constraint.
__ LeaveInternalFrame();
__ mov(pc, lr);
// r0: result
}
void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
Generate_JSEntryTrampolineHelper(masm, false);
}
void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
Generate_JSEntryTrampolineHelper(masm, true);
}
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument.
// r0: actual number of argument
{ Label done;
__ tst(r0, Operand(r0));
__ b(ne, &done);
__ mov(r2, Operand(Factory::undefined_value()));
__ push(r2);
__ add(r0, r0, Operand(1));
__ bind(&done);
}
// 2. Get the function to call from the stack.
// r0: actual number of argument
{ Label done, non_function, function;
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, &non_function);
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
__ cmp(r2, Operand(JS_FUNCTION_TYPE));
__ b(eq, &function);
// Non-function called: Clear the function to force exception.
__ bind(&non_function);
__ mov(r1, Operand(0));
__ b(&done);
// Change the context eagerly because it will be used below to get the
// right global object.
__ bind(&function);
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
__ bind(&done);
}
// 3. Make sure first argument is an object; convert if necessary.
// r0: actual number of arguments
// r1: function
{ Label call_to_object, use_global_receiver, patch_receiver, done;
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ ldr(r2, MemOperand(r2, -kPointerSize));
// r0: actual number of arguments
// r1: function
// r2: first argument
__ tst(r2, Operand(kSmiTagMask));
__ b(eq, &call_to_object);
__ mov(r3, Operand(Factory::null_value()));
__ cmp(r2, r3);
__ b(eq, &use_global_receiver);
__ mov(r3, Operand(Factory::undefined_value()));
__ cmp(r2, r3);
__ b(eq, &use_global_receiver);
__ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
__ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
__ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE));
__ b(lt, &call_to_object);
__ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
__ b(le, &done);
__ bind(&call_to_object);
__ EnterInternalFrame();
// Store number of arguments and function across the call into the runtime.
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
__ push(r0);
__ push(r1);
__ push(r2);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ mov(r2, r0);
// Restore number of arguments and function.
__ pop(r1);
__ pop(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ LeaveInternalFrame();
__ b(&patch_receiver);
// Use the global receiver object from the called function as the receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
__ str(r2, MemOperand(r3, -kPointerSize));
__ bind(&done);
}
// 4. Shift stuff one slot down the stack
// r0: actual number of arguments (including call() receiver)
// r1: function
{ Label loop;
// Calculate the copy start address (destination). Copy end address is sp.
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ add(r2, r2, Operand(kPointerSize)); // copy receiver too
__ bind(&loop);
__ ldr(ip, MemOperand(r2, -kPointerSize));
__ str(ip, MemOperand(r2));
__ sub(r2, r2, Operand(kPointerSize));
__ cmp(r2, sp);
__ b(ne, &loop);
}
// 5. Adjust the actual number of arguments and remove the top element.
// r0: actual number of arguments (including call() receiver)
// r1: function
__ sub(r0, r0, Operand(1));
__ add(sp, sp, Operand(kPointerSize));
// 6. Get the code for the function or the non-function builtin.
// If number of expected arguments matches, then call. Otherwise restart
// the arguments adaptor stub.
// r0: actual number of arguments
// r1: function
{ Label invoke;
__ tst(r1, r1);
__ b(ne, &invoke);
__ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
__ bind(&invoke);
__ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ ldr(r2,
FieldMemOperand(r3,
SharedFunctionInfo::kFormalParameterCountOffset));
__ ldr(r3,
MemOperand(r3, SharedFunctionInfo::kCodeOffset - kHeapObjectTag));
__ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
__ cmp(r2, r0); // Check formal and actual parameter counts.
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET, ne);
// 7. Jump to the code in r3 without checking arguments.
ParameterCount expected(0);
__ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
}
}
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
const int kIndexOffset = -5 * kPointerSize;
const int kLimitOffset = -4 * kPointerSize;
const int kArgsOffset = 2 * kPointerSize;
const int kRecvOffset = 3 * kPointerSize;
const int kFunctionOffset = 4 * kPointerSize;
__ EnterInternalFrame();
__ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function
__ push(r0);
__ ldr(r0, MemOperand(fp, kArgsOffset)); // get the args array
__ push(r0);
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_JS);
// Eagerly check for stack-overflow before starting to push the arguments.
// r0: number of arguments
Label okay;
ExternalReference stack_guard_limit_address =
ExternalReference::address_of_stack_guard_limit();
__ mov(r2, Operand(stack_guard_limit_address));
__ ldr(r2, MemOperand(r2));
__ sub(r2, sp, r2);
__ sub(r2, r2, Operand(3 * kPointerSize)); // limit, index, receiver
__ cmp(r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
__ b(hi, &okay);
// Out of stack space.
__ ldr(r1, MemOperand(fp, kFunctionOffset));
__ push(r1);
__ push(r0);
__ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_JS);
// Push current limit and index.
__ bind(&okay);
__ push(r0); // limit
__ mov(r1, Operand(0)); // initial index
__ push(r1);
// Change context eagerly to get the right global object if necessary.
__ ldr(r0, MemOperand(fp, kFunctionOffset));
__ ldr(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
// Compute the receiver.
Label call_to_object, use_global_receiver, push_receiver;
__ ldr(r0, MemOperand(fp, kRecvOffset));
__ tst(r0, Operand(kSmiTagMask));
__ b(eq, &call_to_object);
__ mov(r1, Operand(Factory::null_value()));
__ cmp(r0, r1);
__ b(eq, &use_global_receiver);
__ mov(r1, Operand(Factory::undefined_value()));
__ cmp(r0, r1);
__ b(eq, &use_global_receiver);
// Check if the receiver is already a JavaScript object.
// r0: receiver
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
__ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
__ b(lt, &call_to_object);
__ cmp(r1, Operand(LAST_JS_OBJECT_TYPE));
__ b(le, &push_receiver);
// Convert the receiver to a regular object.
// r0: receiver
__ bind(&call_to_object);
__ push(r0);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ b(&push_receiver);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
const int kGlobalOffset =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ ldr(r0, FieldMemOperand(cp, kGlobalOffset));
__ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
// r0: receiver
__ bind(&push_receiver);
__ push(r0);
// Copy all arguments from the array to the stack.
Label entry, loop;
__ ldr(r0, MemOperand(fp, kIndexOffset));
__ b(&entry);
// Load the current argument from the arguments array and push it to the
// stack.
// r0: current argument index
__ bind(&loop);
__ ldr(r1, MemOperand(fp, kArgsOffset));
__ push(r1);
__ push(r0);
// Call the runtime to access the property in the arguments array.
__ CallRuntime(Runtime::kGetProperty, 2);
__ push(r0);
// Use inline caching to access the arguments.
__ ldr(r0, MemOperand(fp, kIndexOffset));
__ add(r0, r0, Operand(1 << kSmiTagSize));
__ str(r0, MemOperand(fp, kIndexOffset));
// Test if the copy loop has finished copying all the elements from the
// arguments object.
__ bind(&entry);
__ ldr(r1, MemOperand(fp, kLimitOffset));
__ cmp(r0, r1);
__ b(ne, &loop);
// Invoke the function.
ParameterCount actual(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ ldr(r1, MemOperand(fp, kFunctionOffset));
__ InvokeFunction(r1, actual, CALL_FUNCTION);
// Tear down the internal frame and remove function, receiver and args.
__ LeaveInternalFrame();
__ add(sp, sp, Operand(3 * kPointerSize));
__ mov(pc, lr);
}
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
__ mov(r4, Operand(ArgumentsAdaptorFrame::SENTINEL));
__ stm(db_w, sp, r0.bit() | r1.bit() | r4.bit() | fp.bit() | lr.bit());
__ add(fp, sp, Operand(3 * kPointerSize));
}
static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : result being passed through
// -----------------------------------
// Get the number of arguments passed (as a smi), tear down the frame and
// then tear down the parameters.
__ ldr(r1, MemOperand(fp, -3 * kPointerSize));
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
__ add(sp, sp, Operand(kPointerSize)); // adjust for receiver
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : actual number of arguments
// -- r1 : function (passed through to callee)
// -- r2 : expected number of arguments
// -- r3 : code entry to call
// -----------------------------------
Label invoke, dont_adapt_arguments;
Label enough, too_few;
__ cmp(r0, Operand(r2));
__ b(lt, &too_few);
__ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
__ b(eq, &dont_adapt_arguments);
{ // Enough parameters: actual >= expected
__ bind(&enough);
EnterArgumentsAdaptorFrame(masm);
// Calculate copy start address into r0 and copy end address into r2.
// r0: actual number of arguments as a smi
// r1: function
// r2: expected number of arguments
// r3: code entry to call
__ add(r0, fp, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
// adjust for return address and receiver
__ add(r0, r0, Operand(2 * kPointerSize));
__ sub(r2, r0, Operand(r2, LSL, kPointerSizeLog2));
// Copy the arguments (including the receiver) to the new stack frame.
// r0: copy start address
// r1: function
// r2: copy end address
// r3: code entry to call
Label copy;
__ bind(&copy);
__ ldr(ip, MemOperand(r0, 0));
__ push(ip);
__ cmp(r0, r2); // Compare before moving to next argument.
__ sub(r0, r0, Operand(kPointerSize));
__ b(ne, &copy);
__ b(&invoke);
}
{ // Too few parameters: Actual < expected
__ bind(&too_few);
EnterArgumentsAdaptorFrame(masm);
// Calculate copy start address into r0 and copy end address is fp.
// r0: actual number of arguments as a smi
// r1: function
// r2: expected number of arguments
// r3: code entry to call
__ add(r0, fp, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
// Copy the arguments (including the receiver) to the new stack frame.
// r0: copy start address
// r1: function
// r2: expected number of arguments
// r3: code entry to call
Label copy;
__ bind(&copy);
// Adjust load for return address and receiver.
__ ldr(ip, MemOperand(r0, 2 * kPointerSize));
__ push(ip);
__ cmp(r0, fp); // Compare before moving to next argument.
__ sub(r0, r0, Operand(kPointerSize));
__ b(ne, &copy);
// Fill the remaining expected arguments with undefined.
// r1: function
// r2: expected number of arguments
// r3: code entry to call
__ mov(ip, Operand(Factory::undefined_value()));
__ sub(r2, fp, Operand(r2, LSL, kPointerSizeLog2));
__ sub(r2, r2, Operand(4 * kPointerSize)); // Adjust for frame.
Label fill;
__ bind(&fill);
__ push(ip);
__ cmp(sp, r2);
__ b(ne, &fill);
}
// Call the entry point.
__ bind(&invoke);
__ Call(r3);
// Exit frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ mov(pc, lr);
// -------------------------------------------
// Dont adapt arguments.
// -------------------------------------------
__ bind(&dont_adapt_arguments);
__ mov(pc, r3);
}
#undef __
} } // namespace v8::internal

756
deps/v8/src/builtins-ia32.cc

@ -0,0 +1,756 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "codegen-inl.h"
namespace v8 { namespace internal {
#define __ masm->
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
// TODO(1238487): Don't pass the function in a static variable.
ExternalReference passed = ExternalReference::builtin_passed_function();
__ mov(Operand::StaticVariable(passed), edi);
// The actual argument count has already been loaded into register
// eax, but JumpToBuiltin expects eax to contain the number of
// arguments including the receiver.
__ inc(eax);
__ JumpToBuiltin(ExternalReference(id));
}
void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax: number of arguments
// -- edi: constructor function
// -----------------------------------
// Enter a construct frame.
__ EnterConstructFrame();
// Store a smi-tagged arguments count on the stack.
__ shl(eax, kSmiTagSize);
__ push(eax);
// Push the function to invoke on the stack.
__ push(edi);
// Try to allocate the object without transitioning into C code. If any of the
// preconditions is not met, the code bails out to the runtime call.
Label rt_call, allocated;
if (FLAG_inline_new) {
Label undo_allocation;
ExternalReference debug_step_in_fp =
ExternalReference::debug_step_in_fp_address();
__ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
__ j(not_equal, &rt_call);
// Check that function is not a Smi.
__ test(edi, Immediate(kSmiTagMask));
__ j(zero, &rt_call);
// Check that function is a JSFunction
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
__ j(not_equal, &rt_call);
// Verified that the constructor is a JSFunction.
// Load the initial map and verify that it is in fact a map.
// edi: constructor
__ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &rt_call);
// edi: constructor
// eax: initial map (if proven valid below)
__ CmpObjectType(eax, MAP_TYPE, ebx);
__ j(not_equal, &rt_call);
// Check that the constructor is not constructing a JSFunction (see comments
// in Runtime_NewObject in runtime.cc). In which case the initial map's
// instance type would be JS_FUNCTION_TYPE.
// edi: constructor
// eax: initial map
__ CmpInstanceType(eax, JS_FUNCTION_TYPE);
__ j(equal, &rt_call);
// Now allocate the JSObject on the heap.
// edi: constructor
// eax: initial map
__ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
__ shl(edi, kPointerSizeLog2);
// Make sure that the maximum heap object size will never cause us
// problem here, because it is always greater than the maximum
// instance size that can be represented in a byte.
ASSERT(Heap::MaxHeapObjectSize() >= (1 << kBitsPerByte));
ExternalReference new_space_allocation_top =
ExternalReference::new_space_allocation_top_address();
__ mov(ebx, Operand::StaticVariable(new_space_allocation_top));
__ add(edi, Operand(ebx)); // Calculate new top
ExternalReference new_space_allocation_limit =
ExternalReference::new_space_allocation_limit_address();
__ cmp(edi, Operand::StaticVariable(new_space_allocation_limit));
__ j(greater_equal, &rt_call);
// Allocated the JSObject, now initialize the fields.
// eax: initial map
// ebx: JSObject
// edi: start of next object
__ mov(Operand(ebx, JSObject::kMapOffset), eax);
__ mov(ecx, Factory::empty_fixed_array());
__ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
__ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
// Set extra fields in the newly allocated object.
// eax: initial map
// ebx: JSObject
// edi: start of next object
{ Label loop, entry;
__ mov(edx, Factory::undefined_value());
__ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
__ jmp(&entry);
__ bind(&loop);
__ mov(Operand(ecx, 0), edx);
__ add(Operand(ecx), Immediate(kPointerSize));
__ bind(&entry);
__ cmp(ecx, Operand(edi));
__ j(less, &loop);
}
// Mostly done with the JSObject. Add the heap tag and store the new top, so
// that we can continue and jump into the continuation code at any time from
// now on. Any failures need to undo the setting of the new top, so that the
// heap is in a consistent state and verifiable.
// eax: initial map
// ebx: JSObject
// edi: start of next object
__ or_(Operand(ebx), Immediate(kHeapObjectTag));
__ mov(Operand::StaticVariable(new_space_allocation_top), edi);
// Check if a properties array should be setup and allocate one if needed.
// Otherwise initialize the properties to the empty_fixed_array as well.
// eax: initial map
// ebx: JSObject
// edi: start of next object
__ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
__ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
// Calculate unused properties past the end of the in-object properties.
__ sub(edx, Operand(ecx));
__ test(edx, Operand(edx));
// Done if no extra properties are to be allocated.
__ j(zero, &allocated);
// Scale the number of elements by pointer size and add the header for
// FixedArrays to the start of the next object calculation from above.
// eax: initial map
// ebx: JSObject
// edi: start of next object (will be start of FixedArray)
// edx: number of elements in properties array
ASSERT(Heap::MaxHeapObjectSize() >
(FixedArray::kHeaderSize + 255*kPointerSize));
__ lea(ecx, Operand(edi, edx, times_4, FixedArray::kHeaderSize));
__ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit));
__ j(greater_equal, &undo_allocation);
__ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
// Initialize the FixedArray.
// ebx: JSObject
// edi: FixedArray
// edx: number of elements
// ecx: start of next object
__ mov(eax, Factory::fixed_array_map());
__ mov(Operand(edi, JSObject::kMapOffset), eax); // setup the map
__ mov(Operand(edi, Array::kLengthOffset), edx); // and length
// Initialize the fields to undefined.
// ebx: JSObject
// edi: FixedArray
// ecx: start of next object
{ Label loop, entry;
__ mov(edx, Factory::undefined_value());
__ lea(eax, Operand(edi, FixedArray::kHeaderSize));
__ jmp(&entry);
__ bind(&loop);
__ mov(Operand(eax, 0), edx);
__ add(Operand(eax), Immediate(kPointerSize));
__ bind(&entry);
__ cmp(eax, Operand(ecx));
__ j(less, &loop);
}
// Store the initialized FixedArray into the properties field of
// the JSObject
// ebx: JSObject
// edi: FixedArray
__ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag
__ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi);
// Continue with JSObject being successfully allocated
// ebx: JSObject
__ jmp(&allocated);
// Undo the setting of the new top so that the heap is verifiable. For
// example, the map's unused properties potentially do not match the
// allocated objects unused properties.
// ebx: JSObject (previous new top)
__ bind(&undo_allocation);
__ xor_(Operand(ebx), Immediate(kHeapObjectTag)); // clear the heap tag
__ mov(Operand::StaticVariable(new_space_allocation_top), ebx);
}
// Allocate the new receiver object using the runtime call.
// edi: function (constructor)
__ bind(&rt_call);
// Must restore edi (constructor) before calling runtime.
__ mov(edi, Operand(esp, 0));
__ push(edi);
__ CallRuntime(Runtime::kNewObject, 1);
__ mov(ebx, Operand(eax)); // store result in ebx
// New object allocated.
// ebx: newly allocated object
__ bind(&allocated);
// Retrieve the function from the stack.
__ pop(edi);
// Retrieve smi-tagged arguments count from the stack.
__ mov(eax, Operand(esp, 0));
__ shr(eax, kSmiTagSize);
// Push the allocated receiver to the stack. We need two copies
// because we may have to return the original one and the calling
// conventions dictate that the called function pops the receiver.
__ push(ebx);
__ push(ebx);
// Setup pointer to last argument.
__ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
// Copy arguments and receiver to the expression stack.
Label loop, entry;
__ mov(ecx, Operand(eax));
__ jmp(&entry);
__ bind(&loop);
__ push(Operand(ebx, ecx, times_4, 0));
__ bind(&entry);
__ dec(ecx);
__ j(greater_equal, &loop);
// Call the function.
ParameterCount actual(eax);
__ InvokeFunction(edi, actual, CALL_FUNCTION);
// Restore context from the frame.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
// If the result is a smi, it is *not* an object in the ECMA sense.
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &use_receiver, not_taken);
// If the type of the result (stored in its map) is less than
// FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
__ j(greater_equal, &exit, not_taken);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);
__ mov(eax, Operand(esp, 0));
// Restore the arguments count and leave the construct frame.
__ bind(&exit);
__ mov(ebx, Operand(esp, kPointerSize)); // get arguments count
__ LeaveConstructFrame();
// Remove caller arguments from the stack and return.
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ pop(ecx);
__ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
__ push(ecx);
__ ret(0);
}
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Clear the context before we push it when entering the JS frame.
__ xor_(esi, Operand(esi)); // clear esi
// Enter an internal frame.
__ EnterInternalFrame();
// Load the previous frame pointer (ebx) to access C arguments
__ mov(ebx, Operand(ebp, 0));
// Get the function from the frame and setup the context.
__ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
__ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
// Push the function and the receiver onto the stack.
__ push(ecx);
__ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
// Load the number of arguments and setup pointer to the arguments.
__ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
__ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
// Copy arguments to the stack in a loop.
Label loop, entry;
__ xor_(ecx, Operand(ecx)); // clear ecx
__ jmp(&entry);
__ bind(&loop);
__ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
__ push(Operand(edx, 0)); // dereference handle
__ inc(Operand(ecx));
__ bind(&entry);
__ cmp(ecx, Operand(eax));
__ j(not_equal, &loop);
// Get the function from the stack and call it.
__ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver
// Invoke the code.
if (is_construct) {
__ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(eax);
__ InvokeFunction(edi, actual, CALL_FUNCTION);
}
// Exit the JS frame. Notice that this also removes the empty
// context and the function left on the stack by the code
// invocation.
__ LeaveInternalFrame();
__ ret(1 * kPointerSize); // remove receiver
}
void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
Generate_JSEntryTrampolineHelper(masm, false);
}
void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
Generate_JSEntryTrampolineHelper(masm, true);
}
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument.
{ Label done;
__ test(eax, Operand(eax));
__ j(not_zero, &done, taken);
__ pop(ebx);
__ push(Immediate(Factory::undefined_value()));
__ push(ebx);
__ inc(eax);
__ bind(&done);
}
// 2. Get the function to call from the stack.
{ Label done, non_function, function;
// +1 ~ return address.
__ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize));
__ test(edi, Immediate(kSmiTagMask));
__ j(zero, &non_function, not_taken);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, &function, taken);
// Non-function called: Clear the function to force exception.
__ bind(&non_function);
__ xor_(edi, Operand(edi));
__ jmp(&done);
// Function called: Change context eagerly to get the right global object.
__ bind(&function);
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
__ bind(&done);
}
// 3. Make sure first argument is an object; convert if necessary.
{ Label call_to_object, use_global_receiver, patch_receiver, done;
__ mov(ebx, Operand(esp, eax, times_4, 0));
__ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object);
__ cmp(ebx, Factory::null_value());
__ j(equal, &use_global_receiver);
__ cmp(ebx, Factory::undefined_value());
__ j(equal, &use_global_receiver);
__ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
__ j(less, &call_to_object);
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
__ j(less_equal, &done);
__ bind(&call_to_object);
__ EnterInternalFrame(); // preserves eax, ebx, edi
// Store the arguments count on the stack (smi tagged).
ASSERT(kSmiTag == 0);
__ shl(eax, kSmiTagSize);
__ push(eax);
__ push(edi); // save edi across the call
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, eax);
__ pop(edi); // restore edi after the call
// Get the arguments count and untag it.
__ pop(eax);
__ shr(eax, kSmiTagSize);
__ LeaveInternalFrame();
__ jmp(&patch_receiver);
// Use the global receiver object from the called function as the receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ mov(ebx, FieldOperand(esi, kGlobalIndex));
__ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx);
__ bind(&done);
}
// 4. Shift stuff one slot down the stack.
{ Label loop;
__ lea(ecx, Operand(eax, +1)); // +1 ~ copy receiver too
__ bind(&loop);
__ mov(ebx, Operand(esp, ecx, times_4, 0));
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
__ dec(ecx);
__ j(not_zero, &loop);
}
// 5. Remove TOS (copy of last arguments), but keep return address.
__ pop(ebx);
__ pop(ecx);
__ push(ebx);
__ dec(eax);
// 6. Check that function really was a function and get the code to
// call from the function and check that the number of expected
// arguments matches what we're providing.
{ Label invoke;
__ test(edi, Operand(edi));
__ j(not_zero, &invoke, taken);
__ xor_(ebx, Operand(ebx));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
__ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
__ bind(&invoke);
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(ebx,
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
__ lea(edx, FieldOperand(edx, Code::kHeaderSize));
__ cmp(eax, Operand(ebx));
__ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
}
// 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0);
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
}
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ EnterInternalFrame();
__ push(Operand(ebp, 4 * kPointerSize)); // push this
__ push(Operand(ebp, 2 * kPointerSize)); // push arguments
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
if (FLAG_check_stack) {
// We need to catch preemptions right here, otherwise an unlucky preemption
// could show up as a failed apply.
ExternalReference stack_guard_limit =
ExternalReference::address_of_stack_guard_limit();
Label retry_preemption;
Label no_preemption;
__ bind(&retry_preemption);
__ mov(edi, Operand::StaticVariable(stack_guard_limit));
__ cmp(esp, Operand(edi));
__ j(above, &no_preemption, taken);
// Preemption!
// Because builtins always remove the receiver from the stack, we
// have to fake one to avoid underflowing the stack.
__ push(eax);
__ push(Immediate(Smi::FromInt(0)));
// Do call to runtime routine.
__ CallRuntime(Runtime::kStackGuard, 1);
__ pop(eax);
__ jmp(&retry_preemption);
__ bind(&no_preemption);
Label okay;
// Make ecx the space we have left.
__ mov(ecx, Operand(esp));
__ sub(ecx, Operand(edi));
// Make edx the space we need for the array when it is unrolled onto the
// stack.
__ mov(edx, Operand(eax));
__ shl(edx, kPointerSizeLog2 - kSmiTagSize);
__ cmp(ecx, Operand(edx));
__ j(greater, &okay, taken);
// Too bad: Out of stack space.
__ push(Operand(ebp, 4 * kPointerSize)); // push this
__ push(eax);
__ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
__ bind(&okay);
}
// Push current index and limit.
const int kLimitOffset =
StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
__ push(eax); // limit
__ push(Immediate(0)); // index
// Change context eagerly to get the right global object if
// necessary.
__ mov(edi, Operand(ebp, 4 * kPointerSize));
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
// Compute the receiver.
Label call_to_object, use_global_receiver, push_receiver;
__ mov(ebx, Operand(ebp, 3 * kPointerSize));
__ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object);
__ cmp(ebx, Factory::null_value());
__ j(equal, &use_global_receiver);
__ cmp(ebx, Factory::undefined_value());
__ j(equal, &use_global_receiver);
// If given receiver is already a JavaScript object then there's no
// reason for converting it.
__ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
__ j(less, &call_to_object);
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
__ j(less_equal, &push_receiver);
// Convert the receiver to an object.
__ bind(&call_to_object);
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, Operand(eax));
__ jmp(&push_receiver);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
const int kGlobalOffset =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ mov(ebx, FieldOperand(esi, kGlobalOffset));
__ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
__ bind(&push_receiver);
__ push(ebx);
// Copy all arguments from the array to the stack.
Label entry, loop;
__ mov(eax, Operand(ebp, kIndexOffset));
__ jmp(&entry);
__ bind(&loop);
__ mov(ecx, Operand(ebp, 2 * kPointerSize)); // load arguments
__ push(ecx);
__ push(eax);
// Use inline caching to speed up access to arguments.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// It is important that we do not have a test instruction after the
// call. A test instruction after the call is used to indicate that
// we have generated an inline version of the keyed load. In this
// case, we know that we are not generating a test instruction next.
// Remove IC arguments from the stack and push the nth argument.
__ add(Operand(esp), Immediate(2 * kPointerSize));
__ push(eax);
// Update the index on the stack and in register eax.
__ mov(eax, Operand(ebp, kIndexOffset));
__ add(Operand(eax), Immediate(1 << kSmiTagSize));
__ mov(Operand(ebp, kIndexOffset), eax);
__ bind(&entry);
__ cmp(eax, Operand(ebp, kLimitOffset));
__ j(not_equal, &loop);
// Invoke the function.
ParameterCount actual(eax);
__ shr(eax, kSmiTagSize);
__ mov(edi, Operand(ebp, 4 * kPointerSize));
__ InvokeFunction(edi, actual, CALL_FUNCTION);
__ LeaveInternalFrame();
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
}
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
__ push(ebp);
__ mov(ebp, Operand(esp));
// Store the arguments adaptor context sentinel.
__ push(Immediate(ArgumentsAdaptorFrame::SENTINEL));
// Push the function on the stack.
__ push(edi);
// Preserve the number of arguments on the stack. Must preserve both
// eax and ebx because these registers are used when copying the
// arguments and the receiver.
ASSERT(kSmiTagSize == 1);
__ lea(ecx, Operand(eax, eax, times_1, kSmiTag));
__ push(ecx);
}
static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
// Retrieve the number of arguments from the stack.
__ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
// Leave the frame.
__ leave();
// Remove caller arguments from the stack.
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ pop(ecx);
__ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
__ push(ecx);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : actual number of arguments
// -- ebx : expected number of arguments
// -- edx : code entry to call
// -----------------------------------
Label invoke, dont_adapt_arguments;
__ IncrementCounter(&Counters::arguments_adaptors, 1);
Label enough, too_few;
__ cmp(eax, Operand(ebx));
__ j(less, &too_few);
__ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
__ j(equal, &dont_adapt_arguments);
{ // Enough parameters: Actual >= expected.
__ bind(&enough);
EnterArgumentsAdaptorFrame(masm);
// Copy receiver and all expected arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(eax, Operand(ebp, eax, times_4, offset));
__ mov(ecx, -1); // account for receiver
Label copy;
__ bind(&copy);
__ inc(ecx);
__ push(Operand(eax, 0));
__ sub(Operand(eax), Immediate(kPointerSize));
__ cmp(ecx, Operand(ebx));
__ j(less, &copy);
__ jmp(&invoke);
}
{ // Too few parameters: Actual < expected.
__ bind(&too_few);
EnterArgumentsAdaptorFrame(masm);
// Copy receiver and all actual arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(edi, Operand(ebp, eax, times_4, offset));
__ mov(ecx, -1); // account for receiver
Label copy;
__ bind(&copy);
__ inc(ecx);
__ push(Operand(edi, 0));
__ sub(Operand(edi), Immediate(kPointerSize));
__ cmp(ecx, Operand(eax));
__ j(less, &copy);
// Fill remaining expected arguments with undefined values.
Label fill;
__ bind(&fill);
__ inc(ecx);
__ push(Immediate(Factory::undefined_value()));
__ cmp(ecx, Operand(ebx));
__ j(less, &fill);
// Restore function pointer.
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
}
// Call the entry point.
__ bind(&invoke);
__ call(Operand(edx));
// Leave frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ ret(0);
// -------------------------------------------
// Dont adapt arguments.
// -------------------------------------------
__ bind(&dont_adapt_arguments);
__ jmp(Operand(edx));
}
#undef __
} } // namespace v8::internal

744
deps/v8/src/builtins.cc

@ -0,0 +1,744 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "api.h"
#include "bootstrapper.h"
#include "builtins.h"
#include "ic-inl.h"
namespace v8 { namespace internal {
// ----------------------------------------------------------------------------
// Support macros for defining builtins in C.
// ----------------------------------------------------------------------------
//
// A builtin function is defined by writing:
//
// BUILTIN(name) {
// ...
// }
// BUILTIN_END
//
// In the body of the builtin function, the variable 'receiver' is visible.
// The arguments can be accessed through:
//
// BUILTIN_ARG(0): Receiver (also available as 'receiver')
// BUILTIN_ARG(1): First argument
// ...
// BUILTIN_ARG(n): Last argument
//
// and they evaluate to undefined values if too few arguments were
// passed to the builtin function invocation.
//
// __argc__ is the number of arguments including the receiver.
// ----------------------------------------------------------------------------
// TODO(1238487): We should consider passing whether or not the
// builtin was invoked as a constructor as part of the
// arguments. Maybe we also want to pass the called function?
#define BUILTIN(name) \
static Object* Builtin_##name(int __argc__, Object** __argv__) { \
Handle<Object> receiver(&__argv__[0]);
// Use an inline function to avoid evaluating the index (n) more than
// once in the BUILTIN_ARG macro.
static inline Object* __builtin_arg__(int n, int argc, Object** argv) {
ASSERT(n >= 0);
return (argc > n) ? argv[-n] : Heap::undefined_value();
}
// NOTE: Argument 0 is the receiver. The first 'real' argument is
// argument 1 - BUILTIN_ARG(1).
#define BUILTIN_ARG(n) (__builtin_arg__(n, __argc__, __argv__))
#define BUILTIN_END \
return Heap::undefined_value(); \
}
// TODO(1238487): Get rid of this function that determines if the
// builtin is called as a constructor. This may be a somewhat slow
// operation due to the stack frame iteration.
static inline bool CalledAsConstructor() {
StackFrameIterator it;
ASSERT(it.frame()->is_exit());
it.Advance();
StackFrame* frame = it.frame();
return frame->is_construct();
}
// ----------------------------------------------------------------------------
Handle<Code> Builtins::GetCode(JavaScript id, bool* resolved) {
Code* code = Builtins::builtin(Builtins::Illegal);
*resolved = false;
if (Top::context() != NULL) {
Object* object = Top::builtins()->javascript_builtin(id);
if (object->IsJSFunction()) {
Handle<JSFunction> function(JSFunction::cast(object));
// Make sure the number of parameters match the formal parameter count.
ASSERT(function->shared()->formal_parameter_count() ==
Builtins::GetArgumentsCount(id));
if (function->is_compiled() || CompileLazy(function, CLEAR_EXCEPTION)) {
code = function->code();
*resolved = true;
}
}
}
return Handle<Code>(code);
}
BUILTIN(Illegal) {
UNREACHABLE();
}
BUILTIN_END
BUILTIN(EmptyFunction) {
}
BUILTIN_END
BUILTIN(ArrayCode) {
JSArray* array;
if (CalledAsConstructor()) {
array = JSArray::cast(*receiver);
} else {
// Allocate the JS Array
JSFunction* constructor =
Top::context()->global_context()->array_function();
Object* obj = Heap::AllocateJSObject(constructor);
if (obj->IsFailure()) return obj;
array = JSArray::cast(obj);
}
// 'array' now contains the JSArray we should initialize.
// Optimize the case where there is one argument and the argument is a
// small smi.
if (__argc__ == 2) {
Object* obj = BUILTIN_ARG(1);
if (obj->IsSmi()) {
int len = Smi::cast(obj)->value();
if (len >= 0 && len < JSObject::kInitialMaxFastElementArray) {
Object* obj = Heap::AllocateFixedArrayWithHoles(len);
if (obj->IsFailure()) return obj;
array->SetContent(FixedArray::cast(obj));
return array;
}
}
// Take the argument as the length.
obj = array->Initialize(0);
if (obj->IsFailure()) return obj;
if (__argc__ == 2) return array->SetElementsLength(BUILTIN_ARG(1));
}
// Optimize the case where there are no parameters passed.
if (__argc__ == 1) return array->Initialize(4);
// Take the arguments as elements.
int number_of_elements = __argc__ - 1;
Smi* len = Smi::FromInt(number_of_elements);
Object* obj = Heap::AllocateFixedArrayWithHoles(len->value());
if (obj->IsFailure()) return obj;
FixedArray* elms = FixedArray::cast(obj);
WriteBarrierMode mode = elms->GetWriteBarrierMode();
// Fill in the content
for (int index = 0; index < number_of_elements; index++) {
elms->set(index, BUILTIN_ARG(index+1), mode);
}
// Set length and elements on the array.
array->set_elements(FixedArray::cast(obj));
array->set_length(len, SKIP_WRITE_BARRIER);
return array;
}
BUILTIN_END
BUILTIN(ArrayPush) {
JSArray* array = JSArray::cast(*receiver);
ASSERT(array->HasFastElements());
// Make sure we have space for the elements.
int len = Smi::cast(array->length())->value();
// Set new length.
int new_length = len + __argc__ - 1;
FixedArray* elms = FixedArray::cast(array->elements());
if (new_length <= elms->length()) {
// Backing storage has extra space for the provided values.
for (int index = 0; index < __argc__ - 1; index++) {
elms->set(index + len, BUILTIN_ARG(index+1));
}
} else {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
if (obj->IsFailure()) return obj;
FixedArray* new_elms = FixedArray::cast(obj);
WriteBarrierMode mode = new_elms->GetWriteBarrierMode();
// Fill out the new array with old elements.
for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
// Add the provided values.
for (int index = 0; index < __argc__ - 1; index++) {
new_elms->set(index + len, BUILTIN_ARG(index+1), mode);
}
// Set the new backing storage.
array->set_elements(new_elms);
}
// Set the length.
array->set_length(Smi::FromInt(new_length), SKIP_WRITE_BARRIER);
return array->length();
}
BUILTIN_END
BUILTIN(ArrayPop) {
JSArray* array = JSArray::cast(*receiver);
ASSERT(array->HasFastElements());
Object* undefined = Heap::undefined_value();
int len = Smi::cast(array->length())->value();
if (len == 0) return undefined;
// Get top element
FixedArray* elms = FixedArray::cast(array->elements());
Object* top = elms->get(len - 1);
// Set the length.
array->set_length(Smi::FromInt(len - 1), SKIP_WRITE_BARRIER);
if (!top->IsTheHole()) {
// Delete the top element.
elms->set_the_hole(len - 1);
return top;
}
// Remember to check the prototype chain.
JSFunction* array_function =
Top::context()->global_context()->array_function();
JSObject* prototype = JSObject::cast(array_function->prototype());
top = prototype->GetElement(len - 1);
return top;
}
BUILTIN_END
// -----------------------------------------------------------------------------
//
// Returns the holder JSObject if the function can legally be called
// with this receiver. Returns Heap::null_value() if the call is
// illegal. Any arguments that don't fit the expected type is
// overwritten with undefined. Arguments that do fit the expected
// type is overwritten with the object in the prototype chain that
// actually has that type.
static inline Object* TypeCheck(int argc,
Object** argv,
FunctionTemplateInfo* info) {
Object* recv = argv[0];
Object* sig_obj = info->signature();
if (sig_obj->IsUndefined()) return recv;
SignatureInfo* sig = SignatureInfo::cast(sig_obj);
// If necessary, check the receiver
Object* recv_type = sig->receiver();
Object* holder = recv;
if (!recv_type->IsUndefined()) {
for (; holder != Heap::null_value(); holder = holder->GetPrototype()) {
if (holder->IsInstanceOf(FunctionTemplateInfo::cast(recv_type))) {
break;
}
}
if (holder == Heap::null_value()) return holder;
}
Object* args_obj = sig->args();
// If there is no argument signature we're done
if (args_obj->IsUndefined()) return holder;
FixedArray* args = FixedArray::cast(args_obj);
int length = args->length();
if (argc <= length) length = argc - 1;
for (int i = 0; i < length; i++) {
Object* argtype = args->get(i);
if (argtype->IsUndefined()) continue;
Object** arg = &argv[-1 - i];
Object* current = *arg;
for (; current != Heap::null_value(); current = current->GetPrototype()) {
if (current->IsInstanceOf(FunctionTemplateInfo::cast(argtype))) {
*arg = current;
break;
}
}
if (current == Heap::null_value()) *arg = Heap::undefined_value();
}
return holder;
}
BUILTIN(HandleApiCall) {
HandleScope scope;
bool is_construct = CalledAsConstructor();
// TODO(1238487): This is not nice. We need to get rid of this
// kludgy behavior and start handling API calls in a more direct
// way - maybe compile specialized stubs lazily?.
Handle<JSFunction> function =
Handle<JSFunction>(JSFunction::cast(Builtins::builtin_passed_function));
if (is_construct) {
Handle<FunctionTemplateInfo> desc =
Handle<FunctionTemplateInfo>(
FunctionTemplateInfo::cast(function->shared()->function_data()));
bool pending_exception = false;
Factory::ConfigureInstance(desc, Handle<JSObject>::cast(receiver),
&pending_exception);
ASSERT(Top::has_pending_exception() == pending_exception);
if (pending_exception) return Failure::Exception();
}
FunctionTemplateInfo* fun_data =
FunctionTemplateInfo::cast(function->shared()->function_data());
Object* raw_holder = TypeCheck(__argc__, __argv__, fun_data);
if (raw_holder->IsNull()) {
// This function cannot be called with the given receiver. Abort!
Handle<Object> obj =
Factory::NewTypeError("illegal_invocation", HandleVector(&function, 1));
return Top::Throw(*obj);
}
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined()) {
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
Object* callback_obj = call_data->callback();
v8::InvocationCallback callback =
v8::ToCData<v8::InvocationCallback>(callback_obj);
Object* data_obj = call_data->data();
Object* result;
v8::Local<v8::Object> self =
v8::Utils::ToLocal(Handle<JSObject>::cast(receiver));
Handle<Object> data_handle(data_obj);
v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
ASSERT(raw_holder->IsJSObject());
v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
Handle<JSObject> holder_handle(JSObject::cast(raw_holder));
v8::Local<v8::Object> holder = v8::Utils::ToLocal(holder_handle);
LOG(ApiObjectAccess("call", JSObject::cast(*receiver)));
v8::Arguments args = v8::ImplementationUtilities::NewArguments(
data,
holder,
callee,
is_construct,
reinterpret_cast<void**>(__argv__ - 1),
__argc__ - 1);
v8::Handle<v8::Value> value;
{
// Leaving JavaScript.
VMState state(EXTERNAL);
value = callback(args);
}
if (value.IsEmpty()) {
result = Heap::undefined_value();
} else {
result = *reinterpret_cast<Object**>(*value);
}
RETURN_IF_SCHEDULED_EXCEPTION();
if (!is_construct || result->IsJSObject()) return result;
}
return *receiver;
}
BUILTIN_END
// Handle calls to non-function objects created through the API that
// support calls.
BUILTIN(HandleApiCallAsFunction) {
// Non-functions are never called as constructors.
ASSERT(!CalledAsConstructor());
// Get the object called.
JSObject* obj = JSObject::cast(*receiver);
// Get the invocation callback from the function descriptor that was
// used to create the called object.
ASSERT(obj->map()->has_instance_call_handler());
JSFunction* constructor = JSFunction::cast(obj->map()->constructor());
Object* template_info = constructor->shared()->function_data();
Object* handler =
FunctionTemplateInfo::cast(template_info)->instance_call_handler();
ASSERT(!handler->IsUndefined());
CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
Object* callback_obj = call_data->callback();
v8::InvocationCallback callback =
v8::ToCData<v8::InvocationCallback>(callback_obj);
// Get the data for the call and perform the callback.
Object* data_obj = call_data->data();
Object* result;
{ HandleScope scope;
v8::Local<v8::Object> self =
v8::Utils::ToLocal(Handle<JSObject>::cast(receiver));
Handle<Object> data_handle(data_obj);
v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
Handle<JSFunction> callee_handle(constructor);
v8::Local<v8::Function> callee = v8::Utils::ToLocal(callee_handle);
LOG(ApiObjectAccess("call non-function", JSObject::cast(*receiver)));
v8::Arguments args = v8::ImplementationUtilities::NewArguments(
data,
self,
callee,
false,
reinterpret_cast<void**>(__argv__ - 1),
__argc__ - 1);
v8::Handle<v8::Value> value;
{
// Leaving JavaScript.
VMState state(EXTERNAL);
value = callback(args);
}
if (value.IsEmpty()) {
result = Heap::undefined_value();
} else {
result = *reinterpret_cast<Object**>(*value);
}
}
// Check for exceptions and return result.
RETURN_IF_SCHEDULED_EXCEPTION();
return result;
}
BUILTIN_END
// TODO(1238487): This is a nasty hack. We need to improve the way we
// call builtins considerable to get rid of this and the hairy macros
// in builtins.cc.
Object* Builtins::builtin_passed_function;
static void Generate_LoadIC_ArrayLength(MacroAssembler* masm) {
LoadIC::GenerateArrayLength(masm);
}
static void Generate_LoadIC_StringLength(MacroAssembler* masm) {
LoadIC::GenerateStringLength(masm);
}
static void Generate_LoadIC_FunctionPrototype(MacroAssembler* masm) {
LoadIC::GenerateFunctionPrototype(masm);
}
static void Generate_LoadIC_Initialize(MacroAssembler* masm) {
LoadIC::GenerateInitialize(masm);
}
static void Generate_LoadIC_PreMonomorphic(MacroAssembler* masm) {
LoadIC::GeneratePreMonomorphic(masm);
}
static void Generate_LoadIC_Miss(MacroAssembler* masm) {
LoadIC::GenerateMiss(masm);
}
static void Generate_LoadIC_Megamorphic(MacroAssembler* masm) {
LoadIC::GenerateMegamorphic(masm);
}
static void Generate_LoadIC_Normal(MacroAssembler* masm) {
LoadIC::GenerateNormal(masm);
}
static void Generate_KeyedLoadIC_Initialize(MacroAssembler* masm) {
KeyedLoadIC::GenerateInitialize(masm);
}
static void Generate_KeyedLoadIC_Miss(MacroAssembler* masm) {
KeyedLoadIC::GenerateMiss(masm);
}
static void Generate_KeyedLoadIC_Generic(MacroAssembler* masm) {
KeyedLoadIC::GenerateGeneric(masm);
}
static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
KeyedLoadIC::GeneratePreMonomorphic(masm);
}
static void Generate_StoreIC_Initialize(MacroAssembler* masm) {
StoreIC::GenerateInitialize(masm);
}
static void Generate_StoreIC_Miss(MacroAssembler* masm) {
StoreIC::GenerateMiss(masm);
}
static void Generate_StoreIC_ExtendStorage(MacroAssembler* masm) {
StoreIC::GenerateExtendStorage(masm);
}
static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
StoreIC::GenerateMegamorphic(masm);
}
static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
KeyedStoreIC::GenerateGeneric(masm);
}
static void Generate_KeyedStoreIC_ExtendStorage(MacroAssembler* masm) {
KeyedStoreIC::GenerateExtendStorage(masm);
}
static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
KeyedStoreIC::GenerateMiss(masm);
}
static void Generate_KeyedStoreIC_Initialize(MacroAssembler* masm) {
KeyedStoreIC::GenerateInitialize(masm);
}
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateLoadICDebugBreak(masm);
}
static void Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateStoreICDebugBreak(masm);
}
static void Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateKeyedLoadICDebugBreak(masm);
}
static void Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateKeyedStoreICDebugBreak(masm);
}
static void Generate_ConstructCall_DebugBreak(MacroAssembler* masm) {
Debug::GenerateConstructCallDebugBreak(masm);
}
static void Generate_Return_DebugBreak(MacroAssembler* masm) {
Debug::GenerateReturnDebugBreak(masm);
}
static void Generate_Return_DebugBreakEntry(MacroAssembler* masm) {
Debug::GenerateReturnDebugBreakEntry(masm);
}
static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
Debug::GenerateStubNoRegistersDebugBreak(masm);
}
Object* Builtins::builtins_[builtin_count] = { NULL, };
const char* Builtins::names_[builtin_count] = { NULL, };
#define DEF_ENUM_C(name) FUNCTION_ADDR(Builtin_##name),
Address Builtins::c_functions_[cfunction_count] = {
BUILTIN_LIST_C(DEF_ENUM_C)
};
#undef DEF_ENUM_C
#define DEF_JS_NAME(name, ignore) #name,
#define DEF_JS_ARGC(ignore, argc) argc,
const char* Builtins::javascript_names_[id_count] = {
BUILTINS_LIST_JS(DEF_JS_NAME)
};
int Builtins::javascript_argc_[id_count] = {
BUILTINS_LIST_JS(DEF_JS_ARGC)
};
#undef DEF_JS_NAME
#undef DEF_JS_ARGC
static bool is_initialized = false;
void Builtins::Setup(bool create_heap_objects) {
ASSERT(!is_initialized);
// Create a scope for the handles in the builtins.
HandleScope scope;
struct BuiltinDesc {
byte* generator;
byte* c_code;
const char* s_name; // name is only used for generating log information.
int name;
Code::Flags flags;
};
#define DEF_FUNCTION_PTR_C(name) \
{ FUNCTION_ADDR(Generate_Adaptor), \
FUNCTION_ADDR(Builtin_##name), \
#name, \
c_##name, \
Code::ComputeFlags(Code::BUILTIN) \
},
#define DEF_FUNCTION_PTR_A(name, kind, state) \
{ FUNCTION_ADDR(Generate_##name), \
NULL, \
#name, \
name, \
Code::ComputeFlags(Code::kind, state) \
},
// Define array of pointers to generators and C builtin functions.
static BuiltinDesc functions[] = {
BUILTIN_LIST_C(DEF_FUNCTION_PTR_C)
BUILTIN_LIST_A(DEF_FUNCTION_PTR_A)
BUILTIN_LIST_DEBUG_A(DEF_FUNCTION_PTR_A)
// Terminator:
{ NULL, NULL, NULL, builtin_count, static_cast<Code::Flags>(0) }
};
#undef DEF_FUNCTION_PTR_C
#undef DEF_FUNCTION_PTR_A
// For now we generate builtin adaptor code into a stack-allocated
// buffer, before copying it into individual code objects.
byte buffer[4*KB];
// Traverse the list of builtins and generate an adaptor in a
// separate code object for each one.
for (int i = 0; i < builtin_count; i++) {
if (create_heap_objects) {
MacroAssembler masm(buffer, sizeof buffer);
// Generate the code/adaptor.
typedef void (*Generator)(MacroAssembler*, int);
Generator g = FUNCTION_CAST<Generator>(functions[i].generator);
// We pass all arguments to the generator, but it may not use all of
// them. This works because the first arguments are on top of the
// stack.
g(&masm, functions[i].name);
// Move the code into the object heap.
CodeDesc desc;
masm.GetCode(&desc);
Code::Flags flags = functions[i].flags;
Object* code;
{
// During startup it's OK to always allocate and defer GC to later.
// This simplifies things because we don't need to retry.
AlwaysAllocateScope __scope__;
code = Heap::CreateCode(desc, NULL, flags, masm.CodeObject());
if (code->IsFailure()) {
v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
}
}
// Add any unresolved jumps or calls to the fixup list in the
// bootstrapper.
Bootstrapper::AddFixup(Code::cast(code), &masm);
// Log the event and add the code to the builtins array.
LOG(CodeCreateEvent("Builtin", Code::cast(code), functions[i].s_name));
builtins_[i] = code;
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_builtin_code) {
PrintF("Builtin: %s\n", functions[i].s_name);
Code::cast(code)->Disassemble(functions[i].s_name);
PrintF("\n");
}
#endif
} else {
// Deserializing. The values will be filled in during IterateBuiltins.
builtins_[i] = NULL;
}
names_[i] = functions[i].s_name;
}
// Mark as initialized.
is_initialized = true;
}
void Builtins::TearDown() {
is_initialized = false;
}
void Builtins::IterateBuiltins(ObjectVisitor* v) {
v->VisitPointers(&builtins_[0], &builtins_[0] + builtin_count);
}
const char* Builtins::Lookup(byte* pc) {
if (is_initialized) { // may be called during initialization (disassembler!)
for (int i = 0; i < builtin_count; i++) {
Code* entry = Code::cast(builtins_[i]);
if (entry->contains(pc)) {
return names_[i];
}
}
}
return NULL;
}
} } // namespace v8::internal

217
deps/v8/src/builtins.h

@ -0,0 +1,217 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_BUILTINS_H_
#define V8_BUILTINS_H_
namespace v8 { namespace internal {
// Define list of builtins implemented in C.
#define BUILTIN_LIST_C(V) \
V(Illegal) \
\
V(EmptyFunction) \
\
V(ArrayCode) \
\
V(ArrayPush) \
V(ArrayPop) \
\
V(HandleApiCall) \
V(HandleApiCallAsFunction)
// Define list of builtins implemented in assembly.
#define BUILTIN_LIST_A(V) \
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \
V(JSConstructCall, BUILTIN, UNINITIALIZED) \
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED) \
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED) \
\
V(LoadIC_Miss, BUILTIN, UNINITIALIZED) \
V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED) \
V(StoreIC_Miss, BUILTIN, UNINITIALIZED) \
V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED) \
\
V(StoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \
V(KeyedStoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \
\
V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED) \
V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC) \
V(LoadIC_Normal, LOAD_IC, MONOMORPHIC) \
V(LoadIC_ArrayLength, LOAD_IC, MONOMORPHIC) \
V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC) \
V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC) \
V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC) \
\
V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED) \
V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC) \
V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC) \
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
\
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC) \
\
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
V(FunctionCall, BUILTIN, UNINITIALIZED) \
V(FunctionApply, BUILTIN, UNINITIALIZED)
// Define list of builtins used by the debugger implemented in assembly.
#define BUILTIN_LIST_DEBUG_A(V) \
V(Return_DebugBreak, BUILTIN, DEBUG_BREAK) \
V(Return_DebugBreakEntry, BUILTIN, DEBUG_BREAK) \
V(ConstructCall_DebugBreak, BUILTIN, DEBUG_BREAK) \
V(StubNoRegisters_DebugBreak, BUILTIN, DEBUG_BREAK) \
V(LoadIC_DebugBreak, LOAD_IC, DEBUG_BREAK) \
V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK) \
V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK) \
V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK)
// Define list of builtins implemented in JavaScript.
#define BUILTINS_LIST_JS(V) \
V(EQUALS, 1) \
V(STRICT_EQUALS, 1) \
V(COMPARE, 2) \
V(ADD, 1) \
V(SUB, 1) \
V(MUL, 1) \
V(DIV, 1) \
V(MOD, 1) \
V(BIT_OR, 1) \
V(BIT_AND, 1) \
V(BIT_XOR, 1) \
V(UNARY_MINUS, 0) \
V(BIT_NOT, 0) \
V(SHL, 1) \
V(SAR, 1) \
V(SHR, 1) \
V(DELETE, 1) \
V(IN, 1) \
V(INSTANCE_OF, 1) \
V(GET_KEYS, 0) \
V(FILTER_KEY, 1) \
V(CALL_NON_FUNCTION, 0) \
V(TO_OBJECT, 0) \
V(TO_NUMBER, 0) \
V(TO_STRING, 0) \
V(STRING_ADD_LEFT, 1) \
V(STRING_ADD_RIGHT, 1) \
V(APPLY_PREPARE, 1) \
V(APPLY_OVERFLOW, 1)
class ObjectVisitor;
class Builtins : public AllStatic {
public:
// Generate all builtin code objects. Should be called once during
// VM initialization.
static void Setup(bool create_heap_objects);
static void TearDown();
// Garbage collection support.
static void IterateBuiltins(ObjectVisitor* v);
// Disassembler support.
static const char* Lookup(byte* pc);
enum Name {
#define DEF_ENUM_C(name) name,
#define DEF_ENUM_A(name, kind, state) name,
BUILTIN_LIST_C(DEF_ENUM_C)
BUILTIN_LIST_A(DEF_ENUM_A)
BUILTIN_LIST_DEBUG_A(DEF_ENUM_A)
#undef DEF_ENUM_C
#undef DEF_ENUM_A
builtin_count
};
enum CFunctionId {
#define DEF_ENUM_C(name) c_##name,
BUILTIN_LIST_C(DEF_ENUM_C)
#undef DEF_ENUM_C
cfunction_count
};
enum JavaScript {
#define DEF_ENUM(name, ignore) name,
BUILTINS_LIST_JS(DEF_ENUM)
#undef DEF_ENUM
id_count
};
static Code* builtin(Name name) {
// Code::cast cannot be used here since we access builtins
// during the marking phase of mark sweep. See IC::Clear.
return reinterpret_cast<Code*>(builtins_[name]);
}
static Address builtin_address(Name name) {
return reinterpret_cast<Address>(&builtins_[name]);
}
static Address c_function_address(CFunctionId id) {
return c_functions_[id];
}
static const char* GetName(JavaScript id) { return javascript_names_[id]; }
static int GetArgumentsCount(JavaScript id) { return javascript_argc_[id]; }
static Handle<Code> GetCode(JavaScript id, bool* resolved);
static int NumberOfJavaScriptBuiltins() { return id_count; }
static Object* builtin_passed_function;
private:
// The external C++ functions called from the code.
static Address c_functions_[cfunction_count];
// Note: These are always Code objects, but to conform with
// IterateBuiltins() above which assumes Object**'s for the callback
// function f, we use an Object* array here.
static Object* builtins_[builtin_count];
static const char* names_[builtin_count];
static const char* javascript_names_[id_count];
static int javascript_argc_[id_count];
static void Generate_Adaptor(MacroAssembler* masm, CFunctionId id);
static void Generate_JSConstructCall(MacroAssembler* masm);
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm);
static void Generate_FunctionCall(MacroAssembler* masm);
static void Generate_FunctionApply(MacroAssembler* masm);
};
} } // namespace v8::internal
#endif // V8_BUILTINS_H_

103
deps/v8/src/bytecodes-irregexp.h

@ -0,0 +1,103 @@
// Copyright 2008-2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_BYTECODES_IRREGEXP_H_
#define V8_BYTECODES_IRREGEXP_H_
namespace v8 { namespace internal {
static const int BYTECODE_MASK = 0xff;
// The first argument is packed in with the byte code in one word, but so it
// has 24 bits, but it can be positive and negative so only use 23 bits for
// positive values.
static const unsigned int MAX_FIRST_ARG = 0x7fffffu;
static const int BYTECODE_SHIFT = 8;
#define BYTECODE_ITERATOR(V) \
V(BREAK, 0, 4) /* bc8 */ \
V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
V(POP_CP, 10, 4) /* bc8 pad24 */ \
V(POP_BT, 11, 4) /* bc8 pad24 */ \
V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
V(FAIL, 13, 4) /* bc8 pad24 */ \
V(SUCCEED, 14, 4) /* bc8 pad24 */ \
V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 addr32 */ \
V(CHECK_LT, 32, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_GT, 33, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_NOT_BACK_REF, 34, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 35, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_REGS_EQUAL, 36, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
V(LOOKUP_MAP1, 37, 12) /* bc8 pad8 start16 bit_map_addr32 addr32 */ \
V(LOOKUP_MAP2, 38, 96) /* bc8 pad8 start16 half_nibble_map_addr32* */ \
V(LOOKUP_MAP8, 39, 96) /* bc8 pad8 start16 byte_map addr32* */ \
V(LOOKUP_HI_MAP8, 40, 96) /* bc8 start24 byte_map_addr32 addr32* */ \
V(CHECK_REGISTER_LT, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_GE, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_EQ_POS, 43, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
V(CHECK_NOT_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
V(CHECK_GREEDY, 46, 8) /* bc8 pad24 addr32 */ \
V(ADVANCE_CP_AND_GOTO, 47, 8) /* bc8 offset24 addr32 */
#define DECLARE_BYTECODES(name, code, length) \
static const int BC_##name = code;
BYTECODE_ITERATOR(DECLARE_BYTECODES)
#undef DECLARE_BYTECODES
#define DECLARE_BYTECODE_LENGTH(name, code, length) \
static const int BC_##name##_LENGTH = length;
BYTECODE_ITERATOR(DECLARE_BYTECODE_LENGTH)
#undef DECLARE_BYTECODE_LENGTH
} }
#endif // V8_BYTECODES_IRREGEXP_H_

85
deps/v8/src/char-predicates-inl.h

@ -0,0 +1,85 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_CHAR_PREDICATES_INL_H_
#define V8_CHAR_PREDICATES_INL_H_
#include "char-predicates.h"
namespace v8 { namespace internal {
inline bool IsCarriageReturn(uc32 c) {
return c == 0x000D;
}
inline bool IsLineFeed(uc32 c) {
return c == 0x000A;
}
static inline bool IsInRange(int value, int lower_limit, int higher_limit) {
ASSERT(lower_limit <= higher_limit);
return static_cast<unsigned int>(value - lower_limit) <=
static_cast<unsigned int>(higher_limit - lower_limit);
}
inline bool IsDecimalDigit(uc32 c) {
// ECMA-262, 3rd, 7.8.3 (p 16)
return IsInRange(c, '0', '9');
}
inline bool IsHexDigit(uc32 c) {
// ECMA-262, 3rd, 7.6 (p 15)
return IsDecimalDigit(c) || IsInRange(c | 0x20, 'a', 'f');
}
inline bool IsRegExpWord(uc16 c) {
return IsInRange(c | 0x20, 'a', 'z')
|| IsDecimalDigit(c)
|| (c == '_');
}
inline bool IsRegExpNewline(uc16 c) {
switch (c) {
// CR LF LS PS
case 0x000A: case 0x000D: case 0x2028: case 0x2029:
return false;
default:
return true;
}
}
} } // namespace v8::internal
#endif // V8_CHAR_PREDICATES_INL_H_

64
deps/v8/src/char-predicates.h

@ -0,0 +1,64 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_CHAR_PREDICATES_H_
#define V8_CHAR_PREDICATES_H_
namespace v8 { namespace internal {
// Unicode character predicates as defined by ECMA-262, 3rd,
// used for lexical analysis.
inline bool IsCarriageReturn(uc32 c);
inline bool IsLineFeed(uc32 c);
inline bool IsDecimalDigit(uc32 c);
inline bool IsHexDigit(uc32 c);
inline bool IsRegExpWord(uc32 c);
inline bool IsRegExpNewline(uc32 c);
struct IdentifierStart {
static inline bool Is(uc32 c) {
switch (c) {
case '$': case '_': case '\\': return true;
default: return unibrow::Letter::Is(c);
}
}
};
struct IdentifierPart {
static inline bool Is(uc32 c) {
return IdentifierStart::Is(c)
|| unibrow::Number::Is(c)
|| unibrow::CombiningMark::Is(c)
|| unibrow::ConnectorPunctuation::Is(c);
}
};
} } // namespace v8::internal
#endif // V8_CHAR_PREDICATES_H_

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save