Reverting patch on wrong branch.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@668 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
2a6c90e668
commit
2da0c25569
@ -1,13 +0,0 @@
|
||||
# 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>
|
@ -1,403 +0,0 @@
|
||||
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.
|
||||
|
@ -1,46 +0,0 @@
|
||||
This license applies to all parts of V8 that are not externally
|
||||
maintained libraries. The externally maintained libraries used by V8
|
||||
are:
|
||||
|
||||
- Jscre, located under third_party/jscre. This code is copyrighted
|
||||
by the University of Cambridge and Apple Inc. and released under a
|
||||
2-clause BSD license.
|
||||
|
||||
- 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.
|
||||
|
||||
These libraries have their own licenses; we recommend you read them,
|
||||
as their terms may differ from the terms below.
|
||||
|
||||
Copyright 2006-2008, 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.
|
@ -1,578 +0,0 @@
|
||||
# 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
|
||||
|
||||
|
||||
LIBRARY_FLAGS = {
|
||||
'all': {
|
||||
'CPPDEFINES': ['ENABLE_LOGGING_AND_PROFILING']
|
||||
},
|
||||
'gcc': {
|
||||
'all': {
|
||||
'DIALECTFLAGS': ['-ansi'],
|
||||
'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'],
|
||||
'CXXFLAGS': ['$CCFLAGS', '-fno-rtti', '-fno-exceptions'],
|
||||
'LIBS': ['pthread']
|
||||
},
|
||||
'mode:debug': {
|
||||
'CCFLAGS': ['-g', '-O0'],
|
||||
'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG']
|
||||
},
|
||||
'mode:release': {
|
||||
'CCFLAGS': ['-O3', '-fomit-frame-pointer']
|
||||
},
|
||||
'wordsize:64': {
|
||||
'CCFLAGS': ['-m32'],
|
||||
'LINKFLAGS': ['-m32']
|
||||
}
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'DIALECTFLAGS': ['/nologo'],
|
||||
'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'],
|
||||
'CXXFLAGS': ['$CCFLAGS', '/GR-', '/Gy'],
|
||||
'CPPDEFINES': ['WIN32', '_USE_32BIT_TIME_T', 'PCRE_STATIC'],
|
||||
'LINKFLAGS': ['/NOLOGO', '/MACHINE:X86', '/INCREMENTAL:NO',
|
||||
'/NXCOMPAT', '/IGNORE:4221'],
|
||||
'ARFLAGS': ['/NOLOGO'],
|
||||
'CCPDBFLAGS': ['/Zi']
|
||||
},
|
||||
'mode:debug': {
|
||||
'CCFLAGS': ['/Od', '/Gm', '/MTd'],
|
||||
'CPPDEFINES': ['_DEBUG', 'ENABLE_DISASSEMBLER', 'DEBUG'],
|
||||
'LINKFLAGS': ['/DEBUG']
|
||||
},
|
||||
'mode:release': {
|
||||
'CCFLAGS': ['/O2', '/MT', '/GL'],
|
||||
'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG'],
|
||||
'ARFLAGS': ['/LTCG']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
V8_EXTRA_FLAGS = {
|
||||
'gcc': {
|
||||
'all': {
|
||||
'CXXFLAGS': [], #['-fvisibility=hidden'],
|
||||
'WARNINGFLAGS': ['-pedantic', '-Wall', '-Werror', '-W',
|
||||
'-Wno-unused-parameter']
|
||||
},
|
||||
'arch:arm': {
|
||||
'CPPDEFINES': ['ARM']
|
||||
},
|
||||
'disassembler:on': {
|
||||
'CPPDEFINES': ['ENABLE_DISASSEMBLER']
|
||||
}
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'WARNINGFLAGS': ['/W3', '/WX', '/wd4355', '/wd4800']
|
||||
},
|
||||
'library:shared': {
|
||||
'CPPDEFINES': ['BUILDING_V8_SHARED']
|
||||
},
|
||||
'arch:arm': {
|
||||
'CPPDEFINES': ['ARM']
|
||||
},
|
||||
'disassembler:on': {
|
||||
'CPPDEFINES': ['ENABLE_DISASSEMBLER']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JSCRE_EXTRA_FLAGS = {
|
||||
'gcc': {
|
||||
'all': {
|
||||
'CPPDEFINES': ['SUPPORT_UTF8', 'NO_RECURSE', 'SUPPORT_UCP'],
|
||||
'WARNINGFLAGS': ['-w']
|
||||
},
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'CPPDEFINES': ['SUPPORT_UTF8', 'NO_RECURSE', 'SUPPORT_UCP'],
|
||||
'WARNINGFLAGS': ['/W3', '/WX', '/wd4355', '/wd4800']
|
||||
},
|
||||
'library:shared': {
|
||||
'CPPDEFINES': ['BUILDING_V8_SHARED']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DTOA_EXTRA_FLAGS = {
|
||||
'gcc': {
|
||||
'all': {
|
||||
'WARNINGFLAGS': ['-Werror']
|
||||
}
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'WARNINGFLAGS': ['/WX', '/wd4018', '/wd4244']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CCTEST_EXTRA_FLAGS = {
|
||||
'all': {
|
||||
'CPPPATH': [join(root_dir, 'src')],
|
||||
'LIBS': ['$LIBRARY']
|
||||
},
|
||||
'gcc': {
|
||||
'all': {
|
||||
'LIBPATH': [abspath('.')]
|
||||
},
|
||||
'wordsize:64': {
|
||||
'CCFLAGS': ['-m32'],
|
||||
'LINKFLAGS': ['-m32']
|
||||
},
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'CPPDEFINES': ['_HAS_EXCEPTIONS=0']
|
||||
},
|
||||
'library:shared': {
|
||||
'CPPDEFINES': ['USING_V8_SHARED']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SAMPLE_FLAGS = {
|
||||
'all': {
|
||||
'CPPPATH': [join(abspath('.'), 'include')],
|
||||
'LIBS': ['$LIBRARY'],
|
||||
},
|
||||
'gcc': {
|
||||
'all': {
|
||||
'LIBS': ['pthread'],
|
||||
'LIBPATH': ['.']
|
||||
},
|
||||
'wordsize:64': {
|
||||
'CCFLAGS': ['-m32'],
|
||||
'LINKFLAGS': ['-m32']
|
||||
},
|
||||
'mode:release': {
|
||||
'CCFLAGS': ['-O2']
|
||||
},
|
||||
'mode:debug': {
|
||||
'CCFLAGS': ['-g', '-O0']
|
||||
}
|
||||
},
|
||||
'msvc': {
|
||||
'all': {
|
||||
'CCFLAGS': ['/nologo'],
|
||||
'LINKFLAGS': ['/nologo'],
|
||||
},
|
||||
'library:shared': {
|
||||
'CPPDEFINES': ['USING_V8_SHARED']
|
||||
},
|
||||
'prof:on': {
|
||||
'LINKFLAGS': ['/MAP']
|
||||
},
|
||||
'mode:release': {
|
||||
'CCFLAGS': ['/O2', '/MT'],
|
||||
'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG']
|
||||
},
|
||||
'mode:debug': {
|
||||
'CCFLAGS': ['/Od', '/MTd'],
|
||||
'LINKFLAGS': ['/DEBUG']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
D8_FLAGS = {
|
||||
'gcc': {
|
||||
'console:readline': {
|
||||
'LIBS': ['readline']
|
||||
}
|
||||
},
|
||||
'msvc': { }
|
||||
}
|
||||
|
||||
|
||||
SUFFIXES = {
|
||||
'release': '',
|
||||
'debug': '_g'
|
||||
}
|
||||
|
||||
|
||||
def Abort(message):
|
||||
print message
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def GuessOS():
|
||||
id = platform.system()
|
||||
if id == 'Linux':
|
||||
return 'linux'
|
||||
elif id == 'Darwin':
|
||||
return 'macos'
|
||||
elif id == 'Windows':
|
||||
return 'win32'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def GuessWordsize():
|
||||
if '64' in platform.machine():
|
||||
return '64'
|
||||
else:
|
||||
return '32'
|
||||
|
||||
|
||||
def GuessToolchain(os):
|
||||
tools = Environment()['TOOLS']
|
||||
if 'gcc' in tools:
|
||||
return 'gcc'
|
||||
elif 'msvc' in tools:
|
||||
return 'msvc'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
OS_GUESS = GuessOS()
|
||||
TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS)
|
||||
ARCH_GUESS = utils.GuessArchitecture()
|
||||
WORDSIZE_GUESS = GuessWordsize()
|
||||
|
||||
|
||||
SIMPLE_OPTIONS = {
|
||||
'toolchain': {
|
||||
'values': ['gcc', 'msvc'],
|
||||
'default': TOOLCHAIN_GUESS,
|
||||
'help': 'the toolchain to use'
|
||||
},
|
||||
'os': {
|
||||
'values': ['linux', 'macos', 'win32'],
|
||||
'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'],
|
||||
'default': 'off',
|
||||
'help': 'build using snapshots for faster start-up'
|
||||
},
|
||||
'prof': {
|
||||
'values': ['on', 'off'],
|
||||
'default': 'off',
|
||||
'help': 'enable profiling of build target'
|
||||
},
|
||||
'library': {
|
||||
'values': ['static', 'shared'],
|
||||
'default': 'static',
|
||||
'help': 'the type of library to produce'
|
||||
},
|
||||
'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 (NAME1:value1,NAME2:value2)', '')
|
||||
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.")
|
||||
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.cctest_targets = []
|
||||
self.sample_targets = []
|
||||
self.d8_targets = []
|
||||
self.options = options
|
||||
self.env_overrides = env_overrides
|
||||
self.samples = samples
|
||||
self.use_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']
|
||||
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 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 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 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):
|
||||
# The environment overrides are in the format NAME1:value1,NAME2:value2
|
||||
overrides = {}
|
||||
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)
|
||||
jscre_flags = context.AddRelevantFlags(library_flags, JSCRE_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,
|
||||
'jscre': jscre_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) = env.SConscript(
|
||||
join('src', 'SConscript'),
|
||||
build_dir=join('obj', target_id),
|
||||
exports='context',
|
||||
duplicate=False
|
||||
)
|
||||
|
||||
# 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'])
|
||||
|
||||
SourceSignatures(env['sourcesignatures'])
|
||||
|
||||
libraries = []
|
||||
cctests = []
|
||||
samples = []
|
||||
d8s = []
|
||||
modes = SplitList(env['mode'])
|
||||
for mode in modes:
|
||||
context = BuildSpecific(env.Copy(), mode, env_overrides)
|
||||
libraries += context.library_targets
|
||||
cctests += context.cctest_targets
|
||||
samples += context.sample_targets
|
||||
d8s += context.d8_targets
|
||||
|
||||
env.Alias('library', libraries)
|
||||
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()
|
@ -1,29 +0,0 @@
|
||||
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.
|
@ -1,221 +0,0 @@
|
||||
// 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 = '2';
|
||||
|
||||
|
||||
// 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);
|
||||
runner.NotifyScore(Math.round(100 * score));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
this.runner.NotifyResult(this.name, Math.round(100 * score));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,880 +0,0 @@
|
||||
// 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);
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -1,539 +0,0 @@
|
||||
// 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";
|
||||
};
|
@ -1,171 +0,0 @@
|
||||
<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>
|
||||
<style>
|
||||
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: 200%; height: 2em}
|
||||
h2{font-size: 140%; height: 2em}
|
||||
h3{font-size: 100%; height: 2em}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
</style>
|
||||
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
<div class="title"><h2>Revisions of the benchmark suite</h2></div>
|
||||
|
||||
<p><i>Please 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.</i></p>
|
||||
|
||||
<div class="title"><h3>Version 1</h3></div>
|
||||
|
||||
<p>Initial release.</p>
|
||||
|
||||
<div class="title"><h3>Version 2</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>
|
||||
|
||||
|
||||
</td><td style="text-align: center">
|
||||
<div class="run">
|
||||
<div id="status" style="text-align: center; margin-top: 75px; 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>
|
@ -1,59 +0,0 @@
|
||||
// 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');
|
||||
|
||||
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 });
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -1,143 +0,0 @@
|
||||
// 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 debug event from which occoured (from the DebugEvent
|
||||
* enumeration)
|
||||
* \param exec_state execution state (JavaScript object)
|
||||
* \param event_data event specific data (JavaScript object)
|
||||
* \param data value passed by the user to AddDebugEventListener
|
||||
*/
|
||||
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
|
||||
* 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);
|
||||
|
||||
|
||||
class EXPORT Debug {
|
||||
public:
|
||||
// Add a C debug event listener.
|
||||
static bool AddDebugEventListener(DebugEventCallback that,
|
||||
Handle<Value> data = Handle<Value>());
|
||||
|
||||
// Add a JavaScript debug event listener.
|
||||
static bool AddDebugEventListener(v8::Handle<v8::Function> that,
|
||||
Handle<Value> data = Handle<Value>());
|
||||
|
||||
// Remove a C debug event listener.
|
||||
static void RemoveDebugEventListener(DebugEventCallback that);
|
||||
|
||||
// Remove a JavaScript debug event listener.
|
||||
static void RemoveDebugEventListener(v8::Handle<v8::Function> that);
|
||||
|
||||
// Generate a stack dump.
|
||||
static void StackDump();
|
||||
|
||||
// Break execution of JavaScript.
|
||||
static void DebugBreak();
|
||||
|
||||
// Message based interface. The message protocol is JSON.
|
||||
static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL);
|
||||
static void SendCommand(const uint16_t* command, int length);
|
||||
};
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
||||
|
||||
#undef EXPORT
|
||||
|
||||
|
||||
#endif // V8_DEBUG_H_
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
# 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')
|
@ -1,42 +0,0 @@
|
||||
// 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();
|
@ -1,624 +0,0 @@
|
||||
// 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>
|
||||
|
||||
// To avoid warnings from <map> on windows we disable exceptions.
|
||||
#define _HAS_EXCEPTIONS 0
|
||||
#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);
|
||||
}
|
@ -1,258 +0,0 @@
|
||||
// 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 <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
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 main(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.\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;
|
||||
}
|
||||
|
||||
|
||||
// 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]);
|
||||
printf("%s", *str);
|
||||
}
|
||||
printf("\n");
|
||||
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]);
|
||||
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);
|
||||
printf("%s\n", *str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReportException(v8::TryCatch* try_catch) {
|
||||
v8::HandleScope handle_scope;
|
||||
v8::String::Utf8Value exception(try_catch->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);
|
||||
} else {
|
||||
// Print (filename):(line number): (message).
|
||||
v8::String::Utf8Value filename(message->GetScriptResourceName());
|
||||
int linenum = message->GetLineNumber();
|
||||
printf("%s:%i: %s\n", *filename, linenum, *exception);
|
||||
// Print line of source code.
|
||||
v8::String::Utf8Value sourceline(message->GetSourceLine());
|
||||
printf("%s\n", *sourceline);
|
||||
// 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");
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
# 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',
|
||||
'disassembler.cc', 'execution.cc', 'factory.cc', 'flags.cc', 'frames.cc',
|
||||
'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc',
|
||||
'jsregexp.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
|
||||
'parser.cc', 'property.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', 'zone.cc'
|
||||
],
|
||||
'arch:arm': ['assembler-arm.cc', 'builtins-arm.cc', 'codegen-arm.cc',
|
||||
'cpu-arm.cc', 'disasm-arm.cc', 'frames-arm.cc', 'ic-arm.cc',
|
||||
'macro-assembler-arm.cc', 'stub-cache-arm.cc'],
|
||||
'arch:ia32': ['assembler-ia32.cc', 'builtins-ia32.cc', 'codegen-ia32.cc',
|
||||
'cpu-ia32.cc', 'disasm-ia32.cc', 'frames-ia32.cc', 'ic-ia32.cc',
|
||||
'macro-assembler-ia32.cc', 'stub-cache-ia32.cc'],
|
||||
'simulator:arm': ['simulator-arm.cc'],
|
||||
'os:linux': ['platform-linux.cc'],
|
||||
'os:macos': ['platform-macos.cc'],
|
||||
'os:nullos': ['platform-nullos.cc'],
|
||||
'os:win32': ['platform-win32.cc'],
|
||||
'mode:release': [],
|
||||
'mode:debug': ['objects-debug.cc', 'prettyprinter.cc']
|
||||
}
|
||||
|
||||
|
||||
D8_FILES = {
|
||||
'all': [
|
||||
'd8.cc'
|
||||
],
|
||||
'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()
|
||||
|
||||
|
||||
JSCRE_FILES = '''
|
||||
pcre_compile.cpp
|
||||
pcre_exec.cpp
|
||||
pcre_tables.cpp
|
||||
pcre_ucp_searchfuncs.cpp
|
||||
pcre_xclass.cpp
|
||||
'''.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 JSCRE.
|
||||
jscre_env = env.Copy()
|
||||
jscre_env.Replace(**context.flags['jscre'])
|
||||
jscre_files = [join('third_party', 'jscre', s) for s in JSCRE_FILES]
|
||||
jscre_obj = context.ConfigureObject(jscre_env, jscre_files)
|
||||
|
||||
# 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 = [jscre_obj, dtoa_obj, source_objs]
|
||||
|
||||
# Create snapshot if necessary.
|
||||
empty_snapshot_obj = context.ConfigureObject(env, 'snapshot-empty.cc')
|
||||
if context.use_snapshot:
|
||||
mksnapshot_src = 'mksnapshot.cc'
|
||||
mksnapshot = env.Program('mksnapshot', [mksnapshot_src, libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
|
||||
snapshot_cc = env.Snapshot('snapshot.cc', mksnapshot, LOGFILE=File('snapshot.log').abspath)
|
||||
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)
|
||||
|
||||
|
||||
(library_objs, d8_objs) = ConfigureObjectFiles()
|
||||
Return('library_objs d8_objs')
|
@ -1,510 +0,0 @@
|
||||
// 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::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::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->FindCode(),
|
||||
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
|
@ -1,97 +0,0 @@
|
||||
// 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(ScriptLineOffset) \
|
||||
V(ScriptColumnOffset) \
|
||||
V(ScriptType) \
|
||||
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* ScriptGetSource(Object* object, void*);
|
||||
static Object* ScriptGetLineOffset(Object* object, void*);
|
||||
static Object* ScriptGetColumnOffset(Object* object, void*);
|
||||
static Object* ScriptGetType(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_
|
@ -1,187 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
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
|
@ -1,167 +0,0 @@
|
||||
// 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 function uses malloc. This version of StrDup
|
||||
// uses new and calls the FatalProcessOutOfMemory handler if
|
||||
// allocation fails.
|
||||
char* StrDup(const char* str);
|
||||
|
||||
|
||||
// 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_
|
File diff suppressed because it is too large
Load Diff
@ -1,490 +0,0 @@
|
||||
// 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 "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 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() {
|
||||
return &v8::HandleScope::current_;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void ZapHandleRange(void** begin, void** end) {
|
||||
v8::HandleScope::ZapRange(begin, end);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
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(Template* that);
|
||||
static inline v8::internal::Handle<v8::internal::FunctionTemplateInfo>
|
||||
OpenHandle(FunctionTemplate* that);
|
||||
static inline v8::internal::Handle<v8::internal::ObjectTemplateInfo>
|
||||
OpenHandle(ObjectTemplate* that);
|
||||
static inline v8::internal::Handle<v8::internal::Object>
|
||||
OpenHandle(Data* data);
|
||||
static inline v8::internal::Handle<v8::internal::JSObject>
|
||||
OpenHandle(v8::Object* data);
|
||||
static inline v8::internal::Handle<v8::internal::JSArray>
|
||||
OpenHandle(v8::Array* data);
|
||||
static inline v8::internal::Handle<v8::internal::String>
|
||||
OpenHandle(String* data);
|
||||
static inline v8::internal::Handle<v8::internal::JSFunction>
|
||||
OpenHandle(Script* data);
|
||||
static inline v8::internal::Handle<v8::internal::JSFunction>
|
||||
OpenHandle(Function* data);
|
||||
static inline v8::internal::Handle<v8::internal::JSObject>
|
||||
OpenHandle(Message* message);
|
||||
static inline v8::internal::Handle<v8::internal::Context>
|
||||
OpenHandle(v8::Context* context);
|
||||
static inline v8::internal::Handle<v8::internal::SignatureInfo>
|
||||
OpenHandle(v8::Signature* sig);
|
||||
static inline v8::internal::Handle<v8::internal::TypeSwitchInfo>
|
||||
OpenHandle(v8::TypeSwitch* that);
|
||||
static inline v8::internal::Handle<v8::internal::Proxy>
|
||||
OpenHandle(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(
|
||||
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(v8::From* that) { \
|
||||
return v8::internal::Handle<v8::internal::To>( \
|
||||
reinterpret_cast<v8::internal::To**>(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.
|
||||
ImplementationUtilities::HandleScopeData handle_scope_data_;
|
||||
|
||||
static void Iterate(ObjectVisitor* v,
|
||||
List<void**>* blocks,
|
||||
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
|
||||
ImplementationUtilities::ZapHandleRange(block, &block[kHandleBlockSize]);
|
||||
#endif
|
||||
DeleteArray(block);
|
||||
}
|
||||
spare = reinterpret_cast<Object**>(blocks.RemoveLast());
|
||||
#ifdef DEBUG
|
||||
ImplementationUtilities::ZapHandleRange(
|
||||
reinterpret_cast<void**>(spare),
|
||||
reinterpret_cast<void**>(&spare[kHandleBlockSize]));
|
||||
#endif
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_API_H_
|
@ -1,93 +0,0 @@
|
||||
// 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) {
|
||||
var serialNumber = %GetTemplateField(data, kApiSerialNumberOffset);
|
||||
if (!(serialNumber in kApiFunctionCache)) {
|
||||
kApiFunctionCache[serialNumber] = null;
|
||||
var fun = %CreateApiFunction(data);
|
||||
if (name) %FunctionSetName(fun, name);
|
||||
kApiFunctionCache[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);
|
||||
}
|
||||
return kApiFunctionCache[serialNumber];
|
||||
}
|
||||
|
||||
|
||||
function ConfigureTemplateInstance(obj, data) {
|
||||
var properties = %GetTemplateField(data, kApiPropertyListOffset);
|
||||
if (properties) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
// 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_
|
@ -1,959 +0,0 @@
|
||||
// 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
|
||||
var arg_number = 0, arg_count = %_ArgumentsLength();
|
||||
var n = 0;
|
||||
|
||||
var A = $Array(1 + arg_count);
|
||||
var E = this;
|
||||
|
||||
while (true) {
|
||||
if (IS_ARRAY(E)) {
|
||||
// This is an array of intervals or an array of keys. Keys are
|
||||
// represented by non-negative integers. Intervals are represented by
|
||||
// negative integers, followed by positive counts. The interval start
|
||||
// is determined by subtracting the entry from -1. There may also be
|
||||
// undefined entries in the array which should be skipped.
|
||||
var intervals = %GetArrayKeys(E, E.length);
|
||||
var length = intervals.length;
|
||||
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++) {
|
||||
if (j in E) {
|
||||
A[n + j] = E[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The case where key is undefined also ends here.
|
||||
if (!IS_UNDEFINED(key)) {
|
||||
A[n + key] = E[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
n += E.length;
|
||||
} else {
|
||||
A[n++] = E;
|
||||
}
|
||||
if (arg_number == arg_count) break;
|
||||
E = %_Arguments(arg_number++);
|
||||
}
|
||||
|
||||
A.length = n; // may contain empty arrays
|
||||
return A;
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
if (custom_compare) {
|
||||
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];
|
||||
// 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], element);
|
||||
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];
|
||||
// Issue 95: Keep the pivot element out of the comparisons to avoid
|
||||
// infinite recursion if comparefn(pivot, pivot) != 0.
|
||||
a[pivot_index] = a[to - 1];
|
||||
a[to - 1] = pivot;
|
||||
var low_end = from; // Upper bound of the elements lower than pivot.
|
||||
var high_start = to - 1; // Lower bound of the elements greater than pivot.
|
||||
for (var i = from; i < high_start; ) {
|
||||
var element = a[i];
|
||||
var order = Compare(element, pivot);
|
||||
if (order < 0) {
|
||||
a[i] = a[low_end];
|
||||
a[low_end] = element;
|
||||
low_end++;
|
||||
i++;
|
||||
} else if (order > 0) {
|
||||
high_start--;
|
||||
a[i] = a[high_start];
|
||||
a[high_start] = element;
|
||||
} else { // order == 0
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Restore the pivot element to its rightful place.
|
||||
a[to - 1] = a[high_start];
|
||||
a[high_start] = pivot;
|
||||
high_start++;
|
||||
QuickSort(a, from, low_end);
|
||||
QuickSort(a, high_start, to);
|
||||
}
|
||||
|
||||
var old_length = ToUint32(this.length);
|
||||
|
||||
%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 = [];
|
||||
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.push(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();
|
@ -1,243 +0,0 @@
|
||||
// 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_));
|
||||
return Assembler::target_address_at(pc_);
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::set_target_address(Address target) {
|
||||
ASSERT(IsCodeTarget(rmode_));
|
||||
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*>(pc_);
|
||||
}
|
||||
|
||||
|
||||
Address RelocInfo::call_address() {
|
||||
ASSERT(is_call_instruction());
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::set_call_address(Address target) {
|
||||
ASSERT(is_call_instruction());
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
Object* RelocInfo::call_object() {
|
||||
ASSERT(is_call_instruction());
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Object** RelocInfo::call_object_address() {
|
||||
ASSERT(is_call_instruction());
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::set_call_object(Object* target) {
|
||||
ASSERT(is_call_instruction());
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::is_call_instruction() {
|
||||
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_
|
File diff suppressed because it is too large
Load Diff
@ -1,784 +0,0 @@
|
||||
// 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_;
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// The pc store offset may be 8 or 12 depending on the processor implementation.
|
||||
int PcStoreOffset();
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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 inbetween 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);
|
||||
|
||||
int pc_offset() const { return pc_ - buffer_; }
|
||||
int last_position() const { return last_position_; }
|
||||
bool last_position_is_statement() const {
|
||||
return last_position_is_statement_;
|
||||
}
|
||||
|
||||
// Temporary helper function. Used by codegen.cc.
|
||||
int last_statement_position() const { return last_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);
|
||||
|
||||
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 last_position_;
|
||||
bool last_position_is_statement_;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_ASSEMBLER_ARM_H_
|
@ -1,266 +0,0 @@
|
||||
// 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 && is_call_instruction()) {
|
||||
// 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_);
|
||||
}
|
||||
|
||||
|
||||
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(is_call_instruction());
|
||||
return Assembler::target_address_at(pc_ + 1);
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::set_call_address(Address target) {
|
||||
ASSERT(is_call_instruction());
|
||||
Assembler::set_target_address_at(pc_ + 1, target);
|
||||
}
|
||||
|
||||
|
||||
Object* RelocInfo::call_object() {
|
||||
ASSERT(is_call_instruction());
|
||||
return *call_object_address();
|
||||
}
|
||||
|
||||
|
||||
Object** RelocInfo::call_object_address() {
|
||||
ASSERT(is_call_instruction());
|
||||
return reinterpret_cast<Object**>(pc_ + 1);
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::set_call_object(Object* target) {
|
||||
ASSERT(is_call_instruction());
|
||||
*call_object_address() = target;
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::is_call_instruction() {
|
||||
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(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::NONE) RecordRelocInfo(x.rmode_);
|
||||
emit(x.x_);
|
||||
}
|
||||
|
||||
|
||||
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, // reg == 0
|
||||
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);
|
||||
*reinterpret_cast<int32_t*>(&buf_[len_]) = 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_
|
File diff suppressed because it is too large
Load Diff
@ -1,811 +0,0 @@
|
||||
// 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_; }
|
||||
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 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,
|
||||
sign = 8,
|
||||
not_sign = 9,
|
||||
parity_even = 10,
|
||||
parity_odd = 11,
|
||||
less = 12,
|
||||
greater_equal = 13,
|
||||
less_equal = 14,
|
||||
greater = 15,
|
||||
|
||||
// aliases
|
||||
zero = equal,
|
||||
not_zero = not_equal,
|
||||
negative = sign,
|
||||
positive = not_sign
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
bool is_zero() const { return x_ == 0 && rmode_ == RelocInfo::NONE; }
|
||||
bool is_int8() const {
|
||||
return -128 <= x_ && x_ < 128 && rmode_ == RelocInfo::NONE;
|
||||
}
|
||||
|
||||
private:
|
||||
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:
|
||||
// Mutable because reg in ModR/M byte is set by Assembler via set_reg().
|
||||
mutable byte buf_[6];
|
||||
// The number of bytes in buf_.
|
||||
unsigned int len_;
|
||||
// Only valid if len_ > 4.
|
||||
RelocInfo::Mode rmode_;
|
||||
|
||||
inline void set_modrm(int mod, // reg == 0
|
||||
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);
|
||||
inline void set_reg(Register reg) const;
|
||||
|
||||
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.....1| ......0|
|
||||
// [ next | type |
|
||||
|
||||
class Displacement BASE_EMBEDDED {
|
||||
public:
|
||||
enum Type {
|
||||
UNCONDITIONAL_JUMP,
|
||||
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, 1> {};
|
||||
class NextField: public BitField<int, 1, 32-1> {};
|
||||
|
||||
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 { 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_ & (1 << f); }
|
||||
// Check whether a feature is currently enabled.
|
||||
static bool IsEnabled(Feature f) { return enabled_ & (1 << f); }
|
||||
// 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_ |= (1 << f);
|
||||
}
|
||||
~Scope() { CpuFeatures::enabled_ = old_enabled_; }
|
||||
private:
|
||||
uint32_t old_enabled_;
|
||||
#else
|
||||
public:
|
||||
explicit Scope(Feature f) {}
|
||||
#endif
|
||||
};
|
||||
private:
|
||||
static uint32_t supported_;
|
||||
static uint32_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 inbetween 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 othogonal. 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 carefull 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 pop(Register dst);
|
||||
void pop(const Operand& dst);
|
||||
|
||||
// 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, Handle<Object> handle);
|
||||
void mov(Register dst, const Operand& 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);
|
||||
|
||||
// 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 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 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 bts(const Operand& dst, Register src);
|
||||
|
||||
// Miscellaneous
|
||||
void hlt();
|
||||
void int3();
|
||||
void nop();
|
||||
void rdtsc();
|
||||
void ret(int imm16);
|
||||
void leave();
|
||||
|
||||
// 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 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 frndint();
|
||||
|
||||
void sahf();
|
||||
|
||||
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 last_statement_position() const { return last_statement_position_; }
|
||||
int last_position() const { return last_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:
|
||||
// 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 last_position_;
|
||||
int last_statement_position_;
|
||||
|
||||
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);
|
||||
|
||||
// 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_operand(const Operand& adr, Register reg);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
// 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_
|
@ -1,575 +0,0 @@
|
||||
// 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"
|
||||
|
||||
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 = Debug::GetCodeTarget(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::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::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
|
@ -1,476 +0,0 @@
|
||||
// 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 : public ZoneObject { // LabelShadows are dynamically allocated.
|
||||
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 Displacement;
|
||||
friend class LabelShadow;
|
||||
};
|
||||
|
||||
|
||||
// A LabelShadow represents a label that is temporarily shadowed by another
|
||||
// label (represented by the original label during shadowing). They are used
|
||||
// to catch jumps to labels in certain contexts, e.g. try blocks. After
|
||||
// shadowing ends, the formerly shadowed label is again represented by the
|
||||
// original label and the LabelShadow can be used as a label in its own
|
||||
// right, representing the formerly shadowing label.
|
||||
class LabelShadow : public Label {
|
||||
public:
|
||||
explicit LabelShadow(Label* original) {
|
||||
ASSERT(original != NULL);
|
||||
original_label_ = original;
|
||||
original_pos_ = original->pos_;
|
||||
original->Unuse();
|
||||
#ifdef DEBUG
|
||||
is_shadowing_ = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
~LabelShadow() {
|
||||
ASSERT(!is_shadowing_);
|
||||
}
|
||||
|
||||
void StopShadowing() {
|
||||
ASSERT(is_shadowing_ && is_unused());
|
||||
pos_ = original_label_->pos_;
|
||||
original_label_->pos_ = original_pos_;
|
||||
#ifdef DEBUG
|
||||
is_shadowing_ = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Label* original_label() const { return original_label_; }
|
||||
|
||||
private:
|
||||
// During shadowing, the currently shadowing label. After shadowing, the
|
||||
// label that was shadowed.
|
||||
Label* original_label_;
|
||||
|
||||
// During shadowing, the saved state of the original label.
|
||||
int original_pos_;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool is_shadowing_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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_)
|
||||
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/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 patch_code(byte* instructions, int instruction_count);
|
||||
|
||||
// Patch the code with a call.
|
||||
void patch_code_with_call(Address target, int guard_bytes);
|
||||
INLINE(bool is_call_instruction());
|
||||
|
||||
#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);
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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_limit()
|
||||
static ExternalReference address_of_stack_guard_limit();
|
||||
|
||||
// Function Debug::Break()
|
||||
static ExternalReference debug_break();
|
||||
|
||||
// Static variable Heap::NewSpaceStart()
|
||||
static ExternalReference new_space_start();
|
||||
|
||||
// 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_
|
@ -1,368 +0,0 @@
|
||||
// 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, false, 0);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// All the Accept member functions for each syntax tree node type.
|
||||
|
||||
#define DECL_ACCEPT(type) \
|
||||
void type::Accept(Visitor* 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 {
|
||||
kind_ = value_->AsLiteral() == NULL ? COMPUTED : CONSTANT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) {
|
||||
key_ = new Literal(value->name());
|
||||
value_ = value;
|
||||
kind_ = is_getter ? GETTER : SETTER;
|
||||
}
|
||||
|
||||
|
||||
void LabelCollector::AddLabel(Label* label) {
|
||||
// Add the label to the collector, but discard duplicates.
|
||||
int length = labels_->length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (labels_->at(i) == label) return;
|
||||
}
|
||||
labels_->Add(label);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementation of Visitor
|
||||
|
||||
|
||||
void Visitor::VisitStatements(ZoneList<Statement*>* statements) {
|
||||
for (int i = 0; i < statements->length(); i++) {
|
||||
Visit(statements->at(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Visitor::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_NODE_TYPE(MAKE_ACCEPT)
|
||||
#undef MAKE_ACCEPT
|
||||
|
||||
|
||||
#define MAKE_CONVERSION(Name) \
|
||||
RegExp##Name* RegExpTree::As##Name() { \
|
||||
return NULL; \
|
||||
}
|
||||
FOR_EACH_REG_EXP_NODE_TYPE(MAKE_CONVERSION)
|
||||
#undef MAKE_CONVERSION
|
||||
|
||||
|
||||
#define MAKE_CONVERSION(Name) \
|
||||
RegExp##Name* RegExp##Name::As##Name() { \
|
||||
return this; \
|
||||
}
|
||||
FOR_EACH_REG_EXP_NODE_TYPE(MAKE_CONVERSION)
|
||||
#undef MAKE_CONVERSION
|
||||
|
||||
|
||||
|
||||
RegExpEmpty RegExpEmpty::kInstance;
|
||||
|
||||
|
||||
// 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<char> ToString() { return stream_.ToCString(); }
|
||||
#define MAKE_CASE(Name) virtual void* Visit##Name(RegExp##Name*, void* data);
|
||||
FOR_EACH_REG_EXP_NODE_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->nodes()->length(); i++) {
|
||||
stream()->Add(" ");
|
||||
that->nodes()->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) {
|
||||
if (that.is_character_class()) {
|
||||
stream()->Add("&%c", that.from());
|
||||
} else if (that.IsSingleton()) {
|
||||
stream()->Add("%c", that.from());
|
||||
} else {
|
||||
stream()->Add("%c-%c", that.from(), 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("'%w'", that->data());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) {
|
||||
stream()->Add("(# %i ", that->min());
|
||||
if (that->max() == RegExpQuantifier::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<char> RegExpTree::ToString() {
|
||||
RegExpUnparser unparser;
|
||||
Accept(&unparser, NULL);
|
||||
return unparser.ToString();
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
1448
regexp2000/src/ast.h
1448
regexp2000/src/ast.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
// 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 manangment.
|
||||
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 boostrapping is active.
|
||||
static bool IsActive();
|
||||
|
||||
// Encoding/decoding support for fixup flags.
|
||||
class FixupFlagsIsPCRelative: public BitField<bool, 0, 1> {};
|
||||
class FixupFlagsArgumentsCount: public BitField<uint32_t, 1, 32-1> {};
|
||||
};
|
||||
|
||||
}} // namespace v8::internal
|
||||
|
||||
#endif // V8_BOOTSTRAPPER_H_
|
@ -1,821 +0,0 @@
|
||||
// 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 >= excpected
|
||||
__ 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(©);
|
||||
__ ldr(ip, MemOperand(r0, 0));
|
||||
__ push(ip);
|
||||
__ cmp(r0, r2); // Compare before moving to next argument.
|
||||
__ sub(r0, r0, Operand(kPointerSize));
|
||||
__ b(ne, ©);
|
||||
|
||||
__ 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(©);
|
||||
// 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, ©);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList pointer_regs) {
|
||||
// Save the content of all general purpose registers in memory. This copy in
|
||||
// memory is later pushed onto the JS expression stack for the fake JS frame
|
||||
// generated and also to the C frame generated on top of that. In the JS
|
||||
// frame ONLY the registers containing pointers will be pushed on the
|
||||
// expression stack. This causes the GC to update these pointers so that
|
||||
// they will have the correct value when returning from the debugger.
|
||||
__ SaveRegistersToMemory(kJSCallerSaved);
|
||||
|
||||
// This is a direct call from a debug breakpoint. To build a fake JS frame
|
||||
// with no parameters push a function and a receiver, keep the current
|
||||
// return address in lr, and set r0 to zero.
|
||||
__ mov(ip, Operand(ExternalReference::the_hole_value_location()));
|
||||
__ ldr(r3, MemOperand(ip));
|
||||
__ mov(r0, Operand(0)); // Null receiver and zero arguments.
|
||||
__ stm(db_w, sp, r0.bit() | r3.bit()); // push function and receiver
|
||||
|
||||
// r0: number of arguments.
|
||||
// What follows is an inlined version of EnterJSFrame(0, 0).
|
||||
// It needs to be kept in sync if any calling conventions are changed.
|
||||
|
||||
// Compute parameter pointer before making changes
|
||||
// ip = sp + kPointerSize*(args_len+1); // +1 for receiver, args_len == 0
|
||||
__ add(ip, sp, Operand(kPointerSize));
|
||||
|
||||
__ mov(r3, Operand(0)); // args_len to be saved
|
||||
__ mov(r2, Operand(cp)); // context to be saved
|
||||
|
||||
// push in reverse order: context (r2), args_len (r3), caller_pp, caller_fp,
|
||||
// sp_on_exit (ip == pp), return address
|
||||
__ stm(db_w, sp, r2.bit() | r3.bit() | pp.bit() | fp.bit() |
|
||||
ip.bit() | lr.bit());
|
||||
// Setup new frame pointer.
|
||||
__ add(fp, sp, Operand(-StandardFrameConstants::kContextOffset));
|
||||
__ mov(pp, Operand(ip)); // setup new parameter pointer
|
||||
// r0 is already set to 0 as spare slot to store caller code object during GC
|
||||
__ push(r0); // code pointer
|
||||
|
||||
// Inlined EnterJSFrame ends here.
|
||||
|
||||
// Store the registers containing object pointers on the expression stack to
|
||||
// make sure that these are correctly updated during GC.
|
||||
// Use sp as base to push.
|
||||
__ CopyRegistersFromMemoryToStack(sp, pointer_regs);
|
||||
|
||||
#ifdef DEBUG
|
||||
__ RecordComment("// Calling from debug break to runtime - come in - over");
|
||||
#endif
|
||||
// r0 is already 0, no arguments
|
||||
__ mov(r1, Operand(ExternalReference::debug_break()));
|
||||
|
||||
CEntryDebugBreakStub ceb;
|
||||
__ CallStub(&ceb);
|
||||
|
||||
// Restore the register values containing object pointers from the expression
|
||||
// stack in the reverse order as they where pushed.
|
||||
// Use sp as base to pop.
|
||||
__ CopyRegistersFromStackToMemory(sp, r3, pointer_regs);
|
||||
|
||||
// What follows is an inlined version of ExitJSFrame(0).
|
||||
// It needs to be kept in sync if any calling conventions are changed.
|
||||
// NOTE: loading the return address to lr and discarding the (fake) function
|
||||
// is an addition to this inlined copy.
|
||||
|
||||
__ mov(sp, Operand(fp)); // respect ABI stack constraint
|
||||
__ ldm(ia, sp, pp.bit() | fp.bit() | sp.bit() | lr.bit());
|
||||
__ pop(); // discard fake function
|
||||
|
||||
// Inlined ExitJSFrame ends here.
|
||||
|
||||
// Finally restore all registers.
|
||||
__ RestoreRegistersFromMemory(kJSCallerSaved);
|
||||
|
||||
// Now that the break point has been handled, resume normal execution by
|
||||
// jumping to the target address intended by the caller and that was
|
||||
// overwritten by the address of DebugBreakXXX.
|
||||
__ mov(ip, Operand(ExternalReference(Debug_Address::AfterBreakTarget())));
|
||||
__ ldr(ip, MemOperand(ip));
|
||||
__ Jump(ip);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for IC load (from ic-arm.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : receiver
|
||||
// -- r2 : name
|
||||
// -- lr : return address
|
||||
// -- [sp] : receiver
|
||||
// -----------------------------------
|
||||
// Registers r0 and r2 contain objects that needs to be pushed on the
|
||||
// expression stack of the fake JS frame.
|
||||
Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for IC store (from ic-arm.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : receiver
|
||||
// -- r2 : name
|
||||
// -- lr : return address
|
||||
// -- [sp] : receiver
|
||||
// -----------------------------------
|
||||
// Registers r0 and r2 contain objects that needs to be pushed on the
|
||||
// expression stack of the fake JS frame.
|
||||
Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Keyed load IC not implemented on ARM.
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Keyed store IC not implemented on ARM.
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CallIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for IC call (from ic-arm.cc)
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0: number of arguments
|
||||
// -- r1: receiver
|
||||
// -- lr: return address
|
||||
// -----------------------------------
|
||||
// Register r1 contains an object that needs to be pushed on the expression
|
||||
// stack of the fake JS frame. r0 is the actual number of arguments not
|
||||
// encoded as a smi, therefore it cannot be on the expression stack of the
|
||||
// fake JS frame as it can easily be an invalid pointer (e.g. 1). r0 will be
|
||||
// pushed on the stack of the C frame and restored from there.
|
||||
Generate_DebugBreakCallHelper(masm, r1.bit());
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ConstructCall_DebugBreak(MacroAssembler* masm) {
|
||||
// In places other than IC call sites it is expected that r0 is TOS which
|
||||
// is an object - this is not generally the case so this should be used with
|
||||
// care.
|
||||
Generate_DebugBreakCallHelper(masm, r0.bit());
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_Return_DebugBreak(MacroAssembler* masm) {
|
||||
// In places other than IC call sites it is expected that r0 is TOS which
|
||||
// is an object - this is not generally the case so this should be used with
|
||||
// care.
|
||||
Generate_DebugBreakCallHelper(masm, r0.bit());
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_Return_DebugBreakEntry(MacroAssembler* masm) {
|
||||
// Generate nothing as this handling of debug break return is not done this
|
||||
// way on ARM - yet.
|
||||
}
|
||||
|
||||
void Builtins::Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
|
||||
// Generate nothing as CodeStub CallFunction is not used on ARM.
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,902 +0,0 @@
|
||||
// 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.
|
||||
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
|
||||
__ mov(eax, FieldOperand(edi, JSFunction::kMapOffset));
|
||||
__ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
|
||||
__ cmp(eax, JS_FUNCTION_TYPE);
|
||||
__ 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)
|
||||
__ mov(ebx, FieldOperand(eax, JSFunction::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ebx, MAP_TYPE);
|
||||
__ 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
|
||||
__ movzx_b(ebx, FieldOperand(eax, Map::kInstanceTypeOffset));
|
||||
__ cmp(ebx, 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(Operand(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(Operand(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(Operand(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);
|
||||
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); // get the map
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ecx, JS_FUNCTION_TYPE);
|
||||
__ 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(Operand(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);
|
||||
|
||||
// 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(Operand(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(©);
|
||||
__ inc(ecx);
|
||||
__ push(Operand(eax, 0));
|
||||
__ sub(Operand(eax), Immediate(kPointerSize));
|
||||
__ cmp(ecx, Operand(ebx));
|
||||
__ j(less, ©);
|
||||
__ 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(©);
|
||||
__ inc(ecx);
|
||||
__ push(Operand(edi, 0));
|
||||
__ sub(Operand(edi), Immediate(kPointerSize));
|
||||
__ cmp(ecx, Operand(eax));
|
||||
__ j(less, ©);
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList pointer_regs,
|
||||
bool convert_call_to_jmp) {
|
||||
// Save the content of all general purpose registers in memory. This copy in
|
||||
// memory is later pushed onto the JS expression stack for the fake JS frame
|
||||
// generated and also to the C frame generated on top of that. In the JS
|
||||
// frame ONLY the registers containing pointers will be pushed on the
|
||||
// expression stack. This causes the GC to update these pointers so that
|
||||
// they will have the correct value when returning from the debugger.
|
||||
__ SaveRegistersToMemory(kJSCallerSaved);
|
||||
|
||||
// Enter an internal frame.
|
||||
__ EnterInternalFrame();
|
||||
|
||||
// Store the registers containing object pointers on the expression stack to
|
||||
// make sure that these are correctly updated during GC.
|
||||
__ PushRegistersFromMemory(pointer_regs);
|
||||
|
||||
#ifdef DEBUG
|
||||
__ RecordComment("// Calling from debug break to runtime - come in - over");
|
||||
#endif
|
||||
__ Set(eax, Immediate(0)); // no arguments
|
||||
__ mov(Operand(ebx), Immediate(ExternalReference::debug_break()));
|
||||
|
||||
CEntryDebugBreakStub ceb;
|
||||
__ CallStub(&ceb);
|
||||
|
||||
// Restore the register values containing object pointers from the expression
|
||||
// stack in the reverse order as they where pushed.
|
||||
__ PopRegistersToMemory(pointer_regs);
|
||||
|
||||
// Get rid of the internal frame.
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
// If this call did not replace a call but patched other code then there will
|
||||
// be an unwanted return address left on the stack. Here we get rid of that.
|
||||
if (convert_call_to_jmp) {
|
||||
__ pop(eax);
|
||||
}
|
||||
|
||||
// Finally restore all registers.
|
||||
__ RestoreRegistersFromMemory(kJSCallerSaved);
|
||||
|
||||
// Now that the break point has been handled, resume normal execution by
|
||||
// jumping to the target address intended by the caller and that was
|
||||
// overwritten by the address of DebugBreakXXX.
|
||||
ExternalReference after_break_target =
|
||||
ExternalReference(Debug_Address::AfterBreakTarget());
|
||||
__ jmp(Operand::StaticVariable(after_break_target));
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state for IC load call (from ic-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, ecx.bit(), false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
|
||||
// REgister state for IC store call (from ic-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : value
|
||||
// -- ecx : name
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state for keyed IC load call (from ic-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// No registers used on entry.
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, 0, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state for keyed IC load call (from ic-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : value
|
||||
// -----------------------------------
|
||||
// Register eax contains an object that needs to be pushed on the
|
||||
// expression stack of the fake JS frame.
|
||||
Generate_DebugBreakCallHelper(masm, eax.bit(), false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CallIC_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state for keyed IC call call (from ic-ia32.cc)
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax: number of arguments
|
||||
// -----------------------------------
|
||||
// The number of arguments in eax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, 0, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ConstructCall_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state just before return from JS function (from codegen-ia32.cc).
|
||||
// eax is the actual number of arguments not encoded as a smi see comment
|
||||
// above IC call.
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax: number of arguments
|
||||
// -----------------------------------
|
||||
// The number of arguments in eax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, 0, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_Return_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state just before return from JS function (from codegen-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax: return value
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, eax.bit(), true);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_Return_DebugBreakEntry(MacroAssembler* masm) {
|
||||
// OK to clobber ebx as we are returning from a JS function in the code
|
||||
// generated by Ia32CodeGenerator::ExitJSFrame.
|
||||
ExternalReference debug_break_return =
|
||||
ExternalReference(Debug_Address::DebugBreakReturn());
|
||||
__ mov(ebx, Operand::StaticVariable(debug_break_return));
|
||||
__ add(Operand(ebx), Immediate(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ jmp(Operand(ebx));
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
|
||||
// Register state for stub CallFunction (from CallFunctionStub in ic-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// No registers used on entry.
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, 0, false);
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,714 +0,0 @@
|
||||
// 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::kMaxFastElementsLength) {
|
||||
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(OTHER);
|
||||
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(OTHER);
|
||||
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_ShortStringLength(MacroAssembler* masm) {
|
||||
LoadIC::GenerateShortStringLength(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_LoadIC_MediumStringLength(MacroAssembler* masm) {
|
||||
LoadIC::GenerateMediumStringLength(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_LoadIC_LongStringLength(MacroAssembler* masm) {
|
||||
LoadIC::GenerateLongStringLength(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);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
// 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 = Heap::CreateCode(desc, NULL, flags);
|
||||
if (code->IsFailure()) {
|
||||
if (code->IsRetryAfterGC()) {
|
||||
CHECK(Heap::CollectGarbage(Failure::cast(code)->requested(),
|
||||
Failure::cast(code)->allocation_space()));
|
||||
code = Heap::CreateCode(desc, NULL, flags);
|
||||
}
|
||||
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 DEBUG
|
||||
if (FLAG_print_builtin_code) {
|
||||
PrintF("Builtin: %s\n", functions[i].s_name);
|
||||
code->Print();
|
||||
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
|
@ -1,227 +0,0 @@
|
||||
// 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(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_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_ShortStringLength, LOAD_IC, MONOMORPHIC) \
|
||||
V(LoadIC_MediumStringLength, LOAD_IC, MONOMORPHIC) \
|
||||
V(LoadIC_LongStringLength, LOAD_IC, MONOMORPHIC) \
|
||||
V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC) \
|
||||
V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC) \
|
||||
V(LoadIC_DebugBreak, LOAD_IC, DEBUG_BREAK) \
|
||||
\
|
||||
V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED) \
|
||||
V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC) \
|
||||
V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC) \
|
||||
V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK) \
|
||||
\
|
||||
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
|
||||
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
|
||||
V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK) \
|
||||
\
|
||||
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
|
||||
V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC) \
|
||||
V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK) \
|
||||
\
|
||||
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
|
||||
V(FunctionCall, BUILTIN, UNINITIALIZED) \
|
||||
V(FunctionApply, BUILTIN, UNINITIALIZED)
|
||||
|
||||
|
||||
// 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(INC, 0) \
|
||||
V(DEC, 0) \
|
||||
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(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)
|
||||
#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; }
|
||||
|
||||
// Called from stub-cache.cc.
|
||||
static void Generate_CallIC_DebugBreak(MacroAssembler* masm);
|
||||
|
||||
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);
|
||||
|
||||
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_StoreIC_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_ConstructCall_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_Return_DebugBreak(MacroAssembler* masm);
|
||||
static void Generate_Return_DebugBreakEntry(MacroAssembler* masm);
|
||||
static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_BUILTINS_H_
|
@ -1,64 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
inline bool IsDecimalDigit(uc32 c) {
|
||||
// ECMA-262, 3rd, 7.8.3 (p 16)
|
||||
return
|
||||
'0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
|
||||
inline bool IsHexDigit(uc32 c) {
|
||||
// ECMA-262, 3rd, 7.6 (p 15)
|
||||
return
|
||||
('0' <= c && c <= '9') ||
|
||||
('A' <= c && c <= 'F') ||
|
||||
('a' <= c && c <= 'f');
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CHAR_PREDICATES_INL_H_
|
@ -1,62 +0,0 @@
|
||||
// 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);
|
||||
|
||||
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_
|
@ -1,100 +0,0 @@
|
||||
// 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 <stdarg.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "platform.h"
|
||||
#include "top.h"
|
||||
|
||||
using namespace v8::internal;
|
||||
|
||||
static int fatal_error_handler_nesting_depth = 0;
|
||||
|
||||
// Contains protection against recursive calls (faults while handling faults).
|
||||
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...) {
|
||||
fatal_error_handler_nesting_depth++;
|
||||
// First time we try to print an error message
|
||||
if (fatal_error_handler_nesting_depth < 2) {
|
||||
OS::PrintError("\n\n#\n# Fatal error in %s, line %d\n# ", file, line);
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VPrintError(format, arguments);
|
||||
va_end(arguments);
|
||||
OS::PrintError("\n#\n\n");
|
||||
}
|
||||
// First two times we may try to print a stack dump.
|
||||
if (fatal_error_handler_nesting_depth < 3) {
|
||||
if (FLAG_stack_trace_on_abort) {
|
||||
// Call this one twice on double fault
|
||||
Top::PrintStack();
|
||||
}
|
||||
}
|
||||
OS::Abort();
|
||||
}
|
||||
|
||||
|
||||
void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
v8::Handle<v8::Value> expected,
|
||||
const char* value_source,
|
||||
v8::Handle<v8::Value> value) {
|
||||
if (!expected->Equals(value)) {
|
||||
v8::String::Utf8Value value_str(value);
|
||||
v8::String::Utf8Value expected_str(expected);
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s",
|
||||
expected_source, value_source, *expected_str, *value_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* unexpected_source,
|
||||
v8::Handle<v8::Value> unexpected,
|
||||
const char* value_source,
|
||||
v8::Handle<v8::Value> value) {
|
||||
if (unexpected->Equals(value)) {
|
||||
v8::String::Utf8Value value_str(value);
|
||||
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %s",
|
||||
unexpected_source, value_source, *value_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void API_Fatal(const char* location, const char* format, ...) {
|
||||
OS::PrintError("\n#\n# Fatal error in %s\n# ", location);
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VPrintError(format, arguments);
|
||||
va_end(arguments);
|
||||
OS::PrintError("\n#\n\n");
|
||||
OS::Abort();
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
// 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_CHECKS_H_
|
||||
#define V8_CHECKS_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "flags.h"
|
||||
|
||||
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
|
||||
void API_Fatal(const char* location, const char* format, ...);
|
||||
|
||||
// The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during
|
||||
// development, but they should not be relied on in the final product.
|
||||
#ifdef DEBUG
|
||||
#define FATAL(msg) \
|
||||
V8_Fatal(__FILE__, __LINE__, "%s", (msg))
|
||||
#define UNIMPLEMENTED() \
|
||||
V8_Fatal(__FILE__, __LINE__, "unimplemented code")
|
||||
#define UNREACHABLE() \
|
||||
V8_Fatal(__FILE__, __LINE__, "unreachable code")
|
||||
#else
|
||||
#define FATAL(msg) \
|
||||
V8_Fatal("", 0, "%s", (msg))
|
||||
#define UNIMPLEMENTED() \
|
||||
V8_Fatal("", 0, "unimplemented code")
|
||||
#define UNREACHABLE() ((void) 0)
|
||||
#endif
|
||||
|
||||
|
||||
// Used by the CHECK macro -- should not be called directly.
|
||||
static inline void CheckHelper(const char* file,
|
||||
int line,
|
||||
const char* source,
|
||||
bool condition) {
|
||||
if (!condition)
|
||||
V8_Fatal(file, line, "CHECK(%s) failed", source);
|
||||
}
|
||||
|
||||
|
||||
// The CHECK macro checks that the given condition is true; if not, it
|
||||
// prints a message to stderr and aborts.
|
||||
#define CHECK(condition) CheckHelper(__FILE__, __LINE__, #condition, condition)
|
||||
|
||||
|
||||
// Helper function used by the CHECK_EQ function when given int
|
||||
// arguments. Should not be called directly.
|
||||
static inline void CheckEqualsHelper(const char* file, int line,
|
||||
const char* expected_source, int expected,
|
||||
const char* value_source, int value) {
|
||||
if (expected != value) {
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %i\n# Found: %i",
|
||||
expected_source, value_source, expected, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper function used by the CHECK_NE function when given int
|
||||
// arguments. Should not be called directly.
|
||||
static inline void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* unexpected_source,
|
||||
int unexpected,
|
||||
const char* value_source,
|
||||
int value) {
|
||||
if (unexpected == value) {
|
||||
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %i",
|
||||
unexpected_source, value_source, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper function used by the CHECK function when given string
|
||||
// arguments. Should not be called directly.
|
||||
static inline void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
const char* expected,
|
||||
const char* value_source,
|
||||
const char* value) {
|
||||
if (strcmp(expected, value) != 0) {
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s",
|
||||
expected_source, value_source, expected, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
const char* expected,
|
||||
const char* value_source,
|
||||
const char* value) {
|
||||
if (expected == value ||
|
||||
(expected != NULL && value != NULL && strcmp(expected, value) == 0)) {
|
||||
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %s",
|
||||
expected_source, value_source, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper function used by the CHECK function when given pointer
|
||||
// arguments. Should not be called directly.
|
||||
static inline void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
void* expected,
|
||||
const char* value_source,
|
||||
void* value) {
|
||||
if (expected != value) {
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %i\n# Found: %i",
|
||||
expected_source, value_source,
|
||||
reinterpret_cast<int>(expected), reinterpret_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
void* expected,
|
||||
const char* value_source,
|
||||
void* value) {
|
||||
if (expected == value) {
|
||||
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %i",
|
||||
expected_source, value_source, reinterpret_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper function used by the CHECK function when given floating
|
||||
// point arguments. Should not be called directly.
|
||||
static inline void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
double expected,
|
||||
const char* value_source,
|
||||
double value) {
|
||||
// Force values to 64 bit memory to truncate 80 bit precision on IA32.
|
||||
volatile double* exp = new double[1];
|
||||
*exp = expected;
|
||||
volatile double* val = new double[1];
|
||||
*val = value;
|
||||
if (*exp != *val) {
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %f\n# Found: %f",
|
||||
expected_source, value_source, *exp, *val);
|
||||
}
|
||||
delete[] exp;
|
||||
delete[] val;
|
||||
}
|
||||
|
||||
|
||||
namespace v8 {
|
||||
class Value;
|
||||
template <class T> class Handle;
|
||||
}
|
||||
|
||||
|
||||
void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* unexpected_source,
|
||||
v8::Handle<v8::Value> unexpected,
|
||||
const char* value_source,
|
||||
v8::Handle<v8::Value> value);
|
||||
|
||||
|
||||
void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
v8::Handle<v8::Value> expected,
|
||||
const char* value_source,
|
||||
v8::Handle<v8::Value> value);
|
||||
|
||||
|
||||
#define CHECK_EQ(expected, value) CheckEqualsHelper(__FILE__, __LINE__, \
|
||||
#expected, expected, #value, value)
|
||||
|
||||
|
||||
#define CHECK_NE(unexpected, value) CheckNonEqualsHelper(__FILE__, __LINE__, \
|
||||
#unexpected, unexpected, #value, value)
|
||||
|
||||
|
||||
#define CHECK_GT(a, b) CHECK((a) > (b))
|
||||
#define CHECK_GE(a, b) CHECK((a) >= (b))
|
||||
|
||||
|
||||
// This is inspired by the static assertion facility in boost. This
|
||||
// is pretty magical. If it causes you trouble on a platform you may
|
||||
// find a fix in the boost code.
|
||||
template <bool> class StaticAssertion;
|
||||
template <> class StaticAssertion<true> { };
|
||||
// This macro joins two tokens. If one of the tokens is a macro the
|
||||
// helper call causes it to be resolved before joining.
|
||||
#define SEMI_STATIC_JOIN(a, b) SEMI_STATIC_JOIN_HELPER(a, b)
|
||||
#define SEMI_STATIC_JOIN_HELPER(a, b) a##b
|
||||
// Causes an error during compilation of the condition is not
|
||||
// statically known to be true. It is formulated as a typedef so that
|
||||
// it can be used wherever a typedef can be used. Beware that this
|
||||
// actually causes each use to introduce a new defined type with a
|
||||
// name depending on the source line.
|
||||
template <int> class StaticAssertionHelper { };
|
||||
#define STATIC_CHECK(test) \
|
||||
typedef \
|
||||
StaticAssertionHelper<sizeof(StaticAssertion<static_cast<bool>(test)>)> \
|
||||
SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)
|
||||
|
||||
|
||||
// The ASSERT macro is equivalent to CHECK except that it only
|
||||
// generates code in debug builds. Ditto STATIC_ASSERT.
|
||||
#ifdef DEBUG
|
||||
#define ASSERT(condition) CHECK(condition)
|
||||
#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2)
|
||||
#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2)
|
||||
#define STATIC_ASSERT(test) STATIC_CHECK(test)
|
||||
#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition)
|
||||
#else
|
||||
#define ASSERT(condition) ((void) 0)
|
||||
#define ASSERT_EQ(v1, v2) ((void) 0)
|
||||
#define ASSERT_NE(v1, v2) ((void) 0)
|
||||
#define STATIC_ASSERT(test) ((void) 0)
|
||||
#define SLOW_ASSERT(condition) ((void) 0)
|
||||
#endif
|
||||
|
||||
|
||||
#define ASSERT_TAG_ALIGNED(address) \
|
||||
ASSERT((reinterpret_cast<int>(address) & kHeapObjectTagMask) == 0)
|
||||
|
||||
#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & kHeapObjectTagMask) == 0)
|
||||
|
||||
#endif // V8_CHECKS_H_
|
@ -1,140 +0,0 @@
|
||||
// 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 "bootstrapper.h"
|
||||
#include "code-stubs.h"
|
||||
#include "factory.h"
|
||||
#include "macro-assembler.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
Handle<Code> CodeStub::GetCode() {
|
||||
uint32_t key = GetKey();
|
||||
int index = Heap::code_stubs()->FindNumberEntry(key);
|
||||
if (index == -1) {
|
||||
HandleScope scope;
|
||||
|
||||
// Update the static counter each time a new code stub is generated.
|
||||
Counters::code_stubs.Increment();
|
||||
|
||||
// Generate the new code.
|
||||
MacroAssembler masm(NULL, 256);
|
||||
|
||||
// Nested stubs are not allowed for leafs.
|
||||
masm.set_allow_stub_calls(AllowsStubCalls());
|
||||
|
||||
// Generate the code for the stub.
|
||||
masm.set_generating_stub(true);
|
||||
Generate(&masm);
|
||||
|
||||
// Create the code object.
|
||||
CodeDesc desc;
|
||||
masm.GetCode(&desc);
|
||||
|
||||
// Copy the generated code into a heap object, and store the major key.
|
||||
Code::Flags flags = Code::ComputeFlags(Code::STUB);
|
||||
Handle<Code> code = Factory::NewCode(desc, NULL, flags);
|
||||
code->set_major_key(MajorKey());
|
||||
|
||||
// Add unresolved entries in the code to the fixup list.
|
||||
Bootstrapper::AddFixup(*code, &masm);
|
||||
|
||||
LOG(CodeCreateEvent("Stub", *code, GetName()));
|
||||
Counters::total_stubs_code_size.Increment(code->instruction_size());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_code_stubs) {
|
||||
Print();
|
||||
code->Print();
|
||||
PrintF("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the dictionary and the root in Heap.
|
||||
Handle<Dictionary> dict =
|
||||
Factory::DictionaryAtNumberPut(Handle<Dictionary>(Heap::code_stubs()),
|
||||
key,
|
||||
code);
|
||||
Heap::set_code_stubs(*dict);
|
||||
index = Heap::code_stubs()->FindNumberEntry(key);
|
||||
}
|
||||
ASSERT(index != -1);
|
||||
|
||||
return Handle<Code>(Code::cast(Heap::code_stubs()->ValueAt(index)));
|
||||
}
|
||||
|
||||
|
||||
const char* CodeStub::MajorName(CodeStub::Major major_key) {
|
||||
switch (major_key) {
|
||||
case CallFunction:
|
||||
return "CallFunction";
|
||||
case GenericBinaryOp:
|
||||
return "GenericBinaryOp";
|
||||
case SmiOp:
|
||||
return "SmiOp";
|
||||
case Compare:
|
||||
return "Compare";
|
||||
case RecordWrite:
|
||||
return "RecordWrite";
|
||||
case StackCheck:
|
||||
return "StackCheck";
|
||||
case UnarySub:
|
||||
return "UnarySub";
|
||||
case RevertToNumber:
|
||||
return "RevertToNumber";
|
||||
case ToBoolean:
|
||||
return "ToBoolean";
|
||||
case Instanceof:
|
||||
return "Instanceof";
|
||||
case CounterOp:
|
||||
return "CounterOp";
|
||||
case ArgumentsAccess:
|
||||
return "ArgumentsAccess";
|
||||
case Runtime:
|
||||
return "Runtime";
|
||||
case CEntry:
|
||||
return "CEntry";
|
||||
case JSEntry:
|
||||
return "JSEntry";
|
||||
case GetProperty:
|
||||
return "GetProperty";
|
||||
case SetProperty:
|
||||
return "SetProperty";
|
||||
case InvokeBuiltin:
|
||||
return "InvokeBuiltin";
|
||||
case JSExit:
|
||||
return "JSExit";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,109 +0,0 @@
|
||||
// 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_CODE_STUBS_H_
|
||||
#define V8_CODE_STUBS_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Stub is base classes of all stubs.
|
||||
class CodeStub BASE_EMBEDDED {
|
||||
public:
|
||||
enum Major {
|
||||
CallFunction,
|
||||
GenericBinaryOp,
|
||||
SmiOp,
|
||||
Compare,
|
||||
RecordWrite, // Last stub that allows stub calls inside.
|
||||
StackCheck,
|
||||
UnarySub,
|
||||
RevertToNumber,
|
||||
ToBoolean,
|
||||
Instanceof,
|
||||
CounterOp,
|
||||
ArgumentsAccess,
|
||||
Runtime,
|
||||
CEntry,
|
||||
JSEntry,
|
||||
GetProperty, // ARM only
|
||||
SetProperty, // ARM only
|
||||
InvokeBuiltin, // ARM only
|
||||
JSExit, // ARM only
|
||||
NUMBER_OF_IDS
|
||||
};
|
||||
|
||||
// Retrieve the code for the stub. Generate the code if needed.
|
||||
Handle<Code> GetCode();
|
||||
|
||||
static Major MajorKeyFromKey(uint32_t key) {
|
||||
return static_cast<Major>(MajorKeyBits::decode(key));
|
||||
};
|
||||
static int MinorKeyFromKey(uint32_t key) {
|
||||
return MinorKeyBits::decode(key);
|
||||
};
|
||||
static const char* MajorName(Major major_key);
|
||||
|
||||
virtual ~CodeStub() {}
|
||||
|
||||
protected:
|
||||
static const int kMajorBits = 5;
|
||||
static const int kMinorBits = kBitsPerPointer - kMajorBits - kSmiTagSize;
|
||||
|
||||
private:
|
||||
// Generates the assembler code for the stub.
|
||||
virtual void Generate(MacroAssembler* masm) = 0;
|
||||
|
||||
// Returns information for computing the number key.
|
||||
virtual Major MajorKey() = 0;
|
||||
virtual int MinorKey() = 0;
|
||||
|
||||
// Returns a name for logging/debugging purposes.
|
||||
virtual const char* GetName() { return MajorName(MajorKey()); }
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void Print() { PrintF("%s\n", GetName()); }
|
||||
#endif
|
||||
|
||||
// Computes the key based on major and minor.
|
||||
uint32_t GetKey() {
|
||||
ASSERT(static_cast<int>(MajorKey()) < NUMBER_OF_IDS);
|
||||
return MinorKeyBits::encode(MinorKey()) |
|
||||
MajorKeyBits::encode(MajorKey());
|
||||
}
|
||||
|
||||
bool AllowsStubCalls() { return MajorKey() <= RecordWrite; }
|
||||
|
||||
class MajorKeyBits: public BitField<uint32_t, 0, kMajorBits> {};
|
||||
class MinorKeyBits: public BitField<uint32_t, kMajorBits, kMinorBits> {};
|
||||
|
||||
friend class BreakPointIterator;
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CODE_STUBS_H_
|
@ -1,67 +0,0 @@
|
||||
// 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_CODE_H_
|
||||
#define V8_CODE_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Wrapper class for passing expected and actual parameter counts as
|
||||
// either registers or immediate values. Used to make sure that the
|
||||
// caller provides exactly the expected number of parameters to the
|
||||
// callee.
|
||||
class ParameterCount BASE_EMBEDDED {
|
||||
public:
|
||||
explicit ParameterCount(Register reg)
|
||||
: reg_(reg), immediate_(0) { }
|
||||
explicit ParameterCount(int immediate)
|
||||
: reg_(no_reg), immediate_(immediate) { }
|
||||
|
||||
bool is_reg() const { return !reg_.is(no_reg); }
|
||||
bool is_immediate() const { return !is_reg(); }
|
||||
|
||||
Register reg() const {
|
||||
ASSERT(is_reg());
|
||||
return reg_;
|
||||
}
|
||||
int immediate() const {
|
||||
ASSERT(is_immediate());
|
||||
return immediate_;
|
||||
}
|
||||
|
||||
private:
|
||||
const Register reg_;
|
||||
const int immediate_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(ParameterCount);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CODE_H_
|
File diff suppressed because it is too large
Load Diff
@ -1,376 +0,0 @@
|
||||
// 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_CODEGEN_ARM_H_
|
||||
#define V8_CODEGEN_ARM_H_
|
||||
|
||||
#include "scopes.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// Forward declarations
|
||||
class DeferredCode;
|
||||
|
||||
// Mode to overwrite BinaryExpression values.
|
||||
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Reference support
|
||||
|
||||
// A reference is a C++ stack-allocated object that keeps an ECMA
|
||||
// reference on the execution stack while in scope. For variables
|
||||
// the reference is empty, indicating that it isn't necessary to
|
||||
// store state on the stack for keeping track of references to those.
|
||||
// For properties, we keep either one (named) or two (indexed) values
|
||||
// on the execution stack to represent the reference.
|
||||
|
||||
enum InitState { CONST_INIT, NOT_CONST_INIT };
|
||||
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
|
||||
|
||||
class Reference BASE_EMBEDDED {
|
||||
public:
|
||||
// The values of the types is important, see size().
|
||||
enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
|
||||
Reference(CodeGenerator* cgen, Expression* expression);
|
||||
~Reference();
|
||||
|
||||
Expression* expression() const { return expression_; }
|
||||
Type type() const { return type_; }
|
||||
void set_type(Type value) {
|
||||
ASSERT(type_ == ILLEGAL);
|
||||
type_ = value;
|
||||
}
|
||||
|
||||
// The size of the reference or -1 if the reference is illegal.
|
||||
int size() const { return type_; }
|
||||
|
||||
bool is_illegal() const { return type_ == ILLEGAL; }
|
||||
bool is_slot() const { return type_ == SLOT; }
|
||||
bool is_property() const { return type_ == NAMED || type_ == KEYED; }
|
||||
|
||||
// Return the name. Only valid for named property references.
|
||||
Handle<String> GetName();
|
||||
|
||||
// Generate code to push the value of the reference on top of the
|
||||
// expression stack. The reference is expected to be already on top of
|
||||
// the expression stack, and it is left in place with its value above it.
|
||||
void GetValue(TypeofState typeof_state);
|
||||
|
||||
// Generate code to store the value on top of the expression stack in the
|
||||
// reference. The reference is expected to be immediately below the value
|
||||
// on the expression stack. The stored value is left in place (with the
|
||||
// reference intact below it) to support chained assignments.
|
||||
void SetValue(InitState init_state);
|
||||
|
||||
private:
|
||||
CodeGenerator* cgen_;
|
||||
Expression* expression_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Code generation state
|
||||
|
||||
// The state is passed down the AST by the code generator (and back up, in
|
||||
// the form of the state of the label pair). It is threaded through the
|
||||
// call stack. Constructing a state implicitly pushes it on the owning code
|
||||
// generator's stack of states, and destroying one implicitly pops it.
|
||||
|
||||
class CodeGenState BASE_EMBEDDED {
|
||||
public:
|
||||
// Create an initial code generator state. Destroying the initial state
|
||||
// leaves the code generator with a NULL state.
|
||||
explicit CodeGenState(CodeGenerator* owner);
|
||||
|
||||
// Create a code generator state based on a code generator's current
|
||||
// state. The new state has its own typeof state and pair of branch
|
||||
// labels.
|
||||
CodeGenState(CodeGenerator* owner,
|
||||
TypeofState typeof_state,
|
||||
Label* true_target,
|
||||
Label* false_target);
|
||||
|
||||
// Destroy a code generator state and restore the owning code generator's
|
||||
// previous state.
|
||||
~CodeGenState();
|
||||
|
||||
TypeofState typeof_state() const { return typeof_state_; }
|
||||
Label* true_target() const { return true_target_; }
|
||||
Label* false_target() const { return false_target_; }
|
||||
|
||||
private:
|
||||
CodeGenerator* owner_;
|
||||
TypeofState typeof_state_;
|
||||
Label* true_target_;
|
||||
Label* false_target_;
|
||||
CodeGenState* previous_;
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CodeGenerator
|
||||
|
||||
class CodeGenerator: public Visitor {
|
||||
public:
|
||||
// Takes a function literal, generates code for it. This function should only
|
||||
// be called by compiler.cc.
|
||||
static Handle<Code> MakeCode(FunctionLiteral* fun,
|
||||
Handle<Script> script,
|
||||
bool is_eval);
|
||||
|
||||
static void SetFunctionInfo(Handle<JSFunction> fun,
|
||||
int length,
|
||||
int function_token_position,
|
||||
int start_position,
|
||||
int end_position,
|
||||
bool is_expression,
|
||||
bool is_toplevel,
|
||||
Handle<Script> script);
|
||||
|
||||
// Accessors
|
||||
MacroAssembler* masm() { return masm_; }
|
||||
|
||||
CodeGenState* state() { return state_; }
|
||||
void set_state(CodeGenState* state) { state_ = state; }
|
||||
|
||||
void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
|
||||
|
||||
private:
|
||||
// Construction/Destruction
|
||||
CodeGenerator(int buffer_size, Handle<Script> script, bool is_eval);
|
||||
virtual ~CodeGenerator() { delete masm_; }
|
||||
|
||||
// Accessors
|
||||
Scope* scope() const { return scope_; }
|
||||
|
||||
void ProcessDeferred();
|
||||
|
||||
bool is_eval() { return is_eval_; }
|
||||
|
||||
// State
|
||||
bool has_cc() const { return cc_reg_ != al; }
|
||||
TypeofState typeof_state() const { return state_->typeof_state(); }
|
||||
Label* true_target() const { return state_->true_target(); }
|
||||
Label* false_target() const { return state_->false_target(); }
|
||||
|
||||
|
||||
// Node visitors.
|
||||
#define DEF_VISIT(type) \
|
||||
void Visit##type(type* node);
|
||||
NODE_LIST(DEF_VISIT)
|
||||
#undef DEF_VISIT
|
||||
|
||||
// Main code generation function
|
||||
void GenCode(FunctionLiteral* fun);
|
||||
|
||||
// The following are used by class Reference.
|
||||
void LoadReference(Reference* ref);
|
||||
void UnloadReference(Reference* ref);
|
||||
|
||||
// Support functions for accessing parameters and other operands.
|
||||
MemOperand ParameterOperand(int index) const {
|
||||
int num_parameters = scope()->num_parameters();
|
||||
// index -2 corresponds to the activated closure, -1 corresponds
|
||||
// to the receiver
|
||||
ASSERT(-2 <= index && index < num_parameters);
|
||||
int offset = (1 + num_parameters - index) * kPointerSize;
|
||||
return MemOperand(fp, offset);
|
||||
}
|
||||
|
||||
MemOperand FunctionOperand() const {
|
||||
return MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset);
|
||||
}
|
||||
|
||||
MemOperand ContextOperand(Register context, int index) const {
|
||||
return MemOperand(context, Context::SlotOffset(index));
|
||||
}
|
||||
|
||||
MemOperand SlotOperand(Slot* slot, Register tmp);
|
||||
|
||||
// Expressions
|
||||
MemOperand GlobalObject() const {
|
||||
return ContextOperand(cp, Context::GLOBAL_INDEX);
|
||||
}
|
||||
|
||||
void LoadCondition(Expression* x,
|
||||
TypeofState typeof_state,
|
||||
Label* true_target,
|
||||
Label* false_target,
|
||||
bool force_cc);
|
||||
void Load(Expression* x, TypeofState typeof_state = NOT_INSIDE_TYPEOF);
|
||||
void LoadGlobal();
|
||||
void LoadGlobalReceiver(Register scratch);
|
||||
|
||||
// Read a value from a slot and leave it on top of the expression stack.
|
||||
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
|
||||
|
||||
// Special code for typeof expressions: Unfortunately, we must
|
||||
// be careful when loading the expression in 'typeof'
|
||||
// expressions. We are not allowed to throw reference errors for
|
||||
// non-existing properties of the global object, so we must make it
|
||||
// look like an explicit property access, instead of an access
|
||||
// through the context chain.
|
||||
void LoadTypeofExpression(Expression* x);
|
||||
|
||||
void ToBoolean(Label* true_target, Label* false_target);
|
||||
|
||||
void GenericBinaryOperation(Token::Value op);
|
||||
void Comparison(Condition cc, bool strict = false);
|
||||
|
||||
void SmiOperation(Token::Value op, Handle<Object> value, bool reversed);
|
||||
|
||||
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
|
||||
|
||||
// Control flow
|
||||
void Branch(bool if_true, Label* L);
|
||||
void CheckStack();
|
||||
void CleanStack(int num_bytes);
|
||||
|
||||
bool CheckForInlineRuntimeCall(CallRuntime* node);
|
||||
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
|
||||
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
|
||||
|
||||
Handle<Code> ComputeCallInitialize(int argc);
|
||||
|
||||
// Declare global variables and functions in the given array of
|
||||
// name/value pairs.
|
||||
void DeclareGlobals(Handle<FixedArray> pairs);
|
||||
|
||||
// Instantiate the function boilerplate.
|
||||
void InstantiateBoilerplate(Handle<JSFunction> boilerplate);
|
||||
|
||||
// Support for type checks.
|
||||
void GenerateIsSmi(ZoneList<Expression*>* args);
|
||||
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
|
||||
void GenerateIsArray(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for arguments.length and arguments[?].
|
||||
void GenerateArgumentsLength(ZoneList<Expression*>* args);
|
||||
void GenerateArgumentsAccess(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for accessing the value field of an object (used by Date).
|
||||
void GenerateValueOf(ZoneList<Expression*>* args);
|
||||
void GenerateSetValueOf(ZoneList<Expression*>* args);
|
||||
|
||||
// Fast support for charCodeAt(n).
|
||||
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
|
||||
|
||||
// Fast support for object equality testing.
|
||||
void GenerateObjectEquals(ZoneList<Expression*>* args);
|
||||
|
||||
// Methods and constants for fast case switch statement support.
|
||||
//
|
||||
// Only allow fast-case switch if the range of labels is at most
|
||||
// this factor times the number of case labels.
|
||||
// Value is derived from comparing the size of code generated by the normal
|
||||
// switch code for Smi-labels to the size of a single pointer. If code
|
||||
// quality increases this number should be decreased to match.
|
||||
static const int kFastSwitchMaxOverheadFactor = 10;
|
||||
|
||||
// Minimal number of switch cases required before we allow jump-table
|
||||
// optimization.
|
||||
static const int kFastSwitchMinCaseCount = 5;
|
||||
|
||||
// The limit of the range of a fast-case switch, as a factor of the number
|
||||
// of cases of the switch. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
int FastCaseSwitchMaxOverheadFactor();
|
||||
|
||||
// The minimal number of cases in a switch before the fast-case switch
|
||||
// optimization is enabled. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
int FastCaseSwitchMinCaseCount();
|
||||
|
||||
// Allocate a jump table and create code to jump through it.
|
||||
// Should call GenerateFastCaseSwitchCases to generate the code for
|
||||
// all the cases at the appropriate point.
|
||||
void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
|
||||
int min_index,
|
||||
int range,
|
||||
Label* fail_label,
|
||||
Vector<Label*> case_targets,
|
||||
Vector<Label> case_labels);
|
||||
|
||||
// Generate the code for cases for the fast case switch.
|
||||
// Called by GenerateFastCaseSwitchJumpTable.
|
||||
void GenerateFastCaseSwitchCases(SwitchStatement* node,
|
||||
Vector<Label> case_labels);
|
||||
|
||||
// Fast support for constant-Smi switches.
|
||||
void GenerateFastCaseSwitchStatement(SwitchStatement* node,
|
||||
int min_index,
|
||||
int range,
|
||||
int default_index);
|
||||
|
||||
// Fast support for constant-Smi switches. Tests whether switch statement
|
||||
// permits optimization and calls GenerateFastCaseSwitch if it does.
|
||||
// Returns true if the fast-case switch was generated, and false if not.
|
||||
bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
|
||||
|
||||
|
||||
// Bottle-neck interface to call the Assembler to generate the statement
|
||||
// position. This allows us to easily control whether statement positions
|
||||
// should be generated or not.
|
||||
void RecordStatementPosition(Node* node);
|
||||
|
||||
// Activation frames.
|
||||
void EnterJSFrame();
|
||||
void ExitJSFrame();
|
||||
|
||||
|
||||
bool is_eval_; // Tells whether code is generated for eval.
|
||||
Handle<Script> script_;
|
||||
List<DeferredCode*> deferred_;
|
||||
|
||||
// Assembler
|
||||
MacroAssembler* masm_; // to generate code
|
||||
|
||||
// Code generation state
|
||||
Scope* scope_;
|
||||
Condition cc_reg_;
|
||||
CodeGenState* state_;
|
||||
bool is_inside_try_;
|
||||
int break_stack_height_;
|
||||
|
||||
// Labels
|
||||
Label function_return_;
|
||||
|
||||
friend class Reference;
|
||||
friend class Property;
|
||||
friend class VariableProxy;
|
||||
friend class Slot;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CODEGEN_ARM_H_
|
File diff suppressed because it is too large
Load Diff
@ -1,428 +0,0 @@
|
||||
// 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_CODEGEN_IA32_H_
|
||||
#define V8_CODEGEN_IA32_H_
|
||||
|
||||
#include "scopes.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// Forward declarations
|
||||
class DeferredCode;
|
||||
|
||||
// Mode to overwrite BinaryExpression values.
|
||||
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
|
||||
|
||||
enum InitState { CONST_INIT, NOT_CONST_INIT };
|
||||
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Virtual frame
|
||||
|
||||
class VirtualFrame BASE_EMBEDDED {
|
||||
public:
|
||||
explicit VirtualFrame(CodeGenerator* cgen);
|
||||
|
||||
void Enter();
|
||||
void Exit();
|
||||
|
||||
void AllocateLocals();
|
||||
|
||||
Operand Top() const { return Operand(esp, 0); }
|
||||
|
||||
Operand Element(int index) const {
|
||||
return Operand(esp, index * kPointerSize);
|
||||
}
|
||||
|
||||
Operand Local(int index) const {
|
||||
ASSERT(0 <= index && index < frame_local_count_);
|
||||
return Operand(ebp, kLocal0Offset - index * kPointerSize);
|
||||
}
|
||||
|
||||
Operand Function() const { return Operand(ebp, kFunctionOffset); }
|
||||
|
||||
Operand Context() const { return Operand(ebp, kContextOffset); }
|
||||
|
||||
Operand Parameter(int index) const {
|
||||
ASSERT(-1 <= index && index < parameter_count_);
|
||||
return Operand(ebp, (1 + parameter_count_ - index) * kPointerSize);
|
||||
}
|
||||
|
||||
Operand Receiver() const { return Parameter(-1); }
|
||||
|
||||
inline void Drop(int count);
|
||||
|
||||
inline void Pop();
|
||||
inline void Pop(Register reg);
|
||||
inline void Pop(Operand operand);
|
||||
|
||||
inline void Push(Register reg);
|
||||
inline void Push(Operand operand);
|
||||
inline void Push(Immediate immediate);
|
||||
|
||||
private:
|
||||
static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
||||
static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
|
||||
static const int kContextOffset = StandardFrameConstants::kContextOffset;
|
||||
|
||||
MacroAssembler* masm_;
|
||||
int frame_local_count_;
|
||||
int parameter_count_;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Reference support
|
||||
|
||||
// A reference is a C++ stack-allocated object that keeps an ECMA
|
||||
// reference on the execution stack while in scope. For variables
|
||||
// the reference is empty, indicating that it isn't necessary to
|
||||
// store state on the stack for keeping track of references to those.
|
||||
// For properties, we keep either one (named) or two (indexed) values
|
||||
// on the execution stack to represent the reference.
|
||||
|
||||
class Reference BASE_EMBEDDED {
|
||||
public:
|
||||
// The values of the types is important, see size().
|
||||
enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
|
||||
Reference(CodeGenerator* cgen, Expression* expression);
|
||||
~Reference();
|
||||
|
||||
Expression* expression() const { return expression_; }
|
||||
Type type() const { return type_; }
|
||||
void set_type(Type value) {
|
||||
ASSERT(type_ == ILLEGAL);
|
||||
type_ = value;
|
||||
}
|
||||
|
||||
// The size of the reference or -1 if the reference is illegal.
|
||||
int size() const { return type_; }
|
||||
|
||||
bool is_illegal() const { return type_ == ILLEGAL; }
|
||||
bool is_slot() const { return type_ == SLOT; }
|
||||
bool is_property() const { return type_ == NAMED || type_ == KEYED; }
|
||||
|
||||
// Return the name. Only valid for named property references.
|
||||
Handle<String> GetName();
|
||||
|
||||
// Generate code to push the value of the reference on top of the
|
||||
// expression stack. The reference is expected to be already on top of
|
||||
// the expression stack, and it is left in place with its value above it.
|
||||
void GetValue(TypeofState typeof_state);
|
||||
|
||||
// Generate code to store the value on top of the expression stack in the
|
||||
// reference. The reference is expected to be immediately below the value
|
||||
// on the expression stack. The stored value is left in place (with the
|
||||
// reference intact below it) to support chained assignments.
|
||||
void SetValue(InitState init_state);
|
||||
|
||||
private:
|
||||
CodeGenerator* cgen_;
|
||||
Expression* expression_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Code generation state
|
||||
|
||||
// The state is passed down the AST by the code generator (and back up, in
|
||||
// the form of the state of the label pair). It is threaded through the
|
||||
// call stack. Constructing a state implicitly pushes it on the owning code
|
||||
// generator's stack of states, and destroying one implicitly pops it.
|
||||
|
||||
class CodeGenState BASE_EMBEDDED {
|
||||
public:
|
||||
// Create an initial code generator state. Destroying the initial state
|
||||
// leaves the code generator with a NULL state.
|
||||
explicit CodeGenState(CodeGenerator* owner);
|
||||
|
||||
// Create a code generator state based on a code generator's current
|
||||
// state. The new state has its own access type and pair of branch
|
||||
// labels, and no reference.
|
||||
CodeGenState(CodeGenerator* owner,
|
||||
TypeofState typeof_state,
|
||||
Label* true_target,
|
||||
Label* false_target);
|
||||
|
||||
// Destroy a code generator state and restore the owning code generator's
|
||||
// previous state.
|
||||
~CodeGenState();
|
||||
|
||||
TypeofState typeof_state() const { return typeof_state_; }
|
||||
Label* true_target() const { return true_target_; }
|
||||
Label* false_target() const { return false_target_; }
|
||||
|
||||
private:
|
||||
CodeGenerator* owner_;
|
||||
TypeofState typeof_state_;
|
||||
Label* true_target_;
|
||||
Label* false_target_;
|
||||
CodeGenState* previous_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// CodeGenerator
|
||||
|
||||
class CodeGenerator: public Visitor {
|
||||
public:
|
||||
// Takes a function literal, generates code for it. This function should only
|
||||
// be called by compiler.cc.
|
||||
static Handle<Code> MakeCode(FunctionLiteral* fun,
|
||||
Handle<Script> script,
|
||||
bool is_eval);
|
||||
|
||||
static void SetFunctionInfo(Handle<JSFunction> fun,
|
||||
int length,
|
||||
int function_token_position,
|
||||
int start_position,
|
||||
int end_position,
|
||||
bool is_expression,
|
||||
bool is_toplevel,
|
||||
Handle<Script> script);
|
||||
|
||||
// Accessors
|
||||
MacroAssembler* masm() { return masm_; }
|
||||
|
||||
VirtualFrame* frame() const { return frame_; }
|
||||
|
||||
CodeGenState* state() { return state_; }
|
||||
void set_state(CodeGenState* state) { state_ = state; }
|
||||
|
||||
void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
|
||||
|
||||
private:
|
||||
// Construction/Destruction
|
||||
CodeGenerator(int buffer_size, Handle<Script> script, bool is_eval);
|
||||
virtual ~CodeGenerator() { delete masm_; }
|
||||
|
||||
// Accessors
|
||||
Scope* scope() const { return scope_; }
|
||||
|
||||
void ProcessDeferred();
|
||||
|
||||
bool is_eval() { return is_eval_; }
|
||||
|
||||
// State
|
||||
bool has_cc() const { return cc_reg_ >= 0; }
|
||||
TypeofState typeof_state() const { return state_->typeof_state(); }
|
||||
Label* true_target() const { return state_->true_target(); }
|
||||
Label* false_target() const { return state_->false_target(); }
|
||||
|
||||
|
||||
// Node visitors.
|
||||
#define DEF_VISIT(type) \
|
||||
void Visit##type(type* node);
|
||||
NODE_LIST(DEF_VISIT)
|
||||
#undef DEF_VISIT
|
||||
|
||||
// Main code generation function
|
||||
void GenCode(FunctionLiteral* fun);
|
||||
|
||||
// The following are used by class Reference.
|
||||
void LoadReference(Reference* ref);
|
||||
void UnloadReference(Reference* ref);
|
||||
|
||||
Operand ContextOperand(Register context, int index) const {
|
||||
return Operand(context, Context::SlotOffset(index));
|
||||
}
|
||||
|
||||
Operand SlotOperand(Slot* slot, Register tmp);
|
||||
|
||||
|
||||
// Expressions
|
||||
Operand GlobalObject() const {
|
||||
return ContextOperand(esi, Context::GLOBAL_INDEX);
|
||||
}
|
||||
|
||||
void LoadCondition(Expression* x,
|
||||
TypeofState typeof_state,
|
||||
Label* true_target,
|
||||
Label* false_target,
|
||||
bool force_cc);
|
||||
void Load(Expression* x, TypeofState typeof_state = NOT_INSIDE_TYPEOF);
|
||||
void LoadGlobal();
|
||||
void LoadGlobalReceiver(Register scratch);
|
||||
|
||||
// Read a value from a slot and leave it on top of the expression stack.
|
||||
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
|
||||
|
||||
// Special code for typeof expressions: Unfortunately, we must
|
||||
// be careful when loading the expression in 'typeof'
|
||||
// expressions. We are not allowed to throw reference errors for
|
||||
// non-existing properties of the global object, so we must make it
|
||||
// look like an explicit property access, instead of an access
|
||||
// through the context chain.
|
||||
void LoadTypeofExpression(Expression* x);
|
||||
|
||||
void ToBoolean(Label* true_target, Label* false_target);
|
||||
|
||||
void GenericBinaryOperation(Token::Value op,
|
||||
const OverwriteMode overwrite_mode = NO_OVERWRITE);
|
||||
|
||||
void Comparison(Condition cc, bool strict = false);
|
||||
|
||||
// Inline small integer literals. To prevent long attacker-controlled byte
|
||||
// sequences, we only inline small Smis.
|
||||
static const int kMaxSmiInlinedBits = 16;
|
||||
bool IsInlineSmi(Literal* literal);
|
||||
void SmiComparison(Condition cc, Handle<Object> value, bool strict = false);
|
||||
void SmiOperation(Token::Value op,
|
||||
Handle<Object> value,
|
||||
bool reversed,
|
||||
OverwriteMode overwrite_mode);
|
||||
|
||||
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
|
||||
|
||||
// Control flow
|
||||
void Branch(bool if_true, Label* L);
|
||||
void CheckStack();
|
||||
void CleanStack(int num_bytes);
|
||||
|
||||
bool CheckForInlineRuntimeCall(CallRuntime* node);
|
||||
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
|
||||
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
|
||||
|
||||
Handle<Code> ComputeCallInitialize(int argc);
|
||||
|
||||
// Declare global variables and functions in the given array of
|
||||
// name/value pairs.
|
||||
void DeclareGlobals(Handle<FixedArray> pairs);
|
||||
|
||||
// Instantiate the function boilerplate.
|
||||
void InstantiateBoilerplate(Handle<JSFunction> boilerplate);
|
||||
|
||||
// Support for type checks.
|
||||
void GenerateIsSmi(ZoneList<Expression*>* args);
|
||||
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
|
||||
void GenerateIsArray(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for arguments.length and arguments[?].
|
||||
void GenerateArgumentsLength(ZoneList<Expression*>* args);
|
||||
void GenerateArgumentsAccess(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for accessing the value field of an object (used by Date).
|
||||
void GenerateValueOf(ZoneList<Expression*>* args);
|
||||
void GenerateSetValueOf(ZoneList<Expression*>* args);
|
||||
|
||||
// Fast support for charCodeAt(n).
|
||||
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
|
||||
|
||||
// Fast support for object equality testing.
|
||||
void GenerateObjectEquals(ZoneList<Expression*>* args);
|
||||
|
||||
|
||||
// Methods and constants for fast case switch statement support.
|
||||
//
|
||||
// Only allow fast-case switch if the range of labels is at most
|
||||
// this factor times the number of case labels.
|
||||
// Value is derived from comparing the size of code generated by the normal
|
||||
// switch code for Smi-labels to the size of a single pointer. If code
|
||||
// quality increases this number should be decreased to match.
|
||||
static const int kFastSwitchMaxOverheadFactor = 5;
|
||||
|
||||
// Minimal number of switch cases required before we allow jump-table
|
||||
// optimization.
|
||||
static const int kFastSwitchMinCaseCount = 5;
|
||||
|
||||
// The limit of the range of a fast-case switch, as a factor of the number
|
||||
// of cases of the switch. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
int FastCaseSwitchMaxOverheadFactor();
|
||||
|
||||
// The minimal number of cases in a switch before the fast-case switch
|
||||
// optimization is enabled. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
int FastCaseSwitchMinCaseCount();
|
||||
|
||||
// Allocate a jump table and create code to jump through it.
|
||||
// Should call GenerateFastCaseSwitchCases to generate the code for
|
||||
// all the cases at the appropriate point.
|
||||
void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
|
||||
int min_index,
|
||||
int range,
|
||||
Label* fail_label,
|
||||
Vector<Label*> case_targets,
|
||||
Vector<Label> case_labels);
|
||||
|
||||
// Generate the code for cases for the fast case switch.
|
||||
// Called by GenerateFastCaseSwitchJumpTable.
|
||||
void GenerateFastCaseSwitchCases(SwitchStatement* node,
|
||||
Vector<Label> case_labels);
|
||||
|
||||
// Fast support for constant-Smi switches.
|
||||
void GenerateFastCaseSwitchStatement(SwitchStatement* node,
|
||||
int min_index,
|
||||
int range,
|
||||
int default_index);
|
||||
|
||||
// Fast support for constant-Smi switches. Tests whether switch statement
|
||||
// permits optimization and calls GenerateFastCaseSwitch if it does.
|
||||
// Returns true if the fast-case switch was generated, and false if not.
|
||||
bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
|
||||
|
||||
|
||||
// Bottle-neck interface to call the Assembler to generate the statement
|
||||
// position. This allows us to easily control whether statement positions
|
||||
// should be generated or not.
|
||||
void RecordStatementPosition(Node* node);
|
||||
|
||||
bool is_eval_; // Tells whether code is generated for eval.
|
||||
Handle<Script> script_;
|
||||
List<DeferredCode*> deferred_;
|
||||
|
||||
// Assembler
|
||||
MacroAssembler* masm_; // to generate code
|
||||
|
||||
// Code generation state
|
||||
Scope* scope_;
|
||||
VirtualFrame* frame_;
|
||||
Condition cc_reg_;
|
||||
CodeGenState* state_;
|
||||
bool is_inside_try_;
|
||||
int break_stack_height_;
|
||||
|
||||
// Labels
|
||||
Label function_return_;
|
||||
|
||||
friend class VirtualFrame;
|
||||
friend class Reference;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CODEGEN_IA32_H_
|
@ -1,76 +0,0 @@
|
||||
// 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_CODEGEN_INL_H_
|
||||
#define V8_CODEGEN_INL_H_
|
||||
|
||||
#include "codegen.h"
|
||||
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Support for "structured" code comments.
|
||||
//
|
||||
// By selecting matching brackets in disassembler output,
|
||||
// code segments can be identified more easily.
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
class Comment BASE_EMBEDDED {
|
||||
public:
|
||||
Comment(MacroAssembler* masm, const char* msg)
|
||||
: masm_(masm),
|
||||
msg_(msg) {
|
||||
masm_->RecordComment(msg);
|
||||
}
|
||||
|
||||
~Comment() {
|
||||
if (msg_[0] == '[')
|
||||
masm_->RecordComment("]");
|
||||
}
|
||||
|
||||
private:
|
||||
MacroAssembler* masm_;
|
||||
const char* msg_;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class Comment BASE_EMBEDDED {
|
||||
public:
|
||||
Comment(MacroAssembler*, const char*) {}
|
||||
};
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CODEGEN_INL_H_
|
@ -1,474 +0,0 @@
|
||||
// Copyright 2007-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 "bootstrapper.h"
|
||||
#include "codegen-inl.h"
|
||||
#include "debug.h"
|
||||
#include "prettyprinter.h"
|
||||
#include "scopeinfo.h"
|
||||
#include "runtime.h"
|
||||
#include "stub-cache.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
DeferredCode::DeferredCode(CodeGenerator* generator)
|
||||
: masm_(generator->masm()),
|
||||
generator_(generator),
|
||||
statement_position_(masm_->last_statement_position()),
|
||||
position_(masm_->last_position()) {
|
||||
generator->AddDeferred(this);
|
||||
#ifdef DEBUG
|
||||
comment_ = "";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::ProcessDeferred() {
|
||||
while (!deferred_.is_empty()) {
|
||||
DeferredCode* code = deferred_.RemoveLast();
|
||||
MacroAssembler* masm = code->masm();
|
||||
// Record position of deferred code stub.
|
||||
if (code->statement_position() != RelocInfo::kNoPosition) {
|
||||
masm->RecordStatementPosition(code->statement_position());
|
||||
}
|
||||
if (code->position() != RelocInfo::kNoPosition) {
|
||||
masm->RecordPosition(code->position());
|
||||
}
|
||||
// Bind labels and generate the code.
|
||||
masm->bind(code->enter());
|
||||
Comment cmnt(masm, code->comment());
|
||||
code->Generate();
|
||||
if (code->exit()->is_bound()) {
|
||||
masm->jmp(code->exit()); // platform independent?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate the code. Takes a function literal, generates code for it, assemble
|
||||
// all the pieces into a Code object. This function is only to be called by
|
||||
// the compiler.cc code.
|
||||
Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit,
|
||||
Handle<Script> script,
|
||||
bool is_eval) {
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
bool print_code = FLAG_print_code && !Bootstrapper::IsActive();
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
bool print_source = false;
|
||||
bool print_ast = false;
|
||||
const char* ftype;
|
||||
|
||||
if (Bootstrapper::IsActive()) {
|
||||
print_source = FLAG_print_builtin_source;
|
||||
print_ast = FLAG_print_builtin_ast;
|
||||
print_code = FLAG_print_builtin_code;
|
||||
ftype = "builtin";
|
||||
} else {
|
||||
print_source = FLAG_print_source;
|
||||
print_ast = FLAG_print_ast;
|
||||
ftype = "user-defined";
|
||||
}
|
||||
|
||||
if (FLAG_trace_codegen || print_source || print_ast) {
|
||||
PrintF("*** Generate code for %s function: ", ftype);
|
||||
flit->name()->ShortPrint();
|
||||
PrintF(" ***\n");
|
||||
}
|
||||
|
||||
if (print_source) {
|
||||
PrintF("--- Source from AST ---\n%s\n", PrettyPrinter().PrintProgram(flit));
|
||||
}
|
||||
|
||||
if (print_ast) {
|
||||
PrintF("--- AST ---\n%s\n", AstPrinter().PrintProgram(flit));
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
// Generate code.
|
||||
const int initial_buffer_size = 4 * KB;
|
||||
CodeGenerator cgen(initial_buffer_size, script, is_eval);
|
||||
cgen.GenCode(flit);
|
||||
if (cgen.HasStackOverflow()) {
|
||||
ASSERT(!Top::has_pending_exception());
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// Process any deferred code.
|
||||
cgen.ProcessDeferred();
|
||||
|
||||
// Allocate and install the code.
|
||||
CodeDesc desc;
|
||||
cgen.masm()->GetCode(&desc);
|
||||
ScopeInfo<> sinfo(flit->scope());
|
||||
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION);
|
||||
Handle<Code> code = Factory::NewCode(desc, &sinfo, flags);
|
||||
|
||||
// Add unresolved entries in the code to the fixup list.
|
||||
Bootstrapper::AddFixup(*code, cgen.masm());
|
||||
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
if (print_code) {
|
||||
// Print the source code if available.
|
||||
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
|
||||
PrintF("--- Raw source ---\n");
|
||||
StringInputBuffer stream(String::cast(script->source()));
|
||||
stream.Seek(flit->start_position());
|
||||
// flit->end_position() points to the last character in the stream. We
|
||||
// need to compensate by adding one to calculate the length.
|
||||
int source_len = flit->end_position() - flit->start_position() + 1;
|
||||
for (int i = 0; i < source_len; i++) {
|
||||
if (stream.has_more()) PrintF("%c", stream.GetNext());
|
||||
}
|
||||
PrintF("\n\n");
|
||||
}
|
||||
PrintF("--- Code ---\n");
|
||||
code->Disassemble();
|
||||
}
|
||||
#endif // ENABLE_DISASSEMBLER
|
||||
|
||||
if (!code.is_null()) {
|
||||
Counters::total_compiled_code_size.Increment(code->instruction_size());
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
// Sets the function info on a function.
|
||||
// The start_position points to the first '(' character after the function name
|
||||
// in the full script source. When counting characters in the script source the
|
||||
// the first character is number 0 (not 1).
|
||||
void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun,
|
||||
int length,
|
||||
int function_token_position,
|
||||
int start_position,
|
||||
int end_position,
|
||||
bool is_expression,
|
||||
bool is_toplevel,
|
||||
Handle<Script> script) {
|
||||
fun->shared()->set_length(length);
|
||||
fun->shared()->set_formal_parameter_count(length);
|
||||
fun->shared()->set_script(*script);
|
||||
fun->shared()->set_function_token_position(function_token_position);
|
||||
fun->shared()->set_start_position(start_position);
|
||||
fun->shared()->set_end_position(end_position);
|
||||
fun->shared()->set_is_expression(is_expression);
|
||||
fun->shared()->set_is_toplevel(is_toplevel);
|
||||
}
|
||||
|
||||
|
||||
static Handle<Code> ComputeLazyCompile(int argc) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeLazyCompile(argc), Code);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) {
|
||||
// Determine if the function can be lazily compiled. This is
|
||||
// necessary to allow some of our builtin JS files to be lazily
|
||||
// compiled. These builtins cannot be handled lazily by the parser,
|
||||
// since we have to know if a function uses the special natives
|
||||
// syntax, which is something the parser records.
|
||||
bool allow_lazy = node->AllowsLazyCompilation();
|
||||
|
||||
// Generate code
|
||||
Handle<Code> code;
|
||||
if (FLAG_lazy && allow_lazy) {
|
||||
code = ComputeLazyCompile(node->num_parameters());
|
||||
} else {
|
||||
code = MakeCode(node, script_, false);
|
||||
|
||||
// Check for stack-overflow exception.
|
||||
if (code.is_null()) {
|
||||
SetStackOverflow();
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
|
||||
// Function compilation complete.
|
||||
LOG(CodeCreateEvent("Function", *code, *node->name()));
|
||||
}
|
||||
|
||||
// Create a boilerplate function.
|
||||
Handle<JSFunction> function =
|
||||
Factory::NewFunctionBoilerplate(node->name(),
|
||||
node->materialized_literal_count(),
|
||||
node->contains_array_literal(),
|
||||
code);
|
||||
CodeGenerator::SetFunctionInfo(function, node->num_parameters(),
|
||||
node->function_token_position(),
|
||||
node->start_position(), node->end_position(),
|
||||
node->is_expression(), false, script_);
|
||||
|
||||
// Notify debugger that a new function has been added.
|
||||
Debugger::OnNewFunction(function);
|
||||
|
||||
// Set the expected number of properties for instances and return
|
||||
// the resulting function.
|
||||
SetExpectedNofPropertiesFromEstimate(function,
|
||||
node->expected_property_count());
|
||||
return function;
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> CodeGenerator::ComputeCallInitialize(int argc) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc), Code);
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
|
||||
int length = declarations->length();
|
||||
int globals = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
Declaration* node = declarations->at(i);
|
||||
Variable* var = node->proxy()->var();
|
||||
Slot* slot = var->slot();
|
||||
|
||||
// If it was not possible to allocate the variable at compile
|
||||
// time, we need to "declare" it at runtime to make sure it
|
||||
// actually exists in the local context.
|
||||
if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) {
|
||||
VisitDeclaration(node);
|
||||
} else {
|
||||
// Count global variables and functions for later processing
|
||||
globals++;
|
||||
}
|
||||
}
|
||||
|
||||
// Return in case of no declared global functions or variables.
|
||||
if (globals == 0) return;
|
||||
|
||||
// Compute array of global variable and function declarations.
|
||||
Handle<FixedArray> array = Factory::NewFixedArray(2 * globals, TENURED);
|
||||
for (int j = 0, i = 0; i < length; i++) {
|
||||
Declaration* node = declarations->at(i);
|
||||
Variable* var = node->proxy()->var();
|
||||
Slot* slot = var->slot();
|
||||
|
||||
if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) {
|
||||
// Skip - already processed.
|
||||
} else {
|
||||
array->set(j++, *(var->name()));
|
||||
if (node->fun() == NULL) {
|
||||
if (var->mode() == Variable::CONST) {
|
||||
// In case this is const property use the hole.
|
||||
array->set_the_hole(j++);
|
||||
} else {
|
||||
array->set_undefined(j++);
|
||||
}
|
||||
} else {
|
||||
Handle<JSFunction> function = BuildBoilerplate(node->fun());
|
||||
// Check for stack-overflow exception.
|
||||
if (HasStackOverflow()) return;
|
||||
array->set(j++, *function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke the platform-dependent code generator to do the actual
|
||||
// declaration the global variables and functions.
|
||||
DeclareGlobals(array);
|
||||
}
|
||||
|
||||
|
||||
struct InlineRuntimeLUT {
|
||||
void (CodeGenerator::*method)(ZoneList<Expression*>*);
|
||||
const char* name;
|
||||
};
|
||||
|
||||
|
||||
bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
|
||||
ZoneList<Expression*>* args = node->arguments();
|
||||
// Special cases: These 'runtime calls' manipulate the current
|
||||
// frame and are only used 1 or two places, so we generate them
|
||||
// inline instead of generating calls to them. They are used
|
||||
// for implementing Function.prototype.call() and
|
||||
// Function.prototype.apply().
|
||||
static const InlineRuntimeLUT kInlineRuntimeLUT[] = {
|
||||
{&v8::internal::CodeGenerator::GenerateIsSmi,
|
||||
"_IsSmi"},
|
||||
{&v8::internal::CodeGenerator::GenerateIsNonNegativeSmi,
|
||||
"_IsNonNegativeSmi"},
|
||||
{&v8::internal::CodeGenerator::GenerateIsArray,
|
||||
"_IsArray"},
|
||||
{&v8::internal::CodeGenerator::GenerateArgumentsLength,
|
||||
"_ArgumentsLength"},
|
||||
{&v8::internal::CodeGenerator::GenerateArgumentsAccess,
|
||||
"_Arguments"},
|
||||
{&v8::internal::CodeGenerator::GenerateValueOf,
|
||||
"_ValueOf"},
|
||||
{&v8::internal::CodeGenerator::GenerateSetValueOf,
|
||||
"_SetValueOf"},
|
||||
{&v8::internal::CodeGenerator::GenerateFastCharCodeAt,
|
||||
"_FastCharCodeAt"},
|
||||
{&v8::internal::CodeGenerator::GenerateObjectEquals,
|
||||
"_ObjectEquals"}
|
||||
};
|
||||
if (node->name()->length() > 0 && node->name()->Get(0) == '_') {
|
||||
for (unsigned i = 0;
|
||||
i < sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
|
||||
i++) {
|
||||
const InlineRuntimeLUT* entry = kInlineRuntimeLUT + i;
|
||||
if (node->name()->IsEqualTo(CStrVector(entry->name))) {
|
||||
((*this).*(entry->method))(args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateFastCaseSwitchStatement(SwitchStatement* node,
|
||||
int min_index,
|
||||
int range,
|
||||
int default_index) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
// Label pointer per number in range
|
||||
SmartPointer<Label*> case_targets(NewArray<Label*>(range));
|
||||
|
||||
// Label per switch case
|
||||
SmartPointer<Label> case_labels(NewArray<Label>(length));
|
||||
|
||||
Label* fail_label = default_index >= 0 ? &(case_labels[default_index])
|
||||
: node->break_target();
|
||||
|
||||
// Populate array of label pointers for each number in the range.
|
||||
// Initally put the failure label everywhere.
|
||||
for (int i = 0; i < range; i++) {
|
||||
case_targets[i] = fail_label;
|
||||
}
|
||||
|
||||
// Overwrite with label of a case for the number value of that case.
|
||||
// (In reverse order, so that if the same label occurs twice, the
|
||||
// first one wins).
|
||||
for (int i = length-1; i >= 0 ; i--) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (!clause->is_default()) {
|
||||
Object* label_value = *(clause->label()->AsLiteral()->handle());
|
||||
int case_value = Smi::cast(label_value)->value();
|
||||
case_targets[case_value - min_index] = &(case_labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateFastCaseSwitchJumpTable(node,
|
||||
min_index,
|
||||
range,
|
||||
fail_label,
|
||||
Vector<Label*>(*case_targets, range),
|
||||
Vector<Label>(*case_labels, length));
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateFastCaseSwitchCases(
|
||||
SwitchStatement* node,
|
||||
Vector<Label> case_labels) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Comment cmnt(masm(), "[ Case clause");
|
||||
masm()->bind(&(case_labels[i]));
|
||||
VisitStatements(cases->at(i)->statements());
|
||||
}
|
||||
|
||||
masm()->bind(node->break_target());
|
||||
}
|
||||
|
||||
|
||||
bool CodeGenerator::TryGenerateFastCaseSwitchStatement(SwitchStatement* node) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
if (length < FastCaseSwitchMinCaseCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test whether fast-case should be used.
|
||||
int default_index = -1;
|
||||
int min_index = Smi::kMaxValue;
|
||||
int max_index = Smi::kMinValue;
|
||||
for (int i = 0; i < length; i++) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (clause->is_default()) {
|
||||
if (default_index >= 0) {
|
||||
return false; // More than one default label:
|
||||
// Defer to normal case for error.
|
||||
}
|
||||
default_index = i;
|
||||
} else {
|
||||
Expression* label = clause->label();
|
||||
Literal* literal = label->AsLiteral();
|
||||
if (literal == NULL) {
|
||||
return false; // fail fast case
|
||||
}
|
||||
Object* value = *(literal->handle());
|
||||
if (!value->IsSmi()) {
|
||||
return false;
|
||||
}
|
||||
int smi = Smi::cast(value)->value();
|
||||
if (smi < min_index) { min_index = smi; }
|
||||
if (smi > max_index) { max_index = smi; }
|
||||
}
|
||||
}
|
||||
|
||||
// All labels are known to be Smis.
|
||||
int range = max_index - min_index + 1; // |min..max| inclusive
|
||||
if (range / FastCaseSwitchMaxOverheadFactor() > length) {
|
||||
return false; // range of labels is too sparse
|
||||
}
|
||||
|
||||
// Optimization accepted, generate code.
|
||||
GenerateFastCaseSwitchStatement(node, min_index, range, default_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* RuntimeStub::GetName() {
|
||||
return Runtime::FunctionForId(id_)->stub_name;
|
||||
}
|
||||
|
||||
|
||||
void RuntimeStub::Generate(MacroAssembler* masm) {
|
||||
masm->TailCallRuntime(ExternalReference(id_), num_arguments_);
|
||||
}
|
||||
|
||||
|
||||
void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
|
||||
switch (type_) {
|
||||
case READ_LENGTH: GenerateReadLength(masm); break;
|
||||
case READ_ELEMENT: GenerateReadElement(masm); break;
|
||||
case NEW_OBJECT: GenerateNewObject(masm); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,288 +0,0 @@
|
||||
// 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_CODEGEN_H_
|
||||
#define V8_CODEGEN_H_
|
||||
|
||||
#include "ast.h"
|
||||
#include "code-stubs.h"
|
||||
#include "runtime.h"
|
||||
|
||||
// Include the declaration of the architecture defined class CodeGenerator.
|
||||
// The contract to the shared code is that the the CodeGenerator is a subclass
|
||||
// of Visitor and that the following methods are available publicly:
|
||||
// CodeGenerator::MakeCode
|
||||
// CodeGenerator::SetFunctionInfo
|
||||
// CodeGenerator::AddDeferred
|
||||
// CodeGenerator::masm
|
||||
//
|
||||
// These methods are either used privately by the shared code or implemented as
|
||||
// shared code:
|
||||
// CodeGenerator::CodeGenerator
|
||||
// CodeGenerator::~CodeGenerator
|
||||
// CodeGenerator::ProcessDeferred
|
||||
// CodeGenerator::GenCode
|
||||
// CodeGenerator::BuildBoilerplate
|
||||
// CodeGenerator::ComputeCallInitialize
|
||||
// CodeGenerator::ProcessDeclarations
|
||||
// CodeGenerator::DeclareGlobals
|
||||
// CodeGenerator::CheckForInlineRuntimeCall
|
||||
// CodeGenerator::GenerateFastCaseSwitchStatement
|
||||
// CodeGenerator::GenerateFastCaseSwitchCases
|
||||
// CodeGenerator::TryGenerateFastCaseSwitchStatement
|
||||
// CodeGenerator::GenerateFastCaseSwitchJumpTable
|
||||
// CodeGenerator::FastCaseSwitchMinCaseCount
|
||||
// CodeGenerator::FastCaseSwitchMaxOverheadFactor
|
||||
|
||||
#if defined(ARM)
|
||||
#include "codegen-arm.h"
|
||||
#else
|
||||
#include "codegen-ia32.h"
|
||||
#endif
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Use lazy compilation; defaults to true.
|
||||
// NOTE: Do not remove non-lazy compilation until we can properly
|
||||
// install extensions with lazy compilation enabled. At the
|
||||
// moment, this doesn't work for the extensions in Google3,
|
||||
// and we can only run the tests with --nolazy.
|
||||
|
||||
|
||||
// Deferred code objects are small pieces of code that are compiled
|
||||
// out of line. They are used to defer the compilation of uncommon
|
||||
// paths thereby avoiding expensive jumps around uncommon code parts.
|
||||
class DeferredCode: public ZoneObject {
|
||||
public:
|
||||
explicit DeferredCode(CodeGenerator* generator);
|
||||
virtual ~DeferredCode() { }
|
||||
|
||||
virtual void Generate() = 0;
|
||||
|
||||
MacroAssembler* masm() const { return masm_; }
|
||||
CodeGenerator* generator() const { return generator_; }
|
||||
|
||||
Label* enter() { return &enter_; }
|
||||
Label* exit() { return &exit_; }
|
||||
|
||||
int statement_position() const { return statement_position_; }
|
||||
int position() const { return position_; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void set_comment(const char* comment) { comment_ = comment; }
|
||||
const char* comment() const { return comment_; }
|
||||
#else
|
||||
inline void set_comment(const char* comment) { }
|
||||
const char* comment() const { return ""; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// The masm_ field is manipulated when compiling stubs with the
|
||||
// BEGIN_STUB and END_STUB macros. For that reason, it cannot be
|
||||
// constant.
|
||||
MacroAssembler* masm_;
|
||||
|
||||
private:
|
||||
CodeGenerator* const generator_;
|
||||
Label enter_;
|
||||
Label exit_;
|
||||
int statement_position_;
|
||||
int position_;
|
||||
#ifdef DEBUG
|
||||
const char* comment_;
|
||||
#endif
|
||||
DISALLOW_COPY_AND_ASSIGN(DeferredCode);
|
||||
};
|
||||
|
||||
|
||||
// RuntimeStub models code stubs calling entry points in the Runtime class.
|
||||
class RuntimeStub : public CodeStub {
|
||||
public:
|
||||
explicit RuntimeStub(Runtime::FunctionId id, int num_arguments)
|
||||
: id_(id), num_arguments_(num_arguments) { }
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
// Disassembler support. It is useful to be able to print the name
|
||||
// of the runtime function called through this stub.
|
||||
static const char* GetNameFromMinorKey(int minor_key) {
|
||||
return Runtime::FunctionForId(IdField::decode(minor_key))->stub_name;
|
||||
}
|
||||
|
||||
private:
|
||||
Runtime::FunctionId id_;
|
||||
int num_arguments_;
|
||||
|
||||
class ArgumentField: public BitField<int, 0, 16> {};
|
||||
class IdField: public BitField<Runtime::FunctionId, 16, kMinorBits - 16> {};
|
||||
|
||||
Major MajorKey() { return Runtime; }
|
||||
int MinorKey() {
|
||||
return IdField::encode(id_) | ArgumentField::encode(num_arguments_);
|
||||
}
|
||||
|
||||
const char* GetName();
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("RuntimeStub (id %s)\n", Runtime::FunctionForId(id_)->name);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
class StackCheckStub : public CodeStub {
|
||||
public:
|
||||
StackCheckStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
|
||||
const char* GetName() { return "StackCheckStub"; }
|
||||
|
||||
Major MajorKey() { return StackCheck; }
|
||||
int MinorKey() { return 0; }
|
||||
};
|
||||
|
||||
|
||||
class UnarySubStub : public CodeStub {
|
||||
public:
|
||||
UnarySubStub() { }
|
||||
|
||||
private:
|
||||
Major MajorKey() { return UnarySub; }
|
||||
int MinorKey() { return 0; }
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "UnarySubStub"; }
|
||||
};
|
||||
|
||||
|
||||
class CEntryStub : public CodeStub {
|
||||
public:
|
||||
CEntryStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm) { GenerateBody(masm, false); }
|
||||
|
||||
protected:
|
||||
void GenerateBody(MacroAssembler* masm, bool is_debug_break);
|
||||
void GenerateCore(MacroAssembler* masm,
|
||||
Label* throw_normal_exception,
|
||||
Label* throw_out_of_memory_exception,
|
||||
StackFrame::Type frame_type,
|
||||
bool do_gc);
|
||||
void GenerateThrowTOS(MacroAssembler* masm);
|
||||
void GenerateThrowOutOfMemory(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return CEntry; }
|
||||
int MinorKey() { return 0; }
|
||||
|
||||
const char* GetName() { return "CEntryStub"; }
|
||||
};
|
||||
|
||||
|
||||
class CEntryDebugBreakStub : public CEntryStub {
|
||||
public:
|
||||
CEntryDebugBreakStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm) { GenerateBody(masm, true); }
|
||||
|
||||
private:
|
||||
int MinorKey() { return 1; }
|
||||
|
||||
const char* GetName() { return "CEntryDebugBreakStub"; }
|
||||
};
|
||||
|
||||
|
||||
class JSEntryStub : public CodeStub {
|
||||
public:
|
||||
JSEntryStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm) { GenerateBody(masm, false); }
|
||||
|
||||
protected:
|
||||
void GenerateBody(MacroAssembler* masm, bool is_construct);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return JSEntry; }
|
||||
int MinorKey() { return 0; }
|
||||
|
||||
const char* GetName() { return "JSEntryStub"; }
|
||||
};
|
||||
|
||||
|
||||
class JSConstructEntryStub : public JSEntryStub {
|
||||
public:
|
||||
JSConstructEntryStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm) { GenerateBody(masm, true); }
|
||||
|
||||
private:
|
||||
int MinorKey() { return 1; }
|
||||
|
||||
const char* GetName() { return "JSConstructEntryStub"; }
|
||||
};
|
||||
|
||||
|
||||
class ArgumentsAccessStub: public CodeStub {
|
||||
public:
|
||||
enum Type {
|
||||
READ_LENGTH,
|
||||
READ_ELEMENT,
|
||||
NEW_OBJECT
|
||||
};
|
||||
|
||||
explicit ArgumentsAccessStub(Type type) : type_(type) { }
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
Major MajorKey() { return ArgumentsAccess; }
|
||||
int MinorKey() { return type_; }
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
void GenerateReadLength(MacroAssembler* masm);
|
||||
void GenerateReadElement(MacroAssembler* masm);
|
||||
void GenerateNewObject(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "ArgumentsAccessStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("ArgumentsAccessStub (type %d)\n", type_);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_CODEGEN_H_
|
@ -1,180 +0,0 @@
|
||||
// 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 "compilation-cache.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
enum {
|
||||
NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1
|
||||
};
|
||||
|
||||
|
||||
// Keep separate tables for the different entry kinds.
|
||||
static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, };
|
||||
|
||||
|
||||
static Handle<CompilationCacheTable> AllocateTable(int size) {
|
||||
CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size),
|
||||
CompilationCacheTable);
|
||||
}
|
||||
|
||||
|
||||
static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) {
|
||||
Handle<CompilationCacheTable> result;
|
||||
if (tables[entry]->IsUndefined()) {
|
||||
static const int kInitialCacheSize = 64;
|
||||
result = AllocateTable(kInitialCacheSize);
|
||||
tables[entry] = *result;
|
||||
} else {
|
||||
CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]);
|
||||
result = Handle<CompilationCacheTable>(table);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// We only re-use a cached function for some script source code if the
|
||||
// script originates from the same places. This is to avoid issues
|
||||
// when reporting errors, etc.
|
||||
static bool HasOrigin(Handle<JSFunction> boilerplate,
|
||||
Handle<Object> name,
|
||||
int line_offset,
|
||||
int column_offset) {
|
||||
Handle<Script> script =
|
||||
Handle<Script>(Script::cast(boilerplate->shared()->script()));
|
||||
// If the script name isn't set, the boilerplate script should have
|
||||
// an undefined name to have the same origin.
|
||||
if (name.is_null()) {
|
||||
return script->name()->IsUndefined();
|
||||
}
|
||||
// Do the fast bailout checks first.
|
||||
if (line_offset != script->line_offset()->value()) return false;
|
||||
if (column_offset != script->column_offset()->value()) return false;
|
||||
// Check that both names are strings. If not, no match.
|
||||
if (!name->IsString() || !script->name()->IsString()) return false;
|
||||
// Compare the two name strings for equality.
|
||||
return String::cast(*name)->Equals(String::cast(script->name()));
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSFunction> Lookup(Handle<String> source,
|
||||
CompilationCache::Entry entry) {
|
||||
// Make sure not to leak the table into the surrounding handle
|
||||
// scope. Otherwise, we risk keeping old tables around even after
|
||||
// having cleared the cache.
|
||||
Object* result;
|
||||
{ HandleScope scope;
|
||||
Handle<CompilationCacheTable> table = GetTable(entry);
|
||||
result = table->Lookup(*source);
|
||||
}
|
||||
if (result->IsJSFunction()) {
|
||||
return Handle<JSFunction>(JSFunction::cast(result));
|
||||
} else {
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
|
||||
Handle<Object> name,
|
||||
int line_offset,
|
||||
int column_offset) {
|
||||
Handle<JSFunction> result = Lookup(source, SCRIPT);
|
||||
if (result.is_null()) {
|
||||
Counters::compilation_cache_misses.Increment();
|
||||
} else if (HasOrigin(result, name, line_offset, column_offset)) {
|
||||
Counters::compilation_cache_hits.Increment();
|
||||
} else {
|
||||
result = Handle<JSFunction>::null();
|
||||
Counters::compilation_cache_misses.Increment();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
|
||||
Entry entry) {
|
||||
ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL);
|
||||
Handle<JSFunction> result = Lookup(source, entry);
|
||||
if (result.is_null()) {
|
||||
Counters::compilation_cache_misses.Increment();
|
||||
} else {
|
||||
Counters::compilation_cache_hits.Increment();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void CompilationCache::PutFunction(Handle<String> source,
|
||||
Entry entry,
|
||||
Handle<JSFunction> boilerplate) {
|
||||
HandleScope scope;
|
||||
ASSERT(boilerplate->IsBoilerplate());
|
||||
Handle<CompilationCacheTable> table = GetTable(entry);
|
||||
CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
|
||||
}
|
||||
|
||||
|
||||
Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
|
||||
JSRegExp::Flags flags) {
|
||||
Handle<CompilationCacheTable> table = GetTable(REGEXP);
|
||||
Object* result = table->LookupRegExp(*source, flags);
|
||||
if (result->IsFixedArray()) {
|
||||
Counters::regexp_cache_hits.Increment();
|
||||
return Handle<FixedArray>(FixedArray::cast(result));
|
||||
} else {
|
||||
Counters::regexp_cache_misses.Increment();
|
||||
return Handle<FixedArray>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompilationCache::PutRegExp(Handle<String> source,
|
||||
JSRegExp::Flags flags,
|
||||
Handle<FixedArray> data) {
|
||||
HandleScope scope;
|
||||
Handle<CompilationCacheTable> table = GetTable(REGEXP);
|
||||
CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
|
||||
}
|
||||
|
||||
|
||||
void CompilationCache::Clear() {
|
||||
for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
|
||||
tables[i] = Heap::undefined_value();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompilationCache::Iterate(ObjectVisitor* v) {
|
||||
v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,98 +0,0 @@
|
||||
// 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_COMPILATION_CACHE_H_
|
||||
#define V8_COMPILATION_CACHE_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// The compilation cache keeps function boilerplates for compiled
|
||||
// scripts and evals. The boilerplates are looked up using the source
|
||||
// string as the key.
|
||||
class CompilationCache {
|
||||
public:
|
||||
// The same source code string has different compiled code for
|
||||
// scripts and evals. Internally, we use separate caches to avoid
|
||||
// getting the wrong kind of entry when looking up.
|
||||
enum Entry {
|
||||
SCRIPT,
|
||||
EVAL_GLOBAL,
|
||||
EVAL_CONTEXTUAL,
|
||||
REGEXP,
|
||||
LAST_ENTRY = REGEXP
|
||||
};
|
||||
|
||||
// Finds the script function boilerplate for a source
|
||||
// string. Returns an empty handle if the cache doesn't contain a
|
||||
// script for the given source string with the right origin.
|
||||
static Handle<JSFunction> LookupScript(Handle<String> source,
|
||||
Handle<Object> name,
|
||||
int line_offset,
|
||||
int column_offset);
|
||||
|
||||
// Finds the function boilerplate for a source string for
|
||||
// eval. Returns an empty handle if the cache doesn't contain a
|
||||
// script for the given source string.
|
||||
static Handle<JSFunction> LookupEval(Handle<String> source,
|
||||
Entry entry);
|
||||
|
||||
// Returns the regexp data associated with the given regexp if it
|
||||
// is in cache, otherwise an empty handle.
|
||||
static Handle<FixedArray> LookupRegExp(Handle<String> source,
|
||||
JSRegExp::Flags flags);
|
||||
|
||||
// Associate the (source, flags) pair to the given regexp data.
|
||||
// This may overwrite an existing mapping.
|
||||
static void PutRegExp(Handle<String> source,
|
||||
JSRegExp::Flags flags,
|
||||
Handle<FixedArray> data);
|
||||
|
||||
// Associate the (source, kind) pair to the boilerplate. This may
|
||||
// overwrite an existing mapping.
|
||||
static void PutFunction(Handle<String> source,
|
||||
Entry entry,
|
||||
Handle<JSFunction> boilerplate);
|
||||
|
||||
// Clear the cache - also used to initialize the cache at startup.
|
||||
static void Clear();
|
||||
|
||||
// GC support.
|
||||
static void Iterate(ObjectVisitor* v);
|
||||
|
||||
// Notify the cache that a mark-sweep garbage collection is about to
|
||||
// take place. This is used to retire entries from the cache to
|
||||
// avoid keeping them alive too long without using them. For now, we
|
||||
// just clear the cache but we should consider are more
|
||||
// sophisticated LRU scheme.
|
||||
static void MarkCompactPrologue() { Clear(); }
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_COMPILATION_CACHE_H_
|
@ -1,294 +0,0 @@
|
||||
// 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 "bootstrapper.h"
|
||||
#include "codegen-inl.h"
|
||||
#include "compilation-cache.h"
|
||||
#include "compiler.h"
|
||||
#include "debug.h"
|
||||
#include "scopes.h"
|
||||
#include "rewriter.h"
|
||||
#include "usage-analyzer.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
static Handle<Code> MakeCode(FunctionLiteral* literal,
|
||||
Handle<Script> script,
|
||||
bool is_eval) {
|
||||
ASSERT(literal != NULL);
|
||||
|
||||
// Rewrite the AST by introducing .result assignments where needed.
|
||||
if (!Rewriter::Process(literal) || !AnalyzeVariableUsage(literal)) {
|
||||
// Signal a stack overflow by returning a null handle. The stack
|
||||
// overflow exception will be thrown by the caller.
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// Compute top scope and allocate variables. For lazy compilation
|
||||
// the top scope only contains the single lazily compiled function,
|
||||
// so this doesn't re-allocate variables repeatedly.
|
||||
Scope* top = literal->scope();
|
||||
while (top->outer_scope() != NULL) top = top->outer_scope();
|
||||
top->AllocateVariables();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (Bootstrapper::IsActive() ?
|
||||
FLAG_print_builtin_scopes :
|
||||
FLAG_print_scopes) {
|
||||
literal->scope()->Print();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generate code and return it.
|
||||
Handle<Code> result = CodeGenerator::MakeCode(literal, script, is_eval);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSFunction> MakeFunction(bool is_global,
|
||||
bool is_eval,
|
||||
Handle<Script> script,
|
||||
v8::Extension* extension,
|
||||
ScriptDataImpl* pre_data) {
|
||||
ZoneScope zone_scope(DELETE_ON_EXIT);
|
||||
|
||||
// Make sure we have an initial stack limit.
|
||||
StackGuard guard;
|
||||
PostponeInterruptsScope postpone;
|
||||
|
||||
// Notify debugger
|
||||
Debugger::OnBeforeCompile(script);
|
||||
|
||||
// Only allow non-global compiles for eval.
|
||||
ASSERT(is_eval || is_global);
|
||||
|
||||
// Build AST.
|
||||
FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data);
|
||||
|
||||
// Check for parse errors.
|
||||
if (lit == NULL) {
|
||||
ASSERT(Top::has_pending_exception());
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
|
||||
// Measure how long it takes to do the compilation; only take the
|
||||
// rest of the function into account to avoid overlap with the
|
||||
// parsing statistics.
|
||||
StatsRate* rate = is_eval
|
||||
? &Counters::compile_eval
|
||||
: &Counters::compile;
|
||||
StatsRateScope timer(rate);
|
||||
|
||||
// Compile the code.
|
||||
Handle<Code> code = MakeCode(lit, script, is_eval);
|
||||
|
||||
// Check for stack-overflow exceptions.
|
||||
if (code.is_null()) {
|
||||
Top::StackOverflow();
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
|
||||
if (script->name()->IsString()) {
|
||||
SmartPointer<char> data =
|
||||
String::cast(script->name())->ToCString(DISALLOW_NULLS);
|
||||
LOG(CodeCreateEvent(is_eval ? "Eval" : "Script", *code, *data));
|
||||
} else {
|
||||
LOG(CodeCreateEvent(is_eval ? "Eval" : "Script", *code, ""));
|
||||
}
|
||||
|
||||
// Allocate function.
|
||||
Handle<JSFunction> fun =
|
||||
Factory::NewFunctionBoilerplate(lit->name(),
|
||||
lit->materialized_literal_count(),
|
||||
lit->contains_array_literal(),
|
||||
code);
|
||||
|
||||
CodeGenerator::SetFunctionInfo(fun, lit->scope()->num_parameters(),
|
||||
RelocInfo::kNoPosition,
|
||||
lit->start_position(), lit->end_position(),
|
||||
lit->is_expression(), true, script);
|
||||
|
||||
// Hint to the runtime system used when allocating space for initial
|
||||
// property space by setting the expected number of properties for
|
||||
// the instances of the function.
|
||||
SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count());
|
||||
|
||||
// Notify debugger
|
||||
Debugger::OnAfterCompile(script, fun);
|
||||
|
||||
return fun;
|
||||
}
|
||||
|
||||
|
||||
static StaticResource<SafeStringInputBuffer> safe_string_input_buffer;
|
||||
|
||||
|
||||
Handle<JSFunction> Compiler::Compile(Handle<String> source,
|
||||
Handle<Object> script_name,
|
||||
int line_offset, int column_offset,
|
||||
v8::Extension* extension,
|
||||
ScriptDataImpl* input_pre_data) {
|
||||
Counters::total_load_size.Increment(source->length());
|
||||
Counters::total_compile_size.Increment(source->length());
|
||||
|
||||
// The VM is in the COMPILER state until exiting this function.
|
||||
VMState state(COMPILER);
|
||||
|
||||
// Do a lookup in the compilation cache but not for extensions.
|
||||
Handle<JSFunction> result;
|
||||
if (extension == NULL) {
|
||||
result = CompilationCache::LookupScript(source,
|
||||
script_name,
|
||||
line_offset,
|
||||
column_offset);
|
||||
}
|
||||
|
||||
if (result.is_null()) {
|
||||
// No cache entry found. Do pre-parsing and compile the script.
|
||||
ScriptDataImpl* pre_data = input_pre_data;
|
||||
if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) {
|
||||
Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
|
||||
buf->Reset(source.location());
|
||||
pre_data = PreParse(buf.value(), extension);
|
||||
}
|
||||
|
||||
// Create a script object describing the script to be compiled.
|
||||
Handle<Script> script = Factory::NewScript(source);
|
||||
if (!script_name.is_null()) {
|
||||
script->set_name(*script_name);
|
||||
script->set_line_offset(Smi::FromInt(line_offset));
|
||||
script->set_column_offset(Smi::FromInt(column_offset));
|
||||
}
|
||||
|
||||
// Compile the function and add it to the cache.
|
||||
result = MakeFunction(true, false, script, extension, pre_data);
|
||||
if (extension == NULL && !result.is_null()) {
|
||||
CompilationCache::PutFunction(source, CompilationCache::SCRIPT, result);
|
||||
}
|
||||
|
||||
// Get rid of the pre-parsing data (if necessary).
|
||||
if (input_pre_data == NULL && pre_data != NULL) {
|
||||
delete pre_data;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
|
||||
int line_offset,
|
||||
bool is_global) {
|
||||
Counters::total_eval_size.Increment(source->length());
|
||||
Counters::total_compile_size.Increment(source->length());
|
||||
|
||||
// The VM is in the COMPILER state until exiting this function.
|
||||
VMState state(COMPILER);
|
||||
CompilationCache::Entry entry = is_global
|
||||
? CompilationCache::EVAL_GLOBAL
|
||||
: CompilationCache::EVAL_CONTEXTUAL;
|
||||
|
||||
// Do a lookup in the compilation cache; if the entry is not there,
|
||||
// invoke the compiler and add the result to the cache.
|
||||
Handle<JSFunction> result = CompilationCache::LookupEval(source, entry);
|
||||
if (result.is_null()) {
|
||||
// Create a script object describing the script to be compiled.
|
||||
Handle<Script> script = Factory::NewScript(source);
|
||||
script->set_line_offset(Smi::FromInt(line_offset));
|
||||
result = MakeFunction(is_global, true, script, NULL, NULL);
|
||||
if (!result.is_null()) {
|
||||
CompilationCache::PutFunction(source, entry, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared) {
|
||||
ZoneScope zone_scope(DELETE_ON_EXIT);
|
||||
|
||||
// The VM is in the COMPILER state until exiting this function.
|
||||
VMState state(COMPILER);
|
||||
|
||||
// Make sure we have an initial stack limit.
|
||||
StackGuard guard;
|
||||
PostponeInterruptsScope postpone;
|
||||
|
||||
// Compute name, source code and script data.
|
||||
Handle<String> name(String::cast(shared->name()));
|
||||
Handle<Script> script(Script::cast(shared->script()));
|
||||
|
||||
int start_position = shared->start_position();
|
||||
int end_position = shared->end_position();
|
||||
bool is_expression = shared->is_expression();
|
||||
Counters::total_compile_size.Increment(end_position - start_position);
|
||||
|
||||
// Generate the AST for the lazily compiled function. The AST may be
|
||||
// NULL in case of parser stack overflow.
|
||||
FunctionLiteral* lit = MakeLazyAST(script, name,
|
||||
start_position,
|
||||
end_position,
|
||||
is_expression);
|
||||
|
||||
// Check for parse errors.
|
||||
if (lit == NULL) {
|
||||
ASSERT(Top::has_pending_exception());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Measure how long it takes to do the lazy compilation; only take
|
||||
// the rest of the function into account to avoid overlap with the
|
||||
// lazy parsing statistics.
|
||||
StatsRateScope timer(&Counters::compile_lazy);
|
||||
|
||||
// Compile the code.
|
||||
Handle<Code> code = MakeCode(lit, script, false);
|
||||
|
||||
// Check for stack-overflow exception.
|
||||
if (code.is_null()) {
|
||||
Top::StackOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate the code, update the function info, and return the code.
|
||||
LOG(CodeCreateEvent("LazyCompile", *code, *lit->name()));
|
||||
|
||||
// Update the shared function info with the compiled code.
|
||||
shared->set_code(*code);
|
||||
|
||||
// Set the expected number of properties for instances.
|
||||
SetExpectedNofPropertiesFromEstimate(shared, lit->expected_property_count());
|
||||
|
||||
// Check the function has compiled code.
|
||||
ASSERT(shared->is_compiled());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,72 +0,0 @@
|
||||
// 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_COMPILER_H_
|
||||
#define V8_COMPILER_H_
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// The V8 compiler
|
||||
//
|
||||
// General strategy: Source code is translated into an anonymous function w/o
|
||||
// parameters which then can be executed. If the source code contains other
|
||||
// functions, they will be compiled and allocated as part of the compilation
|
||||
// of the source code.
|
||||
|
||||
// Please note this interface returns function boilerplates.
|
||||
// This means you need to call Factory::NewFunctionFromBoilerplate
|
||||
// before you have a real function with context.
|
||||
|
||||
class Compiler : public AllStatic {
|
||||
public:
|
||||
// All routines return a JSFunction.
|
||||
// If an error occurs an exception is raised and
|
||||
// the return handle contains NULL.
|
||||
|
||||
// Compile a String source within a context.
|
||||
static Handle<JSFunction> Compile(Handle<String> source,
|
||||
Handle<Object> script_name,
|
||||
int line_offset, int column_offset,
|
||||
v8::Extension* extension,
|
||||
ScriptDataImpl* script_Data);
|
||||
|
||||
// Compile a String source within a context for Eval.
|
||||
static Handle<JSFunction> CompileEval(Handle<String> source,
|
||||
int line_offset,
|
||||
bool is_global);
|
||||
|
||||
// Compile from function info (used for lazy compilation). Returns
|
||||
// true on success and false if the compilation resulted in a stack
|
||||
// overflow.
|
||||
static bool CompileLazy(Handle<SharedFunctionInfo> shared);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_COMPILER_H_
|
@ -1,208 +0,0 @@
|
||||
// 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_CONSTANTS_ARM_H_
|
||||
#define V8_CONSTANTS_ARM_H_
|
||||
|
||||
namespace assembler { namespace arm {
|
||||
|
||||
// Defines constants and accessor classes to assemble, disassemble and
|
||||
// simulate ARM instructions.
|
||||
//
|
||||
// Constants for specific fields are defined in their respective named enums.
|
||||
// General constants are in an anonymous enum in class Instr.
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
enum Condition {
|
||||
no_condition = -1,
|
||||
EQ = 0,
|
||||
NE = 1,
|
||||
CS = 2,
|
||||
CC = 3,
|
||||
MI = 4,
|
||||
PL = 5,
|
||||
VS = 6,
|
||||
VC = 7,
|
||||
HI = 8,
|
||||
LS = 9,
|
||||
GE = 10,
|
||||
LT = 11,
|
||||
GT = 12,
|
||||
LE = 13,
|
||||
AL = 14,
|
||||
special_condition = 15
|
||||
};
|
||||
|
||||
|
||||
enum Opcode {
|
||||
no_operand = -1,
|
||||
AND = 0,
|
||||
EOR = 1,
|
||||
SUB = 2,
|
||||
RSB = 3,
|
||||
ADD = 4,
|
||||
ADC = 5,
|
||||
SBC = 6,
|
||||
RSC = 7,
|
||||
TST = 8,
|
||||
TEQ = 9,
|
||||
CMP = 10,
|
||||
CMN = 11,
|
||||
ORR = 12,
|
||||
MOV = 13,
|
||||
BIC = 14,
|
||||
MVN = 15
|
||||
};
|
||||
|
||||
|
||||
enum Shift {
|
||||
no_shift = -1,
|
||||
LSL = 0,
|
||||
LSR = 1,
|
||||
ASR = 2,
|
||||
ROR = 3
|
||||
};
|
||||
|
||||
|
||||
enum SoftwareInterruptCodes {
|
||||
// transition to C code
|
||||
call_rt_r5 = 0x10,
|
||||
call_rt_r2 = 0x11,
|
||||
// break point
|
||||
break_point = 0x20
|
||||
};
|
||||
|
||||
|
||||
typedef int32_t instr_t;
|
||||
|
||||
|
||||
// The class Instr enables access to individual fields defined in the ARM
|
||||
// architecture.
|
||||
class Instr {
|
||||
public:
|
||||
enum {
|
||||
kInstrSize = 4,
|
||||
kPCReadOffset = 8
|
||||
};
|
||||
|
||||
// Get the raw instruction bits
|
||||
inline instr_t InstructionBits() const {
|
||||
return *reinterpret_cast<const instr_t*>(this);
|
||||
}
|
||||
|
||||
inline void SetInstructionBits(instr_t value) {
|
||||
*reinterpret_cast<instr_t*>(this) = value;
|
||||
}
|
||||
|
||||
inline int Bit(int nr) const {
|
||||
return (InstructionBits() >> nr) & 1;
|
||||
}
|
||||
|
||||
inline int Bits(int hi, int lo) const {
|
||||
return (InstructionBits() >> lo) & ((2 << (hi - lo)) - 1);
|
||||
}
|
||||
|
||||
|
||||
// Accessors for the different named fields used in the ARM encoding.
|
||||
// Generally applicable fields
|
||||
inline Condition ConditionField() const {
|
||||
return static_cast<Condition>(Bits(31, 28));
|
||||
}
|
||||
inline int TypeField() const { return Bits(27, 25); }
|
||||
|
||||
inline int RnField() const { return Bits(19, 16); }
|
||||
inline int RdField() const { return Bits(15, 12); }
|
||||
|
||||
// Fields used in Data processing instructions
|
||||
inline Opcode OpcodeField() const {
|
||||
return static_cast<Opcode>(Bits(24, 21));
|
||||
}
|
||||
inline int SField() const { return Bit(20); }
|
||||
// with register
|
||||
inline int RmField() const { return Bits(3, 0); }
|
||||
inline Shift ShiftField() const { return static_cast<Shift>(Bits(6, 5)); }
|
||||
inline int RegShiftField() const { return Bit(4); }
|
||||
inline int RsField() const { return Bits(11, 8); }
|
||||
inline int ShiftAmountField() const { return Bits(11, 7); }
|
||||
// with immediate
|
||||
inline int RotateField() const { return Bits(11, 8); }
|
||||
inline int Immed8Field() const { return Bits(7, 0); }
|
||||
|
||||
// Fields used in Load/Store instructions
|
||||
inline int PUField() const { return Bits(24, 23); }
|
||||
inline int BField() const { return Bit(22); }
|
||||
inline int WField() const { return Bit(21); }
|
||||
inline int LField() const { return Bit(20); }
|
||||
// with register uses same fields as Data processing instructions above
|
||||
// with immediate
|
||||
inline int Offset12Field() const { return Bits(11, 0); }
|
||||
// multiple
|
||||
inline int RlistField() const { return Bits(15, 0); }
|
||||
// extra loads and stores
|
||||
inline int SignField() const { return Bit(6); }
|
||||
inline int HField() const { return Bit(5); }
|
||||
inline int ImmedHField() const { return Bits(11, 8); }
|
||||
inline int ImmedLField() const { return Bits(3, 0); }
|
||||
|
||||
// Fields used in Branch instructions
|
||||
inline int LinkField() const { return Bit(24); }
|
||||
inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); }
|
||||
|
||||
// Fields used in Software interrupt instructions
|
||||
inline SoftwareInterruptCodes SwiField() const {
|
||||
return static_cast<SoftwareInterruptCodes>(Bits(23, 0));
|
||||
}
|
||||
|
||||
// Test for special encodings of type 0 instructions (extra loads and stores,
|
||||
// as well as multiplications).
|
||||
inline bool IsSpecialType0() const { return (Bit(7) == 1) && (Bit(4) == 1); }
|
||||
|
||||
// Special accessors that test for existence of a value.
|
||||
inline bool HasS() const { return SField() == 1; }
|
||||
inline bool HasB() const { return BField() == 1; }
|
||||
inline bool HasW() const { return WField() == 1; }
|
||||
inline bool HasL() const { return LField() == 1; }
|
||||
inline bool HasSign() const { return SignField() == 1; }
|
||||
inline bool HasH() const { return HField() == 1; }
|
||||
inline bool HasLink() const { return LinkField() == 1; }
|
||||
|
||||
// Instructions are read of out a code stream. The only way to get a
|
||||
// reference to an instruction is to convert a pointer. There is no way
|
||||
// to allocate or create instances of class Instr.
|
||||
// Use the At(pc) function to create references to Instr.
|
||||
static Instr* At(byte* pc) { return reinterpret_cast<Instr*>(pc); }
|
||||
|
||||
private:
|
||||
// We need to prevent the creation of instances of class Instr.
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace assembler::arm
|
||||
|
||||
#endif // V8_CONSTANTS_ARM_H_
|
@ -1,218 +0,0 @@
|
||||
// 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 "bootstrapper.h"
|
||||
#include "debug.h"
|
||||
#include "scopeinfo.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
JSBuiltinsObject* Context::builtins() {
|
||||
GlobalObject* object = global();
|
||||
if (object->IsJSGlobalObject()) {
|
||||
return JSGlobalObject::cast(object)->builtins();
|
||||
} else {
|
||||
ASSERT(object->IsJSBuiltinsObject());
|
||||
return JSBuiltinsObject::cast(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Context* Context::global_context() {
|
||||
// Fast case: the global object for this context has been set. In
|
||||
// that case, the global object has a direct pointer to the global
|
||||
// context.
|
||||
if (global()->IsGlobalObject()) {
|
||||
return global()->global_context();
|
||||
}
|
||||
// During bootstrapping, the global object might not be set and we
|
||||
// have to search the context chain to find the global context.
|
||||
Context* current = this;
|
||||
while (!current->IsGlobalContext()) {
|
||||
current = Context::cast(JSFunction::cast(current->closure())->context());
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
JSObject* Context::global_proxy() {
|
||||
return global_context()->global_proxy_object();
|
||||
}
|
||||
|
||||
void Context::set_global_proxy(JSObject* object) {
|
||||
global_context()->set_global_proxy_object(object);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
|
||||
int* index_, PropertyAttributes* attributes) {
|
||||
Handle<Context> context(this);
|
||||
|
||||
// The context must be in frame slot 0 (if not debugging).
|
||||
if (kDebug && !Debug::InDebugger()) {
|
||||
StackFrameLocator locator;
|
||||
ASSERT(context->fcontext() ==
|
||||
Context::cast(
|
||||
locator.FindJavaScriptFrame(0)->context())->fcontext());
|
||||
}
|
||||
|
||||
bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
|
||||
*index_ = -1;
|
||||
*attributes = ABSENT;
|
||||
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("Context::Lookup(");
|
||||
name->ShortPrint();
|
||||
PrintF(")\n");
|
||||
}
|
||||
|
||||
do {
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF(" - looking in context %p", *context);
|
||||
if (context->IsGlobalContext()) PrintF(" (global context)");
|
||||
PrintF("\n");
|
||||
}
|
||||
|
||||
// check extension/with object
|
||||
if (context->has_extension()) {
|
||||
Handle<JSObject> extension = Handle<JSObject>(context->extension());
|
||||
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0) {
|
||||
*attributes = extension->GetLocalPropertyAttribute(*name);
|
||||
} else {
|
||||
*attributes = extension->GetPropertyAttribute(*name);
|
||||
}
|
||||
if (*attributes != ABSENT) {
|
||||
// property found
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("=> found property in context object %p\n", *extension);
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->is_function_context()) {
|
||||
// we have context-local slots
|
||||
|
||||
// check non-parameter locals in context
|
||||
Handle<Code> code(context->closure()->code());
|
||||
Variable::Mode mode;
|
||||
int index = ScopeInfo<>::ContextSlotIndex(*code, *name, &mode);
|
||||
ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS);
|
||||
if (index >= 0) {
|
||||
// slot found
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("=> found local in context slot %d (mode = %d)\n",
|
||||
index, mode);
|
||||
}
|
||||
*index_ = index;
|
||||
// Note: Fixed context slots are statically allocated by the compiler.
|
||||
// Statically allocated variables always have a statically known mode,
|
||||
// which is the mode with which they were declared when added to the
|
||||
// scope. Thus, the DYNAMIC mode (which corresponds to dynamically
|
||||
// declared variables that were introduced through declaration nodes)
|
||||
// must not appear here.
|
||||
switch (mode) {
|
||||
case Variable::INTERNAL : // fall through
|
||||
case Variable::VAR : *attributes = NONE; break;
|
||||
case Variable::CONST : *attributes = READ_ONLY; break;
|
||||
case Variable::DYNAMIC : UNREACHABLE(); break;
|
||||
case Variable::TEMPORARY: UNREACHABLE(); break;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// check parameter locals in context
|
||||
int param_index = ScopeInfo<>::ParameterIndex(*code, *name);
|
||||
if (param_index >= 0) {
|
||||
// slot found
|
||||
int index =
|
||||
ScopeInfo<>::ContextSlotIndex(*code,
|
||||
Heap::arguments_shadow_symbol(),
|
||||
NULL);
|
||||
ASSERT(index >= 0); // arguments must exist and be in the heap context
|
||||
Handle<JSObject> arguments(JSObject::cast(context->get(index)));
|
||||
ASSERT(arguments->HasLocalProperty(Heap::length_symbol()));
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("=> found parameter %d in arguments object\n", param_index);
|
||||
}
|
||||
*index_ = param_index;
|
||||
*attributes = NONE;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
// check intermediate context (holding only the function name variable)
|
||||
if (follow_context_chain) {
|
||||
int index = ScopeInfo<>::FunctionContextSlotIndex(*code, *name);
|
||||
if (index >= 0) {
|
||||
// slot found
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("=> found intermediate function in context slot %d\n",
|
||||
index);
|
||||
}
|
||||
*index_ = index;
|
||||
*attributes = READ_ONLY;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// proceed with enclosing context
|
||||
if (context->IsGlobalContext()) {
|
||||
follow_context_chain = false;
|
||||
} else if (context->is_function_context()) {
|
||||
context = Handle<Context>(Context::cast(context->closure()->context()));
|
||||
} else {
|
||||
context = Handle<Context>(context->previous());
|
||||
}
|
||||
} while (follow_context_chain);
|
||||
|
||||
// slot not found
|
||||
if (FLAG_trace_contexts) {
|
||||
PrintF("=> no property/slot found\n");
|
||||
}
|
||||
return Handle<Object>::null();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
bool Context::IsBootstrappingOrContext(Object* object) {
|
||||
// During bootstrapping we allow all objects to pass as
|
||||
// contexts. This is necessary to fix circular dependencies.
|
||||
return Bootstrapper::IsActive() || object->IsContext();
|
||||
}
|
||||
|
||||
|
||||
bool Context::IsBootstrappingOrGlobalObject(Object* object) {
|
||||
// During bootstrapping we allow all objects to pass as global
|
||||
// objects. This is necessary to fix circular dependencies.
|
||||
return Bootstrapper::IsActive() || object->IsGlobalObject();
|
||||
}
|
||||
#endif
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,322 +0,0 @@
|
||||
// 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_CONTEXTS_H_
|
||||
#define V8_CONTEXTS_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
enum ContextLookupFlags {
|
||||
FOLLOW_CONTEXT_CHAIN = 1,
|
||||
FOLLOW_PROTOTYPE_CHAIN = 2,
|
||||
|
||||
DONT_FOLLOW_CHAINS = 0,
|
||||
FOLLOW_CHAINS = FOLLOW_CONTEXT_CHAIN | FOLLOW_PROTOTYPE_CHAIN
|
||||
};
|
||||
|
||||
|
||||
// Heap-allocated activation contexts.
|
||||
//
|
||||
// Contexts are implemented as FixedArray objects; the Context
|
||||
// class is a convenience interface casted on a FixedArray object.
|
||||
//
|
||||
// Note: Context must have no virtual functions and Context objects
|
||||
// must always be allocated via Heap::AllocateContext() or
|
||||
// Factory::NewContext.
|
||||
|
||||
// Comment for special_function_table:
|
||||
// Table for providing optimized/specialized functions.
|
||||
// The array contains triplets [object, general_function, optimized_function].
|
||||
// Primarily added to support built-in optimized variants of
|
||||
// Array.prototype.{push,pop}.
|
||||
|
||||
#define GLOBAL_CONTEXT_FIELDS(V) \
|
||||
V(GLOBAL_PROXY_INDEX, JSObject, global_proxy_object) \
|
||||
V(SECURITY_TOKEN_INDEX, Object, security_token) \
|
||||
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
|
||||
V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
|
||||
V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
|
||||
V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
|
||||
V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
|
||||
V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
|
||||
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
|
||||
V(INITIAL_OBJECT_PROTOTYPE_INDEX, JSObject, initial_object_prototype) \
|
||||
V(CREATE_DATE_FUN_INDEX, JSFunction, create_date_fun) \
|
||||
V(TO_NUMBER_FUN_INDEX, JSFunction, to_number_fun) \
|
||||
V(TO_STRING_FUN_INDEX, JSFunction, to_string_fun) \
|
||||
V(TO_DETAIL_STRING_FUN_INDEX, JSFunction, to_detail_string_fun) \
|
||||
V(TO_OBJECT_FUN_INDEX, JSFunction, to_object_fun) \
|
||||
V(TO_INTEGER_FUN_INDEX, JSFunction, to_integer_fun) \
|
||||
V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
|
||||
V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
|
||||
V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \
|
||||
V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
|
||||
V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
|
||||
V(FUNCTION_MAP_INDEX, Map, function_map) \
|
||||
V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \
|
||||
V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\
|
||||
V(SPECIAL_FUNCTION_TABLE_INDEX, FixedArray, special_function_table) \
|
||||
V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \
|
||||
V(MESSAGE_LISTENERS_INDEX, JSObject, message_listeners) \
|
||||
V(DEBUG_EVENT_LISTENERS_INDEX, JSObject, debug_event_listeners) \
|
||||
V(MAKE_MESSAGE_FUN_INDEX, JSFunction, make_message_fun) \
|
||||
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
|
||||
V(CONFIGURE_GLOBAL_INDEX, JSFunction, configure_global_fun) \
|
||||
V(FUNCTION_CACHE_INDEX, JSObject, function_cache) \
|
||||
V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \
|
||||
V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
|
||||
V(EMPTY_SCRIPT_INDEX, Script, empty_script) \
|
||||
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
|
||||
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
|
||||
V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
|
||||
V(MAP_CACHE_INDEX, Object, map_cache)
|
||||
|
||||
// JSFunctions are pairs (context, function code), sometimes also called
|
||||
// closures. A Context object is used to represent function contexts and
|
||||
// dynamically pushed 'with' contexts (or 'scopes' in ECMA-262 speak).
|
||||
//
|
||||
// At runtime, the contexts build a stack in parallel to the execution
|
||||
// stack, with the top-most context being the current context. All contexts
|
||||
// have the following slots:
|
||||
//
|
||||
// [ closure ] This is the current function. It is the same for all
|
||||
// contexts inside a function. It provides access to the
|
||||
// incoming context (i.e., the outer context, which may
|
||||
// or may not become the current function's context), and
|
||||
// it provides access to the functions code and thus it's
|
||||
// scope information, which in turn contains the names of
|
||||
// statically allocated context slots. The names are needed
|
||||
// for dynamic lookups in the presence of 'with' or 'eval'.
|
||||
//
|
||||
// [ fcontext ] A pointer to the innermost enclosing function context.
|
||||
// It is the same for all contexts *allocated* inside a
|
||||
// function, and the function context's fcontext points
|
||||
// to itself. It is only needed for fast access of the
|
||||
// function context (used for declarations, and static
|
||||
// context slot access).
|
||||
//
|
||||
// [ previous ] A pointer to the previous context. It is NULL for
|
||||
// function contexts, and non-NULL for 'with' contexts.
|
||||
// Used to implement the 'with' statement.
|
||||
//
|
||||
// [ extension ] A pointer to an extension JSObject, or NULL. Used to
|
||||
// implement 'with' statements and dynamic declarations
|
||||
// (through 'eval'). The object in a 'with' statement is
|
||||
// stored in the extension slot of a 'with' context.
|
||||
// Dynamically declared variables/functions are also added
|
||||
// to lazily allocated extension object. Context::Lookup
|
||||
// searches the extension object for properties.
|
||||
//
|
||||
// [ global ] A pointer to the global object. Provided for quick
|
||||
// access to the global object from inside the code (since
|
||||
// we always have a context pointer).
|
||||
//
|
||||
// In addition, function contexts may have statically allocated context slots
|
||||
// to store local variables/functions that are accessed from inner functions
|
||||
// (via static context addresses) or through 'eval' (dynamic context lookups).
|
||||
// Finally, the global context contains additional slots for fast access to
|
||||
// global properties.
|
||||
//
|
||||
// We may be able to simplify the implementation:
|
||||
//
|
||||
// - We may be able to get rid of 'fcontext': We can always use the fact that
|
||||
// previous == NULL for function contexts and so we can search for them. They
|
||||
// are only needed when doing dynamic declarations, and the context chains
|
||||
// tend to be very very short (depth of nesting of 'with' statements). At
|
||||
// the moment we also use it in generated code for context slot accesses -
|
||||
// and there we don't want a loop because of code bloat - but we may not
|
||||
// need it there after all (see comment in codegen_*.cc).
|
||||
//
|
||||
// - If we cannot get rid of fcontext, consider making 'previous' never NULL
|
||||
// except for the global context. This could simplify Context::Lookup.
|
||||
|
||||
class Context: public FixedArray {
|
||||
public:
|
||||
// Conversions.
|
||||
static Context* cast(Object* context) {
|
||||
ASSERT(context->IsContext());
|
||||
return reinterpret_cast<Context*>(context);
|
||||
}
|
||||
|
||||
// The default context slot layout; indices are FixedArray slot indices.
|
||||
enum {
|
||||
// These slots are in all contexts.
|
||||
CLOSURE_INDEX,
|
||||
FCONTEXT_INDEX,
|
||||
PREVIOUS_INDEX,
|
||||
EXTENSION_INDEX,
|
||||
GLOBAL_INDEX,
|
||||
MIN_CONTEXT_SLOTS,
|
||||
|
||||
// These slots are only in global contexts.
|
||||
GLOBAL_PROXY_INDEX = MIN_CONTEXT_SLOTS,
|
||||
SECURITY_TOKEN_INDEX,
|
||||
ARGUMENTS_BOILERPLATE_INDEX,
|
||||
JS_ARRAY_MAP_INDEX,
|
||||
FUNCTION_MAP_INDEX,
|
||||
FUNCTION_INSTANCE_MAP_INDEX,
|
||||
INITIAL_OBJECT_PROTOTYPE_INDEX,
|
||||
BOOLEAN_FUNCTION_INDEX,
|
||||
NUMBER_FUNCTION_INDEX,
|
||||
STRING_FUNCTION_INDEX,
|
||||
OBJECT_FUNCTION_INDEX,
|
||||
ARRAY_FUNCTION_INDEX,
|
||||
DATE_FUNCTION_INDEX,
|
||||
REGEXP_FUNCTION_INDEX,
|
||||
CREATE_DATE_FUN_INDEX,
|
||||
TO_NUMBER_FUN_INDEX,
|
||||
TO_STRING_FUN_INDEX,
|
||||
TO_DETAIL_STRING_FUN_INDEX,
|
||||
TO_OBJECT_FUN_INDEX,
|
||||
TO_INTEGER_FUN_INDEX,
|
||||
TO_UINT32_FUN_INDEX,
|
||||
TO_INT32_FUN_INDEX,
|
||||
TO_BOOLEAN_FUN_INDEX,
|
||||
INSTANTIATE_FUN_INDEX,
|
||||
CONFIGURE_INSTANCE_FUN_INDEX,
|
||||
SPECIAL_FUNCTION_TABLE_INDEX,
|
||||
MESSAGE_LISTENERS_INDEX,
|
||||
DEBUG_EVENT_LISTENERS_INDEX,
|
||||
MAKE_MESSAGE_FUN_INDEX,
|
||||
GET_STACK_TRACE_LINE_INDEX,
|
||||
CONFIGURE_GLOBAL_INDEX,
|
||||
FUNCTION_CACHE_INDEX,
|
||||
RUNTIME_CONTEXT_INDEX,
|
||||
CALL_AS_FUNCTION_DELEGATE_INDEX,
|
||||
EMPTY_SCRIPT_INDEX,
|
||||
SCRIPT_FUNCTION_INDEX,
|
||||
CONTEXT_EXTENSION_FUNCTION_INDEX,
|
||||
OUT_OF_MEMORY_INDEX,
|
||||
MAP_CACHE_INDEX,
|
||||
GLOBAL_CONTEXT_SLOTS
|
||||
};
|
||||
|
||||
// Direct slot access.
|
||||
JSFunction* closure() { return JSFunction::cast(get(CLOSURE_INDEX)); }
|
||||
void set_closure(JSFunction* closure) { set(CLOSURE_INDEX, closure); }
|
||||
|
||||
Context* fcontext() { return Context::cast(get(FCONTEXT_INDEX)); }
|
||||
void set_fcontext(Context* context) { set(FCONTEXT_INDEX, context); }
|
||||
|
||||
Context* previous() {
|
||||
Object* result = unchecked_previous();
|
||||
ASSERT(IsBootstrappingOrContext(result));
|
||||
return reinterpret_cast<Context*>(result);
|
||||
}
|
||||
void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
|
||||
|
||||
bool has_extension() { return unchecked_extension() != NULL; }
|
||||
JSObject* extension() { return JSObject::cast(unchecked_extension()); }
|
||||
void set_extension(JSObject* object) { set(EXTENSION_INDEX, object); }
|
||||
|
||||
GlobalObject* global() {
|
||||
Object* result = get(GLOBAL_INDEX);
|
||||
ASSERT(IsBootstrappingOrGlobalObject(result));
|
||||
return reinterpret_cast<GlobalObject*>(result);
|
||||
}
|
||||
void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); }
|
||||
|
||||
// Returns a JSGlobalProxy object or null.
|
||||
JSObject* global_proxy();
|
||||
void set_global_proxy(JSObject* global);
|
||||
|
||||
// The builtins object.
|
||||
JSBuiltinsObject* builtins();
|
||||
|
||||
// Compute the global context by traversing the context chain.
|
||||
Context* global_context();
|
||||
|
||||
// Tells if this is a function context (as opposed to a 'with' context).
|
||||
bool is_function_context() { return unchecked_previous() == NULL; }
|
||||
|
||||
// Tells whether the global context is marked with out of memory.
|
||||
bool has_out_of_memory() {
|
||||
return global_context()->out_of_memory() == Heap::true_value();
|
||||
}
|
||||
|
||||
// Mark the global context with out of memory.
|
||||
void mark_out_of_memory() {
|
||||
global_context()->set_out_of_memory(Heap::true_value());
|
||||
}
|
||||
|
||||
#define GLOBAL_CONTEXT_FIELD_ACCESSORS(index, type, name) \
|
||||
void set_##name(type* value) { \
|
||||
ASSERT(IsGlobalContext()); \
|
||||
set(index, value); \
|
||||
} \
|
||||
type* name() { \
|
||||
ASSERT(IsGlobalContext()); \
|
||||
return type::cast(get(index)); \
|
||||
}
|
||||
GLOBAL_CONTEXT_FIELDS(GLOBAL_CONTEXT_FIELD_ACCESSORS)
|
||||
#undef GLOBAL_CONTEXT_FIELD_ACCESSORS
|
||||
|
||||
// Lookup the the slot called name, starting with the current context.
|
||||
// There are 4 possible outcomes:
|
||||
//
|
||||
// 1) index_ >= 0 && result->IsContext():
|
||||
// most common case, the result is a Context, and index is the
|
||||
// context slot index, and the slot exists.
|
||||
// attributes == READ_ONLY for the function name variable, NONE otherwise.
|
||||
//
|
||||
// 2) index_ >= 0 && result->IsJSObject():
|
||||
// the result is the JSObject arguments object, the index is the parameter
|
||||
// index, i.e., key into the arguments object, and the property exists.
|
||||
// attributes != ABSENT.
|
||||
//
|
||||
// 3) index_ < 0 && result->IsJSObject():
|
||||
// the result is the JSObject extension context or the global object,
|
||||
// and the name is the property name, and the property exists.
|
||||
// attributes != ABSENT.
|
||||
//
|
||||
// 4) index_ < 0 && result.is_null():
|
||||
// there was no context found with the corresponding property.
|
||||
// attributes == ABSENT.
|
||||
Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags,
|
||||
int* index_, PropertyAttributes* attributes);
|
||||
|
||||
// Code generation support.
|
||||
static int SlotOffset(int index) {
|
||||
return kHeaderSize + index * kPointerSize - kHeapObjectTag;
|
||||
}
|
||||
|
||||
private:
|
||||
// Unchecked access to the slots.
|
||||
Object* unchecked_previous() { return get(PREVIOUS_INDEX); }
|
||||
Object* unchecked_extension() { return get(EXTENSION_INDEX); }
|
||||
|
||||
#ifdef DEBUG
|
||||
// Bootstrapping-aware type checks.
|
||||
static bool IsBootstrappingOrContext(Object* object);
|
||||
static bool IsBootstrappingOrGlobalObject(Object* object);
|
||||
#endif
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CONTEXTS_H_
|
@ -1,93 +0,0 @@
|
||||
// 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_CONVERSIONS_INL_H_
|
||||
#define V8_CONVERSIONS_INL_H_
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h> // required for DBL_MAX and on Win32 for finite()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Extra POSIX/ANSI functions for Win32/MSVC.
|
||||
|
||||
#include "conversions.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// The fast double-to-int conversion routine does not guarantee
|
||||
// rounding towards zero.
|
||||
static inline int FastD2I(double x) {
|
||||
#ifdef __USE_ISOC99
|
||||
// The ISO C99 standard defines the lrint() function which rounds a
|
||||
// double to an integer according to the current rounding direction.
|
||||
return lrint(x);
|
||||
#else
|
||||
// This is incredibly slow on Intel x86. The reason is that rounding
|
||||
// towards zero is implied by the C standard. This means that the
|
||||
// status register of the FPU has to be changed with the 'fldcw'
|
||||
// instruction. This completely stalls the pipeline and takes many
|
||||
// hundreds of clock cycles.
|
||||
return static_cast<int>(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline double DoubleToInteger(double x) {
|
||||
if (isnan(x)) return 0;
|
||||
if (!isfinite(x) || x == 0) return x;
|
||||
return (x >= 0) ? floor(x) : ceil(x);
|
||||
}
|
||||
|
||||
|
||||
int32_t NumberToInt32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToInt32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
uint32_t NumberToUint32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToUint32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
int32_t DoubleToInt32(double x) {
|
||||
int32_t i = FastD2I(x);
|
||||
if (FastI2D(i) == x) return i;
|
||||
static const double two32 = 4294967296.0;
|
||||
static const double two31 = 2147483648.0;
|
||||
if (!isfinite(x) || x == 0) return 0;
|
||||
if (x < 0 || x >= two32) x = fmod(x, two32);
|
||||
x = (x >= 0) ? floor(x) : ceil(x) + two32;
|
||||
return (int32_t) ((x >= two31) ? x - two32 : x);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CONVERSIONS_INL_H_
|
@ -1,702 +0,0 @@
|
||||
// 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 <stdarg.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "conversions-inl.h"
|
||||
#include "factory.h"
|
||||
#include "scanner.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
int HexValue(uc32 c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return c - '0';
|
||||
if ('a' <= c && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if ('A' <= c && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Provide a common interface to getting a character at a certain
|
||||
// index from a char* or a String object.
|
||||
static inline int GetChar(const char* str, int index) {
|
||||
ASSERT(index >= 0 && index < static_cast<int>(strlen(str)));
|
||||
return str[index];
|
||||
}
|
||||
|
||||
|
||||
static inline int GetChar(String* str, int index) {
|
||||
return str->Get(index);
|
||||
}
|
||||
|
||||
|
||||
static inline int GetLength(const char* str) {
|
||||
return strlen(str);
|
||||
}
|
||||
|
||||
|
||||
static inline int GetLength(String* str) {
|
||||
return str->length();
|
||||
}
|
||||
|
||||
|
||||
static inline const char* GetCString(const char* str, int index) {
|
||||
return str + index;
|
||||
}
|
||||
|
||||
|
||||
static inline const char* GetCString(String* str, int index) {
|
||||
char* result = NewArray<char>(str->length() + 1);
|
||||
for (int i = index; i < str->length(); i++) {
|
||||
if (str->Get(i) <= 127) {
|
||||
result[i - index] = static_cast<char>(str->Get(i));
|
||||
} else {
|
||||
result[i - index] = 127; // Force number parsing to fail.
|
||||
}
|
||||
}
|
||||
result[str->length() - index] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static inline void ReleaseCString(const char* original, const char* str) {
|
||||
}
|
||||
|
||||
|
||||
static inline void ReleaseCString(String* original, const char* str) {
|
||||
DeleteArray(const_cast<char *>(str));
|
||||
}
|
||||
|
||||
|
||||
static inline bool IsSpace(const char* str, int index) {
|
||||
ASSERT(index >= 0 && index < static_cast<int>(strlen(str)));
|
||||
return Scanner::kIsWhiteSpace.get(str[index]);
|
||||
}
|
||||
|
||||
|
||||
static inline bool IsSpace(String* str, int index) {
|
||||
return Scanner::kIsWhiteSpace.get(str->Get(index));
|
||||
}
|
||||
|
||||
|
||||
static inline bool SubStringEquals(const char* str,
|
||||
int index,
|
||||
const char* other) {
|
||||
return strncmp(str + index, other, strlen(other)) != 0;
|
||||
}
|
||||
|
||||
|
||||
static inline bool SubStringEquals(String* str, int index, const char* other) {
|
||||
HandleScope scope;
|
||||
int len = strlen(other);
|
||||
int end = index + len < str->length() ? index + len : str->length();
|
||||
Handle<String> slice =
|
||||
Factory::NewStringSlice(Handle<String>(str), index, end);
|
||||
return slice->IsEqualTo(Vector<const char>(other, len));
|
||||
}
|
||||
|
||||
|
||||
// Check if a string should be parsed as an octal number. The string
|
||||
// can be either a char* or a String*.
|
||||
template<class S>
|
||||
static bool ShouldParseOctal(S* s, int i) {
|
||||
int index = i;
|
||||
int len = GetLength(s);
|
||||
if (index < len && GetChar(s, index) != '0') return false;
|
||||
|
||||
// If the first real character (following '0') is not an octal
|
||||
// digit, bail out early. This also takes care of numbers of the
|
||||
// forms 0.xxx and 0exxx by not allowing the first 0 to be
|
||||
// interpreted as an octal.
|
||||
index++;
|
||||
if (index < len) {
|
||||
int d = GetChar(s, index) - '0';
|
||||
if (d < 0 || d > 7) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Traverse all digits (including the first). If there is an octal
|
||||
// prefix which is not a part of a longer decimal prefix, we return
|
||||
// true. Otherwise, false is returned.
|
||||
while (index < len) {
|
||||
int d = GetChar(s, index++) - '0';
|
||||
if (d == 8 || d == 9) return false;
|
||||
if (d < 0 || d > 7) return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
extern "C" double gay_strtod(const char* s00, const char** se);
|
||||
|
||||
|
||||
// Parse an int from a string starting a given index and in a given
|
||||
// radix. The string can be either a char* or a String*.
|
||||
template <class S>
|
||||
static int InternalStringToInt(S* s, int i, int radix, double* value) {
|
||||
int len = GetLength(s);
|
||||
|
||||
// Setup limits for computing the value.
|
||||
ASSERT(2 <= radix && radix <= 36);
|
||||
int lim_0 = '0' + (radix < 10 ? radix : 10);
|
||||
int lim_a = 'a' + (radix - 10);
|
||||
int lim_A = 'A' + (radix - 10);
|
||||
|
||||
// NOTE: The code for computing the value may seem a bit complex at
|
||||
// first glance. It is structured to use 32-bit multiply-and-add
|
||||
// loops as long as possible to avoid loosing precision.
|
||||
|
||||
double v = 0.0;
|
||||
int j;
|
||||
for (j = i; j < len;) {
|
||||
// Parse the longest part of the string starting at index j
|
||||
// possible while keeping the multiplier, and thus the part
|
||||
// itself, within 32 bits.
|
||||
uint32_t part = 0, multiplier = 1;
|
||||
int k;
|
||||
for (k = j; k < len; k++) {
|
||||
int c = GetChar(s, k);
|
||||
if (c >= '0' && c < lim_0) {
|
||||
c = c - '0';
|
||||
} else if (c >= 'a' && c < lim_a) {
|
||||
c = c - 'a' + 10;
|
||||
} else if (c >= 'A' && c < lim_A) {
|
||||
c = c - 'A' + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the value of the part as long as the multiplier fits
|
||||
// in 32 bits. When we can't guarantee that the next iteration
|
||||
// will not overflow the multiplier, we stop parsing the part
|
||||
// by leaving the loop.
|
||||
static const uint32_t kMaximumMultiplier = 0xffffffffU / 36;
|
||||
uint32_t m = multiplier * radix;
|
||||
if (m > kMaximumMultiplier) break;
|
||||
part = part * radix + c;
|
||||
multiplier = m;
|
||||
ASSERT(multiplier > part);
|
||||
}
|
||||
|
||||
// Compute the number of part digits. If no digits were parsed;
|
||||
// we're done parsing the entire string.
|
||||
int digits = k - j;
|
||||
if (digits == 0) break;
|
||||
|
||||
// Update the value and skip the part in the string.
|
||||
ASSERT(multiplier ==
|
||||
pow(static_cast<double>(radix), static_cast<double>(digits)));
|
||||
v = v * multiplier + part;
|
||||
j = k;
|
||||
}
|
||||
|
||||
// If the resulting value is larger than 2^53 the value does not fit
|
||||
// in the mantissa of the double and there is a loss of precision.
|
||||
// When the value is larger than 2^53 the rounding depends on the
|
||||
// code generation. If the code generator spills the double value
|
||||
// it uses 64 bits and if it does not it uses 80 bits.
|
||||
//
|
||||
// If there is a potential for overflow we resort to strtod for
|
||||
// radix 10 numbers to get higher precision. For numbers in another
|
||||
// radix we live with the loss of precision.
|
||||
static const double kPreciseConversionLimit = 9007199254740992.0;
|
||||
if (radix == 10 && v > kPreciseConversionLimit) {
|
||||
const char* cstr = GetCString(s, i);
|
||||
const char* end;
|
||||
v = gay_strtod(cstr, &end);
|
||||
ReleaseCString(s, cstr);
|
||||
}
|
||||
|
||||
*value = v;
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
int StringToInt(String* str, int index, int radix, double* value) {
|
||||
return InternalStringToInt(str, index, radix, value);
|
||||
}
|
||||
|
||||
|
||||
int StringToInt(const char* str, int index, int radix, double* value) {
|
||||
return InternalStringToInt(const_cast<char*>(str), index, radix, value);
|
||||
}
|
||||
|
||||
|
||||
static const double JUNK_STRING_VALUE = OS::nan_value();
|
||||
|
||||
|
||||
// Convert a string to a double value. The string can be either a
|
||||
// char* or a String*.
|
||||
template<class S>
|
||||
static double InternalStringToDouble(S* str,
|
||||
int flags,
|
||||
double empty_string_val) {
|
||||
double result = 0.0;
|
||||
int index = 0;
|
||||
|
||||
int len = GetLength(str);
|
||||
|
||||
// Skip leading spaces.
|
||||
while ((index < len) && IsSpace(str, index)) index++;
|
||||
|
||||
// Is the string empty?
|
||||
if (index >= len) return empty_string_val;
|
||||
|
||||
// Get the first character.
|
||||
uint16_t first = GetChar(str, index);
|
||||
|
||||
// Numbers can only start with '-', '+', '.', 'I' (Infinity), or a digit.
|
||||
if (first != '-' && first != '+' && first != '.' && first != 'I' &&
|
||||
(first > '9' || first < '0')) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
// Compute sign of result based on first character.
|
||||
int sign = 1;
|
||||
if (first == '-') {
|
||||
sign = -1;
|
||||
index++;
|
||||
// String only containing a '-' are junk chars.
|
||||
if (index == len) return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
// do we have a hex number?
|
||||
// (since the string is 0-terminated, it's ok to look one char beyond the end)
|
||||
if ((flags & ALLOW_HEX) != 0 &&
|
||||
(index + 1) < len &&
|
||||
GetChar(str, index) == '0' &&
|
||||
(GetChar(str, index + 1) == 'x' || GetChar(str, index + 1) == 'X')) {
|
||||
index += 2;
|
||||
index = StringToInt(str, index, 16, &result);
|
||||
} else if ((flags & ALLOW_OCTALS) != 0 && ShouldParseOctal(str, index)) {
|
||||
// NOTE: We optimistically try to parse the number as an octal (if
|
||||
// we're allowed to), even though this is not as dictated by
|
||||
// ECMA-262. The reason for doing this is compatibility with IE and
|
||||
// Firefox.
|
||||
index = StringToInt(str, index, 8, &result);
|
||||
} else {
|
||||
const char* cstr = GetCString(str, index);
|
||||
const char* end;
|
||||
// Optimistically parse the number and then, if that fails,
|
||||
// check if it might have been {+,-,}Infinity.
|
||||
result = gay_strtod(cstr, &end);
|
||||
ReleaseCString(str, cstr);
|
||||
if (result != 0.0 || end != cstr) {
|
||||
// It appears that strtod worked
|
||||
index += end - cstr;
|
||||
} else {
|
||||
// Check for {+,-,}Infinity
|
||||
bool is_negative = (GetChar(str, index) == '-');
|
||||
if (GetChar(str, index) == '+' || GetChar(str, index) == '-')
|
||||
index++;
|
||||
if (!SubStringEquals(str, index, "Infinity"))
|
||||
return JUNK_STRING_VALUE;
|
||||
result = is_negative ? -INFINITY : INFINITY;
|
||||
index += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & ALLOW_TRAILING_JUNK) == 0) {
|
||||
// skip trailing spaces
|
||||
while ((index < len) && IsSpace(str, index)) index++;
|
||||
// string ending with junk?
|
||||
if (index < len) return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
return sign * result;
|
||||
}
|
||||
|
||||
|
||||
double StringToDouble(String* str, int flags, double empty_string_val) {
|
||||
return InternalStringToDouble(str, flags, empty_string_val);
|
||||
}
|
||||
|
||||
|
||||
double StringToDouble(const char* str, int flags, double empty_string_val) {
|
||||
return InternalStringToDouble(str, flags, empty_string_val);
|
||||
}
|
||||
|
||||
|
||||
extern "C" char* dtoa(double d, int mode, int ndigits,
|
||||
int* decpt, int* sign, char** rve);
|
||||
|
||||
extern "C" void freedtoa(char* s);
|
||||
|
||||
const char* DoubleToCString(double v, Vector<char> buffer) {
|
||||
StringBuilder builder(buffer.start(), buffer.length());
|
||||
|
||||
switch (fpclassify(v)) {
|
||||
case FP_NAN:
|
||||
builder.AddString("NaN");
|
||||
break;
|
||||
|
||||
case FP_INFINITE:
|
||||
if (v < 0.0) {
|
||||
builder.AddString("-Infinity");
|
||||
} else {
|
||||
builder.AddString("Infinity");
|
||||
}
|
||||
break;
|
||||
|
||||
case FP_ZERO:
|
||||
builder.AddCharacter('0');
|
||||
break;
|
||||
|
||||
default: {
|
||||
int decimal_point;
|
||||
int sign;
|
||||
|
||||
char* decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL);
|
||||
int length = strlen(decimal_rep);
|
||||
|
||||
if (sign) builder.AddCharacter('-');
|
||||
|
||||
if (length <= decimal_point && decimal_point <= 21) {
|
||||
// ECMA-262 section 9.8.1 step 6.
|
||||
builder.AddString(decimal_rep);
|
||||
builder.AddPadding('0', decimal_point - length);
|
||||
|
||||
} else if (0 < decimal_point && decimal_point <= 21) {
|
||||
// ECMA-262 section 9.8.1 step 7.
|
||||
builder.AddSubstring(decimal_rep, decimal_point);
|
||||
builder.AddCharacter('.');
|
||||
builder.AddString(decimal_rep + decimal_point);
|
||||
|
||||
} else if (decimal_point <= 0 && decimal_point > -6) {
|
||||
// ECMA-262 section 9.8.1 step 8.
|
||||
builder.AddString("0.");
|
||||
builder.AddPadding('0', -decimal_point);
|
||||
builder.AddString(decimal_rep);
|
||||
|
||||
} else {
|
||||
// ECMA-262 section 9.8.1 step 9 and 10 combined.
|
||||
builder.AddCharacter(decimal_rep[0]);
|
||||
if (length != 1) {
|
||||
builder.AddCharacter('.');
|
||||
builder.AddString(decimal_rep + 1);
|
||||
}
|
||||
builder.AddCharacter('e');
|
||||
builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
|
||||
int exponent = decimal_point - 1;
|
||||
if (exponent < 0) exponent = -exponent;
|
||||
builder.AddFormatted("%d", exponent);
|
||||
}
|
||||
|
||||
freedtoa(decimal_rep);
|
||||
}
|
||||
}
|
||||
return builder.Finalize();
|
||||
}
|
||||
|
||||
|
||||
const char* IntToCString(int n, Vector<char> buffer) {
|
||||
bool negative = false;
|
||||
if (n < 0) {
|
||||
// We must not negate the most negative int.
|
||||
if (n == kMinInt) return DoubleToCString(n, buffer);
|
||||
negative = true;
|
||||
n = -n;
|
||||
}
|
||||
// Build the string backwards from the least significant digit.
|
||||
int i = buffer.length();
|
||||
buffer[--i] = '\0';
|
||||
do {
|
||||
buffer[--i] = '0' + (n % 10);
|
||||
n /= 10;
|
||||
} while (n);
|
||||
if (negative) buffer[--i] = '-';
|
||||
return buffer.start() + i;
|
||||
}
|
||||
|
||||
|
||||
char* DoubleToFixedCString(double value, int f) {
|
||||
ASSERT(f >= 0);
|
||||
|
||||
bool negative = false;
|
||||
double abs_value = value;
|
||||
if (value < 0) {
|
||||
abs_value = -value;
|
||||
negative = true;
|
||||
}
|
||||
|
||||
if (abs_value >= 1e21) {
|
||||
char arr[100];
|
||||
Vector<char> buffer(arr, ARRAY_SIZE(arr));
|
||||
return StrDup(DoubleToCString(value, buffer));
|
||||
}
|
||||
|
||||
// Find a sufficiently precise decimal representation of n.
|
||||
int decimal_point;
|
||||
int sign;
|
||||
char* decimal_rep = dtoa(abs_value, 3, f, &decimal_point, &sign, NULL);
|
||||
int decimal_rep_length = strlen(decimal_rep);
|
||||
|
||||
// Create a representation that is padded with zeros if needed.
|
||||
int zero_prefix_length = 0;
|
||||
int zero_postfix_length = 0;
|
||||
|
||||
if (decimal_point <= 0) {
|
||||
zero_prefix_length = -decimal_point + 1;
|
||||
decimal_point = 1;
|
||||
}
|
||||
|
||||
if (zero_prefix_length + decimal_rep_length < decimal_point + f) {
|
||||
zero_postfix_length = decimal_point + f - decimal_rep_length -
|
||||
zero_prefix_length;
|
||||
}
|
||||
|
||||
unsigned rep_length =
|
||||
zero_prefix_length + decimal_rep_length + zero_postfix_length;
|
||||
StringBuilder rep_builder(rep_length + 1);
|
||||
rep_builder.AddPadding('0', zero_prefix_length);
|
||||
rep_builder.AddString(decimal_rep);
|
||||
rep_builder.AddPadding('0', zero_postfix_length);
|
||||
char* rep = rep_builder.Finalize();
|
||||
freedtoa(decimal_rep);
|
||||
|
||||
// Create the result string by appending a minus and putting in a
|
||||
// decimal point if needed.
|
||||
unsigned result_size = decimal_point + f + 2;
|
||||
StringBuilder builder(result_size + 1);
|
||||
if (negative) builder.AddCharacter('-');
|
||||
builder.AddSubstring(rep, decimal_point);
|
||||
if (f > 0) {
|
||||
builder.AddCharacter('.');
|
||||
builder.AddSubstring(rep + decimal_point, f);
|
||||
}
|
||||
DeleteArray(rep);
|
||||
return builder.Finalize();
|
||||
}
|
||||
|
||||
|
||||
static char* CreateExponentialRepresentation(char* decimal_rep,
|
||||
int exponent,
|
||||
bool negative,
|
||||
int significant_digits) {
|
||||
bool negative_exponent = false;
|
||||
if (exponent < 0) {
|
||||
negative_exponent = true;
|
||||
exponent = -exponent;
|
||||
}
|
||||
|
||||
// Leave room in the result for appending a minus, for a period, the
|
||||
// letter 'e', a minus or a plus depending on the exponent, and a
|
||||
// three digit exponent.
|
||||
unsigned result_size = significant_digits + 7;
|
||||
StringBuilder builder(result_size + 1);
|
||||
|
||||
if (negative) builder.AddCharacter('-');
|
||||
builder.AddCharacter(decimal_rep[0]);
|
||||
if (significant_digits != 1) {
|
||||
builder.AddCharacter('.');
|
||||
builder.AddString(decimal_rep + 1);
|
||||
builder.AddPadding('0', significant_digits - strlen(decimal_rep));
|
||||
}
|
||||
|
||||
builder.AddCharacter('e');
|
||||
builder.AddCharacter(negative_exponent ? '-' : '+');
|
||||
builder.AddFormatted("%d", exponent);
|
||||
return builder.Finalize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
char* DoubleToExponentialCString(double value, int f) {
|
||||
// f might be -1 to signal that f was undefined in JavaScript.
|
||||
ASSERT(f >= -1 && f <= 20);
|
||||
|
||||
bool negative = false;
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
negative = true;
|
||||
}
|
||||
|
||||
// Find a sufficiently precise decimal representation of n.
|
||||
int decimal_point;
|
||||
int sign;
|
||||
char* decimal_rep = NULL;
|
||||
if (f == -1) {
|
||||
decimal_rep = dtoa(value, 0, 0, &decimal_point, &sign, NULL);
|
||||
f = strlen(decimal_rep) - 1;
|
||||
} else {
|
||||
decimal_rep = dtoa(value, 2, f + 1, &decimal_point, &sign, NULL);
|
||||
}
|
||||
int decimal_rep_length = strlen(decimal_rep);
|
||||
ASSERT(decimal_rep_length > 0);
|
||||
ASSERT(decimal_rep_length <= f + 1);
|
||||
USE(decimal_rep_length);
|
||||
|
||||
int exponent = decimal_point - 1;
|
||||
char* result =
|
||||
CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1);
|
||||
|
||||
freedtoa(decimal_rep);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* DoubleToPrecisionCString(double value, int p) {
|
||||
ASSERT(p >= 1 && p <= 21);
|
||||
|
||||
bool negative = false;
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
negative = true;
|
||||
}
|
||||
|
||||
// Find a sufficiently precise decimal representation of n.
|
||||
int decimal_point;
|
||||
int sign;
|
||||
char* decimal_rep = dtoa(value, 2, p, &decimal_point, &sign, NULL);
|
||||
int decimal_rep_length = strlen(decimal_rep);
|
||||
ASSERT(decimal_rep_length <= p);
|
||||
|
||||
int exponent = decimal_point - 1;
|
||||
|
||||
char* result = NULL;
|
||||
|
||||
if (exponent < -6 || exponent >= p) {
|
||||
result =
|
||||
CreateExponentialRepresentation(decimal_rep, exponent, negative, p);
|
||||
} else {
|
||||
// Use fixed notation.
|
||||
//
|
||||
// Leave room in the result for appending a minus, a period and in
|
||||
// the case where decimal_point is not positive for a zero in
|
||||
// front of the period.
|
||||
unsigned result_size = (decimal_point <= 0)
|
||||
? -decimal_point + p + 3
|
||||
: p + 2;
|
||||
StringBuilder builder(result_size + 1);
|
||||
if (negative) builder.AddCharacter('-');
|
||||
if (decimal_point <= 0) {
|
||||
builder.AddString("0.");
|
||||
builder.AddPadding('0', -decimal_point);
|
||||
builder.AddString(decimal_rep);
|
||||
builder.AddPadding('0', p - decimal_rep_length);
|
||||
} else {
|
||||
const int m = Min(decimal_rep_length, decimal_point);
|
||||
builder.AddSubstring(decimal_rep, m);
|
||||
builder.AddPadding('0', decimal_point - decimal_rep_length);
|
||||
if (decimal_point < p) {
|
||||
builder.AddCharacter('.');
|
||||
const int extra = negative ? 2 : 1;
|
||||
if (decimal_rep_length > decimal_point) {
|
||||
const int len = strlen(decimal_rep + decimal_point);
|
||||
const int n = Min(len, p - (builder.position() - extra));
|
||||
builder.AddSubstring(decimal_rep + decimal_point, n);
|
||||
}
|
||||
builder.AddPadding('0', extra + (p - builder.position()));
|
||||
}
|
||||
}
|
||||
result = builder.Finalize();
|
||||
}
|
||||
|
||||
freedtoa(decimal_rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* DoubleToRadixCString(double value, int radix) {
|
||||
ASSERT(radix >= 2 && radix <= 36);
|
||||
|
||||
// Character array used for conversion.
|
||||
static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
// Buffer for the integer part of the result. 1024 chars is enough
|
||||
// for max integer value in radix 2. We need room for a sign too.
|
||||
static const int kBufferSize = 1100;
|
||||
char integer_buffer[kBufferSize];
|
||||
integer_buffer[kBufferSize - 1] = '\0';
|
||||
|
||||
// Buffer for the decimal part of the result. We only generate up
|
||||
// to kBufferSize - 1 chars for the decimal part.
|
||||
char decimal_buffer[kBufferSize];
|
||||
decimal_buffer[kBufferSize - 1] = '\0';
|
||||
|
||||
// Make sure the value is positive.
|
||||
bool is_negative = value < 0.0;
|
||||
if (is_negative) value = -value;
|
||||
|
||||
// Get the integer part and the decimal part.
|
||||
double integer_part = floor(value);
|
||||
double decimal_part = value - integer_part;
|
||||
|
||||
// Convert the integer part starting from the back. Always generate
|
||||
// at least one digit.
|
||||
int integer_pos = kBufferSize - 2;
|
||||
do {
|
||||
integer_buffer[integer_pos--] =
|
||||
chars[static_cast<int>(fmod(integer_part, radix))];
|
||||
integer_part /= radix;
|
||||
} while (integer_part >= 1.0);
|
||||
// Sanity check.
|
||||
ASSERT(integer_pos > 0);
|
||||
// Add sign if needed.
|
||||
if (is_negative) integer_buffer[integer_pos--] = '-';
|
||||
|
||||
// Convert the decimal part. Repeatedly multiply by the radix to
|
||||
// generate the next char. Never generate more than kBufferSize - 1
|
||||
// chars.
|
||||
//
|
||||
// TODO(1093998): We will often generate a full decimal_buffer of
|
||||
// chars because hitting zero will often not happen. The right
|
||||
// solution would be to continue until the string representation can
|
||||
// be read back and yield the original value. To implement this
|
||||
// efficiently, we probably have to modify dtoa.
|
||||
int decimal_pos = 0;
|
||||
while ((decimal_part > 0.0) && (decimal_pos < kBufferSize - 1)) {
|
||||
decimal_part *= radix;
|
||||
decimal_buffer[decimal_pos++] =
|
||||
chars[static_cast<int>(floor(decimal_part))];
|
||||
decimal_part -= floor(decimal_part);
|
||||
}
|
||||
decimal_buffer[decimal_pos] = '\0';
|
||||
|
||||
// Compute the result size.
|
||||
int integer_part_size = kBufferSize - 2 - integer_pos;
|
||||
// Make room for zero termination.
|
||||
unsigned result_size = integer_part_size + decimal_pos;
|
||||
// If the number has a decimal part, leave room for the period.
|
||||
if (decimal_pos > 0) result_size++;
|
||||
// Allocate result and fill in the parts.
|
||||
StringBuilder builder(result_size + 1);
|
||||
builder.AddSubstring(integer_buffer + integer_pos + 1, integer_part_size);
|
||||
if (decimal_pos > 0) builder.AddCharacter('.');
|
||||
builder.AddSubstring(decimal_buffer, decimal_pos);
|
||||
return builder.Finalize();
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,116 +0,0 @@
|
||||
// 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_CONVERSIONS_H_
|
||||
#define V8_CONVERSIONS_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// The fast double-to-int conversion routine does not guarantee
|
||||
// rounding towards zero.
|
||||
// The result is unspecified if x is infinite or NaN, or if the rounded
|
||||
// integer value is outside the range of type int.
|
||||
static inline int FastD2I(double x);
|
||||
|
||||
|
||||
static inline double FastI2D(int x) {
|
||||
// There is no rounding involved in converting an integer to a
|
||||
// double, so this code should compile to a few instructions without
|
||||
// any FPU pipeline stalls.
|
||||
return static_cast<double>(x);
|
||||
}
|
||||
|
||||
|
||||
static inline double FastUI2D(unsigned x) {
|
||||
// There is no rounding involved in converting an unsigned integer to a
|
||||
// double, so this code should compile to a few instructions without
|
||||
// any FPU pipeline stalls.
|
||||
return static_cast<double>(x);
|
||||
}
|
||||
|
||||
|
||||
// This function should match the exact semantics of ECMA-262 9.4.
|
||||
static inline double DoubleToInteger(double x);
|
||||
|
||||
|
||||
// This function should match the exact semantics of ECMA-262 9.5.
|
||||
static inline int32_t DoubleToInt32(double x);
|
||||
|
||||
|
||||
// This function should match the exact semantics of ECMA-262 9.6.
|
||||
static inline uint32_t DoubleToUint32(double x) {
|
||||
return static_cast<uint32_t>(DoubleToInt32(x));
|
||||
}
|
||||
|
||||
|
||||
// Returns the value (0 .. 15) of a hexadecimal character c.
|
||||
// If c is not a legal hexadecimal character, returns a value < 0.
|
||||
int HexValue(uc32 c);
|
||||
|
||||
|
||||
// Enumeration for allowing octals and ignoring junk when converting
|
||||
// strings to numbers.
|
||||
enum ConversionFlags {
|
||||
NO_FLAGS = 0,
|
||||
ALLOW_HEX = 1,
|
||||
ALLOW_OCTALS = 2,
|
||||
ALLOW_TRAILING_JUNK = 4
|
||||
};
|
||||
|
||||
|
||||
// Convert from Number object to C integer.
|
||||
static inline int32_t NumberToInt32(Object* number);
|
||||
static inline uint32_t NumberToUint32(Object* number);
|
||||
|
||||
|
||||
// Converts a string into a double value according to ECMA-262 9.3.1
|
||||
double StringToDouble(const char* str, int flags, double empty_string_val = 0);
|
||||
double StringToDouble(String* str, int flags, double empty_string_val = 0);
|
||||
|
||||
// Converts a string into an integer.
|
||||
int StringToInt(String* str, int index, int radix, double* value);
|
||||
int StringToInt(const char* str, int index, int radix, double* value);
|
||||
|
||||
// Converts a double to a string value according to ECMA-262 9.8.1.
|
||||
// The buffer should be large enough for any floating point number.
|
||||
// 100 characters is enough.
|
||||
const char* DoubleToCString(double value, Vector<char> buffer);
|
||||
|
||||
// Convert an int to a null-terminated string. The returned string is
|
||||
// located inside the buffer, but not necessarily at the start.
|
||||
const char* IntToCString(int n, Vector<char> buffer);
|
||||
|
||||
// Additional number to string conversions for the number type.
|
||||
// The caller is responsible for calling free on the returned pointer.
|
||||
char* DoubleToFixedCString(double value, int f);
|
||||
char* DoubleToExponentialCString(double value, int f);
|
||||
char* DoubleToPrecisionCString(double value, int f);
|
||||
char* DoubleToRadixCString(double value, int radix);
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CONVERSIONS_H_
|
@ -1,56 +0,0 @@
|
||||
// Copyright 2007-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 "counters.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
CounterLookupCallback StatsTable::lookup_function_ = NULL;
|
||||
|
||||
// Start the timer.
|
||||
void StatsCounterTimer::Start() {
|
||||
if (!counter_.Enabled())
|
||||
return;
|
||||
stop_time_ = 0;
|
||||
start_time_ = OS::Ticks();
|
||||
}
|
||||
|
||||
// Stop the timer and record the results.
|
||||
void StatsCounterTimer::Stop() {
|
||||
if (!counter_.Enabled())
|
||||
return;
|
||||
stop_time_ = OS::Ticks();
|
||||
|
||||
// Compute the delta between start and stop, in milliseconds.
|
||||
int milliseconds = static_cast<int>(stop_time_ - start_time_) / 1000;
|
||||
counter_.Increment(milliseconds);
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,200 +0,0 @@
|
||||
// Copyright 2007-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_COUNTERS_H_
|
||||
#define V8_COUNTERS_H_
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// StatsCounters is an interface for plugging into external
|
||||
// counters for monitoring. Counters can be looked up and
|
||||
// manipulated by name.
|
||||
|
||||
class StatsTable : public AllStatic {
|
||||
public:
|
||||
// Register an application-defined function where
|
||||
// counters can be looked up.
|
||||
static void SetCounterFunction(CounterLookupCallback f) {
|
||||
lookup_function_ = f;
|
||||
}
|
||||
|
||||
static bool HasCounterFunction() {
|
||||
return lookup_function_ != NULL;
|
||||
}
|
||||
|
||||
// Lookup the location of a counter by name. If the lookup
|
||||
// is successful, returns a non-NULL pointer for writing the
|
||||
// value of the counter. Each thread calling this function
|
||||
// may receive a different location to store it's counter.
|
||||
// The return value must not be cached and re-used across
|
||||
// threads, although a single thread is free to cache it.
|
||||
static int *FindLocation(const wchar_t* name) {
|
||||
if (!lookup_function_) return NULL;
|
||||
return lookup_function_(name);
|
||||
}
|
||||
|
||||
private:
|
||||
static CounterLookupCallback lookup_function_;
|
||||
};
|
||||
|
||||
// StatsCounters are dynamically created values which can be tracked in
|
||||
// the StatsTable. They are designed to be lightweight to create and
|
||||
// easy to use.
|
||||
//
|
||||
// Internally, a counter represents a value in a row of a StatsTable.
|
||||
// The row has a 32bit value for each process/thread in the table and also
|
||||
// a name (stored in the table metadata). Since the storage location can be
|
||||
// thread-specific, this class cannot be shared across threads.
|
||||
//
|
||||
// This class is designed to be POD initialized. It will be registered with
|
||||
// the counter system on first use. For example:
|
||||
// StatsCounter c = { L"c:myctr", NULL, false };
|
||||
struct StatsCounter {
|
||||
const wchar_t* name_;
|
||||
int* ptr_;
|
||||
bool lookup_done_;
|
||||
|
||||
// Sets the counter to a specific value.
|
||||
void Set(int value) {
|
||||
int* loc = GetPtr();
|
||||
if (loc) *loc = value;
|
||||
}
|
||||
|
||||
// Increments the counter.
|
||||
void Increment() {
|
||||
int* loc = GetPtr();
|
||||
if (loc) (*loc)++;
|
||||
}
|
||||
|
||||
void Increment(int value) {
|
||||
int* loc = GetPtr();
|
||||
if (loc)
|
||||
(*loc) += value;
|
||||
}
|
||||
|
||||
// Decrements the counter.
|
||||
void Decrement() {
|
||||
int* loc = GetPtr();
|
||||
if (loc) (*loc)--;
|
||||
}
|
||||
|
||||
void Decrement(int value) {
|
||||
int* loc = GetPtr();
|
||||
if (loc) (*loc) -= value;
|
||||
}
|
||||
|
||||
// Is this counter enabled?
|
||||
// Returns false if table is full.
|
||||
bool Enabled() {
|
||||
return GetPtr() != NULL;
|
||||
}
|
||||
|
||||
// Get the internal pointer to the counter. This is used
|
||||
// by the code generator to emit code that manipulates a
|
||||
// given counter without calling the runtime system.
|
||||
int* GetInternalPointer() {
|
||||
int* loc = GetPtr();
|
||||
ASSERT(loc != NULL);
|
||||
return loc;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Returns the cached address of this counter location.
|
||||
int* GetPtr() {
|
||||
if (lookup_done_)
|
||||
return ptr_;
|
||||
lookup_done_ = true;
|
||||
ptr_ = StatsTable::FindLocation(name_);
|
||||
return ptr_;
|
||||
}
|
||||
};
|
||||
|
||||
// StatsCounterTimer t = { { L"t:foo", NULL, false }, 0, 0 };
|
||||
struct StatsCounterTimer {
|
||||
StatsCounter counter_;
|
||||
|
||||
int64_t start_time_;
|
||||
int64_t stop_time_;
|
||||
|
||||
// Start the timer.
|
||||
void Start();
|
||||
|
||||
// Stop the timer and record the results.
|
||||
void Stop();
|
||||
|
||||
// Returns true if the timer is running.
|
||||
bool Running() {
|
||||
return counter_.Enabled() && start_time_ != 0 && stop_time_ == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// A StatsRate is a combination of both a timer and a counter so that
|
||||
// several statistics can be produced:
|
||||
// min, max, avg, count, total
|
||||
//
|
||||
// For example:
|
||||
// StatsCounter c = { { { L"t:myrate", NULL, false }, 0, 0 },
|
||||
// { L"c:myrate", NULL, false } };
|
||||
struct StatsRate {
|
||||
StatsCounterTimer timer_;
|
||||
StatsCounter counter_;
|
||||
|
||||
// Starts the rate timer.
|
||||
void Start() {
|
||||
timer_.Start();
|
||||
}
|
||||
|
||||
// Stops the rate and records the time.
|
||||
void Stop() {
|
||||
if (timer_.Running()) {
|
||||
timer_.Stop();
|
||||
counter_.Increment();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Helper class for scoping a rate.
|
||||
class StatsRateScope BASE_EMBEDDED {
|
||||
public:
|
||||
explicit StatsRateScope(StatsRate* rate) :
|
||||
rate_(rate) {
|
||||
rate_->Start();
|
||||
}
|
||||
~StatsRateScope() {
|
||||
rate_->Stop();
|
||||
}
|
||||
private:
|
||||
StatsRate* rate_;
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_COUNTERS_H_
|
@ -1,124 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// CPU specific code for arm independent of OS goes here.
|
||||
#if defined(__arm__)
|
||||
#include <sys/syscall.h> // for cache flushing.
|
||||
#endif
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
void CPU::Setup() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void CPU::FlushICache(void* start, size_t size) {
|
||||
#if !defined (__arm__)
|
||||
// Not generating ARM instructions for C-code. This means that we are
|
||||
// building an ARM emulator based target. No I$ flushes are necessary.
|
||||
#else
|
||||
// Ideally, we would call
|
||||
// syscall(__ARM_NR_cacheflush, start,
|
||||
// reinterpret_cast<intptr_t>(start) + size, 0);
|
||||
// however, syscall(int, ...) is not supported on all platforms, especially
|
||||
// not when using EABI, so we call the __ARM_NR_cacheflush syscall directly.
|
||||
|
||||
register uint32_t beg asm("a1") = reinterpret_cast<uint32_t>(start);
|
||||
register uint32_t end asm("a2") =
|
||||
reinterpret_cast<uint32_t>(start) + size;
|
||||
register uint32_t flg asm("a3") = 0;
|
||||
#ifdef __ARM_EABI__
|
||||
register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
|
||||
#if defined (__arm__) && !defined(__thumb__)
|
||||
// __arm__ may be defined in thumb mode.
|
||||
asm volatile(
|
||||
"swi 0x0"
|
||||
: "=r" (beg)
|
||||
: "0" (beg), "r" (end), "r" (flg), "r" (scno));
|
||||
#else
|
||||
asm volatile(
|
||||
"@ Enter ARM Mode \n\t"
|
||||
"adr r3, 1f \n\t"
|
||||
"bx r3 \n\t"
|
||||
".ALIGN 4 \n\t"
|
||||
".ARM \n"
|
||||
"1: swi 0x0 \n\t"
|
||||
"@ Enter THUMB Mode\n\t"
|
||||
"adr r3, 2f+1 \n\t"
|
||||
"bx r3 \n\t"
|
||||
".THUMB \n"
|
||||
"2: \n\t"
|
||||
: "=r" (beg)
|
||||
: "0" (beg), "r" (end), "r" (flg), "r" (scno)
|
||||
: "r3");
|
||||
#endif
|
||||
#else
|
||||
#if defined (__arm__) && !defined(__thumb__)
|
||||
// __arm__ may be defined in thumb mode.
|
||||
asm volatile(
|
||||
"swi %1"
|
||||
: "=r" (beg)
|
||||
: "i" (__ARM_NR_cacheflush), "0" (beg), "r" (end), "r" (flg));
|
||||
#else
|
||||
// Do not use the value of __ARM_NR_cacheflush in the inline assembly
|
||||
// below, because the thumb mode value would be used, which would be
|
||||
// wrong, since we switch to ARM mode before executing the swi instruction
|
||||
asm volatile(
|
||||
"@ Enter ARM Mode \n\t"
|
||||
"adr r3, 1f \n\t"
|
||||
"bx r3 \n\t"
|
||||
".ALIGN 4 \n\t"
|
||||
".ARM \n"
|
||||
"1: swi 0x9f0002 \n"
|
||||
"@ Enter THUMB Mode\n\t"
|
||||
"adr r3, 2f+1 \n\t"
|
||||
"bx r3 \n\t"
|
||||
".THUMB \n"
|
||||
"2: \n\t"
|
||||
: "=r" (beg)
|
||||
: "0" (beg), "r" (end), "r" (flg)
|
||||
: "r3");
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CPU::DebugBreak() {
|
||||
#if !defined (__arm__)
|
||||
UNIMPLEMENTED(); // when building ARM emulator target
|
||||
#else
|
||||
asm volatile("bkpt 0");
|
||||
#endif
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,64 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// CPU specific code for ia32 independent of OS goes here.
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
void CPU::Setup() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void CPU::FlushICache(void* start, size_t size) {
|
||||
// No need to flush the instruction cache on Intel. On Intel instruction
|
||||
// cache flushing is only necessary when multiple cores running the same
|
||||
// code simultaneously. V8 (and JavaScript) is single threaded and when code
|
||||
// is patched on an intel CPU the core performing the patching will have its
|
||||
// own instruction cache updated automatically.
|
||||
|
||||
// If flushing of the instruction cache becomes necessary Windows have the
|
||||
// API function FlushInstructionCache.
|
||||
}
|
||||
|
||||
|
||||
void CPU::DebugBreak() {
|
||||
#ifdef WIN32
|
||||
// To avoid Visual Studio runtime support the following code can be used
|
||||
// instead
|
||||
// __asm { int 3 }
|
||||
__debugbreak();
|
||||
#else
|
||||
asm("int $3");
|
||||
#endif
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,64 +0,0 @@
|
||||
// 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 module contains the architecture-specific code. This make the rest of
|
||||
// the code less dependent on differences between different processor
|
||||
// architecture.
|
||||
// The classes have the same definition for all architectures. The
|
||||
// implementation for a particular architecture is put in cpu_<arch>.cc.
|
||||
// The build system then uses the implementation for the target architecture.
|
||||
//
|
||||
|
||||
#ifndef V8_CPU_H_
|
||||
#define V8_CPU_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CPU
|
||||
//
|
||||
// This class has static methods for the architecture specific functions. Add
|
||||
// methods here to cope with differences between the supported architectures.
|
||||
//
|
||||
// For each architecture the file cpu_<arch>.cc contains the implementation of
|
||||
// these functions.
|
||||
|
||||
class CPU : public AllStatic {
|
||||
public:
|
||||
// Initializes the cpu architecture support. Called once at VM startup.
|
||||
static void Setup();
|
||||
|
||||
// Flush instruction cache.
|
||||
static void FlushICache(void* start, size_t size);
|
||||
|
||||
// Try to activate a system level debugger.
|
||||
static void DebugBreak();
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CPU_H_
|
@ -1,120 +0,0 @@
|
||||
// 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 <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
|
||||
#include "d8.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
|
||||
|
||||
class ReadLineEditor: public LineEditor {
|
||||
public:
|
||||
ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { }
|
||||
virtual i::SmartPointer<char> Prompt(const char* prompt);
|
||||
virtual bool Open();
|
||||
virtual bool Close();
|
||||
virtual void AddHistory(const char* str);
|
||||
private:
|
||||
static char** AttemptedCompletion(const char* text, int start, int end);
|
||||
static char* CompletionGenerator(const char* text, int state);
|
||||
static char kWordBreakCharacters[];
|
||||
};
|
||||
|
||||
|
||||
static ReadLineEditor read_line_editor;
|
||||
char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
|
||||
'\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
|
||||
'\0'};
|
||||
|
||||
|
||||
bool ReadLineEditor::Open() {
|
||||
rl_initialize();
|
||||
rl_attempted_completion_function = AttemptedCompletion;
|
||||
rl_completer_word_break_characters = kWordBreakCharacters;
|
||||
rl_bind_key('\t', rl_complete);
|
||||
using_history();
|
||||
return read_history(Shell::kHistoryFileName) == 0;
|
||||
}
|
||||
|
||||
|
||||
bool ReadLineEditor::Close() {
|
||||
return write_history(Shell::kHistoryFileName) == 0;
|
||||
}
|
||||
|
||||
|
||||
i::SmartPointer<char> ReadLineEditor::Prompt(const char* prompt) {
|
||||
char* result = readline(prompt);
|
||||
return i::SmartPointer<char>(result);
|
||||
}
|
||||
|
||||
|
||||
void ReadLineEditor::AddHistory(const char* str) {
|
||||
add_history(str);
|
||||
}
|
||||
|
||||
|
||||
char** ReadLineEditor::AttemptedCompletion(const char* text,
|
||||
int start,
|
||||
int end) {
|
||||
char** result = rl_completion_matches(text, CompletionGenerator);
|
||||
rl_attempted_completion_over = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* ReadLineEditor::CompletionGenerator(const char* text, int state) {
|
||||
static unsigned current_index;
|
||||
static Persistent<Array> current_completions;
|
||||
if (state == 0) {
|
||||
i::SmartPointer<char> full_text(strndup(rl_line_buffer, rl_point));
|
||||
HandleScope scope;
|
||||
Handle<Array> completions =
|
||||
Shell::GetCompletions(String::New(text), String::New(*full_text));
|
||||
current_completions = Persistent<Array>::New(completions);
|
||||
current_index = 0;
|
||||
}
|
||||
if (current_index < current_completions->Length()) {
|
||||
HandleScope scope;
|
||||
Handle<Integer> index = Integer::New(current_index);
|
||||
Handle<Value> str_obj = current_completions->Get(index);
|
||||
current_index++;
|
||||
String::Utf8Value str(str_obj);
|
||||
return strdup(*str);
|
||||
} else {
|
||||
current_completions.Dispose();
|
||||
current_completions.Clear();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace v8
|
@ -1,348 +0,0 @@
|
||||
// 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 "d8.h"
|
||||
#include "debug.h"
|
||||
#include "api.h"
|
||||
#include "natives.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
|
||||
|
||||
const char* Shell::kHistoryFileName = ".d8_history";
|
||||
const char* Shell::kPrompt = "d8> ";
|
||||
|
||||
|
||||
LineEditor *LineEditor::first_ = NULL;
|
||||
|
||||
|
||||
LineEditor::LineEditor(Type type, const char* name)
|
||||
: type_(type),
|
||||
name_(name),
|
||||
next_(first_) {
|
||||
first_ = this;
|
||||
}
|
||||
|
||||
|
||||
LineEditor* LineEditor::Get() {
|
||||
LineEditor* current = first_;
|
||||
LineEditor* best = current;
|
||||
while (current != NULL) {
|
||||
if (current->type_ > best->type_)
|
||||
best = current;
|
||||
current = current->next_;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
class DumbLineEditor: public LineEditor {
|
||||
public:
|
||||
DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
|
||||
virtual i::SmartPointer<char> Prompt(const char* prompt);
|
||||
};
|
||||
|
||||
|
||||
static DumbLineEditor dumb_line_editor;
|
||||
|
||||
|
||||
i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) {
|
||||
static const int kBufferSize = 256;
|
||||
char buffer[kBufferSize];
|
||||
printf("%s", prompt);
|
||||
char* str = fgets(buffer, kBufferSize, stdin);
|
||||
return i::SmartPointer<char>(str ? i::OS::StrDup(str) : str);
|
||||
}
|
||||
|
||||
|
||||
Shell::CounterMap Shell::counter_map_;
|
||||
Persistent<Context> Shell::utility_context_;
|
||||
Persistent<Context> Shell::evaluation_context_;
|
||||
|
||||
|
||||
// Executes a string within the current v8 context.
|
||||
bool Shell::ExecuteString(Handle<String> source,
|
||||
Handle<Value> name,
|
||||
bool print_result,
|
||||
bool report_exceptions) {
|
||||
HandleScope handle_scope;
|
||||
TryCatch try_catch;
|
||||
Handle<Script> script = Script::Compile(source, name);
|
||||
if (script.IsEmpty()) {
|
||||
// Print errors that happened during compilation.
|
||||
if (report_exceptions)
|
||||
ReportException(&try_catch);
|
||||
return false;
|
||||
} else {
|
||||
Handle<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.
|
||||
String::Utf8Value str(result);
|
||||
printf("%s\n", *str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> Shell::Print(const Arguments& args) {
|
||||
bool first = true;
|
||||
for (int i = 0; i < args.Length(); i++) {
|
||||
HandleScope handle_scope;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
String::Utf8Value str(args[i]);
|
||||
printf("%s", *str);
|
||||
}
|
||||
printf("\n");
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> Shell::Load(const Arguments& args) {
|
||||
for (int i = 0; i < args.Length(); i++) {
|
||||
HandleScope handle_scope;
|
||||
String::Utf8Value file(args[i]);
|
||||
Handle<String> source = ReadFile(*file);
|
||||
if (source.IsEmpty()) {
|
||||
return ThrowException(String::New("Error loading file"));
|
||||
}
|
||||
if (!ExecuteString(source, String::New(*file), false, false)) {
|
||||
return ThrowException(String::New("Error executing file"));
|
||||
}
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> Shell::Quit(const Arguments& args) {
|
||||
int exit_code = args[0]->Int32Value();
|
||||
OnExit();
|
||||
exit(exit_code);
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> Shell::Version(const Arguments& args) {
|
||||
return String::New(V8::GetVersion());
|
||||
}
|
||||
|
||||
|
||||
void Shell::ReportException(v8::TryCatch* try_catch) {
|
||||
HandleScope handle_scope;
|
||||
String::Utf8Value exception(try_catch->Exception());
|
||||
Handle<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);
|
||||
} else {
|
||||
// Print (filename):(line number): (message).
|
||||
String::Utf8Value filename(message->GetScriptResourceName());
|
||||
int linenum = message->GetLineNumber();
|
||||
printf("%s:%i: %s\n", *filename, linenum, *exception);
|
||||
// Print line of source code.
|
||||
String::Utf8Value sourceline(message->GetSourceLine());
|
||||
printf("%s\n", *sourceline);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
|
||||
HandleScope handle_scope;
|
||||
Context::Scope context_scope(utility_context_);
|
||||
Handle<Object> global = utility_context_->Global();
|
||||
Handle<Value> fun = global->Get(String::New("GetCompletions"));
|
||||
static const int kArgc = 3;
|
||||
Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
|
||||
Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
|
||||
return handle_scope.Close(Handle<Array>::Cast(val));
|
||||
}
|
||||
|
||||
|
||||
int* Shell::LookupCounter(const wchar_t* name) {
|
||||
CounterMap::iterator item = counter_map_.find(name);
|
||||
if (item != counter_map_.end()) {
|
||||
Counter* result = (*item).second;
|
||||
return result->GetValuePtr();
|
||||
}
|
||||
Counter* result = new Counter(name);
|
||||
counter_map_[name] = result;
|
||||
return result->GetValuePtr();
|
||||
}
|
||||
|
||||
|
||||
void Shell::Initialize() {
|
||||
// Set up counters
|
||||
if (i::FLAG_dump_counters)
|
||||
V8::SetCounterFunction(LookupCounter);
|
||||
// Initialize the global objects
|
||||
HandleScope scope;
|
||||
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
|
||||
global_template->Set(String::New("print"), FunctionTemplate::New(Print));
|
||||
global_template->Set(String::New("load"), FunctionTemplate::New(Load));
|
||||
global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
|
||||
global_template->Set(String::New("version"), FunctionTemplate::New(Version));
|
||||
|
||||
utility_context_ = Context::New(NULL, global_template);
|
||||
utility_context_->SetSecurityToken(Undefined());
|
||||
Context::Scope utility_scope(utility_context_);
|
||||
|
||||
// Install the debugger object in the utility scope
|
||||
i::Debug::Load();
|
||||
i::Debug::debug_context()->set_security_token(i::Heap::undefined_value());
|
||||
i::JSObject* debug = i::Debug::debug_context()->global();
|
||||
utility_context_->Global()->Set(String::New("$debug"),
|
||||
Utils::ToLocal(&debug));
|
||||
|
||||
// Run the d8 shell utility script in the utility context
|
||||
int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
|
||||
i::Vector<const char> shell_source
|
||||
= i::NativesCollection<i::D8>::GetScriptSource(source_index);
|
||||
i::Vector<const char> shell_source_name
|
||||
= i::NativesCollection<i::D8>::GetScriptName(source_index);
|
||||
Handle<String> source = String::New(shell_source.start(),
|
||||
shell_source.length());
|
||||
Handle<String> name = String::New(shell_source_name.start(),
|
||||
shell_source_name.length());
|
||||
Script::Compile(source, name)->Run();
|
||||
|
||||
// Create the evaluation context
|
||||
evaluation_context_ = Context::New(NULL, global_template);
|
||||
evaluation_context_->SetSecurityToken(Undefined());
|
||||
}
|
||||
|
||||
|
||||
void Shell::OnExit() {
|
||||
if (i::FLAG_dump_counters) {
|
||||
::printf("+----------------------------------------+----------+\n");
|
||||
::printf("| Name | Value |\n");
|
||||
::printf("+----------------------------------------+----------+\n");
|
||||
for (CounterMap::iterator i = counter_map_.begin();
|
||||
i != counter_map_.end();
|
||||
i++) {
|
||||
Counter* counter = (*i).second;
|
||||
::printf("| %-38ls | %8i |\n", counter->name(), counter->value());
|
||||
}
|
||||
::printf("+----------------------------------------+----------+\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads a file into a v8 string.
|
||||
Handle<String> Shell::ReadFile(const char* name) {
|
||||
FILE* file = i::OS::FOpen(name, "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;
|
||||
}
|
||||
|
||||
|
||||
void Shell::RunShell() {
|
||||
LineEditor* editor = LineEditor::Get();
|
||||
printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
|
||||
editor->Open();
|
||||
while (true) {
|
||||
HandleScope handle_scope;
|
||||
i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
|
||||
if (input.is_empty())
|
||||
break;
|
||||
editor->AddHistory(*input);
|
||||
Handle<String> name = String::New("(d8)");
|
||||
ExecuteString(String::New(*input), name, true, true);
|
||||
}
|
||||
editor->Close();
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
int Shell::Main(int argc, char* argv[]) {
|
||||
i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
|
||||
Initialize();
|
||||
bool run_shell = (argc == 1);
|
||||
Context::Scope context_scope(evaluation_context_);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char* str = argv[i];
|
||||
HandleScope handle_scope;
|
||||
Handle<String> file_name = v8::String::New(str);
|
||||
Handle<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();
|
||||
OnExit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return v8::Shell::Main(argc, argv);
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
// 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_D8_H_
|
||||
#define V8_D8_H_
|
||||
|
||||
|
||||
// Disable exceptions on windows to not generate warnings from <map>.
|
||||
#define _HAS_EXCEPTIONS 0
|
||||
#include <map>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
|
||||
|
||||
namespace i = v8::internal;
|
||||
|
||||
|
||||
class Counter {
|
||||
public:
|
||||
explicit Counter(const wchar_t* name)
|
||||
: name_(name), value_(0) { }
|
||||
int* GetValuePtr() { return &value_; }
|
||||
const wchar_t* name() { return name_; }
|
||||
int value() { return value_; }
|
||||
private:
|
||||
const wchar_t* name_;
|
||||
int value_;
|
||||
};
|
||||
|
||||
|
||||
class Shell: public i::AllStatic {
|
||||
public:
|
||||
static bool ExecuteString(Handle<String> source,
|
||||
Handle<Value> name,
|
||||
bool print_result,
|
||||
bool report_exceptions);
|
||||
static void ReportException(TryCatch* try_catch);
|
||||
static void Initialize();
|
||||
static void OnExit();
|
||||
static int* LookupCounter(const wchar_t* name);
|
||||
static Handle<String> ReadFile(const char* name);
|
||||
static void RunShell();
|
||||
static int Main(int argc, char* argv[]);
|
||||
static Handle<Array> GetCompletions(Handle<String> text,
|
||||
Handle<String> full);
|
||||
|
||||
static Handle<Value> Print(const Arguments& args);
|
||||
static Handle<Value> Quit(const Arguments& args);
|
||||
static Handle<Value> Version(const Arguments& args);
|
||||
static Handle<Value> Load(const Arguments& args);
|
||||
|
||||
static const char* kHistoryFileName;
|
||||
static const char* kPrompt;
|
||||
private:
|
||||
static Persistent<Context> utility_context_;
|
||||
static Persistent<Context> evaluation_context_;
|
||||
typedef std::map<const wchar_t*, Counter*> CounterMap;
|
||||
static CounterMap counter_map_;
|
||||
};
|
||||
|
||||
|
||||
class LineEditor {
|
||||
public:
|
||||
enum Type { DUMB = 0, READLINE = 1 };
|
||||
LineEditor(Type type, const char* name);
|
||||
virtual ~LineEditor() { }
|
||||
|
||||
virtual i::SmartPointer<char> Prompt(const char* prompt) = 0;
|
||||
virtual bool Open() { return true; }
|
||||
virtual bool Close() { return true; }
|
||||
virtual void AddHistory(const char* str) { }
|
||||
|
||||
const char* name() { return name_; }
|
||||
static LineEditor* Get();
|
||||
private:
|
||||
Type type_;
|
||||
const char* name_;
|
||||
LineEditor* next_;
|
||||
static LineEditor* first_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace v8
|
||||
|
||||
|
||||
#endif // V8_D8_H_
|
@ -1,70 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// How crappy is it that I have to implement completely basic stuff
|
||||
// like this myself? Answer: very.
|
||||
String.prototype.startsWith = function (str) {
|
||||
if (str.length > this.length)
|
||||
return false;
|
||||
return this.substr(0, str.length) == str;
|
||||
};
|
||||
|
||||
function ToInspectableObject(obj) {
|
||||
if (!obj && typeof obj === 'object') {
|
||||
return void 0;
|
||||
} else {
|
||||
return Object(obj);
|
||||
}
|
||||
}
|
||||
|
||||
function GetCompletions(global, last, full) {
|
||||
var full_tokens = full.split();
|
||||
full = full_tokens.pop();
|
||||
var parts = full.split('.');
|
||||
parts.pop();
|
||||
var current = global;
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
var next = current[part];
|
||||
if (!next)
|
||||
return [];
|
||||
current = next;
|
||||
}
|
||||
var result = [];
|
||||
current = ToInspectableObject(current);
|
||||
while (typeof current !== 'undefined') {
|
||||
var mirror = new $debug.ObjectMirror(current);
|
||||
var properties = mirror.properties();
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var name = properties[i].name();
|
||||
if (typeof name === 'string' && name.startsWith(last))
|
||||
result.push(name);
|
||||
}
|
||||
current = ToInspectableObject(current.__proto__);
|
||||
}
|
||||
return result;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,265 +0,0 @@
|
||||
// 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 "dateparser.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
bool DateParser::Parse(String* str, FixedArray* out) {
|
||||
ASSERT(out->length() == OUTPUT_SIZE);
|
||||
|
||||
InputReader in(str);
|
||||
TimeZoneComposer tz;
|
||||
TimeComposer time;
|
||||
DayComposer day;
|
||||
|
||||
while (!in.IsEnd()) {
|
||||
if (in.IsAsciiDigit()) {
|
||||
// Parse a number (possibly with 1 or 2 trailing colons).
|
||||
int n = in.ReadUnsignedNumber();
|
||||
if (in.Skip(':')) {
|
||||
if (in.Skip(':')) {
|
||||
// n + "::"
|
||||
if (!time.IsEmpty()) return false;
|
||||
time.Add(n);
|
||||
time.Add(0);
|
||||
} else {
|
||||
// n + ":"
|
||||
if (!time.Add(n)) return false;
|
||||
}
|
||||
} else if (tz.IsExpecting(n)) {
|
||||
tz.SetAbsoluteMinute(n);
|
||||
} else if (time.IsExpecting(n)) {
|
||||
time.AddFinal(n);
|
||||
// Require end or white space immediately after finalizing time.
|
||||
if (!in.IsEnd() && !in.SkipWhiteSpace()) return false;
|
||||
} else {
|
||||
if (!day.Add(n)) return false;
|
||||
in.Skip('-'); // Ignore suffix '-' for year, month, or day.
|
||||
}
|
||||
} else if (in.IsAsciiAlphaOrAbove()) {
|
||||
// Parse a "word" (sequence of chars. >= 'A').
|
||||
uint32_t pre[KeywordTable::kPrefixLength];
|
||||
int len = in.ReadWord(pre, KeywordTable::kPrefixLength);
|
||||
int index = KeywordTable::Lookup(pre, len);
|
||||
KeywordType type = KeywordTable::GetType(index);
|
||||
|
||||
if (type == AM_PM && !time.IsEmpty()) {
|
||||
time.SetHourOffset(KeywordTable::GetValue(index));
|
||||
} else if (type == MONTH_NAME) {
|
||||
day.SetNamedMonth(KeywordTable::GetValue(index));
|
||||
in.Skip('-'); // Ignore suffix '-' for month names
|
||||
} else if (type == TIME_ZONE_NAME && in.HasReadNumber()) {
|
||||
tz.Set(KeywordTable::GetValue(index));
|
||||
} else {
|
||||
// Garbage words are illegal if no number read yet.
|
||||
if (in.HasReadNumber()) return false;
|
||||
}
|
||||
} else if (in.IsAsciiSign() && (tz.IsUTC() || !time.IsEmpty())) {
|
||||
// Parse UTC offset (only after UTC or time).
|
||||
tz.SetSign(in.GetAsciiSignValue());
|
||||
in.Next();
|
||||
int n = in.ReadUnsignedNumber();
|
||||
if (in.Skip(':')) {
|
||||
tz.SetAbsoluteHour(n);
|
||||
tz.SetAbsoluteMinute(kNone);
|
||||
} else {
|
||||
tz.SetAbsoluteHour(n / 100);
|
||||
tz.SetAbsoluteMinute(n % 100);
|
||||
}
|
||||
} else if (in.Is('(')) {
|
||||
// Ignore anything from '(' to a matching ')' or end of string.
|
||||
in.SkipParentheses();
|
||||
} else if ((in.IsAsciiSign() || in.Is(')')) && in.HasReadNumber()) {
|
||||
// Extra sign or ')' is illegal if no number read yet.
|
||||
return false;
|
||||
} else {
|
||||
// Ignore other characters.
|
||||
in.Next();
|
||||
}
|
||||
}
|
||||
return day.Write(out) && time.Write(out) && tz.Write(out);
|
||||
}
|
||||
|
||||
|
||||
bool DateParser::DayComposer::Write(FixedArray* output) {
|
||||
int year = 0; // Default year is 0 (=> 2000) for KJS compatibility.
|
||||
int month = kNone;
|
||||
int day = kNone;
|
||||
|
||||
if (named_month_ == kNone) {
|
||||
if (index_ < 2) return false;
|
||||
if (index_ == 3 && !IsDay(comp_[0])) {
|
||||
// YMD
|
||||
year = comp_[0];
|
||||
month = comp_[1];
|
||||
day = comp_[2];
|
||||
} else {
|
||||
// MD(Y)
|
||||
month = comp_[0];
|
||||
day = comp_[1];
|
||||
if (index_ == 3) year = comp_[2];
|
||||
}
|
||||
} else {
|
||||
month = named_month_;
|
||||
if (index_ < 1) return false;
|
||||
if (index_ == 1) {
|
||||
// MD or DM
|
||||
day = comp_[0];
|
||||
} else if (!IsDay(comp_[0])) {
|
||||
// YMD, MYD, or YDM
|
||||
year = comp_[0];
|
||||
day = comp_[1];
|
||||
} else {
|
||||
// DMY, MDY, or DYM
|
||||
day = comp_[0];
|
||||
year = comp_[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (Between(year, 0, 49)) year += 2000;
|
||||
else if (Between(year, 50, 99)) year += 1900;
|
||||
|
||||
if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
|
||||
|
||||
output->set(YEAR,
|
||||
Smi::FromInt(year),
|
||||
SKIP_WRITE_BARRIER);
|
||||
output->set(MONTH,
|
||||
Smi::FromInt(month - 1),
|
||||
SKIP_WRITE_BARRIER); // 0-based
|
||||
output->set(DAY,
|
||||
Smi::FromInt(day),
|
||||
SKIP_WRITE_BARRIER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DateParser::TimeComposer::Write(FixedArray* output) {
|
||||
// All time slots default to 0
|
||||
while (index_ < kSize) {
|
||||
comp_[index_++] = 0;
|
||||
}
|
||||
|
||||
int& hour = comp_[0];
|
||||
int& minute = comp_[1];
|
||||
int& second = comp_[2];
|
||||
|
||||
if (hour_offset_ != kNone) {
|
||||
if (!IsHour12(hour)) return false;
|
||||
hour %= 12;
|
||||
hour += hour_offset_;
|
||||
}
|
||||
|
||||
if (!IsHour(hour) || !IsMinute(minute) || !IsSecond(second)) return false;
|
||||
|
||||
output->set(HOUR,
|
||||
Smi::FromInt(hour),
|
||||
SKIP_WRITE_BARRIER);
|
||||
output->set(MINUTE,
|
||||
Smi::FromInt(minute),
|
||||
SKIP_WRITE_BARRIER);
|
||||
output->set(SECOND,
|
||||
Smi::FromInt(second),
|
||||
SKIP_WRITE_BARRIER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DateParser::TimeZoneComposer::Write(FixedArray* output) {
|
||||
if (sign_ != kNone) {
|
||||
if (hour_ == kNone) hour_ = 0;
|
||||
if (minute_ == kNone) minute_ = 0;
|
||||
int total_seconds = sign_ * (hour_ * 3600 + minute_ * 60);
|
||||
if (!Smi::IsValid(total_seconds)) return false;
|
||||
output->set(UTC_OFFSET,
|
||||
Smi::FromInt(total_seconds),
|
||||
SKIP_WRITE_BARRIER);
|
||||
} else {
|
||||
output->set(UTC_OFFSET,
|
||||
Heap::null_value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const int8_t
|
||||
DateParser::KeywordTable::array[][DateParser::KeywordTable::kEntrySize] = {
|
||||
{'j', 'a', 'n', DateParser::MONTH_NAME, 1},
|
||||
{'f', 'e', 'b', DateParser::MONTH_NAME, 2},
|
||||
{'m', 'a', 'r', DateParser::MONTH_NAME, 3},
|
||||
{'a', 'p', 'r', DateParser::MONTH_NAME, 4},
|
||||
{'m', 'a', 'y', DateParser::MONTH_NAME, 5},
|
||||
{'j', 'u', 'n', DateParser::MONTH_NAME, 6},
|
||||
{'j', 'u', 'l', DateParser::MONTH_NAME, 7},
|
||||
{'a', 'u', 'g', DateParser::MONTH_NAME, 8},
|
||||
{'s', 'e', 'p', DateParser::MONTH_NAME, 9},
|
||||
{'o', 'c', 't', DateParser::MONTH_NAME, 10},
|
||||
{'n', 'o', 'v', DateParser::MONTH_NAME, 11},
|
||||
{'d', 'e', 'c', DateParser::MONTH_NAME, 12},
|
||||
{'a', 'm', '\0', DateParser::AM_PM, 0},
|
||||
{'p', 'm', '\0', DateParser::AM_PM, 12},
|
||||
{'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0},
|
||||
{'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0},
|
||||
{'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0},
|
||||
{'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
|
||||
{'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
|
||||
{'e', 'd', 't', DateParser::TIME_ZONE_NAME, -4},
|
||||
{'e', 's', 't', DateParser::TIME_ZONE_NAME, -5},
|
||||
{'m', 'd', 't', DateParser::TIME_ZONE_NAME, -6},
|
||||
{'m', 's', 't', DateParser::TIME_ZONE_NAME, -7},
|
||||
{'p', 'd', 't', DateParser::TIME_ZONE_NAME, -7},
|
||||
{'p', 's', 't', DateParser::TIME_ZONE_NAME, -8},
|
||||
{'\0', '\0', '\0', DateParser::INVALID, 0},
|
||||
};
|
||||
|
||||
|
||||
// We could use perfect hashing here, but this is not a bottleneck.
|
||||
int DateParser::KeywordTable::Lookup(const uint32_t* pre, int len) {
|
||||
int i;
|
||||
for (i = 0; array[i][kTypeOffset] != INVALID; i++) {
|
||||
int j = 0;
|
||||
while (j < kPrefixLength &&
|
||||
pre[j] == static_cast<uint32_t>(array[i][j])) {
|
||||
j++;
|
||||
}
|
||||
// Check if we have a match and the length is legal.
|
||||
// Word longer than keyword is only allowed for month names.
|
||||
if (j == kPrefixLength &&
|
||||
(len <= kPrefixLength || array[i][kTypeOffset] == MONTH_NAME)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,230 +0,0 @@
|
||||
// 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_DATEPARSER_H_
|
||||
#define V8_DATEPARSER_H_
|
||||
|
||||
#include "scanner.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
class DateParser : public AllStatic {
|
||||
public:
|
||||
|
||||
// Parse the string as a date. If parsing succeeds, return true after
|
||||
// filling out the output array as follows (all integers are Smis):
|
||||
// [0]: year
|
||||
// [1]: month (0 = Jan, 1 = Feb, ...)
|
||||
// [2]: day
|
||||
// [3]: hour
|
||||
// [4]: minute
|
||||
// [5]: second
|
||||
// [6]: UTC offset in seconds, or null value if no timezone specified
|
||||
// If parsing fails, return false (content of output array is not defined).
|
||||
static bool Parse(String* str, FixedArray* output);
|
||||
|
||||
enum {YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, UTC_OFFSET, OUTPUT_SIZE};
|
||||
|
||||
private:
|
||||
// Range testing
|
||||
static bool Between(int x, int lo, int hi) { return x >= lo && x <= hi; }
|
||||
// Indicates a missing value.
|
||||
static const int kNone = kMaxInt;
|
||||
|
||||
// InputReader provides basic string parsing and character classification.
|
||||
class InputReader BASE_EMBEDDED {
|
||||
public:
|
||||
explicit InputReader(String* s) : buffer_(s), has_read_number_(false) {
|
||||
Next();
|
||||
}
|
||||
|
||||
// Advance to the next character of the string.
|
||||
void Next() { ch_ = buffer_.has_more() ? buffer_.GetNext() : 0; }
|
||||
|
||||
// Read a string of digits as an unsigned number (cap just below kMaxInt).
|
||||
int ReadUnsignedNumber() {
|
||||
has_read_number_ = true;
|
||||
int n;
|
||||
for (n = 0; IsAsciiDigit() && n < kMaxInt / 10 - 1; Next()) {
|
||||
n = n * 10 + ch_ - '0';
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Read a word (sequence of chars. >= 'A'), fill the given buffer with a
|
||||
// lower-case prefix, and pad any remainder of the buffer with zeroes.
|
||||
// Return word length.
|
||||
int ReadWord(uint32_t* prefix, int prefix_size) {
|
||||
int len;
|
||||
for (len = 0; IsAsciiAlphaOrAbove(); Next(), len++) {
|
||||
if (len < prefix_size) prefix[len] = GetAsciiAlphaLower();
|
||||
}
|
||||
for (int i = len; i < prefix_size; i++) prefix[i] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
// The skip methods return whether they actually skipped something.
|
||||
bool Skip(uint32_t c) { return ch_ == c ? (Next(), true) : false; }
|
||||
|
||||
bool SkipWhiteSpace() {
|
||||
return Scanner::kIsWhiteSpace.get(ch_) ? (Next(), true) : false;
|
||||
}
|
||||
|
||||
bool SkipParentheses() {
|
||||
if (ch_ != '(') return false;
|
||||
int balance = 0;
|
||||
do {
|
||||
if (ch_ == ')') --balance;
|
||||
else if (ch_ == '(') ++balance;
|
||||
Next();
|
||||
} while (balance > 0 && ch_);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Character testing/classification. Non-ASCII digits are not supported.
|
||||
bool Is(uint32_t c) const { return ch_ == c; }
|
||||
bool IsEnd() const { return ch_ == 0; }
|
||||
bool IsAsciiDigit() const { return IsDecimalDigit(ch_); }
|
||||
bool IsAsciiAlphaOrAbove() const { return ch_ >= 'A'; }
|
||||
bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; }
|
||||
|
||||
// Return 1 for '+' and -1 for '-'.
|
||||
int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); }
|
||||
|
||||
// Indicates whether any (possibly empty!) numbers have been read.
|
||||
bool HasReadNumber() const { return has_read_number_; }
|
||||
|
||||
private:
|
||||
// If current character is in 'A'-'Z' or 'a'-'z', return its lower-case.
|
||||
// Else, return something outside of 'A'-'Z' and 'a'-'z'.
|
||||
uint32_t GetAsciiAlphaLower() const { return ch_ | 32; }
|
||||
|
||||
StringInputBuffer buffer_;
|
||||
bool has_read_number_;
|
||||
uint32_t ch_;
|
||||
};
|
||||
|
||||
enum KeywordType { INVALID, MONTH_NAME, TIME_ZONE_NAME, AM_PM };
|
||||
|
||||
// KeywordTable maps names of months, time zones, am/pm to numbers.
|
||||
class KeywordTable : public AllStatic {
|
||||
public:
|
||||
// Look up a word in the keyword table and return an index.
|
||||
// 'pre' contains a prefix of the word, zero-padded to size kPrefixLength
|
||||
// and 'len' is the word length.
|
||||
static int Lookup(const uint32_t* pre, int len);
|
||||
// Get the type of the keyword at index i.
|
||||
static KeywordType GetType(int i) {
|
||||
return static_cast<KeywordType>(array[i][kTypeOffset]);
|
||||
}
|
||||
// Get the value of the keyword at index i.
|
||||
static int GetValue(int i) { return array[i][kValueOffset]; }
|
||||
|
||||
static const int kPrefixLength = 3;
|
||||
static const int kTypeOffset = kPrefixLength;
|
||||
static const int kValueOffset = kTypeOffset + 1;
|
||||
static const int kEntrySize = kValueOffset + 1;
|
||||
static const int8_t array[][kEntrySize];
|
||||
};
|
||||
|
||||
class TimeZoneComposer BASE_EMBEDDED {
|
||||
public:
|
||||
TimeZoneComposer() : sign_(kNone), hour_(kNone), minute_(kNone) {}
|
||||
void Set(int offset_in_hours) {
|
||||
sign_ = offset_in_hours < 0 ? -1 : 1;
|
||||
hour_ = offset_in_hours * sign_;
|
||||
minute_ = 0;
|
||||
}
|
||||
void SetSign(int sign) { sign_ = sign < 0 ? -1 : 1; }
|
||||
void SetAbsoluteHour(int hour) { hour_ = hour; }
|
||||
void SetAbsoluteMinute(int minute) { minute_ = minute; }
|
||||
bool IsExpecting(int n) const {
|
||||
return hour_ != kNone && minute_ == kNone && TimeComposer::IsMinute(n);
|
||||
}
|
||||
bool IsUTC() const { return hour_ == 0 && minute_ == 0; }
|
||||
bool Write(FixedArray* output);
|
||||
private:
|
||||
int sign_;
|
||||
int hour_;
|
||||
int minute_;
|
||||
};
|
||||
|
||||
class TimeComposer BASE_EMBEDDED {
|
||||
public:
|
||||
TimeComposer() : index_(0), hour_offset_(kNone) {}
|
||||
bool IsEmpty() const { return index_ == 0; }
|
||||
bool IsExpecting(int n) const {
|
||||
return (index_ == 1 && IsMinute(n)) || (index_ == 2 && IsSecond(n));
|
||||
}
|
||||
bool Add(int n) {
|
||||
return index_ < kSize ? (comp_[index_++] = n, true) : false;
|
||||
}
|
||||
bool AddFinal(int n) {
|
||||
if (!Add(n)) return false;
|
||||
while (index_ < kSize) comp_[index_++] = 0;
|
||||
return true;
|
||||
}
|
||||
void SetHourOffset(int n) { hour_offset_ = n; }
|
||||
bool Write(FixedArray* output);
|
||||
|
||||
static bool IsMinute(int x) { return Between(x, 0, 59); }
|
||||
private:
|
||||
static bool IsHour(int x) { return Between(x, 0, 23); }
|
||||
static bool IsHour12(int x) { return Between(x, 0, 12); }
|
||||
static bool IsSecond(int x) { return Between(x, 0, 59); }
|
||||
|
||||
static const int kSize = 3;
|
||||
int comp_[kSize];
|
||||
int index_;
|
||||
int hour_offset_;
|
||||
};
|
||||
|
||||
class DayComposer BASE_EMBEDDED {
|
||||
public:
|
||||
DayComposer() : index_(0), named_month_(kNone) {}
|
||||
bool IsEmpty() const { return index_ == 0; }
|
||||
bool Add(int n) {
|
||||
return index_ < kSize ? (comp_[index_++] = n, true) : false;
|
||||
}
|
||||
void SetNamedMonth(int n) { named_month_ = n; }
|
||||
bool Write(FixedArray* output);
|
||||
private:
|
||||
static bool IsMonth(int x) { return Between(x, 1, 12); }
|
||||
static bool IsDay(int x) { return Between(x, 1, 31); }
|
||||
|
||||
static const int kSize = 3;
|
||||
int comp_[kSize];
|
||||
int index_;
|
||||
int named_month_;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_DATEPARSER_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,582 +0,0 @@
|
||||
// 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_V8_DEBUG_H_
|
||||
#define V8_V8_DEBUG_H_
|
||||
|
||||
#include "../include/v8-debug.h"
|
||||
#include "assembler.h"
|
||||
#include "code-stubs.h"
|
||||
#include "execution.h"
|
||||
#include "factory.h"
|
||||
#include "platform.h"
|
||||
#include "string-stream.h"
|
||||
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// Step actions. NOTE: These values are in macros.py as well.
|
||||
enum StepAction {
|
||||
StepNone = -1, // Stepping not prepared.
|
||||
StepOut = 0, // Step out of the current function.
|
||||
StepNext = 1, // Step to the next statement in the current function.
|
||||
StepIn = 2, // Step into new functions invoked or the next statement
|
||||
// in the current function.
|
||||
StepMin = 3, // Perform a minimum step in the current function.
|
||||
StepInMin = 4 // Step into new functions invoked or perform a minimum step
|
||||
// in the current function.
|
||||
};
|
||||
|
||||
|
||||
// Type of exception break. NOTE: These values are in macros.py as well.
|
||||
enum ExceptionBreakType {
|
||||
BreakException = 0,
|
||||
BreakUncaughtException = 1
|
||||
};
|
||||
|
||||
|
||||
// Type of exception break. NOTE: These values are in macros.py as well.
|
||||
enum BreakLocatorType {
|
||||
ALL_BREAK_LOCATIONS = 0,
|
||||
SOURCE_BREAK_LOCATIONS = 1
|
||||
};
|
||||
|
||||
|
||||
// Class for iterating through the break points in a function and changing
|
||||
// them.
|
||||
class BreakLocationIterator {
|
||||
public:
|
||||
explicit BreakLocationIterator(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type);
|
||||
virtual ~BreakLocationIterator();
|
||||
|
||||
void Next();
|
||||
void Next(int count);
|
||||
void FindBreakLocationFromAddress(Address pc);
|
||||
void FindBreakLocationFromPosition(int position);
|
||||
void Reset();
|
||||
bool Done() const;
|
||||
void SetBreakPoint(Handle<Object> break_point_object);
|
||||
void ClearBreakPoint(Handle<Object> break_point_object);
|
||||
void SetOneShot();
|
||||
void ClearOneShot();
|
||||
void PrepareStepIn();
|
||||
bool IsExit() const;
|
||||
bool HasBreakPoint();
|
||||
bool IsDebugBreak();
|
||||
Object* BreakPointObjects();
|
||||
|
||||
|
||||
inline int code_position() { return pc() - debug_info_->code()->entry(); }
|
||||
inline int break_point() { return break_point_; }
|
||||
inline int position() { return position_; }
|
||||
inline int statement_position() { return statement_position_; }
|
||||
inline Address pc() { return reloc_iterator_->rinfo()->pc(); }
|
||||
inline Code* code() { return debug_info_->code(); }
|
||||
inline RelocInfo* rinfo() { return reloc_iterator_->rinfo(); }
|
||||
inline RelocInfo::Mode rmode() const {
|
||||
return reloc_iterator_->rinfo()->rmode();
|
||||
}
|
||||
inline RelocInfo* original_rinfo() {
|
||||
return reloc_iterator_original_->rinfo();
|
||||
}
|
||||
inline RelocInfo::Mode original_rmode() const {
|
||||
return reloc_iterator_original_->rinfo()->rmode();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool RinfoDone() const;
|
||||
void RinfoNext();
|
||||
|
||||
BreakLocatorType type_;
|
||||
int break_point_;
|
||||
int position_;
|
||||
int statement_position_;
|
||||
Handle<DebugInfo> debug_info_;
|
||||
RelocIterator* reloc_iterator_;
|
||||
RelocIterator* reloc_iterator_original_;
|
||||
|
||||
private:
|
||||
void SetDebugBreak();
|
||||
void ClearDebugBreak();
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BreakLocationIterator);
|
||||
};
|
||||
|
||||
|
||||
// Linked list holding debug info objects. The debug info objects are kept as
|
||||
// weak handles to avoid a debug info object to keep a function alive.
|
||||
class DebugInfoListNode {
|
||||
public:
|
||||
explicit DebugInfoListNode(DebugInfo* debug_info);
|
||||
virtual ~DebugInfoListNode();
|
||||
|
||||
DebugInfoListNode* next() { return next_; }
|
||||
void set_next(DebugInfoListNode* next) { next_ = next; }
|
||||
Handle<DebugInfo> debug_info() { return debug_info_; }
|
||||
|
||||
private:
|
||||
// Global (weak) handle to the debug info object.
|
||||
Handle<DebugInfo> debug_info_;
|
||||
|
||||
// Next pointer for linked list.
|
||||
DebugInfoListNode* next_;
|
||||
};
|
||||
|
||||
|
||||
// This class contains the debugger support. The main purpose is to handle
|
||||
// setting break points in the code.
|
||||
//
|
||||
// This class controls the debug info for all functions which currently have
|
||||
// active breakpoints in them. This debug info is held in the heap root object
|
||||
// debug_info which is a FixedArray. Each entry in this list is of class
|
||||
// DebugInfo.
|
||||
class Debug {
|
||||
public:
|
||||
static void Setup(bool create_heap_objects);
|
||||
static bool Load();
|
||||
static void Unload();
|
||||
static bool IsLoaded() { return !debug_context_.is_null(); }
|
||||
static bool InDebugger() { return Top::is_break(); }
|
||||
static void Iterate(ObjectVisitor* v);
|
||||
|
||||
static Object* Break(Arguments args);
|
||||
static void SetBreakPoint(Handle<SharedFunctionInfo> shared,
|
||||
int source_position,
|
||||
Handle<Object> break_point_object);
|
||||
static void ClearBreakPoint(Handle<Object> break_point_object);
|
||||
static void FloodWithOneShot(Handle<SharedFunctionInfo> shared);
|
||||
static void FloodHandlerWithOneShot();
|
||||
static void ChangeBreakOnException(ExceptionBreakType type, bool enable);
|
||||
static void PrepareStep(StepAction step_action, int step_count);
|
||||
static void ClearStepping();
|
||||
static bool StepNextContinue(BreakLocationIterator* break_location_iterator,
|
||||
JavaScriptFrame* frame);
|
||||
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
// Returns whether the operation succedded.
|
||||
static bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
static bool IsDebugBreak(Address addr);
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// point location.
|
||||
static bool IsSourceBreakStub(Code* code);
|
||||
static bool IsBreakStub(Code* code);
|
||||
|
||||
// Find the builtin to use for invoking the debug break
|
||||
static Handle<Code> FindDebugBreak(RelocInfo* rinfo);
|
||||
|
||||
static Handle<Object> GetSourceBreakLocations(
|
||||
Handle<SharedFunctionInfo> shared);
|
||||
static Code* GetCodeTarget(Address target);
|
||||
|
||||
// Getter for the debug_context.
|
||||
inline static Handle<Context> debug_context() { return debug_context_; }
|
||||
|
||||
// Check whether a global object is the debug global object.
|
||||
static bool IsDebugGlobal(GlobalObject* global);
|
||||
|
||||
// Fast check to see if any break points are active.
|
||||
inline static bool has_break_points() { return has_break_points_; }
|
||||
|
||||
static bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
|
||||
static Address step_in_fp() { return thread_local_.step_into_fp_; }
|
||||
static Address* step_in_fp_addr() { return &thread_local_.step_into_fp_; }
|
||||
|
||||
// Getter and setter for the disable break state.
|
||||
static bool disable_break() { return disable_break_; }
|
||||
static void set_disable_break(bool disable_break) {
|
||||
disable_break_ = disable_break;
|
||||
}
|
||||
|
||||
// Getters for the current exception break state.
|
||||
static bool break_on_exception() { return break_on_exception_; }
|
||||
static bool break_on_uncaught_exception() {
|
||||
return break_on_uncaught_exception_;
|
||||
}
|
||||
|
||||
enum AddressId {
|
||||
k_after_break_target_address,
|
||||
k_debug_break_return_address,
|
||||
k_register_address
|
||||
};
|
||||
|
||||
// Support for setting the address to jump to when returning from break point.
|
||||
static Address* after_break_target_address() {
|
||||
return reinterpret_cast<Address*>(&thread_local_.after_break_target_);
|
||||
}
|
||||
|
||||
// Support for saving/restoring registers when handling debug break calls.
|
||||
static Object** register_address(int r) {
|
||||
return ®isters_[r];
|
||||
}
|
||||
|
||||
// Address of the debug break return entry code.
|
||||
static Code* debug_break_return_entry() { return debug_break_return_entry_; }
|
||||
|
||||
// Support for getting the address of the debug break on return code.
|
||||
static Code** debug_break_return_address() {
|
||||
return &debug_break_return_;
|
||||
}
|
||||
|
||||
static const int kEstimatedNofDebugInfoEntries = 16;
|
||||
static const int kEstimatedNofBreakPointsInFunction = 16;
|
||||
|
||||
static void HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data);
|
||||
|
||||
friend class Debugger;
|
||||
friend Handle<FixedArray> GetDebuggedFunctions(); // Found in test-debug.cc
|
||||
|
||||
// Threading support.
|
||||
static char* ArchiveDebug(char* to);
|
||||
static char* RestoreDebug(char* from);
|
||||
static int ArchiveSpacePerThread();
|
||||
|
||||
// Code generation assumptions.
|
||||
static const int kIa32CallInstructionLength = 5;
|
||||
static const int kIa32JSReturnSequenceLength = 6;
|
||||
|
||||
private:
|
||||
static bool CompileDebuggerScript(int index);
|
||||
static void ClearOneShot();
|
||||
static void ActivateStepIn(StackFrame* frame);
|
||||
static void ClearStepIn();
|
||||
static void ClearStepNext();
|
||||
// Returns whether the compile succedded.
|
||||
static bool EnsureCompiled(Handle<SharedFunctionInfo> shared);
|
||||
static void RemoveDebugInfo(Handle<DebugInfo> debug_info);
|
||||
static void SetAfterBreakTarget(JavaScriptFrame* frame);
|
||||
static Handle<Object> CheckBreakPoints(Handle<Object> break_point);
|
||||
static bool CheckBreakPoint(Handle<Object> break_point_object);
|
||||
|
||||
// Global handle to debug context where all the debugger JavaScript code is
|
||||
// loaded.
|
||||
static Handle<Context> debug_context_;
|
||||
|
||||
// Boolean state indicating whether any break points are set.
|
||||
static bool has_break_points_;
|
||||
static DebugInfoListNode* debug_info_list_;
|
||||
|
||||
static bool disable_break_;
|
||||
static bool break_on_exception_;
|
||||
static bool break_on_uncaught_exception_;
|
||||
|
||||
// Per-thread:
|
||||
class ThreadLocal {
|
||||
public:
|
||||
// Step action for last step performed.
|
||||
StepAction last_step_action_;
|
||||
|
||||
// Source statement position from last step next action.
|
||||
int last_statement_position_;
|
||||
|
||||
// Number of steps left to perform before debug event.
|
||||
int step_count_;
|
||||
|
||||
// Frame pointer from last step next action.
|
||||
Address last_fp_;
|
||||
|
||||
// Frame pointer for frame from which step in was performed.
|
||||
Address step_into_fp_;
|
||||
|
||||
// Storage location for jump when exiting debug break calls.
|
||||
Address after_break_target_;
|
||||
};
|
||||
|
||||
// Storage location for registers when handling debug break calls
|
||||
static JSCallerSavedBuffer registers_;
|
||||
static ThreadLocal thread_local_;
|
||||
static void ThreadInit();
|
||||
|
||||
// Code object for debug break return entry code.
|
||||
static Code* debug_break_return_entry_;
|
||||
|
||||
// Code to call for handling debug break on return.
|
||||
static Code* debug_break_return_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Debug);
|
||||
};
|
||||
|
||||
|
||||
class DebugMessageThread;
|
||||
|
||||
class Debugger {
|
||||
public:
|
||||
static void DebugRequest(const uint16_t* json_request, int length);
|
||||
|
||||
static Handle<Object> MakeJSObject(Vector<const char> constructor_name,
|
||||
int argc, Object*** argv,
|
||||
bool* caught_exception);
|
||||
static Handle<Object> MakeExecutionState(bool* caught_exception);
|
||||
static Handle<Object> MakeBreakEvent(Handle<Object> exec_state,
|
||||
Handle<Object> break_points_hit,
|
||||
bool* caught_exception);
|
||||
static Handle<Object> MakeExceptionEvent(Handle<Object> exec_state,
|
||||
Handle<Object> exception,
|
||||
bool uncaught,
|
||||
bool* caught_exception);
|
||||
static Handle<Object> MakeNewFunctionEvent(Handle<Object> func,
|
||||
bool* caught_exception);
|
||||
static Handle<Object> MakeCompileEvent(Handle<Script> script,
|
||||
Handle<Object> script_function,
|
||||
bool* caught_exception);
|
||||
static Handle<String> ProcessRequest(Handle<Object> exec_state,
|
||||
Handle<Object> request,
|
||||
bool stopped);
|
||||
static void OnDebugBreak(Handle<Object> break_points_hit);
|
||||
static void OnException(Handle<Object> exception, bool uncaught);
|
||||
static void OnBeforeCompile(Handle<Script> script);
|
||||
static void OnAfterCompile(Handle<Script> script,
|
||||
Handle<JSFunction> fun);
|
||||
static void OnNewFunction(Handle<JSFunction> fun);
|
||||
static void ProcessDebugEvent(v8::DebugEvent event,
|
||||
Handle<Object> event_data);
|
||||
static void SetMessageHandler(v8::DebugMessageHandler handler, void* data);
|
||||
static void SendMessage(Vector<uint16_t> message);
|
||||
static void ProcessCommand(Vector<const uint16_t> command);
|
||||
static void UpdateActiveDebugger();
|
||||
inline static bool EventActive(v8::DebugEvent event) {
|
||||
// Currently argument event is not used.
|
||||
return !Debugger::compiling_natives_ && Debugger::debugger_active_;
|
||||
}
|
||||
|
||||
static void set_debugger_active(bool debugger_active) {
|
||||
Debugger::debugger_active_ = debugger_active;
|
||||
}
|
||||
static bool debugger_active() { return Debugger::debugger_active_; }
|
||||
static void set_compiling_natives(bool compiling_natives) {
|
||||
Debugger::compiling_natives_ = compiling_natives;
|
||||
}
|
||||
static bool compiling_natives() { return Debugger::compiling_natives_; }
|
||||
static void set_loading_debugger(bool v) { is_loading_debugger_ = v; }
|
||||
static bool is_loading_debugger() { return Debugger::is_loading_debugger_; }
|
||||
|
||||
private:
|
||||
static bool debugger_active_; // Are there any active debugger?
|
||||
static bool compiling_natives_; // Are we compiling natives?
|
||||
static bool is_loading_debugger_; // Are we loading the debugger?
|
||||
static DebugMessageThread* message_thread_;
|
||||
static v8::DebugMessageHandler debug_message_handler_;
|
||||
static void* debug_message_handler_data_;
|
||||
};
|
||||
|
||||
|
||||
// A Queue of Vector<uint16_t> objects. A thread-safe version is
|
||||
// LockingMessageQueue, based on this class.
|
||||
class MessageQueue BASE_EMBEDDED {
|
||||
public:
|
||||
explicit MessageQueue(int size);
|
||||
~MessageQueue();
|
||||
bool IsEmpty() const { return start_ == end_; }
|
||||
Vector<uint16_t> Get();
|
||||
void Put(const Vector<uint16_t>& message);
|
||||
void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
|
||||
private:
|
||||
// Doubles the size of the message queue, and copies the messages.
|
||||
void Expand();
|
||||
|
||||
Vector<uint16_t>* messages_;
|
||||
int start_;
|
||||
int end_;
|
||||
int size_; // The size of the queue buffer. Queue can hold size-1 messages.
|
||||
};
|
||||
|
||||
|
||||
// LockingMessageQueue is a thread-safe circular buffer of Vector<uint16_t>
|
||||
// messages. The message data is not managed by LockingMessageQueue.
|
||||
// Pointers to the data are passed in and out. Implemented by adding a
|
||||
// Mutex to MessageQueue. Includes logging of all puts and gets.
|
||||
class LockingMessageQueue BASE_EMBEDDED {
|
||||
public:
|
||||
explicit LockingMessageQueue(int size);
|
||||
~LockingMessageQueue();
|
||||
bool IsEmpty() const;
|
||||
Vector<uint16_t> Get();
|
||||
void Put(const Vector<uint16_t>& message);
|
||||
void Clear();
|
||||
private:
|
||||
MessageQueue queue_;
|
||||
Mutex* lock_;
|
||||
DISALLOW_COPY_AND_ASSIGN(LockingMessageQueue);
|
||||
};
|
||||
|
||||
|
||||
/* This class is the data for a running thread that serializes
|
||||
* event messages and command processing for the debugger.
|
||||
* All uncommented methods are called only from this message thread.
|
||||
*/
|
||||
class DebugMessageThread: public Thread {
|
||||
public:
|
||||
DebugMessageThread(); // Called from API thread.
|
||||
virtual ~DebugMessageThread(); // Never called.
|
||||
// Called by V8 thread. Reports events from V8 VM.
|
||||
// Also handles command processing in stopped state of V8,
|
||||
// when host_running_ is false.
|
||||
void DebugEvent(v8::DebugEvent,
|
||||
Handle<Object> exec_state,
|
||||
Handle<Object> event_data);
|
||||
// Puts event on the output queue. Called by V8.
|
||||
// This is where V8 hands off
|
||||
// processing of the event to the DebugMessageThread thread,
|
||||
// which forwards it to the debug_message_handler set by the API.
|
||||
void SendMessage(Vector<uint16_t> event_json);
|
||||
// Formats an event into JSON, and calls SendMessage.
|
||||
void SetEventJSONFromEvent(Handle<Object> event_data);
|
||||
// Puts a command coming from the public API on the queue. Called
|
||||
// by the API client thread. This is where the API client hands off
|
||||
// processing of the command to the DebugMessageThread thread.
|
||||
void ProcessCommand(Vector<uint16_t> command);
|
||||
void OnDebuggerInactive();
|
||||
|
||||
// Main function of DebugMessageThread thread.
|
||||
void Run();
|
||||
|
||||
bool host_running_; // Is the debugging host running or stopped?
|
||||
Semaphore* command_received_; // Non-zero when command queue is non-empty.
|
||||
Semaphore* message_received_; // Exactly equal to message queue length.
|
||||
private:
|
||||
bool TwoByteEqualsAscii(Vector<uint16_t> two_byte, const char* ascii);
|
||||
|
||||
static const int kQueueInitialSize = 4;
|
||||
LockingMessageQueue command_queue_;
|
||||
LockingMessageQueue message_queue_;
|
||||
DISALLOW_COPY_AND_ASSIGN(DebugMessageThread);
|
||||
};
|
||||
|
||||
|
||||
// This class is used for entering the debugger. Create an instance in the stack
|
||||
// to enter the debugger. This will set the current break state, make sure the
|
||||
// debugger is loaded and switch to the debugger context. If the debugger for
|
||||
// some reason could not be entered FailedToEnter will return true.
|
||||
class EnterDebugger BASE_EMBEDDED {
|
||||
public:
|
||||
EnterDebugger() : set_(!it_.done()) {
|
||||
// If there is no JavaScript frames on the stack don't switch to new break
|
||||
// and break frame.
|
||||
if (set_) {
|
||||
// Store the previous break is and frame id.
|
||||
break_id_ = Top::break_id();
|
||||
break_frame_id_ = Top::break_frame_id();
|
||||
|
||||
// Create the new break info.
|
||||
Top::new_break(it_.frame()->id());
|
||||
}
|
||||
|
||||
// Make sure that debugger is loaded and enter the debugger context.
|
||||
load_failed_ = !Debug::Load();
|
||||
if (!load_failed_) {
|
||||
// NOTE the member variable save which saves the previous context before
|
||||
// this change.
|
||||
Top::set_context(*Debug::debug_context());
|
||||
}
|
||||
}
|
||||
|
||||
~EnterDebugger() {
|
||||
if (set_) {
|
||||
// Restore to the previous break state.
|
||||
Top::set_break(break_frame_id_, break_id_);
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the debugger could be entered.
|
||||
inline bool FailedToEnter() { return load_failed_; }
|
||||
|
||||
private:
|
||||
JavaScriptFrameIterator it_;
|
||||
const bool set_; // Was the break actually set?
|
||||
StackFrame::Id break_frame_id_; // Previous break frame id.
|
||||
int break_id_; // Previous break id.
|
||||
bool load_failed_; // Did the debugger fail to load?
|
||||
SaveContext save_; // Saves previous context.
|
||||
};
|
||||
|
||||
|
||||
// Stack allocated class for disabling break.
|
||||
class DisableBreak BASE_EMBEDDED {
|
||||
public:
|
||||
// Enter the debugger by storing the previous top context and setting the
|
||||
// current top context to the debugger context.
|
||||
explicit DisableBreak(bool disable_break) {
|
||||
prev_disable_break_ = Debug::disable_break();
|
||||
Debug::set_disable_break(disable_break);
|
||||
}
|
||||
~DisableBreak() {
|
||||
Debug::set_disable_break(prev_disable_break_);
|
||||
}
|
||||
|
||||
private:
|
||||
// The previous state of the disable break used to restore the value when this
|
||||
// object is destructed.
|
||||
bool prev_disable_break_;
|
||||
};
|
||||
|
||||
|
||||
// Debug_Address encapsulates the Address pointers used in generating debug
|
||||
// code.
|
||||
class Debug_Address {
|
||||
public:
|
||||
Debug_Address(Debug::AddressId id, int reg = 0)
|
||||
: id_(id), reg_(reg) {
|
||||
ASSERT(reg == 0 || id == Debug::k_register_address);
|
||||
}
|
||||
|
||||
static Debug_Address AfterBreakTarget() {
|
||||
return Debug_Address(Debug::k_after_break_target_address);
|
||||
}
|
||||
|
||||
static Debug_Address DebugBreakReturn() {
|
||||
return Debug_Address(Debug::k_debug_break_return_address);
|
||||
}
|
||||
|
||||
static Debug_Address Register(int reg) {
|
||||
return Debug_Address(Debug::k_register_address, reg);
|
||||
}
|
||||
|
||||
Address address() const {
|
||||
switch (id_) {
|
||||
case Debug::k_after_break_target_address:
|
||||
return reinterpret_cast<Address>(Debug::after_break_target_address());
|
||||
case Debug::k_debug_break_return_address:
|
||||
return reinterpret_cast<Address>(Debug::debug_break_return_address());
|
||||
case Debug::k_register_address:
|
||||
return reinterpret_cast<Address>(Debug::register_address(reg_));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
private:
|
||||
Debug::AddressId id_;
|
||||
int reg_;
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_V8_DEBUG_H_
|
@ -1,937 +0,0 @@
|
||||
// Copyright 2007-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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "disasm.h"
|
||||
#include "macro-assembler.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace assembler { namespace arm {
|
||||
|
||||
namespace v8i = v8::internal;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Decoder decodes and disassembles instructions into an output buffer.
|
||||
// It uses the converter to convert register names and call destinations into
|
||||
// more informative description.
|
||||
class Decoder {
|
||||
public:
|
||||
Decoder(const disasm::NameConverter& converter,
|
||||
v8::internal::Vector<char> out_buffer)
|
||||
: converter_(converter),
|
||||
out_buffer_(out_buffer),
|
||||
out_buffer_pos_(0) {
|
||||
out_buffer_[out_buffer_pos_] = '\0';
|
||||
}
|
||||
|
||||
~Decoder() {}
|
||||
|
||||
// Writes one disassembled instruction into 'buffer' (0-terminated).
|
||||
// Returns the length of the disassembled machine instruction in bytes.
|
||||
int InstructionDecode(byte* instruction);
|
||||
|
||||
private:
|
||||
const disasm::NameConverter& converter_;
|
||||
v8::internal::Vector<char> out_buffer_;
|
||||
int out_buffer_pos_;
|
||||
|
||||
void PrintChar(const char ch);
|
||||
void Print(const char* str);
|
||||
|
||||
void PrintRegister(int reg);
|
||||
void PrintCondition(Instr* instr);
|
||||
void PrintShiftRm(Instr* instr);
|
||||
void PrintShiftImm(Instr* instr);
|
||||
|
||||
int FormatOption(Instr* instr, const char* option);
|
||||
void Format(Instr* instr, const char* format);
|
||||
void Unknown(Instr* instr);
|
||||
|
||||
void DecodeType0(Instr* instr);
|
||||
void DecodeType1(Instr* instr);
|
||||
void DecodeType2(Instr* instr);
|
||||
void DecodeType3(Instr* instr);
|
||||
void DecodeType4(Instr* instr);
|
||||
void DecodeType5(Instr* instr);
|
||||
void DecodeType6(Instr* instr);
|
||||
void DecodeType7(Instr* instr);
|
||||
};
|
||||
|
||||
|
||||
// Append the ch to the output buffer.
|
||||
void Decoder::PrintChar(const char ch) {
|
||||
out_buffer_[out_buffer_pos_++] = ch;
|
||||
}
|
||||
|
||||
|
||||
// Append the str to the output buffer.
|
||||
void Decoder::Print(const char* str) {
|
||||
char cur = *str++;
|
||||
while (cur != 0 && (out_buffer_pos_ < (out_buffer_.length()-1))) {
|
||||
PrintChar(cur);
|
||||
cur = *str++;
|
||||
}
|
||||
out_buffer_[out_buffer_pos_] = 0;
|
||||
}
|
||||
|
||||
|
||||
static const char* cond_names[16] = {
|
||||
"eq", "ne", "cs" , "cc" , "mi" , "pl" , "vs" , "vc" ,
|
||||
"hi", "ls", "ge", "lt", "gt", "le", "", "invalid",
|
||||
};
|
||||
|
||||
|
||||
// Print the condition guarding the instruction.
|
||||
void Decoder::PrintCondition(Instr* instr) {
|
||||
Print(cond_names[instr->ConditionField()]);
|
||||
}
|
||||
|
||||
|
||||
// Print the register name according to the active name converter.
|
||||
void Decoder::PrintRegister(int reg) {
|
||||
Print(converter_.NameOfCPURegister(reg));
|
||||
}
|
||||
|
||||
|
||||
static const char* shift_names[4] = {
|
||||
"lsl", "lsr", "asr", "ror"
|
||||
};
|
||||
|
||||
|
||||
// Print the register shift operands for the instruction. Generally used for
|
||||
// data processing instructions.
|
||||
void Decoder::PrintShiftRm(Instr* instr) {
|
||||
Shift shift = instr->ShiftField();
|
||||
int shift_amount = instr->ShiftAmountField();
|
||||
int rm = instr->RmField();
|
||||
|
||||
PrintRegister(rm);
|
||||
|
||||
if ((instr->RegShiftField() == 0) && (shift == LSL) && (shift_amount == 0)) {
|
||||
// Special case for using rm only.
|
||||
return;
|
||||
}
|
||||
if (instr->RegShiftField() == 0) {
|
||||
// by immediate
|
||||
if ((shift == ROR) && (shift_amount == 0)) {
|
||||
Print(", RRX");
|
||||
return;
|
||||
} else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
|
||||
shift_amount = 32;
|
||||
}
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
", %s #%d",
|
||||
shift_names[shift], shift_amount);
|
||||
} else {
|
||||
// by register
|
||||
int rs = instr->RsField();
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
", %s ", shift_names[shift]);
|
||||
PrintRegister(rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Print the immediate operand for the instruction. Generally used for data
|
||||
// processing instructions.
|
||||
void Decoder::PrintShiftImm(Instr* instr) {
|
||||
int rotate = instr->RotateField() * 2;
|
||||
int immed8 = instr->Immed8Field();
|
||||
int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
"#%d", imm);
|
||||
}
|
||||
|
||||
|
||||
// FormatOption takes a formatting string and interprets it based on
|
||||
// the current instructions. The format string points to the first
|
||||
// character of the option string (the option escape has already been
|
||||
// consumed by the caller.) FormatOption returns the number of
|
||||
// characters that were consumed from the formatting string.
|
||||
int Decoder::FormatOption(Instr* instr, const char* format) {
|
||||
switch (format[0]) {
|
||||
case 'a': { // 'a: accumulate multiplies
|
||||
if (instr->Bit(21) == 0) {
|
||||
Print("ul");
|
||||
} else {
|
||||
Print("la");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'b': { // 'b: byte loads or stores
|
||||
if (instr->HasB()) {
|
||||
Print("b");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'c': { // 'cond: conditional execution
|
||||
ASSERT((format[1] == 'o') && (format[2] == 'n') && (format[3] =='d'));
|
||||
PrintCondition(instr);
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
case 'h': { // 'h: halfword operation for extra loads and stores
|
||||
if (instr->HasH()) {
|
||||
Print("h");
|
||||
} else {
|
||||
Print("b");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'i': { // 'imm: immediate value for data processing instructions
|
||||
ASSERT((format[1] == 'm') && (format[2] == 'm'));
|
||||
PrintShiftImm(instr);
|
||||
return 3;
|
||||
break;
|
||||
}
|
||||
case 'l': { // 'l: branch and link
|
||||
if (instr->HasLink()) {
|
||||
Print("l");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'm': { // 'msg: for simulator break instructions
|
||||
if (format[1] == 'e') {
|
||||
ASSERT((format[2] == 'm') && (format[3] == 'o') && (format[4] == 'p'));
|
||||
if (instr->HasL()) {
|
||||
Print("ldr");
|
||||
} else {
|
||||
Print("str");
|
||||
}
|
||||
return 5;
|
||||
} else {
|
||||
ASSERT(format[1] == 's' && format[2] == 'g');
|
||||
byte* str =
|
||||
reinterpret_cast<byte*>(instr->InstructionBits() & 0x0fffffff);
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
"%s", converter_.NameInCode(str));
|
||||
return 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'o': {
|
||||
ASSERT(format[1] == 'f' && format[2] == 'f');
|
||||
if (format[3] == '1') {
|
||||
// 'off12: 12-bit offset for load and store instructions
|
||||
ASSERT(format[4] == '2');
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
"%d", instr->Offset12Field());
|
||||
return 5;
|
||||
} else {
|
||||
// 'off8: 8-bit offset for extra load and store instructions
|
||||
ASSERT(format[3] == '8');
|
||||
int offs8 = (instr->ImmedHField() << 4) | instr->ImmedLField();
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
"%d", offs8);
|
||||
return 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': { // 'pu: P and U bits for load and store instructions
|
||||
ASSERT(format[1] == 'u');
|
||||
switch (instr->PUField()) {
|
||||
case 0: {
|
||||
Print("da");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
Print("ia");
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Print("db");
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
Print("ib");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
case 'r': {
|
||||
if (format[1] == 'n') { // 'rn: Rn register
|
||||
int reg = instr->RnField();
|
||||
PrintRegister(reg);
|
||||
return 2;
|
||||
} else if (format[1] == 'd') { // 'rd: Rd register
|
||||
int reg = instr->RdField();
|
||||
PrintRegister(reg);
|
||||
return 2;
|
||||
} else if (format[1] == 's') { // 'rs: Rs register
|
||||
int reg = instr->RsField();
|
||||
PrintRegister(reg);
|
||||
return 2;
|
||||
} else if (format[1] == 'm') { // 'rm: Rm register
|
||||
int reg = instr->RmField();
|
||||
PrintRegister(reg);
|
||||
return 2;
|
||||
} else if (format[1] == 'l') {
|
||||
// 'rlist: register list for load and store multiple instructions
|
||||
ASSERT(format[2] == 'i' && format[3] == 's' && format[4] == 't');
|
||||
int rlist = instr->RlistField();
|
||||
int reg = 0;
|
||||
Print("{");
|
||||
while (rlist != 0) {
|
||||
if ((rlist & 1) != 0) {
|
||||
PrintRegister(reg);
|
||||
if ((rlist >> 1) != 0) {
|
||||
Print(", ");
|
||||
}
|
||||
}
|
||||
reg++;
|
||||
rlist >>= 1;
|
||||
}
|
||||
Print("}");
|
||||
return 5;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
if (format[1] == 'h') { // 'shift_rm: register shift operands
|
||||
ASSERT(format[2] == 'i' && format[3] == 'f' && format[4] == 't'
|
||||
&& format[5] == '_' && format[6] == 'r' && format[7] == 'm');
|
||||
PrintShiftRm(instr);
|
||||
return 8;
|
||||
} else if (format[1] == 'w') {
|
||||
ASSERT(format[2] == 'i');
|
||||
SoftwareInterruptCodes swi = instr->SwiField();
|
||||
switch (swi) {
|
||||
case call_rt_r5:
|
||||
Print("call_rt_r5");
|
||||
break;
|
||||
case call_rt_r2:
|
||||
Print("call_rt_r2");
|
||||
break;
|
||||
case break_point:
|
||||
Print("break_point");
|
||||
break;
|
||||
default:
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(
|
||||
out_buffer_ + out_buffer_pos_,
|
||||
"%d",
|
||||
swi);
|
||||
break;
|
||||
}
|
||||
return 3;
|
||||
} else if (format[1] == 'i') { // 'sign: signed extra loads and stores
|
||||
ASSERT(format[2] == 'g' && format[3] == 'n');
|
||||
if (instr->HasSign()) {
|
||||
Print("s");
|
||||
}
|
||||
return 4;
|
||||
break;
|
||||
} else { // 's: S field of data processing instructions
|
||||
if (instr->HasS()) {
|
||||
Print("s");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't': { // 'target: target of branch instructions
|
||||
ASSERT(format[1] == 'a' && format[2] == 'r' && format[3] == 'g'
|
||||
&& format[4] == 'e' && format[5] == 't');
|
||||
int off = (instr->SImmed24Field() << 2) + 8;
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(
|
||||
out_buffer_ + out_buffer_pos_,
|
||||
"%+d -> %s",
|
||||
off,
|
||||
converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + off));
|
||||
return 6;
|
||||
break;
|
||||
}
|
||||
case 'u': { // 'u: signed or unsigned multiplies
|
||||
if (instr->Bit(22) == 0) {
|
||||
Print("u");
|
||||
} else {
|
||||
Print("s");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'w': { // 'w: W field of load and store instructions
|
||||
if (instr->HasW()) {
|
||||
Print("!");
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Format takes a formatting string for a whole instruction and prints it into
|
||||
// the output buffer. All escaped options are handed to FormatOption to be
|
||||
// parsed further.
|
||||
void Decoder::Format(Instr* instr, const char* format) {
|
||||
char cur = *format++;
|
||||
while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
|
||||
if (cur == '\'') { // Single quote is used as the formatting escape.
|
||||
format += FormatOption(instr, format);
|
||||
} else {
|
||||
out_buffer_[out_buffer_pos_++] = cur;
|
||||
}
|
||||
cur = *format++;
|
||||
}
|
||||
out_buffer_[out_buffer_pos_] = '\0';
|
||||
}
|
||||
|
||||
|
||||
// For currently unimplemented decodings the disassembler calls Unknown(instr)
|
||||
// which will just print "unknown" of the instruction bits.
|
||||
void Decoder::Unknown(Instr* instr) {
|
||||
Format(instr, "unknown");
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType0(Instr* instr) {
|
||||
if (instr->IsSpecialType0()) {
|
||||
// multiply instruction or extra loads and stores
|
||||
if (instr->Bits(7, 4) == 9) {
|
||||
if (instr->Bit(24) == 0) {
|
||||
// multiply instructions
|
||||
if (instr->Bit(23) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
Format(instr, "mul'cond's 'rd, 'rm, 'rs");
|
||||
} else {
|
||||
Format(instr, "mla'cond's 'rd, 'rm, 'rs, 'rn");
|
||||
}
|
||||
} else {
|
||||
Format(instr, "'um'al'cond's 'rn, 'rd, 'rs, 'rm");
|
||||
}
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
}
|
||||
} else {
|
||||
// extra load/store instructions
|
||||
switch (instr->PUField()) {
|
||||
case 0: {
|
||||
if (instr->Bit(22) == 0) {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm");
|
||||
} else {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (instr->Bit(22) == 0) {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm");
|
||||
} else {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (instr->Bit(22) == 0) {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w");
|
||||
} else {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (instr->Bit(22) == 0) {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w");
|
||||
} else {
|
||||
Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The PU field is a 2-bit field.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch (instr->OpcodeField()) {
|
||||
case AND: {
|
||||
Format(instr, "and'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case EOR: {
|
||||
Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case SUB: {
|
||||
Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case RSB: {
|
||||
Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case ADD: {
|
||||
Format(instr, "add'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case ADC: {
|
||||
Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case SBC: {
|
||||
Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case RSC: {
|
||||
Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case TST: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "tst'cond 'rn, 'shift_rm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEQ: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "teq'cond 'rn, 'shift_rm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMP: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "cmp'cond 'rn, 'shift_rm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMN: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "cmn'cond 'rn, 'shift_rm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ORR: {
|
||||
Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case MOV: {
|
||||
Format(instr, "mov'cond's 'rd, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case BIC: {
|
||||
Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
case MVN: {
|
||||
Format(instr, "mvn'cond's 'rd, 'shift_rm");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The Opcode field is a 4-bit field.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType1(Instr* instr) {
|
||||
switch (instr->OpcodeField()) {
|
||||
case AND: {
|
||||
Format(instr, "and'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case EOR: {
|
||||
Format(instr, "eor'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case SUB: {
|
||||
Format(instr, "sub'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case RSB: {
|
||||
Format(instr, "rsb'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case ADD: {
|
||||
Format(instr, "add'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case ADC: {
|
||||
Format(instr, "adc'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case SBC: {
|
||||
Format(instr, "sbc'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case RSC: {
|
||||
Format(instr, "rsc'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case TST: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "tst'cond 'rn, 'imm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEQ: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "teq'cond 'rn, 'imm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMP: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "cmp'cond 'rn, 'imm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMN: {
|
||||
if (instr->HasS()) {
|
||||
Format(instr, "cmn'cond 'rn, 'imm");
|
||||
} else {
|
||||
Unknown(instr); // not used by V8
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ORR: {
|
||||
Format(instr, "orr'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case MOV: {
|
||||
Format(instr, "mov'cond's 'rd, 'imm");
|
||||
break;
|
||||
}
|
||||
case BIC: {
|
||||
Format(instr, "bic'cond's 'rd, 'rn, 'imm");
|
||||
break;
|
||||
}
|
||||
case MVN: {
|
||||
Format(instr, "mvn'cond's 'rd, 'imm");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The Opcode field is a 4-bit field.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType2(Instr* instr) {
|
||||
switch (instr->PUField()) {
|
||||
case 0: {
|
||||
if (instr->HasW()) {
|
||||
Unknown(instr); // not used in V8
|
||||
return;
|
||||
}
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (instr->HasW()) {
|
||||
Unknown(instr); // not used in V8
|
||||
return;
|
||||
}
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The PU field is a 2-bit field.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType3(Instr* instr) {
|
||||
switch (instr->PUField()) {
|
||||
case 0: {
|
||||
ASSERT(!instr->HasW());
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ASSERT(!instr->HasW());
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The PU field is a 2-bit field.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType4(Instr* instr) {
|
||||
ASSERT(instr->Bit(22) == 0); // Privileged mode currently not supported.
|
||||
if (instr->HasL()) {
|
||||
Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
|
||||
} else {
|
||||
Format(instr, "stm'cond'pu 'rn'w, 'rlist");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType5(Instr* instr) {
|
||||
Format(instr, "b'l'cond 'target");
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType6(Instr* instr) {
|
||||
// Coprocessor instructions currently not supported.
|
||||
Unknown(instr);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeType7(Instr* instr) {
|
||||
if (instr->Bit(24) == 1) {
|
||||
Format(instr, "swi'cond 'swi");
|
||||
} else {
|
||||
// Coprocessor instructions currently not supported.
|
||||
Unknown(instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Disassemble the instruction at *instr_ptr into the output buffer.
|
||||
int Decoder::InstructionDecode(byte* instr_ptr) {
|
||||
Instr* instr = Instr::At(instr_ptr);
|
||||
// Print raw instruction bytes.
|
||||
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||
"%08x ",
|
||||
instr->InstructionBits());
|
||||
if (instr->ConditionField() == special_condition) {
|
||||
Format(instr, "break 'msg");
|
||||
return Instr::kInstrSize;
|
||||
}
|
||||
switch (instr->TypeField()) {
|
||||
case 0: {
|
||||
DecodeType0(instr);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
DecodeType1(instr);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
DecodeType2(instr);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
DecodeType3(instr);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
DecodeType4(instr);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
DecodeType5(instr);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
DecodeType6(instr);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
DecodeType7(instr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The type field is 3-bits in the ARM encoding.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Instr::kInstrSize;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace assembler::arm
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace disasm {
|
||||
|
||||
static const char* reg_names[16] = {
|
||||
"r0", "r1", "r2" , "r3" , "r4" , "r5" , "r6" , "r7" ,
|
||||
"r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc",
|
||||
};
|
||||
|
||||
|
||||
const char* NameConverter::NameOfAddress(byte* addr) const {
|
||||
static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
|
||||
v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
|
||||
return tmp_buffer.start();
|
||||
}
|
||||
|
||||
|
||||
const char* NameConverter::NameOfConstant(byte* addr) const {
|
||||
return NameOfAddress(addr);
|
||||
}
|
||||
|
||||
|
||||
const char* NameConverter::NameOfCPURegister(int reg) const {
|
||||
const char* result;
|
||||
if ((0 <= reg) && (reg < 16)) {
|
||||
result = reg_names[reg];
|
||||
} else {
|
||||
result = "noreg";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const char* NameConverter::NameOfXMMRegister(int reg) const {
|
||||
UNREACHABLE(); // ARM does not have any XMM registers
|
||||
return "noxmmreg";
|
||||
}
|
||||
|
||||
|
||||
const char* NameConverter::NameInCode(byte* addr) const {
|
||||
// The default name converter is called for unknown code. So we will not try
|
||||
// to access any memory.
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static NameConverter defaultConverter;
|
||||
|
||||
Disassembler::Disassembler() : converter_(defaultConverter) {}
|
||||
|
||||
|
||||
Disassembler::Disassembler(const NameConverter& converter)
|
||||
: converter_(converter) {}
|
||||
|
||||
|
||||
Disassembler::~Disassembler() {}
|
||||
|
||||
|
||||
int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
|
||||
byte* instruction) {
|
||||
assembler::arm::Decoder d(converter_, buffer);
|
||||
return d.InstructionDecode(instruction);
|
||||
}
|
||||
|
||||
|
||||
int Disassembler::ConstantPoolSizeAt(byte* instruction) {
|
||||
int instruction_bits = *(reinterpret_cast<int*>(instruction));
|
||||
if ((instruction_bits & 0xfff00000) == 0x03000000) {
|
||||
return instruction_bits & 0x0000ffff;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
|
||||
Disassembler d;
|
||||
for (byte* pc = begin; pc < end;) {
|
||||
v8::internal::EmbeddedVector<char, 128> buffer;
|
||||
buffer[0] = '\0';
|
||||
byte* prev_pc = pc;
|
||||
pc += d.InstructionDecode(buffer, pc);
|
||||
fprintf(f, "%p %08x %s\n",
|
||||
prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace disasm
|
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
||||
// Copyright 2007-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_DISASM_H_
|
||||
#define V8_DISASM_H_
|
||||
|
||||
namespace disasm {
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
// Interface and default implementation for converting addresses and
|
||||
// register-numbers to text. The default implementation is machine
|
||||
// specific.
|
||||
class NameConverter {
|
||||
public:
|
||||
virtual ~NameConverter() {}
|
||||
virtual const char* NameOfCPURegister(int reg) const;
|
||||
virtual const char* NameOfXMMRegister(int reg) const;
|
||||
virtual const char* NameOfAddress(byte* addr) const;
|
||||
virtual const char* NameOfConstant(byte* addr) const;
|
||||
virtual const char* NameInCode(byte* addr) const;
|
||||
};
|
||||
|
||||
|
||||
// A generic Disassembler interface
|
||||
class Disassembler {
|
||||
public:
|
||||
// Uses default NameConverter.
|
||||
Disassembler();
|
||||
|
||||
// Caller deallocates converter.
|
||||
explicit Disassembler(const NameConverter& converter);
|
||||
|
||||
virtual ~Disassembler();
|
||||
|
||||
// Writes one disassembled instruction into 'buffer' (0-terminated).
|
||||
// Returns the length of the disassembled machine instruction in bytes.
|
||||
int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
|
||||
|
||||
// Returns -1 if instruction does not mark the beginning of a constant pool,
|
||||
// or the number of entries in the constant pool beginning here.
|
||||
int ConstantPoolSizeAt(byte* instruction);
|
||||
|
||||
// Write disassembly into specified file 'f' using specified NameConverter
|
||||
// (see constructor).
|
||||
static void Disassemble(FILE* f, byte* begin, byte* end);
|
||||
private:
|
||||
const NameConverter& converter_;
|
||||
};
|
||||
|
||||
} // namespace disasm
|
||||
|
||||
#endif // V8_DISASM_H_
|
@ -1,309 +0,0 @@
|
||||
// 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 "code-stubs.h"
|
||||
#include "codegen.h"
|
||||
#include "debug.h"
|
||||
#include "disasm.h"
|
||||
#include "disassembler.h"
|
||||
#include "macro-assembler.h"
|
||||
#include "serialize.h"
|
||||
#include "string-stream.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
|
||||
void Disassembler::Dump(FILE* f, byte* begin, byte* end) {
|
||||
for (byte* pc = begin; pc < end; pc++) {
|
||||
if (f == NULL) {
|
||||
PrintF("%p %4d %02x\n", pc, pc - begin, *pc);
|
||||
} else {
|
||||
fprintf(f, "%p %4d %02x\n", pc, pc - begin, *pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class V8NameConverter: public disasm::NameConverter {
|
||||
public:
|
||||
explicit V8NameConverter(Code* code) : code_(code) {}
|
||||
virtual const char* NameOfAddress(byte* pc) const;
|
||||
virtual const char* NameInCode(byte* addr) const;
|
||||
Code* code() const { return code_; }
|
||||
private:
|
||||
Code* code_;
|
||||
};
|
||||
|
||||
|
||||
const char* V8NameConverter::NameOfAddress(byte* pc) const {
|
||||
static v8::internal::EmbeddedVector<char, 128> buffer;
|
||||
|
||||
const char* name = Builtins::Lookup(pc);
|
||||
if (name != NULL) {
|
||||
OS::SNPrintF(buffer, "%s (%p)", name, pc);
|
||||
return buffer.start();
|
||||
}
|
||||
|
||||
if (code_ != NULL) {
|
||||
int offs = pc - code_->instruction_start();
|
||||
// print as code offset, if it seems reasonable
|
||||
if (0 <= offs && offs < code_->instruction_size()) {
|
||||
OS::SNPrintF(buffer, "%d (%p)", offs, pc);
|
||||
return buffer.start();
|
||||
}
|
||||
}
|
||||
|
||||
return disasm::NameConverter::NameOfAddress(pc);
|
||||
}
|
||||
|
||||
|
||||
const char* V8NameConverter::NameInCode(byte* addr) const {
|
||||
// The V8NameConverter is used for well known code, so we can "safely"
|
||||
// dereference pointers in generated code.
|
||||
return (code_ != NULL) ? reinterpret_cast<const char*>(addr) : "";
|
||||
}
|
||||
|
||||
|
||||
static void DumpBuffer(FILE* f, char* buff) {
|
||||
if (f == NULL) {
|
||||
PrintF("%s", buff);
|
||||
} else {
|
||||
fprintf(f, "%s", buff);
|
||||
}
|
||||
}
|
||||
|
||||
static const int kOutBufferSize = 256 + String::kMaxShortPrintLength;
|
||||
static const int kRelocInfoPosition = 57;
|
||||
|
||||
static int DecodeIt(FILE* f,
|
||||
const V8NameConverter& converter,
|
||||
byte* begin,
|
||||
byte* end) {
|
||||
NoHandleAllocation ha;
|
||||
AssertNoAllocation no_alloc;
|
||||
ExternalReferenceEncoder ref_encoder;
|
||||
|
||||
v8::internal::EmbeddedVector<char, 128> decode_buffer;
|
||||
v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
|
||||
byte* pc = begin;
|
||||
disasm::Disassembler d(converter);
|
||||
RelocIterator* it = NULL;
|
||||
if (converter.code() != NULL) {
|
||||
it = new RelocIterator(converter.code());
|
||||
} else {
|
||||
// No relocation information when printing code stubs.
|
||||
}
|
||||
int constants = -1; // no constants being decoded at the start
|
||||
|
||||
while (pc < end) {
|
||||
// First decode instruction so that we know its length.
|
||||
byte* prev_pc = pc;
|
||||
if (constants > 0) {
|
||||
OS::SNPrintF(decode_buffer,
|
||||
"%08x constant",
|
||||
*reinterpret_cast<int32_t*>(pc));
|
||||
constants--;
|
||||
pc += 4;
|
||||
} else {
|
||||
int num_const = d.ConstantPoolSizeAt(pc);
|
||||
if (num_const >= 0) {
|
||||
OS::SNPrintF(decode_buffer,
|
||||
"%08x constant pool begin",
|
||||
*reinterpret_cast<int32_t*>(pc));
|
||||
constants = num_const;
|
||||
pc += 4;
|
||||
} else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
|
||||
it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
|
||||
// raw pointer embedded in code stream, e.g., jump table
|
||||
byte* ptr = *reinterpret_cast<byte**>(pc);
|
||||
OS::SNPrintF(decode_buffer,
|
||||
"%08x jump table entry %4d",
|
||||
reinterpret_cast<int32_t>(ptr),
|
||||
ptr - begin);
|
||||
pc += 4;
|
||||
} else {
|
||||
decode_buffer[0] = '\0';
|
||||
pc += d.InstructionDecode(decode_buffer, pc);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect RelocInfo for this instruction (prev_pc .. pc-1)
|
||||
List<const char*> comments(4);
|
||||
List<byte*> pcs(1);
|
||||
List<RelocInfo::Mode> rmodes(1);
|
||||
List<intptr_t> datas(1);
|
||||
if (it != NULL) {
|
||||
while (!it->done() && it->rinfo()->pc() < pc) {
|
||||
if (RelocInfo::IsComment(it->rinfo()->rmode())) {
|
||||
// For comments just collect the text.
|
||||
comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
|
||||
} else {
|
||||
// For other reloc info collect all data.
|
||||
pcs.Add(it->rinfo()->pc());
|
||||
rmodes.Add(it->rinfo()->rmode());
|
||||
datas.Add(it->rinfo()->data());
|
||||
}
|
||||
it->next();
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder out(out_buffer.start(), out_buffer.length());
|
||||
|
||||
// Comments.
|
||||
for (int i = 0; i < comments.length(); i++) {
|
||||
out.AddFormatted(" %s\n", comments[i]);
|
||||
}
|
||||
|
||||
// Write out comments, resets outp so that we can format the next line.
|
||||
DumpBuffer(f, out.Finalize());
|
||||
out.Reset();
|
||||
|
||||
// Instruction address and instruction offset.
|
||||
out.AddFormatted("%p %4d ", prev_pc, prev_pc - begin);
|
||||
|
||||
// Instruction.
|
||||
out.AddFormatted("%s", decode_buffer.start());
|
||||
|
||||
// Print all the reloc info for this instruction which are not comments.
|
||||
for (int i = 0; i < pcs.length(); i++) {
|
||||
// Put together the reloc info
|
||||
RelocInfo relocinfo(pcs[i], rmodes[i], datas[i]);
|
||||
|
||||
// Indent the printing of the reloc info.
|
||||
if (i == 0) {
|
||||
// The first reloc info is printed after the disassembled instruction.
|
||||
out.AddPadding(' ', kRelocInfoPosition - out.position());
|
||||
} else {
|
||||
// Additional reloc infos are printed on separate lines.
|
||||
out.AddFormatted("\n");
|
||||
out.AddPadding(' ', kRelocInfoPosition);
|
||||
}
|
||||
|
||||
RelocInfo::Mode rmode = relocinfo.rmode();
|
||||
if (RelocInfo::IsPosition(rmode)) {
|
||||
if (RelocInfo::IsStatementPosition(rmode)) {
|
||||
out.AddFormatted(" ;; debug: statement %d", relocinfo.data());
|
||||
} else {
|
||||
out.AddFormatted(" ;; debug: position %d", relocinfo.data());
|
||||
}
|
||||
} else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
|
||||
HeapStringAllocator allocator;
|
||||
StringStream accumulator(&allocator);
|
||||
relocinfo.target_object()->ShortPrint(&accumulator);
|
||||
SmartPointer<char> obj_name = accumulator.ToCString();
|
||||
out.AddFormatted(" ;; object: %s", *obj_name);
|
||||
} else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
|
||||
const char* reference_name =
|
||||
ref_encoder.NameOfAddress(*relocinfo.target_reference_address());
|
||||
out.AddFormatted(" ;; external reference (%s)", reference_name);
|
||||
} else if (RelocInfo::IsCodeTarget(rmode)) {
|
||||
out.AddFormatted(" ;; code:");
|
||||
if (rmode == RelocInfo::CONSTRUCT_CALL) {
|
||||
out.AddFormatted(" constructor,");
|
||||
}
|
||||
Code* code = Debug::GetCodeTarget(relocinfo.target_address());
|
||||
Code::Kind kind = code->kind();
|
||||
if (code->is_inline_cache_stub()) {
|
||||
if (rmode == RelocInfo::CODE_TARGET_CONTEXT) {
|
||||
out.AddFormatted(" contextual,");
|
||||
}
|
||||
InlineCacheState ic_state = code->ic_state();
|
||||
out.AddFormatted(" %s, %s", Code::Kind2String(kind),
|
||||
Code::ICState2String(ic_state));
|
||||
if (kind == Code::CALL_IC) {
|
||||
out.AddFormatted(", argc = %d", code->arguments_count());
|
||||
}
|
||||
} else if (kind == Code::STUB) {
|
||||
// Reverse lookup required as the minor key cannot be retrieved
|
||||
// from the code object.
|
||||
Object* obj = Heap::code_stubs()->SlowReverseLookup(code);
|
||||
if (obj != Heap::undefined_value()) {
|
||||
ASSERT(obj->IsSmi());
|
||||
// Get the STUB key and extract major and minor key.
|
||||
uint32_t key = Smi::cast(obj)->value();
|
||||
uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
|
||||
ASSERT(code->major_key() == CodeStub::MajorKeyFromKey(key));
|
||||
out.AddFormatted(" %s, %s, ",
|
||||
Code::Kind2String(kind),
|
||||
CodeStub::MajorName(code->major_key()));
|
||||
switch (code->major_key()) {
|
||||
case CodeStub::CallFunction:
|
||||
out.AddFormatted("argc = %d", minor_key);
|
||||
break;
|
||||
case CodeStub::Runtime: {
|
||||
const char* name =
|
||||
RuntimeStub::GetNameFromMinorKey(minor_key);
|
||||
out.AddFormatted("%s", name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
out.AddFormatted("minor: %d", minor_key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.AddFormatted(" %s", Code::Kind2String(kind));
|
||||
}
|
||||
} else {
|
||||
out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode));
|
||||
}
|
||||
}
|
||||
out.AddString("\n");
|
||||
DumpBuffer(f, out.Finalize());
|
||||
out.Reset();
|
||||
}
|
||||
|
||||
delete it;
|
||||
return pc - begin;
|
||||
}
|
||||
|
||||
|
||||
int Disassembler::Decode(FILE* f, byte* begin, byte* end) {
|
||||
V8NameConverter defaultConverter(NULL);
|
||||
return DecodeIt(f, defaultConverter, begin, end);
|
||||
}
|
||||
|
||||
|
||||
// Called by Code::CodePrint.
|
||||
void Disassembler::Decode(FILE* f, Code* code) {
|
||||
byte* begin = Code::cast(code)->instruction_start();
|
||||
byte* end = begin + Code::cast(code)->instruction_size();
|
||||
V8NameConverter v8NameConverter(code);
|
||||
DecodeIt(f, v8NameConverter, begin, end);
|
||||
}
|
||||
|
||||
#else // ENABLE_DISASSEMBLER
|
||||
|
||||
void Disassembler::Dump(FILE* f, byte* begin, byte* end) {}
|
||||
int Disassembler::Decode(FILE* f, byte* begin, byte* end) { return 0; }
|
||||
void Disassembler::Decode(FILE* f, Code* code) {}
|
||||
|
||||
#endif // ENABLE_DISASSEMBLER
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,55 +0,0 @@
|
||||
// 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_DISASSEMBLER_H_
|
||||
#define V8_DISASSEMBLER_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
class Disassembler : public AllStatic {
|
||||
public:
|
||||
// Print the bytes in the interval [begin, end) into f.
|
||||
static void Dump(FILE* f, byte* begin, byte* end);
|
||||
|
||||
// Decode instructions in the the interval [begin, end) and print the
|
||||
// code into f. Returns the number of bytes disassembled or 1 if no
|
||||
// instruction could be decoded.
|
||||
static int Decode(FILE* f, byte* begin, byte* end);
|
||||
|
||||
// Decode instructions in code.
|
||||
static void Decode(FILE* f, Code* code);
|
||||
private:
|
||||
// Decode instruction at pc and print disassembled instruction into f.
|
||||
// Returns the instruction length in bytes, or 1 if the instruction could
|
||||
// not be decoded. The number of characters written is written into
|
||||
// the out parameter char_count.
|
||||
static int Decode(FILE* f, byte* pc, int* char_count);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_DISASSEMBLER_H_
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dtoa needs to have a particular environment set up for it so
|
||||
* instead of using it directly you should use this file.
|
||||
*
|
||||
* The way it works is that when you link with it, its definitions
|
||||
* of dtoa, strtod etc. override the default ones. So if you fail
|
||||
* to link with this library everything will still work, it's just
|
||||
* subtly wrong.
|
||||
*/
|
||||
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(WIN32)
|
||||
#include <endian.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
/* The floating point word order on ARM is big endian when floating point
|
||||
* emulation is used, even if the byte order is little endian */
|
||||
#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(WIN32) && \
|
||||
__FLOAT_WORD_ORDER == __BIG_ENDIAN
|
||||
#define IEEE_MC68k
|
||||
#else
|
||||
#define IEEE_8087
|
||||
#endif
|
||||
|
||||
#define __MATH_H__
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* stdlib.h on Apple's 10.5 and later SDKs will mangle the name of strtod.
|
||||
* If it's included after strtod is redefined as gay_strtod, it will mangle
|
||||
* the name of gay_strtod, which is unwanted. */
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
/* Make sure we use the David M. Gay version of strtod(). On Linux, we
|
||||
* cannot use the same name (maybe the function does not have weak
|
||||
* linkage?). */
|
||||
#define strtod gay_strtod
|
||||
#include "third_party/dtoa/dtoa.c"
|
@ -1,529 +0,0 @@
|
||||
// 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 <stdlib.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "api.h"
|
||||
#include "codegen-inl.h"
|
||||
|
||||
#if defined(ARM) || defined (__arm__) || defined(__thumb__)
|
||||
#include "simulator-arm.h"
|
||||
#else // ia32
|
||||
#include "simulator-ia32.h"
|
||||
#endif
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
static Handle<Object> Invoke(bool construct,
|
||||
Handle<JSFunction> func,
|
||||
Handle<Object> receiver,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* has_pending_exception) {
|
||||
// Make sure we have a real function, not a boilerplate function.
|
||||
ASSERT(!func->IsBoilerplate());
|
||||
|
||||
// Entering JavaScript.
|
||||
VMState state(JS);
|
||||
|
||||
// Guard the stack against too much recursion.
|
||||
StackGuard guard;
|
||||
|
||||
// Placeholder for return value.
|
||||
Object* value = reinterpret_cast<Object*>(kZapValue);
|
||||
|
||||
typedef Object* (*JSEntryFunction)(
|
||||
byte* entry,
|
||||
Object* function,
|
||||
Object* receiver,
|
||||
int argc,
|
||||
Object*** args);
|
||||
|
||||
Handle<Code> code;
|
||||
if (construct) {
|
||||
JSConstructEntryStub stub;
|
||||
code = stub.GetCode();
|
||||
} else {
|
||||
JSEntryStub stub;
|
||||
code = stub.GetCode();
|
||||
}
|
||||
|
||||
{
|
||||
// Save and restore context around invocation and block the
|
||||
// allocation of handles without explicit handle scopes.
|
||||
SaveContext save;
|
||||
NoHandleAllocation na;
|
||||
JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
|
||||
|
||||
// Call the function through the right JS entry stub.
|
||||
value = CALL_GENERATED_CODE(entry, func->code()->entry(), *func,
|
||||
*receiver, argc, args);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
value->Verify();
|
||||
#endif
|
||||
|
||||
// Update the pending exception and external caught flag and return the value.
|
||||
*has_pending_exception = value->IsException();
|
||||
ASSERT(*has_pending_exception == Top::has_pending_exception());
|
||||
Top::propagate_external_caught();
|
||||
|
||||
// If the pending exception is OutOfMemoryException set out_of_memory in
|
||||
// the global context. Note: We have to mark the global context here
|
||||
// since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
|
||||
// set it.
|
||||
if (*has_pending_exception) {
|
||||
if (Top::pending_exception() == Failure::OutOfMemoryException()) {
|
||||
Top::context()->mark_out_of_memory();
|
||||
}
|
||||
}
|
||||
|
||||
return Handle<Object>(value);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::Call(Handle<JSFunction> func,
|
||||
Handle<Object> receiver,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* pending_exception) {
|
||||
return Invoke(false, func, receiver, argc, args, pending_exception);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::New(Handle<JSFunction> func, int argc,
|
||||
Object*** args, bool* pending_exception) {
|
||||
return Invoke(true, func, Top::global(), argc, args, pending_exception);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::TryCall(Handle<JSFunction> func,
|
||||
Handle<Object> receiver,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* caught_exception) {
|
||||
// Enter a try-block while executing the JavaScript code. To avoid
|
||||
// duplicate error printing it must be non-verbose. Also, to avoid
|
||||
// creating message objects during stack overflow we shouldn't
|
||||
// capture messages.
|
||||
v8::TryCatch catcher;
|
||||
catcher.SetVerbose(false);
|
||||
catcher.SetCaptureMessage(false);
|
||||
|
||||
Handle<Object> result = Invoke(false, func, receiver, argc, args,
|
||||
caught_exception);
|
||||
|
||||
if (*caught_exception) {
|
||||
ASSERT(catcher.HasCaught());
|
||||
ASSERT(Top::has_pending_exception());
|
||||
ASSERT(Top::external_caught_exception());
|
||||
Top::optional_reschedule_exception(true);
|
||||
result = v8::Utils::OpenHandle(*catcher.Exception());
|
||||
}
|
||||
|
||||
ASSERT(!Top::has_pending_exception());
|
||||
ASSERT(!Top::external_caught_exception());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
|
||||
ASSERT(!object->IsJSFunction());
|
||||
|
||||
// If you return a function from here, it will be called when an
|
||||
// attempt is made to call the given object as a function.
|
||||
|
||||
// The regular expression code here is really meant more as an
|
||||
// example than anything else. KJS does not support calling regular
|
||||
// expressions as functions, but SpiderMonkey does.
|
||||
if (FLAG_call_regexp) {
|
||||
bool is_regexp =
|
||||
object->IsHeapObject() &&
|
||||
(HeapObject::cast(*object)->map()->constructor() ==
|
||||
*Top::regexp_function());
|
||||
|
||||
if (is_regexp) {
|
||||
Handle<String> exec = Factory::exec_symbol();
|
||||
return Handle<Object>(object->GetProperty(*exec));
|
||||
}
|
||||
}
|
||||
|
||||
// Objects created through the API can have an instance-call handler
|
||||
// that should be used when calling the object as a function.
|
||||
if (object->IsHeapObject() &&
|
||||
HeapObject::cast(*object)->map()->has_instance_call_handler()) {
|
||||
return Handle<JSFunction>(
|
||||
Top::global_context()->call_as_function_delegate());
|
||||
}
|
||||
|
||||
return Factory::undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// Static state for stack guards.
|
||||
StackGuard::ThreadLocal StackGuard::thread_local_;
|
||||
|
||||
|
||||
StackGuard::StackGuard() {
|
||||
ExecutionAccess access;
|
||||
if (thread_local_.nesting_++ == 0 &&
|
||||
thread_local_.jslimit_ != kInterruptLimit) {
|
||||
// NOTE: We assume that the stack grows towards lower addresses.
|
||||
ASSERT(thread_local_.jslimit_ == kIllegalLimit);
|
||||
ASSERT(thread_local_.climit_ == kIllegalLimit);
|
||||
|
||||
thread_local_.initial_jslimit_ = thread_local_.jslimit_ =
|
||||
GENERATED_CODE_STACK_LIMIT(kLimitSize);
|
||||
// NOTE: The check for overflow is not safe as there is no guarantee that
|
||||
// the running thread has its stack in all memory up to address 0x00000000.
|
||||
thread_local_.initial_climit_ = thread_local_.climit_ =
|
||||
reinterpret_cast<uintptr_t>(this) >= kLimitSize ?
|
||||
reinterpret_cast<uintptr_t>(this) - kLimitSize : 0;
|
||||
|
||||
if (thread_local_.interrupt_flags_ != 0) {
|
||||
set_limits(kInterruptLimit, access);
|
||||
}
|
||||
}
|
||||
// make sure we have proper limits setup
|
||||
ASSERT(thread_local_.jslimit_ != kIllegalLimit &&
|
||||
thread_local_.climit_ != kIllegalLimit);
|
||||
}
|
||||
|
||||
|
||||
StackGuard::~StackGuard() {
|
||||
ExecutionAccess access;
|
||||
if (--thread_local_.nesting_ == 0) {
|
||||
set_limits(kIllegalLimit, access);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsStackOverflow() {
|
||||
ExecutionAccess access;
|
||||
return (thread_local_.jslimit_ != kInterruptLimit &&
|
||||
thread_local_.climit_ != kInterruptLimit);
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::EnableInterrupts() {
|
||||
ExecutionAccess access;
|
||||
if (IsSet(access)) {
|
||||
set_limits(kInterruptLimit, access);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::SetStackLimit(uintptr_t limit) {
|
||||
ExecutionAccess access;
|
||||
// If the current limits are special (eg due to a pending interrupt) then
|
||||
// leave them alone.
|
||||
if (thread_local_.jslimit_ == thread_local_.initial_jslimit_) {
|
||||
thread_local_.jslimit_ = limit;
|
||||
}
|
||||
if (thread_local_.climit_ == thread_local_.initial_climit_) {
|
||||
thread_local_.climit_ = limit;
|
||||
}
|
||||
thread_local_.initial_climit_ = limit;
|
||||
thread_local_.initial_jslimit_ = limit;
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::DisableInterrupts() {
|
||||
ExecutionAccess access;
|
||||
reset_limits(access);
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsSet(const ExecutionAccess& lock) {
|
||||
return thread_local_.interrupt_flags_ != 0;
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsInterrupted() {
|
||||
ExecutionAccess access;
|
||||
return thread_local_.interrupt_flags_ & INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::Interrupt() {
|
||||
ExecutionAccess access;
|
||||
thread_local_.interrupt_flags_ |= INTERRUPT;
|
||||
set_limits(kInterruptLimit, access);
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsPreempted() {
|
||||
ExecutionAccess access;
|
||||
return thread_local_.interrupt_flags_ & PREEMPT;
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::Preempt() {
|
||||
ExecutionAccess access;
|
||||
thread_local_.interrupt_flags_ |= PREEMPT;
|
||||
set_limits(kInterruptLimit, access);
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsDebugBreak() {
|
||||
ExecutionAccess access;
|
||||
return thread_local_.interrupt_flags_ & DEBUGBREAK;
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::DebugBreak() {
|
||||
ExecutionAccess access;
|
||||
thread_local_.interrupt_flags_ |= DEBUGBREAK;
|
||||
set_limits(kInterruptLimit, access);
|
||||
}
|
||||
|
||||
|
||||
void StackGuard::Continue(InterruptFlag after_what) {
|
||||
ExecutionAccess access;
|
||||
thread_local_.interrupt_flags_ &= ~static_cast<int>(after_what);
|
||||
if (thread_local_.interrupt_flags_ == 0) {
|
||||
reset_limits(access);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int StackGuard::ArchiveSpacePerThread() {
|
||||
return sizeof(ThreadLocal);
|
||||
}
|
||||
|
||||
|
||||
char* StackGuard::ArchiveStackGuard(char* to) {
|
||||
ExecutionAccess access;
|
||||
memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
|
||||
ThreadLocal blank;
|
||||
thread_local_ = blank;
|
||||
return to + sizeof(ThreadLocal);
|
||||
}
|
||||
|
||||
|
||||
char* StackGuard::RestoreStackGuard(char* from) {
|
||||
ExecutionAccess access;
|
||||
memcpy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
|
||||
return from + sizeof(ThreadLocal);
|
||||
}
|
||||
|
||||
|
||||
// --- C a l l s t o n a t i v e s ---
|
||||
|
||||
#define RETURN_NATIVE_CALL(name, argc, argv, has_pending_exception) \
|
||||
do { \
|
||||
Object** args[argc] = argv; \
|
||||
ASSERT(has_pending_exception != NULL); \
|
||||
return Call(Top::name##_fun(), Top::builtins(), argc, args, \
|
||||
has_pending_exception); \
|
||||
} while (false)
|
||||
|
||||
|
||||
Handle<Object> Execution::ToBoolean(Handle<Object> obj) {
|
||||
// See the similar code in runtime.js:ToBoolean.
|
||||
if (obj->IsBoolean()) return obj;
|
||||
bool result = true;
|
||||
if (obj->IsString()) {
|
||||
result = Handle<String>::cast(obj)->length() != 0;
|
||||
} else if (obj->IsNull() || obj->IsUndefined()) {
|
||||
result = false;
|
||||
} else if (obj->IsNumber()) {
|
||||
double value = obj->Number();
|
||||
result = !((value == 0) || isnan(value));
|
||||
}
|
||||
return Handle<Object>(Heap::ToBoolean(result));
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToNumber(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_number, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToString(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_string, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToDetailString(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_detail_string, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToObject(Handle<Object> obj, bool* exc) {
|
||||
if (obj->IsJSObject()) return obj;
|
||||
RETURN_NATIVE_CALL(to_object, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToInteger(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_integer, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToUint32(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_uint32, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::ToInt32(Handle<Object> obj, bool* exc) {
|
||||
RETURN_NATIVE_CALL(to_int32, 1, { obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Execution::NewDate(double time, bool* exc) {
|
||||
Handle<Object> time_obj = Factory::NewNumber(time);
|
||||
RETURN_NATIVE_CALL(create_date, 1, { time_obj.location() }, exc);
|
||||
}
|
||||
|
||||
|
||||
#undef RETURN_NATIVE_CALL
|
||||
|
||||
|
||||
Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) {
|
||||
int int_index = static_cast<int>(index);
|
||||
if (int_index < 0 || int_index >= string->length()) {
|
||||
return Factory::undefined_value();
|
||||
}
|
||||
|
||||
Handle<Object> char_at =
|
||||
GetProperty(Top::builtins(), Factory::char_at_symbol());
|
||||
if (!char_at->IsJSFunction()) {
|
||||
return Factory::undefined_value();
|
||||
}
|
||||
|
||||
bool caught_exception;
|
||||
Handle<Object> index_object = Factory::NewNumberFromInt(int_index);
|
||||
Object** index_arg[] = { index_object.location() };
|
||||
Handle<Object> result = TryCall(Handle<JSFunction>::cast(char_at),
|
||||
string,
|
||||
ARRAY_SIZE(index_arg),
|
||||
index_arg,
|
||||
&caught_exception);
|
||||
if (caught_exception) {
|
||||
return Factory::undefined_value();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Execution::InstantiateFunction(
|
||||
Handle<FunctionTemplateInfo> data, bool* exc) {
|
||||
// Fast case: see if the function has already been instantiated
|
||||
int serial_number = Smi::cast(data->serial_number())->value();
|
||||
Object* elm =
|
||||
Top::global_context()->function_cache()->GetElement(serial_number);
|
||||
if (!elm->IsUndefined()) return Handle<JSFunction>(JSFunction::cast(elm));
|
||||
// The function has not yet been instantiated in this context; do it.
|
||||
Object** args[1] = { Handle<Object>::cast(data).location() };
|
||||
Handle<Object> result =
|
||||
Call(Top::instantiate_fun(), Top::builtins(), 1, args, exc);
|
||||
if (*exc) return Handle<JSFunction>::null();
|
||||
return Handle<JSFunction>::cast(result);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Execution::InstantiateObject(Handle<ObjectTemplateInfo> data,
|
||||
bool* exc) {
|
||||
if (data->property_list()->IsUndefined() &&
|
||||
!data->constructor()->IsUndefined()) {
|
||||
Object* result;
|
||||
{
|
||||
HandleScope scope;
|
||||
Handle<FunctionTemplateInfo> cons_template =
|
||||
Handle<FunctionTemplateInfo>(
|
||||
FunctionTemplateInfo::cast(data->constructor()));
|
||||
Handle<JSFunction> cons = InstantiateFunction(cons_template, exc);
|
||||
if (*exc) return Handle<JSObject>::null();
|
||||
Handle<Object> value = New(cons, 0, NULL, exc);
|
||||
if (*exc) return Handle<JSObject>::null();
|
||||
result = *value;
|
||||
}
|
||||
ASSERT(!*exc);
|
||||
return Handle<JSObject>(JSObject::cast(result));
|
||||
} else {
|
||||
Object** args[1] = { Handle<Object>::cast(data).location() };
|
||||
Handle<Object> result =
|
||||
Call(Top::instantiate_fun(), Top::builtins(), 1, args, exc);
|
||||
if (*exc) return Handle<JSObject>::null();
|
||||
return Handle<JSObject>::cast(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Execution::ConfigureInstance(Handle<Object> instance,
|
||||
Handle<Object> instance_template,
|
||||
bool* exc) {
|
||||
Object** args[2] = { instance.location(), instance_template.location() };
|
||||
Execution::Call(Top::configure_instance_fun(), Top::builtins(), 2, args, exc);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
|
||||
Handle<JSFunction> fun,
|
||||
Handle<Object> pos,
|
||||
Handle<Object> is_global) {
|
||||
const int argc = 4;
|
||||
Object** args[argc] = { recv.location(),
|
||||
Handle<Object>::cast(fun).location(),
|
||||
pos.location(),
|
||||
is_global.location() };
|
||||
bool caught_exception = false;
|
||||
Handle<Object> result = TryCall(Top::get_stack_trace_line_fun(),
|
||||
Top::builtins(), argc, args,
|
||||
&caught_exception);
|
||||
if (caught_exception || !result->IsString()) return Factory::empty_symbol();
|
||||
return Handle<String>::cast(result);
|
||||
}
|
||||
|
||||
|
||||
// --- G C E x t e n s i o n ---
|
||||
|
||||
const char* GCExtension::kSource = "native function gc();";
|
||||
|
||||
|
||||
v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction(
|
||||
v8::Handle<v8::String> str) {
|
||||
return v8::FunctionTemplate::New(GCExtension::GC);
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) {
|
||||
// All allocation spaces other than NEW_SPACE have the same effect.
|
||||
Heap::CollectGarbage(0, OLD_DATA_SPACE);
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
|
||||
static GCExtension kGCExtension;
|
||||
v8::DeclareExtension kGCExtensionDeclaration(&kGCExtension);
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,262 +0,0 @@
|
||||
// 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_EXECUTION_H_
|
||||
#define V8_EXECUTION_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Flag used to set the interrupt causes.
|
||||
enum InterruptFlag {
|
||||
INTERRUPT = 1 << 0,
|
||||
DEBUGBREAK = 1 << 1,
|
||||
PREEMPT = 1 << 2
|
||||
};
|
||||
|
||||
class Execution : public AllStatic {
|
||||
public:
|
||||
// Call a function, the caller supplies a receiver and an array
|
||||
// of arguments. Arguments are Object* type. After function returns,
|
||||
// pointers in 'args' might be invalid.
|
||||
//
|
||||
// *pending_exception tells whether the invoke resulted in
|
||||
// a pending exception.
|
||||
//
|
||||
static Handle<Object> Call(Handle<JSFunction> func,
|
||||
Handle<Object> receiver,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* pending_exception);
|
||||
|
||||
// Construct object from function, the caller supplies an array of
|
||||
// arguments. Arguments are Object* type. After function returns,
|
||||
// pointers in 'args' might be invalid.
|
||||
//
|
||||
// *pending_exception tells whether the invoke resulted in
|
||||
// a pending exception.
|
||||
//
|
||||
static Handle<Object> New(Handle<JSFunction> func,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* pending_exception);
|
||||
|
||||
// Call a function, just like Call(), but make sure to silently catch
|
||||
// any thrown exceptions. The return value is either the result of
|
||||
// calling the function (if caught exception is false) or the exception
|
||||
// that occurred (if caught exception is true).
|
||||
static Handle<Object> TryCall(Handle<JSFunction> func,
|
||||
Handle<Object> receiver,
|
||||
int argc,
|
||||
Object*** args,
|
||||
bool* caught_exception);
|
||||
|
||||
// ECMA-262 9.2
|
||||
static Handle<Object> ToBoolean(Handle<Object> obj);
|
||||
|
||||
// ECMA-262 9.3
|
||||
static Handle<Object> ToNumber(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.4
|
||||
static Handle<Object> ToInteger(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.5
|
||||
static Handle<Object> ToInt32(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.6
|
||||
static Handle<Object> ToUint32(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.8
|
||||
static Handle<Object> ToString(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.8
|
||||
static Handle<Object> ToDetailString(Handle<Object> obj, bool* exc);
|
||||
|
||||
// ECMA-262 9.9
|
||||
static Handle<Object> ToObject(Handle<Object> obj, bool* exc);
|
||||
|
||||
// Create a new date object from 'time'.
|
||||
static Handle<Object> NewDate(double time, bool* exc);
|
||||
|
||||
// Used to implement [] notation on strings (calls JS code)
|
||||
static Handle<Object> CharAt(Handle<String> str, uint32_t index);
|
||||
|
||||
static Handle<Object> GetFunctionFor();
|
||||
static Handle<JSFunction> InstantiateFunction(
|
||||
Handle<FunctionTemplateInfo> data, bool* exc);
|
||||
static Handle<JSObject> InstantiateObject(Handle<ObjectTemplateInfo> data,
|
||||
bool* exc);
|
||||
static void ConfigureInstance(Handle<Object> instance,
|
||||
Handle<Object> data,
|
||||
bool* exc);
|
||||
static Handle<String> GetStackTraceLine(Handle<Object> recv,
|
||||
Handle<JSFunction> fun,
|
||||
Handle<Object> pos,
|
||||
Handle<Object> is_global);
|
||||
|
||||
// Get a function delegate (or undefined) for the given non-function
|
||||
// object. Used for support calling objects as functions.
|
||||
static Handle<Object> GetFunctionDelegate(Handle<Object> object);
|
||||
};
|
||||
|
||||
|
||||
class ExecutionAccess;
|
||||
|
||||
|
||||
// Stack guards are used to limit the number of nested invocations of
|
||||
// JavaScript and the stack size used in each invocation.
|
||||
class StackGuard BASE_EMBEDDED {
|
||||
public:
|
||||
StackGuard();
|
||||
|
||||
~StackGuard();
|
||||
|
||||
static void SetStackLimit(uintptr_t limit);
|
||||
|
||||
static Address address_of_jslimit() {
|
||||
return reinterpret_cast<Address>(&thread_local_.jslimit_);
|
||||
}
|
||||
|
||||
// Threading support.
|
||||
static char* ArchiveStackGuard(char* to);
|
||||
static char* RestoreStackGuard(char* from);
|
||||
static int ArchiveSpacePerThread();
|
||||
|
||||
static bool IsStackOverflow();
|
||||
static bool IsPreempted();
|
||||
static void Preempt();
|
||||
static bool IsInterrupted();
|
||||
static void Interrupt();
|
||||
static bool IsDebugBreak();
|
||||
static void DebugBreak();
|
||||
static void Continue(InterruptFlag after_what);
|
||||
|
||||
private:
|
||||
// You should hold the ExecutionAccess lock when calling this method.
|
||||
static bool IsSet(const ExecutionAccess& lock);
|
||||
|
||||
// This provides an asynchronous read of the stack limit for the current
|
||||
// thread. There are no locks protecting this, but it is assumed that you
|
||||
// have the global V8 lock if you are using multiple V8 threads.
|
||||
static uintptr_t climit() {
|
||||
return thread_local_.climit_;
|
||||
}
|
||||
|
||||
// You should hold the ExecutionAccess lock when calling this method.
|
||||
static void set_limits(uintptr_t value, const ExecutionAccess& lock) {
|
||||
thread_local_.jslimit_ = value;
|
||||
thread_local_.climit_ = value;
|
||||
}
|
||||
|
||||
// Reset limits to initial values. For example after handling interrupt.
|
||||
// You should hold the ExecutionAccess lock when calling this method.
|
||||
static void reset_limits(const ExecutionAccess& lock) {
|
||||
if (thread_local_.nesting_ == 0) {
|
||||
// No limits have been set yet.
|
||||
set_limits(kIllegalLimit, lock);
|
||||
} else {
|
||||
thread_local_.jslimit_ = thread_local_.initial_jslimit_;
|
||||
thread_local_.climit_ = thread_local_.initial_climit_;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable or disable interrupts.
|
||||
static void EnableInterrupts();
|
||||
static void DisableInterrupts();
|
||||
|
||||
static const uintptr_t kLimitSize = 512 * KB;
|
||||
static const uintptr_t kInterruptLimit = 0xfffffffe;
|
||||
static const uintptr_t kIllegalLimit = 0xffffffff;
|
||||
|
||||
class ThreadLocal {
|
||||
public:
|
||||
ThreadLocal()
|
||||
: initial_jslimit_(kIllegalLimit),
|
||||
jslimit_(kIllegalLimit),
|
||||
initial_climit_(kIllegalLimit),
|
||||
climit_(kIllegalLimit),
|
||||
nesting_(0),
|
||||
postpone_interrupts_nesting_(0),
|
||||
interrupt_flags_(0) {}
|
||||
uintptr_t initial_jslimit_;
|
||||
uintptr_t jslimit_;
|
||||
uintptr_t initial_climit_;
|
||||
uintptr_t climit_;
|
||||
int nesting_;
|
||||
int postpone_interrupts_nesting_;
|
||||
int interrupt_flags_;
|
||||
};
|
||||
|
||||
static ThreadLocal thread_local_;
|
||||
|
||||
friend class StackLimitCheck;
|
||||
friend class PostponeInterruptsScope;
|
||||
};
|
||||
|
||||
|
||||
// Support for checking for stack-overflows in C++ code.
|
||||
class StackLimitCheck BASE_EMBEDDED {
|
||||
public:
|
||||
bool HasOverflowed() const {
|
||||
return reinterpret_cast<uintptr_t>(this) < StackGuard::climit();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Support for temporarily postponing interrupts. When the outermost
|
||||
// postpone scope is left the interrupts will be re-enabled and any
|
||||
// interrupts that occurred while in the scope will be taken into
|
||||
// account.
|
||||
class PostponeInterruptsScope BASE_EMBEDDED {
|
||||
public:
|
||||
PostponeInterruptsScope() {
|
||||
StackGuard::thread_local_.postpone_interrupts_nesting_++;
|
||||
StackGuard::DisableInterrupts();
|
||||
}
|
||||
|
||||
~PostponeInterruptsScope() {
|
||||
if (--StackGuard::thread_local_.postpone_interrupts_nesting_ == 0) {
|
||||
StackGuard::EnableInterrupts();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GCExtension : public v8::Extension {
|
||||
public:
|
||||
GCExtension() : v8::Extension("v8/gc", kSource) {}
|
||||
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
|
||||
v8::Handle<v8::String> name);
|
||||
static v8::Handle<v8::Value> GC(const v8::Arguments& args);
|
||||
private:
|
||||
static const char* kSource;
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_EXECUTION_H_
|
@ -1,841 +0,0 @@
|
||||
// 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 "debug.h"
|
||||
#include "execution.h"
|
||||
#include "factory.h"
|
||||
#include "macro-assembler.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
Handle<FixedArray> Factory::NewFixedArray(int size, PretenureFlag pretenure) {
|
||||
ASSERT(0 <= size);
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFixedArray(size, pretenure), FixedArray);
|
||||
}
|
||||
|
||||
|
||||
Handle<DescriptorArray> Factory::NewDescriptorArray(int number_of_descriptors) {
|
||||
ASSERT(0 <= number_of_descriptors);
|
||||
CALL_HEAP_FUNCTION(DescriptorArray::Allocate(number_of_descriptors),
|
||||
DescriptorArray);
|
||||
}
|
||||
|
||||
|
||||
// Symbols are created in the old generation (data space).
|
||||
Handle<String> Factory::LookupSymbol(Vector<const char> string) {
|
||||
CALL_HEAP_FUNCTION(Heap::LookupSymbol(string), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewStringFromAscii(Vector<const char> string,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateStringFromAscii(string, pretenure), String);
|
||||
}
|
||||
|
||||
Handle<String> Factory::NewStringFromUtf8(Vector<const char> string,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateStringFromUtf8(string, pretenure), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewStringFromTwoByte(Vector<const uc16> string) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateStringFromTwoByte(string), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewRawTwoByteString(int length,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(length, pretenure), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewConsString(Handle<String> first,
|
||||
Handle<String> second) {
|
||||
if (first->length() == 0) return second;
|
||||
if (second->length() == 0) return first;
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first, *second), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewStringSlice(Handle<String> str, int begin, int end) {
|
||||
CALL_HEAP_FUNCTION(str->Slice(begin, end), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewExternalStringFromAscii(
|
||||
ExternalAsciiString::Resource* resource) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateExternalStringFromAscii(resource), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Factory::NewExternalStringFromTwoByte(
|
||||
ExternalTwoByteString::Resource* resource) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateExternalStringFromTwoByte(resource), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<Context> Factory::NewGlobalContext() {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateGlobalContext(), Context);
|
||||
}
|
||||
|
||||
|
||||
Handle<Context> Factory::NewFunctionContext(int length,
|
||||
Handle<JSFunction> closure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFunctionContext(length, *closure), Context);
|
||||
}
|
||||
|
||||
|
||||
Handle<Context> Factory::NewWithContext(Handle<Context> previous,
|
||||
Handle<JSObject> extension) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateWithContext(*previous, *extension), Context);
|
||||
}
|
||||
|
||||
|
||||
Handle<Struct> Factory::NewStruct(InstanceType type) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateStruct(type), Struct);
|
||||
}
|
||||
|
||||
|
||||
Handle<AccessorInfo> Factory::NewAccessorInfo() {
|
||||
Handle<AccessorInfo> info =
|
||||
Handle<AccessorInfo>::cast(NewStruct(ACCESSOR_INFO_TYPE));
|
||||
info->set_flag(0); // Must clear the flag, it was initialized as undefined.
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
Handle<Script> Factory::NewScript(Handle<String> source) {
|
||||
Handle<Script> script = Handle<Script>::cast(NewStruct(SCRIPT_TYPE));
|
||||
script->set_source(*source);
|
||||
script->set_name(Heap::undefined_value());
|
||||
script->set_line_offset(Smi::FromInt(0));
|
||||
script->set_column_offset(Smi::FromInt(0));
|
||||
script->set_wrapper(*Factory::NewProxy(0, TENURED));
|
||||
script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL));
|
||||
return script;
|
||||
}
|
||||
|
||||
|
||||
Handle<Proxy> Factory::NewProxy(Address addr, PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateProxy(addr, pretenure), Proxy);
|
||||
}
|
||||
|
||||
|
||||
Handle<Proxy> Factory::NewProxy(const AccessorDescriptor* desc) {
|
||||
return NewProxy((Address) desc, TENURED);
|
||||
}
|
||||
|
||||
|
||||
Handle<ByteArray> Factory::NewByteArray(int length) {
|
||||
ASSERT(0 <= length);
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateByteArray(length), ByteArray);
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Factory::NewMap(InstanceType type, int instance_size) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateMap(type, instance_size), Map);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewFunctionPrototype(Handle<JSFunction> function) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFunctionPrototype(*function), JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Factory::CopyMap(Handle<Map> src) {
|
||||
CALL_HEAP_FUNCTION(src->Copy(), Map);
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Factory::CopyMapDropTransitions(Handle<Map> src) {
|
||||
CALL_HEAP_FUNCTION(src->CopyDropTransitions(), Map);
|
||||
}
|
||||
|
||||
|
||||
Handle<FixedArray> Factory::CopyFixedArray(Handle<FixedArray> array) {
|
||||
CALL_HEAP_FUNCTION(array->Copy(), FixedArray);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::BaseNewFunctionFromBoilerplate(
|
||||
Handle<JSFunction> boilerplate,
|
||||
Handle<Map> function_map) {
|
||||
ASSERT(boilerplate->IsBoilerplate());
|
||||
ASSERT(!boilerplate->has_initial_map());
|
||||
ASSERT(!boilerplate->has_prototype());
|
||||
ASSERT(boilerplate->properties() == Heap::empty_fixed_array());
|
||||
ASSERT(boilerplate->elements() == Heap::empty_fixed_array());
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFunction(*function_map,
|
||||
boilerplate->shared(),
|
||||
Heap::the_hole_value()),
|
||||
JSFunction);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunctionFromBoilerplate(
|
||||
Handle<JSFunction> boilerplate,
|
||||
Handle<Context> context) {
|
||||
Handle<JSFunction> result =
|
||||
BaseNewFunctionFromBoilerplate(boilerplate, Top::function_map());
|
||||
result->set_context(*context);
|
||||
int number_of_literals = boilerplate->NumberOfLiterals();
|
||||
Handle<FixedArray> literals =
|
||||
Factory::NewFixedArray(number_of_literals, TENURED);
|
||||
if (number_of_literals > 0) {
|
||||
// Store the object, regexp and array functions in the literals
|
||||
// array prefix. These functions will be used when creating
|
||||
// object, regexp and array literals in this function.
|
||||
literals->set(JSFunction::kLiteralGlobalContextIndex,
|
||||
context->global_context());
|
||||
}
|
||||
result->set_literals(*literals);
|
||||
ASSERT(!result->IsBoilerplate());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewNumber(double value,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::NumberFromDouble(value, pretenure), Object);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewNumberFromInt(int value) {
|
||||
CALL_HEAP_FUNCTION(Heap::NumberFromInt32(value), Object);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewNeanderObject() {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(Heap::neander_map()),
|
||||
JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewTypeError(const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
return NewError("MakeTypeError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewTypeError(Handle<String> message) {
|
||||
return NewError("$TypeError", message);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewRangeError(const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
return NewError("MakeRangeError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewRangeError(Handle<String> message) {
|
||||
return NewError("$RangeError", message);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewSyntaxError(const char* type, Handle<JSArray> args) {
|
||||
return NewError("MakeSyntaxError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewSyntaxError(Handle<String> message) {
|
||||
return NewError("$SyntaxError", message);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewReferenceError(const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
return NewError("MakeReferenceError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewReferenceError(Handle<String> message) {
|
||||
return NewError("$ReferenceError", message);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewError(const char* maker, const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
HandleScope scope;
|
||||
Handle<FixedArray> array = Factory::NewFixedArray(args.length());
|
||||
for (int i = 0; i < args.length(); i++) {
|
||||
array->set(i, *args[i]);
|
||||
}
|
||||
Handle<JSArray> object = Factory::NewJSArrayWithElements(array);
|
||||
Handle<Object> result = NewError(maker, type, object);
|
||||
return result.EscapeFrom(&scope);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewEvalError(const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
return NewError("MakeEvalError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewError(const char* type,
|
||||
Vector< Handle<Object> > args) {
|
||||
return NewError("MakeError", type, args);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewError(const char* maker,
|
||||
const char* type,
|
||||
Handle<JSArray> args) {
|
||||
Handle<String> make_str = Factory::LookupAsciiSymbol(maker);
|
||||
Handle<JSFunction> fun =
|
||||
Handle<JSFunction>(
|
||||
JSFunction::cast(
|
||||
Top::builtins()->GetProperty(*make_str)));
|
||||
Handle<Object> type_obj = Factory::LookupAsciiSymbol(type);
|
||||
Object** argv[2] = { type_obj.location(),
|
||||
Handle<Object>::cast(args).location() };
|
||||
|
||||
// Invoke the JavaScript factory method. If an exception is thrown while
|
||||
// running the factory method, use the exception as the result.
|
||||
bool caught_exception;
|
||||
Handle<Object> result = Execution::TryCall(fun,
|
||||
Top::builtins(),
|
||||
2,
|
||||
argv,
|
||||
&caught_exception);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewError(Handle<String> message) {
|
||||
return NewError("$Error", message);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewError(const char* constructor,
|
||||
Handle<String> message) {
|
||||
Handle<String> constr = Factory::LookupAsciiSymbol(constructor);
|
||||
Handle<JSFunction> fun =
|
||||
Handle<JSFunction>(
|
||||
JSFunction::cast(
|
||||
Top::builtins()->GetProperty(*constr)));
|
||||
Object** argv[1] = { Handle<Object>::cast(message).location() };
|
||||
|
||||
// Invoke the JavaScript factory method. If an exception is thrown while
|
||||
// running the factory method, use the exception as the result.
|
||||
bool caught_exception;
|
||||
Handle<Object> result = Execution::TryCall(fun,
|
||||
Top::builtins(),
|
||||
1,
|
||||
argv,
|
||||
&caught_exception);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunction(Handle<String> name,
|
||||
InstanceType type,
|
||||
int instance_size,
|
||||
Handle<Code> code,
|
||||
bool force_initial_map) {
|
||||
// Allocate the function
|
||||
Handle<JSFunction> function = NewFunction(name, the_hole_value());
|
||||
function->set_code(*code);
|
||||
|
||||
if (force_initial_map ||
|
||||
type != JS_OBJECT_TYPE ||
|
||||
instance_size != JSObject::kHeaderSize) {
|
||||
Handle<Map> initial_map = NewMap(type, instance_size);
|
||||
Handle<JSObject> prototype = NewFunctionPrototype(function);
|
||||
initial_map->set_prototype(*prototype);
|
||||
function->set_initial_map(*initial_map);
|
||||
initial_map->set_constructor(*function);
|
||||
} else {
|
||||
ASSERT(!function->has_initial_map());
|
||||
ASSERT(!function->has_prototype());
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunctionBoilerplate(Handle<String> name,
|
||||
int number_of_literals,
|
||||
bool contains_array_literal,
|
||||
Handle<Code> code) {
|
||||
Handle<JSFunction> function = NewFunctionBoilerplate(name);
|
||||
function->set_code(*code);
|
||||
int literals_array_size = number_of_literals;
|
||||
// If the function contains object, regexp or array literals,
|
||||
// allocate extra space for a literals array prefix containing the
|
||||
// object, regexp and array constructor functions.
|
||||
if (number_of_literals > 0 || contains_array_literal) {
|
||||
literals_array_size += JSFunction::kLiteralsPrefixSize;
|
||||
}
|
||||
Handle<FixedArray> literals =
|
||||
Factory::NewFixedArray(literals_array_size, TENURED);
|
||||
function->set_literals(*literals);
|
||||
ASSERT(!function->has_initial_map());
|
||||
ASSERT(!function->has_prototype());
|
||||
return function;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunctionBoilerplate(Handle<String> name) {
|
||||
Handle<SharedFunctionInfo> shared = NewSharedFunctionInfo(name);
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFunction(Heap::boilerplate_function_map(),
|
||||
*shared,
|
||||
Heap::the_hole_value()),
|
||||
JSFunction);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name,
|
||||
InstanceType type,
|
||||
int instance_size,
|
||||
Handle<JSObject> prototype,
|
||||
Handle<Code> code,
|
||||
bool force_initial_map) {
|
||||
// Allocate the function
|
||||
Handle<JSFunction> function = NewFunction(name, prototype);
|
||||
|
||||
function->set_code(*code);
|
||||
|
||||
if (force_initial_map ||
|
||||
type != JS_OBJECT_TYPE ||
|
||||
instance_size != JSObject::kHeaderSize) {
|
||||
Handle<Map> initial_map = NewMap(type, instance_size);
|
||||
function->set_initial_map(*initial_map);
|
||||
initial_map->set_constructor(*function);
|
||||
}
|
||||
|
||||
// Set function.prototype and give the prototype a constructor
|
||||
// property that refers to the function.
|
||||
SetPrototypeProperty(function, prototype);
|
||||
SetProperty(prototype, Factory::constructor_symbol(), function, DONT_ENUM);
|
||||
return function;
|
||||
}
|
||||
|
||||
Handle<Code> Factory::NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
|
||||
Code::Flags flags) {
|
||||
CALL_HEAP_FUNCTION(Heap::CreateCode(desc, sinfo, flags), Code);
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> Factory::CopyCode(Handle<Code> code) {
|
||||
CALL_HEAP_FUNCTION(Heap::CopyCode(*code), Code);
|
||||
}
|
||||
|
||||
|
||||
#define CALL_GC(RETRY) \
|
||||
do { \
|
||||
if (!Heap::CollectGarbage(Failure::cast(RETRY)->requested(), \
|
||||
Failure::cast(RETRY)->allocation_space())) { \
|
||||
/* TODO(1181417): Fix this. */ \
|
||||
V8::FatalProcessOutOfMemory("Factory CALL_GC"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
|
||||
// Allocate the new array. We cannot use the CALL_HEAP_FUNCTION macro here,
|
||||
// because the stack-allocated CallbacksDescriptor instance is not GC safe.
|
||||
Handle<DescriptorArray> Factory::CopyAppendProxyDescriptor(
|
||||
Handle<DescriptorArray> array,
|
||||
Handle<String> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
GC_GREEDY_CHECK();
|
||||
CallbacksDescriptor desc(*key, *value, attributes);
|
||||
Object* obj = array->CopyInsert(&desc, REMOVE_TRANSITIONS);
|
||||
if (obj->IsFailure()) {
|
||||
if (obj->IsRetryAfterGC()) {
|
||||
CALL_GC(obj);
|
||||
CallbacksDescriptor desc(*key, *value, attributes);
|
||||
obj = array->CopyInsert(&desc, REMOVE_TRANSITIONS);
|
||||
}
|
||||
if (obj->IsFailure()) {
|
||||
// TODO(1181417): Fix this.
|
||||
V8::FatalProcessOutOfMemory("CopyAppendProxyDescriptor");
|
||||
}
|
||||
}
|
||||
return Handle<DescriptorArray>(DescriptorArray::cast(obj));
|
||||
}
|
||||
|
||||
#undef CALL_GC
|
||||
|
||||
|
||||
Handle<String> Factory::SymbolFromString(Handle<String> value) {
|
||||
CALL_HEAP_FUNCTION(Heap::LookupSymbol(*value), String);
|
||||
}
|
||||
|
||||
|
||||
Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
Handle<DescriptorArray> array,
|
||||
Handle<Object> descriptors) {
|
||||
v8::NeanderArray callbacks(descriptors);
|
||||
int nof_callbacks = callbacks.length();
|
||||
Handle<DescriptorArray> result =
|
||||
NewDescriptorArray(array->number_of_descriptors() + nof_callbacks);
|
||||
|
||||
// Number of descriptors added to the result so far.
|
||||
int descriptor_count = 0;
|
||||
|
||||
// Copy the descriptors from the array.
|
||||
DescriptorWriter w(*result);
|
||||
for (DescriptorReader r(*array); !r.eos(); r.advance()) {
|
||||
w.WriteFrom(&r);
|
||||
descriptor_count++;
|
||||
}
|
||||
|
||||
// Number of duplicates detected.
|
||||
int duplicates = 0;
|
||||
|
||||
// Fill in new callback descriptors. Process the callbacks from
|
||||
// back to front so that the last callback with a given name takes
|
||||
// precedence over previously added callbacks with that name.
|
||||
for (int i = nof_callbacks - 1; i >= 0; i--) {
|
||||
Handle<AccessorInfo> entry =
|
||||
Handle<AccessorInfo>(AccessorInfo::cast(callbacks.get(i)));
|
||||
// Ensure the key is a symbol before writing into the instance descriptor.
|
||||
Handle<String> key =
|
||||
SymbolFromString(Handle<String>(String::cast(entry->name())));
|
||||
// Check if a descriptor with this name already exists before writing.
|
||||
if (result->LinearSearch(*key, descriptor_count) ==
|
||||
DescriptorArray::kNotFound) {
|
||||
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
|
||||
w.Write(&desc);
|
||||
descriptor_count++;
|
||||
} else {
|
||||
duplicates++;
|
||||
}
|
||||
}
|
||||
|
||||
// If duplicates were detected, allocate a result of the right size
|
||||
// and transfer the elements.
|
||||
if (duplicates > 0) {
|
||||
Handle<DescriptorArray> new_result =
|
||||
NewDescriptorArray(result->number_of_descriptors() - duplicates);
|
||||
DescriptorWriter w(*new_result);
|
||||
DescriptorReader r(*result);
|
||||
while (!w.eos()) {
|
||||
w.WriteFrom(&r);
|
||||
r.advance();
|
||||
}
|
||||
result = new_result;
|
||||
}
|
||||
|
||||
// Sort the result before returning.
|
||||
result->Sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewJSObject(Handle<JSFunction> constructor,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateJSObject(*constructor, pretenure), JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, NOT_TENURED),
|
||||
JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewObjectLiteral(int expected_number_of_properties) {
|
||||
Handle<Map> map = Handle<Map>(Top::object_function()->initial_map());
|
||||
map = Factory::CopyMap(map);
|
||||
map->set_instance_descriptors(Heap::empty_descriptor_array());
|
||||
map->set_unused_property_fields(expected_number_of_properties);
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, TENURED),
|
||||
JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSArray> Factory::NewArrayLiteral(int length) {
|
||||
return NewJSArrayWithElements(NewFixedArray(length), TENURED);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSArray> Factory::NewJSArray(int length,
|
||||
PretenureFlag pretenure) {
|
||||
Handle<JSObject> obj = NewJSObject(Top::array_function(), pretenure);
|
||||
CALL_HEAP_FUNCTION(Handle<JSArray>::cast(obj)->Initialize(length), JSArray);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements,
|
||||
PretenureFlag pretenure) {
|
||||
Handle<JSArray> result =
|
||||
Handle<JSArray>::cast(NewJSObject(Top::array_function(), pretenure));
|
||||
result->SetContent(*elements);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(Handle<String> name) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateSharedFunctionInfo(*name),
|
||||
SharedFunctionInfo);
|
||||
}
|
||||
|
||||
|
||||
Handle<Dictionary> Factory::DictionaryAtNumberPut(Handle<Dictionary> dictionary,
|
||||
uint32_t key,
|
||||
Handle<Object> value) {
|
||||
CALL_HEAP_FUNCTION(dictionary->AtNumberPut(key, *value), Dictionary);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunctionHelper(Handle<String> name,
|
||||
Handle<Object> prototype) {
|
||||
Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name);
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateFunction(*Top::function_map(),
|
||||
*function_share,
|
||||
*prototype),
|
||||
JSFunction);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::NewFunction(Handle<String> name,
|
||||
Handle<Object> prototype) {
|
||||
Handle<JSFunction> fun = NewFunctionHelper(name, prototype);
|
||||
fun->set_context(Top::context()->global_context());
|
||||
return fun;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::ToObject(Handle<Object> object,
|
||||
Handle<Context> global_context) {
|
||||
CALL_HEAP_FUNCTION(object->ToObject(*global_context), Object);
|
||||
}
|
||||
|
||||
|
||||
Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
|
||||
// Get the original code of the function.
|
||||
Handle<Code> code(shared->code());
|
||||
|
||||
// Create a copy of the code before allocating the debug info object to avoid
|
||||
// allocation while setting up the debug info object.
|
||||
Handle<Code> original_code(*Factory::CopyCode(code));
|
||||
|
||||
// Allocate initial fixed array for active break points before allocating the
|
||||
// debug info object to avoid allocation while setting up the debug info
|
||||
// object.
|
||||
Handle<FixedArray> break_points(
|
||||
Factory::NewFixedArray(Debug::kEstimatedNofBreakPointsInFunction));
|
||||
|
||||
// Create and set up the debug info object. Debug info contains function, a
|
||||
// copy of the original code, the executing code and initial fixed array for
|
||||
// active break points.
|
||||
Handle<DebugInfo> debug_info =
|
||||
Handle<DebugInfo>::cast(Factory::NewStruct(DEBUG_INFO_TYPE));
|
||||
debug_info->set_shared(*shared);
|
||||
debug_info->set_original_code(*original_code);
|
||||
debug_info->set_code(*code);
|
||||
debug_info->set_break_points(*break_points);
|
||||
|
||||
// Link debug info to function.
|
||||
shared->set_debug_info(*debug_info);
|
||||
|
||||
return debug_info;
|
||||
}
|
||||
|
||||
|
||||
Handle<JSObject> Factory::NewArgumentsObject(Handle<Object> callee,
|
||||
int length) {
|
||||
CALL_HEAP_FUNCTION(Heap::AllocateArgumentsObject(*callee, length), JSObject);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Factory::CreateApiFunction(
|
||||
Handle<FunctionTemplateInfo> obj, ApiInstanceType instance_type) {
|
||||
Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::HandleApiCall));
|
||||
|
||||
int internal_field_count = 0;
|
||||
if (!obj->instance_template()->IsUndefined()) {
|
||||
Handle<ObjectTemplateInfo> instance_template =
|
||||
Handle<ObjectTemplateInfo>(
|
||||
ObjectTemplateInfo::cast(obj->instance_template()));
|
||||
internal_field_count =
|
||||
Smi::cast(instance_template->internal_field_count())->value();
|
||||
}
|
||||
|
||||
int instance_size = kPointerSize * internal_field_count;
|
||||
InstanceType type = INVALID_TYPE;
|
||||
switch (instance_type) {
|
||||
case JavaScriptObject:
|
||||
type = JS_OBJECT_TYPE;
|
||||
instance_size += JSObject::kHeaderSize;
|
||||
break;
|
||||
case InnerGlobalObject:
|
||||
type = JS_GLOBAL_OBJECT_TYPE;
|
||||
instance_size += JSGlobalObject::kSize;
|
||||
break;
|
||||
case OuterGlobalObject:
|
||||
type = JS_GLOBAL_PROXY_TYPE;
|
||||
instance_size += JSGlobalProxy::kSize;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ASSERT(type != INVALID_TYPE);
|
||||
|
||||
Handle<JSFunction> result =
|
||||
Factory::NewFunction(Factory::empty_symbol(), type, instance_size,
|
||||
code, true);
|
||||
// Set class name.
|
||||
Handle<Object> class_name = Handle<Object>(obj->class_name());
|
||||
if (class_name->IsString()) {
|
||||
result->shared()->set_instance_class_name(*class_name);
|
||||
result->shared()->set_name(*class_name);
|
||||
}
|
||||
|
||||
Handle<Map> map = Handle<Map>(result->initial_map());
|
||||
|
||||
// Mark as undetectable if needed.
|
||||
if (obj->undetectable()) {
|
||||
map->set_is_undetectable();
|
||||
}
|
||||
|
||||
// Mark as hidden for the __proto__ accessor if needed.
|
||||
if (obj->hidden_prototype()) {
|
||||
map->set_is_hidden_prototype();
|
||||
}
|
||||
|
||||
// Mark as needs_access_check if needed.
|
||||
if (obj->needs_access_check()) {
|
||||
map->set_is_access_check_needed();
|
||||
}
|
||||
|
||||
// Set interceptor information in the map.
|
||||
if (!obj->named_property_handler()->IsUndefined()) {
|
||||
map->set_has_named_interceptor();
|
||||
}
|
||||
if (!obj->indexed_property_handler()->IsUndefined()) {
|
||||
map->set_has_indexed_interceptor();
|
||||
}
|
||||
|
||||
// Set instance call-as-function information in the map.
|
||||
if (!obj->instance_call_handler()->IsUndefined()) {
|
||||
map->set_has_instance_call_handler();
|
||||
}
|
||||
|
||||
result->shared()->set_function_data(*obj);
|
||||
result->shared()->DontAdaptArguments();
|
||||
|
||||
// Recursively copy parent templates' accessors, 'data' may be modified.
|
||||
Handle<DescriptorArray> array =
|
||||
Handle<DescriptorArray>(map->instance_descriptors());
|
||||
while (true) {
|
||||
Handle<Object> props = Handle<Object>(obj->property_accessors());
|
||||
if (!props->IsUndefined()) {
|
||||
array = Factory::CopyAppendCallbackDescriptors(array, props);
|
||||
}
|
||||
Handle<Object> parent = Handle<Object>(obj->parent_template());
|
||||
if (parent->IsUndefined()) break;
|
||||
obj = Handle<FunctionTemplateInfo>::cast(parent);
|
||||
}
|
||||
if (!array->IsEmpty()) {
|
||||
map->set_instance_descriptors(*array);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<MapCache> Factory::NewMapCache(int at_least_space_for) {
|
||||
CALL_HEAP_FUNCTION(MapCache::Allocate(at_least_space_for), MapCache);
|
||||
}
|
||||
|
||||
|
||||
static Object* UpdateMapCacheWith(Context* context,
|
||||
FixedArray* keys,
|
||||
Map* map) {
|
||||
Object* result = MapCache::cast(context->map_cache())->Put(keys, map);
|
||||
if (!result->IsFailure()) context->set_map_cache(MapCache::cast(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Handle<MapCache> Factory::AddToMapCache(Handle<Context> context,
|
||||
Handle<FixedArray> keys,
|
||||
Handle<Map> map) {
|
||||
CALL_HEAP_FUNCTION(UpdateMapCacheWith(*context, *keys, *map), MapCache);
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
|
||||
Handle<FixedArray> keys) {
|
||||
if (context->map_cache()->IsUndefined()) {
|
||||
// Allocate the new map cache for the global context.
|
||||
Handle<MapCache> new_cache = NewMapCache(24);
|
||||
context->set_map_cache(*new_cache);
|
||||
}
|
||||
// Check to see whether there is a maching element in the cache.
|
||||
Handle<MapCache> cache =
|
||||
Handle<MapCache>(MapCache::cast(context->map_cache()));
|
||||
Handle<Object> result = Handle<Object>(cache->Lookup(*keys));
|
||||
if (result->IsMap()) return Handle<Map>::cast(result);
|
||||
// Create a new map and add it to the cache.
|
||||
Handle<Map> map =
|
||||
CopyMap(Handle<Map>(context->object_function()->initial_map()));
|
||||
AddToMapCache(context, keys, map);
|
||||
return Handle<Map>(map);
|
||||
}
|
||||
|
||||
|
||||
void Factory::SetRegExpData(Handle<JSRegExp> regexp,
|
||||
JSRegExp::Type type,
|
||||
Handle<String> source,
|
||||
JSRegExp::Flags flags,
|
||||
Handle<Object> data) {
|
||||
Handle<FixedArray> store = NewFixedArray(JSRegExp::kDataSize);
|
||||
store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
|
||||
store->set(JSRegExp::kSourceIndex, *source);
|
||||
store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
|
||||
store->set(JSRegExp::kAtomPatternIndex, *data);
|
||||
regexp->set_data(*store);
|
||||
}
|
||||
|
||||
|
||||
void Factory::ConfigureInstance(Handle<FunctionTemplateInfo> desc,
|
||||
Handle<JSObject> instance,
|
||||
bool* pending_exception) {
|
||||
// Configure the instance by adding the properties specified by the
|
||||
// instance template.
|
||||
Handle<Object> instance_template = Handle<Object>(desc->instance_template());
|
||||
if (!instance_template->IsUndefined()) {
|
||||
Execution::ConfigureInstance(instance,
|
||||
instance_template,
|
||||
pending_exception);
|
||||
} else {
|
||||
*pending_exception = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,343 +0,0 @@
|
||||
// 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_FACTORY_H_
|
||||
#define V8_FACTORY_H_
|
||||
|
||||
#include "heap.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Interface for handle based allocation.
|
||||
|
||||
class Factory : public AllStatic {
|
||||
public:
|
||||
// Allocate a new fixed array.
|
||||
static Handle<FixedArray> NewFixedArray(
|
||||
int size,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
static Handle<DescriptorArray> NewDescriptorArray(int number_of_descriptors);
|
||||
|
||||
static Handle<String> LookupSymbol(Vector<const char> str);
|
||||
static Handle<String> LookupAsciiSymbol(const char* str) {
|
||||
return LookupSymbol(CStrVector(str));
|
||||
}
|
||||
|
||||
|
||||
// String creation functions. Most of the string creation functions take
|
||||
// a Heap::PretenureFlag argument to optionally request that they be
|
||||
// allocated in the old generation. The pretenure flag defaults to
|
||||
// DONT_TENURE.
|
||||
//
|
||||
// Creates a new String object. There are two String encodings: ASCII and
|
||||
// two byte. One should choose between the three string factory functions
|
||||
// based on the encoding of the string buffer that the string is
|
||||
// initialized from.
|
||||
// - ...FromAscii initializes the string from a buffer that is ASCII
|
||||
// encoded (it does not check that the buffer is ASCII encoded) and
|
||||
// the result will be ASCII encoded.
|
||||
// - ...FromUtf8 initializes the string from a buffer that is UTF-8
|
||||
// encoded. If the characters are all single-byte characters, the
|
||||
// result will be ASCII encoded, otherwise it will converted to two
|
||||
// byte.
|
||||
// - ...FromTwoByte initializes the string from a buffer that is two
|
||||
// byte encoded. If the characters are all single-byte characters,
|
||||
// the result will be converted to ASCII, otherwise it will be left as
|
||||
// two byte.
|
||||
//
|
||||
// ASCII strings are pretenured when used as keys in the SourceCodeCache.
|
||||
static Handle<String> NewStringFromAscii(
|
||||
Vector<const char> str,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
// UTF8 strings are pretenured when used for regexp literal patterns and
|
||||
// flags in the parser.
|
||||
static Handle<String> NewStringFromUtf8(
|
||||
Vector<const char> str,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
static Handle<String> NewStringFromTwoByte(Vector<const uc16> str);
|
||||
|
||||
// Allocates and partially initializes a TwoByte String. The characters of
|
||||
// the string are uninitialized. Currently used in regexp code only, where
|
||||
// they are pretenured.
|
||||
static Handle<String> NewRawTwoByteString(
|
||||
int length,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
// Create a new cons string object which consists of a pair of strings.
|
||||
static Handle<String> NewConsString(Handle<String> first,
|
||||
Handle<String> second);
|
||||
|
||||
// Create a new sliced string object which represents a substring of a
|
||||
// backing string.
|
||||
static Handle<String> NewStringSlice(Handle<String> str, int begin, int end);
|
||||
|
||||
// Creates a new external String object. There are two String encodings
|
||||
// in the system: ASCII and two byte. Unlike other String types, it does
|
||||
// not make sense to have a UTF-8 factory function for external strings,
|
||||
// because we cannot change the underlying buffer.
|
||||
static Handle<String> NewExternalStringFromAscii(
|
||||
ExternalAsciiString::Resource* resource);
|
||||
static Handle<String> NewExternalStringFromTwoByte(
|
||||
ExternalTwoByteString::Resource* resource);
|
||||
|
||||
// Create a global (but otherwise uninitialized) context.
|
||||
static Handle<Context> NewGlobalContext();
|
||||
|
||||
// Create a function context.
|
||||
static Handle<Context> NewFunctionContext(int length,
|
||||
Handle<JSFunction> closure);
|
||||
|
||||
// Create a 'with' context.
|
||||
static Handle<Context> NewWithContext(Handle<Context> previous,
|
||||
Handle<JSObject> extension);
|
||||
|
||||
// Return the Symbol maching the passed in string.
|
||||
static Handle<String> SymbolFromString(Handle<String> value);
|
||||
|
||||
// Allocate a new struct. The struct is pretenured (allocated directly in
|
||||
// the old generation).
|
||||
static Handle<Struct> NewStruct(InstanceType type);
|
||||
|
||||
static Handle<AccessorInfo> NewAccessorInfo();
|
||||
|
||||
static Handle<Script> NewScript(Handle<String> source);
|
||||
|
||||
// Proxies are pretenured when allocated by the bootstrapper.
|
||||
static Handle<Proxy> NewProxy(Address addr,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
// Allocate a new proxy. The proxy is pretenured (allocated directly in
|
||||
// the old generation).
|
||||
static Handle<Proxy> NewProxy(const AccessorDescriptor* proxy);
|
||||
|
||||
static Handle<ByteArray> NewByteArray(int length);
|
||||
|
||||
static Handle<Map> NewMap(InstanceType type, int instance_size);
|
||||
|
||||
static Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
|
||||
|
||||
static Handle<Map> CopyMap(Handle<Map> map);
|
||||
|
||||
static Handle<Map> CopyMapDropTransitions(Handle<Map> map);
|
||||
|
||||
static Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array);
|
||||
|
||||
// Numbers (eg, literals) are pretenured by the parser.
|
||||
static Handle<Object> NewNumber(double value,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
static Handle<Object> NewNumberFromInt(int value);
|
||||
|
||||
// These objects are used by the api to create env-independent data
|
||||
// structures in the heap.
|
||||
static Handle<JSObject> NewNeanderObject();
|
||||
|
||||
static Handle<JSObject> NewArgumentsObject(Handle<Object> callee, int length);
|
||||
|
||||
// JS objects are pretenured when allocated by the bootstrapper and
|
||||
// runtime.
|
||||
static Handle<JSObject> NewJSObject(Handle<JSFunction> constructor,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
// JS objects are pretenured when allocated by the bootstrapper and
|
||||
// runtime.
|
||||
static Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
|
||||
|
||||
// Allocate a JS object representing an object literal. The object is
|
||||
// pretenured (allocated directly in the old generation).
|
||||
static Handle<JSObject> NewObjectLiteral(int expected_number_of_properties);
|
||||
|
||||
// Allocate a JS array representing an array literal. The array is
|
||||
// pretenured (allocated directly in the old generation).
|
||||
static Handle<JSArray> NewArrayLiteral(int length);
|
||||
|
||||
// JS arrays are pretenured when allocated by the parser.
|
||||
static Handle<JSArray> NewJSArray(int init_length,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
static Handle<JSArray> NewJSArrayWithElements(
|
||||
Handle<FixedArray> elements,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
static Handle<JSFunction> NewFunction(Handle<String> name,
|
||||
Handle<Object> prototype);
|
||||
|
||||
static Handle<JSFunction> NewFunction(Handle<Object> super, bool is_global);
|
||||
|
||||
static Handle<JSFunction> NewFunctionFromBoilerplate(
|
||||
Handle<JSFunction> boilerplate,
|
||||
Handle<Context> context);
|
||||
|
||||
static Handle<Code> NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
|
||||
Code::Flags flags);
|
||||
|
||||
static Handle<Code> CopyCode(Handle<Code> code);
|
||||
|
||||
static Handle<Object> ToObject(Handle<Object> object,
|
||||
Handle<Context> global_context);
|
||||
|
||||
// Interface for creating error objects.
|
||||
|
||||
static Handle<Object> NewError(const char* maker, const char* type,
|
||||
Handle<JSArray> args);
|
||||
static Handle<Object> NewError(const char* maker, const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
static Handle<Object> NewError(const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
static Handle<Object> NewError(Handle<String> message);
|
||||
static Handle<Object> NewError(const char* constructor,
|
||||
Handle<String> message);
|
||||
|
||||
static Handle<Object> NewTypeError(const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
static Handle<Object> NewTypeError(Handle<String> message);
|
||||
|
||||
static Handle<Object> NewRangeError(const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
static Handle<Object> NewRangeError(Handle<String> message);
|
||||
|
||||
static Handle<Object> NewSyntaxError(const char* type, Handle<JSArray> args);
|
||||
static Handle<Object> NewSyntaxError(Handle<String> message);
|
||||
|
||||
static Handle<Object> NewReferenceError(const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
static Handle<Object> NewReferenceError(Handle<String> message);
|
||||
|
||||
static Handle<Object> NewEvalError(const char* type,
|
||||
Vector< Handle<Object> > args);
|
||||
|
||||
|
||||
static Handle<JSFunction> NewFunction(Handle<String> name,
|
||||
InstanceType type,
|
||||
int instance_size,
|
||||
Handle<Code> code,
|
||||
bool force_initial_map);
|
||||
|
||||
static Handle<JSFunction> NewFunctionBoilerplate(Handle<String> name,
|
||||
int number_of_literals,
|
||||
bool contains_array_literal,
|
||||
Handle<Code> code);
|
||||
|
||||
static Handle<JSFunction> NewFunctionBoilerplate(Handle<String> name);
|
||||
|
||||
static Handle<JSFunction> NewFunction(Handle<Map> function_map,
|
||||
Handle<SharedFunctionInfo> shared, Handle<Object> prototype);
|
||||
|
||||
|
||||
static Handle<JSFunction> NewFunctionWithPrototype(Handle<String> name,
|
||||
InstanceType type,
|
||||
int instance_size,
|
||||
Handle<JSObject> prototype,
|
||||
Handle<Code> code,
|
||||
bool force_initial_map);
|
||||
|
||||
static Handle<DescriptorArray> CopyAppendProxyDescriptor(
|
||||
Handle<DescriptorArray> array,
|
||||
Handle<String> key,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
enum ApiInstanceType {
|
||||
JavaScriptObject,
|
||||
InnerGlobalObject,
|
||||
OuterGlobalObject
|
||||
};
|
||||
|
||||
static Handle<JSFunction> CreateApiFunction(
|
||||
Handle<FunctionTemplateInfo> data,
|
||||
ApiInstanceType type = JavaScriptObject);
|
||||
|
||||
static Handle<JSFunction> InstallMembers(Handle<JSFunction> function);
|
||||
|
||||
// Installs interceptors on the instance. 'desc' is a function template,
|
||||
// and instance is an object instance created by the function of this
|
||||
// function tempalte.
|
||||
static void ConfigureInstance(Handle<FunctionTemplateInfo> desc,
|
||||
Handle<JSObject> instance,
|
||||
bool* pending_exception);
|
||||
|
||||
#define ROOT_ACCESSOR(type, name) \
|
||||
static Handle<type> name() { return Handle<type>(&Heap::name##_); }
|
||||
ROOT_LIST(ROOT_ACCESSOR)
|
||||
#undef ROOT_ACCESSOR_ACCESSOR
|
||||
|
||||
#define SYMBOL_ACCESSOR(name, str) \
|
||||
static Handle<String> name() { return Handle<String>(&Heap::name##_); }
|
||||
SYMBOL_LIST(SYMBOL_ACCESSOR)
|
||||
#undef SYMBOL_ACCESSOR
|
||||
|
||||
static Handle<SharedFunctionInfo> NewSharedFunctionInfo(Handle<String> name);
|
||||
|
||||
static Handle<Dictionary> DictionaryAtNumberPut(Handle<Dictionary>,
|
||||
uint32_t key,
|
||||
Handle<Object> value);
|
||||
|
||||
static Handle<DebugInfo> NewDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
|
||||
// Return a map using the map cache in the global context.
|
||||
// The key the an ordered set of property names.
|
||||
static Handle<Map> ObjectLiteralMapFromCache(Handle<Context> context,
|
||||
Handle<FixedArray> keys);
|
||||
|
||||
// Creates a new FixedArray that holds the data associated with the
|
||||
// regexp and stores it in the regexp.
|
||||
static void SetRegExpData(Handle<JSRegExp> regexp,
|
||||
JSRegExp::Type type,
|
||||
Handle<String> source,
|
||||
JSRegExp::Flags flags,
|
||||
Handle<Object> data);
|
||||
|
||||
private:
|
||||
static Handle<JSFunction> NewFunctionHelper(Handle<String> name,
|
||||
Handle<Object> prototype);
|
||||
|
||||
static Handle<DescriptorArray> CopyAppendCallbackDescriptors(
|
||||
Handle<DescriptorArray> array,
|
||||
Handle<Object> descriptors);
|
||||
|
||||
static Handle<JSFunction> BaseNewFunctionFromBoilerplate(
|
||||
Handle<JSFunction> boilerplate,
|
||||
Handle<Map> function_map);
|
||||
|
||||
// Create a new map cache.
|
||||
static Handle<MapCache> NewMapCache(int at_least_space_for);
|
||||
|
||||
// Update the map cache in the global context with (keys, map)
|
||||
static Handle<MapCache> AddToMapCache(Handle<Context> context,
|
||||
Handle<FixedArray> keys,
|
||||
Handle<Map> map);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_FACTORY_H_
|
@ -1,323 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// This file defines all of the flags. It is separated into different section,
|
||||
// for Debug, Release, Logging and Profiling, etc. To add a new flag, find the
|
||||
// correct section, and use one of the DEFINE_ macros, without a trailing ';'.
|
||||
//
|
||||
// This include does not have a guard, because it is a template-style include,
|
||||
// which can be included multiple times in different modes. It expects to have
|
||||
// a mode defined before it's included. The modes are FLAG_MODE_... below:
|
||||
|
||||
// We want to declare the names of the variables for the header file. Normally
|
||||
// this will just be an extern declaration, but for a readonly flag we let the
|
||||
// compiler make better optimizations by giving it the value.
|
||||
#if defined(FLAG_MODE_DECLARE)
|
||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||
extern ctype FLAG_##nam;
|
||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt) \
|
||||
static ctype const FLAG_##nam = def;
|
||||
|
||||
// We want to supply the actual storage and value for the flag variable in the
|
||||
// .cc file. We only do this for writable flags.
|
||||
#elif defined(FLAG_MODE_DEFINE)
|
||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||
ctype FLAG_##nam = def;
|
||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||
|
||||
// We need to define all of our default values so that the Flag structure can
|
||||
// access them by pointer. These are just used internally inside of one .cc,
|
||||
// for MODE_META, so there is no impact on the flags interface.
|
||||
#elif defined(FLAG_MODE_DEFINE_DEFAULTS)
|
||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||
static ctype const FLAGDEFAULT_##nam = def;
|
||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||
|
||||
|
||||
// We want to write entries into our meta data table, for internal parsing and
|
||||
// printing / etc in the flag parser code. We only do this for writable flags.
|
||||
#elif defined(FLAG_MODE_META)
|
||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||
{ Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt },
|
||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||
|
||||
#else
|
||||
#error No mode supplied when including flags.defs
|
||||
#endif
|
||||
|
||||
#define DEFINE_bool(nam, def, cmt) FLAG(BOOL, bool, nam, def, cmt)
|
||||
#define DEFINE_int(nam, def, cmt) FLAG(INT, int, nam, def, cmt)
|
||||
#define DEFINE_float(nam, def, cmt) FLAG(FLOAT, double, nam, def, cmt)
|
||||
#define DEFINE_string(nam, def, cmt) FLAG(STRING, const char*, nam, def, cmt)
|
||||
|
||||
//
|
||||
// Flags in all modes.
|
||||
//
|
||||
#define FLAG FLAG_FULL
|
||||
|
||||
// assembler-ia32.cc / assembler-arm.cc
|
||||
DEFINE_bool(debug_code, false,
|
||||
"generate extra code (comments, assertions) for debugging")
|
||||
DEFINE_bool(emit_branch_hints, false, "emit branch hints")
|
||||
DEFINE_bool(push_pop_elimination, true,
|
||||
"eliminate redundant push/pops in assembly code")
|
||||
DEFINE_bool(print_push_pop_elimination, false,
|
||||
"print elimination of redundant push/pops in assembly code")
|
||||
|
||||
// bootstrapper.cc
|
||||
DEFINE_string(expose_natives_as, NULL, "expose natives in global object")
|
||||
DEFINE_string(expose_debug_as, NULL, "expose debug in global object")
|
||||
DEFINE_string(natives_file, NULL, "alternative natives file")
|
||||
DEFINE_bool(expose_gc, false, "expose gc extension")
|
||||
|
||||
// builtins-ia32.cc
|
||||
DEFINE_bool(inline_new, true, "use fast inline allocation")
|
||||
|
||||
// checks.cc
|
||||
DEFINE_bool(stack_trace_on_abort, true,
|
||||
"print a stack trace if an assertion failure occurs")
|
||||
|
||||
// codegen-ia32.cc / codegen-arm.cc
|
||||
DEFINE_bool(trace, false, "trace function calls")
|
||||
DEFINE_bool(defer_negation, true, "defer negation operation")
|
||||
DEFINE_bool(check_stack, true,
|
||||
"check stack for overflow, interrupt, breakpoint")
|
||||
|
||||
// codegen.cc
|
||||
DEFINE_bool(lazy, true, "use lazy compilation")
|
||||
DEFINE_bool(debug_info, true, "add debug information to compiled functions")
|
||||
|
||||
// compiler.cc
|
||||
DEFINE_bool(strict, false, "strict error checking")
|
||||
DEFINE_int(min_preparse_length, 1024,
|
||||
"Minimum length for automatic enable preparsing")
|
||||
|
||||
// debug.cc
|
||||
DEFINE_bool(remote_debugging, false, "enable remote debugging")
|
||||
DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response")
|
||||
|
||||
// execution.cc
|
||||
DEFINE_bool(call_regexp, false, "allow calls to RegExp objects")
|
||||
|
||||
// frames.cc
|
||||
DEFINE_int(max_stack_trace_source_length, 300,
|
||||
"maximum length of function source code printed in a stack trace.")
|
||||
|
||||
// heap.cc
|
||||
DEFINE_int(new_space_size, 0, "size of (each semispace in) the new generation")
|
||||
DEFINE_int(old_space_size, 0, "size of the old generation")
|
||||
DEFINE_bool(gc_global, false, "always perform global GCs")
|
||||
DEFINE_int(gc_interval, -1, "garbage collect after <n> allocations")
|
||||
DEFINE_bool(trace_gc, false,
|
||||
"print one trace line following each garbage collection")
|
||||
|
||||
// ic.cc
|
||||
DEFINE_bool(use_ic, true, "use inline caching")
|
||||
|
||||
// macro-assembler-ia32.cc
|
||||
DEFINE_bool(native_code_counters, false,
|
||||
"generate extra code for manipulating stats counters")
|
||||
|
||||
// mark-compact.cc
|
||||
DEFINE_bool(always_compact, false, "Perform compaction on every full GC")
|
||||
DEFINE_bool(never_compact, false,
|
||||
"Never perform compaction on full GC - testing only")
|
||||
DEFINE_bool(cleanup_ics_at_gc, true,
|
||||
"Flush inline caches prior to mark compact collection.")
|
||||
DEFINE_bool(cleanup_caches_in_maps_at_gc, true,
|
||||
"Flush code caches in maps during mark compact cycle.")
|
||||
|
||||
DEFINE_bool(canonicalize_object_literal_maps, true,
|
||||
"Canonicalize maps for object literals.")
|
||||
|
||||
// mksnapshot.cc
|
||||
DEFINE_bool(h, false, "print this message")
|
||||
|
||||
// parser.cc
|
||||
DEFINE_bool(allow_natives_syntax, false, "allow natives syntax")
|
||||
|
||||
// simulator-arm.cc
|
||||
DEFINE_bool(trace_sim, false, "trace simulator execution")
|
||||
DEFINE_int(stop_sim_at, 0, "Simulator stop after x number of instructions")
|
||||
|
||||
// top.cc
|
||||
DEFINE_bool(trace_exception, false,
|
||||
"print stack trace when throwing exceptions")
|
||||
DEFINE_bool(preallocate_message_memory, false,
|
||||
"preallocate some memory to build stack traces.")
|
||||
|
||||
// usage-analyzer.cc
|
||||
DEFINE_bool(usage_computation, true, "compute variable usage counts")
|
||||
|
||||
// v8.cc
|
||||
DEFINE_bool(preemption, false,
|
||||
"activate a 100ms timer that switches between V8 threads")
|
||||
|
||||
// Testing flags test/cctest/test-{flags,api,serialization}.cc
|
||||
DEFINE_bool(testing_bool_flag, true, "testing_bool_flag")
|
||||
DEFINE_int(testing_int_flag, 13, "testing_int_flag")
|
||||
DEFINE_float(testing_float_flag, 2.5, "float-flag")
|
||||
DEFINE_string(testing_string_flag, "Hello, world!", "string-flag")
|
||||
DEFINE_int(testing_prng_seed, 42, "Seed used for threading test randomness")
|
||||
#ifdef WIN32
|
||||
DEFINE_string(testing_serialization_file, "C:\\Windows\\Temp\\serdes",
|
||||
"file in which to testing_serialize heap")
|
||||
#else
|
||||
DEFINE_string(testing_serialization_file, "/tmp/serdes",
|
||||
"file in which to serialize heap")
|
||||
#endif
|
||||
|
||||
//
|
||||
// Dev shell flags
|
||||
//
|
||||
|
||||
DEFINE_bool(dump_counters, false, "Dump counters on exit")
|
||||
|
||||
//
|
||||
// Debug only flags
|
||||
//
|
||||
#undef FLAG
|
||||
#ifdef DEBUG
|
||||
#define FLAG FLAG_FULL
|
||||
#else
|
||||
#define FLAG FLAG_READONLY
|
||||
#endif
|
||||
|
||||
// checks.cc
|
||||
DEFINE_bool(enable_slow_asserts, false,
|
||||
"enable asserts that are slow to execute")
|
||||
|
||||
// code-stubs.cc
|
||||
DEFINE_bool(print_code_stubs, false, "print code stubs")
|
||||
|
||||
// codegen-ia32.cc / codegen-arm.cc
|
||||
DEFINE_bool(trace_codegen, false,
|
||||
"print name of functions for which code is generated")
|
||||
DEFINE_bool(print_builtin_code, false, "print generated code for builtins")
|
||||
DEFINE_bool(print_source, false, "pretty print source code")
|
||||
DEFINE_bool(print_builtin_source, false,
|
||||
"pretty print source code for builtins")
|
||||
DEFINE_bool(print_ast, false, "print source AST")
|
||||
DEFINE_bool(print_builtin_ast, false, "print source AST for builtins")
|
||||
DEFINE_bool(trace_calls, false, "trace calls")
|
||||
DEFINE_bool(trace_builtin_calls, false, "trace builtins calls")
|
||||
DEFINE_string(stop_at, "", "function name where to insert a breakpoint")
|
||||
|
||||
// compiler.cc
|
||||
DEFINE_bool(print_builtin_scopes, false, "print scopes for builtins")
|
||||
DEFINE_bool(print_scopes, false, "print scopes")
|
||||
|
||||
// contexts.cc
|
||||
DEFINE_bool(trace_contexts, false, "trace contexts operations")
|
||||
|
||||
// heap.cc
|
||||
DEFINE_bool(gc_greedy, false, "perform GC prior to some allocations")
|
||||
DEFINE_bool(gc_verbose, false, "print stuff during garbage collection")
|
||||
DEFINE_bool(heap_stats, false, "report heap statistics before and after GC")
|
||||
DEFINE_bool(code_stats, false, "report code statistics after GC")
|
||||
DEFINE_bool(verify_heap, false, "verify heap pointers before and after GC")
|
||||
DEFINE_bool(print_handles, false, "report handles after GC")
|
||||
DEFINE_bool(print_global_handles, false, "report global handles after GC")
|
||||
DEFINE_bool(print_rset, false, "print remembered sets before GC")
|
||||
|
||||
// ic.cc
|
||||
DEFINE_bool(trace_ic, false, "trace inline cache state transitions")
|
||||
|
||||
// objects.cc
|
||||
DEFINE_bool(trace_normalization,
|
||||
false,
|
||||
"prints when objects are turned into dictionaries.")
|
||||
|
||||
// runtime.cc
|
||||
DEFINE_bool(trace_lazy, false, "trace lazy compilation")
|
||||
|
||||
// serialize.cc
|
||||
DEFINE_bool(debug_serialization, false,
|
||||
"write debug information into the snapshot.")
|
||||
|
||||
// spaces.cc
|
||||
DEFINE_bool(collect_heap_spill_statistics, false,
|
||||
"report heap spill statistics along with heap_stats "
|
||||
"(requires heap_stats)")
|
||||
|
||||
DEFINE_bool(trace_regexps, false, "trace regexp execution")
|
||||
|
||||
//
|
||||
// Logging and profiling only flags
|
||||
//
|
||||
#undef FLAG
|
||||
#ifdef ENABLE_LOGGING_AND_PROFILING
|
||||
#define FLAG FLAG_FULL
|
||||
#else
|
||||
#define FLAG FLAG_READONLY
|
||||
#endif
|
||||
|
||||
// log.cc
|
||||
DEFINE_bool(log, false,
|
||||
"Minimal logging (no API, code, GC, suspect, or handles samples).")
|
||||
DEFINE_bool(log_all, false, "Log all events to the log file.")
|
||||
DEFINE_bool(log_api, false, "Log API events to the log file.")
|
||||
DEFINE_bool(log_code, false,
|
||||
"Log code events to the log file without profiling.")
|
||||
DEFINE_bool(log_gc, false,
|
||||
"Log heap samples on garbage collection for the hp2ps tool.")
|
||||
DEFINE_bool(log_handles, false, "Log global handle events.")
|
||||
DEFINE_bool(log_state_changes, false, "Log state changes.")
|
||||
DEFINE_bool(log_suspect, false, "Log suspect operations.")
|
||||
DEFINE_bool(prof, false,
|
||||
"Log statistical profiling information (implies --log-code).")
|
||||
DEFINE_bool(log_regexp, false, "Log regular expression execution.")
|
||||
DEFINE_bool(sliding_state_window, false,
|
||||
"Update sliding state window counters.")
|
||||
DEFINE_string(logfile, "v8.log", "Specify the name of the log file.")
|
||||
|
||||
//
|
||||
// Disassembler only flags
|
||||
//
|
||||
#undef FLAG
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
#define FLAG FLAG_FULL
|
||||
#else
|
||||
#define FLAG FLAG_READONLY
|
||||
#endif
|
||||
|
||||
// codegen-ia32.cc / codegen-arm.cc
|
||||
DEFINE_bool(print_code, false, "print generated code")
|
||||
|
||||
// Cleanup...
|
||||
#undef FLAG_FULL
|
||||
#undef FLAG_READONLY
|
||||
#undef FLAG
|
||||
|
||||
#undef DEFINE_bool
|
||||
#undef DEFINE_int
|
||||
#undef DEFINE_string
|
||||
|
||||
#undef FLAG_MODE_DECLARE
|
||||
#undef FLAG_MODE_DEFINE
|
||||
#undef FLAG_MODE_DEFINE_DEFAULTS
|
||||
#undef FLAG_MODE_META
|
@ -1,459 +0,0 @@
|
||||
// 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 <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// Define all of our flags.
|
||||
#define FLAG_MODE_DEFINE
|
||||
#include "flag-definitions.h"
|
||||
|
||||
// Define all of our flags default values.
|
||||
#define FLAG_MODE_DEFINE_DEFAULTS
|
||||
#include "flag-definitions.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// This structure represents a single entry in the flag system, with a pointer
|
||||
// to the actual flag, default value, comment, etc. This is designed to be POD
|
||||
// initialized as to avoid requiring static constructors.
|
||||
struct Flag {
|
||||
enum FlagType { TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING };
|
||||
|
||||
FlagType type_; // What type of flag, bool, int, or string.
|
||||
const char* name_; // Name of the flag, ex "my_flag".
|
||||
void* valptr_; // Pointer to the global flag variable.
|
||||
const void* defptr_; // Pointer to the default value.
|
||||
const char* cmt_; // A comment about the flags purpose.
|
||||
|
||||
FlagType type() const { return type_; }
|
||||
|
||||
const char* name() const { return name_; }
|
||||
|
||||
const char* comment() const { return cmt_; }
|
||||
|
||||
bool* bool_variable() const {
|
||||
ASSERT(type_ == TYPE_BOOL);
|
||||
return reinterpret_cast<bool*>(valptr_);
|
||||
}
|
||||
|
||||
int* int_variable() const {
|
||||
ASSERT(type_ == TYPE_INT);
|
||||
return reinterpret_cast<int*>(valptr_);
|
||||
}
|
||||
|
||||
double* float_variable() const {
|
||||
ASSERT(type_ == TYPE_FLOAT);
|
||||
return reinterpret_cast<double*>(valptr_);
|
||||
}
|
||||
|
||||
const char** string_variable() const {
|
||||
ASSERT(type_ == TYPE_STRING);
|
||||
return reinterpret_cast<const char**>(valptr_);
|
||||
}
|
||||
|
||||
bool bool_default() const {
|
||||
ASSERT(type_ == TYPE_BOOL);
|
||||
return *reinterpret_cast<const bool*>(defptr_);
|
||||
}
|
||||
|
||||
int int_default() const {
|
||||
ASSERT(type_ == TYPE_INT);
|
||||
return *reinterpret_cast<const int*>(defptr_);
|
||||
}
|
||||
|
||||
double float_default() const {
|
||||
ASSERT(type_ == TYPE_FLOAT);
|
||||
return *reinterpret_cast<const double*>(defptr_);
|
||||
}
|
||||
|
||||
const char* string_default() const {
|
||||
ASSERT(type_ == TYPE_STRING);
|
||||
return *reinterpret_cast<const char* const *>(defptr_);
|
||||
}
|
||||
|
||||
// Compare this flag's current value against the default.
|
||||
bool IsDefault() const {
|
||||
switch (type_) {
|
||||
case TYPE_BOOL:
|
||||
return *bool_variable() == bool_default();
|
||||
case TYPE_INT:
|
||||
return *int_variable() == int_default();
|
||||
case TYPE_FLOAT:
|
||||
return *float_variable() == float_default();
|
||||
case TYPE_STRING:
|
||||
const char* str1 = *string_variable();
|
||||
const char* str2 = string_default();
|
||||
if (str2 == NULL) return str1 == NULL;
|
||||
if (str1 == NULL) return str2 == NULL;
|
||||
return strcmp(str1, str2) == 0;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set a flag back to it's default value.
|
||||
void Reset() {
|
||||
switch (type_) {
|
||||
case TYPE_BOOL:
|
||||
*bool_variable() = bool_default();
|
||||
break;
|
||||
case TYPE_INT:
|
||||
*int_variable() = int_default();
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
*float_variable() = float_default();
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
*string_variable() = string_default();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Flag flags[] = {
|
||||
#define FLAG_MODE_META
|
||||
#include "flag-definitions.h"
|
||||
};
|
||||
|
||||
const size_t num_flags = sizeof(flags) / sizeof(*flags);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
static const char* Type2String(Flag::FlagType type) {
|
||||
switch (type) {
|
||||
case Flag::TYPE_BOOL: return "bool";
|
||||
case Flag::TYPE_INT: return "int";
|
||||
case Flag::TYPE_FLOAT: return "float";
|
||||
case Flag::TYPE_STRING: return "string";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char* ToString(Flag* flag) {
|
||||
Vector<char> value;
|
||||
switch (flag->type()) {
|
||||
case Flag::TYPE_BOOL:
|
||||
value = Vector<char>::New(6);
|
||||
OS::SNPrintF(value, "%s", (*flag->bool_variable() ? "true" : "false"));
|
||||
break;
|
||||
case Flag::TYPE_INT:
|
||||
value = Vector<char>::New(12);
|
||||
OS::SNPrintF(value, "%d", *flag->int_variable());
|
||||
break;
|
||||
case Flag::TYPE_FLOAT:
|
||||
value = Vector<char>::New(20);
|
||||
OS::SNPrintF(value, "%f", *flag->float_variable());
|
||||
break;
|
||||
case Flag::TYPE_STRING:
|
||||
const char* str = *flag->string_variable();
|
||||
if (str) {
|
||||
int length = strlen(str) + 1;
|
||||
value = Vector<char>::New(length);
|
||||
OS::SNPrintF(value, "%s", str);
|
||||
} else {
|
||||
value = Vector<char>::New(5);
|
||||
OS::SNPrintF(value, "NULL");
|
||||
}
|
||||
break;
|
||||
}
|
||||
ASSERT(!value.is_empty());
|
||||
return value.start();
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
List<char *>* FlagList::argv() {
|
||||
List<char *>* args = new List<char*>(8);
|
||||
for (size_t i = 0; i < num_flags; ++i) {
|
||||
Flag* f = &flags[i];
|
||||
if (!f->IsDefault()) {
|
||||
Vector<char> cmdline_flag;
|
||||
if (f->type() != Flag::TYPE_BOOL || *(f->bool_variable())) {
|
||||
int length = strlen(f->name()) + 2 + 1;
|
||||
cmdline_flag = Vector<char>::New(length);
|
||||
OS::SNPrintF(cmdline_flag, "--%s", f->name());
|
||||
} else {
|
||||
int length = strlen(f->name()) + 4 + 1;
|
||||
cmdline_flag = Vector<char>::New(length);
|
||||
OS::SNPrintF(cmdline_flag, "--no%s", f->name());
|
||||
}
|
||||
args->Add(cmdline_flag.start());
|
||||
if (f->type() != Flag::TYPE_BOOL) {
|
||||
args->Add(ToString(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
// Helper function to parse flags: Takes an argument arg and splits it into
|
||||
// a flag name and flag value (or NULL if they are missing). is_bool is set
|
||||
// if the arg started with "-no" or "--no". The buffer may be used to NUL-
|
||||
// terminate the name, it must be large enough to hold any possible name.
|
||||
static void SplitArgument(const char* arg,
|
||||
char* buffer,
|
||||
int buffer_size,
|
||||
const char** name,
|
||||
const char** value,
|
||||
bool* is_bool) {
|
||||
*name = NULL;
|
||||
*value = NULL;
|
||||
*is_bool = false;
|
||||
|
||||
if (*arg == '-') {
|
||||
// find the begin of the flag name
|
||||
arg++; // remove 1st '-'
|
||||
if (*arg == '-')
|
||||
arg++; // remove 2nd '-'
|
||||
if (arg[0] == 'n' && arg[1] == 'o') {
|
||||
arg += 2; // remove "no"
|
||||
*is_bool = true;
|
||||
}
|
||||
*name = arg;
|
||||
|
||||
// find the end of the flag name
|
||||
while (*arg != '\0' && *arg != '=')
|
||||
arg++;
|
||||
|
||||
// get the value if any
|
||||
if (*arg == '=') {
|
||||
// make a copy so we can NUL-terminate flag name
|
||||
int n = arg - *name;
|
||||
CHECK(n < buffer_size); // buffer is too small
|
||||
memcpy(buffer, *name, n);
|
||||
buffer[n] = '\0';
|
||||
*name = buffer;
|
||||
// get the value
|
||||
*value = arg + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline char NormalizeChar(char ch) {
|
||||
return ch == '_' ? '-' : ch;
|
||||
}
|
||||
|
||||
|
||||
static bool EqualNames(const char* a, const char* b) {
|
||||
for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
|
||||
if (a[i] == '\0') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static Flag* FindFlag(const char* name) {
|
||||
for (size_t i = 0; i < num_flags; ++i) {
|
||||
if (EqualNames(name, flags[i].name()))
|
||||
return &flags[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
int FlagList::SetFlagsFromCommandLine(int* argc,
|
||||
char** argv,
|
||||
bool remove_flags) {
|
||||
// parse arguments
|
||||
for (int i = 1; i < *argc;) {
|
||||
int j = i; // j > 0
|
||||
const char* arg = argv[i++];
|
||||
|
||||
// split arg into flag components
|
||||
char buffer[1*KB];
|
||||
const char* name;
|
||||
const char* value;
|
||||
bool is_bool;
|
||||
SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
|
||||
|
||||
if (name != NULL) {
|
||||
// lookup the flag
|
||||
Flag* flag = FindFlag(name);
|
||||
if (flag == NULL) {
|
||||
if (remove_flags) {
|
||||
// We don't recognize this flag but since we're removing
|
||||
// the flags we recognize we assume that the remaining flags
|
||||
// will be processed somewhere else so this flag might make
|
||||
// sense there.
|
||||
continue;
|
||||
} else {
|
||||
fprintf(stderr, "Error: unrecognized flag %s\n", arg);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
// if we still need a flag value, use the next argument if available
|
||||
if (flag->type() != Flag::TYPE_BOOL && value == NULL) {
|
||||
if (i < *argc) {
|
||||
value = argv[i++];
|
||||
} else {
|
||||
fprintf(stderr, "Error: missing value for flag %s of type %s\n",
|
||||
arg, Type2String(flag->type()));
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
// set the flag
|
||||
char* endp = const_cast<char*>(""); // *endp is only read
|
||||
switch (flag->type()) {
|
||||
case Flag::TYPE_BOOL:
|
||||
*flag->bool_variable() = !is_bool;
|
||||
break;
|
||||
case Flag::TYPE_INT:
|
||||
*flag->int_variable() = strtol(value, &endp, 10); // NOLINT
|
||||
break;
|
||||
case Flag::TYPE_FLOAT:
|
||||
*flag->float_variable() = strtod(value, &endp);
|
||||
break;
|
||||
case Flag::TYPE_STRING:
|
||||
*flag->string_variable() = value;
|
||||
break;
|
||||
}
|
||||
|
||||
// handle errors
|
||||
if ((flag->type() == Flag::TYPE_BOOL && value != NULL) ||
|
||||
(flag->type() != Flag::TYPE_BOOL && is_bool) ||
|
||||
*endp != '\0') {
|
||||
fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
|
||||
arg, Type2String(flag->type()));
|
||||
return j;
|
||||
}
|
||||
|
||||
// remove the flag & value from the command
|
||||
if (remove_flags)
|
||||
while (j < i)
|
||||
argv[j++] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// shrink the argument list
|
||||
if (remove_flags) {
|
||||
int j = 1;
|
||||
for (int i = 1; i < *argc; i++) {
|
||||
if (argv[i] != NULL)
|
||||
argv[j++] = argv[i];
|
||||
}
|
||||
*argc = j;
|
||||
}
|
||||
|
||||
// parsed all flags successfully
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char* SkipWhiteSpace(char* p) {
|
||||
while (*p != '\0' && isspace(*p) != 0) p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static char* SkipBlackSpace(char* p) {
|
||||
while (*p != '\0' && isspace(*p) == 0) p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
int FlagList::SetFlagsFromString(const char* str, int len) {
|
||||
// make a 0-terminated copy of str
|
||||
char* copy0 = NewArray<char>(len + 1);
|
||||
memcpy(copy0, str, len);
|
||||
copy0[len] = '\0';
|
||||
|
||||
// strip leading white space
|
||||
char* copy = SkipWhiteSpace(copy0);
|
||||
|
||||
// count the number of 'arguments'
|
||||
int argc = 1; // be compatible with SetFlagsFromCommandLine()
|
||||
for (char* p = copy; *p != '\0'; argc++) {
|
||||
p = SkipBlackSpace(p);
|
||||
p = SkipWhiteSpace(p);
|
||||
}
|
||||
|
||||
// allocate argument array
|
||||
char** argv = NewArray<char*>(argc);
|
||||
|
||||
// split the flags string into arguments
|
||||
argc = 1; // be compatible with SetFlagsFromCommandLine()
|
||||
for (char* p = copy; *p != '\0'; argc++) {
|
||||
argv[argc] = p;
|
||||
p = SkipBlackSpace(p);
|
||||
if (*p != '\0') *p++ = '\0'; // 0-terminate argument
|
||||
p = SkipWhiteSpace(p);
|
||||
}
|
||||
|
||||
// set the flags
|
||||
int result = SetFlagsFromCommandLine(&argc, argv, false);
|
||||
|
||||
// cleanup
|
||||
DeleteArray(argv);
|
||||
// don't delete copy0 since the substrings
|
||||
// may be pointed to by FLAG variables!
|
||||
// (this is a memory leak, but it's minor since this
|
||||
// code is only used for debugging, or perhaps once
|
||||
// during initialization).
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void FlagList::ResetAllFlags() {
|
||||
for (size_t i = 0; i < num_flags; ++i) {
|
||||
flags[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void FlagList::PrintHelp() {
|
||||
for (size_t i = 0; i < num_flags; ++i) {
|
||||
Flag* f = &flags[i];
|
||||
char* value = ToString(f);
|
||||
printf(" --%s (%s) type: %s default: %s\n",
|
||||
f->name(), f->comment(), Type2String(f->type()), value);
|
||||
DeleteArray(value);
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,78 +0,0 @@
|
||||
// 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_FLAGS_H_
|
||||
#define V8_FLAGS_H_
|
||||
|
||||
#include "checks.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
// Declare all of our flags.
|
||||
#define FLAG_MODE_DECLARE
|
||||
#include "flag-definitions.h"
|
||||
|
||||
// The global list of all flags.
|
||||
class FlagList {
|
||||
public:
|
||||
// The list of all flags with a value different from the default
|
||||
// and their values. The format of the list is like the format of the
|
||||
// argv array passed to the main function, e.g.
|
||||
// ("--prof", "--log-file", "v8.prof", "--nolazy").
|
||||
//
|
||||
// The caller is responsible for disposing the list.
|
||||
static List<char *>* argv();
|
||||
|
||||
// Set the flag values by parsing the command line. If remove_flags is
|
||||
// set, the flags and associated values are removed from (argc,
|
||||
// argv). Returns 0 if no error occurred. Otherwise, returns the argv
|
||||
// index > 0 for the argument where an error occurred. In that case,
|
||||
// (argc, argv) will remain unchanged indepdendent of the remove_flags
|
||||
// value, and no assumptions about flag settings should be made.
|
||||
//
|
||||
// The following syntax for flags is accepted (both '-' and '--' are ok):
|
||||
//
|
||||
// --flag (bool flags only)
|
||||
// --noflag (bool flags only)
|
||||
// --flag=value (non-bool flags only, no spaces around '=')
|
||||
// --flag value (non-bool flags only)
|
||||
static int SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags);
|
||||
|
||||
// Set the flag values by parsing the string str. Splits string into argc
|
||||
// substrings argv[], each of which consisting of non-white-space chars,
|
||||
// and then calls SetFlagsFromCommandLine() and returns its result.
|
||||
static int SetFlagsFromString(const char* str, int len);
|
||||
|
||||
// Reset all flags to their default value.
|
||||
static void ResetAllFlags();
|
||||
|
||||
// Print help to stdout with flags, types, and default values.
|
||||
static void PrintHelp();
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_FLAGS_H_
|
@ -1,123 +0,0 @@
|
||||
// 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 "frames-inl.h"
|
||||
#include "assembler-arm-inl.h"
|
||||
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
StackFrame::Type StackFrame::ComputeType(State* state) {
|
||||
ASSERT(state->fp != NULL);
|
||||
if (StandardFrame::IsArgumentsAdaptorFrame(state->fp)) {
|
||||
return ARGUMENTS_ADAPTOR;
|
||||
}
|
||||
// The marker and function offsets overlap. If the marker isn't a
|
||||
// smi then the frame is a JavaScript frame -- and the marker is
|
||||
// really the function.
|
||||
const int offset = StandardFrameConstants::kMarkerOffset;
|
||||
Object* marker = Memory::Object_at(state->fp + offset);
|
||||
if (!marker->IsSmi()) return JAVA_SCRIPT;
|
||||
return static_cast<StackFrame::Type>(Smi::cast(marker)->value());
|
||||
}
|
||||
|
||||
|
||||
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
|
||||
if (fp == 0) return NONE;
|
||||
// Compute frame type and stack pointer.
|
||||
Address sp = fp + ExitFrameConstants::kSPDisplacement;
|
||||
Type type;
|
||||
if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
|
||||
type = EXIT_DEBUG;
|
||||
sp -= kNumJSCallerSaved * kPointerSize;
|
||||
} else {
|
||||
type = EXIT;
|
||||
}
|
||||
// Fill in the state.
|
||||
state->sp = sp;
|
||||
state->fp = fp;
|
||||
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
void ExitFrame::Iterate(ObjectVisitor* v) const {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
int JavaScriptFrame::GetProvidedParametersCount() const {
|
||||
return ComputeParametersCount();
|
||||
}
|
||||
|
||||
|
||||
Address JavaScriptFrame::GetCallerStackPointer() const {
|
||||
int arguments;
|
||||
if (Heap::gc_state() != Heap::NOT_IN_GC) {
|
||||
// The arguments for cooked frames are traversed as if they were
|
||||
// expression stack elements of the calling frame. The reason for
|
||||
// this rather strange decision is that we cannot access the
|
||||
// function during mark-compact GCs when the stack is cooked.
|
||||
// In fact accessing heap objects (like function->shared() below)
|
||||
// at all during GC is problematic.
|
||||
arguments = 0;
|
||||
} else {
|
||||
// Compute the number of arguments by getting the number of formal
|
||||
// parameters of the function. We must remember to take the
|
||||
// receiver into account (+1).
|
||||
JSFunction* function = JSFunction::cast(this->function());
|
||||
arguments = function->shared()->formal_parameter_count() + 1;
|
||||
}
|
||||
const int offset = StandardFrameConstants::kCallerSPOffset;
|
||||
return fp() + offset + (arguments * kPointerSize);
|
||||
}
|
||||
|
||||
|
||||
Address ArgumentsAdaptorFrame::GetCallerStackPointer() const {
|
||||
const int arguments = Smi::cast(GetExpression(0))->value();
|
||||
const int offset = StandardFrameConstants::kCallerSPOffset;
|
||||
return fp() + offset + (arguments + 1) * kPointerSize;
|
||||
}
|
||||
|
||||
|
||||
Address InternalFrame::GetCallerStackPointer() const {
|
||||
// Internal frames have no arguments. The stack pointer of the
|
||||
// caller is at a fixed offset from the frame pointer.
|
||||
return fp() + StandardFrameConstants::kCallerSPOffset;
|
||||
}
|
||||
|
||||
|
||||
Code* JavaScriptFrame::FindCode() const {
|
||||
JSFunction* function = JSFunction::cast(this->function());
|
||||
return function->shared()->code();
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,381 +0,0 @@
|
||||
// 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_FRAMES_ARM_H_
|
||||
#define V8_FRAMES_ARM_H_
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// The ARM ABI does not specify the usage of register r9, which may be reserved
|
||||
// as the static base or thread register on some platforms, in which case we
|
||||
// leave it alone. Adjust the value of kR9Available accordingly:
|
||||
static const int kR9Available = 1; // 1 if available to us, 0 if reserved
|
||||
|
||||
|
||||
// Register list in load/store instructions
|
||||
// Note that the bit values must match those used in actual instruction encoding
|
||||
static const int kNumRegs = 16;
|
||||
|
||||
|
||||
// Caller-saved/arguments registers
|
||||
static const RegList kJSCallerSaved =
|
||||
1 << 0 | // r0 a1
|
||||
1 << 1 | // r1 a2
|
||||
1 << 2 | // r2 a3
|
||||
1 << 3; // r3 a4
|
||||
|
||||
static const int kNumJSCallerSaved = 4;
|
||||
|
||||
typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
|
||||
|
||||
// Return the code of the n-th caller-saved register available to JavaScript
|
||||
// e.g. JSCallerSavedReg(0) returns r0.code() == 0
|
||||
int JSCallerSavedCode(int n);
|
||||
|
||||
|
||||
// Callee-saved registers preserved when switching from C to JavaScript
|
||||
static const RegList kCalleeSaved =
|
||||
1 << 4 | // r4 v1
|
||||
1 << 5 | // r5 v2
|
||||
1 << 6 | // r6 v3
|
||||
1 << 7 | // r7 v4
|
||||
1 << 8 | // r8 v5 (cp in JavaScript code)
|
||||
kR9Available
|
||||
<< 9 | // r9 v6
|
||||
1 << 10 | // r10 v7 (pp in JavaScript code)
|
||||
1 << 11; // r11 v8 (fp in JavaScript code)
|
||||
|
||||
static const int kNumCalleeSaved = 7 + kR9Available;
|
||||
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
|
||||
class StackHandlerConstants : public AllStatic {
|
||||
public:
|
||||
// TODO(1233780): Get rid of the code slot in stack handlers.
|
||||
static const int kCodeOffset = 0 * kPointerSize;
|
||||
static const int kNextOffset = 1 * kPointerSize;
|
||||
static const int kStateOffset = 2 * kPointerSize;
|
||||
static const int kPPOffset = 3 * kPointerSize;
|
||||
static const int kFPOffset = 4 * kPointerSize;
|
||||
static const int kPCOffset = 5 * kPointerSize;
|
||||
|
||||
static const int kAddressDisplacement = -1 * kPointerSize;
|
||||
static const int kSize = kPCOffset + kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
class EntryFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kCallerFPOffset = -3 * kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
class ExitFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Exit frames have a debug marker on the stack.
|
||||
static const int kSPDisplacement = -1 * kPointerSize;
|
||||
|
||||
// The debug marker is just above the frame pointer.
|
||||
static const int kDebugMarkOffset = -1 * kPointerSize;
|
||||
|
||||
static const int kSavedRegistersOffset = 0 * kPointerSize;
|
||||
|
||||
// Let the parameters pointer for exit frames point just below the
|
||||
// frame structure on the stack.
|
||||
static const int kPPDisplacement = 3 * kPointerSize;
|
||||
|
||||
// The caller fields are below the frame pointer on the stack.
|
||||
static const int kCallerFPOffset = +0 * kPointerSize;
|
||||
static const int kCallerPPOffset = +1 * kPointerSize;
|
||||
static const int kCallerPCOffset = +2 * kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
class StandardFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kExpressionsOffset = -3 * kPointerSize;
|
||||
static const int kMarkerOffset = -2 * kPointerSize;
|
||||
static const int kContextOffset = -1 * kPointerSize;
|
||||
static const int kCallerFPOffset = 0 * kPointerSize;
|
||||
static const int kCallerPCOffset = +1 * kPointerSize;
|
||||
static const int kCallerSPOffset = +2 * kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
class JavaScriptFrameConstants : public AllStatic {
|
||||
public:
|
||||
// FP-relative.
|
||||
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
|
||||
static const int kSavedRegistersOffset = +2 * kPointerSize;
|
||||
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
|
||||
|
||||
// PP-relative.
|
||||
static const int kParam0Offset = -2 * kPointerSize;
|
||||
static const int kReceiverOffset = -1 * kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
class ArgumentsAdaptorFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
|
||||
};
|
||||
|
||||
|
||||
class InternalFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
|
||||
};
|
||||
|
||||
|
||||
inline Object* JavaScriptFrame::function() const {
|
||||
const int offset = JavaScriptFrameConstants::kFunctionOffset;
|
||||
Object* result = Memory::Object_at(fp() + offset);
|
||||
ASSERT(result->IsJSFunction());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
// lower | Stack |
|
||||
// addresses | ^ |
|
||||
// | | |
|
||||
// | |
|
||||
// | JS frame |
|
||||
// | |
|
||||
// | |
|
||||
// ----------- +=============+ <--- sp (stack pointer)
|
||||
// | function |
|
||||
// +-------------+
|
||||
// +-------------+
|
||||
// | |
|
||||
// | expressions |
|
||||
// | |
|
||||
// +-------------+
|
||||
// | |
|
||||
// a | locals |
|
||||
// c | |
|
||||
// t +- - - - - - -+ <---
|
||||
// i -4 | local0 | ^
|
||||
// v +-------------+ |
|
||||
// a -3 | code | |
|
||||
// t +-------------+ | kLocal0Offset
|
||||
// i -2 | context | |
|
||||
// o +-------------+ |
|
||||
// n -1 | args_length | v
|
||||
// +-------------+ <--- fp (frame pointer)
|
||||
// 0 | caller_pp |
|
||||
// f +-------------+
|
||||
// r 1 | caller_fp |
|
||||
// a +-------------+
|
||||
// m 2 | sp_on_exit | (pp if return, caller_sp if no return)
|
||||
// e +-------------+
|
||||
// 3 | caller_pc |
|
||||
// +-------------+ <--- caller_sp (incl. parameters)
|
||||
// | |
|
||||
// | parameters |
|
||||
// | |
|
||||
// +- - - - - - -+ <---
|
||||
// -2 | parameter0 | ^
|
||||
// +-------------+ | kParam0Offset
|
||||
// -1 | receiver | v
|
||||
// ----------- +=============+ <--- pp (parameter pointer, r10)
|
||||
// 0 | function |
|
||||
// +-------------+
|
||||
// | |
|
||||
// |caller-saved | (must be valid JS values, traversed during GC)
|
||||
// | regs |
|
||||
// | |
|
||||
// +-------------+
|
||||
// | |
|
||||
// | caller |
|
||||
// higher | expressions |
|
||||
// addresses | |
|
||||
// | |
|
||||
// | JS frame |
|
||||
|
||||
|
||||
|
||||
// Handler frames (part of expressions of JS frames):
|
||||
|
||||
// lower | Stack |
|
||||
// addresses | ^ |
|
||||
// | | |
|
||||
// | |
|
||||
// h | expressions |
|
||||
// a | |
|
||||
// n +-------------+
|
||||
// d -1 | code |
|
||||
// l +-------------+ <--- handler sp
|
||||
// e 0 | next_sp | link to next handler (next handler's sp)
|
||||
// r +-------------+
|
||||
// 1 | state |
|
||||
// f +-------------+
|
||||
// r 2 | pp |
|
||||
// a +-------------+
|
||||
// m 3 | fp |
|
||||
// e +-------------+
|
||||
// 4 | pc |
|
||||
// +-------------+
|
||||
// | |
|
||||
// higher | expressions |
|
||||
// addresses | |
|
||||
|
||||
|
||||
|
||||
// JS entry frames: When calling from C to JS, we construct two extra
|
||||
// frames: An entry frame (C) and a trampoline frame (JS). The
|
||||
// following pictures shows the two frames:
|
||||
|
||||
// lower | Stack |
|
||||
// addresses | ^ |
|
||||
// | | |
|
||||
// | |
|
||||
// | JS frame |
|
||||
// | |
|
||||
// | |
|
||||
// ----------- +=============+ <--- sp (stack pointer)
|
||||
// | |
|
||||
// | parameters |
|
||||
// t | |
|
||||
// r +- - - - - - -+
|
||||
// a | parameter0 |
|
||||
// m +-------------+
|
||||
// p | receiver |
|
||||
// o +-------------+
|
||||
// l | function |
|
||||
// i +-------------+
|
||||
// n -3 | code |
|
||||
// e +-------------+
|
||||
// -2 | NULL | context is always NULL
|
||||
// +-------------+
|
||||
// f -1 | 0 | args_length is always zero
|
||||
// r +-------------+ <--- fp (frame pointer)
|
||||
// a 0 | NULL | caller pp is always NULL for entries
|
||||
// m +-------------+
|
||||
// e 1 | caller_fp |
|
||||
// +-------------+
|
||||
// 2 | sp_on_exit | (caller_sp)
|
||||
// +-------------+
|
||||
// 3 | caller_pc |
|
||||
// ----------- +=============+ <--- caller_sp == pp
|
||||
// . ^
|
||||
// . | try-handler, fake, not GC'ed
|
||||
// . v
|
||||
// +-------------+ <---
|
||||
// -2 | next top pp |
|
||||
// +-------------+
|
||||
// -1 | next top fp |
|
||||
// +-------------+ <--- fp
|
||||
// | r4 | r4-r9 holding non-JS values must be preserved
|
||||
// +-------------+
|
||||
// J | r5 | before being initialized not to confuse GC
|
||||
// S +-------------+
|
||||
// | r6 |
|
||||
// +-------------+
|
||||
// e | r7 |
|
||||
// n +-------------+
|
||||
// t | r8 |
|
||||
// r +-------------+
|
||||
// y [ | r9 | ] only if r9 available
|
||||
// +-------------+
|
||||
// | r10 |
|
||||
// f +-------------+
|
||||
// r | r11 |
|
||||
// a +-------------+
|
||||
// m | caller_sp |
|
||||
// e +-------------+
|
||||
// | caller_pc |
|
||||
// +-------------+ <--- caller_sp
|
||||
// | argv | passed on stack from C code
|
||||
// +-------------+
|
||||
// | |
|
||||
// higher | |
|
||||
// addresses | C frame |
|
||||
|
||||
|
||||
// The first 4 args are passed from C in r0-r3 and are not spilled on entry:
|
||||
// r0: code entry
|
||||
// r1: function
|
||||
// r2: receiver
|
||||
// r3: argc
|
||||
// [sp+0]: argv
|
||||
|
||||
|
||||
// C entry frames: When calling from JS to C, we construct one extra
|
||||
// frame:
|
||||
|
||||
// lower | Stack |
|
||||
// addresses | ^ |
|
||||
// | | |
|
||||
// | |
|
||||
// | C frame |
|
||||
// | |
|
||||
// | |
|
||||
// ----------- +=============+ <--- sp (stack pointer)
|
||||
// | |
|
||||
// | parameters | (first 4 args are passed in r0-r3)
|
||||
// | |
|
||||
// +-------------+ <--- fp (frame pointer)
|
||||
// f 4/5 | caller_fp |
|
||||
// r +-------------+
|
||||
// a 5/6 | sp_on_exit | (pp)
|
||||
// m +-------------+
|
||||
// e 6/7 | caller_pc |
|
||||
// +-------------+ <--- caller_sp (incl. parameters)
|
||||
// 7/8 | |
|
||||
// | parameters |
|
||||
// | |
|
||||
// +- - - - - - -+ <---
|
||||
// -2 | parameter0 | ^
|
||||
// +-------------+ | kParam0Offset
|
||||
// -1 | receiver | v
|
||||
// ----------- +=============+ <--- pp (parameter pointer, r10)
|
||||
// 0 | function |
|
||||
// +-------------+
|
||||
// | |
|
||||
// |caller-saved |
|
||||
// | regs |
|
||||
// | |
|
||||
// +-------------+
|
||||
// | |
|
||||
// | caller |
|
||||
// | expressions |
|
||||
// | |
|
||||
// higher | |
|
||||
// addresses | JS frame |
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_FRAMES_ARM_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user