summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2009-12-05 15:27:56 +0100
committerRyan Dahl <ry@tinyclouds.org>2009-12-05 15:27:56 +0100
commitc8b6ef248e5fc32df62041ec83463923bc8bdc68 (patch)
tree9012b1d0b0f9c380f6c6aa1b8b43dde44532e19e /deps
parentc5d82380f46ed0c992ff56a9e9ddfe6ab2540e62 (diff)
downloadnode-c8b6ef248e5fc32df62041ec83463923bc8bdc68.tar.gz
upgrade v8 to 2.0.3
Diffstat (limited to 'deps')
-rw-r--r--deps/v8/ChangeLog25
-rwxr-xr-xdeps/v8/SConstruct24
-rw-r--r--deps/v8/include/v8.h20
-rwxr-xr-xdeps/v8/src/SConscript4
-rw-r--r--deps/v8/src/accessors.cc70
-rw-r--r--deps/v8/src/accessors.h10
-rw-r--r--deps/v8/src/api.cc46
-rw-r--r--deps/v8/src/arm/assembler-arm.h1
-rw-r--r--deps/v8/src/arm/codegen-arm.cc131
-rw-r--r--deps/v8/src/arm/codegen-arm.h3
-rw-r--r--deps/v8/src/arm/constants-arm.h27
-rw-r--r--deps/v8/src/arm/debug-arm.cc2
-rw-r--r--deps/v8/src/arm/fast-codegen-arm.cc316
-rw-r--r--deps/v8/src/arm/ic-arm.cc13
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.cc52
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.h7
-rw-r--r--deps/v8/src/arm/stub-cache-arm.cc5
-rw-r--r--deps/v8/src/array.js36
-rw-r--r--deps/v8/src/ast.h1
-rw-r--r--deps/v8/src/bootstrapper.cc26
-rw-r--r--deps/v8/src/code-stubs.h1
-rw-r--r--deps/v8/src/codegen.cc1
-rw-r--r--deps/v8/src/compiler.cc50
-rw-r--r--deps/v8/src/d8.cc8
-rw-r--r--deps/v8/src/debug-delay.js21
-rw-r--r--deps/v8/src/debug.h11
-rw-r--r--deps/v8/src/dtoa-config.c8
-rw-r--r--deps/v8/src/factory.cc5
-rw-r--r--deps/v8/src/fast-codegen.cc37
-rw-r--r--deps/v8/src/fast-codegen.h13
-rw-r--r--deps/v8/src/flag-definitions.h8
-rw-r--r--deps/v8/src/global-handles.cc20
-rw-r--r--deps/v8/src/global-handles.h2
-rw-r--r--deps/v8/src/handles.cc48
-rw-r--r--deps/v8/src/handles.h3
-rw-r--r--deps/v8/src/heap-inl.h4
-rw-r--r--deps/v8/src/heap.cc193
-rw-r--r--deps/v8/src/heap.h95
-rw-r--r--deps/v8/src/ia32/assembler-ia32.h2
-rw-r--r--deps/v8/src/ia32/codegen-ia32.cc312
-rw-r--r--deps/v8/src/ia32/codegen-ia32.h34
-rw-r--r--deps/v8/src/ia32/debug-ia32.cc8
-rw-r--r--deps/v8/src/ia32/fast-codegen-ia32.cc319
-rw-r--r--deps/v8/src/ia32/ic-ia32.cc39
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.cc145
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.h39
-rw-r--r--deps/v8/src/ia32/stub-cache-ia32.cc10
-rw-r--r--deps/v8/src/log.cc60
-rw-r--r--deps/v8/src/log.h9
-rw-r--r--deps/v8/src/messages.js65
-rw-r--r--deps/v8/src/mirror-delay.js24
-rw-r--r--deps/v8/src/objects-debug.cc65
-rw-r--r--deps/v8/src/objects-inl.h152
-rw-r--r--deps/v8/src/objects.cc132
-rw-r--r--deps/v8/src/objects.h506
-rw-r--r--deps/v8/src/platform-freebsd.cc5
-rw-r--r--deps/v8/src/platform-linux.cc5
-rw-r--r--deps/v8/src/platform-macos.cc5
-rw-r--r--deps/v8/src/platform-openbsd.cc597
-rw-r--r--deps/v8/src/platform-posix.cc9
-rw-r--r--deps/v8/src/runtime.cc124
-rw-r--r--deps/v8/src/runtime.h11
-rw-r--r--deps/v8/src/runtime.js10
-rw-r--r--deps/v8/src/serialize.cc87
-rw-r--r--deps/v8/src/string-stream.cc2
-rw-r--r--deps/v8/src/stub-cache.cc6
-rw-r--r--deps/v8/src/stub-cache.h4
-rw-r--r--deps/v8/src/utils.cc9
-rw-r--r--deps/v8/src/utils.h3
-rw-r--r--deps/v8/src/v8-counters.h5
-rw-r--r--deps/v8/src/version.cc2
-rw-r--r--deps/v8/src/x64/assembler-x64.h6
-rw-r--r--deps/v8/src/x64/codegen-x64.cc127
-rw-r--r--deps/v8/src/x64/codegen-x64.h3
-rw-r--r--deps/v8/src/x64/debug-x64.cc7
-rw-r--r--deps/v8/src/x64/fast-codegen-x64.cc325
-rw-r--r--deps/v8/src/x64/ic-x64.cc39
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.cc34
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.h9
-rw-r--r--deps/v8/src/x64/stub-cache-x64.cc8
-rw-r--r--deps/v8/test/cctest/test-alloc.cc4
-rw-r--r--deps/v8/test/cctest/test-api.cc231
-rw-r--r--deps/v8/test/cctest/test-debug.cc8
-rw-r--r--deps/v8/test/cctest/test-heap.cc3
-rw-r--r--deps/v8/test/cctest/test-log.cc75
-rw-r--r--deps/v8/test/cctest/test-serialize.cc37
-rw-r--r--deps/v8/test/cctest/test-strings.cc117
-rw-r--r--deps/v8/test/mjsunit/arguments-read-and-assignment.js164
-rw-r--r--deps/v8/test/mjsunit/compiler/jsnatives.js33
-rw-r--r--deps/v8/test/mjsunit/compiler/objectliterals.js57
-rw-r--r--deps/v8/test/mjsunit/regress/regress-526.js32
-rw-r--r--deps/v8/test/mjsunit/regress/regress-r3391.js77
-rw-r--r--deps/v8/test/mjsunit/string-add.js20
-rw-r--r--deps/v8/test/mjsunit/typeof.js40
-rw-r--r--deps/v8/tools/codemap.js2
-rw-r--r--deps/v8/tools/gyp/v8.gyp11
-rwxr-xr-xdeps/v8/tools/presubmit.py71
-rw-r--r--deps/v8/tools/utils.py2
-rw-r--r--deps/v8/tools/v8.xcodeproj/project.pbxproj16
99 files changed, 4250 insertions, 1460 deletions
diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog
index bf2c244b4..825431cde 100644
--- a/deps/v8/ChangeLog
+++ b/deps/v8/ChangeLog
@@ -1,3 +1,26 @@
+2009-12-03: Version 2.0.3
+
+ Optimized handling and adding of strings, for-in and Array.join.
+
+ Heap serialization is now non-destructive.
+
+ Improved profiler support with information on time spend in C++
+ callbacks registered through the API.
+
+ Added commands to the debugger protocol for starting/stopping
+ profiling.
+
+ Enabled the non-optimizing compiler for top-level code.
+
+ Changed the API to only allow strings to be set as data objects on
+ Contexts and scripts to avoid potentially keeping global objects
+ around for too long (issue 528).
+
+ OpenBSD support patch by Peter Valchev <pvalchev@gmail.com>.
+
+ Fixed bugs.
+
+
2009-11-24: Version 2.0.2
Improved profiler support.
@@ -34,7 +57,7 @@
Reverted a change which caused crashes in RegExp replace.
- Reverted a change which caused Chromium ui_tests failure.
+ Reverted a change which caused Chromium ui_tests failure.
2009-10-28: Version 1.3.17
diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct
index 4da9b5b4f..edaa66b75 100755
--- a/deps/v8/SConstruct
+++ b/deps/v8/SConstruct
@@ -149,6 +149,11 @@ LIBRARY_FLAGS = {
'LIBPATH' : ['/usr/local/lib'],
'CCFLAGS': ['-ansi'],
},
+ 'os:openbsd': {
+ 'CPPPATH' : ['/usr/local/include'],
+ 'LIBPATH' : ['/usr/local/lib'],
+ 'CCFLAGS': ['-ansi'],
+ },
'os:win32': {
'CCFLAGS': ['-DWIN32'],
'CXXFLAGS': ['-DWIN32'],
@@ -298,6 +303,9 @@ MKSNAPSHOT_EXTRA_FLAGS = {
'os:freebsd': {
'LIBS': ['execinfo', 'pthread']
},
+ 'os:openbsd': {
+ 'LIBS': ['execinfo', 'pthread']
+ },
'os:win32': {
'LIBS': ['winmm', 'ws2_32'],
},
@@ -344,6 +352,9 @@ CCTEST_EXTRA_FLAGS = {
'os:freebsd': {
'LIBS': ['execinfo', 'pthread']
},
+ 'os:openbsd': {
+ 'LIBS': ['execinfo', 'pthread']
+ },
'os:win32': {
'LIBS': ['winmm', 'ws2_32']
},
@@ -397,7 +408,11 @@ SAMPLE_FLAGS = {
},
'os:freebsd': {
'LIBPATH' : ['/usr/local/lib'],
- 'LIBS': ['execinfo', 'pthread']
+ 'LIBS': ['execinfo', 'pthread']
+ },
+ 'os:openbsd': {
+ 'LIBPATH' : ['/usr/local/lib'],
+ 'LIBS': ['execinfo', 'pthread']
},
'os:win32': {
'LIBS': ['winmm', 'ws2_32']
@@ -504,6 +519,9 @@ D8_FLAGS = {
'os:freebsd': {
'LIBS': ['pthread'],
},
+ 'os:openbsd': {
+ 'LIBS': ['pthread'],
+ },
'os:android': {
'LIBPATH': [ANDROID_TOP + '/out/target/product/generic/obj/lib'],
'LINKFLAGS': ANDROID_LINKFLAGS,
@@ -544,7 +562,7 @@ def GuessToolchain(os):
OS_GUESS = utils.GuessOS()
TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS)
-ARCH_GUESS = utils.GuessArchitecture() or ""
+ARCH_GUESS = utils.GuessArchitecture()
SIMPLE_OPTIONS = {
@@ -554,7 +572,7 @@ SIMPLE_OPTIONS = {
'help': 'the toolchain to use (' + TOOLCHAIN_GUESS + ')'
},
'os': {
- 'values': ['freebsd', 'linux', 'macos', 'win32', 'android'],
+ 'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd'],
'default': OS_GUESS,
'help': 'the os to build for (' + OS_GUESS + ')'
},
diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h
index 78b46136f..a8ee8d432 100644
--- a/deps/v8/include/v8.h
+++ b/deps/v8/include/v8.h
@@ -598,7 +598,7 @@ class V8EXPORT Script {
* with the debugger as this data object is only available through the
* debugger API.
*/
- void SetData(Handle<Value> data);
+ void SetData(Handle<String> data);
};
@@ -2634,7 +2634,7 @@ class V8EXPORT Context {
* with the debugger to provide additional information on the context through
* the debugger API.
*/
- void SetData(Handle<Value> data);
+ void SetData(Handle<String> data);
Local<Value> GetData();
/**
@@ -2819,6 +2819,18 @@ template <> struct SmiConstants<8> {
const int kSmiShiftSize = SmiConstants<sizeof(void*)>::kSmiShiftSize;
const int kSmiValueSize = SmiConstants<sizeof(void*)>::kSmiValueSize;
+template <size_t ptr_size> struct InternalConstants;
+
+// Internal constants for 32-bit systems.
+template <> struct InternalConstants<4> {
+ static const int kStringResourceOffset = 3 * sizeof(void*);
+};
+
+// Internal constants for 64-bit systems.
+template <> struct InternalConstants<8> {
+ static const int kStringResourceOffset = 2 * sizeof(void*);
+};
+
/**
* This class exports constants and functionality from within v8 that
* is necessary to implement inline functions in the v8 api. Don't
@@ -2831,7 +2843,9 @@ class Internals {
// the implementation of v8.
static const int kHeapObjectMapOffset = 0;
static const int kMapInstanceTypeOffset = sizeof(void*) + sizeof(int);
- static const int kStringResourceOffset = 2 * sizeof(void*);
+ static const int kStringResourceOffset =
+ InternalConstants<sizeof(void*)>::kStringResourceOffset;
+
static const int kProxyProxyOffset = sizeof(void*);
static const int kJSObjectHeaderSize = 3 * sizeof(void*);
static const int kFullStringRepresentationMask = 0x07;
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript
index cfa462f50..3b0df1718 100755
--- a/deps/v8/src/SConscript
+++ b/deps/v8/src/SConscript
@@ -159,6 +159,7 @@ SOURCES = {
"""),
'simulator:arm': ['arm/simulator-arm.cc'],
'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'],
+ 'os:openbsd': ['platform-openbsd.cc', 'platform-posix.cc'],
'os:linux': ['platform-linux.cc', 'platform-posix.cc'],
'os:android': ['platform-linux.cc', 'platform-posix.cc'],
'os:macos': ['platform-macos.cc', 'platform-posix.cc'],
@@ -187,6 +188,9 @@ D8_FILES = {
'os:freebsd': [
'd8-posix.cc'
],
+ 'os:openbsd': [
+ 'd8-posix.cc'
+ ],
'os:win32': [
'd8-windows.cc'
],
diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc
index 734c36445..56cf13598 100644
--- a/deps/v8/src/accessors.cc
+++ b/deps/v8/src/accessors.cc
@@ -315,14 +315,11 @@ Object* Accessors::ScriptGetLineEnds(Object* object, void*) {
HandleScope scope;
Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
InitScriptLineEnds(script);
- if (script->line_ends_js_array()->IsUndefined()) {
- Handle<FixedArray> line_ends_fixed_array(
- FixedArray::cast(script->line_ends_fixed_array()));
- Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends_fixed_array);
- Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy);
- script->set_line_ends_js_array(*js_array);
- }
- return script->line_ends_js_array();
+ ASSERT(script->line_ends()->IsFixedArray());
+ Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
+ Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends);
+ Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy);
+ return *js_array;
}
@@ -352,29 +349,38 @@ const AccessorDescriptor Accessors::ScriptContextData = {
//
-// Accessors::ScriptGetEvalFromFunction
+// Accessors::ScriptGetEvalFromScript
//
-Object* Accessors::ScriptGetEvalFromFunction(Object* object, void*) {
+Object* Accessors::ScriptGetEvalFromScript(Object* object, void*) {
Object* script = JSValue::cast(object)->value();
- return Script::cast(script)->eval_from_function();
+ if (!Script::cast(script)->eval_from_shared()->IsUndefined()) {
+ Handle<SharedFunctionInfo> eval_from_shared(
+ SharedFunctionInfo::cast(Script::cast(script)->eval_from_shared()));
+
+ if (eval_from_shared->script()->IsScript()) {
+ Handle<Script> eval_from_script(Script::cast(eval_from_shared->script()));
+ return *GetScriptWrapper(eval_from_script);
+ }
+ }
+ return Heap::undefined_value();
}
-const AccessorDescriptor Accessors::ScriptEvalFromFunction = {
- ScriptGetEvalFromFunction,
+const AccessorDescriptor Accessors::ScriptEvalFromScript = {
+ ScriptGetEvalFromScript,
IllegalSetter,
0
};
//
-// Accessors::ScriptGetEvalFromPosition
+// Accessors::ScriptGetEvalFromScriptPosition
//
-Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) {
+Object* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) {
HandleScope scope;
Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
@@ -386,14 +392,42 @@ Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) {
// Get the function from where eval was called and find the source position
// from the instruction offset.
- Handle<Code> code(JSFunction::cast(script->eval_from_function())->code());
+ Handle<Code> code(SharedFunctionInfo::cast(
+ script->eval_from_shared())->code());
return Smi::FromInt(code->SourcePosition(code->instruction_start() +
script->eval_from_instructions_offset()->value()));
}
-const AccessorDescriptor Accessors::ScriptEvalFromPosition = {
- ScriptGetEvalFromPosition,
+const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = {
+ ScriptGetEvalFromScriptPosition,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetEvalFromFunctionName
+//
+
+
+Object* Accessors::ScriptGetEvalFromFunctionName(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(
+ Script::cast(script)->eval_from_shared()));
+
+
+ // Find the name of the function calling eval.
+ if (!shared->name()->IsUndefined()) {
+ return shared->name();
+ } else {
+ return shared->inferred_name();
+ }
+}
+
+
+const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = {
+ ScriptGetEvalFromFunctionName,
IllegalSetter,
0
};
diff --git a/deps/v8/src/accessors.h b/deps/v8/src/accessors.h
index 51d322ec8..7a840a191 100644
--- a/deps/v8/src/accessors.h
+++ b/deps/v8/src/accessors.h
@@ -51,8 +51,9 @@ namespace internal {
V(ScriptCompilationType) \
V(ScriptLineEnds) \
V(ScriptContextData) \
- V(ScriptEvalFromFunction) \
- V(ScriptEvalFromPosition) \
+ V(ScriptEvalFromScript) \
+ V(ScriptEvalFromScriptPosition) \
+ V(ScriptEvalFromFunctionName) \
V(ObjectPrototype)
// Accessors contains all predefined proxy accessors.
@@ -95,8 +96,9 @@ class Accessors : public AllStatic {
static Object* ScriptGetCompilationType(Object* object, void*);
static Object* ScriptGetLineEnds(Object* object, void*);
static Object* ScriptGetContextData(Object* object, void*);
- static Object* ScriptGetEvalFromFunction(Object* object, void*);
- static Object* ScriptGetEvalFromPosition(Object* object, void*);
+ static Object* ScriptGetEvalFromScript(Object* object, void*);
+ static Object* ScriptGetEvalFromScriptPosition(Object* object, void*);
+ static Object* ScriptGetEvalFromFunctionName(Object* object, void*);
static Object* ObjectGetPrototype(Object* receiver, void*);
static Object* ObjectSetPrototype(JSObject* receiver, Object* value, void*);
diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 220788ba5..93807a7c7 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -126,6 +126,48 @@ static FatalErrorCallback& GetFatalErrorHandler() {
// When V8 cannot allocated memory FatalProcessOutOfMemory is called.
// The default fatal error handler is called and execution is stopped.
void i::V8::FatalProcessOutOfMemory(const char* location) {
+ i::HeapStats heap_stats;
+ int start_marker;
+ heap_stats.start_marker = &start_marker;
+ int new_space_size;
+ heap_stats.new_space_size = &new_space_size;
+ int new_space_capacity;
+ heap_stats.new_space_capacity = &new_space_capacity;
+ int old_pointer_space_size;
+ heap_stats.old_pointer_space_size = &old_pointer_space_size;
+ int old_pointer_space_capacity;
+ heap_stats.old_pointer_space_capacity = &old_pointer_space_capacity;
+ int old_data_space_size;
+ heap_stats.old_data_space_size = &old_data_space_size;
+ int old_data_space_capacity;
+ heap_stats.old_data_space_capacity = &old_data_space_capacity;
+ int code_space_size;
+ heap_stats.code_space_size = &code_space_size;
+ int code_space_capacity;
+ heap_stats.code_space_capacity = &code_space_capacity;
+ int map_space_size;
+ heap_stats.map_space_size = &map_space_size;
+ int map_space_capacity;
+ heap_stats.map_space_capacity = &map_space_capacity;
+ int cell_space_size;
+ heap_stats.cell_space_size = &cell_space_size;
+ int cell_space_capacity;
+ heap_stats.cell_space_capacity = &cell_space_capacity;
+ int lo_space_size;
+ heap_stats.lo_space_size = &lo_space_size;
+ int global_handle_count;
+ heap_stats.global_handle_count = &global_handle_count;
+ int weak_global_handle_count;
+ heap_stats.weak_global_handle_count = &weak_global_handle_count;
+ int pending_global_handle_count;
+ heap_stats.pending_global_handle_count = &pending_global_handle_count;
+ int near_death_global_handle_count;
+ heap_stats.near_death_global_handle_count = &near_death_global_handle_count;
+ int destroyed_global_handle_count;
+ heap_stats.destroyed_global_handle_count = &destroyed_global_handle_count;
+ int end_marker;
+ heap_stats.end_marker = &end_marker;
+ i::Heap::RecordStats(&heap_stats);
i::V8::SetFatalError();
FatalErrorCallback callback = GetFatalErrorHandler();
{
@@ -451,7 +493,7 @@ void Context::Exit() {
}
-void Context::SetData(v8::Handle<Value> data) {
+void Context::SetData(v8::Handle<String> data) {
if (IsDeadCheck("v8::Context::SetData()")) return;
ENTER_V8;
{
@@ -1175,7 +1217,7 @@ Local<Value> Script::Id() {
}
-void Script::SetData(v8::Handle<Value> data) {
+void Script::SetData(v8::Handle<String> data) {
ON_BAILOUT("v8::Script::SetData()", return);
LOG_API("Script::SetData");
{
diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h
index ca0184e35..86bc18a24 100644
--- a/deps/v8/src/arm/assembler-arm.h
+++ b/deps/v8/src/arm/assembler-arm.h
@@ -566,6 +566,7 @@ class Assembler : public Malloced {
// register.
static const int kPcLoadDelta = 8;
+ static const int kJSReturnSequenceLength = 4;
// ---------------------------------------------------------------------------
// Code generation
diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc
index c62756d5a..ea3df6cfb 100644
--- a/deps/v8/src/arm/codegen-arm.cc
+++ b/deps/v8/src/arm/codegen-arm.cc
@@ -326,7 +326,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// Calculate the exact length of the return sequence and make sure that
// the constant pool is not emitted inside of the return sequence.
int32_t sp_delta = (scope_->num_parameters() + 1) * kPointerSize;
- int return_sequence_length = Debug::kARMJSReturnSequenceLength;
+ int return_sequence_length = Assembler::kJSReturnSequenceLength;
if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) {
// Additional mov instruction generated.
return_sequence_length++;
@@ -1560,7 +1560,7 @@ void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
CheckStack(); // TODO(1222600): ignore if body contains calls.
VisitAndSpill(node->body());
- // Compile the test.
+ // Compile the test.
switch (info) {
case ALWAYS_TRUE:
// If control can fall off the end of the body, jump back to the
@@ -1775,19 +1775,77 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
jsobject.Bind();
// Get the set of properties (as a FixedArray or Map).
- frame_->EmitPush(r0); // duplicate the object being enumerated
- frame_->EmitPush(r0);
+ // r0: value to be iterated over
+ frame_->EmitPush(r0); // Push the object being iterated over.
+
+ // Check cache validity in generated code. This is a fast case for
+ // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+ // guarantee cache validity, call the runtime system to check cache
+ // validity or get the property names in a fixed array.
+ JumpTarget call_runtime;
+ JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ JumpTarget check_prototype;
+ JumpTarget use_cache;
+ __ mov(r1, Operand(r0));
+ loop.Bind();
+ // Check that there are no elements.
+ __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
+ __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
+ __ cmp(r2, r4);
+ call_runtime.Branch(ne);
+ // Check that instance descriptors are not empty so that we can
+ // check for an enum cache. Leave the map in r3 for the subsequent
+ // prototype load.
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
+ __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
+ __ cmp(r2, ip);
+ call_runtime.Branch(eq);
+ // Check that there in an enum cache in the non-empty instance
+ // descriptors. This is the case if the next enumeration index
+ // field does not contain a smi.
+ __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
+ __ tst(r2, Operand(kSmiTagMask));
+ call_runtime.Branch(eq);
+ // For all objects but the receiver, check that the cache is empty.
+ // r4: empty fixed array root.
+ __ cmp(r1, r0);
+ check_prototype.Branch(eq);
+ __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
+ __ cmp(r2, r4);
+ call_runtime.Branch(ne);
+ check_prototype.Bind();
+ // Load the prototype from the map and loop if non-null.
+ __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
+ __ LoadRoot(ip, Heap::kNullValueRootIndex);
+ __ cmp(r1, ip);
+ loop.Branch(ne);
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+ use_cache.Jump();
+
+ call_runtime.Bind();
+ // Call the runtime to get the property names for the object.
+ frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
- // If we got a Map, we can do a fast modification check.
- // Otherwise, we got a FixedArray, and we have to do a slow check.
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
+ // r0: map or fixed array (result from call to
+ // Runtime::kGetPropertyNamesFast)
__ mov(r2, Operand(r0));
__ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kMetaMapRootIndex);
__ cmp(r1, ip);
fixed_array.Branch(ne);
+ use_cache.Bind();
// Get enum cache
+ // r0: map (either the result from a call to
+ // Runtime::kGetPropertyNamesFast or has been fetched directly from
+ // the object)
__ mov(r1, Operand(r0));
__ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
__ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
@@ -3308,9 +3366,6 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
// Now r2 has the string type.
__ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
- __ and_(r4, r2, Operand(kStringSizeMask));
- __ add(r4, r4, Operand(String::kLongLengthShift));
- __ mov(r3, Operand(r3, LSR, r4));
// Now r3 has the length of the string. Compare with the index.
__ cmp(r3, Operand(r0, LSR, kSmiTagSize));
__ b(le, &slow);
@@ -3508,6 +3563,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
+ ASSERT_EQ(2, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+
+ frame_->CallRuntime(Runtime::kStringAdd, 2);
+ frame_->EmitPush(r0);
+}
+
+
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);
@@ -5149,8 +5215,53 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
// We jump to here if something goes wrong (one param is not a number of any
// sort or new-space allocation fails).
__ bind(&slow);
+
+ // Push arguments to the stack
__ push(r1);
__ push(r0);
+
+ if (Token::ADD == operation) {
+ // Test for string arguments before calling runtime.
+ // r1 : first argument
+ // r0 : second argument
+ // sp[0] : second argument
+ // sp[1] : first argument
+
+ Label not_strings, not_string1, string1;
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &not_string1);
+ __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &not_string1);
+
+ // First argument is a a string, test second.
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &string1);
+ __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &string1);
+
+ // First and second argument are strings.
+ __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+
+ // Only first argument is a string.
+ __ bind(&string1);
+ __ mov(r0, Operand(2)); // Set number of arguments.
+ __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
+
+ // First argument was not a string, test second.
+ __ bind(&not_string1);
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &not_strings);
+ __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &not_strings);
+
+ // Only second argument is a string.
+ __ b(&not_strings);
+ __ mov(r0, Operand(2)); // Set number of arguments.
+ __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
+
+ __ bind(&not_strings);
+ }
+
__ mov(r0, Operand(1)); // Set number of arguments.
__ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
@@ -6388,7 +6499,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
__ b(eq, &adaptor);
// Check index against formal parameters count limit passed in
- // through register eax. Use unsigned comparison to get negative
+ // through register r0. Use unsigned comparison to get negative
// check for free.
__ cmp(r1, r0);
__ b(cs, &slow);
diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h
index 30a1ae572..ba7f93626 100644
--- a/deps/v8/src/arm/codegen-arm.h
+++ b/deps/v8/src/arm/codegen-arm.h
@@ -366,6 +366,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
+ // Fast support for StringAdd.
+ void GenerateStringAdd(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h
index 58396f877..943220739 100644
--- a/deps/v8/src/arm/constants-arm.h
+++ b/deps/v8/src/arm/constants-arm.h
@@ -43,24 +43,27 @@
# define USE_THUMB_INTERWORK 1
#endif
-#if defined(__ARM_ARCH_5T__) || \
- defined(__ARM_ARCH_5TE__) || \
- defined(__ARM_ARCH_6__) || \
- defined(__ARM_ARCH_7A__) || \
+#if defined(__ARM_ARCH_7A__) || \
+ defined(__ARM_ARCH_7R__) || \
defined(__ARM_ARCH_7__)
-# define CAN_USE_ARMV5_INSTRUCTIONS 1
-# define CAN_USE_THUMB_INSTRUCTIONS 1
+# define CAN_USE_ARMV7_INSTRUCTIONS 1
#endif
-#if defined(__ARM_ARCH_6__) || \
- defined(__ARM_ARCH_7A__) || \
- defined(__ARM_ARCH_7__)
+#if defined(__ARM_ARCH_6__) || \
+ defined(__ARM_ARCH_6J__) || \
+ defined(__ARM_ARCH_6K__) || \
+ defined(__ARM_ARCH_6Z__) || \
+ defined(__ARM_ARCH_6ZK__) || \
+ defined(__ARM_ARCH_6T2__) || \
+ defined(CAN_USE_ARMV7_INSTRUCTIONS)
# define CAN_USE_ARMV6_INSTRUCTIONS 1
#endif
-#if defined(__ARM_ARCH_7A__) || \
- defined(__ARM_ARCH_7__)
-# define CAN_USE_ARMV7_INSTRUCTIONS 1
+#if defined(__ARM_ARCH_5T__) || \
+ defined(__ARM_ARCH_5TE__) || \
+ defined(CAN_USE_ARMV6_INSTRUCTIONS)
+# define CAN_USE_ARMV5_INSTRUCTIONS 1
+# define CAN_USE_THUMB_INSTRUCTIONS 1
#endif
// Simulator should support ARM5 instructions.
diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc
index 102952da7..fc9808d52 100644
--- a/deps/v8/src/arm/debug-arm.cc
+++ b/deps/v8/src/arm/debug-arm.cc
@@ -61,7 +61,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
// Restore the JS frame exit code.
void BreakLocationIterator::ClearDebugBreakAtReturn() {
rinfo()->PatchCode(original_rinfo()->pc(),
- Debug::kARMJSReturnSequenceLength);
+ Assembler::kJSReturnSequenceLength);
}
diff --git a/deps/v8/src/arm/fast-codegen-arm.cc b/deps/v8/src/arm/fast-codegen-arm.cc
index 6d0510e32..ab636b6b8 100644
--- a/deps/v8/src/arm/fast-codegen-arm.cc
+++ b/deps/v8/src/arm/fast-codegen-arm.cc
@@ -73,16 +73,46 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
bool function_in_register = true;
+ // Possibly allocate a local context.
+ if (fun->scope()->num_heap_slots() > 0) {
+ Comment cmnt(masm_, "[ Allocate local context");
+ // Argument to NewContext is the function, which is in r1.
+ __ push(r1);
+ __ CallRuntime(Runtime::kNewContext, 1);
+ function_in_register = false;
+ // Context is returned in both r0 and cp. It replaces the context
+ // passed to us. It's saved in the stack and kept live in cp.
+ __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // Copy any necessary parameters into the context.
+ int num_parameters = fun->scope()->num_parameters();
+ for (int i = 0; i < num_parameters; i++) {
+ Slot* slot = fun->scope()->parameter(i)->slot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
+ (num_parameters - 1 - i) * kPointerSize;
+ // Load parameter from stack.
+ __ ldr(r0, MemOperand(fp, parameter_offset));
+ // Store it in the context
+ __ str(r0, MemOperand(cp, Context::SlotOffset(slot->index())));
+ }
+ }
+ }
+
Variable* arguments = fun->scope()->arguments()->AsVariable();
if (arguments != NULL) {
// Function uses arguments object.
Comment cmnt(masm_, "[ Allocate arguments object");
- __ mov(r3, r1);
+ if (!function_in_register) {
+ // Load this again, if it's used by the local context below.
+ __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ } else {
+ __ mov(r3, r1);
+ }
// Receiver is just before the parameters on the caller's stack.
__ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset +
fun->num_parameters() * kPointerSize));
__ mov(r1, Operand(Smi::FromInt(fun->num_parameters())));
- __ stm(db_w, sp, r1.bit() | r2.bit() | r3.bit());
+ __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit());
// Arguments to ArgumentsAccessStub:
// function, receiver address, parameter count.
@@ -90,33 +120,12 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
// stack frame was an arguments adapter frame.
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
__ CallStub(&stub);
- __ str(r0, MemOperand(fp, SlotOffset(arguments->slot())));
+ // Duplicate the value; move-to-slot operation might clobber registers.
+ __ mov(r3, r0);
+ Move(arguments->slot(), r0, r1, r2);
Slot* dot_arguments_slot =
fun->scope()->arguments_shadow()->AsVariable()->slot();
- __ str(r0, MemOperand(fp, SlotOffset(dot_arguments_slot)));
- function_in_register = false;
- }
-
- // Possibly allocate a local context.
- if (fun->scope()->num_heap_slots() > 0) {
- Comment cmnt(masm_, "[ Allocate local context");
- if (!function_in_register) {
- // Load this again, if it's used by the local context below.
- __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
- }
- // Argument to NewContext is the function, which is in r1.
- __ push(r1);
- __ CallRuntime(Runtime::kNewContext, 1);
- // Context is returned in both r0 and cp. It replaces the context
- // passed to us. It's saved in the stack and kept live in cp.
- __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
-#ifdef DEBUG
- // Assert we do not have to copy any parameters into the context.
- for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) {
- Slot* slot = fun->scope()->parameter(i)->slot();
- ASSERT(slot != NULL && slot->type() != Slot::CONTEXT);
- }
-#endif
+ Move(dot_arguments_slot, r3, r1, r2);
}
// Check the stack for overflow or break request.
@@ -179,7 +188,7 @@ void FastCodeGenerator::EmitReturnSequence(int position) {
// the constant pool is not emitted inside of the return sequence.
int num_parameters = function_->scope()->num_parameters();
int32_t sp_delta = (num_parameters + 1) * kPointerSize;
- int return_sequence_length = Debug::kARMJSReturnSequenceLength;
+ int return_sequence_length = Assembler::kJSReturnSequenceLength;
if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) {
// Additional mov instruction generated.
return_sequence_length++;
@@ -238,7 +247,42 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
-void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
+template <>
+MemOperand FastCodeGenerator::CreateSlotOperand<MemOperand>(
+ Slot* source,
+ Register scratch) {
+ switch (source->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ return MemOperand(fp, SlotOffset(source));
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ function_->scope()->ContextChainLength(source->var()->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return CodeGenerator::ContextOperand(scratch, source->index());
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ // Fall-through.
+ default:
+ UNREACHABLE();
+ return MemOperand(r0, 0); // Dead code to make the compiler happy.
+ }
+}
+
+
+void FastCodeGenerator::Move(Register dst, Slot* source) {
+ // Use dst as scratch.
+ MemOperand location = CreateSlotOperand<MemOperand>(source, dst);
+ __ ldr(dst, location);
+}
+
+
+
+void FastCodeGenerator::Move(Expression::Context context,
+ Slot* source,
+ Register scratch) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
@@ -248,8 +292,8 @@ void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
case Expression::kTest: // Fall through.
case Expression::kValueTest: // Fall through.
case Expression::kTestValue:
- __ ldr(ip, MemOperand(fp, SlotOffset(source)));
- Move(context, ip);
+ Move(scratch, source);
+ Move(context, scratch);
break;
}
}
@@ -272,24 +316,60 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
}
+void FastCodeGenerator::Move(Slot* dst,
+ Register src,
+ Register scratch1,
+ Register scratch2) {
+ switch (dst->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ __ str(src, MemOperand(fp, SlotOffset(dst)));
+ break;
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ function_->scope()->ContextChainLength(dst->var()->scope());
+ __ LoadContext(scratch1, context_chain_length);
+ int index = Context::SlotOffset(dst->index());
+ __ mov(scratch2, Operand(index));
+ __ str(src, MemOperand(scratch1, index));
+ __ RecordWrite(scratch1, scratch2, src);
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+
void FastCodeGenerator::DropAndMove(Expression::Context context,
- Register source) {
+ Register source,
+ int drop_count) {
+ ASSERT(drop_count > 0);
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
- __ pop();
+ __ add(sp, sp, Operand(drop_count * kPointerSize));
break;
case Expression::kValue:
+ if (drop_count > 1) {
+ __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+ }
__ str(source, MemOperand(sp));
break;
case Expression::kTest:
ASSERT(!source.is(sp));
- __ pop();
+ __ add(sp, sp, Operand(drop_count * kPointerSize));
TestAndBranch(source, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
+ if (drop_count > 1) {
+ __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+ }
__ str(source, MemOperand(sp));
TestAndBranch(source, true_label_, &discard);
__ bind(&discard);
@@ -299,6 +379,9 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
}
case Expression::kTestValue: {
Label discard;
+ if (drop_count > 1) {
+ __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+ }
__ str(source, MemOperand(sp));
TestAndBranch(source, &discard, false_label_);
__ bind(&discard);
@@ -376,26 +459,26 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
__ mov(r0, Operand(Factory::the_hole_value()));
if (FLAG_debug_code) {
// Check if we have the correct context pointer.
- __ ldr(r1, CodeGenerator::ContextOperand(
- cp, Context::FCONTEXT_INDEX));
+ __ ldr(r1, CodeGenerator::ContextOperand(cp,
+ Context::FCONTEXT_INDEX));
__ cmp(r1, cp);
__ Check(eq, "Unexpected declaration in current context.");
}
__ str(r0, CodeGenerator::ContextOperand(cp, slot->index()));
// No write barrier since the_hole_value is in old space.
- ASSERT(Heap::InNewSpace(*Factory::the_hole_value()));
+ ASSERT(!Heap::InNewSpace(*Factory::the_hole_value()));
} else if (decl->fun() != NULL) {
Visit(decl->fun());
__ pop(r0);
if (FLAG_debug_code) {
// Check if we have the correct context pointer.
- __ ldr(r1, CodeGenerator::ContextOperand(
- cp, Context::FCONTEXT_INDEX));
+ __ ldr(r1, CodeGenerator::ContextOperand(cp,
+ Context::FCONTEXT_INDEX));
__ cmp(r1, cp);
__ Check(eq, "Unexpected declaration in current context.");
}
__ str(r0, CodeGenerator::ContextOperand(cp, slot->index()));
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+ int offset = Context::SlotOffset(slot->index());
__ mov(r2, Operand(offset));
// We know that we have written a function, which is not a smi.
__ RecordWrite(cp, r2, r0);
@@ -467,53 +550,60 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
DropAndMove(expr->context(), r0);
} else if (rewrite->AsSlot() != NULL) {
Slot* slot = rewrite->AsSlot();
- ASSERT_NE(NULL, slot);
- switch (slot->type()) {
- case Slot::LOCAL:
- case Slot::PARAMETER: {
- Comment cmnt(masm_, "Stack slot");
- Move(expr->context(), rewrite->AsSlot());
- break;
- }
-
- case Slot::CONTEXT: {
- Comment cmnt(masm_, "Context slot");
- int chain_length =
- function_->scope()->ContextChainLength(slot->var()->scope());
- if (chain_length > 0) {
- // Move up the chain of contexts to the context containing the slot.
- __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX));
- // Load the function context (which is the incoming, outer context).
- __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
- for (int i = 1; i < chain_length; i++) {
- __ ldr(r0,
- CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX));
- // Load the function context (which is the incoming, outer context).
- __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
- }
- // The context may be an intermediate context, not a function context.
- __ ldr(r0,
- CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX));
- } else { // Slot is in the current context.
- __ ldr(r0,
- CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX));
+ if (FLAG_debug_code) {
+ switch (slot->type()) {
+ case Slot::LOCAL:
+ case Slot::PARAMETER: {
+ Comment cmnt(masm_, "Stack slot");
+ break;
}
- __ ldr(r0, CodeGenerator::ContextOperand(r0, slot->index()));
- Move(expr->context(), r0);
- break;
+ case Slot::CONTEXT: {
+ Comment cmnt(masm_, "Context slot");
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ break;
+ default:
+ UNREACHABLE();
}
-
- case Slot::LOOKUP:
- UNREACHABLE();
- break;
}
+ Move(expr->context(), slot, r0);
} else {
- // The parameter variable has been rewritten into an explict access to
- // the arguments object.
+ // A variable has been rewritten into an explicit access to
+ // an object property.
Property* property = rewrite->AsProperty();
ASSERT_NOT_NULL(property);
- ASSERT_EQ(expr->context(), property->context());
- Visit(property);
+
+ // Currently the only parameter expressions that can occur are
+ // on the form "slot[literal]".
+
+ // Check that the object is in a slot.
+ Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object_var);
+ Slot* object_slot = object_var->slot();
+ ASSERT_NOT_NULL(object_slot);
+
+ // Load the object.
+ Move(r2, object_slot);
+
+ // Check that the key is a smi.
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ ASSERT(key_literal->handle()->IsSmi());
+
+ // Load the key.
+ __ mov(r1, Operand(key_literal->handle()));
+
+ // Push both as arguments to ic.
+ __ stm(db_w, sp, r2.bit() | r1.bit());
+
+ // Do a KEYED property load.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+
+ // Drop key and object left on the stack by IC, and push the result.
+ DropAndMove(expr->context(), r0, 2);
}
}
@@ -575,8 +665,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1);
}
- // If result_saved == true: the result is saved on top of the stack.
- // If result_saved == false: the result is in r0.
+ // If result_saved == true: The result is saved on top of the
+ // stack and in r0.
+ // If result_saved == false: The result not on the stack, just in r0.
bool result_saved = false;
for (int i = 0; i < expr->properties()->length(); i++) {
@@ -604,6 +695,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// StoreIC leaves the receiver on the stack.
+ __ ldr(r0, MemOperand(sp)); // Restore result into r0.
break;
}
// Fall through.
@@ -615,7 +707,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Visit(value);
ASSERT_EQ(Expression::kValue, value->context());
__ CallRuntime(Runtime::kSetProperty, 3);
- __ ldr(r0, MemOperand(sp)); // Restore result into r0
+ __ ldr(r0, MemOperand(sp)); // Restore result into r0.
break;
case ObjectLiteral::Property::GETTER: // Fall through.
@@ -785,7 +877,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
// Overwrite the global object on the stack with the result if needed.
DropAndMove(expr->context(), r0);
- } else {
+ } else if (var->slot()) {
Slot* slot = var->slot();
ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled.
switch (slot->type()) {
@@ -884,6 +976,35 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
UNREACHABLE();
break;
}
+ } else {
+ Property* property = var->rewrite()->AsProperty();
+ ASSERT_NOT_NULL(property);
+
+ // Load object and key onto the stack.
+ Slot* object_slot = property->obj()->AsSlot();
+ ASSERT_NOT_NULL(object_slot);
+ Move(Expression::kValue, object_slot, r0);
+
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ Move(Expression::kValue, key_literal);
+
+ // Value to store was pushed before object and key on the stack.
+ __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
+
+ // Arguments to ic is value in r0, object and key on stack.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+
+ if (expr->context() == Expression::kEffect) {
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ } else if (expr->context() == Expression::kValue) {
+ // Value is still on the stack in esp[2 * kPointerSize]
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ } else {
+ __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
+ DropAndMove(expr->context(), r0, 3);
+ }
}
}
@@ -1134,9 +1255,14 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) {
void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Comment cmnt(masm_, "[ CallRuntime");
ZoneList<Expression*>* args = expr->arguments();
- Runtime::Function* function = expr->function();
- ASSERT(function != NULL);
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ mov(r1, Operand(expr->name()));
+ __ ldr(r0, CodeGenerator::GlobalObject());
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset));
+ __ stm(db_w, sp, r1.bit() | r0.bit());
+ }
// Push the arguments ("left-to-right").
int arg_count = args->length();
@@ -1145,8 +1271,20 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
ASSERT_EQ(Expression::kValue, args->at(i)->context());
}
- __ CallRuntime(function, arg_count);
- Move(expr->context(), r0);
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP);
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // Discard the function left on TOS.
+ DropAndMove(expr->context(), r0);
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ Move(expr->context(), r0);
+ }
}
diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc
index ba8364545..c56f414a1 100644
--- a/deps/v8/src/arm/ic-arm.cc
+++ b/deps/v8/src/arm/ic-arm.cc
@@ -107,12 +107,17 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
static const int kProbes = 4;
for (int i = 0; i < kProbes; i++) {
// Compute the masked index: (hash + i + i * i) & mask.
- __ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
- __ mov(t1, Operand(t1, LSR, String::kHashShift));
+ __ ldr(t1, FieldMemOperand(r2, String::kHashFieldOffset));
if (i > 0) {
- __ add(t1, t1, Operand(StringDictionary::GetProbeOffset(i)));
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(StringDictionary::GetProbeOffset(i) <
+ 1 << (32 - String::kHashFieldOffset));
+ __ add(t1, t1, Operand(
+ StringDictionary::GetProbeOffset(i) << String::kHashShift));
}
- __ and_(t1, t1, Operand(r3));
+ __ and_(t1, r3, Operand(t1, LSR, String::kHashShift));
// Scale the index by multiplying by the element size.
ASSERT(StringDictionary::kEntrySize == 3);
diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc
index a668cb1f7..aa6570ce1 100644
--- a/deps/v8/src/arm/macro-assembler-arm.cc
+++ b/deps/v8/src/arm/macro-assembler-arm.cc
@@ -155,6 +155,15 @@ void MacroAssembler::Ret(Condition cond) {
}
+void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
+ LoadRoot(ip, Heap::kStackLimitRootIndex);
+ cmp(sp, Operand(ip));
+ b(lo, on_stack_overflow);
+}
+
+
+
+
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
// Empty the const pool.
CheckConstPool(true, true);
@@ -785,15 +794,13 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
mov(scratch1, Operand(new_space_allocation_top));
if ((flags & RESULT_CONTAINS_TOP) == 0) {
ldr(result, MemOperand(scratch1));
- } else {
-#ifdef DEBUG
+ } else if (FLAG_debug_code) {
// Assert that result actually contains top on entry. scratch2 is used
// immediately below so this use of scratch2 does not cause difference with
// respect to register content between debug and release mode.
ldr(scratch2, MemOperand(scratch1));
cmp(result, scratch2);
Check(eq, "Unexpected allocation top");
-#endif
}
// Calculate new top and bail out if new space is exhausted. Use result
@@ -806,7 +813,11 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
cmp(result, Operand(scratch2));
b(hi, gc_required);
- // Update allocation top. result temporarily holds the new top,
+ // Update allocation top. result temporarily holds the new top.
+ if (FLAG_debug_code) {
+ tst(result, Operand(kObjectAlignmentMask));
+ Check(eq, "Unaligned allocation in new space");
+ }
str(result, MemOperand(scratch1));
// Tag and adjust back to start of new object.
@@ -835,15 +846,13 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
mov(scratch1, Operand(new_space_allocation_top));
if ((flags & RESULT_CONTAINS_TOP) == 0) {
ldr(result, MemOperand(scratch1));
- } else {
-#ifdef DEBUG
+ } else if (FLAG_debug_code) {
// Assert that result actually contains top on entry. scratch2 is used
// immediately below so this use of scratch2 does not cause difference with
// respect to register content between debug and release mode.
ldr(scratch2, MemOperand(scratch1));
cmp(result, scratch2);
Check(eq, "Unexpected allocation top");
-#endif
}
// Calculate new top and bail out if new space is exhausted. Use result
@@ -857,7 +866,11 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
cmp(result, Operand(scratch2));
b(hi, gc_required);
- // Update allocation top. result temporarily holds the new top,
+ // Update allocation top. result temporarily holds the new top.
+ if (FLAG_debug_code) {
+ tst(result, Operand(kObjectAlignmentMask));
+ Check(eq, "Unaligned allocation in new space");
+ }
str(result, MemOperand(scratch1));
// Adjust back to start of new object.
@@ -1153,6 +1166,9 @@ void MacroAssembler::Abort(const char* msg) {
RecordComment(msg);
}
#endif
+ // Disable stub call restrictions to always allow calls to abort.
+ set_allow_stub_calls(true);
+
mov(r0, Operand(p0));
push(r0);
mov(r0, Operand(Smi::FromInt(p1 - p0)));
@@ -1162,6 +1178,26 @@ void MacroAssembler::Abort(const char* msg) {
}
+void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
+ if (context_chain_length > 0) {
+ // Move up the chain of contexts to the context containing the slot.
+ ldr(dst, MemOperand(cp, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ // Load the function context (which is the incoming, outer context).
+ ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
+ for (int i = 1; i < context_chain_length; i++) {
+ ldr(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
+ }
+ // The context may be an intermediate context, not a function context.
+ ldr(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ } else { // Slot is in the current function context.
+ // The context may be an intermediate context, not a function context.
+ ldr(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ }
+}
+
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
CodePatcher::CodePatcher(byte* address, int instructions)
: address_(address),
diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h
index 8c247bfbc..09743290f 100644
--- a/deps/v8/src/arm/macro-assembler-arm.h
+++ b/deps/v8/src/arm/macro-assembler-arm.h
@@ -79,6 +79,11 @@ class MacroAssembler: public Assembler {
void RecordWrite(Register object, Register offset, Register scratch);
// ---------------------------------------------------------------------------
+ // Stack limit support
+
+ void StackLimitCheck(Label* on_stack_limit_hit);
+
+ // ---------------------------------------------------------------------------
// Activation frames
void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); }
@@ -99,6 +104,8 @@ class MacroAssembler: public Assembler {
// Align the stack by optionally pushing a Smi zero.
void AlignStack(int offset);
+ void LoadContext(Register dst, int context_chain_length);
+
// ---------------------------------------------------------------------------
// JavaScript invokes
diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc
index 8282655f7..efccaf496 100644
--- a/deps/v8/src/arm/stub-cache-arm.cc
+++ b/deps/v8/src/arm/stub-cache-arm.cc
@@ -105,7 +105,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
__ b(eq, &miss);
// Get the map of the receiver and compute the hash.
- __ ldr(scratch, FieldMemOperand(name, String::kLengthOffset));
+ __ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset));
__ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ add(scratch, scratch, Operand(ip));
__ eor(scratch, scratch, Operand(flags));
@@ -229,10 +229,7 @@ void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm,
miss, &check_wrapper);
// Load length directly from the string.
- __ and_(scratch1, scratch1, Operand(kStringSizeMask));
- __ add(scratch1, scratch1, Operand(String::kHashShift));
__ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
- __ mov(r0, Operand(r0, LSR, scratch1));
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
__ Ret();
diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js
index 94d74a508..20d884ee6 100644
--- a/deps/v8/src/array.js
+++ b/deps/v8/src/array.js
@@ -77,7 +77,8 @@ function SparseJoin(array, len, convert) {
var key = keys[i];
if (key != last_key) {
var e = array[key];
- builder.add(convert(e));
+ if (typeof(e) !== 'string') e = convert(e);
+ builder.add(e);
last_key = key;
}
}
@@ -114,17 +115,36 @@ function Join(array, length, separator, convert) {
if (length == 1) {
var e = array[0];
if (!IS_UNDEFINED(e) || (0 in array)) {
+ if (typeof(e) === 'string') return e;
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));
+ // We pull the empty separator check outside the loop for speed!
+ if (separator.length == 0) {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (!IS_UNDEFINED(e) || (i in array)) {
+ if (typeof(e) !== 'string') e = convert(e);
+ if (e.length > 0) {
+ var elements = builder.elements;
+ elements[elements.length] = e;
+ }
+ }
+ }
+ } else {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (i != 0) builder.add(separator);
+ if (!IS_UNDEFINED(e) || (i in array)) {
+ if (typeof(e) !== 'string') e = convert(e);
+ if (e.length > 0) {
+ var elements = builder.elements;
+ elements[elements.length] = e;
+ }
+ }
}
}
return builder.generate();
@@ -136,12 +156,14 @@ function Join(array, length, separator, convert) {
function ConvertToString(e) {
+ if (typeof(e) === 'string') return e;
if (e == null) return '';
else return ToString(e);
}
function ConvertToLocaleString(e) {
+ if (typeof(e) === 'string') return e;
if (e == null) return '';
else {
// e_obj's toLocaleString might be overwritten, check if it is a function.
@@ -149,7 +171,7 @@ function ConvertToLocaleString(e) {
// See issue 877615.
var e_obj = ToObject(e);
if (IS_FUNCTION(e_obj.toLocaleString))
- return e_obj.toLocaleString();
+ return ToString(e_obj.toLocaleString());
else
return ToString(e);
}
diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h
index 560470f7e..c27d558a2 100644
--- a/deps/v8/src/ast.h
+++ b/deps/v8/src/ast.h
@@ -1080,6 +1080,7 @@ class CallRuntime: public Expression {
Handle<String> name() const { return name_; }
Runtime::Function* function() const { return function_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
+ bool is_jsruntime() const { return function_ == NULL; }
private:
Handle<String> name_;
diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc
index 449196216..deda96f31 100644
--- a/deps/v8/src/bootstrapper.cc
+++ b/deps/v8/src/bootstrapper.cc
@@ -1111,21 +1111,29 @@ bool Genesis::InstallNatives() {
Factory::LookupAsciiSymbol("context_data"),
proxy_context_data,
common_attributes);
- Handle<Proxy> proxy_eval_from_function =
- Factory::NewProxy(&Accessors::ScriptEvalFromFunction);
+ Handle<Proxy> proxy_eval_from_script =
+ Factory::NewProxy(&Accessors::ScriptEvalFromScript);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
script_descriptors,
- Factory::LookupAsciiSymbol("eval_from_function"),
- proxy_eval_from_function,
+ Factory::LookupAsciiSymbol("eval_from_script"),
+ proxy_eval_from_script,
common_attributes);
- Handle<Proxy> proxy_eval_from_position =
- Factory::NewProxy(&Accessors::ScriptEvalFromPosition);
+ Handle<Proxy> proxy_eval_from_script_position =
+ Factory::NewProxy(&Accessors::ScriptEvalFromScriptPosition);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
script_descriptors,
- Factory::LookupAsciiSymbol("eval_from_position"),
- proxy_eval_from_position,
+ Factory::LookupAsciiSymbol("eval_from_script_position"),
+ proxy_eval_from_script_position,
+ common_attributes);
+ Handle<Proxy> proxy_eval_from_function_name =
+ Factory::NewProxy(&Accessors::ScriptEvalFromFunctionName);
+ script_descriptors =
+ Factory::CopyAppendProxyDescriptor(
+ script_descriptors,
+ Factory::LookupAsciiSymbol("eval_from_function_name"),
+ proxy_eval_from_function_name,
common_attributes);
Handle<Map> script_map = Handle<Map>(script_fun->initial_map());
@@ -1338,8 +1346,6 @@ bool Genesis::InstallExtension(v8::RegisteredExtension* current) {
ASSERT(Top::has_pending_exception() != result);
if (!result) {
Top::clear_pending_exception();
- v8::Utils::ReportApiFailure(
- "v8::Context::New()", "Error installing extension");
}
current->set_state(v8::INSTALLED);
return result;
diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h
index 6c50c6db0..25a2d0f55 100644
--- a/deps/v8/src/code-stubs.h
+++ b/deps/v8/src/code-stubs.h
@@ -36,6 +36,7 @@ namespace internal {
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
V(GenericBinaryOp) \
+ V(StringAdd) \
V(SmiOp) \
V(Compare) \
V(RecordWrite) \
diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc
index a6d5fb47f..26e8d7de0 100644
--- a/deps/v8/src/codegen.cc
+++ b/deps/v8/src/codegen.cc
@@ -346,6 +346,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateMathCos, "_Math_cos"},
{&CodeGenerator::GenerateIsObject, "_IsObject"},
{&CodeGenerator::GenerateIsFunction, "_IsFunction"},
+ {&CodeGenerator::GenerateStringAdd, "_StringAdd"},
};
diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc
index 4e80a2450..22b0a03c6 100644
--- a/deps/v8/src/compiler.cc
+++ b/deps/v8/src/compiler.cc
@@ -124,9 +124,12 @@ static Handle<Code> MakeCode(FunctionLiteral* literal,
// If there is no shared function info, try the fast code
// generator for code in the global scope. Otherwise obey the
// explicit hint in the shared function info.
- if (shared.is_null() && !literal->scope()->is_global_scope()) {
+ // If always_fast_compiler is true, always try the fast compiler.
+ if (shared.is_null() && !literal->scope()->is_global_scope() &&
+ !FLAG_always_fast_compiler) {
if (FLAG_trace_bailout) PrintF("Non-global scope\n");
- } else if (!shared.is_null() && !shared->try_fast_codegen()) {
+ } else if (!shared.is_null() && !shared->try_fast_codegen() &&
+ !FLAG_always_fast_compiler) {
if (FLAG_trace_bailout) PrintF("No hint to try fast\n");
} else {
CodeGenSelector selector;
@@ -176,7 +179,8 @@ static Handle<JSFunction> MakeFunction(bool is_global,
// called.
if (is_eval) {
JavaScriptFrameIterator it;
- script->set_eval_from_function(it.frame()->function());
+ script->set_eval_from_shared(
+ JSFunction::cast(it.frame()->function())->shared());
int offset = static_cast<int>(
it.frame()->pc() - it.frame()->code()->instruction_start());
script->set_eval_from_instructions_offset(Smi::FromInt(offset));
@@ -599,11 +603,6 @@ CodeGenSelector::CodeGenTag CodeGenSelector::Select(FunctionLiteral* fun) {
}
}
- if (scope->arguments() != NULL) {
- if (FLAG_trace_bailout) PrintF("function uses 'arguments'\n");
- return NORMAL;
- }
-
has_supported_syntax_ = true;
VisitDeclarations(scope->declarations());
if (!has_supported_syntax_) return NORMAL;
@@ -802,7 +801,17 @@ void CodeGenSelector::VisitVariableProxy(VariableProxy* expr) {
BAILOUT("Lookup slot");
}
} else {
- BAILOUT("access to arguments object");
+#ifdef DEBUG
+ // Only remaining possibility is a property where the object is
+ // a slotted variable and the key is a smi.
+ Property* property = rewrite->AsProperty();
+ ASSERT_NOT_NULL(property);
+ Variable* object = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object);
+ ASSERT_NOT_NULL(object->slot());
+ ASSERT_NOT_NULL(property->key()->AsLiteral());
+ ASSERT(property->key()->AsLiteral()->handle()->IsSmi());
+#endif
}
}
}
@@ -886,12 +895,21 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) {
// All global variables are supported.
if (!var->is_global()) {
if (var->slot() == NULL) {
- // This is a parameter that has rewritten to an arguments access.
- BAILOUT("non-global/non-slot assignment");
- }
- Slot::Type type = var->slot()->type();
- if (type == Slot::LOOKUP) {
- BAILOUT("Lookup slot");
+ Property* property = var->AsProperty();
+ if (property == NULL) {
+ BAILOUT("non-global/non-slot/non-property assignment");
+ }
+ if (property->obj()->AsSlot() == NULL) {
+ BAILOUT("variable rewritten to property non slot object assignment");
+ }
+ if (property->key()->AsLiteral() == NULL) {
+ BAILOUT("variable rewritten to property non literal key assignment");
+ }
+ } else {
+ Slot::Type type = var->slot()->type();
+ if (type == Slot::LOOKUP) {
+ BAILOUT("Lookup slot");
+ }
}
}
} else if (prop != NULL) {
@@ -979,8 +997,6 @@ void CodeGenSelector::VisitCallNew(CallNew* expr) {
void CodeGenSelector::VisitCallRuntime(CallRuntime* expr) {
- // In case of JS runtime function bail out.
- if (expr->function() == NULL) BAILOUT("call JS runtime function");
// Check for inline runtime call
if (expr->name()->Get(0) == '_' &&
CodeGenerator::FindInlineRuntimeLUT(expr->name()) != NULL) {
diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc
index e4658b1ce..dedbd55b4 100644
--- a/deps/v8/src/d8.cc
+++ b/deps/v8/src/d8.cc
@@ -159,7 +159,11 @@ Handle<Value> Shell::Write(const Arguments& args) {
printf(" ");
}
v8::String::Utf8Value str(args[i]);
- fwrite(*str, sizeof(**str), str.length(), stdout);
+ int n = fwrite(*str, sizeof(**str), str.length(), stdout);
+ if (n != str.length()) {
+ printf("Error in fwrite\n");
+ exit(1);
+ }
}
return Undefined();
}
@@ -203,7 +207,7 @@ Handle<Value> Shell::Load(const Arguments& args) {
return ThrowException(String::New("Error loading file"));
}
if (!ExecuteString(source, String::New(*file), false, false)) {
- return ThrowException(String::New("Error executing file"));
+ return ThrowException(String::New("Error executing file"));
}
}
return Undefined();
diff --git a/deps/v8/src/debug-delay.js b/deps/v8/src/debug-delay.js
index 35f7fcd7e..04fde1f99 100644
--- a/deps/v8/src/debug-delay.js
+++ b/deps/v8/src/debug-delay.js
@@ -1245,6 +1245,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.suspendRequest_(request, response);
} else if (request.command == 'version') {
this.versionRequest_(request, response);
+ } else if (request.command == 'profile') {
+ this.profileRequest_(request, response);
} else {
throw new Error('Unknown command "' + request.command + '" in request');
}
@@ -1924,6 +1926,25 @@ DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
};
+DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var modules = parseInt(request.arguments.modules);
+ if (isNaN(modules)) {
+ return response.failed('Modules is not an integer');
+ }
+ if (request.arguments.command == 'resume') {
+ %ProfilerResume(modules);
+ } else if (request.arguments.command == 'pause') {
+ %ProfilerPause(modules);
+ } else {
+ return response.failed('Unknown command');
+ }
+ response.body = {};
+};
+
+
// Check whether the previously processed command caused the VM to become
// running.
DebugCommandProcessor.prototype.isRunning = function() {
diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h
index c5c6b5ee5..24f0db413 100644
--- a/deps/v8/src/debug.h
+++ b/deps/v8/src/debug.h
@@ -370,17 +370,6 @@ class Debug {
// Garbage collection notifications.
static void AfterGarbageCollection();
- // Code generation assumptions.
- static const int kIa32CallInstructionLength = 5;
- static const int kIa32JSReturnSequenceLength = 6;
-
- // The x64 JS return sequence is padded with int3 to make it large
- // enough to hold a call instruction when the debugger patches it.
- static const int kX64CallInstructionLength = 13;
- static const int kX64JSReturnSequenceLength = 13;
-
- static const int kARMJSReturnSequenceLength = 4;
-
// Code generator routines.
static void GenerateLoadICDebugBreak(MacroAssembler* masm);
static void GenerateStoreICDebugBreak(MacroAssembler* masm);
diff --git a/deps/v8/src/dtoa-config.c b/deps/v8/src/dtoa-config.c
index bc0a58a17..a1acd2dd4 100644
--- a/deps/v8/src/dtoa-config.c
+++ b/deps/v8/src/dtoa-config.c
@@ -38,7 +38,7 @@
*/
#if !(defined(__APPLE__) && defined(__MACH__)) && \
- !defined(WIN32) && !defined(__FreeBSD__)
+ !defined(WIN32) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include <endian.h>
#endif
#include <math.h>
@@ -47,14 +47,16 @@
/* 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) && \
- !defined(__FreeBSD__) && __FLOAT_WORD_ORDER == __BIG_ENDIAN
+ !defined(__FreeBSD__) && !defined(__OpenBSD__) && \
+ __FLOAT_WORD_ORDER == __BIG_ENDIAN
#define IEEE_MC68k
#else
#define IEEE_8087
#endif
#define __MATH_H__
-#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__)
+#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__) || \
+ defined(__OpenBSD__)
/* stdlib.h on FreeBSD and 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
diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc
index 3d9cd7a11..83775ef65 100644
--- a/deps/v8/src/factory.cc
+++ b/deps/v8/src/factory.cc
@@ -188,9 +188,8 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
script->set_type(Smi::FromInt(Script::TYPE_NORMAL));
script->set_compilation_type(Smi::FromInt(Script::COMPILATION_TYPE_HOST));
script->set_wrapper(*wrapper);
- script->set_line_ends_fixed_array(Heap::undefined_value());
- script->set_line_ends_js_array(Heap::undefined_value());
- script->set_eval_from_function(Heap::undefined_value());
+ script->set_line_ends(Heap::undefined_value());
+ script->set_eval_from_shared(Heap::undefined_value());
script->set_eval_from_instructions_offset(Smi::FromInt(0));
return script;
diff --git a/deps/v8/src/fast-codegen.cc b/deps/v8/src/fast-codegen.cc
index 53fcf3112..20de80853 100644
--- a/deps/v8/src/fast-codegen.cc
+++ b/deps/v8/src/fast-codegen.cc
@@ -305,12 +305,15 @@ void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
Comment cmnt(masm_, "[ DoWhileStatement");
increment_loop_depth();
- Label body, exit;
+ Label body, exit, stack_limit_hit, stack_check_success;
- // Emit the test at the bottom of the loop.
__ bind(&body);
Visit(stmt->body());
+ // Check stack before looping.
+ __ StackLimitCheck(&stack_limit_hit);
+ __ bind(&stack_check_success);
+
// We are not in an expression context because we have been compiling
// statements. Set up a test expression context for the condition.
ASSERT_EQ(NULL, true_label_);
@@ -322,6 +325,11 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
true_label_ = NULL;
false_label_ = NULL;
+ __ bind(&stack_limit_hit);
+ StackCheckStub stack_stub;
+ __ CallStub(&stack_stub);
+ __ jmp(&stack_check_success);
+
__ bind(&exit);
decrement_loop_depth();
@@ -331,7 +339,7 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
Comment cmnt(masm_, "[ WhileStatement");
increment_loop_depth();
- Label test, body, exit;
+ Label test, body, exit, stack_limit_hit, stack_check_success;
// Emit the test at the bottom of the loop.
__ jmp(&test);
@@ -340,6 +348,10 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
Visit(stmt->body());
__ bind(&test);
+ // Check stack before looping.
+ __ StackLimitCheck(&stack_limit_hit);
+ __ bind(&stack_check_success);
+
// We are not in an expression context because we have been compiling
// statements. Set up a test expression context for the condition.
ASSERT_EQ(NULL, true_label_);
@@ -351,6 +363,11 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
true_label_ = NULL;
false_label_ = NULL;
+ __ bind(&stack_limit_hit);
+ StackCheckStub stack_stub;
+ __ CallStub(&stack_stub);
+ __ jmp(&stack_check_success);
+
__ bind(&exit);
decrement_loop_depth();
@@ -359,7 +376,7 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
Comment cmnt(masm_, "[ ForStatement");
- Label test, body, exit;
+ Label test, body, exit, stack_limit_hit, stack_check_success;
if (stmt->init() != NULL) Visit(stmt->init());
increment_loop_depth();
@@ -367,9 +384,15 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
__ jmp(&test);
__ bind(&body);
Visit(stmt->body());
+
+ // Check stack before looping.
+ __ StackLimitCheck(&stack_limit_hit);
+ __ bind(&stack_check_success);
+
if (stmt->next() != NULL) Visit(stmt->next());
__ bind(&test);
+
if (stmt->cond() == NULL) {
// For an empty test jump to the top of the loop.
__ jmp(&body);
@@ -378,6 +401,7 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
// statements. Set up a test expression context for the condition.
ASSERT_EQ(NULL, true_label_);
ASSERT_EQ(NULL, false_label_);
+
true_label_ = &body;
false_label_ = &exit;
ASSERT(stmt->cond()->context() == Expression::kTest);
@@ -386,6 +410,11 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
false_label_ = NULL;
}
+ __ bind(&stack_limit_hit);
+ StackCheckStub stack_stub;
+ __ CallStub(&stack_stub);
+ __ jmp(&stack_check_success);
+
__ bind(&exit);
decrement_loop_depth();
}
diff --git a/deps/v8/src/fast-codegen.h b/deps/v8/src/fast-codegen.h
index ee2e1d345..9b262a739 100644
--- a/deps/v8/src/fast-codegen.h
+++ b/deps/v8/src/fast-codegen.h
@@ -56,14 +56,21 @@ class FastCodeGenerator: public AstVisitor {
private:
int SlotOffset(Slot* slot);
-
void Move(Expression::Context destination, Register source);
- void Move(Expression::Context destination, Slot* source);
+ void Move(Expression::Context destination, Slot* source, Register scratch);
void Move(Expression::Context destination, Literal* source);
+ void Move(Slot* dst, Register source, Register scratch1, Register scratch2);
+ void Move(Register dst, Slot* source);
+
+ // Templated to allow for Operand on intel and MemOperand on ARM.
+ template <typename MemoryLocation>
+ MemoryLocation CreateSlotOperand(Slot* slot, Register scratch);
// Drop the TOS, and store source to destination.
// If destination is TOS, just overwrite TOS with source.
- void DropAndMove(Expression::Context destination, Register source);
+ void DropAndMove(Expression::Context destination,
+ Register source,
+ int drop_count = 1);
// Test the JavaScript value in source as if in a test context, compile
// control flow to a pair of labels.
diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h
index 8c9bb22dc..88fda123b 100644
--- a/deps/v8/src/flag-definitions.h
+++ b/deps/v8/src/flag-definitions.h
@@ -143,10 +143,12 @@ DEFINE_bool(debug_info, true, "add debug information to compiled functions")
DEFINE_bool(strict, false, "strict error checking")
DEFINE_int(min_preparse_length, 1024,
"minimum length for automatic enable preparsing")
-DEFINE_bool(fast_compiler, false,
+DEFINE_bool(fast_compiler, true,
"use the fast-mode compiler for some top-level code")
DEFINE_bool(trace_bailout, false,
"print reasons for failing to use fast compilation")
+DEFINE_bool(always_fast_compiler, false,
+ "always try using the fast compiler")
// compilation-cache.cc
DEFINE_bool(compilation_cache, true, "enable compilation cache")
@@ -154,9 +156,9 @@ DEFINE_bool(compilation_cache, true, "enable compilation cache")
// debug.cc
DEFINE_bool(remote_debugging, false, "enable remote debugging")
DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response")
-DEFINE_bool(debugger_auto_break, false,
+DEFINE_bool(debugger_auto_break, true,
"automatically set the debug break flag when debugger commands are "
- "in the queue (experimental)")
+ "in the queue")
// frames.cc
DEFINE_int(max_stack_trace_source_length, 300,
diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc
index e519d7077..f3b2b0c50 100644
--- a/deps/v8/src/global-handles.cc
+++ b/deps/v8/src/global-handles.cc
@@ -429,6 +429,26 @@ GlobalHandles::Node* GlobalHandles::head_ = NULL;
GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
+void GlobalHandles::RecordStats(HeapStats* stats) {
+ *stats->global_handle_count = 0;
+ *stats->weak_global_handle_count = 0;
+ *stats->pending_global_handle_count = 0;
+ *stats->near_death_global_handle_count = 0;
+ *stats->destroyed_global_handle_count = 0;
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ *stats->global_handle_count++;
+ if (current->state_ == Node::WEAK) {
+ *stats->weak_global_handle_count++;
+ } else if (current->state_ == Node::PENDING) {
+ *stats->pending_global_handle_count++;
+ } else if (current->state_ == Node::NEAR_DEATH) {
+ *stats->near_death_global_handle_count++;
+ } else if (current->state_ == Node::DESTROYED) {
+ *stats->destroyed_global_handle_count++;
+ }
+ }
+}
+
#ifdef DEBUG
void GlobalHandles::PrintStats() {
diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h
index b9cac5c2a..659f86eca 100644
--- a/deps/v8/src/global-handles.h
+++ b/deps/v8/src/global-handles.h
@@ -78,6 +78,8 @@ class GlobalHandles : public AllStatic {
// Returns the current number of weak handles.
static int NumberOfWeakHandles() { return number_of_weak_handles_; }
+ static void RecordStats(HeapStats* stats);
+
// Returns the current number of weak handles to global objects.
// These handles are also included in NumberOfWeakHandles().
static int NumberOfGlobalObjectWeakHandles() {
diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc
index b42ad241a..d551e21c5 100644
--- a/deps/v8/src/handles.cc
+++ b/deps/v8/src/handles.cc
@@ -429,12 +429,12 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
// Init line_ends array with code positions of line ends inside script
// source.
void InitScriptLineEnds(Handle<Script> script) {
- if (!script->line_ends_fixed_array()->IsUndefined()) return;
+ if (!script->line_ends()->IsUndefined()) return;
if (!script->source()->IsString()) {
ASSERT(script->source()->IsUndefined());
- script->set_line_ends_fixed_array(*(Factory::NewFixedArray(0)));
- ASSERT(script->line_ends_fixed_array()->IsFixedArray());
+ script->set_line_ends(*(Factory::NewFixedArray(0)));
+ ASSERT(script->line_ends()->IsFixedArray());
return;
}
@@ -467,8 +467,8 @@ void InitScriptLineEnds(Handle<Script> script) {
}
ASSERT(array_index == line_count);
- script->set_line_ends_fixed_array(*array);
- ASSERT(script->line_ends_fixed_array()->IsFixedArray());
+ script->set_line_ends(*array);
+ ASSERT(script->line_ends()->IsFixedArray());
}
@@ -477,7 +477,7 @@ int GetScriptLineNumber(Handle<Script> script, int code_pos) {
InitScriptLineEnds(script);
AssertNoAllocation no_allocation;
FixedArray* line_ends_array =
- FixedArray::cast(script->line_ends_fixed_array());
+ FixedArray::cast(script->line_ends());
const int line_ends_len = line_ends_array->length();
int line = -1;
@@ -548,6 +548,12 @@ v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
KeyCollectionType type) {
Handle<FixedArray> content = Factory::empty_fixed_array();
+ Handle<JSObject> arguments_boilerplate =
+ Handle<JSObject>(
+ Top::context()->global_context()->arguments_boilerplate());
+ Handle<JSFunction> arguments_function =
+ Handle<JSFunction>(
+ JSFunction::cast(arguments_boilerplate->map()->constructor()));
// Only collect keys if access is permitted.
for (Handle<Object> p = object;
@@ -577,8 +583,21 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
}
- // Compute the property keys.
- content = UnionOfKeys(content, GetEnumPropertyKeys(current));
+ // We can cache the computed property keys if access checks are
+ // not needed and no interceptors are involved.
+ //
+ // We do not use the cache if the object has elements and
+ // therefore it does not make sense to cache the property names
+ // for arguments objects. Arguments objects will always have
+ // elements.
+ bool cache_enum_keys =
+ ((current->map()->constructor() != *arguments_function) &&
+ !current->IsAccessCheckNeeded() &&
+ !current->HasNamedInterceptor() &&
+ !current->HasIndexedInterceptor());
+ // Compute the property keys and cache them if possible.
+ content =
+ UnionOfKeys(content, GetEnumPropertyKeys(current, cache_enum_keys));
// Add the property keys from the interceptor.
if (current->HasNamedInterceptor()) {
@@ -605,7 +624,8 @@ Handle<JSArray> GetKeysFor(Handle<JSObject> object) {
}
-Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) {
+Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
+ bool cache_result) {
int index = 0;
if (object->HasFastProperties()) {
if (object->map()->instance_descriptors()->HasEnumCache()) {
@@ -628,10 +648,12 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) {
}
}
(*storage)->SortPairs(*sort_array, sort_array->length());
- Handle<FixedArray> bridge_storage =
- Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength);
- DescriptorArray* desc = object->map()->instance_descriptors();
- desc->SetEnumCache(*bridge_storage, *storage);
+ if (cache_result) {
+ Handle<FixedArray> bridge_storage =
+ Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength);
+ DescriptorArray* desc = object->map()->instance_descriptors();
+ desc->SetEnumCache(*bridge_storage, *storage);
+ }
ASSERT(storage->length() == index);
return storage;
} else {
diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h
index f610a34d0..fe820d59e 100644
--- a/deps/v8/src/handles.h
+++ b/deps/v8/src/handles.h
@@ -277,7 +277,8 @@ enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS };
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
KeyCollectionType type);
Handle<JSArray> GetKeysFor(Handle<JSObject> object);
-Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object);
+Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
+ bool cache_result);
// Computes the union of keys and return the result.
// Used for implementing "for (n in object) { }"
diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h
index 0646878ef..eccd5ee2d 100644
--- a/deps/v8/src/heap-inl.h
+++ b/deps/v8/src/heap-inl.h
@@ -41,10 +41,10 @@ int Heap::MaxObjectSizeInPagedSpace() {
Object* Heap::AllocateSymbol(Vector<const char> str,
int chars,
- uint32_t length_field) {
+ uint32_t hash_field) {
unibrow::Utf8InputBuffer<> buffer(str.start(),
static_cast<unsigned>(str.length()));
- return AllocateInternalSymbol(&buffer, chars, length_field);
+ return AllocateInternalSymbol(&buffer, chars, hash_field);
}
diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc
index 06dc59cdd..4e4cd1c05 100644
--- a/deps/v8/src/heap.cc
+++ b/deps/v8/src/heap.cc
@@ -1187,34 +1187,14 @@ bool Heap::CreateInitialMaps() {
roots_[entry.index] = Map::cast(obj);
}
- obj = AllocateMap(SHORT_STRING_TYPE, SeqTwoByteString::kAlignedSize);
+ obj = AllocateMap(STRING_TYPE, SeqTwoByteString::kAlignedSize);
if (obj->IsFailure()) return false;
- set_undetectable_short_string_map(Map::cast(obj));
+ set_undetectable_string_map(Map::cast(obj));
Map::cast(obj)->set_is_undetectable();
- obj = AllocateMap(MEDIUM_STRING_TYPE, SeqTwoByteString::kAlignedSize);
+ obj = AllocateMap(ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize);
if (obj->IsFailure()) return false;
- set_undetectable_medium_string_map(Map::cast(obj));
- Map::cast(obj)->set_is_undetectable();
-
- obj = AllocateMap(LONG_STRING_TYPE, SeqTwoByteString::kAlignedSize);
- if (obj->IsFailure()) return false;
- set_undetectable_long_string_map(Map::cast(obj));
- Map::cast(obj)->set_is_undetectable();
-
- obj = AllocateMap(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize);
- if (obj->IsFailure()) return false;
- set_undetectable_short_ascii_string_map(Map::cast(obj));
- Map::cast(obj)->set_is_undetectable();
-
- obj = AllocateMap(MEDIUM_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize);
- if (obj->IsFailure()) return false;
- set_undetectable_medium_ascii_string_map(Map::cast(obj));
- Map::cast(obj)->set_is_undetectable();
-
- obj = AllocateMap(LONG_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize);
- if (obj->IsFailure()) return false;
- set_undetectable_long_ascii_string_map(Map::cast(obj));
+ set_undetectable_ascii_string_map(Map::cast(obj));
Map::cast(obj)->set_is_undetectable();
obj = AllocateMap(BYTE_ARRAY_TYPE, ByteArray::kAlignedSize);
@@ -1839,10 +1819,19 @@ Object* Heap::AllocateConsString(String* first, String* second) {
// Copy the characters into the new object.
char* dest = SeqAsciiString::cast(result)->GetChars();
// Copy first part.
- char* src = SeqAsciiString::cast(first)->GetChars();
+ const char* src;
+ if (first->IsExternalString()) {
+ src = ExternalAsciiString::cast(first)->resource()->data();
+ } else {
+ src = SeqAsciiString::cast(first)->GetChars();
+ }
for (int i = 0; i < first_length; i++) *dest++ = src[i];
// Copy second part.
- src = SeqAsciiString::cast(second)->GetChars();
+ if (second->IsExternalString()) {
+ src = ExternalAsciiString::cast(second)->resource()->data();
+ } else {
+ src = SeqAsciiString::cast(second)->GetChars();
+ }
for (int i = 0; i < second_length; i++) *dest++ = src[i];
return result;
} else {
@@ -1856,26 +1845,17 @@ Object* Heap::AllocateConsString(String* first, String* second) {
}
}
- Map* map;
- if (length <= String::kMaxShortSize) {
- map = is_ascii ? short_cons_ascii_string_map()
- : short_cons_string_map();
- } else if (length <= String::kMaxMediumSize) {
- map = is_ascii ? medium_cons_ascii_string_map()
- : medium_cons_string_map();
- } else {
- map = is_ascii ? long_cons_ascii_string_map()
- : long_cons_string_map();
- }
+ Map* map = is_ascii ? cons_ascii_string_map() : cons_string_map();
Object* result = Allocate(map,
always_allocate() ? OLD_POINTER_SPACE : NEW_SPACE);
if (result->IsFailure()) return result;
ConsString* cons_string = ConsString::cast(result);
WriteBarrierMode mode = cons_string->GetWriteBarrierMode();
+ cons_string->set_length(length);
+ cons_string->set_hash_field(String::kEmptyHashField);
cons_string->set_first(first, mode);
cons_string->set_second(second, mode);
- cons_string->set_length(length);
return result;
}
@@ -1925,25 +1905,20 @@ Object* Heap::AllocateSubString(String* buffer,
Object* Heap::AllocateExternalStringFromAscii(
ExternalAsciiString::Resource* resource) {
- Map* map;
size_t length = resource->length();
- if (length <= static_cast<size_t>(String::kMaxShortSize)) {
- map = short_external_ascii_string_map();
- } else if (length <= static_cast<size_t>(String::kMaxMediumSize)) {
- map = medium_external_ascii_string_map();
- } else if (length <= static_cast<size_t>(String::kMaxLength)) {
- map = long_external_ascii_string_map();
- } else {
+ if (length > static_cast<size_t>(String::kMaxLength)) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
+ Map* map = external_ascii_string_map();
Object* result = Allocate(map,
always_allocate() ? OLD_DATA_SPACE : NEW_SPACE);
if (result->IsFailure()) return result;
ExternalAsciiString* external_string = ExternalAsciiString::cast(result);
external_string->set_length(static_cast<int>(length));
+ external_string->set_hash_field(String::kEmptyHashField);
external_string->set_resource(resource);
return result;
@@ -1957,13 +1932,15 @@ Object* Heap::AllocateExternalStringFromTwoByte(
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
- Map* map = ExternalTwoByteString::StringMap(static_cast<int>(length));
+
+ Map* map = Heap::external_string_map();
Object* result = Allocate(map,
always_allocate() ? OLD_DATA_SPACE : NEW_SPACE);
if (result->IsFailure()) return result;
ExternalTwoByteString* external_string = ExternalTwoByteString::cast(result);
external_string->set_length(static_cast<int>(length));
+ external_string->set_hash_field(String::kEmptyHashField);
external_string->set_resource(resource);
return result;
@@ -2604,48 +2581,12 @@ Map* Heap::SymbolMapForString(String* string) {
// Find the corresponding symbol map for strings.
Map* map = string->map();
-
- if (map == short_ascii_string_map()) return short_ascii_symbol_map();
- if (map == medium_ascii_string_map()) return medium_ascii_symbol_map();
- if (map == long_ascii_string_map()) return long_ascii_symbol_map();
-
- if (map == short_string_map()) return short_symbol_map();
- if (map == medium_string_map()) return medium_symbol_map();
- if (map == long_string_map()) return long_symbol_map();
-
- if (map == short_cons_string_map()) return short_cons_symbol_map();
- if (map == medium_cons_string_map()) return medium_cons_symbol_map();
- if (map == long_cons_string_map()) return long_cons_symbol_map();
-
- if (map == short_cons_ascii_string_map()) {
- return short_cons_ascii_symbol_map();
- }
- if (map == medium_cons_ascii_string_map()) {
- return medium_cons_ascii_symbol_map();
- }
- if (map == long_cons_ascii_string_map()) {
- return long_cons_ascii_symbol_map();
- }
-
- if (map == short_external_string_map()) {
- return short_external_symbol_map();
- }
- if (map == medium_external_string_map()) {
- return medium_external_symbol_map();
- }
- if (map == long_external_string_map()) {
- return long_external_symbol_map();
- }
-
- if (map == short_external_ascii_string_map()) {
- return short_external_ascii_symbol_map();
- }
- if (map == medium_external_ascii_string_map()) {
- return medium_external_ascii_symbol_map();
- }
- if (map == long_external_ascii_string_map()) {
- return long_external_ascii_symbol_map();
- }
+ if (map == ascii_string_map()) return ascii_symbol_map();
+ if (map == string_map()) return symbol_map();
+ if (map == cons_string_map()) return cons_symbol_map();
+ if (map == cons_ascii_string_map()) return cons_ascii_symbol_map();
+ if (map == external_string_map()) return external_symbol_map();
+ if (map == external_ascii_string_map()) return external_ascii_symbol_map();
// No match found.
return NULL;
@@ -2654,7 +2595,7 @@ Map* Heap::SymbolMapForString(String* string) {
Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
int chars,
- uint32_t length_field) {
+ uint32_t hash_field) {
// Ensure the chars matches the number of characters in the buffer.
ASSERT(static_cast<unsigned>(chars) == buffer->Length());
// Determine whether the string is ascii.
@@ -2669,22 +2610,10 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
Map* map;
if (is_ascii) {
- if (chars <= String::kMaxShortSize) {
- map = short_ascii_symbol_map();
- } else if (chars <= String::kMaxMediumSize) {
- map = medium_ascii_symbol_map();
- } else {
- map = long_ascii_symbol_map();
- }
+ map = ascii_symbol_map();
size = SeqAsciiString::SizeFor(chars);
} else {
- if (chars <= String::kMaxShortSize) {
- map = short_symbol_map();
- } else if (chars <= String::kMaxMediumSize) {
- map = medium_symbol_map();
- } else {
- map = long_symbol_map();
- }
+ map = symbol_map();
size = SeqTwoByteString::SizeFor(chars);
}
@@ -2695,9 +2624,10 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer,
if (result->IsFailure()) return result;
reinterpret_cast<HeapObject*>(result)->set_map(map);
- // The hash value contains the length of the string.
+ // Set length and hash fields of the allocated string.
String* answer = String::cast(result);
- answer->set_length_field(length_field);
+ answer->set_length(chars);
+ answer->set_hash_field(hash_field);
ASSERT_EQ(size, answer->Size());
@@ -2728,19 +2658,10 @@ Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) {
}
if (result->IsFailure()) return result;
- // Determine the map based on the string's length.
- Map* map;
- if (length <= String::kMaxShortSize) {
- map = short_ascii_string_map();
- } else if (length <= String::kMaxMediumSize) {
- map = medium_ascii_string_map();
- } else {
- map = long_ascii_string_map();
- }
-
// Partially initialize the object.
- HeapObject::cast(result)->set_map(map);
+ HeapObject::cast(result)->set_map(ascii_string_map());
String::cast(result)->set_length(length);
+ String::cast(result)->set_hash_field(String::kEmptyHashField);
ASSERT_EQ(size, HeapObject::cast(result)->Size());
return result;
}
@@ -2765,19 +2686,10 @@ Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) {
}
if (result->IsFailure()) return result;
- // Determine the map based on the string's length.
- Map* map;
- if (length <= String::kMaxShortSize) {
- map = short_string_map();
- } else if (length <= String::kMaxMediumSize) {
- map = medium_string_map();
- } else {
- map = long_string_map();
- }
-
// Partially initialize the object.
- HeapObject::cast(result)->set_map(map);
+ HeapObject::cast(result)->set_map(string_map());
String::cast(result)->set_length(length);
+ String::cast(result)->set_hash_field(String::kEmptyHashField);
ASSERT_EQ(size, HeapObject::cast(result)->Size());
return result;
}
@@ -2998,6 +2910,11 @@ bool Heap::IdleNotification() {
last_gc_count = gc_count_;
} else if (number_idle_notifications == kIdlesBeforeMarkSweep) {
+ // Before doing the mark-sweep collections we clear the
+ // compilation cache to avoid hanging on to source code and
+ // generated code for cached functions.
+ CompilationCache::Clear();
+
CollectAllGarbage(false);
new_space_.Shrink();
last_gc_count = gc_count_;
@@ -3356,6 +3273,26 @@ bool Heap::ConfigureHeapDefault() {
}
+void Heap::RecordStats(HeapStats* stats) {
+ *stats->start_marker = 0xDECADE00;
+ *stats->end_marker = 0xDECADE01;
+ *stats->new_space_size = new_space_.Size();
+ *stats->new_space_capacity = new_space_.Capacity();
+ *stats->old_pointer_space_size = old_pointer_space_->Size();
+ *stats->old_pointer_space_capacity = old_pointer_space_->Capacity();
+ *stats->old_data_space_size = old_data_space_->Size();
+ *stats->old_data_space_capacity = old_data_space_->Capacity();
+ *stats->code_space_size = code_space_->Size();
+ *stats->code_space_capacity = code_space_->Capacity();
+ *stats->map_space_size = map_space_->Size();
+ *stats->map_space_capacity = map_space_->Capacity();
+ *stats->cell_space_size = cell_space_->Size();
+ *stats->cell_space_capacity = cell_space_->Capacity();
+ *stats->lo_space_size = lo_space_->Size();
+ GlobalHandles::RecordStats(stats);
+}
+
+
int Heap::PromotedSpaceSize() {
return old_pointer_space_->Size()
+ old_data_space_->Size()
diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h
index 90d714c7f..b37fe4b5b 100644
--- a/deps/v8/src/heap.h
+++ b/deps/v8/src/heap.h
@@ -59,50 +59,20 @@ namespace internal {
V(Object, termination_exception, TerminationException) \
V(Map, hash_table_map, HashTableMap) \
V(FixedArray, empty_fixed_array, EmptyFixedArray) \
- V(Map, short_string_map, ShortStringMap) \
- V(Map, medium_string_map, MediumStringMap) \
- V(Map, long_string_map, LongStringMap) \
- V(Map, short_ascii_string_map, ShortAsciiStringMap) \
- V(Map, medium_ascii_string_map, MediumAsciiStringMap) \
- V(Map, long_ascii_string_map, LongAsciiStringMap) \
- V(Map, short_symbol_map, ShortSymbolMap) \
- V(Map, medium_symbol_map, MediumSymbolMap) \
- V(Map, long_symbol_map, LongSymbolMap) \
- V(Map, short_ascii_symbol_map, ShortAsciiSymbolMap) \
- V(Map, medium_ascii_symbol_map, MediumAsciiSymbolMap) \
- V(Map, long_ascii_symbol_map, LongAsciiSymbolMap) \
- V(Map, short_cons_symbol_map, ShortConsSymbolMap) \
- V(Map, medium_cons_symbol_map, MediumConsSymbolMap) \
- V(Map, long_cons_symbol_map, LongConsSymbolMap) \
- V(Map, short_cons_ascii_symbol_map, ShortConsAsciiSymbolMap) \
- V(Map, medium_cons_ascii_symbol_map, MediumConsAsciiSymbolMap) \
- V(Map, long_cons_ascii_symbol_map, LongConsAsciiSymbolMap) \
- V(Map, short_external_symbol_map, ShortExternalSymbolMap) \
- V(Map, medium_external_symbol_map, MediumExternalSymbolMap) \
- V(Map, long_external_symbol_map, LongExternalSymbolMap) \
- V(Map, short_external_ascii_symbol_map, ShortExternalAsciiSymbolMap) \
- V(Map, medium_external_ascii_symbol_map, MediumExternalAsciiSymbolMap) \
- V(Map, long_external_ascii_symbol_map, LongExternalAsciiSymbolMap) \
- V(Map, short_cons_string_map, ShortConsStringMap) \
- V(Map, medium_cons_string_map, MediumConsStringMap) \
- V(Map, long_cons_string_map, LongConsStringMap) \
- V(Map, short_cons_ascii_string_map, ShortConsAsciiStringMap) \
- V(Map, medium_cons_ascii_string_map, MediumConsAsciiStringMap) \
- V(Map, long_cons_ascii_string_map, LongConsAsciiStringMap) \
- V(Map, short_external_string_map, ShortExternalStringMap) \
- V(Map, medium_external_string_map, MediumExternalStringMap) \
- V(Map, long_external_string_map, LongExternalStringMap) \
- V(Map, short_external_ascii_string_map, ShortExternalAsciiStringMap) \
- V(Map, medium_external_ascii_string_map, MediumExternalAsciiStringMap) \
- V(Map, long_external_ascii_string_map, LongExternalAsciiStringMap) \
- V(Map, undetectable_short_string_map, UndetectableShortStringMap) \
- V(Map, undetectable_medium_string_map, UndetectableMediumStringMap) \
- V(Map, undetectable_long_string_map, UndetectableLongStringMap) \
- V(Map, undetectable_short_ascii_string_map, UndetectableShortAsciiStringMap) \
- V(Map, \
- undetectable_medium_ascii_string_map, \
- UndetectableMediumAsciiStringMap) \
- V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap) \
+ V(Map, string_map, StringMap) \
+ V(Map, ascii_string_map, AsciiStringMap) \
+ V(Map, symbol_map, SymbolMap) \
+ V(Map, ascii_symbol_map, AsciiSymbolMap) \
+ V(Map, cons_symbol_map, ConsSymbolMap) \
+ V(Map, cons_ascii_symbol_map, ConsAsciiSymbolMap) \
+ V(Map, external_symbol_map, ExternalSymbolMap) \
+ V(Map, external_ascii_symbol_map, ExternalAsciiSymbolMap) \
+ V(Map, cons_string_map, ConsStringMap) \
+ V(Map, cons_ascii_string_map, ConsAsciiStringMap) \
+ V(Map, external_string_map, ExternalStringMap) \
+ V(Map, external_ascii_string_map, ExternalAsciiStringMap) \
+ V(Map, undetectable_string_map, UndetectableStringMap) \
+ V(Map, undetectable_ascii_string_map, UndetectableAsciiStringMap) \
V(Map, pixel_array_map, PixelArrayMap) \
V(Map, external_byte_array_map, ExternalByteArrayMap) \
V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \
@@ -219,6 +189,7 @@ namespace internal {
// Forward declaration of the GCTracer class.
class GCTracer;
+class HeapStats;
// The all static Heap captures the interface to the global object heap.
@@ -409,11 +380,11 @@ class Heap : public AllStatic {
// Please note this function does not perform a garbage collection.
static inline Object* AllocateSymbol(Vector<const char> str,
int chars,
- uint32_t length_field);
+ uint32_t hash_field);
static Object* AllocateInternalSymbol(unibrow::CharacterStream* buffer,
int chars,
- uint32_t length_field);
+ uint32_t hash_field);
static Object* AllocateExternalSymbol(Vector<const char> str,
int chars);
@@ -895,6 +866,8 @@ class Heap : public AllStatic {
static RootListIndex RootIndexForExternalArrayType(
ExternalArrayType array_type);
+ static void RecordStats(HeapStats* stats);
+
private:
static int reserved_semispace_size_;
static int max_semispace_size_;
@@ -910,7 +883,10 @@ class Heap : public AllStatic {
static int linear_allocation_scope_depth_;
static bool context_disposed_pending_;
- static const int kMaxMapSpaceSize = 8*MB;
+ // The number of MapSpace pages is limited by the way we pack
+ // Map pointers during GC.
+ static const int kMaxMapSpaceSize =
+ (1 << MapWord::kMapPageIndexBits) * Page::kPageSize;
#if defined(V8_TARGET_ARCH_X64)
static const int kMaxObjectSizeInNewSpace = 512*KB;
@@ -1127,6 +1103,31 @@ class Heap : public AllStatic {
};
+class HeapStats {
+ public:
+ int *start_marker;
+ int *new_space_size;
+ int *new_space_capacity;
+ int *old_pointer_space_size;
+ int *old_pointer_space_capacity;
+ int *old_data_space_size;
+ int *old_data_space_capacity;
+ int *code_space_size;
+ int *code_space_capacity;
+ int *map_space_size;
+ int *map_space_capacity;
+ int *cell_space_size;
+ int *cell_space_capacity;
+ int *lo_space_size;
+ int *global_handle_count;
+ int *weak_global_handle_count;
+ int *pending_global_handle_count;
+ int *near_death_global_handle_count;
+ int *destroyed_global_handle_count;
+ int *end_marker;
+};
+
+
class AlwaysAllocateScope {
public:
AlwaysAllocateScope() {
diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h
index 962206fb7..662ebc902 100644
--- a/deps/v8/src/ia32/assembler-ia32.h
+++ b/deps/v8/src/ia32/assembler-ia32.h
@@ -464,6 +464,8 @@ class Assembler : public Malloced {
// to jump to.
static const int kPatchReturnSequenceAddressOffset = 1; // JMP imm32.
+ static const int kCallInstructionLength = 5;
+ static const int kJSReturnSequenceLength = 6;
// ---------------------------------------------------------------------------
// Code generation
diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc
index ac2a7a026..7c8ff31f6 100644
--- a/deps/v8/src/ia32/codegen-ia32.cc
+++ b/deps/v8/src/ia32/codegen-ia32.cc
@@ -2490,7 +2490,7 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Check that the size of the code used for returning matches what is
// expected by the debugger.
- ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
+ ASSERT_EQ(Assembler::kJSReturnSequenceLength,
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
}
@@ -3056,13 +3056,59 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
jsobject.Bind();
// Get the set of properties (as a FixedArray or Map).
// eax: value to be iterated over
- frame_->EmitPush(eax); // push the object being iterated over (slot 4)
+ frame_->EmitPush(eax); // Push the object being iterated over.
+ // Check cache validity in generated code. This is a fast case for
+ // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+ // guarantee cache validity, call the runtime system to check cache
+ // validity or get the property names in a fixed array.
+ JumpTarget call_runtime;
+ JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ JumpTarget check_prototype;
+ JumpTarget use_cache;
+ __ mov(ecx, eax);
+ loop.Bind();
+ // Check that there are no elements.
+ __ mov(edx, FieldOperand(ecx, JSObject::kElementsOffset));
+ __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
+ call_runtime.Branch(not_equal);
+ // Check that instance descriptors are not empty so that we can
+ // check for an enum cache. Leave the map in ebx for the subsequent
+ // prototype load.
+ __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
+ __ cmp(Operand(edx), Immediate(Factory::empty_descriptor_array()));
+ call_runtime.Branch(equal);
+ // Check that there in an enum cache in the non-empty instance
+ // descriptors. This is the case if the next enumeration index
+ // field does not contain a smi.
+ __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset));
+ __ test(edx, Immediate(kSmiTagMask));
+ call_runtime.Branch(zero);
+ // For all objects but the receiver, check that the cache is empty.
+ __ cmp(ecx, Operand(eax));
+ check_prototype.Branch(equal);
+ __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
+ __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
+ call_runtime.Branch(not_equal);
+ check_prototype.Bind();
+ // Load the prototype from the map and loop if non-null.
+ __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
+ __ cmp(Operand(ecx), Immediate(Factory::null_value()));
+ loop.Branch(not_equal);
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
+ use_cache.Jump();
+
+ call_runtime.Bind();
+ // Call the runtime to get the property names for the object.
frame_->EmitPush(eax); // push the Object (slot 4) for the runtime call
frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
- // If we got a Map, we can do a fast modification check.
- // Otherwise, we got a FixedArray, and we have to do a slow check.
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
// eax: map or fixed array (result from call to
// Runtime::kGetPropertyNamesFast)
__ mov(edx, Operand(eax));
@@ -3070,9 +3116,13 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
__ cmp(ecx, Factory::meta_map());
fixed_array.Branch(not_equal);
+ use_cache.Bind();
// Get enum cache
- // eax: map (result from call to Runtime::kGetPropertyNamesFast)
+ // eax: map (either the result from a call to
+ // Runtime::kGetPropertyNamesFast or has been fetched directly from
+ // the object)
__ mov(ecx, Operand(eax));
+
__ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset));
// Get the bridge array held in the enumeration index field.
__ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
@@ -4777,18 +4827,8 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
__ test(ecx, Immediate(kIsNotStringMask));
__ j(not_zero, &slow_case);
- // Here we make assumptions about the tag values and the shifts needed.
- // See the comment in objects.h.
- ASSERT(kLongStringTag == 0);
- ASSERT(kMediumStringTag + String::kLongLengthShift ==
- String::kMediumLengthShift);
- ASSERT(kShortStringTag + String::kLongLengthShift ==
- String::kShortLengthShift);
- __ and_(ecx, kStringSizeMask);
- __ add(Operand(ecx), Immediate(String::kLongLengthShift));
// Fetch the length field into the temporary register.
__ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
- __ shr_cl(temp.reg());
// Check for index out of range.
__ cmp(index.reg(), Operand(temp.reg()));
__ j(greater_equal, &slow_case);
@@ -5222,6 +5262,18 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
+ ASSERT_EQ(2, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+
+ StringAddStub stub(NO_STRING_ADD_FLAGS);
+ Result answer = frame_->CallStub(&stub, 2);
+ frame_->Push(&answer);
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
@@ -6502,11 +6554,8 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
// String value => false iff empty.
__ cmp(ecx, FIRST_NONSTRING_TYPE);
__ j(above_equal, &not_string);
- __ and_(ecx, kStringSizeMask);
- __ cmp(ecx, kShortStringTag);
- __ j(not_equal, &true_result); // Empty string is always short.
__ mov(edx, FieldOperand(eax, String::kLengthOffset));
- __ shr(edx, String::kShortLengthShift);
+ __ test(edx, Operand(edx));
__ j(zero, &false_result);
__ jmp(&true_result);
@@ -7042,7 +7091,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
switch (op_) {
case Token::ADD: {
// Test for string arguments before calling runtime.
- Label not_strings, both_strings, not_string1, string1;
+ Label not_strings, not_string1, string1;
Result answer;
__ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
__ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
@@ -7057,8 +7106,9 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, edx);
__ j(above_equal, &string1);
- // First and second argument are strings.
- __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+ // First and second argument are strings. Jump to the string add stub.
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ __ TailCallStub(&stub);
// Only first argument is a string.
__ bind(&string1);
@@ -8185,6 +8235,224 @@ int CompareStub::MinorKey() {
return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0);
}
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label string_add_runtime;
+
+ // Load the two arguments.
+ __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
+ __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ if (string_check_) {
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &string_add_runtime);
+ __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
+ __ j(above_equal, &string_add_runtime);
+
+ // First argument is a a string, test second.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &string_add_runtime);
+ __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
+ __ j(above_equal, &string_add_runtime);
+ }
+
+ // Both arguments are strings.
+ // eax: first string
+ // edx: second string
+ // Check if either of the strings are empty. In that case return the other.
+ Label second_not_zero_length, both_not_zero_length;
+ __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
+ __ test(ecx, Operand(ecx));
+ __ j(not_zero, &second_not_zero_length);
+ // Second string is empty, result is first string which is already in eax.
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&second_not_zero_length);
+ __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
+ __ test(ebx, Operand(ebx));
+ __ j(not_zero, &both_not_zero_length);
+ // First string is empty, result is second string which is in edx.
+ __ mov(eax, edx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Both strings are non-empty.
+ // eax: first string
+ // ebx: length of first string
+ // ecx: length of second string
+ // edx: second string
+ // Look at the length of the result of adding the two strings.
+ Label string_add_flat_result;
+ __ bind(&both_not_zero_length);
+ __ add(ebx, Operand(ecx));
+ // Use the runtime system when adding two one character strings, as it
+ // contains optimizations for this specific case using the symbol table.
+ __ cmp(ebx, 2);
+ __ j(equal, &string_add_runtime);
+ // Check if resulting string will be flat.
+ __ cmp(ebx, String::kMinNonFlatLength);
+ __ j(below, &string_add_flat_result);
+ // Handle exceptionally long strings in the runtime system.
+ ASSERT((String::kMaxLength & 0x80000000) == 0);
+ __ cmp(ebx, String::kMaxLength);
+ __ j(above, &string_add_runtime);
+
+ // If result is not supposed to be flat allocate a cons string object. If both
+ // strings are ascii the result is an ascii cons string.
+ Label non_ascii, allocated;
+ __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
+ __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
+ __ and_(ecx, Operand(edi));
+ __ test(ecx, Immediate(kAsciiStringTag));
+ __ j(zero, &non_ascii);
+ // Allocate an acsii cons string.
+ __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
+ __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+ __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
+ __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
+ __ mov(eax, ecx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&non_ascii);
+ // Allocate a two byte cons string.
+ __ AllocateConsString(ecx, edi, no_reg, &string_add_runtime);
+ __ jmp(&allocated);
+
+ // Handle creating a flat result. First check that both strings are not
+ // external strings.
+ // eax: first string
+ // ebx: length of resulting flat string
+ // edx: second string
+ __ bind(&string_add_flat_result);
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ and_(ecx, kStringRepresentationMask);
+ __ cmp(ecx, kExternalStringTag);
+ __ j(equal, &string_add_runtime);
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ and_(ecx, kStringRepresentationMask);
+ __ cmp(ecx, kExternalStringTag);
+ __ j(equal, &string_add_runtime);
+ // Now check if both strings are ascii strings.
+ // eax: first string
+ // ebx: length of resulting flat string
+ // edx: second string
+ Label non_ascii_string_add_flat_result;
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ ASSERT(kAsciiStringTag != 0);
+ __ test(ecx, Immediate(kAsciiStringTag));
+ __ j(zero, &non_ascii_string_add_flat_result);
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ test(ecx, Immediate(kAsciiStringTag));
+ __ j(zero, &string_add_runtime);
+ // Both strings are ascii strings. As they are short they are both flat.
+ __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
+ // eax: result string
+ __ mov(ecx, eax);
+ // Locate first character of result.
+ __ add(Operand(ecx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // Load first argument and locate first character.
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+ __ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // eax: result string
+ // ecx: first character of result
+ // edx: first char of first argument
+ // edi: length of first argument
+ GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
+ // Load second argument and locate first character.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+ __ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // eax: result string
+ // ecx: next character of result
+ // edx: first char of second argument
+ // edi: length of second argument
+ GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Handle creating a flat two byte result.
+ // eax: first string - known to be two byte
+ // ebx: length of resulting flat string
+ // edx: second string
+ __ bind(&non_ascii_string_add_flat_result);
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ and_(ecx, kAsciiStringTag);
+ __ j(not_zero, &string_add_runtime);
+ // Both strings are two byte strings. As they are short they are both
+ // flat.
+ __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
+ // eax: result string
+ __ mov(ecx, eax);
+ // Locate first character of result.
+ __ add(Operand(ecx),
+ Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // Load first argument and locate first character.
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+ __ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ add(Operand(edx),
+ Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // eax: result string
+ // ecx: first character of result
+ // edx: first char of first argument
+ // edi: length of first argument
+ GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
+ // Load second argument and locate first character.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+ __ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // eax: result string
+ // ecx: next character of result
+ // edx: first char of second argument
+ // edi: length of second argument
+ GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Just jump to runtime to add the two strings.
+ __ bind(&string_add_runtime);
+ __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+}
+
+
+void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
+ Label loop;
+ __ bind(&loop);
+ // This loop just copies one character at a time, as it is only used for very
+ // short strings.
+ if (ascii) {
+ __ mov_b(scratch, Operand(src, 0));
+ __ mov_b(Operand(dest, 0), scratch);
+ __ add(Operand(src), Immediate(1));
+ __ add(Operand(dest), Immediate(1));
+ } else {
+ __ mov_w(scratch, Operand(src, 0));
+ __ mov_w(Operand(dest, 0), scratch);
+ __ add(Operand(src), Immediate(2));
+ __ add(Operand(dest), Immediate(2));
+ }
+ __ sub(Operand(count), Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h
index ebab3caa1..11a5163db 100644
--- a/deps/v8/src/ia32/codegen-ia32.h
+++ b/deps/v8/src/ia32/codegen-ia32.h
@@ -546,6 +546,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
+ // Fast support for StringAdd.
+ void GenerateStringAdd(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@@ -737,6 +740,37 @@ class GenericBinaryOpStub: public CodeStub {
};
+// Flag that indicates how to generate code for the stub StringAddStub.
+enum StringAddFlags {
+ NO_STRING_ADD_FLAGS = 0,
+ NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub.
+};
+
+
+class StringAddStub: public CodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) {
+ string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
+ }
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return string_check_ ? 0 : 1; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateCopyCharacters(MacroAssembler* masm,
+ Register desc,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii);
+
+ // Should the stub check whether arguments are strings?
+ bool string_check_;
+};
+
+
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_
diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc
index 2d20117aa..5ebe1e070 100644
--- a/deps/v8/src/ia32/debug-ia32.cc
+++ b/deps/v8/src/ia32/debug-ia32.cc
@@ -45,17 +45,17 @@ bool BreakLocationIterator::IsDebugBreakAtReturn() {
// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc
// for the precise return instructions sequence.
void BreakLocationIterator::SetDebugBreakAtReturn() {
- ASSERT(Debug::kIa32JSReturnSequenceLength >=
- Debug::kIa32CallInstructionLength);
+ ASSERT(Assembler::kJSReturnSequenceLength >=
+ Assembler::kCallInstructionLength);
rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(),
- Debug::kIa32JSReturnSequenceLength - Debug::kIa32CallInstructionLength);
+ Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
}
// Restore the JS frame exit code.
void BreakLocationIterator::ClearDebugBreakAtReturn() {
rinfo()->PatchCode(original_rinfo()->pc(),
- Debug::kIa32JSReturnSequenceLength);
+ Assembler::kJSReturnSequenceLength);
}
diff --git a/deps/v8/src/ia32/fast-codegen-ia32.cc b/deps/v8/src/ia32/fast-codegen-ia32.cc
index a01d754e4..c5d544127 100644
--- a/deps/v8/src/ia32/fast-codegen-ia32.cc
+++ b/deps/v8/src/ia32/fast-codegen-ia32.cc
@@ -74,11 +74,41 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
bool function_in_register = true;
+ // Possibly allocate a local context.
+ if (fun->scope()->num_heap_slots() > 0) {
+ Comment cmnt(masm_, "[ Allocate local context");
+ // Argument to NewContext is the function, which is still in edi.
+ __ push(edi);
+ __ CallRuntime(Runtime::kNewContext, 1);
+ function_in_register = false;
+ // Context is returned in both eax and esi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in esi.
+ __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
+
+ // Copy parameters into context if necessary.
+ int num_parameters = fun->scope()->num_parameters();
+ for (int i = 0; i < num_parameters; i++) {
+ Slot* slot = fun->scope()->parameter(i)->slot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
+ (num_parameters - 1 - i) * kPointerSize;
+ // Load parameter from stack.
+ __ mov(eax, Operand(ebp, parameter_offset));
+ // Store it in the context
+ __ mov(Operand(esi, Context::SlotOffset(slot->index())), eax);
+ }
+ }
+ }
+
Variable* arguments = fun->scope()->arguments()->AsVariable();
if (arguments != NULL) {
// Function uses arguments object.
Comment cmnt(masm_, "[ Allocate arguments object");
- __ push(edi);
+ if (function_in_register) {
+ __ push(edi);
+ } else {
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ }
// Receiver is just before the parameters on the caller's stack.
__ lea(edx, Operand(ebp, StandardFrameConstants::kCallerSPOffset +
fun->num_parameters() * kPointerSize));
@@ -90,36 +120,13 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
// stack frame was an arguments adapter frame.
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
__ CallStub(&stub);
- __ mov(Operand(ebp, SlotOffset(arguments->slot())), eax);
+ __ mov(ecx, eax); // Duplicate result.
+ Move(arguments->slot(), eax, ebx, edx);
Slot* dot_arguments_slot =
fun->scope()->arguments_shadow()->AsVariable()->slot();
- __ mov(Operand(ebp, SlotOffset(dot_arguments_slot)), eax);
-
- function_in_register = false;
+ Move(dot_arguments_slot, ecx, ebx, edx);
}
- // Possibly allocate a local context.
- if (fun->scope()->num_heap_slots() > 0) {
- Comment cmnt(masm_, "[ Allocate local context");
- if (function_in_register) {
- // Argument to NewContext is the function, still in edi.
- __ push(edi);
- } else {
- // Argument to NewContext is the function, no longer in edi.
- __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
- }
- __ CallRuntime(Runtime::kNewContext, 1);
- // Context is returned in both eax and esi. It replaces the context
- // passed to us. It's saved in the stack and kept live in esi.
- __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
-#ifdef DEBUG
- // Assert we do not have to copy any parameters into the context.
- for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) {
- Slot* slot = fun->scope()->parameter(i)->slot();
- ASSERT(slot != NULL && slot->type() != Slot::CONTEXT);
- }
-#endif
- }
{ Comment cmnt(masm_, "[ Declarations");
VisitDeclarations(fun->scope()->declarations());
@@ -180,7 +187,7 @@ void FastCodeGenerator::EmitReturnSequence(int position) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Check that the size of the code used for returning matches what is
// expected by the debugger.
- ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
+ ASSERT_EQ(Assembler::kJSReturnSequenceLength,
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
}
@@ -220,20 +227,54 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
-void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
+template <>
+Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source,
+ Register scratch) {
+ switch (source->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ return Operand(ebp, SlotOffset(source));
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ function_->scope()->ContextChainLength(source->var()->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return CodeGenerator::ContextOperand(scratch, source->index());
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ // Fall-through.
+ default:
+ UNREACHABLE();
+ return Operand(eax, 0); // Dead code to make the compiler happy.
+ }
+}
+
+
+void FastCodeGenerator::Move(Register dst, Slot* source) {
+ Operand location = CreateSlotOperand<Operand>(source, dst);
+ __ mov(dst, location);
+}
+
+
+void FastCodeGenerator::Move(Expression::Context context,
+ Slot* source,
+ Register scratch) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
break;
- case Expression::kValue:
- __ push(Operand(ebp, SlotOffset(source)));
+ case Expression::kValue: {
+ Operand location = CreateSlotOperand<Operand>(source, scratch);
+ __ push(location);
break;
+ }
case Expression::kTest: // Fall through.
case Expression::kValueTest: // Fall through.
case Expression::kTestValue:
- __ mov(eax, Operand(ebp, SlotOffset(source)));
- Move(context, eax);
+ Move(scratch, source);
+ Move(context, scratch);
break;
}
}
@@ -258,24 +299,61 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
}
+void FastCodeGenerator::Move(Slot* dst,
+ Register src,
+ Register scratch1,
+ Register scratch2) {
+ switch (dst->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ __ mov(Operand(ebp, SlotOffset(dst)), src);
+ break;
+ case Slot::CONTEXT: {
+ ASSERT(!src.is(scratch1));
+ ASSERT(!src.is(scratch2));
+ ASSERT(!scratch1.is(scratch2));
+ int context_chain_length =
+ function_->scope()->ContextChainLength(dst->var()->scope());
+ __ LoadContext(scratch1, context_chain_length);
+ __ mov(Operand(scratch1, Context::SlotOffset(dst->index())), src);
+ int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
+ __ RecordWrite(scratch1, offset, src, scratch2);
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ default:
+ UNREACHABLE();
+ }
+}
+
+
void FastCodeGenerator::DropAndMove(Expression::Context context,
- Register source) {
+ Register source,
+ int count) {
+ ASSERT(count > 0);
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
- __ add(Operand(esp), Immediate(kPointerSize));
+ __ add(Operand(esp), Immediate(count * kPointerSize));
break;
case Expression::kValue:
+ if (count > 1) {
+ __ add(Operand(esp), Immediate((count - 1) * kPointerSize));
+ }
__ mov(Operand(esp, 0), source);
break;
case Expression::kTest:
ASSERT(!source.is(esp));
- __ add(Operand(esp), Immediate(kPointerSize));
+ __ add(Operand(esp), Immediate(count * kPointerSize));
TestAndBranch(source, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
+ if (count > 1) {
+ __ add(Operand(esp), Immediate((count - 1) * kPointerSize));
+ }
__ mov(Operand(esp, 0), source);
TestAndBranch(source, true_label_, &discard);
__ bind(&discard);
@@ -285,6 +363,9 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
}
case Expression::kTestValue: {
Label discard;
+ if (count > 1) {
+ __ add(Operand(esp), Immediate((count - 1) * kPointerSize));
+ }
__ mov(Operand(esp, 0), source);
TestAndBranch(source, &discard, false_label_);
__ bind(&discard);
@@ -380,6 +461,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
}
__ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax);
// No write barrier since the_hole_value is in old space.
+ ASSERT(!Heap::InNewSpace(*Factory::the_hole_value()));
} else if (decl->fun() != NULL) {
Visit(decl->fun());
__ pop(eax);
@@ -391,7 +473,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
__ Check(equal, "Unexpected declaration in current context.");
}
__ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax);
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+ int offset = Context::SlotOffset(slot->index());
__ RecordWrite(esi, offset, eax, ecx);
}
break;
@@ -464,53 +546,61 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
DropAndMove(expr->context(), eax);
} else if (rewrite->AsSlot() != NULL) {
Slot* slot = rewrite->AsSlot();
- switch (slot->type()) {
- case Slot::LOCAL:
- case Slot::PARAMETER: {
- Comment cmnt(masm_, "Stack slot");
- Move(expr->context(), slot);
- break;
- }
-
- case Slot::CONTEXT: {
- Comment cmnt(masm_, "Context slot");
- int chain_length =
- function_->scope()->ContextChainLength(slot->var()->scope());
- if (chain_length > 0) {
- // Move up the chain of contexts to the context containing the slot.
- __ mov(eax,
- Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX)));
- // Load the function context (which is the incoming, outer context).
- __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset));
- for (int i = 1; i < chain_length; i++) {
- __ mov(eax,
- Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)));
- __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset));
- }
- // The context may be an intermediate context, not a function context.
- __ mov(eax,
- Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX)));
- } else { // Slot is in the current function context.
- // The context may be an intermediate context, not a function context.
- __ mov(eax,
- Operand(esi, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ if (FLAG_debug_code) {
+ switch (slot->type()) {
+ case Slot::LOCAL:
+ case Slot::PARAMETER: {
+ Comment cmnt(masm_, "Stack slot");
+ break;
}
- __ mov(eax, Operand(eax, Context::SlotOffset(slot->index())));
- Move(expr->context(), eax);
- break;
+ case Slot::CONTEXT: {
+ Comment cmnt(masm_, "Context slot");
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ break;
+ default:
+ UNREACHABLE();
}
-
- case Slot::LOOKUP:
- UNREACHABLE();
- break;
}
+ Move(expr->context(), slot, eax);
} else {
- // The parameter variable has been rewritten into an explict access to
- // the arguments object.
+ Comment cmnt(masm_, "Variable rewritten to Property");
+ // A variable has been rewritten into an explicit access to
+ // an object property.
Property* property = rewrite->AsProperty();
ASSERT_NOT_NULL(property);
- ASSERT_EQ(expr->context(), property->context());
- Visit(property);
+
+ // Currently the only parameter expressions that can occur are
+ // on the form "slot[literal]".
+
+ // Check that the object is in a slot.
+ Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object_var);
+ Slot* object_slot = object_var->slot();
+ ASSERT_NOT_NULL(object_slot);
+
+ // Load the object.
+ Move(Expression::kValue, object_slot, eax);
+
+ // Check that the key is a smi.
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ ASSERT(key_literal->handle()->IsSmi());
+
+ // Load the key.
+ Move(Expression::kValue, key_literal);
+
+ // Do a KEYED property load.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // Notice: We must not have a "test eax, ..." instruction after
+ // the call. It is treated specially by the LoadIC code.
+ __ nop();
+
+ // Drop key and object left on the stack by IC, and push the result.
+ DropAndMove(expr->context(), eax, 2);
}
}
@@ -575,8 +665,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1);
}
- // If result_saved == true: the result is saved on top of the stack.
- // If result_saved == false: the result not on the stack, just is in eax.
+ // If result_saved == true: The result is saved on top of the
+ // stack and in eax.
+ // If result_saved == false: The result not on the stack, just in eax.
bool result_saved = false;
for (int i = 0; i < expr->properties()->length(); i++) {
@@ -601,6 +692,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// StoreIC leaves the receiver on the stack.
+ __ mov(eax, Operand(esp, 0)); // Restore result into eax.
break;
}
// fall through
@@ -776,34 +868,34 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
// Overwrite the receiver on the stack with the result if needed.
DropAndMove(expr->context(), eax);
- } else {
+ } else if (var->slot() != NULL) {
Slot* slot = var->slot();
- ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled.
switch (slot->type()) {
case Slot::LOCAL:
case Slot::PARAMETER: {
+ Operand target = Operand(ebp, SlotOffset(var->slot()));
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
// Perform assignment and discard value.
- __ pop(Operand(ebp, SlotOffset(var->slot())));
+ __ pop(target);
break;
case Expression::kValue:
// Perform assignment and preserve value.
__ mov(eax, Operand(esp, 0));
- __ mov(Operand(ebp, SlotOffset(var->slot())), eax);
+ __ mov(target, eax);
break;
case Expression::kTest:
// Perform assignment and test (and discard) value.
__ pop(eax);
- __ mov(Operand(ebp, SlotOffset(var->slot())), eax);
+ __ mov(target, eax);
TestAndBranch(eax, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
__ mov(eax, Operand(esp, 0));
- __ mov(Operand(ebp, SlotOffset(var->slot())), eax);
+ __ mov(target, eax);
TestAndBranch(eax, true_label_, &discard);
__ bind(&discard);
__ add(Operand(esp), Immediate(kPointerSize));
@@ -813,7 +905,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
case Expression::kTestValue: {
Label discard;
__ mov(eax, Operand(esp, 0));
- __ mov(Operand(ebp, SlotOffset(var->slot())), eax);
+ __ mov(target, eax);
TestAndBranch(eax, &discard, false_label_);
__ bind(&discard);
__ add(Operand(esp), Immediate(kPointerSize));
@@ -868,6 +960,35 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
UNREACHABLE();
break;
}
+ } else {
+ Property* property = var->rewrite()->AsProperty();
+ ASSERT_NOT_NULL(property);
+
+ // Load object and key onto the stack.
+ Slot* object_slot = property->obj()->AsSlot();
+ ASSERT_NOT_NULL(object_slot);
+ Move(Expression::kValue, object_slot, eax);
+
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ Move(Expression::kValue, key_literal);
+
+ // Value to store was pushed before object and key on the stack.
+ __ mov(eax, Operand(esp, 2 * kPointerSize));
+
+ // Arguments to ic is value in eax, object and key on stack.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+
+ if (expr->context() == Expression::kEffect) {
+ __ add(Operand(esp), Immediate(3 * kPointerSize));
+ } else if (expr->context() == Expression::kValue) {
+ // Value is still on the stack in esp[2 * kPointerSize]
+ __ add(Operand(esp), Immediate(2 * kPointerSize));
+ } else {
+ __ mov(eax, Operand(esp, 2 * kPointerSize));
+ DropAndMove(expr->context(), eax, 3);
+ }
}
}
@@ -1127,9 +1248,13 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) {
void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Comment cmnt(masm_, "[ CallRuntime");
ZoneList<Expression*>* args = expr->arguments();
- Runtime::Function* function = expr->function();
- ASSERT(function != NULL);
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ push(Immediate(expr->name()));
+ __ mov(eax, CodeGenerator::GlobalObject());
+ __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
+ }
// Push the arguments ("left-to-right").
int arg_count = args->length();
@@ -1138,8 +1263,20 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
ASSERT_EQ(Expression::kValue, args->at(i)->context());
}
- __ CallRuntime(function, arg_count);
- Move(expr->context(), eax);
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP);
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ // Discard the function left on TOS.
+ DropAndMove(expr->context(), eax);
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ Move(expr->context(), eax);
+ }
}
diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc
index 3aa3c3467..6988fe09f 100644
--- a/deps/v8/src/ia32/ic-ia32.cc
+++ b/deps/v8/src/ia32/ic-ia32.cc
@@ -31,6 +31,7 @@
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
+#include "utils.h"
namespace v8 {
namespace internal {
@@ -108,7 +109,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
StringDictionary::kElementsStartIndex * kPointerSize;
for (int i = 0; i < kProbes; i++) {
// Compute the masked index: (hash + i + i * i) & mask.
- __ mov(r1, FieldOperand(name, String::kLengthOffset));
+ __ mov(r1, FieldOperand(name, String::kHashFieldOffset));
__ shr(r1, String::kHashShift);
if (i > 0) {
__ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i)));
@@ -216,18 +217,6 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
}
-#ifdef DEBUG
-// For use in assert below.
-static int TenToThe(int exponent) {
- ASSERT(exponent <= 9);
- ASSERT(exponent >= 1);
- int answer = 10;
- for (int i = 1; i < exponent; i++) answer *= 10;
- return answer;
-}
-#endif
-
-
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- esp[0] : return address
@@ -309,7 +298,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx);
__ j(above_equal, &slow);
// Is the string an array index, with cached numeric value?
- __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
+ __ mov(ebx, FieldOperand(eax, String::kHashFieldOffset));
__ test(ebx, Immediate(String::kIsArrayIndexMask));
__ j(not_zero, &index_string, not_taken);
@@ -324,20 +313,16 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ mov(eax, Operand(ecx));
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
- // Array index string: If short enough use cache in length/hash field (ebx).
- // We assert that there are enough bits in an int32_t after the hash shift
- // bits have been subtracted to allow space for the length and the cached
- // array index.
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << (String::kShortLengthShift - String::kHashShift)));
+ (1 << String::kArrayIndexValueBits));
__ bind(&index_string);
- const int kLengthFieldLimit =
- (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift;
- __ cmp(ebx, kLengthFieldLimit);
- __ j(above_equal, &slow);
__ mov(eax, Operand(ebx));
- __ and_(eax, (1 << String::kShortLengthShift) - 1);
- __ shr(eax, String::kLongLengthShift);
+ __ and_(eax, String::kArrayIndexHashMask);
+ __ shr(eax, String::kHashShift);
__ jmp(&index_int);
}
@@ -403,13 +388,13 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
__ movsx_b(eax, Operand(ecx, eax, times_1, 0));
break;
case kExternalUnsignedByteArray:
- __ mov_b(eax, Operand(ecx, eax, times_1, 0));
+ __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
break;
case kExternalShortArray:
__ movsx_w(eax, Operand(ecx, eax, times_2, 0));
break;
case kExternalUnsignedShortArray:
- __ mov_w(eax, Operand(ecx, eax, times_2, 0));
+ __ movzx_w(eax, Operand(ecx, eax, times_2, 0));
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc
index 010433e16..b91caa8cc 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.cc
+++ b/deps/v8/src/ia32/macro-assembler-ia32.cc
@@ -213,6 +213,13 @@ void MacroAssembler::RecordWrite(Register object, int offset,
}
+void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
+ cmp(esp,
+ Operand::StaticVariable(ExternalReference::address_of_stack_limit()));
+ j(below, on_stack_overflow);
+}
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
@@ -680,6 +687,11 @@ void MacroAssembler::LoadAllocationTopHelper(Register result,
void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
Register scratch) {
+ if (FLAG_debug_code) {
+ test(result_end, Immediate(kObjectAlignmentMask));
+ Check(zero, "Unaligned allocation in new space");
+ }
+
ExternalReference new_space_allocation_top =
ExternalReference::new_space_allocation_top_address();
@@ -813,6 +825,109 @@ void MacroAssembler::AllocateHeapNumber(Register result,
}
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ mov(scratch1, length);
+ ASSERT(kShortSize == 2);
+ shl(scratch1, 1);
+ add(Operand(scratch1), Immediate(kObjectAlignmentMask));
+ and_(Operand(scratch1), Immediate(~kObjectAlignmentMask));
+
+ // Allocate two byte string in new space.
+ AllocateInNewSpace(SeqTwoByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(Factory::string_map()));
+ mov(FieldOperand(result, String::kLengthOffset), length);
+ mov(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0);
+ mov(scratch1, length);
+ ASSERT(kCharSize == 1);
+ add(Operand(scratch1), Immediate(kObjectAlignmentMask));
+ and_(Operand(scratch1), Immediate(~kObjectAlignmentMask));
+
+ // Allocate ascii string in new space.
+ AllocateInNewSpace(SeqAsciiString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(Factory::ascii_string_map()));
+ mov(FieldOperand(result, String::kLengthOffset), length);
+ mov(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ AllocateInNewSpace(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(Factory::cons_string_map()));
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ AllocateInNewSpace(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(Factory::cons_ascii_string_map()));
+}
+
+
void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen,
Register result,
Register op,
@@ -906,6 +1021,12 @@ void MacroAssembler::CallStub(CodeStub* stub) {
}
+void MacroAssembler::TailCallStub(CodeStub* stub) {
+ ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
+ jmp(stub->GetCode(), RelocInfo::CODE_TARGET);
+}
+
+
void MacroAssembler::StubReturn(int argc) {
ASSERT(argc >= 1 && generating_stub());
ret((argc - 1) * kPointerSize);
@@ -1185,6 +1306,26 @@ Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
}
+void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
+ if (context_chain_length > 0) {
+ // Move up the chain of contexts to the context containing the slot.
+ mov(dst, Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ // Load the function context (which is the incoming, outer context).
+ mov(dst, FieldOperand(dst, JSFunction::kContextOffset));
+ for (int i = 1; i < context_chain_length; i++) {
+ mov(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ mov(dst, FieldOperand(dst, JSFunction::kContextOffset));
+ }
+ // The context may be an intermediate context, not a function context.
+ mov(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ } else { // Slot is in the current function context.
+ // The context may be an intermediate context, not a function context.
+ mov(dst, Operand(esi, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ }
+}
+
+
+
void MacroAssembler::Ret() {
ret(0);
}
@@ -1252,11 +1393,15 @@ void MacroAssembler::Abort(const char* msg) {
RecordComment(msg);
}
#endif
+ // Disable stub call restrictions to always allow calls to abort.
+ set_allow_stub_calls(true);
+
push(eax);
push(Immediate(p0));
push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0))));
CallRuntime(Runtime::kAbort, 2);
// will not return here
+ int3();
}
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h
index 248aa7776..a41d42e82 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.h
+++ b/deps/v8/src/ia32/macro-assembler-ia32.h
@@ -69,6 +69,12 @@ class MacroAssembler: public Assembler {
#endif
// ---------------------------------------------------------------------------
+ // Stack limit support
+
+ // Do simple test for stack overflow. This doesn't handle an overflow.
+ void StackLimitCheck(Label* on_stack_limit_hit);
+
+ // ---------------------------------------------------------------------------
// Activation frames
void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); }
@@ -90,6 +96,8 @@ class MacroAssembler: public Assembler {
// argument in register esi.
void LeaveExitFrame(ExitFrame::Mode mode);
+ // Find the function context up the context chain.
+ void LoadContext(Register dst, int context_chain_length);
// ---------------------------------------------------------------------------
// JavaScript invokes
@@ -175,7 +183,7 @@ class MacroAssembler: public Assembler {
// scratch can be passed as no_reg in which case an additional object
// reference will be added to the reloc info. The returned pointers in result
// and result_end have not yet been tagged as heap objects. If
- // result_contains_top_on_entry is true the contnt of result is known to be
+ // result_contains_top_on_entry is true the content of result is known to be
// the allocation top on entry (could be result_end from a previous call to
// AllocateInNewSpace). If result_contains_top_on_entry is true scratch
// should be no_reg as it is never used.
@@ -217,6 +225,32 @@ class MacroAssembler: public Assembler {
Register scratch2,
Label* gc_required);
+ // Allocate a sequential string. All the header fields of the string object
+ // are initialized.
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+
+ // Allocate a raw cons string object. Only the map field of the result is
+ // initialized.
+ void AllocateConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
// ---------------------------------------------------------------------------
// Support functions.
@@ -254,6 +288,9 @@ class MacroAssembler: public Assembler {
// Call a code stub.
void CallStub(CodeStub* stub);
+ // Tail call a code stub (jump).
+ void TailCallStub(CodeStub* stub);
+
// Return from a code stub after popping its arguments.
void StubReturn(int argc);
diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc
index f9f986afe..425c51dca 100644
--- a/deps/v8/src/ia32/stub-cache-ia32.cc
+++ b/deps/v8/src/ia32/stub-cache-ia32.cc
@@ -126,7 +126,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
__ j(zero, &miss, not_taken);
// Get the map of the receiver and compute the hash.
- __ mov(scratch, FieldOperand(name, String::kLengthOffset));
+ __ mov(scratch, FieldOperand(name, String::kHashFieldOffset));
__ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, flags);
__ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
@@ -135,7 +135,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ProbeTable(masm, flags, kPrimary, name, scratch, extra);
// Primary miss: Compute hash for secondary probe.
- __ mov(scratch, FieldOperand(name, String::kLengthOffset));
+ __ mov(scratch, FieldOperand(name, String::kHashFieldOffset));
__ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, flags);
__ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
@@ -234,13 +234,9 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
// scratch register.
GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper);
- // Load length directly from the string.
+ // Load length from the string and convert to a smi.
__ bind(&load_length);
- __ and_(scratch, kStringSizeMask);
__ mov(eax, FieldOperand(receiver, String::kLengthOffset));
- // ecx is also the receiver.
- __ lea(ecx, Operand(scratch, String::kLongLengthShift));
- __ shr_cl(eax);
__ shl(eax, kSmiTagSize);
__ ret(0);
diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc
index aec813d97..bbce926c8 100644
--- a/deps/v8/src/log.cc
+++ b/deps/v8/src/log.cc
@@ -680,22 +680,51 @@ class CompressionHelper {
#endif // ENABLE_LOGGING_AND_PROFILING
-void Logger::CallbackEvent(String* name, Address entry_point) {
#ifdef ENABLE_LOGGING_AND_PROFILING
+void Logger::CallbackEventInternal(const char* prefix, const char* name,
+ Address entry_point) {
if (!Log::IsEnabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("%s,%s,",
log_events_[CODE_CREATION_EVENT], log_events_[CALLBACK_TAG]);
msg.AppendAddress(entry_point);
- SmartPointer<char> str =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
- msg.Append(",1,\"%s\"", *str);
+ msg.Append(",1,\"%s%s\"", prefix, name);
if (FLAG_compress_log) {
ASSERT(compression_helper_ != NULL);
if (!compression_helper_->HandleMessage(&msg)) return;
}
msg.Append('\n');
msg.WriteToLogFile();
+}
+#endif
+
+
+void Logger::CallbackEvent(String* name, Address entry_point) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!Log::IsEnabled() || !FLAG_log_code) return;
+ SmartPointer<char> str =
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ CallbackEventInternal("", *str, entry_point);
+#endif
+}
+
+
+void Logger::GetterCallbackEvent(String* name, Address entry_point) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!Log::IsEnabled() || !FLAG_log_code) return;
+ SmartPointer<char> str =
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ CallbackEventInternal("get ", *str, entry_point);
+#endif
+}
+
+
+void Logger::SetterCallbackEvent(String* name, Address entry_point) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!Log::IsEnabled() || !FLAG_log_code) return;
+ SmartPointer<char> str =
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ CallbackEventInternal("set ", *str, entry_point);
#endif
}
@@ -1098,6 +1127,7 @@ void Logger::ResumeProfiler(int flags) {
LOG(UncheckedStringEvent("profiler", "resume"));
FLAG_log_code = true;
LogCompiledFunctions();
+ LogAccessorCallbacks();
if (!FLAG_sliding_state_window) ticker_->Start();
}
profiler_->resume();
@@ -1242,6 +1272,28 @@ void Logger::LogCompiledFunctions() {
DeleteArray(sfis);
}
+
+void Logger::LogAccessorCallbacks() {
+ AssertNoAllocation no_alloc;
+ HeapIterator iterator;
+ while (iterator.has_next()) {
+ HeapObject* obj = iterator.next();
+ ASSERT(obj != NULL);
+ if (!obj->IsAccessorInfo()) continue;
+ AccessorInfo* ai = AccessorInfo::cast(obj);
+ if (!ai->name()->IsString()) continue;
+ String* name = String::cast(ai->name());
+ Address getter_entry = v8::ToCData<Address>(ai->getter());
+ if (getter_entry != 0) {
+ LOG(GetterCallbackEvent(name, getter_entry));
+ }
+ Address setter_entry = v8::ToCData<Address>(ai->setter());
+ if (setter_entry != 0) {
+ LOG(SetterCallbackEvent(name, setter_entry));
+ }
+ }
+}
+
#endif
diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h
index f099f02b6..4d5acced6 100644
--- a/deps/v8/src/log.h
+++ b/deps/v8/src/log.h
@@ -208,6 +208,8 @@ class Logger {
// ==== Events logged by --log-code. ====
// Emits a code event for a callback function.
static void CallbackEvent(String* name, Address entry_point);
+ static void GetterCallbackEvent(String* name, Address entry_point);
+ static void SetterCallbackEvent(String* name, Address entry_point);
// Emits a code create event.
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code, const char* source);
@@ -273,6 +275,8 @@ class Logger {
// Logs all compiled functions found in the heap.
static void LogCompiledFunctions();
+ // Logs all accessor callbacks found in the heap.
+ static void LogAccessorCallbacks();
// Used for logging stubs found in the snapshot.
static void LogCodeObject(Object* code_object);
@@ -287,6 +291,11 @@ class Logger {
// Emits the profiler's first message.
static void ProfilerBeginEvent();
+ // Emits callback event messages.
+ static void CallbackEventInternal(const char* prefix,
+ const char* name,
+ Address entry_point);
+
// Emits aliases for compressed messages.
static void LogAliases();
diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js
index 27207928c..1e5053d7e 100644
--- a/deps/v8/src/messages.js
+++ b/deps/v8/src/messages.js
@@ -238,14 +238,15 @@ function MakeError(type, args) {
Script.prototype.lineFromPosition = function(position) {
var lower = 0;
var upper = this.lineCount() - 1;
+ var line_ends = this.line_ends;
// We'll never find invalid positions so bail right away.
- if (position > this.line_ends[upper]) {
+ if (position > line_ends[upper]) {
return -1;
}
// This means we don't have to safe-guard indexing line_ends[i - 1].
- if (position <= this.line_ends[0]) {
+ if (position <= line_ends[0]) {
return 0;
}
@@ -253,9 +254,9 @@ Script.prototype.lineFromPosition = function(position) {
while (upper >= 1) {
var i = (lower + upper) >> 1;
- if (position > this.line_ends[i]) {
+ if (position > line_ends[i]) {
lower = i + 1;
- } else if (position <= this.line_ends[i - 1]) {
+ } else if (position <= line_ends[i - 1]) {
upper = i - 1;
} else {
return i;
@@ -278,8 +279,9 @@ Script.prototype.locationFromPosition = function (position,
if (line == -1) return null;
// Determine start, end and column.
- var start = line == 0 ? 0 : this.line_ends[line - 1] + 1;
- var end = this.line_ends[line];
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
if (end > 0 && StringCharAt.call(this.source, end - 1) == '\r') end--;
var column = position - start;
@@ -368,8 +370,9 @@ Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
return null;
}
- var from_position = from_line == 0 ? 0 : this.line_ends[from_line - 1] + 1;
- var to_position = to_line == 0 ? 0 : this.line_ends[to_line - 1] + 1;
+ var line_ends = this.line_ends;
+ var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
+ var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
// Return a source slice with line numbers re-adjusted to the resource.
return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset,
@@ -391,8 +394,9 @@ Script.prototype.sourceLine = function (opt_line) {
}
// Return the source line.
- var start = line == 0 ? 0 : this.line_ends[line - 1] + 1;
- var end = this.line_ends[line];
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
return StringSubstring.call(this.source, start, end);
}
@@ -625,10 +629,7 @@ CallSite.prototype.isEval = function () {
CallSite.prototype.getEvalOrigin = function () {
var script = %FunctionGetScript(this.fun);
- if (!script || script.compilation_type != 1)
- return null;
- return new CallSite(null, script.eval_from_function,
- script.eval_from_position);
+ return FormatEvalOrigin(script);
};
CallSite.prototype.getFunction = function () {
@@ -696,7 +697,7 @@ CallSite.prototype.getColumnNumber = function () {
if (script) {
location = script.locationFromPosition(this.pos, true);
}
- return location ? location.column : null;
+ return location ? location.column + 1: null;
};
CallSite.prototype.isNative = function () {
@@ -715,12 +716,44 @@ CallSite.prototype.isConstructor = function () {
return this.fun === constructor;
};
+function FormatEvalOrigin(script) {
+ var eval_origin = "";
+ if (script.eval_from_function_name) {
+ eval_origin += script.eval_from_function_name;
+ } else {
+ eval_origin += "<anonymous>";
+ }
+
+ var eval_from_script = script.eval_from_script;
+ if (eval_from_script) {
+ if (eval_from_script.compilation_type == 1) {
+ // eval script originated from another eval.
+ eval_origin += " (eval at " + FormatEvalOrigin(eval_from_script) + ")";
+ } else {
+ // eval script originated from "real" scource.
+ if (eval_from_script.name) {
+ eval_origin += " (" + eval_from_script.name;
+ var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
+ if (location) {
+ eval_origin += ":" + (location.line + 1);
+ eval_origin += ":" + (location.column + 1);
+ }
+ eval_origin += ")"
+ } else {
+ eval_origin += " (unknown source)";
+ }
+ }
+ }
+
+ return eval_origin;
+};
+
function FormatSourcePosition(frame) {
var fileLocation = "";
if (frame.isNative()) {
fileLocation = "native";
} else if (frame.isEval()) {
- fileLocation = "eval at " + FormatSourcePosition(frame.getEvalOrigin());
+ fileLocation = "eval at " + frame.getEvalOrigin();
} else {
var fileName = frame.getFileName();
if (fileName) {
diff --git a/deps/v8/src/mirror-delay.js b/deps/v8/src/mirror-delay.js
index 82fb9c228..ba663b2a2 100644
--- a/deps/v8/src/mirror-delay.js
+++ b/deps/v8/src/mirror-delay.js
@@ -1793,16 +1793,21 @@ ScriptMirror.prototype.context = function() {
};
-ScriptMirror.prototype.evalFromFunction = function() {
- return MakeMirror(this.script_.eval_from_function);
+ScriptMirror.prototype.evalFromScript = function() {
+ return MakeMirror(this.script_.eval_from_script);
+};
+
+
+ScriptMirror.prototype.evalFromFunctionName = function() {
+ return MakeMirror(this.script_.eval_from_function_name);
};
ScriptMirror.prototype.evalFromLocation = function() {
- var eval_from_function = this.evalFromFunction();
- if (!eval_from_function.isUndefined()) {
- var position = this.script_.eval_from_position;
- return eval_from_function.script().locationFromPosition(position, true);
+ var eval_from_script = this.evalFromScript();
+ if (!eval_from_script.isUndefined()) {
+ var position = this.script_.eval_from_script_position;
+ return eval_from_script.locationFromPosition(position, true);
}
};
@@ -2080,12 +2085,15 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
// For compilation type eval emit information on the script from which
// eval was called if a script is present.
if (mirror.compilationType() == 1 &&
- mirror.evalFromFunction().script()) {
+ mirror.evalFromScript()) {
content.evalFromScript =
- this.serializeReference(mirror.evalFromFunction().script());
+ this.serializeReference(mirror.evalFromScript());
var evalFromLocation = mirror.evalFromLocation()
content.evalFromLocation = { line: evalFromLocation.line,
column: evalFromLocation.column}
+ if (mirror.evalFromFunctionName()) {
+ content.evalFromFunctionName = mirror.evalFromFunctionName();
+ }
}
if (mirror.context()) {
content.context = this.serializeReference(mirror.context());
diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc
index 19c945d98..36f65eee8 100644
--- a/deps/v8/src/objects-debug.cc
+++ b/deps/v8/src/objects-debug.cc
@@ -547,42 +547,18 @@ static const char* TypeToString(InstanceType type) {
case INVALID_TYPE: return "INVALID";
case MAP_TYPE: return "MAP";
case HEAP_NUMBER_TYPE: return "HEAP_NUMBER";
- case SHORT_SYMBOL_TYPE:
- case MEDIUM_SYMBOL_TYPE:
- case LONG_SYMBOL_TYPE: return "SYMBOL";
- case SHORT_ASCII_SYMBOL_TYPE:
- case MEDIUM_ASCII_SYMBOL_TYPE:
- case LONG_ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL";
- case SHORT_CONS_SYMBOL_TYPE:
- case MEDIUM_CONS_SYMBOL_TYPE:
- case LONG_CONS_SYMBOL_TYPE: return "CONS_SYMBOL";
- case SHORT_CONS_ASCII_SYMBOL_TYPE:
- case MEDIUM_CONS_ASCII_SYMBOL_TYPE:
- case LONG_CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL";
- case SHORT_EXTERNAL_ASCII_SYMBOL_TYPE:
- case MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE:
- case LONG_EXTERNAL_ASCII_SYMBOL_TYPE:
- case SHORT_EXTERNAL_SYMBOL_TYPE:
- case MEDIUM_EXTERNAL_SYMBOL_TYPE:
- case LONG_EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL";
- case SHORT_ASCII_STRING_TYPE:
- case MEDIUM_ASCII_STRING_TYPE:
- case LONG_ASCII_STRING_TYPE: return "ASCII_STRING";
- case SHORT_STRING_TYPE:
- case MEDIUM_STRING_TYPE:
- case LONG_STRING_TYPE: return "TWO_BYTE_STRING";
- case SHORT_CONS_STRING_TYPE:
- case MEDIUM_CONS_STRING_TYPE:
- case LONG_CONS_STRING_TYPE:
- case SHORT_CONS_ASCII_STRING_TYPE:
- case MEDIUM_CONS_ASCII_STRING_TYPE:
- case LONG_CONS_ASCII_STRING_TYPE: return "CONS_STRING";
- case SHORT_EXTERNAL_ASCII_STRING_TYPE:
- case MEDIUM_EXTERNAL_ASCII_STRING_TYPE:
- case LONG_EXTERNAL_ASCII_STRING_TYPE:
- case SHORT_EXTERNAL_STRING_TYPE:
- case MEDIUM_EXTERNAL_STRING_TYPE:
- case LONG_EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING";
+ case SYMBOL_TYPE: return "SYMBOL";
+ case ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL";
+ case CONS_SYMBOL_TYPE: return "CONS_SYMBOL";
+ case CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL";
+ case EXTERNAL_ASCII_SYMBOL_TYPE:
+ case EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL";
+ case ASCII_STRING_TYPE: return "ASCII_STRING";
+ case STRING_TYPE: return "TWO_BYTE_STRING";
+ case CONS_STRING_TYPE:
+ case CONS_ASCII_STRING_TYPE: return "CONS_STRING";
+ case EXTERNAL_ASCII_STRING_TYPE:
+ case EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING";
case FIXED_ARRAY_TYPE: return "FIXED_ARRAY";
case BYTE_ARRAY_TYPE: return "BYTE_ARRAY";
case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY";
@@ -1140,8 +1116,7 @@ void Script::ScriptVerify() {
VerifyPointer(data());
VerifyPointer(wrapper());
type()->SmiVerify();
- VerifyPointer(line_ends_fixed_array());
- VerifyPointer(line_ends_js_array());
+ VerifyPointer(line_ends());
VerifyPointer(id());
}
@@ -1160,6 +1135,20 @@ void Script::ScriptPrint() {
type()->ShortPrint();
PrintF("\n - id: ");
id()->ShortPrint();
+ PrintF("\n - data: ");
+ data()->ShortPrint();
+ PrintF("\n - context data: ");
+ context_data()->ShortPrint();
+ PrintF("\n - wrapper: ");
+ wrapper()->ShortPrint();
+ PrintF("\n - compilation type: ");
+ compilation_type()->ShortPrint();
+ PrintF("\n - line ends: ");
+ line_ends()->ShortPrint();
+ PrintF("\n - eval from shared: ");
+ eval_from_shared()->ShortPrint();
+ PrintF("\n - eval from instructions offset: ");
+ eval_from_instructions_offset()->ShortPrint();
PrintF("\n");
}
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index 7a6444de5..8514a412c 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -280,11 +280,6 @@ STATIC_CHECK((kStringRepresentationMask | kStringEncodingMask) ==
Internals::kFullStringRepresentationMask);
-uint32_t StringShape::size_tag() {
- return (type_ & kStringSizeMask);
-}
-
-
bool StringShape::IsSequentialAscii() {
return full_representation_tag() == (kSeqStringTag | kAsciiStringTag);
}
@@ -921,25 +916,6 @@ HeapObject* MapWord::ToForwardingAddress() {
}
-bool MapWord::IsSerializationAddress() {
- return HAS_SMI_TAG(reinterpret_cast<Object*>(value_));
-}
-
-
-MapWord MapWord::FromSerializationAddress(int raw) {
- // When the map word is being used as a serialization address we Smi-encode
- // the serialization address (which is always a smallish positive integer).
- return MapWord(reinterpret_cast<uintptr_t>(Smi::FromInt(raw)));
-}
-
-
-int MapWord::ToSerializationAddress() {
- // When the map word is being used as a serialization address we treat the
- // map word as a Smi and get the small integer that it encodes.
- return reinterpret_cast<Smi*>(value_)->value();
-}
-
-
bool MapWord::IsMarked() {
return (value_ & kMarkingMask) == 0;
}
@@ -1635,44 +1611,25 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) {
INT_ACCESSORS(Array, length, kLengthOffset)
-bool String::Equals(String* other) {
- if (other == this) return true;
- if (StringShape(this).IsSymbol() && StringShape(other).IsSymbol()) {
- return false;
- }
- return SlowEquals(other);
-}
-
-
-int String::length() {
- uint32_t len = READ_INT_FIELD(this, kLengthOffset);
+INT_ACCESSORS(String, length, kLengthOffset)
- ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
- ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
- ASSERT(kLongStringTag == 0);
- return len >> (StringShape(this).size_tag() + kLongLengthShift);
+uint32_t String::hash_field() {
+ return READ_UINT32_FIELD(this, kHashFieldOffset);
}
-void String::set_length(int value) {
- ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
- ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
- ASSERT(kLongStringTag == 0);
-
- WRITE_INT_FIELD(this,
- kLengthOffset,
- value << (StringShape(this).size_tag() + kLongLengthShift));
-}
-
-
-uint32_t String::length_field() {
- return READ_UINT32_FIELD(this, kLengthOffset);
+void String::set_hash_field(uint32_t value) {
+ WRITE_UINT32_FIELD(this, kHashFieldOffset, value);
}
-void String::set_length_field(uint32_t value) {
- WRITE_UINT32_FIELD(this, kLengthOffset, value);
+bool String::Equals(String* other) {
+ if (other == this) return true;
+ if (StringShape(this).IsSymbol() && StringShape(other).IsSymbol()) {
+ return false;
+ }
+ return SlowEquals(other);
}
@@ -1779,30 +1736,12 @@ void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) {
int SeqTwoByteString::SeqTwoByteStringSize(InstanceType instance_type) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
-
- ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
- ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
- ASSERT(kLongStringTag == 0);
-
- // Use the map (and not 'this') to compute the size tag, since
- // TwoByteStringSize is called during GC when maps are encoded.
- length >>= StringShape(instance_type).size_tag() + kLongLengthShift;
-
return SizeFor(length);
}
int SeqAsciiString::SeqAsciiStringSize(InstanceType instance_type) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
-
- ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift);
- ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift);
- ASSERT(kLongStringTag == 0);
-
- // Use the map (and not 'this') to compute the size tag, since
- // AsciiStringSize is called during GC when maps are encoded.
- length >>= StringShape(instance_type).size_tag() + kLongLengthShift;
-
return SizeFor(length);
}
@@ -1850,34 +1789,6 @@ void ExternalAsciiString::set_resource(
}
-Map* ExternalAsciiString::StringMap(int length) {
- Map* map;
- // Number of characters: determines the map.
- if (length <= String::kMaxShortSize) {
- map = Heap::short_external_ascii_string_map();
- } else if (length <= String::kMaxMediumSize) {
- map = Heap::medium_external_ascii_string_map();
- } else {
- map = Heap::long_external_ascii_string_map();
- }
- return map;
-}
-
-
-Map* ExternalAsciiString::SymbolMap(int length) {
- Map* map;
- // Number of characters: determines the map.
- if (length <= String::kMaxShortSize) {
- map = Heap::short_external_ascii_symbol_map();
- } else if (length <= String::kMaxMediumSize) {
- map = Heap::medium_external_ascii_symbol_map();
- } else {
- map = Heap::long_external_ascii_symbol_map();
- }
- return map;
-}
-
-
ExternalTwoByteString::Resource* ExternalTwoByteString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset));
}
@@ -1889,34 +1800,6 @@ void ExternalTwoByteString::set_resource(
}
-Map* ExternalTwoByteString::StringMap(int length) {
- Map* map;
- // Number of characters: determines the map.
- if (length <= String::kMaxShortSize) {
- map = Heap::short_external_string_map();
- } else if (length <= String::kMaxMediumSize) {
- map = Heap::medium_external_string_map();
- } else {
- map = Heap::long_external_string_map();
- }
- return map;
-}
-
-
-Map* ExternalTwoByteString::SymbolMap(int length) {
- Map* map;
- // Number of characters: determines the map.
- if (length <= String::kMaxShortSize) {
- map = Heap::short_external_symbol_map();
- } else if (length <= String::kMaxMediumSize) {
- map = Heap::medium_external_symbol_map();
- } else {
- map = Heap::long_external_symbol_map();
- }
- return map;
-}
-
-
byte ByteArray::get(int index) {
ASSERT(index >= 0 && index < this->length());
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
@@ -2441,9 +2324,8 @@ ACCESSORS(Script, context_data, Object, kContextOffset)
ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
ACCESSORS(Script, type, Smi, kTypeOffset)
ACCESSORS(Script, compilation_type, Smi, kCompilationTypeOffset)
-ACCESSORS(Script, line_ends_fixed_array, Object, kLineEndsFixedArrayOffset)
-ACCESSORS(Script, line_ends_js_array, Object, kLineEndsJSArrayOffset)
-ACCESSORS(Script, eval_from_function, Object, kEvalFromFunctionOffset)
+ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
+ACCESSORS(Script, eval_from_shared, Object, kEvalFromSharedOffset)
ACCESSORS(Script, eval_from_instructions_offset, Smi,
kEvalFrominstructionsOffsetOffset)
@@ -2900,13 +2782,13 @@ NumberDictionary* JSObject::element_dictionary() {
bool String::HasHashCode() {
- return (length_field() & kHashComputedMask) != 0;
+ return (hash_field() & kHashComputedMask) != 0;
}
uint32_t String::Hash() {
// Fast case: has hash code already been computed?
- uint32_t field = length_field();
+ uint32_t field = hash_field();
if (field & kHashComputedMask) return field >> kHashShift;
// Slow case: compute hash code and set it.
return ComputeAndSetHash();
@@ -2923,7 +2805,7 @@ StringHasher::StringHasher(int length)
bool StringHasher::has_trivial_hash() {
- return length_ > String::kMaxMediumSize;
+ return length_ > String::kMaxHashCalcLength;
}
@@ -2979,7 +2861,7 @@ uint32_t StringHasher::GetHash() {
bool String::AsArrayIndex(uint32_t* index) {
- uint32_t field = length_field();
+ uint32_t field = hash_field();
if ((field & kHashComputedMask) && !(field & kIsArrayIndexMask)) return false;
return SlowAsArrayIndex(index);
}
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index f37658551..0f8dca398 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -37,6 +37,7 @@
#include "scanner.h"
#include "scopeinfo.h"
#include "string-stream.h"
+#include "utils.h"
#ifdef ENABLE_DISASSEMBLER
#include "disassembler.h"
@@ -754,19 +755,21 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
ASSERT(size >= ExternalString::kSize);
bool is_symbol = this->IsSymbol();
int length = this->length();
+ int hash_field = this->hash_field();
// Morph the object to an external string by adjusting the map and
// reinitializing the fields.
- this->set_map(ExternalTwoByteString::StringMap(length));
+ this->set_map(Heap::external_string_map());
ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
self->set_length(length);
+ self->set_hash_field(hash_field);
self->set_resource(resource);
// Additionally make the object into an external symbol if the original string
// was a symbol to start with.
if (is_symbol) {
self->Hash(); // Force regeneration of the hash value.
// Now morph this external string into a external symbol.
- self->set_map(ExternalTwoByteString::SymbolMap(length));
+ this->set_map(Heap::external_symbol_map());
}
// Fill the remainder of the string with dead wood.
@@ -798,19 +801,21 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
ASSERT(size >= ExternalString::kSize);
bool is_symbol = this->IsSymbol();
int length = this->length();
+ int hash_field = this->hash_field();
// Morph the object to an external string by adjusting the map and
// reinitializing the fields.
- this->set_map(ExternalAsciiString::StringMap(length));
+ this->set_map(Heap::external_ascii_string_map());
ExternalAsciiString* self = ExternalAsciiString::cast(this);
self->set_length(length);
+ self->set_hash_field(hash_field);
self->set_resource(resource);
// Additionally make the object into an external symbol if the original string
// was a symbol to start with.
if (is_symbol) {
self->Hash(); // Force regeneration of the hash value.
// Now morph this external string into a external symbol.
- self->set_map(ExternalAsciiString::SymbolMap(length));
+ this->set_map(Heap::external_ascii_symbol_map());
}
// Fill the remainder of the string with dead wood.
@@ -822,7 +827,7 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
void String::StringShortPrint(StringStream* accumulator) {
int len = length();
- if (len > kMaxMediumSize) {
+ if (len > kMaxShortPrintLength) {
accumulator->Add("<Very long string[%u]>", len);
return;
}
@@ -2628,33 +2633,24 @@ bool JSObject::ReferencesObject(Object* obj) {
// Tests for the fast common case for property enumeration:
-// - this object has an enum cache
-// - this object has no elements
-// - no prototype has enumerable properties/elements
-// - neither this object nor any prototype has interceptors
+// - This object and all prototypes has an enum cache (which means that it has
+// no interceptors and needs no access checks).
+// - This object has no elements.
+// - No prototype has enumerable properties/elements.
bool JSObject::IsSimpleEnum() {
- JSObject* arguments_boilerplate =
- Top::context()->global_context()->arguments_boilerplate();
- JSFunction* arguments_function =
- JSFunction::cast(arguments_boilerplate->map()->constructor());
- if (IsAccessCheckNeeded()) return false;
- if (map()->constructor() == arguments_function) return false;
-
for (Object* o = this;
o != Heap::null_value();
o = JSObject::cast(o)->GetPrototype()) {
JSObject* curr = JSObject::cast(o);
- if (!curr->HasFastProperties()) return false;
if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
+ ASSERT(!curr->HasNamedInterceptor());
+ ASSERT(!curr->HasIndexedInterceptor());
+ ASSERT(!curr->IsAccessCheckNeeded());
if (curr->NumberOfEnumElements() > 0) return false;
- if (curr->HasNamedInterceptor()) return false;
- if (curr->HasIndexedInterceptor()) return false;
if (curr != this) {
FixedArray* curr_fixed_array =
FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
- if (curr_fixed_array->length() > 0) {
- return false;
- }
+ if (curr_fixed_array->length() > 0) return false;
}
}
return true;
@@ -4484,23 +4480,11 @@ bool String::MarkAsUndetectable() {
if (StringShape(this).IsSymbol()) return false;
Map* map = this->map();
- if (map == Heap::short_string_map()) {
- this->set_map(Heap::undetectable_short_string_map());
- return true;
- } else if (map == Heap::medium_string_map()) {
- this->set_map(Heap::undetectable_medium_string_map());
+ if (map == Heap::string_map()) {
+ this->set_map(Heap::undetectable_string_map());
return true;
- } else if (map == Heap::long_string_map()) {
- this->set_map(Heap::undetectable_long_string_map());
- return true;
- } else if (map == Heap::short_ascii_string_map()) {
- this->set_map(Heap::undetectable_short_ascii_string_map());
- return true;
- } else if (map == Heap::medium_ascii_string_map()) {
- this->set_map(Heap::undetectable_medium_ascii_string_map());
- return true;
- } else if (map == Heap::long_ascii_string_map()) {
- this->set_map(Heap::undetectable_long_ascii_string_map());
+ } else if (map == Heap::ascii_string_map()) {
+ this->set_map(Heap::undetectable_ascii_string_map());
return true;
}
// Rest cannot be marked as undetectable
@@ -4523,17 +4507,17 @@ bool String::IsEqualTo(Vector<const char> str) {
uint32_t String::ComputeAndSetHash() {
// Should only be called if hash code has not yet been computed.
- ASSERT(!(length_field() & kHashComputedMask));
+ ASSERT(!(hash_field() & kHashComputedMask));
// Compute the hash code.
StringInputBuffer buffer(this);
- uint32_t field = ComputeLengthAndHashField(&buffer, length());
+ uint32_t field = ComputeHashField(&buffer, length());
// Store the hash code in the object.
- set_length_field(field);
+ set_hash_field(field);
// Check the hash code is there.
- ASSERT(length_field() & kHashComputedMask);
+ ASSERT(hash_field() & kHashComputedMask);
uint32_t result = field >> kHashShift;
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
return result;
@@ -4573,9 +4557,10 @@ bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
bool String::SlowAsArrayIndex(uint32_t* index) {
if (length() <= kMaxCachedArrayIndexLength) {
Hash(); // force computation of hash code
- uint32_t field = length_field();
+ uint32_t field = hash_field();
if ((field & kIsArrayIndexMask) == 0) return false;
- *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift;
+ // Isolate the array index form the full hash field.
+ *index = (kArrayIndexHashMask & field) >> kHashShift;
return true;
} else {
StringInputBuffer buffer(this);
@@ -4584,37 +4569,42 @@ bool String::SlowAsArrayIndex(uint32_t* index) {
}
-static inline uint32_t HashField(uint32_t hash, bool is_array_index) {
+static inline uint32_t HashField(uint32_t hash,
+ bool is_array_index,
+ int length = -1) {
uint32_t result =
- (hash << String::kLongLengthShift) | String::kHashComputedMask;
- if (is_array_index) result |= String::kIsArrayIndexMask;
+ (hash << String::kHashShift) | String::kHashComputedMask;
+ if (is_array_index) {
+ // For array indexes mix the length into the hash as an array index could
+ // be zero.
+ ASSERT(length > 0);
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ result |= String::kIsArrayIndexMask;
+ result |= length << String::kArrayIndexHashLengthShift;
+ }
return result;
}
uint32_t StringHasher::GetHashField() {
ASSERT(is_valid());
- if (length_ <= String::kMaxShortSize) {
- uint32_t payload;
+ if (length_ <= String::kMaxHashCalcLength) {
if (is_array_index()) {
- payload = v8::internal::HashField(array_index(), true);
+ return v8::internal::HashField(array_index(), true, length_);
} else {
- payload = v8::internal::HashField(GetHash(), false);
+ return v8::internal::HashField(GetHash(), false);
}
- return (payload & ((1 << String::kShortLengthShift) - 1)) |
- (length_ << String::kShortLengthShift);
- } else if (length_ <= String::kMaxMediumSize) {
uint32_t payload = v8::internal::HashField(GetHash(), false);
- return (payload & ((1 << String::kMediumLengthShift) - 1)) |
- (length_ << String::kMediumLengthShift);
+ return payload;
} else {
return v8::internal::HashField(length_, false);
}
}
-uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer,
- int length) {
+uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
+ int length) {
StringHasher hasher(length);
// Very long strings have a trivial hash that doesn't inspect the
@@ -6177,6 +6167,7 @@ Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
return pt->GetPropertyWithReceiver(receiver, name, attributes);
}
+
Object* JSObject::GetLocalPropertyPostInterceptor(
JSObject* receiver,
String* name,
@@ -6478,6 +6469,15 @@ int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
int JSObject::NumberOfEnumElements() {
+ // Fast case for objects with no elements.
+ if (!IsJSValue() && HasFastElements()) {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>(
+ Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedArray::cast(elements())->length());
+ if (length == 0) return 0;
+ }
+ // Compute the number of enumerable elements.
return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
}
@@ -6737,19 +6737,19 @@ class RegExpKey : public HashTableKey {
class Utf8SymbolKey : public HashTableKey {
public:
explicit Utf8SymbolKey(Vector<const char> string)
- : string_(string), length_field_(0) { }
+ : string_(string), hash_field_(0) { }
bool IsMatch(Object* string) {
return String::cast(string)->IsEqualTo(string_);
}
uint32_t Hash() {
- if (length_field_ != 0) return length_field_ >> String::kHashShift;
+ if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
unibrow::Utf8InputBuffer<> buffer(string_.start(),
static_cast<unsigned>(string_.length()));
chars_ = buffer.Length();
- length_field_ = String::ComputeLengthAndHashField(&buffer, chars_);
- uint32_t result = length_field_ >> String::kHashShift;
+ hash_field_ = String::ComputeHashField(&buffer, chars_);
+ uint32_t result = hash_field_ >> String::kHashShift;
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
return result;
}
@@ -6759,12 +6759,12 @@ class Utf8SymbolKey : public HashTableKey {
}
Object* AsObject() {
- if (length_field_ == 0) Hash();
- return Heap::AllocateSymbol(string_, chars_, length_field_);
+ if (hash_field_ == 0) Hash();
+ return Heap::AllocateSymbol(string_, chars_, hash_field_);
}
Vector<const char> string_;
- uint32_t length_field_;
+ uint32_t hash_field_;
int chars_; // Caches the number of characters when computing the hash code.
};
@@ -6805,7 +6805,7 @@ class SymbolKey : public HashTableKey {
StringInputBuffer buffer(string_);
return Heap::AllocateInternalSymbol(&buffer,
string_->length(),
- string_->length_field());
+ string_->hash_field());
}
static uint32_t StringHash(Object* obj) {
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index d22e7231f..671978ab7 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -221,248 +221,128 @@ enum PropertyNormalizationMode {
// NOTE: Everything following JS_VALUE_TYPE is considered a
// JSObject for GC purposes. The first four entries here have typeof
// 'object', whereas JS_FUNCTION_TYPE has typeof 'function'.
-#define INSTANCE_TYPE_LIST_ALL(V) \
- V(SHORT_SYMBOL_TYPE) \
- V(MEDIUM_SYMBOL_TYPE) \
- V(LONG_SYMBOL_TYPE) \
- V(SHORT_ASCII_SYMBOL_TYPE) \
- V(MEDIUM_ASCII_SYMBOL_TYPE) \
- V(LONG_ASCII_SYMBOL_TYPE) \
- V(SHORT_CONS_SYMBOL_TYPE) \
- V(MEDIUM_CONS_SYMBOL_TYPE) \
- V(LONG_CONS_SYMBOL_TYPE) \
- V(SHORT_CONS_ASCII_SYMBOL_TYPE) \
- V(MEDIUM_CONS_ASCII_SYMBOL_TYPE) \
- V(LONG_CONS_ASCII_SYMBOL_TYPE) \
- V(SHORT_EXTERNAL_SYMBOL_TYPE) \
- V(MEDIUM_EXTERNAL_SYMBOL_TYPE) \
- V(LONG_EXTERNAL_SYMBOL_TYPE) \
- V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE) \
- V(MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE) \
- V(LONG_EXTERNAL_ASCII_SYMBOL_TYPE) \
- V(SHORT_STRING_TYPE) \
- V(MEDIUM_STRING_TYPE) \
- V(LONG_STRING_TYPE) \
- V(SHORT_ASCII_STRING_TYPE) \
- V(MEDIUM_ASCII_STRING_TYPE) \
- V(LONG_ASCII_STRING_TYPE) \
- V(SHORT_CONS_STRING_TYPE) \
- V(MEDIUM_CONS_STRING_TYPE) \
- V(LONG_CONS_STRING_TYPE) \
- V(SHORT_CONS_ASCII_STRING_TYPE) \
- V(MEDIUM_CONS_ASCII_STRING_TYPE) \
- V(LONG_CONS_ASCII_STRING_TYPE) \
- V(SHORT_EXTERNAL_STRING_TYPE) \
- V(MEDIUM_EXTERNAL_STRING_TYPE) \
- V(LONG_EXTERNAL_STRING_TYPE) \
- V(SHORT_EXTERNAL_ASCII_STRING_TYPE) \
- V(MEDIUM_EXTERNAL_ASCII_STRING_TYPE) \
- V(LONG_EXTERNAL_ASCII_STRING_TYPE) \
- V(LONG_PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \
- \
- V(MAP_TYPE) \
- V(HEAP_NUMBER_TYPE) \
- V(FIXED_ARRAY_TYPE) \
- V(CODE_TYPE) \
- V(JS_GLOBAL_PROPERTY_CELL_TYPE) \
- V(ODDBALL_TYPE) \
- V(PROXY_TYPE) \
- V(BYTE_ARRAY_TYPE) \
- V(PIXEL_ARRAY_TYPE) \
- /* Note: the order of these external array */ \
- /* types is relied upon in */ \
- /* Object::IsExternalArray(). */ \
- V(EXTERNAL_BYTE_ARRAY_TYPE) \
- V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \
- V(EXTERNAL_SHORT_ARRAY_TYPE) \
- V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \
- V(EXTERNAL_INT_ARRAY_TYPE) \
- V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \
- V(EXTERNAL_FLOAT_ARRAY_TYPE) \
- V(FILLER_TYPE) \
- \
- V(ACCESSOR_INFO_TYPE) \
- V(ACCESS_CHECK_INFO_TYPE) \
- V(INTERCEPTOR_INFO_TYPE) \
- V(SHARED_FUNCTION_INFO_TYPE) \
- V(CALL_HANDLER_INFO_TYPE) \
- V(FUNCTION_TEMPLATE_INFO_TYPE) \
- V(OBJECT_TEMPLATE_INFO_TYPE) \
- V(SIGNATURE_INFO_TYPE) \
- V(TYPE_SWITCH_INFO_TYPE) \
- V(SCRIPT_TYPE) \
- \
- V(JS_VALUE_TYPE) \
- V(JS_OBJECT_TYPE) \
- V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
- V(JS_GLOBAL_OBJECT_TYPE) \
- V(JS_BUILTINS_OBJECT_TYPE) \
- V(JS_GLOBAL_PROXY_TYPE) \
- V(JS_ARRAY_TYPE) \
- V(JS_REGEXP_TYPE) \
- \
- V(JS_FUNCTION_TYPE) \
+#define INSTANCE_TYPE_LIST_ALL(V) \
+ V(SYMBOL_TYPE) \
+ V(ASCII_SYMBOL_TYPE) \
+ V(CONS_SYMBOL_TYPE) \
+ V(CONS_ASCII_SYMBOL_TYPE) \
+ V(EXTERNAL_SYMBOL_TYPE) \
+ V(EXTERNAL_ASCII_SYMBOL_TYPE) \
+ V(STRING_TYPE) \
+ V(ASCII_STRING_TYPE) \
+ V(CONS_STRING_TYPE) \
+ V(CONS_ASCII_STRING_TYPE) \
+ V(EXTERNAL_STRING_TYPE) \
+ V(EXTERNAL_ASCII_STRING_TYPE) \
+ V(PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \
+ \
+ V(MAP_TYPE) \
+ V(HEAP_NUMBER_TYPE) \
+ V(FIXED_ARRAY_TYPE) \
+ V(CODE_TYPE) \
+ V(JS_GLOBAL_PROPERTY_CELL_TYPE) \
+ V(ODDBALL_TYPE) \
+ V(PROXY_TYPE) \
+ V(BYTE_ARRAY_TYPE) \
+ V(PIXEL_ARRAY_TYPE) \
+ /* Note: the order of these external array */ \
+ /* types is relied upon in */ \
+ /* Object::IsExternalArray(). */ \
+ V(EXTERNAL_BYTE_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \
+ V(EXTERNAL_SHORT_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \
+ V(EXTERNAL_INT_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \
+ V(EXTERNAL_FLOAT_ARRAY_TYPE) \
+ V(FILLER_TYPE) \
+ \
+ V(ACCESSOR_INFO_TYPE) \
+ V(ACCESS_CHECK_INFO_TYPE) \
+ V(INTERCEPTOR_INFO_TYPE) \
+ V(SHARED_FUNCTION_INFO_TYPE) \
+ V(CALL_HANDLER_INFO_TYPE) \
+ V(FUNCTION_TEMPLATE_INFO_TYPE) \
+ V(OBJECT_TEMPLATE_INFO_TYPE) \
+ V(SIGNATURE_INFO_TYPE) \
+ V(TYPE_SWITCH_INFO_TYPE) \
+ V(SCRIPT_TYPE) \
+ \
+ V(JS_VALUE_TYPE) \
+ V(JS_OBJECT_TYPE) \
+ V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
+ V(JS_GLOBAL_OBJECT_TYPE) \
+ V(JS_BUILTINS_OBJECT_TYPE) \
+ V(JS_GLOBAL_PROXY_TYPE) \
+ V(JS_ARRAY_TYPE) \
+ V(JS_REGEXP_TYPE) \
+ \
+ V(JS_FUNCTION_TYPE) \
#ifdef ENABLE_DEBUGGER_SUPPORT
-#define INSTANCE_TYPE_LIST_DEBUGGER(V) \
- V(DEBUG_INFO_TYPE) \
+#define INSTANCE_TYPE_LIST_DEBUGGER(V) \
+ V(DEBUG_INFO_TYPE) \
V(BREAK_POINT_INFO_TYPE)
#else
#define INSTANCE_TYPE_LIST_DEBUGGER(V)
#endif
-#define INSTANCE_TYPE_LIST(V) \
- INSTANCE_TYPE_LIST_ALL(V) \
+#define INSTANCE_TYPE_LIST(V) \
+ INSTANCE_TYPE_LIST_ALL(V) \
INSTANCE_TYPE_LIST_DEBUGGER(V)
// Since string types are not consecutive, this macro is used to
// iterate over them.
#define STRING_TYPE_LIST(V) \
- V(SHORT_SYMBOL_TYPE, \
+ V(SYMBOL_TYPE, \
SeqTwoByteString::kAlignedSize, \
- short_symbol, \
- ShortSymbol) \
- V(MEDIUM_SYMBOL_TYPE, \
- SeqTwoByteString::kAlignedSize, \
- medium_symbol, \
- MediumSymbol) \
- V(LONG_SYMBOL_TYPE, \
- SeqTwoByteString::kAlignedSize, \
- long_symbol, \
- LongSymbol) \
- V(SHORT_ASCII_SYMBOL_TYPE, \
- SeqAsciiString::kAlignedSize, \
- short_ascii_symbol, \
- ShortAsciiSymbol) \
- V(MEDIUM_ASCII_SYMBOL_TYPE, \
+ symbol, \
+ Symbol) \
+ V(ASCII_SYMBOL_TYPE, \
SeqAsciiString::kAlignedSize, \
- medium_ascii_symbol, \
- MediumAsciiSymbol) \
- V(LONG_ASCII_SYMBOL_TYPE, \
- SeqAsciiString::kAlignedSize, \
- long_ascii_symbol, \
- LongAsciiSymbol) \
- V(SHORT_CONS_SYMBOL_TYPE, \
- ConsString::kSize, \
- short_cons_symbol, \
- ShortConsSymbol) \
- V(MEDIUM_CONS_SYMBOL_TYPE, \
+ ascii_symbol, \
+ AsciiSymbol) \
+ V(CONS_SYMBOL_TYPE, \
ConsString::kSize, \
- medium_cons_symbol, \
- MediumConsSymbol) \
- V(LONG_CONS_SYMBOL_TYPE, \
+ cons_symbol, \
+ ConsSymbol) \
+ V(CONS_ASCII_SYMBOL_TYPE, \
ConsString::kSize, \
- long_cons_symbol, \
- LongConsSymbol) \
- V(SHORT_CONS_ASCII_SYMBOL_TYPE, \
- ConsString::kSize, \
- short_cons_ascii_symbol, \
- ShortConsAsciiSymbol) \
- V(MEDIUM_CONS_ASCII_SYMBOL_TYPE, \
- ConsString::kSize, \
- medium_cons_ascii_symbol, \
- MediumConsAsciiSymbol) \
- V(LONG_CONS_ASCII_SYMBOL_TYPE, \
- ConsString::kSize, \
- long_cons_ascii_symbol, \
- LongConsAsciiSymbol) \
- V(SHORT_EXTERNAL_SYMBOL_TYPE, \
- ExternalTwoByteString::kSize, \
- short_external_symbol, \
- ShortExternalSymbol) \
- V(MEDIUM_EXTERNAL_SYMBOL_TYPE, \
+ cons_ascii_symbol, \
+ ConsAsciiSymbol) \
+ V(EXTERNAL_SYMBOL_TYPE, \
ExternalTwoByteString::kSize, \
- medium_external_symbol, \
- MediumExternalSymbol) \
- V(LONG_EXTERNAL_SYMBOL_TYPE, \
- ExternalTwoByteString::kSize, \
- long_external_symbol, \
- LongExternalSymbol) \
- V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE, \
- ExternalAsciiString::kSize, \
- short_external_ascii_symbol, \
- ShortExternalAsciiSymbol) \
- V(MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE, \
+ external_symbol, \
+ ExternalSymbol) \
+ V(EXTERNAL_ASCII_SYMBOL_TYPE, \
ExternalAsciiString::kSize, \
- medium_external_ascii_symbol, \
- MediumExternalAsciiSymbol) \
- V(LONG_EXTERNAL_ASCII_SYMBOL_TYPE, \
- ExternalAsciiString::kSize, \
- long_external_ascii_symbol, \
- LongExternalAsciiSymbol) \
- V(SHORT_STRING_TYPE, \
- SeqTwoByteString::kAlignedSize, \
- short_string, \
- ShortString) \
- V(MEDIUM_STRING_TYPE, \
+ external_ascii_symbol, \
+ ExternalAsciiSymbol) \
+ V(STRING_TYPE, \
SeqTwoByteString::kAlignedSize, \
- medium_string, \
- MediumString) \
- V(LONG_STRING_TYPE, \
- SeqTwoByteString::kAlignedSize, \
- long_string, \
- LongString) \
- V(SHORT_ASCII_STRING_TYPE, \
- SeqAsciiString::kAlignedSize, \
- short_ascii_string, \
- ShortAsciiString) \
- V(MEDIUM_ASCII_STRING_TYPE, \
+ string, \
+ String) \
+ V(ASCII_STRING_TYPE, \
SeqAsciiString::kAlignedSize, \
- medium_ascii_string, \
- MediumAsciiString) \
- V(LONG_ASCII_STRING_TYPE, \
- SeqAsciiString::kAlignedSize, \
- long_ascii_string, \
- LongAsciiString) \
- V(SHORT_CONS_STRING_TYPE, \
- ConsString::kSize, \
- short_cons_string, \
- ShortConsString) \
- V(MEDIUM_CONS_STRING_TYPE, \
+ ascii_string, \
+ AsciiString) \
+ V(CONS_STRING_TYPE, \
ConsString::kSize, \
- medium_cons_string, \
- MediumConsString) \
- V(LONG_CONS_STRING_TYPE, \
+ cons_string, \
+ ConsString) \
+ V(CONS_ASCII_STRING_TYPE, \
ConsString::kSize, \
- long_cons_string, \
- LongConsString) \
- V(SHORT_CONS_ASCII_STRING_TYPE, \
- ConsString::kSize, \
- short_cons_ascii_string, \
- ShortConsAsciiString) \
- V(MEDIUM_CONS_ASCII_STRING_TYPE, \
- ConsString::kSize, \
- medium_cons_ascii_string, \
- MediumConsAsciiString) \
- V(LONG_CONS_ASCII_STRING_TYPE, \
- ConsString::kSize, \
- long_cons_ascii_string, \
- LongConsAsciiString) \
- V(SHORT_EXTERNAL_STRING_TYPE, \
- ExternalTwoByteString::kSize, \
- short_external_string, \
- ShortExternalString) \
- V(MEDIUM_EXTERNAL_STRING_TYPE, \
- ExternalTwoByteString::kSize, \
- medium_external_string, \
- MediumExternalString) \
- V(LONG_EXTERNAL_STRING_TYPE, \
+ cons_ascii_string, \
+ ConsAsciiString) \
+ V(EXTERNAL_STRING_TYPE, \
ExternalTwoByteString::kSize, \
- long_external_string, \
- LongExternalString) \
- V(SHORT_EXTERNAL_ASCII_STRING_TYPE, \
+ external_string, \
+ ExternalString) \
+ V(EXTERNAL_ASCII_STRING_TYPE, \
ExternalAsciiString::kSize, \
- short_external_ascii_string, \
- ShortExternalAsciiString) \
- V(MEDIUM_EXTERNAL_ASCII_STRING_TYPE, \
- ExternalAsciiString::kSize, \
- medium_external_ascii_string, \
- MediumExternalAsciiString) \
- V(LONG_EXTERNAL_ASCII_STRING_TYPE, \
- ExternalAsciiString::kSize, \
- long_external_ascii_string, \
- LongExternalAsciiString)
+ external_ascii_string, \
+ ExternalAsciiString) \
// A struct is a simple object a set of object-valued fields. Including an
// object type in this causes the compiler to generate most of the boilerplate
@@ -473,27 +353,27 @@ enum PropertyNormalizationMode {
// Note that for subtle reasons related to the ordering or numerical values of
// type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
// manually.
-#define STRUCT_LIST_ALL(V) \
- V(ACCESSOR_INFO, AccessorInfo, accessor_info) \
- V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
- V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
- V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
- V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
- V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
- V(SIGNATURE_INFO, SignatureInfo, signature_info) \
- V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
+#define STRUCT_LIST_ALL(V) \
+ V(ACCESSOR_INFO, AccessorInfo, accessor_info) \
+ V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
+ V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
+ V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
+ V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
+ V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
+ V(SIGNATURE_INFO, SignatureInfo, signature_info) \
+ V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
V(SCRIPT, Script, script)
#ifdef ENABLE_DEBUGGER_SUPPORT
-#define STRUCT_LIST_DEBUGGER(V) \
- V(DEBUG_INFO, DebugInfo, debug_info) \
+#define STRUCT_LIST_DEBUGGER(V) \
+ V(DEBUG_INFO, DebugInfo, debug_info) \
V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
#else
#define STRUCT_LIST_DEBUGGER(V)
#endif
-#define STRUCT_LIST(V) \
- STRUCT_LIST_ALL(V) \
+#define STRUCT_LIST(V) \
+ STRUCT_LIST_ALL(V) \
STRUCT_LIST_DEBUGGER(V)
// We use the full 8 bits of the instance_type field to encode heap object
@@ -509,15 +389,6 @@ const uint32_t kIsSymbolMask = 0x20;
const uint32_t kNotSymbolTag = 0x0;
const uint32_t kSymbolTag = 0x20;
-// If bit 7 is clear, bits 3 and 4 are the string's size (short, medium or
-// long). These values are very special in that they are also used to shift
-// the length field to get the length, removing the hash value. This avoids
-// using if or switch when getting the length of a string.
-const uint32_t kStringSizeMask = 0x18;
-const uint32_t kShortStringTag = 0x18;
-const uint32_t kMediumStringTag = 0x10;
-const uint32_t kLongStringTag = 0x00;
-
// If bit 7 is clear then bit 2 indicates whether the string consists of
// two-byte characters or one-byte characters.
const uint32_t kStringEncodingMask = 0x4;
@@ -547,60 +418,20 @@ const uint32_t kShortcutTypeTag = kConsStringTag;
enum InstanceType {
- SHORT_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSeqStringTag,
- MEDIUM_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSeqStringTag,
- LONG_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kSeqStringTag,
- SHORT_ASCII_SYMBOL_TYPE =
- kShortStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
- MEDIUM_ASCII_SYMBOL_TYPE =
- kMediumStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
- LONG_ASCII_SYMBOL_TYPE =
- kLongStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag,
- SHORT_CONS_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kConsStringTag,
- MEDIUM_CONS_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kConsStringTag,
- LONG_CONS_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kConsStringTag,
- SHORT_CONS_ASCII_SYMBOL_TYPE =
- kShortStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
- MEDIUM_CONS_ASCII_SYMBOL_TYPE =
- kMediumStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
- LONG_CONS_ASCII_SYMBOL_TYPE =
- kLongStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag,
- SHORT_EXTERNAL_SYMBOL_TYPE =
- kShortStringTag | kSymbolTag | kExternalStringTag,
- MEDIUM_EXTERNAL_SYMBOL_TYPE =
- kMediumStringTag | kSymbolTag | kExternalStringTag,
- LONG_EXTERNAL_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kExternalStringTag,
- SHORT_EXTERNAL_ASCII_SYMBOL_TYPE =
- kShortStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
- MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE =
- kMediumStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
- LONG_EXTERNAL_ASCII_SYMBOL_TYPE =
- kLongStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag,
- SHORT_STRING_TYPE = kShortStringTag | kSeqStringTag,
- MEDIUM_STRING_TYPE = kMediumStringTag | kSeqStringTag,
- LONG_STRING_TYPE = kLongStringTag | kSeqStringTag,
- SHORT_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kSeqStringTag,
- MEDIUM_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kSeqStringTag,
- LONG_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kSeqStringTag,
- SHORT_CONS_STRING_TYPE = kShortStringTag | kConsStringTag,
- MEDIUM_CONS_STRING_TYPE = kMediumStringTag | kConsStringTag,
- LONG_CONS_STRING_TYPE = kLongStringTag | kConsStringTag,
- SHORT_CONS_ASCII_STRING_TYPE =
- kShortStringTag | kAsciiStringTag | kConsStringTag,
- MEDIUM_CONS_ASCII_STRING_TYPE =
- kMediumStringTag | kAsciiStringTag | kConsStringTag,
- LONG_CONS_ASCII_STRING_TYPE =
- kLongStringTag | kAsciiStringTag | kConsStringTag,
- SHORT_EXTERNAL_STRING_TYPE = kShortStringTag | kExternalStringTag,
- MEDIUM_EXTERNAL_STRING_TYPE = kMediumStringTag | kExternalStringTag,
- LONG_EXTERNAL_STRING_TYPE = kLongStringTag | kExternalStringTag,
- SHORT_EXTERNAL_ASCII_STRING_TYPE =
- kShortStringTag | kAsciiStringTag | kExternalStringTag,
- MEDIUM_EXTERNAL_ASCII_STRING_TYPE =
- kMediumStringTag | kAsciiStringTag | kExternalStringTag,
- LONG_EXTERNAL_ASCII_STRING_TYPE =
- kLongStringTag | kAsciiStringTag | kExternalStringTag,
- LONG_PRIVATE_EXTERNAL_ASCII_STRING_TYPE = LONG_EXTERNAL_ASCII_STRING_TYPE,
+ SYMBOL_TYPE = kSymbolTag | kSeqStringTag,
+ ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag,
+ CONS_SYMBOL_TYPE = kSymbolTag | kConsStringTag,
+ CONS_ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kConsStringTag,
+ EXTERNAL_SYMBOL_TYPE = kSymbolTag | kExternalStringTag,
+ EXTERNAL_ASCII_SYMBOL_TYPE =
+ kAsciiStringTag | kSymbolTag | kExternalStringTag,
+ STRING_TYPE = kSeqStringTag,
+ ASCII_STRING_TYPE = kAsciiStringTag | kSeqStringTag,
+ CONS_STRING_TYPE = kConsStringTag,
+ CONS_ASCII_STRING_TYPE = kAsciiStringTag | kConsStringTag,
+ EXTERNAL_STRING_TYPE = kExternalStringTag,
+ EXTERNAL_ASCII_STRING_TYPE = kAsciiStringTag | kExternalStringTag,
+ PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE,
MAP_TYPE = kNotStringTag,
HEAP_NUMBER_TYPE,
@@ -999,16 +830,6 @@ class MapWord BASE_EMBEDDED {
// View this map word as a forwarding address.
inline HeapObject* ToForwardingAddress();
- // True if this map word is a serialization address. This will only be the
- // case during a destructive serialization of the heap.
- inline bool IsSerializationAddress();
-
- // Create a map word from a serialization address.
- static inline MapWord FromSerializationAddress(int raw);
-
- // View this map word as a serialization address.
- inline int ToSerializationAddress();
-
// Marking phase of full collection: the map word of live objects is
// marked, and may be marked as overflowed (eg, the object is live, its
// children have not been visited, and it does not fit in the marking
@@ -3182,14 +3003,11 @@ class Script: public Struct {
DECL_ACCESSORS(compilation_type, Smi)
// [line_ends]: FixedArray of line ends positions.
- DECL_ACCESSORS(line_ends_fixed_array, Object)
+ DECL_ACCESSORS(line_ends, Object)
- // [line_ends]: JSArray of line ends positions.
- DECL_ACCESSORS(line_ends_js_array, Object)
-
- // [eval_from_function]: for eval scripts the funcion from which eval was
- // called.
- DECL_ACCESSORS(eval_from_function, Object)
+ // [eval_from_shared]: for eval scripts the shared funcion info for the
+ // function from which eval was called.
+ DECL_ACCESSORS(eval_from_shared, Object)
// [eval_from_instructions_offset]: the instruction offset in the code for the
// function from which eval was called where eval was called.
@@ -3215,19 +3033,11 @@ class Script: public Struct {
static const int kWrapperOffset = kContextOffset + kPointerSize;
static const int kTypeOffset = kWrapperOffset + kPointerSize;
static const int kCompilationTypeOffset = kTypeOffset + kPointerSize;
- // We have the line ends array both in FixedArray form and in JSArray form.
- // The FixedArray form is useful when we don't have a context and so can't
- // create a JSArray. The JSArray form is useful when we want to see the
- // array from JS code (e.g. debug-delay.js) which cannot handle unboxed
- // FixedArray objects.
- static const int kLineEndsFixedArrayOffset =
- kCompilationTypeOffset + kPointerSize;
- static const int kLineEndsJSArrayOffset =
- kLineEndsFixedArrayOffset + kPointerSize;
- static const int kIdOffset = kLineEndsJSArrayOffset + kPointerSize;
- static const int kEvalFromFunctionOffset = kIdOffset + kPointerSize;
+ static const int kLineEndsOffset = kCompilationTypeOffset + kPointerSize;
+ static const int kIdOffset = kLineEndsOffset + kPointerSize;
+ static const int kEvalFromSharedOffset = kIdOffset + kPointerSize;
static const int kEvalFrominstructionsOffsetOffset =
- kEvalFromFunctionOffset + kPointerSize;
+ kEvalFromSharedOffset + kPointerSize;
static const int kSize = kEvalFrominstructionsOffsetOffset + kPointerSize;
private:
@@ -3910,12 +3720,9 @@ class String: public HeapObject {
inline int length();
inline void set_length(int value);
- // Get and set the uninterpreted length field of the string. Notice
- // that the length field is also used to cache the hash value of
- // strings. In order to get or set the actual length of the string
- // use the length() and set_length methods.
- inline uint32_t length_field();
- inline void set_length_field(uint32_t value);
+ // Get and set the hash field of the string.
+ inline uint32_t hash_field();
+ inline void set_hash_field(uint32_t value);
inline bool IsAsciiRepresentation();
inline bool IsTwoByteRepresentation();
@@ -3986,8 +3793,8 @@ class String: public HeapObject {
// Returns a hash value used for the property table
inline uint32_t Hash();
- static uint32_t ComputeLengthAndHashField(unibrow::CharacterStream* buffer,
- int length);
+ static uint32_t ComputeHashField(unibrow::CharacterStream* buffer,
+ int length);
static bool ComputeArrayIndex(unibrow::CharacterStream* buffer,
uint32_t* index,
@@ -4018,7 +3825,8 @@ class String: public HeapObject {
// Layout description.
static const int kLengthOffset = HeapObject::kHeaderSize;
- static const int kSize = kLengthOffset + kIntSize;
+ static const int kHashFieldOffset = kLengthOffset + kIntSize;
+ static const int kSize = kHashFieldOffset + kIntSize;
// Notice: kSize is not pointer-size aligned if pointers are 64-bit.
// Maximum number of characters to consider when trying to convert a string
@@ -4042,22 +3850,30 @@ class String: public HeapObject {
static const int kIsArrayIndexMask = 1 << 1;
static const int kNofLengthBitFields = 2;
+ // Shift constant retrieving hash code from hash field.
+ static const int kHashShift = kNofLengthBitFields;
+
// Array index strings this short can keep their index in the hash
// field.
static const int kMaxCachedArrayIndexLength = 7;
- // Shift constants for retrieving length and hash code from
- // length/hash field.
- static const int kHashShift = kNofLengthBitFields;
- static const int kShortLengthShift = kHashShift + kShortStringTag;
- static const int kMediumLengthShift = kHashShift + kMediumStringTag;
- static const int kLongLengthShift = kHashShift + kLongStringTag;
+ // For strings which are array indexes the hash value has the string length
+ // mixed into the hash, mainly to avoid a hash value of zero which would be
+ // the case for the string '0'. 24 bits are used for the array index value.
+ static const int kArrayIndexHashLengthShift = 24 + kNofLengthBitFields;
+ static const int kArrayIndexHashMask = (1 << kArrayIndexHashLengthShift) - 1;
+ static const int kArrayIndexValueBits =
+ kArrayIndexHashLengthShift - kHashShift;
+
+ // Value of empty hash field indicating that the hash is not computed.
+ static const int kEmptyHashField = 0;
+
+ // Maximal string length.
+ static const int kMaxLength = (1 << (32 - 2)) - 1;
- // Maximal string length that can be stored in the hash/length field for
- // different types of strings.
- static const int kMaxShortSize = (1 << (32 - kShortLengthShift)) - 1;
- static const int kMaxMediumSize = (1 << (32 - kMediumLengthShift)) - 1;
- static const int kMaxLength = (1 << (32 - kLongLengthShift)) - 1;
+ // Max length for computing hash. For strings longer than this limit the
+ // string length is used as the hash value.
+ static const int kMaxHashCalcLength = 16383;
// Limit for truncation in short printing.
static const int kMaxShortPrintLength = 1024;
@@ -4339,9 +4155,6 @@ class ExternalAsciiString: public ExternalString {
unsigned* offset,
unsigned chars);
- // Identify the map for the external string/symbol with a particular length.
- static inline Map* StringMap(int length);
- static inline Map* SymbolMap(int length);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalAsciiString);
};
@@ -4374,9 +4187,6 @@ class ExternalTwoByteString: public ExternalString {
unsigned* offset_ptr,
unsigned chars);
- // Identify the map for the external string/symbol with a particular length.
- static inline Map* StringMap(int length);
- static inline Map* SymbolMap(int length);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalTwoByteString);
};
diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc
index 9b452fa9e..353d16540 100644
--- a/deps/v8/src/platform-freebsd.cc
+++ b/deps/v8/src/platform-freebsd.cc
@@ -89,11 +89,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() {
}
-double OS::nan_value() {
- return NAN;
-}
-
-
int OS::ActivationFrameAlignment() {
// 16 byte alignment on FreeBSD
return 16;
diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc
index 9ce0be011..bfcd8fba7 100644
--- a/deps/v8/src/platform-linux.cc
+++ b/deps/v8/src/platform-linux.cc
@@ -95,11 +95,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() {
}
-double OS::nan_value() {
- return NAN;
-}
-
-
#ifdef __arm__
bool OS::ArmCpuHasFeature(CpuFeature feature) {
const char* search_string = NULL;
diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc
index d79cff14a..0d5be45ee 100644
--- a/deps/v8/src/platform-macos.cc
+++ b/deps/v8/src/platform-macos.cc
@@ -252,11 +252,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() {
}
-double OS::nan_value() {
- return NAN;
-}
-
-
int OS::ActivationFrameAlignment() {
// OS X activation frames must be 16 byte-aligned; see "Mac OS X ABI
// Function Call Guide".
diff --git a/deps/v8/src/platform-openbsd.cc b/deps/v8/src/platform-openbsd.cc
new file mode 100644
index 000000000..6d273047d
--- /dev/null
+++ b/deps/v8/src/platform-openbsd.cc
@@ -0,0 +1,597 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Platform specific code for OpenBSD goes here. For the POSIX comaptible parts
+// the implementation is in platform-posix.cc.
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include <sys/types.h> // mmap & munmap
+#include <sys/mman.h> // mmap & munmap
+#include <sys/stat.h> // open
+#include <sys/fcntl.h> // open
+#include <unistd.h> // getpagesize
+#include <execinfo.h> // backtrace, backtrace_symbols
+#include <strings.h> // index
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform.h"
+
+
+namespace v8 {
+namespace internal {
+
+// 0 is never a valid thread id on OpenBSD since tids and pids share a
+// name space and pid 0 is used to kill the group (see man 2 kill).
+static const pthread_t kNoThread = (pthread_t) 0;
+
+
+double ceiling(double x) {
+ // Correct as on OS X
+ if (-1.0 < x && x < 0.0) {
+ return -0.0;
+ } else {
+ return ceil(x);
+ }
+}
+
+
+void OS::Setup() {
+ // Seed the random number generator.
+ // Convert the current time to a 64-bit integer first, before converting it
+ // to an unsigned. Going directly can cause an overflow and the seed to be
+ // set to all ones. The seed will be identical for different instances that
+ // call this setup code within the same millisecond.
+ uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
+ srandom(static_cast<unsigned int>(seed));
+}
+
+
+uint64_t OS::CpuFeaturesImpliedByPlatform() {
+ return 0; // OpenBSD runs on anything.
+}
+
+
+int OS::ActivationFrameAlignment() {
+ // 16 byte alignment on OpenBSD
+ return 16;
+}
+
+
+// We keep the lowest and highest addresses mapped as a quick way of
+// determining that pointers are outside the heap (used mostly in assertions
+// and verification). The estimate is conservative, ie, not all addresses in
+// 'allocated' space are actually allocated to our heap. The range is
+// [lowest, highest), inclusive on the low and and exclusive on the high end.
+static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+size_t OS::AllocateAlignment() {
+ return getpagesize();
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool executable) {
+ const size_t msize = RoundUp(requested, getpagesize());
+ int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
+ void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (mbase == MAP_FAILED) {
+ LOG(StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::Free(void* buf, const size_t length) {
+ int result = munmap(buf, length);
+ USE(result);
+ ASSERT(result == 0);
+}
+
+
+#ifdef ENABLE_HEAP_PROTECTION
+
+void OS::Protect(void* address, size_t size) {
+ UNIMPLEMENTED();
+}
+
+
+void OS::Unprotect(void* address, size_t size, bool is_executable) {
+ UNIMPLEMENTED();
+}
+
+#endif
+
+
+void OS::Sleep(int milliseconds) {
+ unsigned int ms = static_cast<unsigned int>(milliseconds);
+ usleep(1000 * ms);
+}
+
+
+void OS::Abort() {
+ // Redirect to std abort to signal abnormal program termination.
+ abort();
+}
+
+
+void OS::DebugBreak() {
+#if defined(__arm__) || defined(__thumb__)
+ asm("bkpt 0");
+#else
+ asm("int $3");
+#endif
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) munmap(memory_, size_);
+ fclose(file_);
+}
+
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+static unsigned StringToLong(char* buffer) {
+ return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
+}
+#endif
+
+
+void OS::LogSharedLibraryAddresses() {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ static const int MAP_LENGTH = 1024;
+ int fd = open("/proc/self/maps", O_RDONLY);
+ if (fd < 0) return;
+ while (true) {
+ char addr_buffer[11];
+ addr_buffer[0] = '0';
+ addr_buffer[1] = 'x';
+ addr_buffer[10] = 0;
+ int result = read(fd, addr_buffer + 2, 8);
+ if (result < 8) break;
+ unsigned start = StringToLong(addr_buffer);
+ result = read(fd, addr_buffer + 2, 1);
+ if (result < 1) break;
+ if (addr_buffer[2] != '-') break;
+ result = read(fd, addr_buffer + 2, 8);
+ if (result < 8) break;
+ unsigned end = StringToLong(addr_buffer);
+ char buffer[MAP_LENGTH];
+ int bytes_read = -1;
+ do {
+ bytes_read++;
+ if (bytes_read >= MAP_LENGTH - 1)
+ break;
+ result = read(fd, buffer + bytes_read, 1);
+ if (result < 1) break;
+ } while (buffer[bytes_read] != '\n');
+ buffer[bytes_read] = 0;
+ // Ignore mappings that are not executable.
+ if (buffer[3] != 'x') continue;
+ char* start_of_path = index(buffer, '/');
+ // There may be no filename in this line. Skip to next.
+ if (start_of_path == NULL) continue;
+ buffer[bytes_read] = 0;
+ LOG(SharedLibraryEvent(start_of_path, start, end));
+ }
+ close(fd);
+#endif
+}
+
+
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ UNIMPLEMENTED();
+ return 1;
+}
+
+
+// Constants used for mmap.
+static const int kMmapFd = -1;
+static const int kMmapFdOffset = 0;
+
+
+VirtualMemory::VirtualMemory(size_t size) {
+ address_ = mmap(NULL, size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ kMmapFd, kMmapFdOffset);
+ size_ = size;
+}
+
+
+VirtualMemory::~VirtualMemory() {
+ if (IsReserved()) {
+ if (0 == munmap(address(), size())) address_ = MAP_FAILED;
+ }
+}
+
+
+bool VirtualMemory::IsReserved() {
+ return address_ != MAP_FAILED;
+}
+
+
+bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
+ int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
+ if (MAP_FAILED == mmap(address, size, prot,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ kMmapFd, kMmapFdOffset)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(address, size);
+ return true;
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return mmap(address, size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ kMmapFd, kMmapFdOffset) != MAP_FAILED;
+}
+
+
+class ThreadHandle::PlatformData : public Malloced {
+ public:
+ explicit PlatformData(ThreadHandle::Kind kind) {
+ Initialize(kind);
+ }
+
+ void Initialize(ThreadHandle::Kind kind) {
+ switch (kind) {
+ case ThreadHandle::SELF: thread_ = pthread_self(); break;
+ case ThreadHandle::INVALID: thread_ = kNoThread; break;
+ }
+ }
+ pthread_t thread_; // Thread handle for pthread.
+};
+
+
+ThreadHandle::ThreadHandle(Kind kind) {
+ data_ = new PlatformData(kind);
+}
+
+
+void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
+ data_->Initialize(kind);
+}
+
+
+ThreadHandle::~ThreadHandle() {
+ delete data_;
+}
+
+
+bool ThreadHandle::IsSelf() const {
+ return pthread_equal(data_->thread_, pthread_self());
+}
+
+
+bool ThreadHandle::IsValid() const {
+ return data_->thread_ != kNoThread;
+}
+
+
+Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+}
+
+
+Thread::~Thread() {
+}
+
+
+static void* ThreadEntry(void* arg) {
+ Thread* thread = reinterpret_cast<Thread*>(arg);
+ // This is also initialized by the first argument to pthread_create() but we
+ // don't know which thread will run first (the original thread or the new
+ // one) so we initialize it here too.
+ thread->thread_handle_data()->thread_ = pthread_self();
+ ASSERT(thread->IsValid());
+ thread->Run();
+ return NULL;
+}
+
+
+void Thread::Start() {
+ pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
+ ASSERT(IsValid());
+}
+
+
+void Thread::Join() {
+ pthread_join(thread_handle_data()->thread_, NULL);
+}
+
+
+Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
+ pthread_key_t key;
+ int result = pthread_key_create(&key, NULL);
+ USE(result);
+ ASSERT(result == 0);
+ return static_cast<LocalStorageKey>(key);
+}
+
+
+void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
+ int result = pthread_key_delete(pthread_key);
+ USE(result);
+ ASSERT(result == 0);
+}
+
+
+void* Thread::GetThreadLocal(LocalStorageKey key) {
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
+ return pthread_getspecific(pthread_key);
+}
+
+
+void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
+ pthread_setspecific(pthread_key, value);
+}
+
+
+void Thread::YieldCPU() {
+ sched_yield();
+}
+
+
+class OpenBSDMutex : public Mutex {
+ public:
+
+ OpenBSDMutex() {
+ pthread_mutexattr_t attrs;
+ int result = pthread_mutexattr_init(&attrs);
+ ASSERT(result == 0);
+ result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
+ ASSERT(result == 0);
+ result = pthread_mutex_init(&mutex_, &attrs);
+ ASSERT(result == 0);
+ }
+
+ virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
+
+ virtual int Lock() {
+ int result = pthread_mutex_lock(&mutex_);
+ return result;
+ }
+
+ virtual int Unlock() {
+ int result = pthread_mutex_unlock(&mutex_);
+ return result;
+ }
+
+ private:
+ pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
+};
+
+
+Mutex* OS::CreateMutex() {
+ return new OpenBSDMutex();
+}
+
+
+class OpenBSDSemaphore : public Semaphore {
+ public:
+ explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void OpenBSDSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+bool OpenBSDSemaphore::Wait(int timeout) {
+ const long kOneSecondMicros = 1000000; // NOLINT
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+ while (true) {
+ int result = sem_trywait(&sem_);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new OpenBSDSemaphore(count);
+}
+
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+static Sampler* active_sampler_ = NULL;
+
+static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
+ USE(info);
+ if (signal != SIGPROF) return;
+ if (active_sampler_ == NULL) return;
+
+ TickSample sample;
+
+ // We always sample the VM state.
+ sample.state = Logger::state();
+
+ active_sampler_->Tick(&sample);
+}
+
+
+class Sampler::PlatformData : public Malloced {
+ public:
+ PlatformData() {
+ signal_handler_installed_ = false;
+ }
+
+ bool signal_handler_installed_;
+ struct sigaction old_signal_handler_;
+ struct itimerval old_timer_value_;
+};
+
+
+Sampler::Sampler(int interval, bool profiling)
+ : interval_(interval), profiling_(profiling), active_(false) {
+ data_ = new PlatformData();
+}
+
+
+Sampler::~Sampler() {
+ delete data_;
+}
+
+
+void Sampler::Start() {
+ // There can only be one active sampler at the time on POSIX
+ // platforms.
+ if (active_sampler_ != NULL) return;
+
+ // Request profiling signals.
+ struct sigaction sa;
+ sa.sa_sigaction = ProfilerSignalHandler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
+ data_->signal_handler_installed_ = true;
+
+ // Set the itimer to generate a tick for each interval.
+ itimerval itimer;
+ itimer.it_interval.tv_sec = interval_ / 1000;
+ itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
+ itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
+ itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
+ setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
+
+ // Set this sampler as the active sampler.
+ active_sampler_ = this;
+ active_ = true;
+}
+
+
+void Sampler::Stop() {
+ // Restore old signal handler
+ if (data_->signal_handler_installed_) {
+ setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
+ sigaction(SIGPROF, &data_->old_signal_handler_, 0);
+ data_->signal_handler_installed_ = false;
+ }
+
+ // This sampler is no longer the active sampler.
+ active_sampler_ = NULL;
+ active_ = false;
+}
+
+#endif // ENABLE_LOGGING_AND_PROFILING
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc
index 1e1245c5d..41e0e64f3 100644
--- a/deps/v8/src/platform-posix.cc
+++ b/deps/v8/src/platform-posix.cc
@@ -27,7 +27,7 @@
// Platform specific code for POSIX goes here. This is not a platform on its
// own but contains the parts which are the same across POSIX platforms Linux,
-// Mac OS and FreeBSD.
+// Mac OS, FreeBSD and OpenBSD.
#include <unistd.h>
#include <errno.h>
@@ -61,6 +61,13 @@ double modulo(double x, double y) {
return fmod(x, y);
}
+
+double OS::nan_value() {
+ // NAN from math.h is defined in C99 and not in POSIX.
+ return NAN;
+}
+
+
// ----------------------------------------------------------------------------
// POSIX date/time support.
//
diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc
index 6ae22330a..65dfd1326 100644
--- a/deps/v8/src/runtime.cc
+++ b/deps/v8/src/runtime.cc
@@ -788,51 +788,72 @@ static Object* Runtime_InitializeVarGlobal(Arguments args) {
// case of callbacks in the prototype chain (this rules out using
// SetProperty). We have IgnoreAttributesAndSetLocalProperty for
// this.
+ // Note that objects can have hidden prototypes, so we need to traverse
+ // the whole chain of hidden prototypes to do a 'local' lookup.
+ JSObject* real_holder = global;
LookupResult lookup;
- global->LocalLookup(*name, &lookup);
- if (!lookup.IsProperty()) {
- if (assign) {
- return global->IgnoreAttributesAndSetLocalProperty(*name,
- args[1],
- attributes);
+ while (true) {
+ real_holder->LocalLookup(*name, &lookup);
+ if (lookup.IsProperty()) {
+ // Determine if this is a redeclaration of something read-only.
+ if (lookup.IsReadOnly()) {
+ // If we found readonly property on one of hidden prototypes,
+ // just shadow it.
+ if (real_holder != Top::context()->global()) break;
+ return ThrowRedeclarationError("const", name);
+ }
+
+ // Determine if this is a redeclaration of an intercepted read-only
+ // property and figure out if the property exists at all.
+ bool found = true;
+ PropertyType type = lookup.type();
+ if (type == INTERCEPTOR) {
+ HandleScope handle_scope;
+ Handle<JSObject> holder(real_holder);
+ PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
+ real_holder = *holder;
+ if (intercepted == ABSENT) {
+ // The interceptor claims the property isn't there. We need to
+ // make sure to introduce it.
+ found = false;
+ } else if ((intercepted & READ_ONLY) != 0) {
+ // The property is present, but read-only. Since we're trying to
+ // overwrite it with a variable declaration we must throw a
+ // re-declaration error. However if we found readonly property
+ // on one of hidden prototypes, just shadow it.
+ if (real_holder != Top::context()->global()) break;
+ return ThrowRedeclarationError("const", name);
+ }
+ }
+
+ if (found && !assign) {
+ // The global property is there and we're not assigning any value
+ // to it. Just return.
+ return Heap::undefined_value();
+ }
+
+ // Assign the value (or undefined) to the property.
+ Object* value = (assign) ? args[1] : Heap::undefined_value();
+ return real_holder->SetProperty(&lookup, *name, value, attributes);
}
- return Heap::undefined_value();
- }
- // Determine if this is a redeclaration of something read-only.
- if (lookup.IsReadOnly()) {
- return ThrowRedeclarationError("const", name);
- }
+ Object* proto = real_holder->GetPrototype();
+ if (!proto->IsJSObject())
+ break;
- // Determine if this is a redeclaration of an intercepted read-only
- // property and figure out if the property exists at all.
- bool found = true;
- PropertyType type = lookup.type();
- if (type == INTERCEPTOR) {
- PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
- if (intercepted == ABSENT) {
- // The interceptor claims the property isn't there. We need to
- // make sure to introduce it.
- found = false;
- } else if ((intercepted & READ_ONLY) != 0) {
- // The property is present, but read-only. Since we're trying to
- // overwrite it with a variable declaration we must throw a
- // re-declaration error.
- return ThrowRedeclarationError("const", name);
- }
- // Restore global object from context (in case of GC).
- global = Top::context()->global();
- }
+ if (!JSObject::cast(proto)->map()->is_hidden_prototype())
+ break;
- if (found && !assign) {
- // The global property is there and we're not assigning any value
- // to it. Just return.
- return Heap::undefined_value();
+ real_holder = JSObject::cast(proto);
}
- // Assign the value (or undefined) to the property.
- Object* value = (assign) ? args[1] : Heap::undefined_value();
- return global->SetProperty(&lookup, *name, value, attributes);
+ global = Top::context()->global();
+ if (assign) {
+ return global->IgnoreAttributesAndSetLocalProperty(*name,
+ args[1],
+ attributes);
+ }
+ return Heap::undefined_value();
}
@@ -3762,6 +3783,7 @@ static Object* Runtime_StringAdd(Arguments args) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, str1, args[0]);
CONVERT_CHECKED(String, str2, args[1]);
+ Counters::string_add_runtime.Increment();
return Heap::AllocateConsString(str1, str2);
}
@@ -4987,6 +5009,9 @@ static Object* Runtime_DebugPrint(Arguments args) {
PrintF("DebugPrint: ");
}
args[0]->Print();
+ if (args[0]->IsHeapObject()) {
+ HeapObject::cast(args[0])->map()->Print();
+ }
#else
// ShortPrint is available in release mode. Print is not.
args[0]->ShortPrint();
@@ -7667,8 +7692,31 @@ static Object* Runtime_FunctionGetInferredName(Arguments args) {
CONVERT_CHECKED(JSFunction, f, args[0]);
return f->shared()->inferred_name();
}
+
#endif // ENABLE_DEBUGGER_SUPPORT
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+static Object* Runtime_ProfilerResume(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 1);
+
+ CONVERT_CHECKED(Smi, smi_modules, args[0]);
+ v8::V8::ResumeProfilerEx(smi_modules->value());
+ return Heap::undefined_value();
+}
+
+
+static Object* Runtime_ProfilerPause(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 1);
+
+ CONVERT_CHECKED(Smi, smi_modules, args[0]);
+ v8::V8::PauseProfilerEx(smi_modules->value());
+ return Heap::undefined_value();
+}
+
+#endif // ENABLE_LOGGING_AND_PROFILING
// Finds the script object from the script data. NOTE: This operation uses
// heap traversal to find the function generated for the source position
diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h
index c05ae6b7b..858023317 100644
--- a/deps/v8/src/runtime.h
+++ b/deps/v8/src/runtime.h
@@ -318,6 +318,14 @@ namespace internal {
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
#endif
+#ifdef ENABLE_LOGGING_AND_PROFILING
+#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) \
+ F(ProfilerResume, 1, 1) \
+ F(ProfilerPause, 1, 1)
+#else
+#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F)
+#endif
+
#ifdef DEBUG
#define RUNTIME_FUNCTION_LIST_DEBUG(F) \
/* Testing */ \
@@ -336,7 +344,8 @@ namespace internal {
RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \
RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
RUNTIME_FUNCTION_LIST_DEBUG(F) \
- RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
+ RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \
+ RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F)
// ----------------------------------------------------------------------------
// Runtime provides access to all C++ runtime functions.
diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js
index ba19871f1..105749a75 100644
--- a/deps/v8/src/runtime.js
+++ b/deps/v8/src/runtime.js
@@ -146,16 +146,16 @@ function COMPARE(x, ncr) {
function ADD(x) {
// Fast case: Check for number operands and do the addition.
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
- if (IS_STRING(this) && IS_STRING(x)) return %StringAdd(this, x);
+ if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);
// Default implementation.
var a = %ToPrimitive(this, NO_HINT);
var b = %ToPrimitive(x, NO_HINT);
if (IS_STRING(a)) {
- return %StringAdd(a, %ToString(b));
+ return %_StringAdd(a, %ToString(b));
} else if (IS_STRING(b)) {
- return %StringAdd(%ToString(a), b);
+ return %_StringAdd(%ToString(a), b);
} else {
return %NumberAdd(%ToNumber(a), %ToNumber(b));
}
@@ -173,7 +173,7 @@ function STRING_ADD_LEFT(y) {
: %ToString(%ToPrimitive(y, NO_HINT));
}
}
- return %StringAdd(this, y);
+ return %_StringAdd(this, y);
}
@@ -189,7 +189,7 @@ function STRING_ADD_RIGHT(y) {
: %ToString(%ToPrimitive(x, NO_HINT));
}
}
- return %StringAdd(x, y);
+ return %_StringAdd(x, y);
}
diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc
index 00cd69ec0..899e2e7a5 100644
--- a/deps/v8/src/serialize.cc
+++ b/deps/v8/src/serialize.cc
@@ -44,6 +44,69 @@
namespace v8 {
namespace internal {
+// Mapping objects to their location after deserialization.
+// This is used during building, but not at runtime by V8.
+class SerializationAddressMapper {
+ public:
+ static bool IsMapped(HeapObject* obj) {
+ EnsureMapExists();
+ return serialization_map_->Lookup(Key(obj), Hash(obj), false) != NULL;
+ }
+
+ static int MappedTo(HeapObject* obj) {
+ ASSERT(IsMapped(obj));
+ return reinterpret_cast<intptr_t>(serialization_map_->Lookup(Key(obj),
+ Hash(obj),
+ false)->value);
+ }
+
+ static void Map(HeapObject* obj, int to) {
+ EnsureMapExists();
+ ASSERT(!IsMapped(obj));
+ HashMap::Entry* entry =
+ serialization_map_->Lookup(Key(obj), Hash(obj), true);
+ entry->value = Value(to);
+ }
+
+ static void Zap() {
+ if (serialization_map_ != NULL) {
+ delete serialization_map_;
+ }
+ serialization_map_ = NULL;
+ }
+
+ private:
+ static bool SerializationMatchFun(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
+ static uint32_t Hash(HeapObject* obj) {
+ return reinterpret_cast<intptr_t>(obj->address());
+ }
+
+ static void* Key(HeapObject* obj) {
+ return reinterpret_cast<void*>(obj->address());
+ }
+
+ static void* Value(int v) {
+ return reinterpret_cast<void*>(v);
+ }
+
+ static void EnsureMapExists() {
+ if (serialization_map_ == NULL) {
+ serialization_map_ = new HashMap(&SerializationMatchFun);
+ }
+ }
+
+ static HashMap* serialization_map_;
+};
+
+
+HashMap* SerializationAddressMapper::serialization_map_ = NULL;
+
+
+
+
// -----------------------------------------------------------------------------
// Coding of external references.
@@ -871,6 +934,7 @@ void Serializer::Serialize() {
Heap::IterateRoots(this, VISIT_ONLY_STRONG);
delete external_reference_encoder_;
external_reference_encoder_ = NULL;
+ SerializationAddressMapper::Zap();
}
@@ -894,10 +958,9 @@ void Serializer::SerializeObject(
ReferenceRepresentation reference_representation) {
CHECK(o->IsHeapObject());
HeapObject* heap_object = HeapObject::cast(o);
- MapWord map_word = heap_object->map_word();
- if (map_word.IsSerializationAddress()) {
+ if (SerializationAddressMapper::IsMapped(heap_object)) {
int space = SpaceOfAlreadySerializedObject(heap_object);
- int address = map_word.ToSerializationAddress();
+ int address = SerializationAddressMapper::MappedTo(heap_object);
int offset = CurrentAllocationAddress(space) - address;
bool from_start = true;
if (SpaceIsPaged(space)) {
@@ -965,24 +1028,23 @@ void Serializer::ObjectSerializer::Serialize() {
}
sink_->PutInt(size >> kObjectAlignmentBits, "Size in words");
- // Get the map before overwriting it.
- Map* map = object_->map();
// Mark this object as already serialized.
bool start_new_page;
- object_->set_map_word(MapWord::FromSerializationAddress(
- serializer_->Allocate(space, size, &start_new_page)));
+ SerializationAddressMapper::Map(
+ object_,
+ serializer_->Allocate(space, size, &start_new_page));
if (start_new_page) {
sink_->Put(START_NEW_PAGE_SERIALIZATION, "NewPage");
sink_->PutSection(space, "NewPageSpace");
}
// Serialize the map (first word of the object).
- serializer_->SerializeObject(map, TAGGED_REPRESENTATION);
+ serializer_->SerializeObject(object_->map(), TAGGED_REPRESENTATION);
// Serialize the rest of the object.
CHECK_EQ(0, bytes_processed_so_far_);
bytes_processed_so_far_ = kPointerSize;
- object_->IterateBody(map->instance_type(), size, this);
+ object_->IterateBody(object_->map()->instance_type(), size, this);
OutputRawData(object_->address() + size);
}
@@ -1044,12 +1106,9 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString(
Address references_start = reinterpret_cast<Address>(resource_pointer);
OutputRawData(references_start);
for (int i = 0; i < Natives::GetBuiltinsCount(); i++) {
- // Use raw_unchecked when maps are munged.
- Object* source = Heap::raw_unchecked_natives_source_cache()->get(i);
+ Object* source = Heap::natives_source_cache()->get(i);
if (!source->IsUndefined()) {
- // Don't use cast when maps are munged.
- ExternalAsciiString* string =
- reinterpret_cast<ExternalAsciiString*>(source);
+ ExternalAsciiString* string = ExternalAsciiString::cast(source);
typedef v8::String::ExternalAsciiStringResource Resource;
Resource* resource = string->resource();
if (resource == *resource_pointer) {
diff --git a/deps/v8/src/string-stream.cc b/deps/v8/src/string-stream.cc
index eb5d1e31e..d1859a20f 100644
--- a/deps/v8/src/string-stream.cc
+++ b/deps/v8/src/string-stream.cc
@@ -188,7 +188,7 @@ void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
void StringStream::PrintObject(Object* o) {
o->ShortPrint(this);
if (o->IsString()) {
- if (String::cast(o)->length() <= String::kMaxMediumSize) {
+ if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
return;
}
} else if (o->IsNumber() || o->IsOddball()) {
diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc
index a399e4563..51d9ddb8f 100644
--- a/deps/v8/src/stub-cache.cc
+++ b/deps/v8/src/stub-cache.cc
@@ -750,6 +750,9 @@ Object* LoadCallbackProperty(Arguments args) {
{
// Leaving JavaScript.
VMState state(EXTERNAL);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ state.set_external_callback(getter_address);
+#endif
result = fun(v8::Utils::ToLocal(args.at<String>(4)), info);
}
RETURN_IF_SCHEDULED_EXCEPTION();
@@ -773,6 +776,9 @@ Object* StoreCallbackProperty(Arguments args) {
{
// Leaving JavaScript.
VMState state(EXTERNAL);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ state.set_external_callback(setter_address);
+#endif
fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info);
}
RETURN_IF_SCHEDULED_EXCEPTION();
diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h
index e26892026..788c5324f 100644
--- a/deps/v8/src/stub-cache.h
+++ b/deps/v8/src/stub-cache.h
@@ -226,9 +226,9 @@ class StubCache : public AllStatic {
// hash code would effectively throw away two bits of the hash
// code.
ASSERT(kHeapObjectTagSize == String::kHashShift);
- // Compute the hash of the name (use entire length field).
+ // Compute the hash of the name (use entire hash field).
ASSERT(name->HasHashCode());
- uint32_t field = name->length_field();
+ uint32_t field = name->hash_field();
// Using only the low bits in 64-bit mode is unlikely to increase the
// risk of collision even if the heap is spread over an area larger than
// 4Gb (and not at all if it isn't).
diff --git a/deps/v8/src/utils.cc b/deps/v8/src/utils.cc
index ce5aceda3..08ee16ff2 100644
--- a/deps/v8/src/utils.cc
+++ b/deps/v8/src/utils.cc
@@ -309,4 +309,13 @@ char* StringBuilder::Finalize() {
return buffer_.start();
}
+
+int TenToThe(int exponent) {
+ ASSERT(exponent <= 9);
+ ASSERT(exponent >= 1);
+ int answer = 10;
+ for (int i = 1; i < exponent; i++) answer *= 10;
+ return answer;
+}
+
} } // namespace v8::internal
diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h
index c271ae17f..0fd24ec9a 100644
--- a/deps/v8/src/utils.h
+++ b/deps/v8/src/utils.h
@@ -584,6 +584,9 @@ static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
}
+// Calculate 10^exponent.
+int TenToThe(int exponent);
+
} } // namespace v8::internal
#endif // V8_UTILS_H_
diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h
index b3f29f530..d6f53fab1 100644
--- a/deps/v8/src/v8-counters.h
+++ b/deps/v8/src/v8-counters.h
@@ -153,8 +153,9 @@ namespace internal {
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
SC(compute_entry_frame, V8.ComputeEntryFrame) \
SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
- SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs)
-
+ SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
+ SC(string_add_runtime, V8.StringAddRuntime) \
+ SC(string_add_native, V8.StringAddNative)
// This file contains all the v8 counters that are in use.
class Counters : AllStatic {
diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc
index f9dc9b75f..36a42f8f3 100644
--- a/deps/v8/src/version.cc
+++ b/deps/v8/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 0
-#define BUILD_NUMBER 2
+#define BUILD_NUMBER 3
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h
index 50f4e0e45..fa7d33b1b 100644
--- a/deps/v8/src/x64/assembler-x64.h
+++ b/deps/v8/src/x64/assembler-x64.h
@@ -482,6 +482,12 @@ class Assembler : public Malloced {
static const int kPatchReturnSequenceAddressOffset = 13 - 4;
// TODO(X64): Rename this, removing the "Real", after changing the above.
static const int kRealPatchReturnSequenceAddressOffset = 2;
+
+ // The x64 JS return sequence is padded with int3 to make it large
+ // enough to hold a call instruction when the debugger patches it.
+ static const int kCallInstructionLength = 13;
+ static const int kJSReturnSequenceLength = 13;
+
// ---------------------------------------------------------------------------
// Code generation
//
diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc
index b32357266..36f0e635f 100644
--- a/deps/v8/src/x64/codegen-x64.cc
+++ b/deps/v8/src/x64/codegen-x64.cc
@@ -505,13 +505,13 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) {
// Add padding that will be overwritten by a debugger breakpoint.
// frame_->Exit() generates "movq rsp, rbp; pop rbp; ret k"
// with length 7 (3 + 1 + 3).
- const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+ const int kPadding = Assembler::kJSReturnSequenceLength - 7;
for (int i = 0; i < kPadding; ++i) {
masm_->int3();
}
// Check that the size of the code used for returning matches what is
// expected by the debugger.
- ASSERT_EQ(Debug::kX64JSReturnSequenceLength,
+ ASSERT_EQ(Assembler::kJSReturnSequenceLength,
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
DeleteFrame();
@@ -1662,8 +1662,54 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
jsobject.Bind();
// Get the set of properties (as a FixedArray or Map).
// rax: value to be iterated over
- frame_->EmitPush(rax); // push the object being iterated over (slot 4)
+ frame_->EmitPush(rax); // Push the object being iterated over.
+
+ // Check cache validity in generated code. This is a fast case for
+ // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+ // guarantee cache validity, call the runtime system to check cache
+ // validity or get the property names in a fixed array.
+ JumpTarget call_runtime;
+ JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ JumpTarget check_prototype;
+ JumpTarget use_cache;
+ __ movq(rcx, rax);
+ loop.Bind();
+ // Check that there are no elements.
+ __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset));
+ __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
+ call_runtime.Branch(not_equal);
+ // Check that instance descriptors are not empty so that we can
+ // check for an enum cache. Leave the map in ebx for the subsequent
+ // prototype load.
+ __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
+ __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex);
+ call_runtime.Branch(equal);
+ // Check that there in an enum cache in the non-empty instance
+ // descriptors. This is the case if the next enumeration index
+ // field does not contain a smi.
+ __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset));
+ is_smi = masm_->CheckSmi(rdx);
+ call_runtime.Branch(is_smi);
+ // For all objects but the receiver, check that the cache is empty.
+ __ cmpq(rcx, rax);
+ check_prototype.Branch(equal);
+ __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset));
+ __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
+ call_runtime.Branch(not_equal);
+ check_prototype.Bind();
+ // Load the prototype from the map and loop if non-null.
+ __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
+ __ CompareRoot(rcx, Heap::kNullValueRootIndex);
+ loop.Branch(not_equal);
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
+ use_cache.Jump();
+
+ call_runtime.Bind();
+ // Call the runtime to get the property names for the object.
frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call
frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
@@ -1676,8 +1722,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
__ CompareRoot(rcx, Heap::kMetaMapRootIndex);
fixed_array.Branch(not_equal);
+ use_cache.Bind();
// Get enum cache
- // rax: map (result from call to Runtime::kGetPropertyNamesFast)
+ // rax: map (either the result from a call to
+ // Runtime::kGetPropertyNamesFast or has been fetched directly from
+ // the object)
__ movq(rcx, rax);
__ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset));
// Get the bridge array held in the enumeration index field.
@@ -3767,20 +3816,8 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
__ testb(rcx, Immediate(kIsNotStringMask));
__ j(not_zero, &slow_case);
- // Here we make assumptions about the tag values and the shifts needed.
- // See the comment in objects.h.
- ASSERT(kLongStringTag == 0);
- ASSERT(kMediumStringTag + String::kLongLengthShift ==
- String::kMediumLengthShift);
- ASSERT(kShortStringTag + String::kLongLengthShift ==
- String::kShortLengthShift);
- __ and_(rcx, Immediate(kStringSizeMask));
- __ addq(rcx, Immediate(String::kLongLengthShift));
- // Fetch the length field into the temporary register.
- __ movl(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
- __ shrl_cl(temp.reg());
// Check for index out of range.
- __ cmpl(index.reg(), temp.reg());
+ __ cmpl(index.reg(), FieldOperand(object.reg(), String::kLengthOffset));
__ j(greater_equal, &slow_case);
// Reload the instance type (into the temp register this time)..
__ movq(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
@@ -4008,6 +4045,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
+ ASSERT_EQ(2, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+
+ Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
+ frame_->Push(&answer);
+}
+
+
void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
JumpTarget leave, null, function, non_function_constructor;
@@ -6175,11 +6223,8 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
// String value => false iff empty.
__ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE));
__ j(above_equal, &not_string);
- __ and_(rcx, Immediate(kStringSizeMask));
- __ cmpq(rcx, Immediate(kShortStringTag));
- __ j(not_equal, &true_result); // Empty string is always short.
__ movl(rdx, FieldOperand(rax, String::kLengthOffset));
- __ shr(rdx, Immediate(String::kShortLengthShift));
+ __ testl(rdx, rdx);
__ j(zero, &false_result);
__ jmp(&true_result);
@@ -7732,9 +7777,47 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ push(rcx);
}
switch (op_) {
- case Token::ADD:
+ case Token::ADD: {
+ // Test for string arguments before calling runtime.
+ Label not_strings, both_strings, not_string1, string1;
+ Condition is_smi;
+ Result answer;
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // First argument.
+ __ movq(rax, Operand(rsp, 1 * kPointerSize)); // Second argument.
+ is_smi = masm->CheckSmi(rdx);
+ __ j(is_smi, &not_string1);
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rdx);
+ __ j(above_equal, &not_string1);
+
+ // First argument is a a string, test second.
+ is_smi = masm->CheckSmi(rax);
+ __ j(is_smi, &string1);
+ __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax);
+ __ j(above_equal, &string1);
+
+ // First and second argument are strings.
+ Runtime::Function* f = Runtime::FunctionForId(Runtime::kStringAdd);
+ __ TailCallRuntime(ExternalReference(f), 2, f->result_size);
+
+ // Only first argument is a string.
+ __ bind(&string1);
+ __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
+
+ // First argument was not a string, test second.
+ __ bind(&not_string1);
+ is_smi = masm->CheckSmi(rax);
+ __ j(is_smi, &not_strings);
+ __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax);
+ __ j(above_equal, &not_strings);
+
+ // Only second argument is a string.
+ __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
+
+ __ bind(&not_strings);
+ // Neither argument is a string.
__ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
break;
+ }
case Token::SUB:
__ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
break;
diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h
index 20df41a50..8539884aa 100644
--- a/deps/v8/src/x64/codegen-x64.h
+++ b/deps/v8/src/x64/codegen-x64.h
@@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
+ // Fast support for StringAdd.
+ void GenerateStringAdd(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc
index 49240b407..bc88d4668 100644
--- a/deps/v8/src/x64/debug-x64.cc
+++ b/deps/v8/src/x64/debug-x64.cc
@@ -181,7 +181,7 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
void BreakLocationIterator::ClearDebugBreakAtReturn() {
rinfo()->PatchCode(original_rinfo()->pc(),
- Debug::kX64JSReturnSequenceLength);
+ Assembler::kJSReturnSequenceLength);
}
@@ -191,9 +191,10 @@ bool BreakLocationIterator::IsDebugBreakAtReturn() {
void BreakLocationIterator::SetDebugBreakAtReturn() {
- ASSERT(Debug::kX64JSReturnSequenceLength >= Debug::kX64CallInstructionLength);
+ ASSERT(Assembler::kJSReturnSequenceLength >=
+ Assembler::kCallInstructionLength);
rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(),
- Debug::kX64JSReturnSequenceLength - Debug::kX64CallInstructionLength);
+ Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
}
#endif // ENABLE_DEBUGGER_SUPPORT
diff --git a/deps/v8/src/x64/fast-codegen-x64.cc b/deps/v8/src/x64/fast-codegen-x64.cc
index bb85ef5d6..f73f2b90f 100644
--- a/deps/v8/src/x64/fast-codegen-x64.cc
+++ b/deps/v8/src/x64/fast-codegen-x64.cc
@@ -76,11 +76,43 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
bool function_in_register = true;
+ // Possibly allocate a local context.
+ if (fun->scope()->num_heap_slots() > 0) {
+ Comment cmnt(masm_, "[ Allocate local context");
+ // Argument to NewContext is the function, which is still in rdi.
+ __ push(rdi);
+ __ CallRuntime(Runtime::kNewContext, 1);
+ function_in_register = false;
+ // Context is returned in both rax and rsi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in rsi.
+ __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
+
+ // Copy any necessary parameters into the context.
+ int num_parameters = fun->scope()->num_parameters();
+ for (int i = 0; i < num_parameters; i++) {
+ Slot* slot = fun->scope()->parameter(i)->slot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
+ (num_parameters - 1 - i) * kPointerSize;
+ // Load parameter from stack.
+ __ movq(rax, Operand(rbp, parameter_offset));
+ // Store it in the context
+ __ movq(Operand(rsi, Context::SlotOffset(slot->index())), rax);
+ }
+ }
+ }
+
+ // Possibly allocate an arguments object.
Variable* arguments = fun->scope()->arguments()->AsVariable();
if (arguments != NULL) {
- // Function uses arguments object.
+ // Arguments object must be allocated after the context object, in
+ // case the "arguments" or ".arguments" variables are in the context.
Comment cmnt(masm_, "[ Allocate arguments object");
- __ push(rdi);
+ if (function_in_register) {
+ __ push(rdi);
+ } else {
+ __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ }
// The receiver is just before the parameters on the caller's stack.
__ lea(rdx, Operand(rbp, StandardFrameConstants::kCallerSPOffset +
fun->num_parameters() * kPointerSize));
@@ -93,34 +125,11 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
__ CallStub(&stub);
// Store new arguments object in both "arguments" and ".arguments" slots.
- __ movq(Operand(rbp, SlotOffset(arguments->slot())), rax);
+ __ movq(rcx, rax);
+ Move(arguments->slot(), rax, rbx, rdx);
Slot* dot_arguments_slot =
fun->scope()->arguments_shadow()->AsVariable()->slot();
- __ movq(Operand(rbp, SlotOffset(dot_arguments_slot)), rax);
- function_in_register = false;
- }
-
- // Possibly allocate a local context.
- if (fun->scope()->num_heap_slots() > 0) {
- Comment cmnt(masm_, "[ Allocate local context");
- if (function_in_register) {
- // Argument to NewContext is the function, still in rdi.
- __ push(rdi);
- } else {
- // Argument to NewContext is the function, no longer in rdi.
- __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
- }
- __ CallRuntime(Runtime::kNewContext, 1);
- // Context is returned in both rax and rsi. It replaces the context
- // passed to us. It's saved in the stack and kept live in rsi.
- __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
-#ifdef DEBUG
- // Assert we do not have to copy any parameters into the context.
- for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) {
- Slot* slot = fun->scope()->parameter(i)->slot();
- ASSERT(slot != NULL && slot->type() != Slot::CONTEXT);
- }
-#endif
+ Move(dot_arguments_slot, rcx, rbx, rdx);
}
{ Comment cmnt(masm_, "[ Stack check");
@@ -180,13 +189,13 @@ void FastCodeGenerator::EmitReturnSequence(int position) {
// Add padding that will be overwritten by a debugger breakpoint. We
// have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
// (3 + 1 + 3).
- const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+ const int kPadding = Assembler::kJSReturnSequenceLength - 7;
for (int i = 0; i < kPadding; ++i) {
masm_->int3();
}
// Check that the size of the code used for returning matches what is
// expected by the debugger.
- ASSERT_EQ(Debug::kX64JSReturnSequenceLength,
+ ASSERT_EQ(Assembler::kJSReturnSequenceLength,
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
}
@@ -227,20 +236,54 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
-void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
+template <>
+Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source,
+ Register scratch) {
+ switch (source->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ return Operand(rbp, SlotOffset(source));
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ function_->scope()->ContextChainLength(source->var()->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return CodeGenerator::ContextOperand(scratch, source->index());
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ // Fall-through.
+ default:
+ UNREACHABLE();
+ return Operand(rax, 0); // Dead code to make the compiler happy.
+ }
+}
+
+
+void FastCodeGenerator::Move(Register dst, Slot* source) {
+ Operand location = CreateSlotOperand<Operand>(source, dst);
+ __ movq(dst, location);
+}
+
+
+void FastCodeGenerator::Move(Expression::Context context,
+ Slot* source,
+ Register scratch) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
break;
- case Expression::kValue:
- __ push(Operand(rbp, SlotOffset(source)));
+ case Expression::kValue: {
+ Operand location = CreateSlotOperand<Operand>(source, scratch);
+ __ push(location);
break;
+ }
case Expression::kTest: // Fall through.
case Expression::kValueTest: // Fall through.
case Expression::kTestValue:
- __ movq(rax, Operand(rbp, SlotOffset(source)));
- Move(context, rax);
+ Move(scratch, source);
+ Move(context, scratch);
break;
}
}
@@ -265,24 +308,61 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
}
+void FastCodeGenerator::Move(Slot* dst,
+ Register src,
+ Register scratch1,
+ Register scratch2) {
+ switch (dst->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ __ movq(Operand(rbp, SlotOffset(dst)), src);
+ break;
+ case Slot::CONTEXT: {
+ ASSERT(!src.is(scratch1));
+ ASSERT(!src.is(scratch2));
+ ASSERT(!scratch1.is(scratch2));
+ int context_chain_length =
+ function_->scope()->ContextChainLength(dst->var()->scope());
+ __ LoadContext(scratch1, context_chain_length);
+ __ movq(Operand(scratch1, Context::SlotOffset(dst->index())), src);
+ int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
+ __ RecordWrite(scratch1, offset, src, scratch2);
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ default:
+ UNREACHABLE();
+ }
+}
+
+
void FastCodeGenerator::DropAndMove(Expression::Context context,
- Register source) {
+ Register source,
+ int drop_count) {
+ ASSERT(drop_count > 0);
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
- __ addq(rsp, Immediate(kPointerSize));
+ __ addq(rsp, Immediate(drop_count * kPointerSize));
break;
case Expression::kValue:
+ if (drop_count > 1) {
+ __ addq(rsp, Immediate((drop_count - 1) * kPointerSize));
+ }
__ movq(Operand(rsp, 0), source);
break;
case Expression::kTest:
ASSERT(!source.is(rsp));
- __ addq(rsp, Immediate(kPointerSize));
+ __ addq(rsp, Immediate(drop_count * kPointerSize));
TestAndBranch(source, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
+ if (drop_count > 1) {
+ __ addq(rsp, Immediate((drop_count - 1) * kPointerSize));
+ }
__ movq(Operand(rsp, 0), source);
TestAndBranch(source, true_label_, &discard);
__ bind(&discard);
@@ -382,26 +462,26 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
__ Move(rax, Factory::the_hole_value());
if (FLAG_debug_code) {
// Check if we have the correct context pointer.
- __ movq(rbx, CodeGenerator::ContextOperand(
- rsi, Context::FCONTEXT_INDEX));
+ __ movq(rbx, CodeGenerator::ContextOperand(rsi,
+ Context::FCONTEXT_INDEX));
__ cmpq(rbx, rsi);
__ Check(equal, "Unexpected declaration in current context.");
}
__ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax);
// No write barrier since the_hole_value is in old space.
- ASSERT(Heap::InNewSpace(*Factory::the_hole_value()));
+ ASSERT(!Heap::InNewSpace(*Factory::the_hole_value()));
} else if (decl->fun() != NULL) {
Visit(decl->fun());
__ pop(rax);
if (FLAG_debug_code) {
// Check if we have the correct context pointer.
- __ movq(rbx, CodeGenerator::ContextOperand(
- rsi, Context::FCONTEXT_INDEX));
+ __ movq(rbx, CodeGenerator::ContextOperand(rsi,
+ Context::FCONTEXT_INDEX));
__ cmpq(rbx, rsi);
__ Check(equal, "Unexpected declaration in current context.");
}
__ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax);
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+ int offset = Context::SlotOffset(slot->index());
__ RecordWrite(rsi, offset, rax, rcx);
}
break;
@@ -473,53 +553,59 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
DropAndMove(expr->context(), rax);
} else if (rewrite->AsSlot() != NULL) {
Slot* slot = rewrite->AsSlot();
- switch (slot->type()) {
- case Slot::LOCAL:
- case Slot::PARAMETER: {
- Comment cmnt(masm_, "Stack slot");
- Move(expr->context(), slot);
- break;
- }
-
- case Slot::CONTEXT: {
- Comment cmnt(masm_, "Context slot");
- int chain_length =
- function_->scope()->ContextChainLength(slot->var()->scope());
- if (chain_length > 0) {
- // Move up the chain of contexts to the context containing the slot.
- __ movq(rax,
- Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX)));
- // Load the function context (which is the incoming, outer context).
- __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
- for (int i = 1; i < chain_length; i++) {
- __ movq(rax,
- Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)));
- __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
- }
- // The context may be an intermediate context, not a function context.
- __ movq(rax,
- Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)));
- } else { // Slot is in the current function context.
- // The context may be an intermediate context, not a function context.
- __ movq(rax,
- Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ if (FLAG_debug_code) {
+ switch (slot->type()) {
+ case Slot::LOCAL:
+ case Slot::PARAMETER: {
+ Comment cmnt(masm_, "Stack slot");
+ break;
}
- __ movq(rax, Operand(rax, Context::SlotOffset(slot->index())));
- Move(expr->context(), rax);
- break;
+ case Slot::CONTEXT: {
+ Comment cmnt(masm_, "Context slot");
+ break;
+ }
+ case Slot::LOOKUP:
+ UNIMPLEMENTED();
+ break;
+ default:
+ UNREACHABLE();
}
-
- case Slot::LOOKUP:
- UNREACHABLE();
- break;
}
+ Move(expr->context(), slot, rax);
} else {
- // The parameter variable has been rewritten into an explict access to
- // the arguments object.
+ // A variable has been rewritten into an explicit access to
+ // an object property.
Property* property = rewrite->AsProperty();
ASSERT_NOT_NULL(property);
- ASSERT_EQ(expr->context(), property->context());
- Visit(property);
+
+ // Currently the only parameter expressions that can occur are
+ // on the form "slot[literal]".
+
+ // Check that the object is in a slot.
+ Variable* object = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object);
+ Slot* object_slot = object->slot();
+ ASSERT_NOT_NULL(object_slot);
+
+ // Load the object.
+ Move(Expression::kValue, object_slot, rax);
+
+ // Check that the key is a smi.
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ ASSERT(key_literal->handle()->IsSmi());
+
+ // Load the key.
+ Move(Expression::kValue, key_literal);
+
+ // Do a KEYED property load.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // Notice: We must not have a "test rax, ..." instruction after
+ // the call. It is treated specially by the LoadIC code.
+
+ // Drop key and object left on the stack by IC, and push the result.
+ DropAndMove(expr->context(), rax, 2);
}
}
@@ -580,8 +666,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1);
}
- // If result_saved == true: the result is saved on top of the stack.
- // If result_saved == false: the result is not on the stack, just in rax.
+ // If result_saved == true: The result is saved on top of the
+ // stack and in rax.
+ // If result_saved == false: The result not on the stack, just in rax.
bool result_saved = false;
for (int i = 0; i < expr->properties()->length(); i++) {
@@ -606,6 +693,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// StoreIC leaves the receiver on the stack.
+ __ movq(rax, Operand(rsp, 0)); // Restore result back into rax.
break;
}
// fall through
@@ -781,7 +869,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
// Overwrite the global object on the stack with the result if needed.
DropAndMove(expr->context(), rax);
- } else {
+ } else if (var->slot()) {
Slot* slot = var->slot();
ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled.
switch (slot->type()) {
@@ -873,6 +961,36 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
UNREACHABLE();
break;
}
+ } else {
+ Property* property = var->AsProperty();
+ ASSERT_NOT_NULL(property);
+ // A variable has been rewritten into a property on an object.
+
+ // Load object and key onto the stack.
+ Slot* object_slot = property->obj()->AsSlot();
+ ASSERT_NOT_NULL(object_slot);
+ Move(Expression::kValue, object_slot, rax);
+
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ Move(Expression::kValue, key_literal);
+
+ // Value to store was pushed before object and key on the stack.
+ __ movq(rax, Operand(rsp, 2 * kPointerSize));
+
+ // Arguments to ic is value in rax, object and key on stack.
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+
+ if (expr->context() == Expression::kEffect) {
+ __ addq(rsp, Immediate(3 * kPointerSize));
+ } else if (expr->context() == Expression::kValue) {
+ // Value is still on the stack in rsp[2 * kPointerSize]
+ __ addq(rsp, Immediate(2 * kPointerSize));
+ } else {
+ __ movq(rax, Operand(rsp, 2 * kPointerSize));
+ DropAndMove(expr->context(), rax, 3);
+ }
}
}
@@ -969,9 +1087,9 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
Visit(expr->key());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
- // By emitting a nop we make sure that we do not have a "test rax,..."
- // instruction after the call it is treated specially by the LoadIC code.
- __ nop();
+ // Notice: We must not have a "test rax, ..." instruction after
+ // the call. It is treated specially by the LoadIC code.
+
// Drop key left on the stack by IC.
__ addq(rsp, Immediate(kPointerSize));
}
@@ -1054,7 +1172,7 @@ void FastCodeGenerator::VisitCall(Call* expr) {
SetSourcePosition(prop->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
- // By emitting a nop we make sure that we do not have a "test eax,..."
+ // By emitting a nop we make sure that we do not have a "test rax,..."
// instruction after the call it is treated specially by the LoadIC code.
__ nop();
// Drop key left on the stack by IC.
@@ -1134,9 +1252,13 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) {
void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Comment cmnt(masm_, "[ CallRuntime");
ZoneList<Expression*>* args = expr->arguments();
- Runtime::Function* function = expr->function();
- ASSERT(function != NULL);
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ Push(expr->name());
+ __ movq(rax, CodeGenerator::GlobalObject());
+ __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset));
+ }
// Push the arguments ("left-to-right").
int arg_count = args->length();
@@ -1145,8 +1267,19 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
ASSERT_EQ(Expression::kValue, args->at(i)->context());
}
- __ CallRuntime(function, arg_count);
- Move(expr->context(), rax);
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP);
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ // Discard the function left on TOS.
+ DropAndMove(expr->context(), rax);
+ } else {
+ __ CallRuntime(expr->function(), arg_count);
+ Move(expr->context(), rax);
+ }
}
void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc
index 1642a0490..ccbc615bd 100644
--- a/deps/v8/src/x64/ic-x64.cc
+++ b/deps/v8/src/x64/ic-x64.cc
@@ -31,6 +31,7 @@
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
+#include "utils.h"
namespace v8 {
namespace internal {
@@ -107,7 +108,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
StringDictionary::kElementsStartIndex * kPointerSize;
for (int i = 0; i < kProbes; i++) {
// Compute the masked index: (hash + i + i * i) & mask.
- __ movl(r1, FieldOperand(name, String::kLengthOffset));
+ __ movl(r1, FieldOperand(name, String::kHashFieldOffset));
__ shrl(r1, Immediate(String::kHashShift));
if (i > 0) {
__ addl(r1, Immediate(StringDictionary::GetProbeOffset(i)));
@@ -239,18 +240,6 @@ void KeyedLoadIC::Generate(MacroAssembler* masm,
}
-#ifdef DEBUG
-// For use in assert below.
-static int TenToThe(int exponent) {
- ASSERT(exponent <= 9);
- ASSERT(exponent >= 1);
- int answer = 10;
- for (int i = 1; i < exponent; i++) answer *= 10;
- return answer;
-}
-#endif
-
-
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rsp[0] : return address
@@ -327,7 +316,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdx);
__ j(above_equal, &slow);
// Is the string an array index, with cached numeric value?
- __ movl(rbx, FieldOperand(rax, String::kLengthOffset));
+ __ movl(rbx, FieldOperand(rax, String::kHashFieldOffset));
__ testl(rbx, Immediate(String::kIsArrayIndexMask));
// If the string is a symbol, do a quick inline probe of the receiver's
@@ -342,20 +331,16 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ movq(rax, rcx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
- // Array index string: If short enough use cache in length/hash field (rbx).
- // We assert that there are enough bits in an int32_t after the hash shift
- // bits have been subtracted to allow space for the length and the cached
- // array index.
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << (String::kShortLengthShift - String::kHashShift)));
+ (1 << String::kArrayIndexValueBits));
__ bind(&index_string);
- const int kLengthFieldLimit =
- (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift;
- __ cmpl(rbx, Immediate(kLengthFieldLimit));
- __ j(above_equal, &slow);
__ movl(rax, rbx);
- __ and_(rax, Immediate((1 << String::kShortLengthShift) - 1));
- __ shrl(rax, Immediate(String::kLongLengthShift));
+ __ and_(rax, Immediate(String::kArrayIndexHashMask));
+ __ shrl(rax, Immediate(String::kHashShift));
__ jmp(&index_int);
}
@@ -393,7 +378,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
// ExternalArray.
// rax: index (as a smi)
// rcx: JSObject
- __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ movq(rcx, FieldOperand(rcx, JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
Heap::RootIndexForExternalArrayType(array_type));
__ j(not_equal, &slow);
@@ -413,7 +398,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
__ movsxbq(rax, Operand(rcx, rax, times_1, 0));
break;
case kExternalUnsignedByteArray:
- __ movb(rax, Operand(rcx, rax, times_1, 0));
+ __ movzxbq(rax, Operand(rcx, rax, times_1, 0));
break;
case kExternalShortArray:
__ movsxwq(rax, Operand(rcx, rax, times_2, 0));
diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc
index 9dea61671..71157914c 100644
--- a/deps/v8/src/x64/macro-assembler-x64.cc
+++ b/deps/v8/src/x64/macro-assembler-x64.cc
@@ -67,6 +67,12 @@ void MacroAssembler::CompareRoot(Operand with, Heap::RootListIndex index) {
}
+void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
+ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ j(below, on_stack_overflow);
+}
+
+
static void RecordWriteHelper(MacroAssembler* masm,
Register object,
Register addr,
@@ -282,6 +288,9 @@ void MacroAssembler::Abort(const char* msg) {
RecordComment(msg);
}
#endif
+ // Disable stub call restrictions to always allow calls to abort.
+ set_allow_stub_calls(true);
+
push(rax);
movq(kScratchRegister, p0, RelocInfo::NONE);
push(kScratchRegister);
@@ -291,6 +300,7 @@ void MacroAssembler::Abort(const char* msg) {
push(kScratchRegister);
CallRuntime(Runtime::kAbort, 2);
// will not return here
+ int3();
}
@@ -2088,6 +2098,11 @@ void MacroAssembler::LoadAllocationTopHelper(Register result,
void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
Register scratch) {
+ if (FLAG_debug_code) {
+ testq(result_end, Immediate(kObjectAlignmentMask));
+ Check(zero, "Unaligned allocation in new space");
+ }
+
ExternalReference new_space_allocation_top =
ExternalReference::new_space_allocation_top_address();
@@ -2229,6 +2244,25 @@ void MacroAssembler::AllocateHeapNumber(Register result,
}
+void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
+ if (context_chain_length > 0) {
+ // Move up the chain of contexts to the context containing the slot.
+ movq(dst, Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ // Load the function context (which is the incoming, outer context).
+ movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
+ for (int i = 1; i < context_chain_length; i++) {
+ movq(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ movq(dst, FieldOperand(dst, JSFunction::kContextOffset));
+ }
+ // The context may be an intermediate context, not a function context.
+ movq(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ } else { // context is the current function context.
+ // The context may be an intermediate context, not a function context.
+ movq(dst, Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ }
+}
+
+
CodePatcher::CodePatcher(byte* address, int size)
: address_(address), size_(size), masm_(address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h
index 11cdfc3c4..9e7c25c95 100644
--- a/deps/v8/src/x64/macro-assembler-x64.h
+++ b/deps/v8/src/x64/macro-assembler-x64.h
@@ -98,6 +98,12 @@ class MacroAssembler: public Assembler {
#endif
// ---------------------------------------------------------------------------
+ // Stack limit support
+
+ // Do simple test for stack overflow. This doesn't handle an overflow.
+ void StackLimitCheck(Label* on_stack_limit_hit);
+
+ // ---------------------------------------------------------------------------
// Activation frames
void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); }
@@ -542,6 +548,9 @@ class MacroAssembler: public Assembler {
// occurred.
void IllegalOperation(int num_arguments);
+ // Find the function context up the context chain.
+ void LoadContext(Register dst, int context_chain_length);
+
// ---------------------------------------------------------------------------
// Runtime calls
diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc
index 584fd2b21..55b0b87cd 100644
--- a/deps/v8/src/x64/stub-cache-x64.cc
+++ b/deps/v8/src/x64/stub-cache-x64.cc
@@ -173,7 +173,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
__ JumpIfSmi(receiver, &miss);
// Get the map of the receiver and compute the hash.
- __ movl(scratch, FieldOperand(name, String::kLengthOffset));
+ __ movl(scratch, FieldOperand(name, String::kHashFieldOffset));
// Use only the low 32 bits of the map pointer.
__ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, Immediate(flags));
@@ -183,7 +183,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ProbeTable(masm, flags, kPrimary, name, scratch);
// Primary miss: Compute hash for secondary probe.
- __ movl(scratch, FieldOperand(name, String::kLengthOffset));
+ __ movl(scratch, FieldOperand(name, String::kHashFieldOffset));
__ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, Immediate(flags));
__ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize));
@@ -323,11 +323,7 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
// Load length directly from the string.
__ bind(&load_length);
- __ and_(scratch, Immediate(kStringSizeMask));
__ movl(rax, FieldOperand(receiver, String::kLengthOffset));
- // rcx is also the receiver.
- __ lea(rcx, Operand(scratch, String::kLongLengthShift));
- __ shr_cl(rax);
__ Integer32ToSmi(rax, rax);
__ ret(0);
diff --git a/deps/v8/test/cctest/test-alloc.cc b/deps/v8/test/cctest/test-alloc.cc
index 7921d2abe..315a34ed5 100644
--- a/deps/v8/test/cctest/test-alloc.cc
+++ b/deps/v8/test/cctest/test-alloc.cc
@@ -65,9 +65,9 @@ static Object* AllocateAfterFailures() {
// Old data space.
OldSpace* old_data_space = Heap::old_data_space();
- static const int kOldDataSpaceFillerSize = SeqAsciiString::SizeFor(0);
+ static const int kOldDataSpaceFillerSize = ByteArray::SizeFor(0);
while (old_data_space->Available() > kOldDataSpaceFillerSize) {
- CHECK(!Heap::AllocateRawAsciiString(0, TENURED)->IsFailure());
+ CHECK(!Heap::AllocateByteArray(0, TENURED)->IsFailure());
}
CHECK(!Heap::AllocateRawAsciiString(100, TENURED)->IsFailure());
diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc
index 6791685e1..6d6c174fd 100644
--- a/deps/v8/test/cctest/test-api.cc
+++ b/deps/v8/test/cctest/test-api.cc
@@ -2670,6 +2670,40 @@ THREADED_TEST(AutoExtensions) {
}
+static const char* kSyntaxErrorInExtensionSource =
+ "[";
+
+
+// Test that a syntax error in an extension does not cause a fatal
+// error but results in an empty context.
+THREADED_TEST(SyntaxErrorExtensions) {
+ v8::HandleScope handle_scope;
+ v8::RegisterExtension(new Extension("syntaxerror",
+ kSyntaxErrorInExtensionSource));
+ const char* extension_names[] = { "syntaxerror" };
+ v8::ExtensionConfiguration extensions(1, extension_names);
+ v8::Handle<Context> context = Context::New(&extensions);
+ CHECK(context.IsEmpty());
+}
+
+
+static const char* kExceptionInExtensionSource =
+ "throw 42";
+
+
+// Test that an exception when installing an extension does not cause
+// a fatal error but results in an empty context.
+THREADED_TEST(ExceptionExtensions) {
+ v8::HandleScope handle_scope;
+ v8::RegisterExtension(new Extension("exception",
+ kExceptionInExtensionSource));
+ const char* extension_names[] = { "exception" };
+ v8::ExtensionConfiguration extensions(1, extension_names);
+ v8::Handle<Context> context = Context::New(&extensions);
+ CHECK(context.IsEmpty());
+}
+
+
static void CheckDependencies(const char* name, const char* expected) {
v8::HandleScope handle_scope;
v8::ExtensionConfiguration config(1, &name);
@@ -7029,27 +7063,17 @@ static void MorphAString(i::String* string,
CHECK(i::StringShape(string).IsExternal());
if (string->IsAsciiRepresentation()) {
// Check old map is not symbol or long.
- CHECK(string->map() == i::Heap::short_external_ascii_string_map() ||
- string->map() == i::Heap::medium_external_ascii_string_map());
+ CHECK(string->map() == i::Heap::external_ascii_string_map());
// Morph external string to be TwoByte string.
- if (string->length() <= i::String::kMaxShortSize) {
- string->set_map(i::Heap::short_external_string_map());
- } else {
- string->set_map(i::Heap::medium_external_string_map());
- }
+ string->set_map(i::Heap::external_string_map());
i::ExternalTwoByteString* morphed =
i::ExternalTwoByteString::cast(string);
morphed->set_resource(uc16_resource);
} else {
// Check old map is not symbol or long.
- CHECK(string->map() == i::Heap::short_external_string_map() ||
- string->map() == i::Heap::medium_external_string_map());
+ CHECK(string->map() == i::Heap::external_string_map());
// Morph external string to be ASCII string.
- if (string->length() <= i::String::kMaxShortSize) {
- string->set_map(i::Heap::short_external_ascii_string_map());
- } else {
- string->set_map(i::Heap::medium_external_ascii_string_map());
- }
+ string->set_map(i::Heap::external_ascii_string_map());
i::ExternalAsciiString* morphed =
i::ExternalAsciiString::cast(string);
morphed->set_resource(ascii_resource);
@@ -8059,6 +8083,85 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
result = CompileRun("ext_array[1] = 23;");
CHECK_EQ(23, result->Int32Value());
+ // Test more complex manipulations which cause eax to contain values
+ // that won't be completely overwritten by loads from the arrays.
+ // This catches bugs in the instructions used for the KeyedLoadIC
+ // for byte and word types.
+ {
+ const int kXSize = 300;
+ const int kYSize = 300;
+ const int kLargeElementCount = kXSize * kYSize * 4;
+ ElementType* large_array_data =
+ static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
+ i::Handle<ExternalArrayClass> large_array =
+ i::Handle<ExternalArrayClass>::cast(
+ i::Factory::NewExternalArray(kLargeElementCount,
+ array_type,
+ array_data));
+ v8::Handle<v8::Object> large_obj = v8::Object::New();
+ // Set the elements to be the external array.
+ large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
+ array_type,
+ kLargeElementCount);
+ context->Global()->Set(v8_str("large_array"), large_obj);
+ // Initialize contents of a few rows.
+ for (int x = 0; x < 300; x++) {
+ int row = 0;
+ int offset = row * 300 * 4;
+ large_array_data[offset + 4 * x + 0] = (ElementType) 127;
+ large_array_data[offset + 4 * x + 1] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 2] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 3] = (ElementType) 127;
+ row = 150;
+ offset = row * 300 * 4;
+ large_array_data[offset + 4 * x + 0] = (ElementType) 127;
+ large_array_data[offset + 4 * x + 1] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 2] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 3] = (ElementType) 127;
+ row = 298;
+ offset = row * 300 * 4;
+ large_array_data[offset + 4 * x + 0] = (ElementType) 127;
+ large_array_data[offset + 4 * x + 1] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 2] = (ElementType) 0;
+ large_array_data[offset + 4 * x + 3] = (ElementType) 127;
+ }
+ // The goal of the code below is to make "offset" large enough
+ // that the computation of the index (which goes into eax) has
+ // high bits set which will not be overwritten by a byte or short
+ // load.
+ result = CompileRun("var failed = false;"
+ "var offset = 0;"
+ "for (var i = 0; i < 300; i++) {"
+ " if (large_array[4 * i] != 127 ||"
+ " large_array[4 * i + 1] != 0 ||"
+ " large_array[4 * i + 2] != 0 ||"
+ " large_array[4 * i + 3] != 127) {"
+ " failed = true;"
+ " }"
+ "}"
+ "offset = 150 * 300 * 4;"
+ "for (var i = 0; i < 300; i++) {"
+ " if (large_array[offset + 4 * i] != 127 ||"
+ " large_array[offset + 4 * i + 1] != 0 ||"
+ " large_array[offset + 4 * i + 2] != 0 ||"
+ " large_array[offset + 4 * i + 3] != 127) {"
+ " failed = true;"
+ " }"
+ "}"
+ "offset = 298 * 300 * 4;"
+ "for (var i = 0; i < 300; i++) {"
+ " if (large_array[offset + 4 * i] != 127 ||"
+ " large_array[offset + 4 * i + 1] != 0 ||"
+ " large_array[offset + 4 * i + 2] != 0 ||"
+ " large_array[offset + 4 * i + 3] != 127) {"
+ " failed = true;"
+ " }"
+ "}"
+ "!failed;");
+ CHECK_EQ(true, result->BooleanValue());
+ free(large_array_data);
+ }
+
free(array_data);
}
@@ -8409,3 +8512,103 @@ THREADED_TEST(SpaghettiStackReThrow) {
v8::String::Utf8Value value(try_catch.Exception());
CHECK_EQ(0, strcmp(*value, "Hey!"));
}
+
+
+static int GetGlobalObjectsCount() {
+ int count = 0;
+ v8::internal::HeapIterator it;
+ while (it.has_next()) {
+ v8::internal::HeapObject* object = it.next();
+ if (object->IsJSGlobalObject()) count++;
+ }
+ return count;
+}
+
+
+TEST(Regress528) {
+ v8::V8::Initialize();
+
+ v8::HandleScope scope;
+ v8::Persistent<Context> context;
+ v8::Persistent<Context> other_context;
+ int gc_count;
+
+ // Create a context used to keep the code from aging in the compilation
+ // cache.
+ other_context = Context::New();
+
+ // Context-dependent context data creates reference from the compilation
+ // cache to the global object.
+ const char* source_simple = "1";
+ context = Context::New();
+ {
+ v8::HandleScope scope;
+
+ context->Enter();
+ Local<v8::String> obj = v8::String::New("");
+ context->SetData(obj);
+ CompileRun(source_simple);
+ context->Exit();
+ }
+ context.Dispose();
+ for (gc_count = 1; gc_count < 10; gc_count++) {
+ other_context->Enter();
+ CompileRun(source_simple);
+ other_context->Exit();
+ v8::internal::Heap::CollectAllGarbage(false);
+ if (GetGlobalObjectsCount() == 1) break;
+ }
+ CHECK_GE(2, gc_count);
+ CHECK_EQ(1, GetGlobalObjectsCount());
+
+ // Eval in a function creates reference from the compilation cache to the
+ // global object.
+ const char* source_eval = "function f(){eval('1')}; f()";
+ context = Context::New();
+ {
+ v8::HandleScope scope;
+
+ context->Enter();
+ CompileRun(source_eval);
+ context->Exit();
+ }
+ context.Dispose();
+ for (gc_count = 1; gc_count < 10; gc_count++) {
+ other_context->Enter();
+ CompileRun(source_eval);
+ other_context->Exit();
+ v8::internal::Heap::CollectAllGarbage(false);
+ if (GetGlobalObjectsCount() == 1) break;
+ }
+ CHECK_GE(2, gc_count);
+ CHECK_EQ(1, GetGlobalObjectsCount());
+
+ // Looking up the line number for an exception creates reference from the
+ // compilation cache to the global object.
+ const char* source_exception = "function f(){throw 1;} f()";
+ context = Context::New();
+ {
+ v8::HandleScope scope;
+
+ context->Enter();
+ v8::TryCatch try_catch;
+ CompileRun(source_exception);
+ CHECK(try_catch.HasCaught());
+ v8::Handle<v8::Message> message = try_catch.Message();
+ CHECK(!message.IsEmpty());
+ CHECK_EQ(1, message->GetLineNumber());
+ context->Exit();
+ }
+ context.Dispose();
+ for (gc_count = 1; gc_count < 10; gc_count++) {
+ other_context->Enter();
+ CompileRun(source_exception);
+ other_context->Exit();
+ v8::internal::Heap::CollectAllGarbage(false);
+ if (GetGlobalObjectsCount() == 1) break;
+ }
+ CHECK_GE(2, gc_count);
+ CHECK_EQ(1, GetGlobalObjectsCount());
+
+ other_context.Dispose();
+}
diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc
index d938174e7..5b7219301 100644
--- a/deps/v8/test/cctest/test-debug.cc
+++ b/deps/v8/test/cctest/test-debug.cc
@@ -5016,7 +5016,7 @@ TEST(ScriptNameAndData) {
v8::ScriptOrigin origin2 = v8::ScriptOrigin(v8::String::New("new name"));
v8::Handle<v8::Script> script2 = v8::Script::Compile(script, &origin2);
script2->Run();
- script2->SetData(data_obj);
+ script2->SetData(data_obj->ToString());
f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
f->Call(env->Global(), 0, NULL);
CHECK_EQ(3, break_point_hit_count);
@@ -5069,8 +5069,8 @@ TEST(ContextData) {
CHECK(context_2->GetData()->IsUndefined());
// Set and check different data values.
- v8::Handle<v8::Value> data_1 = v8::Number::New(1);
- v8::Handle<v8::Value> data_2 = v8::String::New("2");
+ v8::Handle<v8::String> data_1 = v8::String::New("1");
+ v8::Handle<v8::String> data_2 = v8::String::New("2");
context_1->SetData(data_1);
context_2->SetData(data_2);
CHECK(context_1->GetData()->StrictEquals(data_1));
@@ -5233,7 +5233,7 @@ static void ExecuteScriptForContextCheck() {
CHECK(context_1->GetData()->IsUndefined());
// Set and check a data value.
- v8::Handle<v8::Value> data_1 = v8::Number::New(1);
+ v8::Handle<v8::String> data_1 = v8::String::New("1");
context_1->SetData(data_1);
CHECK(context_1->GetData()->StrictEquals(data_1));
diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc
index fb9a48e79..17bee5b06 100644
--- a/deps/v8/test/cctest/test-heap.cc
+++ b/deps/v8/test/cctest/test-heap.cc
@@ -37,8 +37,7 @@ TEST(HeapMaps) {
CheckMap(Heap::meta_map(), MAP_TYPE, Map::kSize);
CheckMap(Heap::heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, FixedArray::kHeaderSize);
- CheckMap(Heap::long_string_map(), LONG_STRING_TYPE,
- SeqTwoByteString::kAlignedSize);
+ CheckMap(Heap::string_map(), STRING_TYPE, SeqTwoByteString::kAlignedSize);
}
diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc
index de29fe097..85ff331a6 100644
--- a/deps/v8/test/cctest/test-log.cc
+++ b/deps/v8/test/cctest/test-log.cc
@@ -538,6 +538,81 @@ TEST(LogCallbacks) {
}
+static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
+ const v8::AccessorInfo& info) {
+ return v8::Handle<v8::Value>();
+}
+
+static void Prop1Setter(v8::Local<v8::String> property,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info) {
+}
+
+static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
+ const v8::AccessorInfo& info) {
+ return v8::Handle<v8::Value>();
+}
+
+TEST(LogAccessorCallbacks) {
+ const bool saved_prof_lazy = i::FLAG_prof_lazy;
+ const bool saved_prof = i::FLAG_prof;
+ const bool saved_prof_auto = i::FLAG_prof_auto;
+ i::FLAG_prof = true;
+ i::FLAG_prof_lazy = false;
+ i::FLAG_prof_auto = false;
+ i::FLAG_logfile = "*";
+
+ // If tests are being run manually, V8 will be already initialized
+ // by the bottom test.
+ const bool need_to_set_up_logger = i::V8::IsRunning();
+ v8::HandleScope scope;
+ v8::Handle<v8::Context> env = v8::Context::New();
+ if (need_to_set_up_logger) Logger::Setup();
+ env->Enter();
+
+ // Skip all initially logged stuff.
+ EmbeddedVector<char, 102400> buffer;
+ int log_pos = GetLogLines(0, &buffer);
+
+ v8::Persistent<v8::FunctionTemplate> obj =
+ v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
+ obj->SetClassName(v8::String::New("Obj"));
+ v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate();
+ inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter);
+ inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
+
+ i::Logger::LogAccessorCallbacks();
+ log_pos = GetLogLines(log_pos, &buffer);
+ CHECK_GT(log_pos, 0);
+ buffer[log_pos] = 0;
+ printf("%s", buffer.start());
+
+ EmbeddedVector<char, 100> prop1_getter_record;
+ i::OS::SNPrintF(prop1_getter_record,
+ "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
+ Prop1Getter);
+ CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start()));
+ EmbeddedVector<char, 100> prop1_setter_record;
+ i::OS::SNPrintF(prop1_setter_record,
+ "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
+ Prop1Setter);
+ CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start()));
+ EmbeddedVector<char, 100> prop2_getter_record;
+ i::OS::SNPrintF(prop2_getter_record,
+ "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
+ Prop2Getter);
+ CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start()));
+
+ obj.Dispose();
+
+ env->Exit();
+ Logger::TearDown();
+ i::FLAG_prof_lazy = saved_prof_lazy;
+ i::FLAG_prof = saved_prof;
+ i::FLAG_prof_auto = saved_prof_auto;
+}
+
+
static inline bool IsStringEqualTo(const char* r, const char* s) {
return strncmp(r, s, strlen(r)) == 0;
}
diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc
index 9ed487450..8f4441ac7 100644
--- a/deps/v8/test/cctest/test-serialize.cc
+++ b/deps/v8/test/cctest/test-serialize.cc
@@ -192,6 +192,15 @@ TEST(Serialize) {
}
+// Test that heap serialization is non-destructive.
+TEST(SerializeTwice) {
+ Serializer::Enable();
+ v8::V8::Initialize();
+ Serialize();
+ Serialize();
+}
+
+
//----------------------------------------------------------------------------
// Tests that the heap can be deserialized.
@@ -218,7 +227,17 @@ DEPENDENT_TEST(Deserialize, Serialize) {
Deserialize();
- fflush(stdout);
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ env->Enter();
+
+ SanityCheck();
+}
+
+
+DEPENDENT_TEST(DeserializeFromSecondSerialization, SerializeTwice) {
+ v8::HandleScope scope;
+
+ Deserialize();
v8::Persistent<v8::Context> env = v8::Context::New();
env->Enter();
@@ -242,6 +261,22 @@ DEPENDENT_TEST(DeserializeAndRunScript2, Serialize) {
}
+DEPENDENT_TEST(DeserializeFromSecondSerializationAndRunScript2,
+ SerializeTwice) {
+ v8::HandleScope scope;
+
+ Deserialize();
+
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ env->Enter();
+
+ const char* c_source = "\"1234\".length";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ CHECK_EQ(4, script->Run()->Int32Value());
+}
+
+
TEST(TestThatAlwaysSucceeds) {
}
diff --git a/deps/v8/test/cctest/test-strings.cc b/deps/v8/test/cctest/test-strings.cc
index 0e9bf7a06..59a40af2a 100644
--- a/deps/v8/test/cctest/test-strings.cc
+++ b/deps/v8/test/cctest/test-strings.cc
@@ -63,6 +63,21 @@ class Resource: public v8::String::ExternalStringResource,
};
+class AsciiResource: public v8::String::ExternalAsciiStringResource,
+ public ZoneObject {
+ public:
+ explicit AsciiResource(Vector<const char> string): data_(string.start()) {
+ length_ = string.length();
+ }
+ virtual const char* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ const char* data_;
+ size_t length_;
+};
+
+
static void InitializeBuildingBlocks(
Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) {
// A list of pointers that we don't have any interest in cleaning up.
@@ -329,25 +344,89 @@ TEST(Utf8Conversion) {
}
-class TwoByteResource: public v8::String::ExternalStringResource {
- public:
- TwoByteResource(const uint16_t* data, size_t length, bool* destructed)
- : data_(data), length_(length), destructed_(destructed) {
- CHECK_NE(destructed, NULL);
- *destructed_ = false;
- }
+TEST(ExternalShortStringAdd) {
+ ZoneScope zone(DELETE_ON_EXIT);
- virtual ~TwoByteResource() {
- CHECK_NE(destructed_, NULL);
- CHECK(!*destructed_);
- *destructed_ = true;
- }
+ InitializeVM();
+ v8::HandleScope handle_scope;
- const uint16_t* data() const { return data_; }
- size_t length() const { return length_; }
+ // Make sure we cover all always-flat lengths and at least one above.
+ static const int kMaxLength = 20;
+ CHECK_GT(kMaxLength, i::String::kMinNonFlatLength);
+
+ // Allocate two JavaScript arrays for holding short strings.
+ v8::Handle<v8::Array> ascii_external_strings =
+ v8::Array::New(kMaxLength + 1);
+ v8::Handle<v8::Array> non_ascii_external_strings =
+ v8::Array::New(kMaxLength + 1);
+
+ // Generate short ascii and non-ascii external strings.
+ for (int i = 0; i <= kMaxLength; i++) {
+ char* ascii = Zone::NewArray<char>(i + 1);
+ for (int j = 0; j < i; j++) {
+ ascii[j] = 'a';
+ }
+ // Terminating '\0' is left out on purpose. It is not required for external
+ // string data.
+ AsciiResource* ascii_resource =
+ new AsciiResource(Vector<const char>(ascii, i));
+ v8::Local<v8::String> ascii_external_string =
+ v8::String::NewExternal(ascii_resource);
+
+ ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string);
+ uc16* non_ascii = Zone::NewArray<uc16>(i + 1);
+ for (int j = 0; j < i; j++) {
+ non_ascii[j] = 0x1234;
+ }
+ // Terminating '\0' is left out on purpose. It is not required for external
+ // string data.
+ Resource* resource = new Resource(Vector<const uc16>(non_ascii, i));
+ v8::Local<v8::String> non_ascii_external_string =
+ v8::String::NewExternal(resource);
+ non_ascii_external_strings->Set(v8::Integer::New(i),
+ non_ascii_external_string);
+ }
- private:
- const uint16_t* data_;
- size_t length_;
- bool* destructed_;
-};
+ // Add the arrays with the short external strings in the global object.
+ v8::Handle<v8::Object> global = env->Global();
+ global->Set(v8_str("external_ascii"), ascii_external_strings);
+ global->Set(v8_str("external_non_ascii"), non_ascii_external_strings);
+ global->Set(v8_str("max_length"), v8::Integer::New(kMaxLength));
+
+ // Add short external ascii and non-ascii strings checking the result.
+ static const char* source =
+ "function test() {"
+ " var ascii_chars = 'aaaaaaaaaaaaaaaaaaaa';"
+ " var non_ascii_chars = '\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234';" //NOLINT
+ " if (ascii_chars.length != max_length) return 1;"
+ " if (non_ascii_chars.length != max_length) return 2;"
+ " var ascii = Array(max_length + 1);"
+ " var non_ascii = Array(max_length + 1);"
+ " for (var i = 0; i <= max_length; i++) {"
+ " ascii[i] = ascii_chars.substring(0, i);"
+ " non_ascii[i] = non_ascii_chars.substring(0, i);"
+ " };"
+ " for (var i = 0; i <= max_length; i++) {"
+ " if (ascii[i] != external_ascii[i]) return 3;"
+ " if (non_ascii[i] != external_non_ascii[i]) return 4;"
+ " for (var j = 0; j < i; j++) {"
+ " if (external_ascii[i] !="
+ " (external_ascii[j] + external_ascii[i - j])) return 5;"
+ " if (external_non_ascii[i] !="
+ " (external_non_ascii[j] + external_non_ascii[i - j])) return 6;"
+ " if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return 7;"
+ " if (ascii[i] != (ascii[j] + ascii[i - j])) return 8;"
+ " if (ascii[i] != (external_ascii[j] + ascii[i - j])) return 9;"
+ " if (ascii[i] != (ascii[j] + external_ascii[i - j])) return 10;"
+ " if (non_ascii[i] !="
+ " (external_non_ascii[j] + non_ascii[i - j])) return 11;"
+ " if (non_ascii[i] !="
+ " (non_ascii[j] + external_non_ascii[i - j])) return 12;"
+ " }"
+ " }"
+ " return 0;"
+ "};"
+ "test()";
+ CHECK_EQ(0,
+ v8::Script::Compile(v8::String::New(source))->Run()->Int32Value());
+}
diff --git a/deps/v8/test/mjsunit/arguments-read-and-assignment.js b/deps/v8/test/mjsunit/arguments-read-and-assignment.js
new file mode 100644
index 000000000..c5d34bfa9
--- /dev/null
+++ b/deps/v8/test/mjsunit/arguments-read-and-assignment.js
@@ -0,0 +1,164 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Testing basic functionality of the arguments object.
+// Introduced to ensure that the fast compiler does the right thing.
+// The arguments object itself.
+assertEquals(42, function(){ return arguments;}(42)[0],
+ "return arguments value");
+assertEquals(42, function(){ return arguments;}(42)[0],
+ "arguments in plain value context");
+assertEquals(42, function(){ arguments;return 42}(37),
+ "arguments in effect context");
+assertEquals(42, function(){ if(arguments)return 42;}(),
+ "arguments in a boolean context");
+assertEquals(42, function(){ return arguments || true;}(42)[0],
+ "arguments in a short-circuit boolean context - or");
+assertEquals(true, function(){ return arguments && [true];}(42)[0],
+ "arguments in a short-circuit boolean context - and");
+assertEquals(42, function(){ arguments = 42; return 42;}(),
+ "arguments assignment");
+// Properties of the arguments object.
+assertEquals(42, function(){ return arguments[0]; }(42),
+ "args[0] value returned");
+assertEquals(42, function(){ arguments[0]; return 42}(),
+ "args[0] value ignored");
+assertEquals(42, function(){ if (arguments[0]) return 42; }(37),
+ "args[0] to boolean");
+assertEquals(42, function(){ return arguments[0] || "no"; }(42),
+ "args[0] short-circuit boolean or true");
+assertEquals(42, function(){ return arguments[0] || 42; }(0),
+ "args[0] short-circuit boolean or false");
+assertEquals(37, function(){ return arguments[0] && 37; }(42),
+ "args[0] short-circuit boolean and true");
+assertEquals(0, function(){ return arguments[0] && 42; }(0),
+ "args[0] short-circuit boolean and false");
+assertEquals(42, function(){ arguments[0] = 42; return arguments[0]; }(37),
+ "args[0] assignment");
+// Link between arguments and parameters.
+assertEquals(42, function(a) { arguments[0] = 42; return a; }(37),
+ "assign args[0]->a");
+assertEquals(42, function(a) { a = 42; return arguments[0]; }(37),
+ "assign a->args[0]");
+assertEquals(54, function(a, b) { arguments[1] = 54; return b; }(42, 37),
+ "assign args[1]->b:b");
+assertEquals(54, function(a, b) { b = 54; return arguments[1]; }(42, 47),
+ "assign b->args[1]:b");
+assertEquals(42, function(a, b) { arguments[1] = 54; return a; }(42, 37),
+ "assign args[1]->b:a");
+assertEquals(42, function(a, b) { b = 54; return arguments[0]; }(42, 47),
+ "assign b->args[1]:a");
+
+// Capture parameters in nested contexts.
+assertEquals(33,
+ function(a,b) {
+ return a + arguments[0] +
+ function(b){ return a + b + arguments[0]; }(b); }(7,6),
+ "captured parameters");
+assertEquals(42, function(a) {
+ arguments[0] = 42;
+ return function(b){ return a; }();
+ }(37),
+ "capture value returned");
+assertEquals(42,
+ function(a) {
+ arguments[0] = 26;
+ return function(b){ a; return 42; }();
+ }(37),
+ "capture value ignored");
+assertEquals(42,
+ function(a) {
+ arguments[0] = 26;
+ return function(b){ if (a) return 42; }();
+ }(37),
+ "capture to boolean");
+assertEquals(42,
+ function(a) {
+ arguments[0] = 42;
+ return function(b){ return a || "no"; }();
+ }(37),
+ "capture short-circuit boolean or true");
+assertEquals(0,
+ function(a) {
+ arguments[0] = 0;
+ return function(b){ return a && 42; }();
+ }(37),
+ "capture short-circuit boolean and false");
+// Deeply nested.
+assertEquals(42,
+ function(a,b) {
+ return arguments[2] +
+ function(){
+ return b +
+ function() {
+ return a;
+ }();
+ }();
+ }(7,14,21),
+ "deep nested capture");
+
+// Assignment to captured parameters.
+assertEquals(42, function(a,b) {
+ arguments[1] = 11;
+ return a + function(){ a = b; return a; }() + a;
+ }(20, 37), "captured assignment");
+
+// Inside non-function scopes.
+assertEquals(42,
+ function(a) {
+ arguments[0] = 20;
+ with ({ b : 22 }) { return a + b; }
+ }(37),
+ "a in with");
+assertEquals(42,
+ function(a) {
+ with ({ b : 22 }) { return arguments[0] + b; }
+ }(20),
+ "args in with");
+assertEquals(42,
+ function(a) {
+ arguments[0] = 20;
+ with ({ b : 22 }) {
+ return function() { return a; }() + b; }
+ }(37),
+ "captured a in with");
+assertEquals(42,
+ function(a) {
+ arguments[0] = 12;
+ with ({ b : 22 }) {
+ return function f() {
+ try { throw 8 } catch(e) { return e + a };
+ }() + b;
+ }
+ }(37),
+ "in a catch in a named function captured a in with ");
+// Escaping arguments.
+function weirdargs(a,b,c) { if (!a) return arguments;
+ return [b[2],c]; }
+var args1 = weirdargs(false, null, 40);
+var res = weirdargs(true, args1, 15);
+assertEquals(40, res[0], "return old args element");
+assertEquals(15, res[1], "return own args element"); \ No newline at end of file
diff --git a/deps/v8/test/mjsunit/compiler/jsnatives.js b/deps/v8/test/mjsunit/compiler/jsnatives.js
new file mode 100644
index 000000000..f5d6ac464
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/jsnatives.js
@@ -0,0 +1,33 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax
+
+// Test call of JS runtime functions.
+
+var a = %GlobalParseInt("21", 16);
+assertEquals(33, a);
diff --git a/deps/v8/test/mjsunit/compiler/objectliterals.js b/deps/v8/test/mjsunit/compiler/objectliterals.js
new file mode 100644
index 000000000..788acb480
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/objectliterals.js
@@ -0,0 +1,57 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test object literals with getter, setter and prototype properties.
+
+var o = { x: 41, get bar() { return {x:42} } };
+
+assertEquals(41, o.x);
+assertEquals(42, o.bar.x);
+
+o = { f: function() { return 41 },
+ get bar() { return this.x },
+ x:0,
+ set bar(t) { this.x = t },
+ g: function() { return 43 }
+};
+o.bar = 7;
+assertEquals(7, o.bar);
+assertEquals(7, o.x);
+assertEquals(41, o.f());
+assertEquals(43, o.g());
+
+p = {x:42};
+o = {get foo() { return this.x; },
+ f: function() { return this.foo + 1 },
+ set bar(t) { this.x = t; },
+ __proto__: p,
+};
+assertEquals(42, o.x);
+assertEquals(42, o.foo);
+assertEquals(43, o.f());
+o.bar = 44;
+assertEquals(44, o.foo);
diff --git a/deps/v8/test/mjsunit/regress/regress-526.js b/deps/v8/test/mjsunit/regress/regress-526.js
new file mode 100644
index 000000000..0cae97aa2
--- /dev/null
+++ b/deps/v8/test/mjsunit/regress/regress-526.js
@@ -0,0 +1,32 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test object literals with computed property and getter.
+
+var o = { foo: function() { }, get bar() { return {x:42} } };
+
+assertEquals(42, o.bar.x);
diff --git a/deps/v8/test/mjsunit/regress/regress-r3391.js b/deps/v8/test/mjsunit/regress/regress-r3391.js
new file mode 100644
index 000000000..d55728436
--- /dev/null
+++ b/deps/v8/test/mjsunit/regress/regress-r3391.js
@@ -0,0 +1,77 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Check what we do if toLocaleString doesn't return a string when we are
+// calling Array.prototype.toLocaleString. The standard is somewhat
+// vague on this point. This test is now passed by both V8 and JSC.
+
+var evil_called = 0;
+var evil_locale_called = 0;
+var exception_thrown = 0;
+
+function evil_to_string() {
+ evil_called++;
+ return this;
+}
+
+function evil_to_locale_string() {
+ evil_locale_called++;
+ return this;
+}
+
+var o = {toString: evil_to_string, toLocaleString: evil_to_locale_string};
+
+try {
+ [o].toLocaleString();
+} catch(e) {
+ exception_thrown++;
+}
+
+assertEquals(1, evil_called, "evil1");
+assertEquals(1, evil_locale_called, "local1");
+assertEquals(1, exception_thrown, "exception1");
+
+try {
+ [o].toString();
+} catch(e) {
+ exception_thrown++;
+}
+
+assertEquals(2, evil_called, "evil2");
+assertEquals(1, evil_locale_called, "local2");
+assertEquals(2, exception_thrown, "exception2");
+
+try {
+ [o].join(o);
+} catch(e) {
+ exception_thrown++;
+}
+
+assertEquals(3, evil_called, "evil3");
+assertEquals(1, evil_locale_called, "local3");
+assertEquals(3, exception_thrown, "exception3");
+print("ok");
diff --git a/deps/v8/test/mjsunit/string-add.js b/deps/v8/test/mjsunit/string-add.js
index c42cf793b..f226ca18c 100644
--- a/deps/v8/test/mjsunit/string-add.js
+++ b/deps/v8/test/mjsunit/string-add.js
@@ -173,3 +173,23 @@ assertEquals(0, null + null, "uu");
assertEquals("42strz", reswz, "swwz");
assertEquals(84, resww, "swww");
})(1);
+
+// Generate ascii and non ascii strings from length 0 to 20.
+var ascii = 'aaaaaaaaaaaaaaaaaaaa';
+var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';
+assertEquals(20, ascii.length);
+assertEquals(20, non_ascii.length);
+var a = Array(21);
+var b = Array(21);
+for (var i = 0; i <= 20; i++) {
+ a[i] = ascii.substring(0, i);
+ b[i] = non_ascii.substring(0, i);
+}
+
+// Add ascii and non-ascii strings generating strings with length from 0 to 20.
+for (var i = 0; i <= 20; i++) {
+ for (var j = 0; j < i; j++) {
+ assertEquals(a[i], a[j] + a[i - j])
+ assertEquals(b[i], b[j] + b[i - j])
+ }
+}
diff --git a/deps/v8/test/mjsunit/typeof.js b/deps/v8/test/mjsunit/typeof.js
new file mode 100644
index 000000000..b460fbba9
--- /dev/null
+++ b/deps/v8/test/mjsunit/typeof.js
@@ -0,0 +1,40 @@
+// 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.
+
+// Flags: --nofast-compiler
+
+// The type of a regular expression should be 'function', including in
+// the context of string equality comparisons.
+
+var r = new RegExp;
+assertEquals('function', typeof r);
+assertTrue(typeof r == 'function');
+
+function test(x, y) { return x == y; }
+assertFalse(test('object', typeof r));
+
+assertFalse(typeof r == 'object');
diff --git a/deps/v8/tools/codemap.js b/deps/v8/tools/codemap.js
index 404127f23..af511f642 100644
--- a/deps/v8/tools/codemap.js
+++ b/deps/v8/tools/codemap.js
@@ -244,7 +244,7 @@ devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() {
devtools.profiler.CodeMap.NameGenerator = function() {
- this.knownNames_ = [];
+ this.knownNames_ = {};
};
diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp
index d7ee73f6e..ba7224b4a 100644
--- a/deps/v8/tools/gyp/v8.gyp
+++ b/deps/v8/tools/gyp/v8.gyp
@@ -506,6 +506,17 @@
],
}
],
+ ['OS=="openbsd"', {
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ]},
+ 'sources': [
+ '../../src/platform-openbsd.cc',
+ '../../src/platform-posix.cc'
+ ],
+ }
+ ],
['OS=="mac"', {
'sources': [
'../../src/platform-macos.cc',
diff --git a/deps/v8/tools/presubmit.py b/deps/v8/tools/presubmit.py
index 5a99c2add..3f27c001a 100755
--- a/deps/v8/tools/presubmit.py
+++ b/deps/v8/tools/presubmit.py
@@ -28,9 +28,11 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import md5
import optparse
import os
from os.path import abspath, join, dirname, basename, exists
+import pickle
import re
import sys
import subprocess
@@ -93,6 +95,50 @@ whitespace/todo
""".split()
+class FileContentsCache(object):
+
+ def __init__(self, sums_file_name):
+ self.sums = {}
+ self.sums_file_name = sums_file_name
+
+ def Load(self):
+ try:
+ sums_file = None
+ try:
+ sums_file = open(self.sums_file_name, 'r')
+ self.sums = pickle.load(sums_file)
+ except IOError:
+ # File might not exist, this is OK.
+ pass
+ finally:
+ if sums_file:
+ sums_file.close()
+
+ def Save(self):
+ try:
+ sums_file = open(self.sums_file_name, 'w')
+ pickle.dump(self.sums, sums_file)
+ finally:
+ sums_file.close()
+
+ def FilterUnchangedFiles(self, files):
+ changed_or_new = []
+ for file in files:
+ try:
+ handle = open(file, "r")
+ file_sum = md5.new(handle.read()).digest()
+ if not file in self.sums or self.sums[file] != file_sum:
+ changed_or_new.append(file)
+ self.sums[file] = file_sum
+ finally:
+ handle.close()
+ return changed_or_new
+
+ def RemoveFile(self, file):
+ if file in self.sums:
+ self.sums.pop(file)
+
+
class SourceFileProcessor(object):
"""
Utility class that can run through a directory structure, find all relevant
@@ -137,7 +183,7 @@ class CppLintProcessor(SourceFileProcessor):
or (name == 'third_party'))
IGNORE_LINT = ['flag-definitions.h']
-
+
def IgnoreFile(self, name):
return (super(CppLintProcessor, self).IgnoreFile(name)
or (name in CppLintProcessor.IGNORE_LINT))
@@ -146,13 +192,32 @@ class CppLintProcessor(SourceFileProcessor):
return ['src', 'public', 'samples', join('test', 'cctest')]
def ProcessFiles(self, files, path):
+ good_files_cache = FileContentsCache('.cpplint-cache')
+ good_files_cache.Load()
+ files = good_files_cache.FilterUnchangedFiles(files)
+ if len(files) == 0:
+ print 'No changes in files detected. Skipping cpplint check.'
+ return True
+
filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES])
command = ['cpplint.py', '--filter', filt] + join(files)
local_cpplint = join(path, "tools", "cpplint.py")
if exists(local_cpplint):
command = ['python', local_cpplint, '--filter', filt] + join(files)
- process = subprocess.Popen(command)
- return process.wait() == 0
+
+ process = subprocess.Popen(command, stderr=subprocess.PIPE)
+ LINT_ERROR_PATTERN = re.compile(r'^(.+)[:(]\d+[:)]')
+ while True:
+ out_line = process.stderr.readline()
+ if out_line == '' and process.poll() != None:
+ break
+ sys.stderr.write(out_line)
+ m = LINT_ERROR_PATTERN.match(out_line)
+ if m:
+ good_files_cache.RemoveFile(m.group(1))
+
+ good_files_cache.Save()
+ return process.returncode == 0
COPYRIGHT_HEADER_PATTERN = re.compile(
diff --git a/deps/v8/tools/utils.py b/deps/v8/tools/utils.py
index 78d1e0d62..196bb0555 100644
--- a/deps/v8/tools/utils.py
+++ b/deps/v8/tools/utils.py
@@ -55,6 +55,8 @@ def GuessOS():
return 'win32'
elif id == 'FreeBSD':
return 'freebsd'
+ elif id == 'OpenBSD':
+ return 'openbsd'
else:
return None
diff --git a/deps/v8/tools/v8.xcodeproj/project.pbxproj b/deps/v8/tools/v8.xcodeproj/project.pbxproj
index d2af6262b..3ffd18299 100644
--- a/deps/v8/tools/v8.xcodeproj/project.pbxproj
+++ b/deps/v8/tools/v8.xcodeproj/project.pbxproj
@@ -214,6 +214,10 @@
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
+ 9FBE03DE10BD409900F8BFBA /* fast-codegen.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */; };
+ 9FBE03DF10BD409900F8BFBA /* fast-codegen.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */; };
+ 9FBE03E210BD40EA00F8BFBA /* fast-codegen-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */; };
+ 9FBE03E510BD412600F8BFBA /* fast-codegen-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */; };
9FC86ABD0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; };
9FC86ABE0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; };
/* End PBXBuildFile section */
@@ -550,6 +554,10 @@
9F4B7B880FCC877A00DC4117 /* log-utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "log-utils.h"; sourceTree = "<group>"; };
9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "func-name-inferrer.cc"; sourceTree = "<group>"; };
9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "func-name-inferrer.h"; sourceTree = "<group>"; };
+ 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "fast-codegen.cc"; sourceTree = "<group>"; };
+ 9FBE03DD10BD409900F8BFBA /* fast-codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "fast-codegen.h"; sourceTree = "<group>"; };
+ 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "fast-codegen-ia32.cc"; path = "ia32/fast-codegen-ia32.cc"; sourceTree = "<group>"; };
+ 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "fast-codegen-arm.cc"; path = "arm/fast-codegen-arm.cc"; sourceTree = "<group>"; };
9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "oprofile-agent.cc"; sourceTree = "<group>"; };
9FC86ABC0F5FEDAC00F22668 /* oprofile-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "oprofile-agent.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -715,6 +723,10 @@
897FF1310E719B8F00D62E90 /* execution.h */,
897FF1320E719B8F00D62E90 /* factory.cc */,
897FF1330E719B8F00D62E90 /* factory.h */,
+ 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */,
+ 9FBE03DD10BD409900F8BFBA /* fast-codegen.h */,
+ 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */,
+ 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */,
89471C7F0EB23EE400B6874B /* flag-definitions.h */,
897FF1350E719B8F00D62E90 /* flags.cc */,
897FF1360E719B8F00D62E90 /* flags.h */,
@@ -1225,6 +1237,8 @@
9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */,
8981F6001010501900D1520E /* frame-element.cc in Sources */,
9F11D9A0105AF0A300EBE5B2 /* heap-profiler.cc in Sources */,
+ 9FBE03DE10BD409900F8BFBA /* fast-codegen.cc in Sources */,
+ 9FBE03E210BD40EA00F8BFBA /* fast-codegen-ia32.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1332,6 +1346,8 @@
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */,
8981F6011010502800D1520E /* frame-element.cc in Sources */,
9F11D9A1105AF0A300EBE5B2 /* heap-profiler.cc in Sources */,
+ 9FBE03DF10BD409900F8BFBA /* fast-codegen.cc in Sources */,
+ 9FBE03E510BD412600F8BFBA /* fast-codegen-arm.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};