Version 3.20.2
Remove deprecated heap profiler methods from V8 public API
Mark i18n functions as native and set proper names (issue 2745)
Correctly report stack trace when current function is FunctionApply builtin (Chromium issue 252097)
Enable GDBJIT interface for standalone by default.
Fix debuggersupport=off build. (issue 2754)
Introduce -m64 flag for making x64 when the default gcc compiler is for X32
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@15498 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index e78623d..3c113b2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2013-07-05: Version 3.20.2
+
+ Remove deprecated heap profiler methods from V8 public API
+
+ Mark i18n functions as native and set proper names
+ (issue 2745)
+
+ Correctly report stack trace when current function is FunctionApply
+ builtin (Chromium issue 252097)
+
+ Enable GDBJIT interface for standalone by default.
+
+ Fix debuggersupport=off build. (issue 2754)
+
+ Introduce -m64 flag for making x64 when the default gcc compiler is for
+ X32
+
+ Performance and stability improvements on all platforms.
+
+
2013-07-02: Version 3.20.1
Implemented WeakMap.prototype.clear function. (issue 2753)
diff --git a/Makefile b/Makefile
index 78e22d7..16c004f 100644
--- a/Makefile
+++ b/Makefile
@@ -80,10 +80,13 @@
ifeq ($(extrachecks), off)
GYPFLAGS += -Dv8_enable_extra_checks=0
endif
-# gdbjit=on
+# gdbjit=on/off
ifeq ($(gdbjit), on)
GYPFLAGS += -Dv8_enable_gdbjit=1
endif
+ifeq ($(gdbjit), off)
+ GYPFLAGS += -Dv8_enable_gdbjit=0
+endif
# vtunejit=on
ifeq ($(vtunejit), on)
GYPFLAGS += -Dv8_enable_vtunejit=1
@@ -116,6 +119,10 @@
ifeq ($(regexp), interpreted)
GYPFLAGS += -Dv8_interpreted_regexp=1
endif
+# i18nsupport=on
+ifeq ($(i18nsupport), on)
+ GYPFLAGS += -Dv8_enable_i18n_support=1
+endif
# arm specific flags.
# armv7=false/true
ifeq ($(armv7), false)
diff --git a/build/common.gypi b/build/common.gypi
index 09abe29..dbb33a8 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -104,6 +104,10 @@
# Interpreted regexp engine exists as platform-independent alternative
# based where the regular expression is compiled to a bytecode.
'v8_interpreted_regexp%': 0,
+
+ # Enable ECMAScript Internationalization API. Enabling this feature will
+ # add a dependency on the ICU library.
+ 'v8_enable_i18n_support%': 0,
},
'target_defaults': {
'conditions': [
@@ -125,6 +129,9 @@
['v8_interpreted_regexp==1', {
'defines': ['V8_INTERPRETED_REGEXP',],
}],
+ ['v8_enable_i18n_support==1', {
+ 'defines': ['V8_I18N_SUPPORT',],
+ }],
['v8_target_arch=="arm"', {
'defines': [
'V8_TARGET_ARCH_ARM',
@@ -452,6 +459,26 @@
}],
],
}],
+ ['(OS=="linux") and (v8_target_arch=="x64")', {
+ # Check whether the host compiler and target compiler support the
+ # '-m64' option and set it if so.
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'variables': {
+ 'm64flag': '<!((echo | $(echo ${CXX_host:-$(which g++)}) -m64 -E - > /dev/null 2>&1) && echo "-m64" || true)',
+ },
+ 'cflags': [ '<(m64flag)' ],
+ 'ldflags': [ '<(m64flag)' ],
+ }],
+ ['_toolset=="target"', {
+ 'variables': {
+ 'm64flag': '<!((echo | $(echo ${CXX_target:-<(CXX)}) -m64 -E - > /dev/null 2>&1) && echo "-m64" || true)',
+ },
+ 'cflags': [ '<(m64flag)' ],
+ 'ldflags': [ '<(m64flag)' ],
+ }]
+ ],
+ }],
['OS=="freebsd" or OS=="openbsd"', {
'cflags': [ '-I/usr/local/include' ],
}],
diff --git a/build/standalone.gypi b/build/standalone.gypi
index b14adfa..b1303c8 100644
--- a/build/standalone.gypi
+++ b/build/standalone.gypi
@@ -87,6 +87,12 @@
}, {
'os_posix%': 1,
}],
+ ['(v8_target_arch=="ia32" or v8_target_arch=="x64") and \
+ (OS=="linux" or OS=="mac")', {
+ 'v8_enable_gdbjit%': 1,
+ }, {
+ 'v8_enable_gdbjit%': 0,
+ }],
],
# Default ARM variable settings.
'armv7%': 'default',
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 8e6251a..df3bd9f 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -415,9 +415,6 @@
/** Returns a snapshot by index. */
const HeapSnapshot* GetHeapSnapshot(int index);
- /** Returns a profile by uid. */
- V8_DEPRECATED(const HeapSnapshot* FindHeapSnapshot(unsigned uid));
-
/**
* Returns SnapshotObjectId for a heap object referenced by |value| if
* it has been seen by the heap profiler, kUnknownObjectId otherwise.
diff --git a/src/api.cc b/src/api.cc
index ee80579..638a25f 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -38,6 +38,7 @@
#include "compiler.h"
#include "conversions-inl.h"
#include "counters.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
@@ -394,6 +395,9 @@
kSnapshotContext,
kLibraries,
kExperimentalLibraries,
+#if defined(ENABLE_I18N_SUPPORT)
+ kI18NExtension,
+#endif
kCompressedStartupDataCount
};
@@ -433,6 +437,17 @@
exp_libraries_source.length();
compressed_data[kExperimentalLibraries].raw_size =
i::ExperimentalNatives::GetRawScriptsSize();
+
+#if defined(ENABLE_I18N_SUPPORT)
+ i::Vector<const ii:byte> i18n_extension_source =
+ i::I18NNatives::GetScriptsSource();
+ compressed_data[kI18NExtension].data =
+ reinterpret_cast<const char*>(i18n_extension_source.start());
+ compressed_data[kI18NExtension].compressed_size =
+ i18n_extension_source.length();
+ compressed_data[kI18NExtension].raw_size =
+ i::I18NNatives::GetRawScriptsSize();
+#endif
#endif
}
@@ -462,6 +477,15 @@
decompressed_data[kExperimentalLibraries].data,
decompressed_data[kExperimentalLibraries].raw_size);
i::ExperimentalNatives::SetRawScriptsSource(exp_libraries_source);
+
+#if defined(ENABLE_I18N_SUPPORT)
+ ASSERT_EQ(i::I18NNatives::GetRawScriptsSize(),
+ decompressed_data[kI18NExtension].raw_size);
+ i::Vector<const char> i18n_extension_source(
+ decompressed_data[kI18NExtension].data,
+ decompressed_data[kI18NExtension].raw_size);
+ i::I18NNatives::SetRawScriptsSource(i18n_extension_source);
+#endif
#endif
}
@@ -3495,7 +3519,7 @@
// to propagate outside.
TryCatch try_catch;
EXCEPTION_PREAMBLE(isolate);
- i::Handle<i::Object> result = i::SetPrototype(self, value_obj);
+ i::Handle<i::Object> result = i::JSObject::SetPrototype(self, value_obj);
has_pending_exception = result.is_null();
EXCEPTION_BAILOUT_CHECK(isolate, false);
return true;
@@ -7721,12 +7745,6 @@
}
-const HeapSnapshot* HeapProfiler::FindHeapSnapshot(unsigned uid) {
- return reinterpret_cast<const HeapSnapshot*>(
- reinterpret_cast<i::HeapProfiler*>(this)->FindSnapshot(uid));
-}
-
-
SnapshotObjectId HeapProfiler::GetObjectId(Handle<Value> value) {
i::Handle<i::Object> obj = Utils::OpenHandle(*value);
return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotObjectId(obj);
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index c28b641..b08353e 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1796,13 +1796,6 @@
}
-LInstruction* LChunkBuilder::DoFixedArrayBaseLength(
- HFixedArrayBaseLength* instr) {
- LOperand* array = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
-}
-
-
LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
LOperand* map = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LMapEnumLength(map));
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index f34b623..39cab8f 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -99,7 +99,6 @@
V(DoubleToSmi) \
V(DummyUse) \
V(ElementsKind) \
- V(FixedArrayBaseLength) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
@@ -1281,20 +1280,6 @@
};
-class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> {
- public:
- explicit LFixedArrayBaseLength(LOperand* value) {
- inputs_[0] = value;
- }
-
- LOperand* value() { return inputs_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength,
- "fixed-array-base-length")
- DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength)
-};
-
-
class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LMapEnumLength(LOperand* value) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 8525095..272db15 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -1846,13 +1846,6 @@
}
-void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) {
- Register result = ToRegister(instr->result());
- Register array = ToRegister(instr->value());
- __ ldr(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset));
-}
-
-
void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->value());
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 9e4dc40..cce20ff 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -33,6 +33,7 @@
#include "bootstrapper.h"
#include "codegen.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "runtime.h"
diff --git a/src/arm/regexp-macro-assembler-arm.cc b/src/arm/regexp-macro-assembler-arm.cc
index 0acb737..189ea8d 100644
--- a/src/arm/regexp-macro-assembler-arm.cc
+++ b/src/arm/regexp-macro-assembler-arm.cc
@@ -29,6 +29,7 @@
#if V8_TARGET_ARCH_ARM
+#include "cpu-profiler.h"
#include "unicode.h"
#include "log.h"
#include "code-stubs.h"
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index d5b46de..49333eb 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -45,6 +45,10 @@
#include "extensions/statistics-extension.h"
#include "code-stubs.h"
+#if defined(V8_I18N_SUPPORT)
+#include "extensions/i18n/i18n-extension.h"
+#endif
+
namespace v8 {
namespace internal {
@@ -102,6 +106,9 @@
GCExtension::Register();
ExternalizeStringExtension::Register();
StatisticsExtension::Register();
+#if defined(V8_I18N_SUPPORT)
+ v8_i18n::Extension::Register();
+#endif
}
@@ -2279,6 +2286,12 @@
InstallExtension(isolate, "v8/statistics", &extension_states);
}
+#if defined(V8_I18N_SUPPORT)
+ if (FLAG_enable_i18n) {
+ InstallExtension(isolate, "v8/i18n", &extension_states);
+ }
+#endif
+
if (extensions == NULL) return true;
// Install required extensions
int count = v8::ImplementationUtilities::GetNameCount(extensions);
diff --git a/src/builtins.cc b/src/builtins.cc
index 5cc7915..be04ddf 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -31,6 +31,7 @@
#include "arguments.h"
#include "bootstrapper.h"
#include "builtins.h"
+#include "cpu-profiler.h"
#include "gdb-jit.h"
#include "ic-inl.h"
#include "heap-profiler.h"
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc
index 06c9039..96266af 100644
--- a/src/code-stubs-hydrogen.cc
+++ b/src/code-stubs-hydrogen.cc
@@ -510,8 +510,7 @@
HInstruction* elements = AddLoadElements(js_array);
- HInstruction* elements_length =
- AddInstruction(new(zone) HFixedArrayBaseLength(elements));
+ HInstruction* elements_length = AddLoadFixedArrayLength(elements);
HValue* new_elements = BuildAllocateElementsAndInitializeElementsHeader(
context(), to_kind, elements_length);
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 2d94ec9..2ed2ba3 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -29,6 +29,7 @@
#include "bootstrapper.h"
#include "code-stubs.h"
+#include "cpu-profiler.h"
#include "stub-cache.h"
#include "factory.h"
#include "gdb-jit.h"
diff --git a/src/codegen.cc b/src/codegen.cc
index 7328e40..8029d2f 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -30,6 +30,7 @@
#include "bootstrapper.h"
#include "codegen.h"
#include "compiler.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "prettyprinter.h"
#include "rewriter.h"
diff --git a/src/compiler.cc b/src/compiler.cc
index f643b9d..8edb41d 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -32,6 +32,7 @@
#include "bootstrapper.h"
#include "codegen.h"
#include "compilation-cache.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -1058,7 +1059,7 @@
} else if (status != OptimizingCompiler::SUCCEEDED) {
info->set_bailout_reason("failed/bailed out last time");
status = optimizing_compiler->AbortOptimization();
- } else if (isolate->debugger()->IsDebuggerActive()) {
+ } else if (isolate->DebuggerHasBreakPoints()) {
info->set_bailout_reason("debugger is active");
status = optimizing_compiler->AbortOptimization();
} else {
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index be64dd7..b3800f5 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -62,10 +62,9 @@
}
-void ProfilerEventsProcessor::AddCurrentStack() {
+void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
TickSampleEventRecord record(enqueue_order_);
TickSample* sample = &record.sample;
- Isolate* isolate = Isolate::Current();
sample->state = isolate->current_vm_state();
sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
for (StackTraceFrameIterator it(isolate);
@@ -428,7 +427,7 @@
if (profiles_->StartProfiling(title, next_profile_uid_++, record_samples)) {
StartProcessorIfNotStarted();
}
- processor_->AddCurrentStack();
+ processor_->AddCurrentStack(isolate_);
}
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
index 61d40f0..77fdb06 100644
--- a/src/cpu-profiler.h
+++ b/src/cpu-profiler.h
@@ -161,7 +161,7 @@
void Enqueue(const CodeEventsContainer& event);
// Puts current stack into tick sample events buffer.
- void AddCurrentStack();
+ void AddCurrentStack(Isolate* isolate);
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
diff --git a/src/d8.js b/src/d8.js
index df10461..3efea06 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -1020,7 +1020,7 @@
args.substring(nextPos + 1, args.length) : 'all';
if (!arg2) {
arg2 = 'all'; // if unspecified, set for all.
- } if (arg2 == 'unc') { // check for short cut.
+ } else if (arg2 == 'unc') { // check for short cut.
arg2 = 'uncaught';
}
excType = arg2;
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc
index c6c098e..f322e85 100644
--- a/src/deoptimizer.cc
+++ b/src/deoptimizer.cc
@@ -714,6 +714,10 @@
// Print some helpful diagnostic information.
int64_t start = OS::Ticks();
+ if (FLAG_log_timer_events &&
+ compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) {
+ LOG(isolate(), CodeDeoptEvent(compiled_code_));
+ }
if (trace_) {
PrintF("[deoptimizing (DEOPT %s): begin 0x%08" V8PRIxPTR " ",
MessageFor(bailout_type_),
diff --git a/src/extensions/i18n/break-iterator.cc b/src/extensions/i18n/break-iterator.cc
new file mode 100644
index 0000000..1225360
--- /dev/null
+++ b/src/extensions/i18n/break-iterator.cc
@@ -0,0 +1,331 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "break-iterator.h"
+
+#include <string.h>
+
+#include "i18n-utils.h"
+#include "unicode/brkiter.h"
+#include "unicode/locid.h"
+#include "unicode/rbbi.h"
+
+namespace v8_i18n {
+
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
+static icu::UnicodeString* ResetAdoptedText(v8::Handle<v8::Object>,
+ v8::Handle<v8::Value>);
+static icu::BreakIterator* InitializeBreakIterator(v8::Handle<v8::String>,
+ v8::Handle<v8::Object>,
+ v8::Handle<v8::Object>);
+static icu::BreakIterator* CreateICUBreakIterator(const icu::Locale&,
+ v8::Handle<v8::Object>);
+static void SetResolvedSettings(const icu::Locale&,
+ icu::BreakIterator*,
+ v8::Handle<v8::Object>);
+
+icu::BreakIterator* BreakIterator::UnpackBreakIterator(
+ v8::Handle<v8::Object> obj) {
+ v8::HandleScope handle_scope;
+
+ // v8::ObjectTemplate doesn't have HasInstance method so we can't check
+ // if obj is an instance of BreakIterator class. We'll check for a property
+ // that has to be in the object. The same applies to other services, like
+ // Collator and DateTimeFormat.
+ if (obj->HasOwnProperty(v8::String::New("breakIterator"))) {
+ return static_cast<icu::BreakIterator*>(
+ obj->GetAlignedPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void BreakIterator::DeleteBreakIterator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a break iterator.
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
+ delete UnpackBreakIterator(handle);
+
+ delete static_cast<icu::UnicodeString*>(
+ handle->GetAlignedPointerFromInternalField(1));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+// Throws a JavaScript exception.
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
+ // Returns undefined, and schedules an exception to be thrown.
+ return v8::ThrowException(v8::Exception::Error(
+ v8::String::New("BreakIterator method called on an object "
+ "that is not a BreakIterator.")));
+}
+
+// Deletes the old value and sets the adopted text in corresponding
+// JavaScript object.
+icu::UnicodeString* ResetAdoptedText(
+ v8::Handle<v8::Object> obj, v8::Handle<v8::Value> value) {
+ // Get the previous value from the internal field.
+ icu::UnicodeString* text = static_cast<icu::UnicodeString*>(
+ obj->GetAlignedPointerFromInternalField(1));
+ delete text;
+
+ // Assign new value to the internal pointer.
+ v8::String::Value text_value(value);
+ text = new icu::UnicodeString(
+ reinterpret_cast<const UChar*>(*text_value), text_value.length());
+ obj->SetAlignedPointerInInternalField(1, text);
+
+ // Return new unicode string pointer.
+ return text;
+}
+
+void BreakIterator::JSInternalBreakIteratorAdoptText(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New(
+ "Internal error. Iterator and text have to be specified.")));
+ return;
+ }
+
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ break_iterator->setText(*ResetAdoptedText(args[0]->ToObject(), args[1]));
+}
+
+void BreakIterator::JSInternalBreakIteratorFirst(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->first()));
+}
+
+void BreakIterator::JSInternalBreakIteratorNext(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->next()));
+}
+
+void BreakIterator::JSInternalBreakIteratorCurrent(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->current()));
+}
+
+void BreakIterator::JSInternalBreakIteratorBreakType(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ // TODO(cira): Remove cast once ICU fixes base BreakIterator class.
+ icu::RuleBasedBreakIterator* rule_based_iterator =
+ static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
+ int32_t status = rule_based_iterator->getRuleStatus();
+ // Keep return values in sync with JavaScript BreakType enum.
+ v8::Handle<v8::String> result;
+ if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
+ result = v8::String::New("none");
+ } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
+ result = v8::String::New("number");
+ } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
+ result = v8::String::New("letter");
+ } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
+ result = v8::String::New("kana");
+ } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
+ result = v8::String::New("ideo");
+ } else {
+ result = v8::String::New("unknown");
+ }
+ args.GetReturnValue().Set(result);
+}
+
+void BreakIterator::JSCreateBreakIterator(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() ||
+ !args[2]->IsObject()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, wrong parameters.")));
+ return;
+ }
+
+ v8::Isolate* isolate = args.GetIsolate();
+ v8::Local<v8::ObjectTemplate> break_iterator_template =
+ Utils::GetTemplate2(isolate);
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object = break_iterator_template->NewInstance();
+ // But the handle shouldn't be empty.
+ // That can happen if there was a stack overflow when creating the object.
+ if (local_object.IsEmpty()) {
+ args.GetReturnValue().Set(local_object);
+ return;
+ }
+
+ // Set break iterator as internal field of the resulting JS object.
+ icu::BreakIterator* break_iterator = InitializeBreakIterator(
+ args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
+
+ if (!break_iterator) {
+ v8::ThrowException(v8::Exception::Error(v8::String::New(
+ "Internal error. Couldn't create ICU break iterator.")));
+ return;
+ } else {
+ local_object->SetAlignedPointerInInternalField(0, break_iterator);
+ // Make sure that the pointer to adopted text is NULL.
+ local_object->SetAlignedPointerInInternalField(1, NULL);
+
+ v8::TryCatch try_catch;
+ local_object->Set(v8::String::New("breakIterator"),
+ v8::String::New("valid"));
+ if (try_catch.HasCaught()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, couldn't set property.")));
+ return;
+ }
+ }
+
+ v8::Persistent<v8::Object> wrapper(isolate, local_object);
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DeleteBreakIterator);
+ args.GetReturnValue().Set(wrapper);
+ wrapper.ClearAndLeak();
+}
+
+static icu::BreakIterator* InitializeBreakIterator(
+ v8::Handle<v8::String> locale,
+ v8::Handle<v8::Object> options,
+ v8::Handle<v8::Object> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::AsciiValue bcp47_locale(locale);
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::BreakIterator* break_iterator =
+ CreateICUBreakIterator(icu_locale, options);
+ if (!break_iterator) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ break_iterator = CreateICUBreakIterator(no_extension_locale, options);
+
+ // Set resolved settings (locale).
+ SetResolvedSettings(no_extension_locale, break_iterator, resolved);
+ } else {
+ SetResolvedSettings(icu_locale, break_iterator, resolved);
+ }
+
+ return break_iterator;
+}
+
+static icu::BreakIterator* CreateICUBreakIterator(
+ const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
+ UErrorCode status = U_ZERO_ERROR;
+ icu::BreakIterator* break_iterator = NULL;
+ icu::UnicodeString type;
+ if (!Utils::ExtractStringSetting(options, "type", &type)) {
+ // Type had to be in the options. This would be an internal error.
+ return NULL;
+ }
+
+ if (type == UNICODE_STRING_SIMPLE("character")) {
+ break_iterator =
+ icu::BreakIterator::createCharacterInstance(icu_locale, status);
+ } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
+ break_iterator =
+ icu::BreakIterator::createSentenceInstance(icu_locale, status);
+ } else if (type == UNICODE_STRING_SIMPLE("line")) {
+ break_iterator =
+ icu::BreakIterator::createLineInstance(icu_locale, status);
+ } else {
+ // Defualt is word iterator.
+ break_iterator =
+ icu::BreakIterator::createWordInstance(icu_locale, status);
+ }
+
+ if (U_FAILURE(status)) {
+ delete break_iterator;
+ return NULL;
+ }
+
+ return break_iterator;
+}
+
+static void SetResolvedSettings(const icu::Locale& icu_locale,
+ icu::BreakIterator* date_format,
+ v8::Handle<v8::Object> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ resolved->Set(v8::String::New("locale"), v8::String::New(result));
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ resolved->Set(v8::String::New("locale"), v8::String::New("und"));
+ }
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/break-iterator.h b/src/extensions/i18n/break-iterator.h
new file mode 100644
index 0000000..c44c20f
--- /dev/null
+++ b/src/extensions/i18n/break-iterator.h
@@ -0,0 +1,85 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
+#define V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class BreakIterator;
+class UnicodeString;
+}
+
+namespace v8_i18n {
+
+class BreakIterator {
+ public:
+ static void JSCreateBreakIterator(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks iterator object from corresponding JavaScript object.
+ static icu::BreakIterator* UnpackBreakIterator(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the BreakIterator once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteBreakIterator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param);
+
+ // Assigns new text to the iterator.
+ static void JSInternalBreakIteratorAdoptText(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Moves iterator to the beginning of the string and returns new position.
+ static void JSInternalBreakIteratorFirst(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Moves iterator to the next position and returns it.
+ static void JSInternalBreakIteratorNext(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Returns current iterator's current position.
+ static void JSInternalBreakIteratorCurrent(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Returns type of the item from current position.
+ // This call is only valid for word break iterators. Others just return 0.
+ static void JSInternalBreakIteratorBreakType(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ BreakIterator() {}
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
diff --git a/src/extensions/i18n/break-iterator.js b/src/extensions/i18n/break-iterator.js
new file mode 100644
index 0000000..eefd8c2
--- /dev/null
+++ b/src/extensions/i18n/break-iterator.js
@@ -0,0 +1,197 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Initializes the given object so it's a valid BreakIterator instance.
+ * Useful for subclassing.
+ */
+function initializeBreakIterator(iterator, locales, options) {
+ native function NativeJSCreateBreakIterator();
+
+ if (iterator.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize v8BreakIterator object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'breakiterator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'type', getOption(
+ 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
+
+ var locale = resolveLocale('breakiterator', locales, options);
+ var resolved = Object.defineProperties({}, {
+ requestedLocale: {value: locale.locale, writable: true},
+ type: {value: internalOptions.type, writable: true},
+ locale: {writable: true}
+ });
+
+ var internalIterator = NativeJSCreateBreakIterator(locale.locale,
+ internalOptions,
+ resolved);
+
+ Object.defineProperty(iterator, 'iterator', {value: internalIterator});
+ Object.defineProperty(iterator, 'resolved', {value: resolved});
+ Object.defineProperty(iterator, '__initializedIntlObject',
+ {value: 'breakiterator'});
+
+ return iterator;
+}
+
+
+/**
+ * Constructs Intl.v8BreakIterator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'v8BreakIterator', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.v8BreakIterator(locales, options);
+ }
+
+ return initializeBreakIterator(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * BreakIterator resolvedOptions method.
+ */
+%SetProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'breakiterator') {
+ throw new TypeError('resolvedOptions method called on a non-object or ' +
+ 'on a object that is not Intl.v8BreakIterator.');
+ }
+
+ var segmenter = this;
+ var locale = getOptimalLanguageTag(segmenter.resolved.requestedLocale,
+ segmenter.resolved.locale);
+
+ return {
+ locale: locale,
+ type: segmenter.resolved.type
+ };
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.v8BreakIterator, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('breakiterator', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf);
+%SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf);
+
+
+/**
+ * Adopts text to segment using the iterator. Old text, if present,
+ * gets discarded.
+ */
+function adoptText(iterator, text) {
+ native function NativeJSBreakIteratorAdoptText();
+ NativeJSBreakIteratorAdoptText(iterator.iterator, String(text));
+}
+
+
+/**
+ * Returns index of the first break in the string and moves current pointer.
+ */
+function first(iterator) {
+ native function NativeJSBreakIteratorFirst();
+ return NativeJSBreakIteratorFirst(iterator.iterator);
+}
+
+
+/**
+ * Returns the index of the next break and moves the pointer.
+ */
+function next(iterator) {
+ native function NativeJSBreakIteratorNext();
+ return NativeJSBreakIteratorNext(iterator.iterator);
+}
+
+
+/**
+ * Returns index of the current break.
+ */
+function current(iterator) {
+ native function NativeJSBreakIteratorCurrent();
+ return NativeJSBreakIteratorCurrent(iterator.iterator);
+}
+
+
+/**
+ * Returns type of the current break.
+ */
+function breakType(iterator) {
+ native function NativeJSBreakIteratorBreakType();
+ return NativeJSBreakIteratorBreakType(iterator.iterator);
+}
+
+
+addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1);
+addBoundMethod(Intl.v8BreakIterator, 'first', first, 0);
+addBoundMethod(Intl.v8BreakIterator, 'next', next, 0);
+addBoundMethod(Intl.v8BreakIterator, 'current', current, 0);
+addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0);
diff --git a/src/extensions/i18n/collator.cc b/src/extensions/i18n/collator.cc
new file mode 100644
index 0000000..4ffa414
--- /dev/null
+++ b/src/extensions/i18n/collator.cc
@@ -0,0 +1,363 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "collator.h"
+
+#include "i18n-utils.h"
+#include "unicode/coll.h"
+#include "unicode/locid.h"
+#include "unicode/ucol.h"
+
+namespace v8_i18n {
+
+static icu::Collator* InitializeCollator(
+ v8::Handle<v8::String>, v8::Handle<v8::Object>, v8::Handle<v8::Object>);
+
+static icu::Collator* CreateICUCollator(
+ const icu::Locale&, v8::Handle<v8::Object>);
+
+static bool SetBooleanAttribute(
+ UColAttribute, const char*, v8::Handle<v8::Object>, icu::Collator*);
+
+static void SetResolvedSettings(
+ const icu::Locale&, icu::Collator*, v8::Handle<v8::Object>);
+
+static void SetBooleanSetting(
+ UColAttribute, icu::Collator*, const char*, v8::Handle<v8::Object>);
+
+icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) {
+ v8::HandleScope handle_scope;
+
+ if (obj->HasOwnProperty(v8::String::New("collator"))) {
+ return static_cast<icu::Collator*>(
+ obj->GetAlignedPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void Collator::DeleteCollator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a collator.
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
+ delete UnpackCollator(handle);
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+// Throws a JavaScript exception.
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
+ // Returns undefined, and schedules an exception to be thrown.
+ return v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Collator method called on an object "
+ "that is not a Collator.")));
+}
+
+// When there's an ICU error, throw a JavaScript error with |message|.
+static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) {
+ return v8::ThrowException(v8::Exception::Error(v8::String::New(message)));
+}
+
+// static
+void Collator::JSInternalCompare(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 || !args[0]->IsObject() ||
+ !args[1]->IsString() || !args[2]->IsString()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Collator and two string arguments are required.")));
+ return;
+ }
+
+ icu::Collator* collator = UnpackCollator(args[0]->ToObject());
+ if (!collator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ v8::String::Value string_value1(args[1]);
+ v8::String::Value string_value2(args[2]);
+ const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1);
+ const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2);
+ UErrorCode status = U_ZERO_ERROR;
+ UCollationResult result = collator->compare(
+ string1, string_value1.length(), string2, string_value2.length(), status);
+
+ if (U_FAILURE(status)) {
+ ThrowExceptionForICUError(
+ "Internal error. Unexpected failure in Collator.compare.");
+ return;
+ }
+
+ args.GetReturnValue().Set(result);
+}
+
+void Collator::JSCreateCollator(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() ||
+ !args[2]->IsObject()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error, wrong parameters.")));
+ return;
+ }
+
+ v8::Isolate* isolate = args.GetIsolate();
+ v8::Local<v8::ObjectTemplate> intl_collator_template =
+ Utils::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object = intl_collator_template->NewInstance();
+ // But the handle shouldn't be empty.
+ // That can happen if there was a stack overflow when creating the object.
+ if (local_object.IsEmpty()) {
+ args.GetReturnValue().Set(local_object);
+ return;
+ }
+
+ // Set collator as internal field of the resulting JS object.
+ icu::Collator* collator = InitializeCollator(
+ args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
+
+ if (!collator) {
+ v8::ThrowException(v8::Exception::Error(v8::String::New(
+ "Internal error. Couldn't create ICU collator.")));
+ return;
+ } else {
+ local_object->SetAlignedPointerInInternalField(0, collator);
+
+ // Make it safer to unpack later on.
+ v8::TryCatch try_catch;
+ local_object->Set(v8::String::New("collator"), v8::String::New("valid"));
+ if (try_catch.HasCaught()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, couldn't set property.")));
+ return;
+ }
+ }
+
+ v8::Persistent<v8::Object> wrapper(isolate, local_object);
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DeleteCollator);
+ args.GetReturnValue().Set(wrapper);
+ wrapper.ClearAndLeak();
+}
+
+static icu::Collator* InitializeCollator(v8::Handle<v8::String> locale,
+ v8::Handle<v8::Object> options,
+ v8::Handle<v8::Object> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::AsciiValue bcp47_locale(locale);
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::Collator* collator = CreateICUCollator(icu_locale, options);
+ if (!collator) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ collator = CreateICUCollator(no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system).
+ SetResolvedSettings(no_extension_locale, collator, resolved);
+ } else {
+ SetResolvedSettings(icu_locale, collator, resolved);
+ }
+
+ return collator;
+}
+
+static icu::Collator* CreateICUCollator(
+ const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
+ // Make collator from options.
+ icu::Collator* collator = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ collator = icu::Collator::createInstance(icu_locale, status);
+
+ if (U_FAILURE(status)) {
+ delete collator;
+ return NULL;
+ }
+
+ // Set flags first, and then override them with sensitivity if necessary.
+ SetBooleanAttribute(UCOL_NUMERIC_COLLATION, "numeric", options, collator);
+
+ // Normalization is always on, by the spec. We are free to optimize
+ // if the strings are already normalized (but we don't have a way to tell
+ // that right now).
+ collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
+
+ icu::UnicodeString case_first;
+ if (Utils::ExtractStringSetting(options, "caseFirst", &case_first)) {
+ if (case_first == UNICODE_STRING_SIMPLE("upper")) {
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
+ } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
+ } else {
+ // Default (false/off).
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
+ }
+ }
+
+ icu::UnicodeString sensitivity;
+ if (Utils::ExtractStringSetting(options, "sensitivity", &sensitivity)) {
+ if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
+ collator->setStrength(icu::Collator::PRIMARY);
+ } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
+ collator->setStrength(icu::Collator::SECONDARY);
+ } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
+ collator->setStrength(icu::Collator::PRIMARY);
+ collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
+ } else {
+ // variant (default)
+ collator->setStrength(icu::Collator::TERTIARY);
+ }
+ }
+
+ bool ignore;
+ if (Utils::ExtractBooleanSetting(options, "ignorePunctuation", &ignore)) {
+ if (ignore) {
+ collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
+ }
+ }
+
+ return collator;
+}
+
+static bool SetBooleanAttribute(UColAttribute attribute,
+ const char* name,
+ v8::Handle<v8::Object> options,
+ icu::Collator* collator) {
+ UErrorCode status = U_ZERO_ERROR;
+ bool result;
+ if (Utils::ExtractBooleanSetting(options, name, &result)) {
+ collator->setAttribute(attribute, result ? UCOL_ON : UCOL_OFF, status);
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void SetResolvedSettings(const icu::Locale& icu_locale,
+ icu::Collator* collator,
+ v8::Handle<v8::Object> resolved) {
+ SetBooleanSetting(UCOL_NUMERIC_COLLATION, collator, "numeric", resolved);
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
+ case UCOL_LOWER_FIRST:
+ resolved->Set(v8::String::New("caseFirst"), v8::String::New("lower"));
+ break;
+ case UCOL_UPPER_FIRST:
+ resolved->Set(v8::String::New("caseFirst"), v8::String::New("upper"));
+ break;
+ default:
+ resolved->Set(v8::String::New("caseFirst"), v8::String::New("false"));
+ }
+
+ switch (collator->getAttribute(UCOL_STRENGTH, status)) {
+ case UCOL_PRIMARY: {
+ resolved->Set(v8::String::New("strength"), v8::String::New("primary"));
+
+ // case level: true + s1 -> case, s1 -> base.
+ if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("case"));
+ } else {
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("base"));
+ }
+ break;
+ }
+ case UCOL_SECONDARY:
+ resolved->Set(v8::String::New("strength"), v8::String::New("secondary"));
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("accent"));
+ break;
+ case UCOL_TERTIARY:
+ resolved->Set(v8::String::New("strength"), v8::String::New("tertiary"));
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
+ break;
+ case UCOL_QUATERNARY:
+ // We shouldn't get quaternary and identical from ICU, but if we do
+ // put them into variant.
+ resolved->Set(v8::String::New("strength"), v8::String::New("quaternary"));
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
+ break;
+ default:
+ resolved->Set(v8::String::New("strength"), v8::String::New("identical"));
+ resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
+ }
+
+ if (UCOL_SHIFTED == collator->getAttribute(UCOL_ALTERNATE_HANDLING, status)) {
+ resolved->Set(v8::String::New("ignorePunctuation"),
+ v8::Boolean::New(true));
+ } else {
+ resolved->Set(v8::String::New("ignorePunctuation"),
+ v8::Boolean::New(false));
+ }
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ resolved->Set(v8::String::New("locale"), v8::String::New(result));
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ resolved->Set(v8::String::New("locale"), v8::String::New("und"));
+ }
+}
+
+static void SetBooleanSetting(UColAttribute attribute,
+ icu::Collator* collator,
+ const char* property,
+ v8::Handle<v8::Object> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+ if (UCOL_ON == collator->getAttribute(attribute, status)) {
+ resolved->Set(v8::String::New(property), v8::Boolean::New(true));
+ } else {
+ resolved->Set(v8::String::New(property), v8::Boolean::New(false));
+ }
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/collator.h b/src/extensions/i18n/collator.h
new file mode 100644
index 0000000..a3991b9
--- /dev/null
+++ b/src/extensions/i18n/collator.h
@@ -0,0 +1,68 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_COLLATOR_H_
+#define V8_EXTENSIONS_I18N_COLLATOR_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class Collator;
+class UnicodeString;
+}
+
+namespace v8_i18n {
+
+class Collator {
+ public:
+ static void JSCreateCollator(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks collator object from corresponding JavaScript object.
+ static icu::Collator* UnpackCollator(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the Collator once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteCollator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param);
+
+ // Compare two strings and returns -1, 0 and 1 depending on
+ // whether string1 is smaller than, equal to or larger than string2.
+ static void JSInternalCompare(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ Collator() {}
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_COLLATOR_H_
diff --git a/src/extensions/i18n/collator.js b/src/extensions/i18n/collator.js
new file mode 100644
index 0000000..3483515
--- /dev/null
+++ b/src/extensions/i18n/collator.js
@@ -0,0 +1,212 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Initializes the given object so it's a valid Collator instance.
+ * Useful for subclassing.
+ */
+function initializeCollator(collator, locales, options) {
+ native function NativeJSCreateCollator();
+
+ if (collator.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize Collator object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'collator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'usage', getOption(
+ 'usage', 'string', ['sort', 'search'], 'sort'));
+
+ var sensitivity = getOption('sensitivity', 'string',
+ ['base', 'accent', 'case', 'variant']);
+ if (sensitivity === undefined && internalOptions.usage === 'sort') {
+ sensitivity = 'variant';
+ }
+ defineWEProperty(internalOptions, 'sensitivity', sensitivity);
+
+ defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
+ 'ignorePunctuation', 'boolean', undefined, false));
+
+ var locale = resolveLocale('collator', locales, options);
+
+ // ICU can't take kb, kc... parameters through localeID, so we need to pass
+ // them as options.
+ // One exception is -co- which has to be part of the extension, but only for
+ // usage: sort, and its value can't be 'standard' or 'search'.
+ var extensionMap = parseExtension(locale.extension);
+ setOptions(
+ options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
+
+ var collation = 'default';
+ var extension = '';
+ if (extensionMap.hasOwnProperty('co') && internalOptions.usage === 'sort') {
+ if (ALLOWED_CO_VALUES.indexOf(extensionMap.co) !== -1) {
+ extension = '-u-co-' + extensionMap.co;
+ // ICU can't tell us what the collation is, so save user's input.
+ collation = extensionMap.co;
+ }
+ } else if (internalOptions.usage === 'search') {
+ extension = '-u-co-search';
+ }
+ defineWEProperty(internalOptions, 'collation', collation);
+
+ var requestedLocale = locale.locale + extension;
+
+ // We define all properties C++ code may produce, to prevent security
+ // problems. If malicious user decides to redefine Object.prototype.locale
+ // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
+ // Object.defineProperties will either succeed defining or throw an error.
+ var resolved = Object.defineProperties({}, {
+ caseFirst: {writable: true},
+ collation: {value: internalOptions.collation, writable: true},
+ ignorePunctuation: {writable: true},
+ locale: {writable: true},
+ numeric: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ sensitivity: {writable: true},
+ strength: {writable: true},
+ usage: {value: internalOptions.usage, writable: true}
+ });
+
+ var internalCollator = NativeJSCreateCollator(requestedLocale,
+ internalOptions,
+ resolved);
+
+ // Writable, configurable and enumerable are set to false by default.
+ Object.defineProperty(collator, 'collator', {value: internalCollator});
+ Object.defineProperty(collator, '__initializedIntlObject',
+ {value: 'collator'});
+ Object.defineProperty(collator, 'resolved', {value: resolved});
+
+ return collator;
+}
+
+
+/**
+ * Constructs Intl.Collator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'Collator', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.Collator(locales, options);
+ }
+
+ return initializeCollator(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * Collator resolvedOptions method.
+ */
+%SetProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'collator') {
+ throw new TypeError('resolvedOptions method called on a non-object ' +
+ 'or on a object that is not Intl.Collator.');
+ }
+
+ var coll = this;
+ var locale = getOptimalLanguageTag(coll.resolved.requestedLocale,
+ coll.resolved.locale);
+
+ return {
+ locale: locale,
+ usage: coll.resolved.usage,
+ sensitivity: coll.resolved.sensitivity,
+ ignorePunctuation: coll.resolved.ignorePunctuation,
+ numeric: coll.resolved.numeric,
+ caseFirst: coll.resolved.caseFirst,
+ collation: coll.resolved.collation
+ };
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions');
+%FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.Collator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('collator', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.Collator.supportedLocalesOf);
+%SetNativeFlag(Intl.Collator.supportedLocalesOf);
+
+
+/**
+ * When the compare method is called with two arguments x and y, it returns a
+ * Number other than NaN that represents the result of a locale-sensitive
+ * String comparison of x with y.
+ * The result is intended to order String values in the sort order specified
+ * by the effective locale and collation options computed during construction
+ * of this Collator object, and will be negative, zero, or positive, depending
+ * on whether x comes before y in the sort order, the Strings are equal under
+ * the sort order, or x comes after y in the sort order, respectively.
+ */
+function compare(collator, x, y) {
+ native function NativeJSInternalCompare();
+ return NativeJSInternalCompare(collator.collator, String(x), String(y));
+};
+
+
+addBoundMethod(Intl.Collator, 'compare', compare, 2);
diff --git a/src/extensions/i18n/date-format.cc b/src/extensions/i18n/date-format.cc
new file mode 100644
index 0000000..1058e37
--- /dev/null
+++ b/src/extensions/i18n/date-format.cc
@@ -0,0 +1,329 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "date-format.h"
+
+#include <string.h>
+
+#include "i18n-utils.h"
+#include "unicode/calendar.h"
+#include "unicode/dtfmtsym.h"
+#include "unicode/dtptngen.h"
+#include "unicode/locid.h"
+#include "unicode/numsys.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timezone.h"
+
+namespace v8_i18n {
+
+static icu::SimpleDateFormat* InitializeDateTimeFormat(v8::Handle<v8::String>,
+ v8::Handle<v8::Object>,
+ v8::Handle<v8::Object>);
+static icu::SimpleDateFormat* CreateICUDateFormat(const icu::Locale&,
+ v8::Handle<v8::Object>);
+static void SetResolvedSettings(const icu::Locale&,
+ icu::SimpleDateFormat*,
+ v8::Handle<v8::Object>);
+
+icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
+ v8::Handle<v8::Object> obj) {
+ v8::HandleScope handle_scope;
+
+ if (obj->HasOwnProperty(v8::String::New("dateFormat"))) {
+ return static_cast<icu::SimpleDateFormat*>(
+ obj->GetAlignedPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void DateFormat::DeleteDateFormat(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a date time formatter.
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
+ delete UnpackDateFormat(handle);
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+void DateFormat::JSInternalFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ double millis = 0.0;
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsDate()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New(
+ "Internal error. Formatter and date value have to be specified.")));
+ return;
+ } else {
+ millis = v8::Date::Cast(*args[1])->NumberValue();
+ }
+
+ icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject());
+ if (!date_format) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("DateTimeFormat method called on an object "
+ "that is not a DateTimeFormat.")));
+ return;
+ }
+
+ icu::UnicodeString result;
+ date_format->format(millis, result);
+
+ args.GetReturnValue().Set(v8::String::New(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
+}
+
+void DateFormat::JSInternalParse(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::UnicodeString string_date;
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New(
+ "Internal error. Formatter and string have to be specified.")));
+ return;
+ } else {
+ if (!Utils::V8StringToUnicodeString(args[1], &string_date)) {
+ string_date = "";
+ }
+ }
+
+ icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject());
+ if (!date_format) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("DateTimeFormat method called on an object "
+ "that is not a DateTimeFormat.")));
+ return;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ UDate date = date_format->parse(string_date, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ args.GetReturnValue().Set(v8::Date::New(static_cast<double>(date)));
+}
+
+void DateFormat::JSCreateDateTimeFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 ||
+ !args[0]->IsString() ||
+ !args[1]->IsObject() ||
+ !args[2]->IsObject()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, wrong parameters.")));
+ return;
+ }
+
+ v8::Isolate* isolate = args.GetIsolate();
+ v8::Local<v8::ObjectTemplate> date_format_template =
+ Utils::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object = date_format_template->NewInstance();
+ // But the handle shouldn't be empty.
+ // That can happen if there was a stack overflow when creating the object.
+ if (local_object.IsEmpty()) {
+ args.GetReturnValue().Set(local_object);
+ return;
+ }
+
+ // Set date time formatter as internal field of the resulting JS object.
+ icu::SimpleDateFormat* date_format = InitializeDateTimeFormat(
+ args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
+
+ if (!date_format) {
+ v8::ThrowException(v8::Exception::Error(v8::String::New(
+ "Internal error. Couldn't create ICU date time formatter.")));
+ return;
+ } else {
+ local_object->SetAlignedPointerInInternalField(0, date_format);
+
+ v8::TryCatch try_catch;
+ local_object->Set(v8::String::New("dateFormat"), v8::String::New("valid"));
+ if (try_catch.HasCaught()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, couldn't set property.")));
+ return;
+ }
+ }
+
+ v8::Persistent<v8::Object> wrapper(isolate, local_object);
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DeleteDateFormat);
+ args.GetReturnValue().Set(wrapper);
+ wrapper.ClearAndLeak();
+}
+
+static icu::SimpleDateFormat* InitializeDateTimeFormat(
+ v8::Handle<v8::String> locale,
+ v8::Handle<v8::Object> options,
+ v8::Handle<v8::Object> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::AsciiValue bcp47_locale(locale);
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::SimpleDateFormat* date_format = CreateICUDateFormat(icu_locale, options);
+ if (!date_format) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ date_format = CreateICUDateFormat(no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system, calendar).
+ SetResolvedSettings(no_extension_locale, date_format, resolved);
+ } else {
+ SetResolvedSettings(icu_locale, date_format, resolved);
+ }
+
+ return date_format;
+}
+
+static icu::SimpleDateFormat* CreateICUDateFormat(
+ const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
+ // Create time zone as specified by the user. We have to re-create time zone
+ // since calendar takes ownership.
+ icu::TimeZone* tz = NULL;
+ icu::UnicodeString timezone;
+ if (Utils::ExtractStringSetting(options, "timeZone", &timezone)) {
+ tz = icu::TimeZone::createTimeZone(timezone);
+ } else {
+ tz = icu::TimeZone::createDefault();
+ }
+
+ // Create a calendar using locale, and apply time zone to it.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Calendar* calendar =
+ icu::Calendar::createInstance(tz, icu_locale, status);
+
+ // Make formatter from skeleton. Calendar and numbering system are added
+ // to the locale as Unicode extension (if they were specified at all).
+ icu::SimpleDateFormat* date_format = NULL;
+ icu::UnicodeString skeleton;
+ if (Utils::ExtractStringSetting(options, "skeleton", &skeleton)) {
+ icu::DateTimePatternGenerator* generator =
+ icu::DateTimePatternGenerator::createInstance(icu_locale, status);
+ icu::UnicodeString pattern;
+ if (U_SUCCESS(status)) {
+ pattern = generator->getBestPattern(skeleton, status);
+ delete generator;
+ }
+
+ date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
+ if (U_SUCCESS(status)) {
+ date_format->adoptCalendar(calendar);
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete calendar;
+ delete date_format;
+ date_format = NULL;
+ }
+
+ return date_format;
+}
+
+static void SetResolvedSettings(const icu::Locale& icu_locale,
+ icu::SimpleDateFormat* date_format,
+ v8::Handle<v8::Object> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+ icu::UnicodeString pattern;
+ date_format->toPattern(pattern);
+ resolved->Set(v8::String::New("pattern"),
+ v8::String::New(reinterpret_cast<const uint16_t*>(
+ pattern.getBuffer()), pattern.length()));
+
+ // Set time zone and calendar.
+ if (date_format) {
+ const icu::Calendar* calendar = date_format->getCalendar();
+ const char* calendar_name = calendar->getType();
+ resolved->Set(v8::String::New("calendar"), v8::String::New(calendar_name));
+
+ const icu::TimeZone& tz = calendar->getTimeZone();
+ icu::UnicodeString time_zone;
+ tz.getID(time_zone);
+
+ icu::UnicodeString canonical_time_zone;
+ icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
+ if (U_SUCCESS(status)) {
+ if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
+ resolved->Set(v8::String::New("timeZone"), v8::String::New("UTC"));
+ } else {
+ resolved->Set(v8::String::New("timeZone"),
+ v8::String::New(reinterpret_cast<const uint16_t*>(
+ canonical_time_zone.getBuffer()),
+ canonical_time_zone.length()));
+ }
+ }
+ }
+
+ // Ugly hack. ICU doesn't expose numbering system in any way, so we have
+ // to assume that for given locale NumberingSystem constructor produces the
+ // same digits as NumberFormat/Calendar would.
+ status = U_ZERO_ERROR;
+ icu::NumberingSystem* numbering_system =
+ icu::NumberingSystem::createInstance(icu_locale, status);
+ if (U_SUCCESS(status)) {
+ const char* ns = numbering_system->getName();
+ resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns));
+ } else {
+ resolved->Set(v8::String::New("numberingSystem"), v8::Undefined());
+ }
+ delete numbering_system;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ resolved->Set(v8::String::New("locale"), v8::String::New(result));
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ resolved->Set(v8::String::New("locale"), v8::String::New("und"));
+ }
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/date-format.h b/src/extensions/i18n/date-format.h
new file mode 100644
index 0000000..daa5964
--- /dev/null
+++ b/src/extensions/i18n/date-format.h
@@ -0,0 +1,71 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_DATE_FORMAT_H_
+#define V8_EXTENSIONS_I18N_DATE_FORMAT_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class SimpleDateFormat;
+}
+
+namespace v8_i18n {
+
+class DateFormat {
+ public:
+ static void JSCreateDateTimeFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks date format object from corresponding JavaScript object.
+ static icu::SimpleDateFormat* UnpackDateFormat(
+ v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the DateFormat once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteDateFormat(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param);
+
+ // Formats date and returns corresponding string.
+ static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Parses date and returns corresponding Date object or undefined if parse
+ // failed.
+ static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ DateFormat();
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_DATE_FORMAT_H_
diff --git a/src/extensions/i18n/date-format.js b/src/extensions/i18n/date-format.js
new file mode 100644
index 0000000..04e7a7c
--- /dev/null
+++ b/src/extensions/i18n/date-format.js
@@ -0,0 +1,478 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Returns a string that matches LDML representation of the options object.
+ */
+function toLDMLString(options) {
+ var getOption = getGetOption(options, 'dateformat');
+
+ var ldmlString = '';
+
+ var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
+
+ option = getOption('era', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
+
+ option = getOption('year', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
+
+ option = getOption('month', 'string',
+ ['2-digit', 'numeric', 'narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
+ 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
+
+ option = getOption('day', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(
+ option, {'2-digit': 'dd', 'numeric': 'd'});
+
+ var hr12 = getOption('hour12', 'boolean');
+ option = getOption('hour', 'string', ['2-digit', 'numeric']);
+ if (hr12 === undefined) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
+ } else if (hr12 === true) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
+ } else {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
+ }
+
+ option = getOption('minute', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
+
+ option = getOption('second', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
+
+ option = getOption('timeZoneName', 'string', ['short', 'long']);
+ ldmlString += appendToLDMLString(option, {short: 'v', long: 'vv'});
+
+ return ldmlString;
+}
+
+
+/**
+ * Returns either LDML equivalent of the current option or empty string.
+ */
+function appendToLDMLString(option, pairs) {
+ if (option !== undefined) {
+ return pairs[option];
+ } else {
+ return '';
+ }
+}
+
+
+/**
+ * Returns object that matches LDML representation of the date.
+ */
+function fromLDMLString(ldmlString) {
+ // First remove '' quoted text, so we lose 'Uhr' strings.
+ ldmlString = ldmlString.replace(QUOTED_STRING_RE, '');
+
+ var options = {};
+ var match = ldmlString.match(/E{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
+
+ match = ldmlString.match(/G{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
+
+ match = ldmlString.match(/y{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'year', match, {y: 'numeric', yy: '2-digit'});
+
+ match = ldmlString.match(/M{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
+ M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
+
+ // Sometimes we get L instead of M for month - standalone name.
+ match = ldmlString.match(/L{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
+ L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
+
+ match = ldmlString.match(/d{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'day', match, {d: 'numeric', dd: '2-digit'});
+
+ match = ldmlString.match(/h{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = true;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {h: 'numeric', hh: '2-digit'});
+
+ match = ldmlString.match(/H{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = false;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {H: 'numeric', HH: '2-digit'});
+
+ match = ldmlString.match(/m{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'minute', match, {m: 'numeric', mm: '2-digit'});
+
+ match = ldmlString.match(/s{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'second', match, {s: 'numeric', ss: '2-digit'});
+
+ match = ldmlString.match(/v{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'timeZoneName', match, {v: 'short', vv: 'long'});
+
+ return options;
+}
+
+
+function appendToDateTimeObject(options, option, match, pairs) {
+ if (match === null) {
+ if (!options.hasOwnProperty(option)) {
+ defineWEProperty(options, option, undefined);
+ }
+ return options;
+ }
+
+ var property = match[0];
+ defineWEProperty(options, option, pairs[property]);
+
+ return options;
+}
+
+
+/**
+ * Returns options with at least default values in it.
+ */
+function toDateTimeOptions(options, required, defaults) {
+ if (options === undefined) {
+ options = null;
+ } else {
+ options = toObject(options);
+ }
+
+ options = Object.apply(this, [options]);
+
+ var needsDefault = true;
+ if ((required === 'date' || required === 'any') &&
+ (options.weekday !== undefined || options.year !== undefined ||
+ options.month !== undefined || options.day !== undefined)) {
+ needsDefault = false;
+ }
+
+ if ((required === 'time' || required === 'any') &&
+ (options.hour !== undefined || options.minute !== undefined ||
+ options.second !== undefined)) {
+ needsDefault = false;
+ }
+
+ if (needsDefault && (defaults === 'date' || defaults === 'all')) {
+ Object.defineProperty(options, 'year', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'month', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'day', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ if (needsDefault && (defaults === 'time' || defaults === 'all')) {
+ Object.defineProperty(options, 'hour', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'minute', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'second', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ return options;
+}
+
+
+/**
+ * Initializes the given object so it's a valid DateTimeFormat instance.
+ * Useful for subclassing.
+ */
+function initializeDateTimeFormat(dateFormat, locales, options) {
+ native function NativeJSCreateDateTimeFormat();
+
+ if (dateFormat.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize DateTimeFormat object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var locale = resolveLocale('dateformat', locales, options);
+
+ options = toDateTimeOptions(options, 'any', 'date');
+
+ var getOption = getGetOption(options, 'dateformat');
+
+ // We implement only best fit algorithm, but still need to check
+ // if the formatMatcher values are in range.
+ var matcher = getOption('formatMatcher', 'string',
+ ['basic', 'best fit'], 'best fit');
+
+ // Build LDML string for the skeleton that we pass to the formatter.
+ var ldmlString = toLDMLString(options);
+
+ // Filter out supported extension keys so we know what to put in resolved
+ // section later on.
+ // We need to pass calendar and number system to the method.
+ var tz = canonicalizeTimeZoneID(options.timeZone);
+
+ // ICU prefers options to be passed using -u- extension key/values, so
+ // we need to build that.
+ var internalOptions = {};
+ var extensionMap = parseExtension(locale.extension);
+ var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = Object.defineProperties({}, {
+ calendar: {writable: true},
+ day: {writable: true},
+ era: {writable: true},
+ hour12: {writable: true},
+ hour: {writable: true},
+ locale: {writable: true},
+ minute: {writable: true},
+ month: {writable: true},
+ numberingSystem: {writable: true},
+ pattern: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ second: {writable: true},
+ timeZone: {writable: true},
+ timeZoneName: {writable: true},
+ tz: {value: tz, writable: true},
+ weekday: {writable: true},
+ year: {writable: true}
+ });
+
+ var formatter = NativeJSCreateDateTimeFormat(
+ requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
+
+ if (tz !== undefined && tz !== resolved.timeZone) {
+ throw new RangeError('Unsupported time zone specified ' + tz);
+ }
+
+ Object.defineProperty(dateFormat, 'formatter', {value: formatter});
+ Object.defineProperty(dateFormat, 'resolved', {value: resolved});
+ Object.defineProperty(dateFormat, '__initializedIntlObject',
+ {value: 'dateformat'});
+
+ return dateFormat;
+}
+
+
+/**
+ * Constructs Intl.DateTimeFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'DateTimeFormat', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.DateTimeFormat(locales, options);
+ }
+
+ return initializeDateTimeFormat(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * DateTimeFormat resolvedOptions method.
+ */
+%SetProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'dateformat') {
+ throw new TypeError('resolvedOptions method called on a non-object or ' +
+ 'on a object that is not Intl.DateTimeFormat.');
+ }
+
+ var format = this;
+ var fromPattern = fromLDMLString(format.resolved.pattern);
+ var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar];
+ if (userCalendar === undefined) {
+ // Use ICU name if we don't have a match. It shouldn't happen, but
+ // it would be too strict to throw for this.
+ userCalendar = format.resolved.calendar;
+ }
+
+ var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
+ format.resolved.locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format.resolved.numberingSystem,
+ calendar: userCalendar,
+ timeZone: format.resolved.timeZone
+ };
+
+ addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
+ addWECPropertyIfDefined(result, 'era', fromPattern.era);
+ addWECPropertyIfDefined(result, 'year', fromPattern.year);
+ addWECPropertyIfDefined(result, 'month', fromPattern.month);
+ addWECPropertyIfDefined(result, 'day', fromPattern.day);
+ addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
+ addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
+ addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
+ addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
+ addWECPropertyIfDefined(result, 'second', fromPattern.second);
+
+ return result;
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('dateformat', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(date)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ */
+function formatDate(formatter, dateValue) {
+ native function NativeJSInternalDateFormat();
+
+ var dateMs;
+ if (dateValue === undefined) {
+ dateMs = Date.now();
+ } else {
+ dateMs = Number(dateValue);
+ }
+
+ if (!isFinite(dateMs)) {
+ throw new RangeError('Provided date is not in valid range.');
+ }
+
+ return NativeJSInternalDateFormat(formatter.formatter, new Date(dateMs));
+}
+
+
+/**
+ * Returns a Date object representing the result of calling ToString(value)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ * Returns undefined if date string cannot be parsed.
+ */
+function parseDate(formatter, value) {
+ native function NativeJSInternalDateParse();
+ return NativeJSInternalDateParse(formatter.formatter, String(value));
+}
+
+
+// 0 because date is optional argument.
+addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
+addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
+
+
+/**
+ * Returns canonical Area/Location name, or throws an exception if the zone
+ * name is invalid IANA name.
+ */
+function canonicalizeTimeZoneID(tzID) {
+ // Skip undefined zones.
+ if (tzID === undefined) {
+ return tzID;
+ }
+
+ // Special case handling (UTC, GMT).
+ var upperID = tzID.toUpperCase();
+ if (upperID === 'UTC' || upperID === 'GMT' ||
+ upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
+ return 'UTC';
+ }
+
+ // We expect only _ and / beside ASCII letters.
+ // All inputs should conform to Area/Location from now on.
+ var match = TIMEZONE_NAME_CHECK_RE.exec(tzID);
+ if (match === null) {
+ throw new RangeError('Expected Area/Location for time zone, got ' + tzID);
+ }
+
+ var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]);
+ var i = 3;
+ while (match[i] !== undefined && i < match.length) {
+ result = result + '_' + toTitleCaseWord(match[i]);
+ i++;
+ }
+
+ return result;
+}
diff --git a/src/extensions/i18n/footer.js b/src/extensions/i18n/footer.js
new file mode 100644
index 0000000..ac33f1e
--- /dev/null
+++ b/src/extensions/i18n/footer.js
@@ -0,0 +1,40 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+// Fix RegExp global state so we don't fail WebKit layout test:
+// fast/js/regexp-caching.html
+// It seems that 'g' or test() operations leave state changed.
+var CLEANUP_RE = new RegExp('');
+CLEANUP_RE.test('');
+
+return Intl;
+}());
diff --git a/src/extensions/i18n/globals.js b/src/extensions/i18n/globals.js
new file mode 100644
index 0000000..68fabe7
--- /dev/null
+++ b/src/extensions/i18n/globals.js
@@ -0,0 +1,168 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+
+/**
+ * List of available services.
+ */
+var AVAILABLE_SERVICES = ['collator',
+ 'numberformat',
+ 'dateformat',
+ 'breakiterator'];
+
+/**
+ * Caches available locales for each service.
+ */
+var AVAILABLE_LOCALES = {
+ 'collator': undefined,
+ 'numberformat': undefined,
+ 'dateformat': undefined,
+ 'breakiterator': undefined
+};
+
+/**
+ * Caches default ICU locale.
+ */
+var DEFAULT_ICU_LOCALE = undefined;
+
+/**
+ * Unicode extension regular expression.
+ */
+var UNICODE_EXTENSION_RE = new RegExp('-u(-[a-z0-9]{2,8})+', 'g');
+
+/**
+ * Matches any Unicode extension.
+ */
+var ANY_EXTENSION_RE = new RegExp('-[a-z0-9]{1}-.*', 'g');
+
+/**
+ * Replace quoted text (single quote, anything but the quote and quote again).
+ */
+var QUOTED_STRING_RE = new RegExp("'[^']+'", 'g');
+
+/**
+ * Matches valid service name.
+ */
+var SERVICE_RE =
+ new RegExp('^(collator|numberformat|dateformat|breakiterator)$');
+
+/**
+ * Validates a language tag against bcp47 spec.
+ * Actual value is assigned on first run.
+ */
+var LANGUAGE_TAG_RE = undefined;
+
+/**
+ * Helps find duplicate variants in the language tag.
+ */
+var LANGUAGE_VARIANT_RE = undefined;
+
+/**
+ * Helps find duplicate singletons in the language tag.
+ */
+var LANGUAGE_SINGLETON_RE = undefined;
+
+/**
+ * Matches valid IANA time zone names.
+ */
+var TIMEZONE_NAME_CHECK_RE =
+ new RegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$');
+
+/**
+ * Maps ICU calendar names into LDML type.
+ */
+var ICU_CALENDAR_MAP = {
+ 'gregorian': 'gregory',
+ 'japanese': 'japanese',
+ 'buddhist': 'buddhist',
+ 'roc': 'roc',
+ 'persian': 'persian',
+ 'islamic-civil': 'islamicc',
+ 'islamic': 'islamic',
+ 'hebrew': 'hebrew',
+ 'chinese': 'chinese',
+ 'indian': 'indian',
+ 'coptic': 'coptic',
+ 'ethiopic': 'ethiopic',
+ 'ethiopic-amete-alem': 'ethioaa'
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a collator.
+ */
+var COLLATOR_KEY_MAP = {
+ 'kn': {'property': 'numeric', 'type': 'boolean'},
+ 'kf': {'property': 'caseFirst', 'type': 'string',
+ 'values': ['false', 'lower', 'upper']}
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a number format.
+ */
+var NUMBER_FORMAT_KEY_MAP = {
+ 'nu': {'property': undefined, 'type': 'string'}
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a date/time format.
+ */
+var DATETIME_FORMAT_KEY_MAP = {
+ 'ca': {'property': undefined, 'type': 'string'},
+ 'nu': {'property': undefined, 'type': 'string'}
+};
+
+/**
+ * Allowed -u-co- values. List taken from:
+ * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
+ */
+var ALLOWED_CO_VALUES = [
+ 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
+ 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
+];
+
+/**
+ * Object attributes (configurable, writable, enumerable).
+ * To combine attributes, OR them.
+ * Values/names are copied from v8/include/v8.h:PropertyAttribute
+ */
+var ATTRIBUTES = {
+ 'NONE': 0,
+ 'READ_ONLY': 1,
+ 'DONT_ENUM': 2,
+ 'DONT_DELETE': 4
+};
+
+/**
+ * Error message for when function object is created with new and it's not
+ * a constructor.
+ */
+var ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR =
+ 'Function object that\'s not a constructor was created with new';
diff --git a/src/extensions/i18n/header.js b/src/extensions/i18n/header.js
new file mode 100644
index 0000000..1c0a2d8
--- /dev/null
+++ b/src/extensions/i18n/header.js
@@ -0,0 +1,41 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Intl object is a single object that has some named properties,
+ * all of which are constructors.
+ */
+var Intl = (function() {
+
+'use strict';
+
+var Intl = {};
diff --git a/src/extensions/i18n/i18n-extension.cc b/src/extensions/i18n/i18n-extension.cc
new file mode 100644
index 0000000..eb7652e
--- /dev/null
+++ b/src/extensions/i18n/i18n-extension.cc
@@ -0,0 +1,116 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "i18n-extension.h"
+
+#include "break-iterator.h"
+#include "collator.h"
+#include "date-format.h"
+#include "locale.h"
+#include "natives.h"
+#include "number-format.h"
+
+using v8::internal::I18NNatives;
+
+namespace v8_i18n {
+
+Extension::Extension()
+ : v8::Extension("v8/i18n",
+ reinterpret_cast<const char*>(
+ I18NNatives::GetScriptsSource().start()),
+ 0,
+ 0,
+ I18NNatives::GetScriptsSource().length()) {}
+
+v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
+ v8::Handle<v8::String> name) {
+ // Standalone, helper methods.
+ if (name->Equals(v8::String::New("NativeJSCanonicalizeLanguageTag"))) {
+ return v8::FunctionTemplate::New(JSCanonicalizeLanguageTag);
+ } else if (name->Equals(v8::String::New("NativeJSAvailableLocalesOf"))) {
+ return v8::FunctionTemplate::New(JSAvailableLocalesOf);
+ } else if (name->Equals(v8::String::New("NativeJSGetDefaultICULocale"))) {
+ return v8::FunctionTemplate::New(JSGetDefaultICULocale);
+ } else if (name->Equals(v8::String::New("NativeJSGetLanguageTagVariants"))) {
+ return v8::FunctionTemplate::New(JSGetLanguageTagVariants);
+ }
+
+ // Date format and parse.
+ if (name->Equals(v8::String::New("NativeJSCreateDateTimeFormat"))) {
+ return v8::FunctionTemplate::New(DateFormat::JSCreateDateTimeFormat);
+ } else if (name->Equals(v8::String::New("NativeJSInternalDateFormat"))) {
+ return v8::FunctionTemplate::New(DateFormat::JSInternalFormat);
+ } else if (name->Equals(v8::String::New("NativeJSInternalDateParse"))) {
+ return v8::FunctionTemplate::New(DateFormat::JSInternalParse);
+ }
+
+ // Number format and parse.
+ if (name->Equals(v8::String::New("NativeJSCreateNumberFormat"))) {
+ return v8::FunctionTemplate::New(NumberFormat::JSCreateNumberFormat);
+ } else if (name->Equals(v8::String::New("NativeJSInternalNumberFormat"))) {
+ return v8::FunctionTemplate::New(NumberFormat::JSInternalFormat);
+ } else if (name->Equals(v8::String::New("NativeJSInternalNumberParse"))) {
+ return v8::FunctionTemplate::New(NumberFormat::JSInternalParse);
+ }
+
+ // Collator.
+ if (name->Equals(v8::String::New("NativeJSCreateCollator"))) {
+ return v8::FunctionTemplate::New(Collator::JSCreateCollator);
+ } else if (name->Equals(v8::String::New("NativeJSInternalCompare"))) {
+ return v8::FunctionTemplate::New(Collator::JSInternalCompare);
+ }
+
+ // Break iterator.
+ if (name->Equals(v8::String::New("NativeJSCreateBreakIterator"))) {
+ return v8::FunctionTemplate::New(BreakIterator::JSCreateBreakIterator);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorAdoptText"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorAdoptText);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorFirst"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorFirst);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorNext"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorNext);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorCurrent"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorCurrent);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorBreakType"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorBreakType);
+ }
+
+ return v8::Handle<v8::FunctionTemplate>();
+}
+
+void Extension::Register() {
+ static Extension i18n_extension;
+ static v8::DeclareExtension extension_declaration(&i18n_extension);
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/i18n-extension.h b/src/extensions/i18n/i18n-extension.h
new file mode 100644
index 0000000..050c336
--- /dev/null
+++ b/src/extensions/i18n/i18n-extension.h
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
+#define V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
+
+#include "v8.h"
+
+namespace v8_i18n {
+
+class Extension : public v8::Extension {
+ public:
+ Extension();
+
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+
+ static void Register();
+
+ private:
+ static Extension* extension_;
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
diff --git a/src/extensions/i18n/i18n-utils.cc b/src/extensions/i18n/i18n-utils.cc
new file mode 100644
index 0000000..d8d3c12
--- /dev/null
+++ b/src/extensions/i18n/i18n-utils.cc
@@ -0,0 +1,174 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "i18n-utils.h"
+
+#include <string.h>
+
+#include "unicode/unistr.h"
+
+namespace v8_i18n {
+
+// static
+void Utils::StrNCopy(char* dest, int length, const char* src) {
+ if (!dest || !src) return;
+
+ strncpy(dest, src, length);
+ dest[length - 1] = '\0';
+}
+
+// static
+bool Utils::V8StringToUnicodeString(const v8::Handle<v8::Value>& input,
+ icu::UnicodeString* output) {
+ v8::String::Utf8Value utf8_value(input);
+
+ if (*utf8_value == NULL) return false;
+
+ output->setTo(icu::UnicodeString::fromUTF8(*utf8_value));
+
+ return true;
+}
+
+// static
+bool Utils::ExtractStringSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ icu::UnicodeString* result) {
+ if (!setting || !result) return false;
+
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> value = settings->Get(v8::String::New(setting));
+ if (try_catch.HasCaught()) {
+ return false;
+ }
+ // No need to check if |value| is empty because it's taken care of
+ // by TryCatch above.
+ if (!value->IsUndefined() && !value->IsNull() && value->IsString()) {
+ return V8StringToUnicodeString(value, result);
+ }
+ return false;
+}
+
+// static
+bool Utils::ExtractIntegerSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ int32_t* result) {
+ if (!setting || !result) return false;
+
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> value = settings->Get(v8::String::New(setting));
+ if (try_catch.HasCaught()) {
+ return false;
+ }
+ // No need to check if |value| is empty because it's taken care of
+ // by TryCatch above.
+ if (!value->IsUndefined() && !value->IsNull() && value->IsNumber()) {
+ *result = static_cast<int32_t>(value->Int32Value());
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Utils::ExtractBooleanSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ bool* result) {
+ if (!setting || !result) return false;
+
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> value = settings->Get(v8::String::New(setting));
+ if (try_catch.HasCaught()) {
+ return false;
+ }
+ // No need to check if |value| is empty because it's taken care of
+ // by TryCatch above.
+ if (!value->IsUndefined() && !value->IsNull() && value->IsBoolean()) {
+ *result = static_cast<bool>(value->BooleanValue());
+ return true;
+ }
+ return false;
+}
+
+// static
+void Utils::AsciiToUChar(const char* source,
+ int32_t source_length,
+ UChar* target,
+ int32_t target_length) {
+ int32_t length =
+ source_length < target_length ? source_length : target_length;
+
+ if (length <= 0) {
+ return;
+ }
+
+ for (int32_t i = 0; i < length - 1; ++i) {
+ target[i] = static_cast<UChar>(source[i]);
+ }
+
+ target[length - 1] = 0x0u;
+}
+
+// static
+// Chrome Linux doesn't like static initializers in class, so we create
+// template on demand.
+v8::Local<v8::ObjectTemplate> Utils::GetTemplate(v8::Isolate* isolate) {
+ static v8::Persistent<v8::ObjectTemplate> icu_template;
+
+ if (icu_template.IsEmpty()) {
+ v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New());
+
+ // Set aside internal field for ICU class.
+ raw_template->SetInternalFieldCount(1);
+
+ icu_template.Reset(isolate, raw_template);
+ }
+
+ return v8::Local<v8::ObjectTemplate>::New(isolate, icu_template);
+}
+
+// static
+// Chrome Linux doesn't like static initializers in class, so we create
+// template on demand. This one has 2 internal fields.
+v8::Local<v8::ObjectTemplate> Utils::GetTemplate2(v8::Isolate* isolate) {
+ static v8::Persistent<v8::ObjectTemplate> icu_template_2;
+
+ if (icu_template_2.IsEmpty()) {
+ v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New());
+
+ // Set aside internal field for ICU class and additional data.
+ raw_template->SetInternalFieldCount(2);
+
+ icu_template_2.Reset(isolate, raw_template);
+ }
+
+ return v8::Local<v8::ObjectTemplate>::New(isolate, icu_template_2);
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/i18n-utils.h b/src/extensions/i18n/i18n-utils.h
new file mode 100644
index 0000000..db5d1b6
--- /dev/null
+++ b/src/extensions/i18n/i18n-utils.h
@@ -0,0 +1,91 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_SRC_UTILS_H_
+#define V8_EXTENSIONS_I18N_SRC_UTILS_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class UnicodeString;
+}
+
+namespace v8_i18n {
+
+class Utils {
+ public:
+ // Safe string copy. Null terminates the destination. Copies at most
+ // (length - 1) bytes.
+ // We can't use snprintf since it's not supported on all relevant platforms.
+ // We can't use OS::SNPrintF, it's only for internal code.
+ static void StrNCopy(char* dest, int length, const char* src);
+
+ // Converts v8::String into UnicodeString. Returns false if input
+ // can't be converted into utf8.
+ static bool V8StringToUnicodeString(const v8::Handle<v8::Value>& input,
+ icu::UnicodeString* output);
+
+ // Extract a String setting named in |settings| and set it to |result|.
+ // Return true if it's specified. Otherwise, return false.
+ static bool ExtractStringSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ icu::UnicodeString* result);
+
+ // Extract a Integer setting named in |settings| and set it to |result|.
+ // Return true if it's specified. Otherwise, return false.
+ static bool ExtractIntegerSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ int32_t* result);
+
+ // Extract a Boolean setting named in |settings| and set it to |result|.
+ // Return true if it's specified. Otherwise, return false.
+ static bool ExtractBooleanSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ bool* result);
+
+ // Converts ASCII array into UChar array.
+ // Target is always \0 terminated.
+ static void AsciiToUChar(const char* source,
+ int32_t source_length,
+ UChar* target,
+ int32_t target_length);
+
+ // Creates an ObjectTemplate with one internal field.
+ static v8::Local<v8::ObjectTemplate> GetTemplate(v8::Isolate* isolate);
+
+ // Creates an ObjectTemplate with two internal fields.
+ static v8::Local<v8::ObjectTemplate> GetTemplate2(v8::Isolate* isolate);
+
+ private:
+ Utils() {}
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_UTILS_H_
diff --git a/src/extensions/i18n/i18n-utils.js b/src/extensions/i18n/i18n-utils.js
new file mode 100644
index 0000000..d7e9486
--- /dev/null
+++ b/src/extensions/i18n/i18n-utils.js
@@ -0,0 +1,541 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Adds bound method to the prototype of the given object.
+ */
+function addBoundMethod(obj, methodName, implementation, length) {
+ function getter() {
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject === undefined) {
+ throw new TypeError('Method ' + methodName + ' called on a ' +
+ 'non-object or on a wrong type of object.');
+ }
+ var internalName = '__bound' + methodName + '__';
+ if (this[internalName] === undefined) {
+ var that = this;
+ var boundMethod;
+ if (length === undefined || length === 2) {
+ boundMethod = function(x, y) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ return implementation(that, x, y);
+ }
+ } else if (length === 1) {
+ boundMethod = function(x) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ return implementation(that, x);
+ }
+ } else {
+ boundMethod = function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ // DateTimeFormat.format needs to be 0 arg method, but can stil
+ // receive optional dateValue param. If one was provided, pass it
+ // along.
+ if (arguments.length > 0) {
+ return implementation(that, arguments[0]);
+ } else {
+ return implementation(that);
+ }
+ }
+ }
+ %FunctionSetName(boundMethod, internalName);
+ %FunctionRemovePrototype(boundMethod);
+ %SetNativeFlag(boundMethod);
+ this[internalName] = boundMethod;
+ }
+ return this[internalName];
+ }
+
+ %FunctionSetName(getter, methodName);
+ %FunctionRemovePrototype(getter);
+ %SetNativeFlag(getter);
+
+ Object.defineProperty(obj.prototype, methodName, {
+ get: getter,
+ enumerable: false,
+ configurable: true
+ });
+}
+
+
+/**
+ * Returns an intersection of locales and service supported locales.
+ * Parameter locales is treated as a priority list.
+ */
+function supportedLocalesOf(service, locales, options) {
+ if (service.match(SERVICE_RE) === null) {
+ throw new Error('Internal error, wrong service type: ' + service);
+ }
+
+ // Provide defaults if matcher was not specified.
+ if (options === undefined) {
+ options = {};
+ } else {
+ options = toObject(options);
+ }
+
+ var matcher = options.localeMatcher;
+ if (matcher !== undefined) {
+ matcher = String(matcher);
+ if (matcher !== 'lookup' && matcher !== 'best fit') {
+ throw new RangeError('Illegal value for localeMatcher:' + matcher);
+ }
+ } else {
+ matcher = 'best fit';
+ }
+
+ var requestedLocales = initializeLocaleList(locales);
+
+ // Cache these, they don't ever change per service.
+ if (AVAILABLE_LOCALES[service] === undefined) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ // Use either best fit or lookup algorithm to match locales.
+ if (matcher === 'best fit') {
+ return initializeLocaleList(bestFitSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+ }
+
+ return initializeLocaleList(lookupSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the BCP 47 Lookup algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
+ var matchedLocales = [];
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove -u- extension.
+ var locale = requestedLocales[i].replace(UNICODE_EXTENSION_RE, '');
+ do {
+ if (availableLocales[locale] !== undefined) {
+ // Push requested locale not the resolved one.
+ matchedLocales.push(requestedLocales[i]);
+ break;
+ }
+ // Truncate locale if possible, if not break.
+ var pos = locale.lastIndexOf('-');
+ if (pos === -1) {
+ break;
+ }
+ locale = locale.substring(0, pos);
+ } while (true);
+ }
+
+ return matchedLocales;
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the implementation
+ * dependent algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupSupportedLocalesOf(requestedLocales, availableLocales);
+}
+
+
+/**
+ * Returns a getOption function that extracts property value for given
+ * options object. If property is missing it returns defaultValue. If value
+ * is out of range for that property it throws RangeError.
+ */
+function getGetOption(options, caller) {
+ if (options === undefined) {
+ throw new Error('Internal ' + caller + ' error. ' +
+ 'Default options are missing.');
+ }
+
+ var getOption = function getOption(property, type, values, defaultValue) {
+ if (options[property] !== undefined) {
+ var value = options[property];
+ switch (type) {
+ case 'boolean':
+ value = Boolean(value);
+ break;
+ case 'string':
+ value = String(value);
+ break;
+ case 'number':
+ value = Number(value);
+ break;
+ default:
+ throw new Error('Internal error. Wrong value type.');
+ }
+ if (values !== undefined && values.indexOf(value) === -1) {
+ throw new RangeError('Value ' + value + ' out of range for ' + caller +
+ ' options property ' + property);
+ }
+
+ return value;
+ }
+
+ return defaultValue;
+ }
+
+ return getOption;
+}
+
+
+/**
+ * Compares a BCP 47 language priority list requestedLocales against the locales
+ * in availableLocales and determines the best available language to meet the
+ * request. Two algorithms are available to match the locales: the Lookup
+ * algorithm described in RFC 4647 section 3.4, and an implementation dependent
+ * best-fit algorithm. Independent of the locale matching algorithm, options
+ * specified through Unicode locale extension sequences are negotiated
+ * separately, taking the caller's relevant extension keys and locale data as
+ * well as client-provided options into consideration. Returns an object with
+ * a locale property whose value is the language tag of the selected locale,
+ * and properties for each key in relevantExtensionKeys providing the selected
+ * value for that key.
+ */
+function resolveLocale(service, requestedLocales, options) {
+ requestedLocales = initializeLocaleList(requestedLocales);
+
+ var getOption = getGetOption(options, service);
+ var matcher = getOption('localeMatcher', 'string',
+ ['lookup', 'best fit'], 'best fit');
+ var resolved;
+ if (matcher === 'lookup') {
+ resolved = lookupMatcher(service, requestedLocales);
+ } else {
+ resolved = bestFitMatcher(service, requestedLocales);
+ }
+
+ return resolved;
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using basic
+ * lookup algorithm.
+ */
+function lookupMatcher(service, requestedLocales) {
+ native function NativeJSGetDefaultICULocale();
+
+ if (service.match(SERVICE_RE) === null) {
+ throw new Error('Internal error, wrong service type: ' + service);
+ }
+
+ // Cache these, they don't ever change per service.
+ if (AVAILABLE_LOCALES[service] === undefined) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove all extensions.
+ var locale = requestedLocales[i].replace(ANY_EXTENSION_RE, '');
+ do {
+ if (AVAILABLE_LOCALES[service][locale] !== undefined) {
+ // Return the resolved locale and extension.
+ var extensionMatch = requestedLocales[i].match(UNICODE_EXTENSION_RE);
+ var extension = (extensionMatch === null) ? '' : extensionMatch[0];
+ return {'locale': locale, 'extension': extension, 'position': i};
+ }
+ // Truncate locale if possible.
+ var pos = locale.lastIndexOf('-');
+ if (pos === -1) {
+ break;
+ }
+ locale = locale.substring(0, pos);
+ } while (true);
+ }
+
+ // Didn't find a match, return default.
+ if (DEFAULT_ICU_LOCALE === undefined) {
+ DEFAULT_ICU_LOCALE = NativeJSGetDefaultICULocale();
+ }
+
+ return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using
+ * implementation dependend algorithm.
+ */
+function bestFitMatcher(service, requestedLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupMatcher(service, requestedLocales);
+}
+
+
+/**
+ * Parses Unicode extension into key - value map.
+ * Returns empty object if the extension string is invalid.
+ * We are not concerned with the validity of the values at this point.
+ */
+function parseExtension(extension) {
+ var extensionSplit = extension.split('-');
+
+ // Assume ['', 'u', ...] input, but don't throw.
+ if (extensionSplit.length <= 2 ||
+ (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
+ return {};
+ }
+
+ // Key is {2}alphanum, value is {3,8}alphanum.
+ // Some keys may not have explicit values (booleans).
+ var extensionMap = {};
+ var previousKey = undefined;
+ for (var i = 2; i < extensionSplit.length; ++i) {
+ var length = extensionSplit[i].length;
+ var element = extensionSplit[i];
+ if (length === 2) {
+ extensionMap[element] = undefined;
+ previousKey = element;
+ } else if (length >= 3 && length <=8 && previousKey !== undefined) {
+ extensionMap[previousKey] = element;
+ previousKey = undefined;
+ } else {
+ // There is a value that's too long, or that doesn't have a key.
+ return {};
+ }
+ }
+
+ return extensionMap;
+}
+
+
+/**
+ * Converts parameter to an Object if possible.
+ */
+function toObject(value) {
+ if (value === undefined || value === null) {
+ throw new TypeError('Value cannot be converted to an Object.');
+ }
+
+ return Object(value);
+}
+
+
+/**
+ * Populates internalOptions object with boolean key-value pairs
+ * from extensionMap and options.
+ * Returns filtered extension (number and date format constructors use
+ * Unicode extensions for passing parameters to ICU).
+ * It's used for extension-option pairs only, e.g. kn-normalization, but not
+ * for 'sensitivity' since it doesn't have extension equivalent.
+ * Extensions like nu and ca don't have options equivalent, so we place
+ * undefined in the map.property to denote that.
+ */
+function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
+ var extension = '';
+
+ var updateExtension = function updateExtension(key, value) {
+ return '-' + key + '-' + String(value);
+ }
+
+ var updateProperty = function updateProperty(property, type, value) {
+ if (type === 'boolean' && (typeof value === 'string')) {
+ value = (value === 'true') ? true : false;
+ }
+
+ if (property !== undefined) {
+ defineWEProperty(outOptions, property, value);
+ }
+ }
+
+ for (var key in keyValues) {
+ if (keyValues.hasOwnProperty(key)) {
+ var value = undefined;
+ var map = keyValues[key];
+ if (map.property !== undefined) {
+ // This may return true if user specifies numeric: 'false', since
+ // Boolean('nonempty') === true.
+ value = getOption(map.property, map.type, map.values);
+ }
+ if (value !== undefined) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ continue;
+ }
+ // User options didn't have it, check Unicode extension.
+ // Here we want to convert strings 'true', 'false' into proper Boolean
+ // values (not a user error).
+ if (extensionMap.hasOwnProperty(key)) {
+ value = extensionMap[key];
+ if (value !== undefined) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ } else if (map.type === 'boolean') {
+ // Boolean keys are allowed not to have values in Unicode extension.
+ // Those default to true.
+ updateProperty(map.property, map.type, true);
+ extension += updateExtension(key, true);
+ }
+ }
+ }
+ }
+
+ return extension === ''? '' : '-u' + extension;
+}
+
+
+/**
+ * Converts all OwnProperties into
+ * configurable: false, writable: false, enumerable: true.
+ */
+function freezeArray(array) {
+ array.forEach(function(element, index) {
+ Object.defineProperty(array, index, {value: element,
+ configurable: false,
+ writable: false,
+ enumerable: true});
+ });
+
+ Object.defineProperty(array, 'length', {value: array.length,
+ writable: false});
+
+ return array;
+}
+
+
+/**
+ * It's sometimes desireable to leave user requested locale instead of ICU
+ * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
+ * one, if that was what user requested).
+ * This function returns user specified tag if its maximized form matches ICU
+ * resolved locale. If not we return ICU result.
+ */
+function getOptimalLanguageTag(original, resolved) {
+ // Returns Array<Object>, where each object has maximized and base properties.
+ // Maximized: zh -> zh-Hans-CN
+ // Base: zh-CN-u-ca-gregory -> zh-CN
+ native function NativeJSGetLanguageTagVariants();
+
+ // Take care of grandfathered or simple cases.
+ if (original === resolved) {
+ return original;
+ }
+
+ var locales = NativeJSGetLanguageTagVariants([original, resolved]);
+ if (locales[0].maximized !== locales[1].maximized) {
+ return resolved;
+ }
+
+ // Preserve extensions of resolved locale, but swap base tags with original.
+ var resolvedBase = new RegExp('^' + locales[1].base);
+ return resolved.replace(resolvedBase, locales[0].base);
+}
+
+
+/**
+ * Returns an Object that contains all of supported locales for a given
+ * service.
+ * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
+ * that is supported. This is required by the spec.
+ */
+function getAvailableLocalesOf(service) {
+ native function NativeJSAvailableLocalesOf();
+ var available = NativeJSAvailableLocalesOf(service);
+
+ for (var i in available) {
+ if (available.hasOwnProperty(i)) {
+ var parts = i.match(/^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
+ if (parts !== null) {
+ // Build xx-ZZ. We don't care about the actual value,
+ // as long it's not undefined.
+ available[parts[1] + '-' + parts[3]] = null;
+ }
+ }
+ }
+
+ return available;
+}
+
+
+/**
+ * Defines a property and sets writable and enumerable to true.
+ * Configurable is false by default.
+ */
+function defineWEProperty(object, property, value) {
+ Object.defineProperty(object, property,
+ {value: value, writable: true, enumerable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets configurable descriptor to false.
+ */
+function addWEPropertyIfDefined(object, property, value) {
+ if (value !== undefined) {
+ defineWEProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Defines a property and sets writable, enumerable and configurable to true.
+ */
+function defineWECProperty(object, property, value) {
+ Object.defineProperty(object, property,
+ {value: value,
+ writable: true,
+ enumerable: true,
+ configurable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets all descriptors to true.
+ */
+function addWECPropertyIfDefined(object, property, value) {
+ if (value !== undefined) {
+ defineWECProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Returns titlecased word, aMeRricA -> America.
+ */
+function toTitleCaseWord(word) {
+ return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase();
+}
diff --git a/src/extensions/i18n/locale.cc b/src/extensions/i18n/locale.cc
new file mode 100644
index 0000000..b32cc30
--- /dev/null
+++ b/src/extensions/i18n/locale.cc
@@ -0,0 +1,248 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "locale.h"
+
+#include <string.h>
+
+#include "unicode/brkiter.h"
+#include "unicode/coll.h"
+#include "unicode/datefmt.h"
+#include "unicode/numfmt.h"
+#include "unicode/uloc.h"
+#include "unicode/uversion.h"
+
+namespace v8_i18n {
+
+void JSCanonicalizeLanguageTag(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Expect locale id which is a string.
+ if (args.Length() != 1 || !args[0]->IsString()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Locale identifier, as a string, is required.")));
+ return;
+ }
+
+ UErrorCode error = U_ZERO_ERROR;
+
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+
+ // Return value which denotes invalid language tag.
+ const char* const kInvalidTag = "invalid-tag";
+
+ v8::String::AsciiValue locale_id(args[0]->ToString());
+ if (*locale_id == NULL) {
+ args.GetReturnValue().Set(v8::String::New(kInvalidTag));
+ return;
+ }
+
+ uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &error);
+ if (U_FAILURE(error) || icu_length == 0) {
+ args.GetReturnValue().Set(v8::String::New(kInvalidTag));
+ return;
+ }
+
+ char result[ULOC_FULLNAME_CAPACITY];
+
+ // Force strict BCP47 rules.
+ uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);
+
+ if (U_FAILURE(error)) {
+ args.GetReturnValue().Set(v8::String::New(kInvalidTag));
+ return;
+ }
+
+ args.GetReturnValue().Set(v8::String::New(result));
+}
+
+void JSAvailableLocalesOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Expect service name which is a string.
+ if (args.Length() != 1 || !args[0]->IsString()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Service identifier, as a string, is required.")));
+ return;
+ }
+
+ const icu::Locale* available_locales = NULL;
+
+ int32_t count = 0;
+ v8::String::AsciiValue service(args[0]->ToString());
+ if (strcmp(*service, "collator") == 0) {
+ available_locales = icu::Collator::getAvailableLocales(count);
+ } else if (strcmp(*service, "numberformat") == 0) {
+ available_locales = icu::NumberFormat::getAvailableLocales(count);
+ } else if (strcmp(*service, "dateformat") == 0) {
+ available_locales = icu::DateFormat::getAvailableLocales(count);
+ } else if (strcmp(*service, "breakiterator") == 0) {
+ available_locales = icu::BreakIterator::getAvailableLocales(count);
+ }
+
+ v8::TryCatch try_catch;
+ UErrorCode error = U_ZERO_ERROR;
+ char result[ULOC_FULLNAME_CAPACITY];
+ v8::Handle<v8::Object> locales = v8::Object::New();
+
+ for (int32_t i = 0; i < count; ++i) {
+ const char* icu_name = available_locales[i].getName();
+
+ error = U_ZERO_ERROR;
+ // No need to force strict BCP47 rules.
+ uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
+ if (U_FAILURE(error)) {
+ // This shouldn't happen, but lets not break the user.
+ continue;
+ }
+
+ // Index is just a dummy value for the property value.
+ locales->Set(v8::String::New(result), v8::Integer::New(i));
+ if (try_catch.HasCaught()) {
+ // Ignore error, but stop processing and return.
+ break;
+ }
+ }
+
+ args.GetReturnValue().Set(locales);
+}
+
+void JSGetDefaultICULocale(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::Locale default_locale;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ UErrorCode status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ default_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ args.GetReturnValue().Set(v8::String::New(result));
+ return;
+ }
+
+ args.GetReturnValue().Set(v8::String::New("und"));
+}
+
+void JSGetLanguageTagVariants(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::TryCatch try_catch;
+
+ // Expect an array of strings.
+ if (args.Length() != 1 || !args[0]->IsArray()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error. Expected Array<String>.")));
+ return;
+ }
+
+ v8::Local<v8::Array> input = v8::Local<v8::Array>::Cast(args[0]);
+ v8::Handle<v8::Array> output = v8::Array::New(input->Length());
+ for (unsigned int i = 0; i < input->Length(); ++i) {
+ v8::Local<v8::Value> locale_id = input->Get(i);
+ if (try_catch.HasCaught()) {
+ break;
+ }
+
+ if (!locale_id->IsString()) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error. Array element is missing "
+ "or it isn't a string.")));
+ return;
+ }
+
+ v8::String::AsciiValue ascii_locale_id(locale_id);
+ if (*ascii_locale_id == NULL) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error. Non-ASCII locale identifier.")));
+ return;
+ }
+
+ UErrorCode error = U_ZERO_ERROR;
+
+ // Convert from BCP47 to ICU format.
+ // de-DE-u-co-phonebk -> de_DE@collation=phonebook
+ char icu_locale[ULOC_FULLNAME_CAPACITY];
+ int icu_locale_length = 0;
+ uloc_forLanguageTag(*ascii_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY,
+ &icu_locale_length, &error);
+ if (U_FAILURE(error) || icu_locale_length == 0) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error. Failed to convert locale to ICU.")));
+ return;
+ }
+
+ // Maximize the locale.
+ // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook
+ char icu_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_addLikelySubtags(
+ icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Remove extensions from maximized locale.
+ // de_Latn_DE@collation=phonebook -> de_Latn_DE
+ char icu_base_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_getBaseName(
+ icu_max_locale, icu_base_max_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Get original name without extensions.
+ // de_DE@collation=phonebook -> de_DE
+ char icu_base_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_getBaseName(
+ icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Convert from ICU locale format to BCP47 format.
+ // de_Latn_DE -> de-Latn-DE
+ char base_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_toLanguageTag(icu_base_max_locale, base_max_locale,
+ ULOC_FULLNAME_CAPACITY, FALSE, &error);
+
+ // de_DE -> de-DE
+ char base_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_toLanguageTag(
+ icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY, FALSE, &error);
+
+ if (U_FAILURE(error)) {
+ v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Internal error. Couldn't generate maximized "
+ "or base locale.")));
+ return;
+ }
+
+ v8::Handle<v8::Object> result = v8::Object::New();
+ result->Set(v8::String::New("maximized"), v8::String::New(base_max_locale));
+ result->Set(v8::String::New("base"), v8::String::New(base_locale));
+ if (try_catch.HasCaught()) {
+ break;
+ }
+
+ output->Set(i, result);
+ if (try_catch.HasCaught()) {
+ break;
+ }
+ }
+
+ args.GetReturnValue().Set(output);
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/locale.h b/src/extensions/i18n/locale.h
new file mode 100644
index 0000000..c39568e
--- /dev/null
+++ b/src/extensions/i18n/locale.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_SRC_LOCALE_H_
+#define V8_EXTENSIONS_I18N_SRC_LOCALE_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace v8_i18n {
+
+// Canonicalizes the BCP47 language tag using BCP47 rules.
+// Returns 'invalid-tag' in case input was not well formed.
+void JSCanonicalizeLanguageTag(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+// Returns a list of available locales for collator, date or number formatter.
+void JSAvailableLocalesOf(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+// Returns default ICU locale.
+void JSGetDefaultICULocale(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+// Returns an array of objects, that have maximized and base names of inputs.
+// Unicode extensions are dropped from both.
+// Input: ['zh-TW-u-nu-thai', 'sr']
+// Output: [{maximized: 'zh-Hant-TW', base: 'zh-TW'},
+// {maximized: 'sr-Cyrl-RS', base: 'sr'}]
+void JSGetLanguageTagVariants(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_LOCALE_H_
diff --git a/src/extensions/i18n/locale.js b/src/extensions/i18n/locale.js
new file mode 100644
index 0000000..ea95b87
--- /dev/null
+++ b/src/extensions/i18n/locale.js
@@ -0,0 +1,192 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Canonicalizes the language tag, or throws in case the tag is invalid.
+ */
+function canonicalizeLanguageTag(localeID) {
+ native function NativeJSCanonicalizeLanguageTag();
+
+ // null is typeof 'object' so we have to do extra check.
+ if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
+ localeID === null) {
+ throw new TypeError('Language ID should be string or object.');
+ }
+
+ var localeString = String(localeID);
+
+ if (isValidLanguageTag(localeString) === false) {
+ throw new RangeError('Invalid language tag: ' + localeString);
+ }
+
+ // This call will strip -kn but not -kn-true extensions.
+ // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265.
+ // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after
+ // upgrade to ICU 4.9.
+ var tag = NativeJSCanonicalizeLanguageTag(localeString);
+ if (tag === 'invalid-tag') {
+ throw new RangeError('Invalid language tag: ' + localeString);
+ }
+
+ return tag;
+}
+
+
+/**
+ * Returns an array where all locales are canonicalized and duplicates removed.
+ * Throws on locales that are not well formed BCP47 tags.
+ */
+function initializeLocaleList(locales) {
+ var seen = [];
+ if (locales === undefined) {
+ // Constructor is called without arguments.
+ seen = [];
+ } else {
+ // We allow single string localeID.
+ if (typeof locales === 'string') {
+ seen.push(canonicalizeLanguageTag(locales));
+ return freezeArray(seen);
+ }
+
+ var o = toObject(locales);
+ // Converts it to UInt32 (>>> is shr on 32bit integers).
+ var len = o.length >>> 0;
+
+ for (var k = 0; k < len; k++) {
+ if (k in o) {
+ var value = o[k];
+
+ var tag = canonicalizeLanguageTag(value);
+
+ if (seen.indexOf(tag) === -1) {
+ seen.push(tag);
+ }
+ }
+ }
+ }
+
+ return freezeArray(seen);
+}
+
+
+/**
+ * Validates the language tag. Section 2.2.9 of the bcp47 spec
+ * defines a valid tag.
+ *
+ * ICU is too permissible and lets invalid tags, like
+ * hant-cmn-cn, through.
+ *
+ * Returns false if the language tag is invalid.
+ */
+function isValidLanguageTag(locale) {
+ // Check if it's well-formed, including grandfadered tags.
+ if (LANGUAGE_TAG_RE.test(locale) === false) {
+ return false;
+ }
+
+ // Just return if it's a x- form. It's all private.
+ if (locale.indexOf('x-') === 0) {
+ return true;
+ }
+
+ // Check if there are any duplicate variants or singletons (extensions).
+
+ // Remove private use section.
+ locale = locale.split(/-x-/)[0];
+
+ // Skip language since it can match variant regex, so we start from 1.
+ // We are matching i-klingon here, but that's ok, since i-klingon-klingon
+ // is not valid and would fail LANGUAGE_TAG_RE test.
+ var variants = [];
+ var extensions = [];
+ var parts = locale.split(/-/);
+ for (var i = 1; i < parts.length; i++) {
+ var value = parts[i];
+ if (LANGUAGE_VARIANT_RE.test(value) === true && extensions.length === 0) {
+ if (variants.indexOf(value) === -1) {
+ variants.push(value);
+ } else {
+ return false;
+ }
+ }
+
+ if (LANGUAGE_SINGLETON_RE.test(value) === true) {
+ if (extensions.indexOf(value) === -1) {
+ extensions.push(value);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+/**
+ * Builds a regular expresion that validates the language tag
+ * against bcp47 spec.
+ * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
+ * Runs on load and initializes the global REs.
+ */
+(function() {
+ var alpha = '[a-zA-Z]';
+ var digit = '[0-9]';
+ var alphanum = '(' + alpha + '|' + digit + ')';
+ var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
+ 'zh-min|zh-min-nan|zh-xiang)';
+ var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
+ 'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
+ 'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
+ var grandfathered = '(' + irregular + '|' + regular + ')';
+ var privateUse = '(x(-' + alphanum + '{1,8})+)';
+
+ var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
+ LANGUAGE_SINGLETON_RE = new RegExp('^' + singleton + '$', 'i');
+
+ var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
+
+ var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
+ LANGUAGE_VARIANT_RE = new RegExp('^' + variant + '$', 'i');
+
+ var region = '(' + alpha + '{2}|' + digit + '{3})';
+ var script = '(' + alpha + '{4})';
+ var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
+ var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
+ alpha + '{5,8})';
+ var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
+ variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
+
+ var languageTag =
+ '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
+ LANGUAGE_TAG_RE = new RegExp(languageTag, 'i');
+})();
diff --git a/src/extensions/i18n/number-format.cc b/src/extensions/i18n/number-format.cc
new file mode 100644
index 0000000..2240b08
--- /dev/null
+++ b/src/extensions/i18n/number-format.cc
@@ -0,0 +1,418 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#include "number-format.h"
+
+#include <string.h>
+
+#include "i18n-utils.h"
+#include "unicode/curramt.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.h"
+#include "unicode/locid.h"
+#include "unicode/numfmt.h"
+#include "unicode/numsys.h"
+#include "unicode/uchar.h"
+#include "unicode/ucurr.h"
+#include "unicode/unum.h"
+#include "unicode/uversion.h"
+
+namespace v8_i18n {
+
+static icu::DecimalFormat* InitializeNumberFormat(v8::Handle<v8::String>,
+ v8::Handle<v8::Object>,
+ v8::Handle<v8::Object>);
+static icu::DecimalFormat* CreateICUNumberFormat(const icu::Locale&,
+ v8::Handle<v8::Object>);
+static void SetResolvedSettings(const icu::Locale&,
+ icu::DecimalFormat*,
+ v8::Handle<v8::Object>);
+
+icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
+ v8::Handle<v8::Object> obj) {
+ v8::HandleScope handle_scope;
+
+ // v8::ObjectTemplate doesn't have HasInstance method so we can't check
+ // if obj is an instance of NumberFormat class. We'll check for a property
+ // that has to be in the object. The same applies to other services, like
+ // Collator and DateTimeFormat.
+ if (obj->HasOwnProperty(v8::String::New("numberFormat"))) {
+ return static_cast<icu::DecimalFormat*>(
+ obj->GetAlignedPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a date time formatter.
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
+ delete UnpackNumberFormat(handle);
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+void NumberFormat::JSInternalFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsNumber()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Formatter and numeric value have to be specified.")));
+ return;
+ }
+
+ icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject());
+ if (!number_format) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("NumberFormat method called on an object "
+ "that is not a NumberFormat.")));
+ return;
+ }
+
+ // ICU will handle actual NaN value properly and return NaN string.
+ icu::UnicodeString result;
+ number_format->format(args[1]->NumberValue(), result);
+
+ args.GetReturnValue().Set(v8::String::New(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
+}
+
+void NumberFormat::JSInternalParse(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Formatter and string have to be specified.")));
+ return;
+ }
+
+ icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject());
+ if (!number_format) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("NumberFormat method called on an object "
+ "that is not a NumberFormat.")));
+ return;
+ }
+
+ // ICU will handle actual NaN value properly and return NaN string.
+ icu::UnicodeString string_number;
+ if (!Utils::V8StringToUnicodeString(args[1]->ToString(), &string_number)) {
+ string_number = "";
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Formattable result;
+ // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49
+ // to be part of Chrome.
+ // TODO(cira): Include currency parsing code using parseCurrency call.
+ // We need to check if the formatter parses all currencies or only the
+ // one it was constructed with (it will impact the API - how to return ISO
+ // code and the value).
+ number_format->parse(string_number, result, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ switch (result.getType()) {
+ case icu::Formattable::kDouble:
+ args.GetReturnValue().Set(result.getDouble());
+ return;
+ case icu::Formattable::kLong:
+ args.GetReturnValue().Set(v8::Number::New(result.getLong()));
+ return;
+ case icu::Formattable::kInt64:
+ args.GetReturnValue().Set(v8::Number::New(result.getInt64()));
+ return;
+ default:
+ return;
+ }
+}
+
+void NumberFormat::JSCreateNumberFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 ||
+ !args[0]->IsString() ||
+ !args[1]->IsObject() ||
+ !args[2]->IsObject()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, wrong parameters.")));
+ return;
+ }
+
+ v8::Isolate* isolate = args.GetIsolate();
+ v8::Local<v8::ObjectTemplate> number_format_template =
+ Utils::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object = number_format_template->NewInstance();
+ // But the handle shouldn't be empty.
+ // That can happen if there was a stack overflow when creating the object.
+ if (local_object.IsEmpty()) {
+ args.GetReturnValue().Set(local_object);
+ return;
+ }
+
+ // Set number formatter as internal field of the resulting JS object.
+ icu::DecimalFormat* number_format = InitializeNumberFormat(
+ args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
+
+ if (!number_format) {
+ v8::ThrowException(v8::Exception::Error(v8::String::New(
+ "Internal error. Couldn't create ICU number formatter.")));
+ return;
+ } else {
+ local_object->SetAlignedPointerInInternalField(0, number_format);
+
+ v8::TryCatch try_catch;
+ local_object->Set(v8::String::New("numberFormat"),
+ v8::String::New("valid"));
+ if (try_catch.HasCaught()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, couldn't set property.")));
+ return;
+ }
+ }
+
+ v8::Persistent<v8::Object> wrapper(isolate, local_object);
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DeleteNumberFormat);
+ args.GetReturnValue().Set(wrapper);
+ wrapper.ClearAndLeak();
+}
+
+static icu::DecimalFormat* InitializeNumberFormat(
+ v8::Handle<v8::String> locale,
+ v8::Handle<v8::Object> options,
+ v8::Handle<v8::Object> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::AsciiValue bcp47_locale(locale);
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::DecimalFormat* number_format =
+ CreateICUNumberFormat(icu_locale, options);
+ if (!number_format) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ number_format = CreateICUNumberFormat(no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system).
+ SetResolvedSettings(no_extension_locale, number_format, resolved);
+ } else {
+ SetResolvedSettings(icu_locale, number_format, resolved);
+ }
+
+ return number_format;
+}
+
+static icu::DecimalFormat* CreateICUNumberFormat(
+ const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
+ // Make formatter from options. Numbering system is added
+ // to the locale as Unicode extension (if it was specified at all).
+ UErrorCode status = U_ZERO_ERROR;
+ icu::DecimalFormat* number_format = NULL;
+ icu::UnicodeString style;
+ icu::UnicodeString currency;
+ if (Utils::ExtractStringSetting(options, "style", &style)) {
+ if (style == UNICODE_STRING_SIMPLE("currency")) {
+ Utils::ExtractStringSetting(options, "currency", ¤cy);
+
+ icu::UnicodeString display;
+ Utils::ExtractStringSetting(options, "currencyDisplay", &display);
+#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
+ icu::NumberFormat::EStyles style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ style = icu::NumberFormat::kIsoCurrencyStyle;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ style = icu::NumberFormat::kPluralCurrencyStyle;
+ } else {
+ style = icu::NumberFormat::kCurrencyStyle;
+ }
+#else // ICU version is 4.8 or above (we ignore versions below 4.0).
+ UNumberFormatStyle style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ style = UNUM_CURRENCY_ISO;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ style = UNUM_CURRENCY_PLURAL;
+ } else {
+ style = UNUM_CURRENCY;
+ }
+#endif
+
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, style, status));
+ } else if (style == UNICODE_STRING_SIMPLE("percent")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createPercentInstance(icu_locale, status));
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+ // Make sure 1.1% doesn't go into 2%.
+ number_format->setMinimumFractionDigits(1);
+ } else {
+ // Make a decimal instance by default.
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, status));
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+
+ // Set all options.
+ if (!currency.isEmpty()) {
+ number_format->setCurrency(currency.getBuffer(), status);
+ }
+
+ int32_t digits;
+ if (Utils::ExtractIntegerSetting(
+ options, "minimumIntegerDigits", &digits)) {
+ number_format->setMinimumIntegerDigits(digits);
+ }
+
+ if (Utils::ExtractIntegerSetting(
+ options, "minimumFractionDigits", &digits)) {
+ number_format->setMinimumFractionDigits(digits);
+ }
+
+ if (Utils::ExtractIntegerSetting(
+ options, "maximumFractionDigits", &digits)) {
+ number_format->setMaximumFractionDigits(digits);
+ }
+
+ bool significant_digits_used = false;
+ if (Utils::ExtractIntegerSetting(
+ options, "minimumSignificantDigits", &digits)) {
+ number_format->setMinimumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ if (Utils::ExtractIntegerSetting(
+ options, "maximumSignificantDigits", &digits)) {
+ number_format->setMaximumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ number_format->setSignificantDigitsUsed(significant_digits_used);
+
+ bool grouping;
+ if (Utils::ExtractBooleanSetting(options, "useGrouping", &grouping)) {
+ number_format->setGroupingUsed(grouping);
+ }
+
+ // Set rounding mode.
+ number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
+
+ return number_format;
+}
+
+static void SetResolvedSettings(const icu::Locale& icu_locale,
+ icu::DecimalFormat* number_format,
+ v8::Handle<v8::Object> resolved) {
+ icu::UnicodeString pattern;
+ number_format->toPattern(pattern);
+ resolved->Set(v8::String::New("pattern"),
+ v8::String::New(reinterpret_cast<const uint16_t*>(
+ pattern.getBuffer()), pattern.length()));
+
+ // Set resolved currency code in options.currency if not empty.
+ icu::UnicodeString currency(number_format->getCurrency());
+ if (!currency.isEmpty()) {
+ resolved->Set(v8::String::New("currency"),
+ v8::String::New(reinterpret_cast<const uint16_t*>(
+ currency.getBuffer()), currency.length()));
+ }
+
+ // Ugly hack. ICU doesn't expose numbering system in any way, so we have
+ // to assume that for given locale NumberingSystem constructor produces the
+ // same digits as NumberFormat would.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::NumberingSystem* numbering_system =
+ icu::NumberingSystem::createInstance(icu_locale, status);
+ if (U_SUCCESS(status)) {
+ const char* ns = numbering_system->getName();
+ resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns));
+ } else {
+ resolved->Set(v8::String::New("numberingSystem"), v8::Undefined());
+ }
+ delete numbering_system;
+
+ resolved->Set(v8::String::New("useGrouping"),
+ v8::Boolean::New(number_format->isGroupingUsed()));
+
+ resolved->Set(v8::String::New("minimumIntegerDigits"),
+ v8::Integer::New(number_format->getMinimumIntegerDigits()));
+
+ resolved->Set(v8::String::New("minimumFractionDigits"),
+ v8::Integer::New(number_format->getMinimumFractionDigits()));
+
+ resolved->Set(v8::String::New("maximumFractionDigits"),
+ v8::Integer::New(number_format->getMaximumFractionDigits()));
+
+ if (resolved->HasOwnProperty(v8::String::New("minimumSignificantDigits"))) {
+ resolved->Set(v8::String::New("minimumSignificantDigits"), v8::Integer::New(
+ number_format->getMinimumSignificantDigits()));
+ }
+
+ if (resolved->HasOwnProperty(v8::String::New("maximumSignificantDigits"))) {
+ resolved->Set(v8::String::New("maximumSignificantDigits"), v8::Integer::New(
+ number_format->getMaximumSignificantDigits()));
+ }
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ resolved->Set(v8::String::New("locale"), v8::String::New(result));
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ resolved->Set(v8::String::New("locale"), v8::String::New("und"));
+ }
+}
+
+} // namespace v8_i18n
diff --git a/src/extensions/i18n/number-format.h b/src/extensions/i18n/number-format.h
new file mode 100644
index 0000000..d4dbc4d
--- /dev/null
+++ b/src/extensions/i18n/number-format.h
@@ -0,0 +1,69 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
+#define V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class DecimalFormat;
+}
+
+namespace v8_i18n {
+
+class NumberFormat {
+ public:
+ static void JSCreateNumberFormat(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks date format object from corresponding JavaScript object.
+ static icu::DecimalFormat* UnpackNumberFormat(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the NumberFormat once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteNumberFormat(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param);
+
+ // Formats number and returns corresponding string.
+ static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Parses a string and returns a number.
+ static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ NumberFormat();
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
diff --git a/src/extensions/i18n/number-format.js b/src/extensions/i18n/number-format.js
new file mode 100644
index 0000000..1cd3db1
--- /dev/null
+++ b/src/extensions/i18n/number-format.js
@@ -0,0 +1,295 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Verifies that the input is a well-formed ISO 4217 currency code.
+ * Don't uppercase to test. It could convert invalid code into a valid one.
+ * For example \u00DFP (Eszett+P) becomes SSP.
+ */
+function isWellFormedCurrencyCode(currency) {
+ return typeof currency == "string" &&
+ currency.length == 3 &&
+ currency.match(/[^A-Za-z]/) == null;
+}
+
+
+/**
+ * Returns the valid digit count for a property, or throws RangeError on
+ * a value out of the range.
+ */
+function getNumberOption(options, property, min, max, fallback) {
+ var value = options[property];
+ if (value !== undefined) {
+ value = Number(value);
+ if (isNaN(value) || value < min || value > max) {
+ throw new RangeError(property + ' value is out of range.');
+ }
+ return Math.floor(value);
+ }
+
+ return fallback;
+}
+
+
+/**
+ * Initializes the given object so it's a valid NumberFormat instance.
+ * Useful for subclassing.
+ */
+function initializeNumberFormat(numberFormat, locales, options) {
+ native function NativeJSCreateNumberFormat();
+
+ if (numberFormat.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize NumberFormat object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'numberformat');
+
+ var locale = resolveLocale('numberformat', locales, options);
+
+ var internalOptions = {};
+ defineWEProperty(internalOptions, 'style', getOption(
+ 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
+
+ var currency = getOption('currency', 'string');
+ if (currency !== undefined && !isWellFormedCurrencyCode(currency)) {
+ throw new RangeError('Invalid currency code: ' + currency);
+ }
+
+ if (internalOptions.style === 'currency' && currency === undefined) {
+ throw new TypeError('Currency code is required with currency style.');
+ }
+
+ var currencyDisplay = getOption(
+ 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
+ if (internalOptions.style === 'currency') {
+ defineWEProperty(internalOptions, 'currency', currency.toUpperCase());
+ defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
+ }
+
+ // Digit ranges.
+ var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
+ defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
+
+ var mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
+ defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
+
+ var mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, 3);
+ defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
+
+ var mnsd = options['minimumSignificantDigits'];
+ var mxsd = options['maximumSignificantDigits'];
+ if (mnsd !== undefined || mxsd !== undefined) {
+ mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0);
+ defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
+
+ mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
+ defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
+ }
+
+ // Grouping.
+ defineWEProperty(internalOptions, 'useGrouping', getOption(
+ 'useGrouping', 'boolean', undefined, true));
+
+ // ICU prefers options to be passed using -u- extension key/values for
+ // number format, so we need to build that.
+ var extensionMap = parseExtension(locale.extension);
+ var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = Object.defineProperties({}, {
+ currency: {writable: true},
+ currencyDisplay: {writable: true},
+ locale: {writable: true},
+ maximumFractionDigits: {writable: true},
+ minimumFractionDigits: {writable: true},
+ minimumIntegerDigits: {writable: true},
+ numberingSystem: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ style: {value: internalOptions.style, writable: true},
+ useGrouping: {writable: true}
+ });
+ if (internalOptions.hasOwnProperty('minimumSignificantDigits')) {
+ defineWEProperty(resolved, 'minimumSignificantDigits', undefined);
+ }
+ if (internalOptions.hasOwnProperty('maximumSignificantDigits')) {
+ defineWEProperty(resolved, 'maximumSignificantDigits', undefined);
+ }
+ var formatter = NativeJSCreateNumberFormat(requestedLocale,
+ internalOptions,
+ resolved);
+
+ // We can't get information about number or currency style from ICU, so we
+ // assume user request was fulfilled.
+ if (internalOptions.style === 'currency') {
+ Object.defineProperty(resolved, 'currencyDisplay', {value: currencyDisplay,
+ writable: true});
+ }
+
+ Object.defineProperty(numberFormat, 'formatter', {value: formatter});
+ Object.defineProperty(numberFormat, 'resolved', {value: resolved});
+ Object.defineProperty(numberFormat, '__initializedIntlObject',
+ {value: 'numberformat'});
+
+ return numberFormat;
+}
+
+
+/**
+ * Constructs Intl.NumberFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'NumberFormat', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.NumberFormat(locales, options);
+ }
+
+ return initializeNumberFormat(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * NumberFormat resolvedOptions method.
+ */
+%SetProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'numberformat') {
+ throw new TypeError('resolvedOptions method called on a non-object' +
+ ' or on a object that is not Intl.NumberFormat.');
+ }
+
+ var format = this;
+ var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
+ format.resolved.locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format.resolved.numberingSystem,
+ style: format.resolved.style,
+ useGrouping: format.resolved.useGrouping,
+ minimumIntegerDigits: format.resolved.minimumIntegerDigits,
+ minimumFractionDigits: format.resolved.minimumFractionDigits,
+ maximumFractionDigits: format.resolved.maximumFractionDigits,
+ };
+
+ if (result.style === 'currency') {
+ defineWECProperty(result, 'currency', format.resolved.currency);
+ defineWECProperty(result, 'currencyDisplay',
+ format.resolved.currencyDisplay);
+ }
+
+ if (format.resolved.hasOwnProperty('minimumSignificantDigits')) {
+ defineWECProperty(result, 'minimumSignificantDigits',
+ format.resolved.minimumSignificantDigits);
+ }
+
+ if (format.resolved.hasOwnProperty('maximumSignificantDigits')) {
+ defineWECProperty(result, 'maximumSignificantDigits',
+ format.resolved.maximumSignificantDigits);
+ }
+
+ return result;
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('numberformat', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.NumberFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(value)
+ * according to the effective locale and the formatting options of this
+ * NumberFormat.
+ */
+function formatNumber(formatter, value) {
+ native function NativeJSInternalNumberFormat();
+
+ // Spec treats -0 and +0 as 0.
+ var number = Number(value);
+ if (number === -0) {
+ number = 0;
+ }
+
+ return NativeJSInternalNumberFormat(formatter.formatter, number);
+}
+
+
+/**
+ * Returns a Number that represents string value that was passed in.
+ */
+function parseNumber(formatter, value) {
+ native function NativeJSInternalNumberParse();
+
+ return NativeJSInternalNumberParse(formatter.formatter, String(value));
+}
+
+
+addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
+addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
diff --git a/src/extensions/i18n/overrides.js b/src/extensions/i18n/overrides.js
new file mode 100644
index 0000000..b2d60b3
--- /dev/null
+++ b/src/extensions/i18n/overrides.js
@@ -0,0 +1,220 @@
+// Copyright 2013 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+
+// Save references to Intl objects and methods we use, for added security.
+var savedObjects = {
+ 'collator': Intl.Collator,
+ 'numberformat': Intl.NumberFormat,
+ 'dateformatall': Intl.DateTimeFormat,
+ 'dateformatdate': Intl.DateTimeFormat,
+ 'dateformattime': Intl.DateTimeFormat
+};
+
+
+// Default (created with undefined locales and options parameters) collator,
+// number and date format instances. They'll be created as needed.
+var defaultObjects = {
+ 'collator': undefined,
+ 'numberformat': undefined,
+ 'dateformatall': undefined,
+ 'dateformatdate': undefined,
+ 'dateformattime': undefined,
+};
+
+
+/**
+ * Returns cached or newly created instance of a given service.
+ * We cache only default instances (where no locales or options are provided).
+ */
+function cachedOrNewService(service, locales, options, defaults) {
+ var useOptions = (defaults === undefined) ? options : defaults;
+ if (locales === undefined && options === undefined) {
+ if (defaultObjects[service] === undefined) {
+ defaultObjects[service] = new savedObjects[service](locales, useOptions);
+ }
+ return defaultObjects[service];
+ }
+ return new savedObjects[service](locales, useOptions);
+}
+
+
+/**
+ * Compares this and that, and returns less than 0, 0 or greater than 0 value.
+ * Overrides the built-in method.
+ */
+Object.defineProperty(String.prototype, 'localeCompare', {
+ value: function(that) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (this === undefined || this === null) {
+ throw new TypeError('Method invoked on undefined or null value.');
+ }
+
+ var locales = arguments[1];
+ var options = arguments[2];
+ var collator = cachedOrNewService('collator', locales, options);
+ return compare(collator, this, that);
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(String.prototype.localeCompare, 'localeCompare');
+%FunctionRemovePrototype(String.prototype.localeCompare);
+%SetNativeFlag(String.prototype.localeCompare);
+
+
+/**
+ * Formats a Number object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used.
+ */
+Object.defineProperty(Number.prototype, 'toLocaleString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!(this instanceof Number) && typeof(this) !== 'number') {
+ throw new TypeError('Method invoked on an object that is not Number.');
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ var numberFormat = cachedOrNewService('numberformat', locales, options);
+ return formatNumber(numberFormat, this);
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Number.prototype.toLocaleString, 'toLocaleString');
+%FunctionRemovePrototype(Number.prototype.toLocaleString);
+%SetNativeFlag(Number.prototype.toLocaleString);
+
+
+/**
+ * Returns actual formatted date or fails if date parameter is invalid.
+ */
+function toLocaleDateTime(date, locales, options, required, defaults, service) {
+ if (!(date instanceof Date)) {
+ throw new TypeError('Method invoked on an object that is not Date.');
+ }
+
+ if (isNaN(date)) {
+ return 'Invalid Date';
+ }
+
+ var internalOptions = toDateTimeOptions(options, required, defaults);
+
+ var dateFormat =
+ cachedOrNewService(service, locales, options, internalOptions);
+
+ return formatDate(dateFormat, date);
+}
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - both date and time are
+ * present in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'any', 'all', 'dateformatall');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleString, 'toLocaleString');
+%FunctionRemovePrototype(Date.prototype.toLocaleString);
+%SetNativeFlag(Date.prototype.toLocaleString);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only date is present
+ * in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleDateString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'date', 'date', 'dateformatdate');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleDateString, 'toLocaleDateString');
+%FunctionRemovePrototype(Date.prototype.toLocaleDateString);
+%SetNativeFlag(Date.prototype.toLocaleDateString);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only time is present
+ * in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleTimeString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'time', 'time', 'dateformattime');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleTimeString, 'toLocaleTimeString');
+%FunctionRemovePrototype(Date.prototype.toLocaleTimeString);
+%SetNativeFlag(Date.prototype.toLocaleTimeString);
diff --git a/src/factory.cc b/src/factory.cc
index 673bb23..63b2379 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -31,6 +31,7 @@
#include "debug.h"
#include "execution.h"
#include "factory.h"
+#include "isolate-inl.h"
#include "macro-assembler.h"
#include "objects.h"
#include "objects-visiting.h"
@@ -660,7 +661,8 @@
result->is_compiled() &&
!function_info->is_toplevel() &&
function_info->allows_lazy_compilation() &&
- !function_info->optimization_disabled()) {
+ !function_info->optimization_disabled() &&
+ !isolate()->DebuggerHasBreakPoints()) {
result->MarkForLazyRecompilation();
}
return result;
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index b07354a..a0f907d 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -361,6 +361,7 @@
"enable use of constant pools for double immediate (ARM only)")
// bootstrapper.cc
+DEFINE_bool(enable_i18n, true, "enable i18n extension")
DEFINE_string(expose_natives_as, NULL, "expose natives in global object")
DEFINE_string(expose_debug_as, NULL, "expose debug in global object")
DEFINE_bool(expose_gc, false, "expose gc extension")
@@ -469,7 +470,7 @@
"it is adjusted.")
DEFINE_bool(collect_maps, true,
"garbage collect maps from which no objects can be reached")
-DEFINE_bool(weak_embedded_maps_in_optimized_code, false,
+DEFINE_bool(weak_embedded_maps_in_optimized_code, true,
"make maps embedded in optimized code weak")
DEFINE_bool(flush_code, true,
"flush code that we expect not to use again (during full gc)")
diff --git a/src/frames.cc b/src/frames.cc
index e883c98..0408aa9 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -216,21 +216,40 @@
SafeStackFrameIterator::SafeStackFrameIterator(
Isolate* isolate,
- Address fp, Address sp, Address low_bound, Address high_bound) :
- StackFrameIteratorBase(isolate, false),
- low_bound_(low_bound), high_bound_(high_bound) {
+ Address fp, Address sp, Address js_entry_sp)
+ : StackFrameIteratorBase(isolate, false),
+ low_bound_(sp),
+ high_bound_(js_entry_sp),
+ top_frame_type_(StackFrame::NONE) {
StackFrame::State state;
StackFrame::Type type;
ThreadLocalTop* top = isolate->thread_local_top();
if (IsValidTop(top)) {
type = ExitFrame::GetStateForFramePointer(Isolate::c_entry_fp(top), &state);
+ top_frame_type_ = type;
} else if (IsValidStackAddress(fp)) {
ASSERT(fp != NULL);
state.fp = fp;
state.sp = sp;
state.pc_address = StackFrame::ResolveReturnAddressLocation(
reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp)));
- type = StackFrame::ComputeType(this, &state);
+ // StackFrame::ComputeType will read both kContextOffset and kMarkerOffset,
+ // we check only that kMarkerOffset is within the stack bounds and do
+ // compile time check that kContextOffset slot is pushed on the stack before
+ // kMarkerOffset.
+ STATIC_ASSERT(StandardFrameConstants::kMarkerOffset <
+ StandardFrameConstants::kContextOffset);
+ Address frame_marker = fp + StandardFrameConstants::kMarkerOffset;
+ if (IsValidStackAddress(frame_marker)) {
+ type = StackFrame::ComputeType(this, &state);
+ top_frame_type_ = type;
+ } else {
+ // Mark the frame as JAVA_SCRIPT if we cannot determine its type.
+ // The frame anyways will be skipped.
+ type = StackFrame::JAVA_SCRIPT;
+ // Top frame is incomplete so we cannot reliably determine its type.
+ top_frame_type_ = StackFrame::NONE;
+ }
} else {
return;
}
diff --git a/src/frames.h b/src/frames.h
index 9ca218a..0a5b609 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -881,11 +881,13 @@
public:
SafeStackFrameIterator(Isolate* isolate,
Address fp, Address sp,
- Address low_bound, Address high_bound);
+ Address js_entry_sp);
inline JavaScriptFrame* frame() const;
void Advance();
+ StackFrame::Type top_frame_type() const { return top_frame_type_; }
+
private:
void AdvanceOneFrame();
@@ -899,6 +901,7 @@
const Address low_bound_;
const Address high_bound_;
+ StackFrame::Type top_frame_type_;
};
diff --git a/src/gdb-jit.cc b/src/gdb-jit.cc
index f4a7c2d..825d1e7 100644
--- a/src/gdb-jit.cc
+++ b/src/gdb-jit.cc
@@ -500,10 +500,10 @@
#if defined(__MACH_O)
class MachO BASE_EMBEDDED {
public:
- MachO() : sections_(6) { }
+ explicit MachO(Zone* zone) : zone_(zone), sections_(6, zone) { }
uint32_t AddSection(MachOSection* section) {
- sections_.Add(section);
+ sections_.Add(section, zone_);
return sections_.length() - 1;
}
@@ -620,7 +620,7 @@
cmd->filesize = w->position() - (uintptr_t)cmd->fileoff;
}
-
+ Zone* zone_;
ZoneList<MachOSection*> sections_;
};
#endif // defined(__MACH_O)
@@ -629,7 +629,7 @@
#if defined(__ELF)
class ELF BASE_EMBEDDED {
public:
- explicit ELF(Zone* zone) : sections_(6, zone) {
+ explicit ELF(Zone* zone) : zone_(zone), sections_(6, zone) {
sections_.Add(new(zone) ELFSection("", ELFSection::TYPE_NULL, 0), zone);
sections_.Add(new(zone) ELFStringTable(".shstrtab"), zone);
}
@@ -644,8 +644,8 @@
return sections_[index];
}
- uint32_t AddSection(ELFSection* section, Zone* zone) {
- sections_.Add(section, zone);
+ uint32_t AddSection(ELFSection* section) {
+ sections_.Add(section, zone_);
section->set_index(sections_.length() - 1);
return sections_.length() - 1;
}
@@ -743,6 +743,7 @@
}
}
+ Zone* zone_;
ZoneList<ELFSection*> sections_;
};
@@ -1026,8 +1027,8 @@
ELFStringTable* strtab = new(zone) ELFStringTable(".strtab");
// Symbol table should be followed by the linked string table.
- elf->AddSection(symtab, zone);
- elf->AddSection(strtab, zone);
+ elf->AddSection(symtab);
+ elf->AddSection(strtab);
symtab->Add(ELFSymbol("V8 Code",
0,
@@ -1793,12 +1794,12 @@
Zone* zone,
DebugObject* obj) {
if (desc->IsLineInfoAvailable()) {
- obj->AddSection(new(zone) DebugInfoSection(desc), zone);
- obj->AddSection(new(zone) DebugAbbrevSection(desc), zone);
- obj->AddSection(new(zone) DebugLineSection(desc), zone);
+ obj->AddSection(new(zone) DebugInfoSection(desc));
+ obj->AddSection(new(zone) DebugAbbrevSection(desc));
+ obj->AddSection(new(zone) DebugLineSection(desc));
}
#if V8_TARGET_ARCH_X64
- obj->AddSection(new(zone) UnwindInfoSection(desc), zone);
+ obj->AddSection(new(zone) UnwindInfoSection(desc));
#endif
}
@@ -1918,14 +1919,15 @@
static JITCodeEntry* CreateELFObject(CodeDescription* desc, Isolate* isolate) {
#ifdef __MACH_O
- MachO mach_o;
+ Zone zone(isolate);
+ MachO mach_o(&zone);
Writer w(&mach_o);
- mach_o.AddSection(new MachOTextSection(kCodeAlignment,
- desc->CodeStart(),
- desc->CodeSize()));
+ mach_o.AddSection(new(&zone) MachOTextSection(kCodeAlignment,
+ desc->CodeStart(),
+ desc->CodeSize()));
- CreateDWARFSections(desc, &mach_o);
+ CreateDWARFSections(desc, &zone, &mach_o);
mach_o.Write(&w, desc->CodeStart(), desc->CodeSize());
#else
@@ -1941,8 +1943,7 @@
desc->CodeStart(),
0,
desc->CodeSize(),
- ELFSection::FLAG_ALLOC | ELFSection::FLAG_EXEC),
- &zone);
+ ELFSection::FLAG_ALLOC | ELFSection::FLAG_EXEC));
CreateSymbolsTable(desc, &zone, &elf, text_section_index);
@@ -2165,6 +2166,24 @@
}
+void GDBJITInterface::RemoveCodeRange(Address start, Address end) {
+ HashMap* entries = GetEntries();
+ Zone zone(Isolate::Current());
+ ZoneList<Code*> dead_codes(1, &zone);
+
+ for (HashMap::Entry* e = entries->Start(); e != NULL; e = entries->Next(e)) {
+ Code* code = reinterpret_cast<Code*>(e->key);
+ if (code->address() >= start && code->address() < end) {
+ dead_codes.Add(code, &zone);
+ }
+ }
+
+ for (int i = 0; i < dead_codes.length(); i++) {
+ RemoveCode(dead_codes.at(i));
+ }
+}
+
+
void GDBJITInterface::RegisterDetailedLineInfo(Code* code,
GDBJITLineInfo* line_info) {
ScopedLock lock(mutex.Pointer());
diff --git a/src/gdb-jit.h b/src/gdb-jit.h
index cc052c1..a34d3d3 100644
--- a/src/gdb-jit.h
+++ b/src/gdb-jit.h
@@ -131,6 +131,8 @@
static void RemoveCode(Code* code);
+ static void RemoveCodeRange(Address start, Address end);
+
static void RegisterDetailedLineInfo(Code* code, GDBJITLineInfo* line_info);
};
diff --git a/src/handles.cc b/src/handles.cc
index 382b3fb..7d4b25f 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -294,13 +294,6 @@
}
-Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) {
- const bool skip_hidden_prototypes = false;
- CALL_HEAP_FUNCTION(obj->GetIsolate(),
- obj->SetPrototype(*value, skip_hidden_prototypes), Object);
-}
-
-
Handle<Object> LookupSingleCharacterStringFromCode(Isolate* isolate,
uint32_t index) {
CALL_HEAP_FUNCTION(
diff --git a/src/handles.h b/src/handles.h
index 0cd4f5b..5976b75 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -30,6 +30,7 @@
#include "allocation.h"
#include "apiutils.h"
+#include "objects.h"
namespace v8 {
namespace internal {
@@ -244,8 +245,6 @@
Handle<Object> obj,
Handle<Object> key);
-Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value);
-
Handle<Object> LookupSingleCharacterStringFromCode(Isolate* isolate,
uint32_t index);
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index 4f6fdb1..e517df4 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -124,11 +124,6 @@
}
-HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
- return snapshots_->GetSnapshot(uid);
-}
-
-
SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
if (!obj->IsHeapObject())
return v8::HeapProfiler::kUnknownObjectId;
diff --git a/src/heap-profiler.h b/src/heap-profiler.h
index 1ed73b9..5ae60fa 100644
--- a/src/heap-profiler.h
+++ b/src/heap-profiler.h
@@ -28,6 +28,7 @@
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
+#include "heap-snapshot-generator-inl.h"
#include "isolate.h"
namespace v8 {
@@ -65,7 +66,6 @@
SnapshotObjectId PushHeapObjectsStats(OutputStream* stream);
int GetSnapshotsCount();
HeapSnapshot* GetSnapshot(int index);
- HeapSnapshot* FindSnapshot(unsigned uid);
SnapshotObjectId GetSnapshotObjectId(Handle<Object> obj);
void DeleteAllSnapshots();
diff --git a/src/heap-snapshot-generator.cc b/src/heap-snapshot-generator.cc
index dd8896e..f959aee 100644
--- a/src/heap-snapshot-generator.cc
+++ b/src/heap-snapshot-generator.cc
@@ -189,15 +189,11 @@
template <> struct SnapshotSizeConstants<4> {
static const int kExpectedHeapGraphEdgeSize = 12;
static const int kExpectedHeapEntrySize = 24;
- static const int kExpectedHeapSnapshotsCollectionSize = 100;
- static const int kExpectedHeapSnapshotSize = 132;
};
template <> struct SnapshotSizeConstants<8> {
static const int kExpectedHeapGraphEdgeSize = 24;
static const int kExpectedHeapEntrySize = 32;
- static const int kExpectedHeapSnapshotsCollectionSize = 152;
- static const int kExpectedHeapSnapshotSize = 160;
};
} // namespace
@@ -238,7 +234,7 @@
HeapEntry* HeapSnapshot::AddRootEntry() {
ASSERT(root_index_ == HeapEntry::kNoEntry);
ASSERT(entries_.is_empty()); // Root entry must be the first one.
- HeapEntry* entry = AddEntry(HeapEntry::kObject,
+ HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
"",
HeapObjectsMap::kInternalRootObjectId,
0);
@@ -250,7 +246,7 @@
HeapEntry* HeapSnapshot::AddGcRootsEntry() {
ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
- HeapEntry* entry = AddEntry(HeapEntry::kObject,
+ HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
"(GC roots)",
HeapObjectsMap::kGcRootsObjectId,
0);
@@ -263,7 +259,7 @@
ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
HeapEntry* entry = AddEntry(
- HeapEntry::kObject,
+ HeapEntry::kSynthetic,
VisitorSynchronization::kTagNames[tag],
HeapObjectsMap::GetNthGcSubrootId(tag),
0);
@@ -353,8 +349,6 @@
size_t HeapSnapshot::RawSnapshotSize() const {
- STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::kExpectedHeapSnapshotSize ==
- sizeof(HeapSnapshot)); // NOLINT
return
sizeof(*this) +
GetMemoryUsedByList(entries_) +
@@ -578,7 +572,6 @@
HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap)
: is_tracking_objects_(false),
- snapshots_uids_(HeapSnapshotsMatch),
token_enumerator_(new TokenEnumerator()),
ids_(heap) {
}
@@ -607,29 +600,12 @@
ids_.SnapshotGenerationFinished();
if (snapshot != NULL) {
snapshots_.Add(snapshot);
- HashMap::Entry* entry =
- snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
- static_cast<uint32_t>(snapshot->uid()),
- true);
- ASSERT(entry->value == NULL);
- entry->value = snapshot;
}
}
-HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
- HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
- static_cast<uint32_t>(uid),
- false);
- return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
-}
-
-
void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
snapshots_.RemoveElement(snapshot);
- unsigned uid = snapshot->uid();
- snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
- static_cast<uint32_t>(uid));
}
@@ -656,13 +632,9 @@
size_t HeapSnapshotsCollection::GetUsedMemorySize() const {
- STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::
- kExpectedHeapSnapshotsCollectionSize ==
- sizeof(HeapSnapshotsCollection)); // NOLINT
size_t size = sizeof(*this);
size += names_.GetUsedMemorySize();
size += ids_.GetUsedMemorySize();
- size += sizeof(HashMap::Entry) * snapshots_uids_.capacity();
size += GetMemoryUsedByList(snapshots_);
for (int i = 0; i < snapshots_.length(); ++i) {
size += snapshots_[i]->RawSnapshotSize();
diff --git a/src/heap-snapshot-generator.h b/src/heap-snapshot-generator.h
index d3c8f1b..cd1ec29 100644
--- a/src/heap-snapshot-generator.h
+++ b/src/heap-snapshot-generator.h
@@ -28,6 +28,8 @@
#ifndef V8_HEAP_SNAPSHOT_GENERATOR_H_
#define V8_HEAP_SNAPSHOT_GENERATOR_H_
+#include "profile-generator-inl.h"
+
namespace v8 {
namespace internal {
@@ -301,7 +303,6 @@
HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
void SnapshotGenerationFinished(HeapSnapshot* snapshot);
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
- HeapSnapshot* GetSnapshot(unsigned uid);
void RemoveSnapshot(HeapSnapshot* snapshot);
StringsStorage* names() { return &names_; }
@@ -321,14 +322,8 @@
size_t GetUsedMemorySize() const;
private:
- INLINE(static bool HeapSnapshotsMatch(void* key1, void* key2)) {
- return key1 == key2;
- }
-
bool is_tracking_objects_; // Whether tracking object moves is needed.
List<HeapSnapshot*> snapshots_;
- // Mapping from snapshots' uids to HeapSnapshot* pointers.
- HashMap snapshots_uids_;
StringsStorage names_;
TokenEnumerator* token_enumerator_;
// Mapping from HeapObject addresses to objects' uids.
diff --git a/src/heap.cc b/src/heap.cc
index 3fac601..9d1ac8c 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -32,6 +32,7 @@
#include "bootstrapper.h"
#include "codegen.h"
#include "compilation-cache.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "deoptimizer.h"
#include "global-handles.h"
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index ed6ab15..26bda87 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -111,7 +111,6 @@
V(ElementsKind) \
V(EnterInlined) \
V(EnvironmentMarker) \
- V(FixedArrayBaseLength) \
V(ForceRepresentation) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
@@ -2542,29 +2541,6 @@
};
-class HFixedArrayBaseLength: public HUnaryOperation {
- public:
- explicit HFixedArrayBaseLength(HValue* value) : HUnaryOperation(value) {
- set_type(HType::Smi());
- set_representation(Representation::Smi());
- SetFlag(kUseGVN);
- SetGVNFlag(kDependsOnArrayLengths);
- }
-
- virtual Representation RequiredInputRepresentation(int index) {
- return Representation::Tagged();
- }
-
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength)
-
- protected:
- virtual bool DataEquals(HValue* other) { return true; }
-
- private:
- virtual bool IsDeletable() const { return true; }
-};
-
-
class HMapEnumLength: public HUnaryOperation {
public:
explicit HMapEnumLength(HValue* value) : HUnaryOperation(value) {
@@ -5877,6 +5853,7 @@
: elements_kind_(elements_kind),
index_offset_(0),
is_dehoisted_(false),
+ is_uninitialized_(false),
new_space_dominator_(NULL) {
SetOperandAt(0, obj);
SetOperandAt(1, key);
@@ -5937,6 +5914,9 @@
virtual Representation observed_input_representation(int index) {
if (index < 2) return RequiredInputRepresentation(index);
+ if (IsUninitialized()) {
+ return Representation::None();
+ }
if (IsFastSmiElementsKind(elements_kind())) {
return Representation::Smi();
}
@@ -5963,6 +5943,10 @@
void SetKey(HValue* key) { SetOperandAt(1, key); }
bool IsDehoisted() { return is_dehoisted_; }
void SetDehoisted(bool is_dehoisted) { is_dehoisted_ = is_dehoisted; }
+ bool IsUninitialized() { return is_uninitialized_; }
+ void SetUninitialized(bool is_uninitialized) {
+ is_uninitialized_ = is_uninitialized;
+ }
bool IsConstantHoleStore() {
return value()->IsConstant() && HConstant::cast(value())->IsTheHole();
@@ -5993,7 +5977,8 @@
private:
ElementsKind elements_kind_;
uint32_t index_offset_;
- bool is_dehoisted_;
+ bool is_dehoisted_ : 1;
+ bool is_uninitialized_ : 1;
HValue* new_space_dominator_;
};
diff --git a/src/hydrogen-osr.cc b/src/hydrogen-osr.cc
new file mode 100644
index 0000000..19a1c77
--- /dev/null
+++ b/src/hydrogen-osr.cc
@@ -0,0 +1,123 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "hydrogen.h"
+#include "hydrogen-osr.h"
+
+namespace v8 {
+namespace internal {
+
+// True iff. we are compiling for OSR and the statement is the entry.
+bool HOsrBuilder::HasOsrEntryAt(IterationStatement* statement) {
+ return statement->OsrEntryId() == builder_->current_info()->osr_ast_id();
+}
+
+
+// Build a new loop header block and set it as the current block.
+HBasicBlock *HOsrBuilder::BuildLoopEntry() {
+ HBasicBlock* loop_entry = builder_->CreateLoopHeaderBlock();
+ builder_->current_block()->Goto(loop_entry);
+ builder_->set_current_block(loop_entry);
+ return loop_entry;
+}
+
+
+HBasicBlock* HOsrBuilder::BuildPossibleOsrLoopEntry(
+ IterationStatement* statement) {
+ // Check if there is an OSR here first.
+ if (!HasOsrEntryAt(statement)) return BuildLoopEntry();
+
+ Zone* zone = builder_->zone();
+ HGraph* graph = builder_->graph();
+
+ // only one OSR point per compile is allowed.
+ ASSERT(graph->osr() == NULL);
+
+ // remember this builder as the one OSR builder in the graph.
+ graph->set_osr(this);
+
+ HBasicBlock* non_osr_entry = graph->CreateBasicBlock();
+ osr_entry_ = graph->CreateBasicBlock();
+ HValue* true_value = graph->GetConstantTrue();
+ HBranch* test = new(zone) HBranch(true_value, non_osr_entry, osr_entry_);
+ builder_->current_block()->Finish(test);
+
+ HBasicBlock* loop_predecessor = graph->CreateBasicBlock();
+ non_osr_entry->Goto(loop_predecessor);
+
+ builder_->set_current_block(osr_entry_);
+ osr_entry_->set_osr_entry();
+ BailoutId osr_entry_id = statement->OsrEntryId();
+
+ HEnvironment *environment = builder_->environment();
+ int first_expression_index = environment->first_expression_index();
+ int length = environment->length();
+ osr_values_ = new(zone) ZoneList<HUnknownOSRValue*>(length, zone);
+
+ for (int i = 0; i < first_expression_index; ++i) {
+ HUnknownOSRValue* osr_value = builder_->Add<HUnknownOSRValue>();
+ environment->Bind(i, osr_value);
+ osr_values_->Add(osr_value, zone);
+ }
+
+ if (first_expression_index != length) {
+ environment->Drop(length - first_expression_index);
+ for (int i = first_expression_index; i < length; ++i) {
+ HUnknownOSRValue* osr_value = builder_->Add<HUnknownOSRValue>();
+ environment->Push(osr_value);
+ osr_values_->Add(osr_value, zone);
+ }
+ }
+
+ builder_->AddSimulate(osr_entry_id);
+ builder_->Add<HOsrEntry>(osr_entry_id);
+ HContext* context = builder_->Add<HContext>();
+ environment->BindContext(context);
+ builder_->current_block()->Goto(loop_predecessor);
+ loop_predecessor->SetJoinId(statement->EntryId());
+ builder_->set_current_block(loop_predecessor);
+
+ // Create the final loop entry
+ osr_loop_entry_ = BuildLoopEntry();
+ return osr_loop_entry_;
+}
+
+
+void HOsrBuilder::FinishGraph() {
+ // do nothing for now.
+}
+
+
+void HOsrBuilder::FinishOsrValues() {
+ const ZoneList<HPhi*>* phis = osr_loop_entry_->phis();
+ for (int j = 0; j < phis->length(); j++) {
+ HPhi* phi = phis->at(j);
+ osr_values_->at(phi->merged_index())->set_incoming_value(phi);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/src/hydrogen-osr.h b/src/hydrogen-osr.h
new file mode 100644
index 0000000..0c6b65d
--- /dev/null
+++ b/src/hydrogen-osr.h
@@ -0,0 +1,70 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_HYDROGEN_OSR_H_
+#define V8_HYDROGEN_OSR_H_
+
+#include "hydrogen.h"
+#include "ast.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// Responsible for building graph parts related to OSR and otherwise
+// setting up the graph to do an OSR compile.
+class HOsrBuilder : public ZoneObject {
+ public:
+ explicit HOsrBuilder(HOptimizedGraphBuilder* builder)
+ : builder_(builder),
+ osr_entry_(NULL),
+ osr_loop_entry_(NULL),
+ osr_values_(NULL) { }
+ // Creates the loop entry block for the given statement, setting up OSR
+ // entries as necessary, and sets the current block to the new block.
+ HBasicBlock* BuildPossibleOsrLoopEntry(IterationStatement* statement);
+
+ // Process the hydrogen graph after it has been completed, performing
+ // any OSR-specific cleanups or changes.
+ void FinishGraph();
+
+ // Process the OSR values and phis after initial graph optimization.
+ void FinishOsrValues();
+
+ private:
+ HBasicBlock* BuildLoopEntry();
+ bool HasOsrEntryAt(IterationStatement* statement);
+
+ HOptimizedGraphBuilder* builder_;
+ HBasicBlock* osr_entry_;
+ HBasicBlock* osr_loop_entry_;
+ ZoneList<HUnknownOSRValue*>* osr_values_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_OSR_H_
diff --git a/src/hydrogen-range-analysis.cc b/src/hydrogen-range-analysis.cc
new file mode 100644
index 0000000..0d4d970
--- /dev/null
+++ b/src/hydrogen-range-analysis.cc
@@ -0,0 +1,169 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "hydrogen-range-analysis.h"
+
+namespace v8 {
+namespace internal {
+
+
+void HRangeAnalysisPhase::TraceRange(const char* msg, ...) {
+ if (FLAG_trace_range) {
+ va_list arguments;
+ va_start(arguments, msg);
+ OS::VPrint(msg, arguments);
+ va_end(arguments);
+ }
+}
+
+
+void HRangeAnalysisPhase::Analyze(HBasicBlock* block) {
+ TraceRange("Analyzing block B%d\n", block->block_id());
+
+ int last_changed_range = changed_ranges_.length() - 1;
+
+ // Infer range based on control flow.
+ if (block->predecessors()->length() == 1) {
+ HBasicBlock* pred = block->predecessors()->first();
+ if (pred->end()->IsCompareIDAndBranch()) {
+ InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
+ }
+ }
+
+ // Process phi instructions.
+ for (int i = 0; i < block->phis()->length(); ++i) {
+ HPhi* phi = block->phis()->at(i);
+ InferRange(phi);
+ }
+
+ // Go through all instructions of the current block.
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ InferRange(it.Current());
+ }
+
+ // Continue analysis in all dominated blocks.
+ for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
+ Analyze(block->dominated_blocks()->at(i));
+ }
+
+ RollBackTo(last_changed_range);
+}
+
+
+void HRangeAnalysisPhase::InferControlFlowRange(HCompareIDAndBranch* test,
+ HBasicBlock* dest) {
+ ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
+ if (test->representation().IsSmiOrInteger32()) {
+ Token::Value op = test->token();
+ if (test->SecondSuccessor() == dest) {
+ op = Token::NegateCompareOp(op);
+ }
+ Token::Value inverted_op = Token::ReverseCompareOp(op);
+ UpdateControlFlowRange(op, test->left(), test->right());
+ UpdateControlFlowRange(inverted_op, test->right(), test->left());
+ }
+}
+
+
+// We know that value [op] other. Use this information to update the range on
+// value.
+void HRangeAnalysisPhase::UpdateControlFlowRange(Token::Value op,
+ HValue* value,
+ HValue* other) {
+ Range temp_range;
+ Range* range = other->range() != NULL ? other->range() : &temp_range;
+ Range* new_range = NULL;
+
+ TraceRange("Control flow range infer %d %s %d\n",
+ value->id(),
+ Token::Name(op),
+ other->id());
+
+ if (op == Token::EQ || op == Token::EQ_STRICT) {
+ // The same range has to apply for value.
+ new_range = range->Copy(graph()->zone());
+ } else if (op == Token::LT || op == Token::LTE) {
+ new_range = range->CopyClearLower(graph()->zone());
+ if (op == Token::LT) {
+ new_range->AddConstant(-1);
+ }
+ } else if (op == Token::GT || op == Token::GTE) {
+ new_range = range->CopyClearUpper(graph()->zone());
+ if (op == Token::GT) {
+ new_range->AddConstant(1);
+ }
+ }
+
+ if (new_range != NULL && !new_range->IsMostGeneric()) {
+ AddRange(value, new_range);
+ }
+}
+
+
+void HRangeAnalysisPhase::InferRange(HValue* value) {
+ ASSERT(!value->HasRange());
+ if (!value->representation().IsNone()) {
+ value->ComputeInitialRange(graph()->zone());
+ Range* range = value->range();
+ TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
+ value->id(),
+ value->Mnemonic(),
+ range->lower(),
+ range->upper());
+ }
+}
+
+
+void HRangeAnalysisPhase::RollBackTo(int index) {
+ for (int i = index + 1; i < changed_ranges_.length(); ++i) {
+ changed_ranges_[i]->RemoveLastAddedRange();
+ }
+ changed_ranges_.Rewind(index + 1);
+}
+
+
+void HRangeAnalysisPhase::AddRange(HValue* value, Range* range) {
+ Range* original_range = value->range();
+ value->AddNewRange(range, graph()->zone());
+ changed_ranges_.Add(value, zone());
+ Range* new_range = value->range();
+ TraceRange("Updated range of %d set to [%d,%d]\n",
+ value->id(),
+ new_range->lower(),
+ new_range->upper());
+ if (original_range != NULL) {
+ TraceRange("Original range was [%d,%d]\n",
+ original_range->lower(),
+ original_range->upper());
+ }
+ TraceRange("New information was [%d,%d]\n",
+ range->lower(),
+ range->upper());
+}
+
+
+} } // namespace v8::internal
diff --git a/src/hydrogen-range-analysis.h b/src/hydrogen-range-analysis.h
new file mode 100644
index 0000000..52ce109
--- /dev/null
+++ b/src/hydrogen-range-analysis.h
@@ -0,0 +1,61 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_HYDROGEN_RANGE_ANALYSIS_H_
+#define V8_HYDROGEN_RANGE_ANALYSIS_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HRangeAnalysisPhase : public HPhase {
+ public:
+ explicit HRangeAnalysisPhase(HGraph* graph)
+ : HPhase("H_Range analysis", graph), changed_ranges_(16, zone()) { }
+
+ void Run() {
+ Analyze(graph()->entry_block());
+ }
+
+ private:
+ void TraceRange(const char* msg, ...);
+ void Analyze(HBasicBlock* block);
+ void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
+ void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
+ void InferRange(HValue* value);
+ void RollBackTo(int index);
+ void AddRange(HValue* value, Range* range);
+
+ ZoneList<HValue*> changed_ranges_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_RANGE_ANALYSIS_H_
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 432a6bc..7679f93 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -37,6 +37,8 @@
#include "hydrogen-escape-analysis.h"
#include "hydrogen-infer-representation.h"
#include "hydrogen-gvn.h"
+#include "hydrogen-osr.h"
+#include "hydrogen-range-analysis.h"
#include "hydrogen-uint32-analysis.h"
#include "lithium-allocator.h"
#include "parser.h"
@@ -1132,7 +1134,7 @@
length_checker.IfCompare(length, key, Token::EQ);
length_checker.Then();
- HValue* current_capacity = Add<HFixedArrayBaseLength>(elements);
+ HValue* current_capacity = AddLoadFixedArrayLength(elements);
IfBuilder capacity_checker(this);
@@ -1188,7 +1190,7 @@
Handle<Map>(heap->fixed_cow_array_map()));
cow_checker.Then();
- HValue* capacity = Add<HFixedArrayBaseLength>(elements);
+ HValue* capacity = AddLoadFixedArrayLength(elements);
HValue* new_elements = BuildGrowElementsCapacity(object, elements,
kind, length, capacity);
@@ -1243,10 +1245,10 @@
if (is_js_array) {
length = AddLoad(object, HObjectAccess::ForArrayLength(), mapcheck,
Representation::Smi());
- length->set_type(HType::Smi());
} else {
- length = Add<HFixedArrayBaseLength>(elements);
+ length = AddLoadFixedArrayLength(elements);
}
+ length->set_type(HType::Smi());
HValue* checked_key = NULL;
if (IsExternalArrayElementsKind(elements_kind)) {
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
@@ -1416,6 +1418,14 @@
}
+HLoadNamedField* HGraphBuilder::AddLoadFixedArrayLength(HValue* object) {
+ HLoadNamedField* instr = AddLoad(object, HObjectAccess::ForFixedArrayLength(),
+ NULL, Representation::Smi());
+ instr->set_type(HType::Smi());
+ return instr;
+}
+
+
HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* context,
HValue* old_capacity) {
Zone* zone = this->zone();
@@ -1907,7 +1917,8 @@
break_scope_(NULL),
inlined_count_(0),
globals_(10, info->zone()),
- inline_bailout_(false) {
+ inline_bailout_(false),
+ osr_(new(info->zone()) HOsrBuilder(this)) {
// This is not initialized in the initializer list because the
// constructor for the initial state relies on function_state_ == NULL
// to know it's the initial state.
@@ -1975,6 +1986,7 @@
values_(16, info->zone()),
phi_list_(NULL),
uint32_instructions_(NULL),
+ osr_(NULL),
info_(info),
zone_(info->zone()),
is_recursive_(false),
@@ -2568,169 +2580,6 @@
}
-class HRangeAnalysis BASE_EMBEDDED {
- public:
- explicit HRangeAnalysis(HGraph* graph) :
- graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { }
-
- void Analyze();
-
- private:
- void TraceRange(const char* msg, ...);
- void Analyze(HBasicBlock* block);
- void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
- void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
- void InferRange(HValue* value);
- void RollBackTo(int index);
- void AddRange(HValue* value, Range* range);
-
- HGraph* graph_;
- Zone* zone_;
- ZoneList<HValue*> changed_ranges_;
-};
-
-
-void HRangeAnalysis::TraceRange(const char* msg, ...) {
- if (FLAG_trace_range) {
- va_list arguments;
- va_start(arguments, msg);
- OS::VPrint(msg, arguments);
- va_end(arguments);
- }
-}
-
-
-void HRangeAnalysis::Analyze() {
- HPhase phase("H_Range analysis", graph_);
- Analyze(graph_->entry_block());
-}
-
-
-void HRangeAnalysis::Analyze(HBasicBlock* block) {
- TraceRange("Analyzing block B%d\n", block->block_id());
-
- int last_changed_range = changed_ranges_.length() - 1;
-
- // Infer range based on control flow.
- if (block->predecessors()->length() == 1) {
- HBasicBlock* pred = block->predecessors()->first();
- if (pred->end()->IsCompareIDAndBranch()) {
- InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
- }
- }
-
- // Process phi instructions.
- for (int i = 0; i < block->phis()->length(); ++i) {
- HPhi* phi = block->phis()->at(i);
- InferRange(phi);
- }
-
- // Go through all instructions of the current block.
- for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
- InferRange(it.Current());
- }
-
- // Continue analysis in all dominated blocks.
- for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
- Analyze(block->dominated_blocks()->at(i));
- }
-
- RollBackTo(last_changed_range);
-}
-
-
-void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
- HBasicBlock* dest) {
- ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
- if (test->representation().IsSmiOrInteger32()) {
- Token::Value op = test->token();
- if (test->SecondSuccessor() == dest) {
- op = Token::NegateCompareOp(op);
- }
- Token::Value inverted_op = Token::ReverseCompareOp(op);
- UpdateControlFlowRange(op, test->left(), test->right());
- UpdateControlFlowRange(inverted_op, test->right(), test->left());
- }
-}
-
-
-// We know that value [op] other. Use this information to update the range on
-// value.
-void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
- HValue* value,
- HValue* other) {
- Range temp_range;
- Range* range = other->range() != NULL ? other->range() : &temp_range;
- Range* new_range = NULL;
-
- TraceRange("Control flow range infer %d %s %d\n",
- value->id(),
- Token::Name(op),
- other->id());
-
- if (op == Token::EQ || op == Token::EQ_STRICT) {
- // The same range has to apply for value.
- new_range = range->Copy(zone_);
- } else if (op == Token::LT || op == Token::LTE) {
- new_range = range->CopyClearLower(zone_);
- if (op == Token::LT) {
- new_range->AddConstant(-1);
- }
- } else if (op == Token::GT || op == Token::GTE) {
- new_range = range->CopyClearUpper(zone_);
- if (op == Token::GT) {
- new_range->AddConstant(1);
- }
- }
-
- if (new_range != NULL && !new_range->IsMostGeneric()) {
- AddRange(value, new_range);
- }
-}
-
-
-void HRangeAnalysis::InferRange(HValue* value) {
- ASSERT(!value->HasRange());
- if (!value->representation().IsNone()) {
- value->ComputeInitialRange(zone_);
- Range* range = value->range();
- TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
- value->id(),
- value->Mnemonic(),
- range->lower(),
- range->upper());
- }
-}
-
-
-void HRangeAnalysis::RollBackTo(int index) {
- for (int i = index + 1; i < changed_ranges_.length(); ++i) {
- changed_ranges_[i]->RemoveLastAddedRange();
- }
- changed_ranges_.Rewind(index + 1);
-}
-
-
-void HRangeAnalysis::AddRange(HValue* value, Range* range) {
- Range* original_range = value->range();
- value->AddNewRange(range, zone_);
- changed_ranges_.Add(value, zone_);
- Range* new_range = value->range();
- TraceRange("Updated range of %d set to [%d,%d]\n",
- value->id(),
- new_range->lower(),
- new_range->upper());
- if (original_range != NULL) {
- TraceRange("Original range was [%d,%d]\n",
- original_range->lower(),
- original_range->upper());
- }
- TraceRange("New information was [%d,%d]\n",
- range->lower(),
- range->upper());
-}
-
-
class HStackCheckEliminator BASE_EMBEDDED {
public:
explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
@@ -3524,6 +3373,9 @@
!type_info->matches_inlined_type_change_checksum(composite_checksum));
type_info->set_inlined_type_change_checksum(composite_checksum);
+ // Perform any necessary OSR-specific cleanups or changes to the graph.
+ osr_->FinishGraph();
+
return true;
}
@@ -3567,13 +3419,7 @@
}
CollectPhis();
- if (has_osr_loop_entry()) {
- const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
- for (int j = 0; j < phis->length(); j++) {
- HPhi* phi = phis->at(j);
- osr_values()->at(phi->merged_index())->set_incoming_value(phi);
- }
- }
+ if (has_osr()) osr()->FinishOsrValues();
Run<HInferRepresentationPhase>();
@@ -3598,10 +3444,8 @@
if (FLAG_use_gvn) Run<HGlobalValueNumberingPhase>();
- if (FLAG_use_range) {
- HRangeAnalysis range_analysis(this);
- range_analysis.Analyze();
- }
+ if (FLAG_use_range) Run<HRangeAnalysisPhase>();
+
ComputeMinusZeroChecks();
// Eliminate redundant stack checks on backwards branches.
@@ -4681,59 +4525,6 @@
}
-bool HOptimizedGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
- return statement->OsrEntryId() == current_info()->osr_ast_id();
-}
-
-
-bool HOptimizedGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
- if (!HasOsrEntryAt(statement)) return false;
-
- HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
- HBasicBlock* osr_entry = graph()->CreateBasicBlock();
- HValue* true_value = graph()->GetConstantTrue();
- HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
- current_block()->Finish(test);
-
- HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
- non_osr_entry->Goto(loop_predecessor);
-
- set_current_block(osr_entry);
- osr_entry->set_osr_entry();
- BailoutId osr_entry_id = statement->OsrEntryId();
- int first_expression_index = environment()->first_expression_index();
- int length = environment()->length();
- ZoneList<HUnknownOSRValue*>* osr_values =
- new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
-
- for (int i = 0; i < first_expression_index; ++i) {
- HUnknownOSRValue* osr_value = Add<HUnknownOSRValue>();
- environment()->Bind(i, osr_value);
- osr_values->Add(osr_value, zone());
- }
-
- if (first_expression_index != length) {
- environment()->Drop(length - first_expression_index);
- for (int i = first_expression_index; i < length; ++i) {
- HUnknownOSRValue* osr_value = Add<HUnknownOSRValue>();
- environment()->Push(osr_value);
- osr_values->Add(osr_value, zone());
- }
- }
-
- graph()->set_osr_values(osr_values);
-
- AddSimulate(osr_entry_id);
- Add<HOsrEntry>(osr_entry_id);
- HContext* context = Add<HContext>();
- environment()->BindContext(context);
- current_block()->Goto(loop_predecessor);
- loop_predecessor->SetJoinId(statement->EntryId());
- set_current_block(loop_predecessor);
- return true;
-}
-
-
void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
HBasicBlock* loop_entry,
BreakAndContinueInfo* break_info) {
@@ -4753,11 +4544,7 @@
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
- bool osr_entry = PreProcessOsrEntry(stmt);
- HBasicBlock* loop_entry = CreateLoopHeaderBlock();
- current_block()->Goto(loop_entry);
- set_current_block(loop_entry);
- if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
BreakAndContinueInfo break_info(stmt);
CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
@@ -4796,12 +4583,7 @@
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
- bool osr_entry = PreProcessOsrEntry(stmt);
- HBasicBlock* loop_entry = CreateLoopHeaderBlock();
- current_block()->Goto(loop_entry);
- set_current_block(loop_entry);
- if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
-
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
// If the condition is constant true, do not generate a branch.
HBasicBlock* loop_successor = NULL;
@@ -4843,11 +4625,7 @@
CHECK_ALIVE(Visit(stmt->init()));
}
ASSERT(current_block() != NULL);
- bool osr_entry = PreProcessOsrEntry(stmt);
- HBasicBlock* loop_entry = CreateLoopHeaderBlock();
- current_block()->Goto(loop_entry);
- set_current_block(loop_entry);
- if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
HBasicBlock* loop_successor = NULL;
if (stmt->cond() != NULL) {
@@ -4931,11 +4709,7 @@
HForInCacheArray::cast(array)->set_index_cache(
HForInCacheArray::cast(index_cache));
- bool osr_entry = PreProcessOsrEntry(stmt);
- HBasicBlock* loop_entry = CreateLoopHeaderBlock();
- current_block()->Goto(loop_entry);
- set_current_block(loop_entry);
- if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
HValue* index = environment()->ExpressionStackAt(0);
HValue* limit = environment()->ExpressionStackAt(1);
@@ -5544,7 +5318,9 @@
Handle<Object> raw_boilerplate(literals->get(expr->literal_index()),
isolate());
+ bool uninitialized = false;
if (raw_boilerplate->IsUndefined()) {
+ uninitialized = true;
raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
isolate(), literals, expr->constant_elements());
if (raw_boilerplate.is_null()) {
@@ -5640,10 +5416,12 @@
case FAST_ELEMENTS:
case FAST_HOLEY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- Add<HStoreKeyed>(elements, key, value,
- boilerplate_elements_kind);
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ HStoreKeyed* instr = Add<HStoreKeyed>(elements, key, value,
+ boilerplate_elements_kind);
+ instr->SetUninitialized(uninitialized);
break;
+ }
default:
UNREACHABLE();
break;
@@ -6809,7 +6587,7 @@
LAST_ELEMENTS_KIND);
if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
&& todo_external_array) {
- HInstruction* length = Add<HFixedArrayBaseLength>(elements);
+ HInstruction* length = AddLoadFixedArrayLength(elements);
checked_key = Add<HBoundsCheck>(key, length);
external_elements = Add<HLoadExternalArrayPointer>(elements);
}
@@ -6869,7 +6647,7 @@
if_jsarray->GotoNoSimulate(join);
set_current_block(if_fastobject);
- length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
+ length = AddLoadFixedArrayLength(elements);
checked_key = Add<HBoundsCheck>(key, length);
access = AddInstruction(BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
@@ -9244,12 +9022,6 @@
Representation combined_rep = ToRepresentation(combined_type);
Representation left_rep = ToRepresentation(left_type);
Representation right_rep = ToRepresentation(right_type);
- // Check if this expression was ever executed according to type feedback.
- // Note that for the special typeof/null/undefined cases we get unknown here.
- if (combined_type->Is(Type::None())) {
- AddSoftDeoptimize();
- combined_type = left_type = right_type = handle(Type::Any(), isolate());
- }
CHECK_ALIVE(VisitForValue(expr->left()));
CHECK_ALIVE(VisitForValue(expr->right()));
@@ -9316,11 +9088,23 @@
result->set_position(expr->position());
return ast_context()->ReturnInstruction(result, expr->id());
}
+
+ // Code below assumes that we don't fall through.
+ UNREACHABLE();
} else if (op == Token::IN) {
HIn* result = new(zone()) HIn(context, left, right);
result->set_position(expr->position());
return ast_context()->ReturnInstruction(result, expr->id());
- } else if (combined_type->Is(Type::Receiver())) {
+ }
+
+ // Cases handled below depend on collected type feedback. They should
+ // soft deoptimize when there is no type feedback.
+ if (combined_type->Is(Type::None())) {
+ AddSoftDeoptimize();
+ combined_type = left_type = right_type = handle(Type::Any(), isolate());
+ }
+
+ if (combined_type->Is(Type::Receiver())) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 3ea3262..f80aca1 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -46,6 +46,7 @@
class HEnvironment;
class HGraph;
class HLoopInformation;
+class HOsrBuilder;
class HTracer;
class LAllocator;
class LChunk;
@@ -358,24 +359,16 @@
void Verify(bool do_full_verify) const;
#endif
- bool has_osr_loop_entry() {
- return osr_loop_entry_.is_set();
+ bool has_osr() {
+ return osr_ != NULL;
}
- HBasicBlock* osr_loop_entry() {
- return osr_loop_entry_.get();
+ void set_osr(HOsrBuilder* osr) {
+ osr_ = osr;
}
- void set_osr_loop_entry(HBasicBlock* entry) {
- osr_loop_entry_.set(entry);
- }
-
- ZoneList<HUnknownOSRValue*>* osr_values() {
- return osr_values_.get();
- }
-
- void set_osr_values(ZoneList<HUnknownOSRValue*>* values) {
- osr_values_.set(values);
+ HOsrBuilder* osr() {
+ return osr_;
}
int update_type_change_checksum(int delta) {
@@ -495,8 +488,7 @@
SetOncePointer<HConstant> constant_invalid_context_;
SetOncePointer<HArgumentsObject> arguments_object_;
- SetOncePointer<HBasicBlock> osr_loop_entry_;
- SetOncePointer<ZoneList<HUnknownOSRValue*> > osr_values_;
+ HOsrBuilder* osr_;
CompilationInfo* info_;
Zone* zone_;
@@ -1135,6 +1127,8 @@
HLoadNamedField* AddLoadElements(HValue *object, HValue *typecheck = NULL);
+ HLoadNamedField* AddLoadFixedArrayLength(HValue *object);
+
class IfBuilder {
public:
explicit IfBuilder(HGraphBuilder* builder,
@@ -1436,7 +1430,6 @@
int no_side_effects_scope_count_;
};
-
class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
public:
// A class encapsulating (lazily-allocated) break and continue blocks for
@@ -1594,8 +1587,6 @@
void VisitArithmeticExpression(BinaryOperation* expr);
bool PreProcessOsrEntry(IterationStatement* statement);
- // True iff. we are compiling for OSR and the statement is the entry.
- bool HasOsrEntryAt(IterationStatement* statement);
void VisitLoopBody(IterationStatement* stmt,
HBasicBlock* loop_entry,
BreakAndContinueInfo* break_info);
@@ -1957,9 +1948,12 @@
bool inline_bailout_;
+ HOsrBuilder* osr_;
+
friend class FunctionState; // Pushes and pops the state stack.
friend class AstContext; // Pushes and pops the AST context stack.
friend class KeyedLoadFastElementStub;
+ friend class HOsrBuilder;
DISALLOW_COPY_AND_ASSIGN(HOptimizedGraphBuilder);
};
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 96fb98e..defae1c 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -1773,14 +1773,6 @@
}
-void LCodeGen::DoFixedArrayBaseLength(
- LFixedArrayBaseLength* instr) {
- Register result = ToRegister(instr->result());
- Register array = ToRegister(instr->value());
- __ mov(result, FieldOperand(array, FixedArrayBase::kLengthOffset));
-}
-
-
void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->value());
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index c01cf8c..8231c4e 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1816,13 +1816,6 @@
}
-LInstruction* LChunkBuilder::DoFixedArrayBaseLength(
- HFixedArrayBaseLength* instr) {
- LOperand* array = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
-}
-
-
LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
LOperand* map = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LMapEnumLength(map));
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index 59c6c81..e48e881 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -94,7 +94,6 @@
V(DoubleToSmi) \
V(DummyUse) \
V(ElementsKind) \
- V(FixedArrayBaseLength) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
@@ -1251,20 +1250,6 @@
};
-class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> {
- public:
- explicit LFixedArrayBaseLength(LOperand* value) {
- inputs_[0] = value;
- }
-
- LOperand* value() { return inputs_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength,
- "fixed-array-base-length")
- DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength)
-};
-
-
class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LMapEnumLength(LOperand* value) {
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 63477aa..a9a0268 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -31,6 +31,7 @@
#include "bootstrapper.h"
#include "codegen.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "runtime.h"
#include "serialize.h"
diff --git a/src/ia32/regexp-macro-assembler-ia32.cc b/src/ia32/regexp-macro-assembler-ia32.cc
index 54fe8bf..f478e57 100644
--- a/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/src/ia32/regexp-macro-assembler-ia32.cc
@@ -29,6 +29,7 @@
#if V8_TARGET_ARCH_IA32
+#include "cpu-profiler.h"
#include "unicode.h"
#include "log.h"
#include "regexp-stack.h"
diff --git a/src/isolate.cc b/src/isolate.cc
index fec3dc6..6a87580 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -34,6 +34,7 @@
#include "bootstrapper.h"
#include "codegen.h"
#include "compilation-cache.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "deoptimizer.h"
#include "heap-profiler.h"
@@ -46,6 +47,7 @@
#include "platform.h"
#include "regexp-stack.h"
#include "runtime-profiler.h"
+#include "sampler.h"
#include "scopeinfo.h"
#include "serialize.h"
#include "simulator.h"
@@ -1762,6 +1764,7 @@
descriptor_lookup_cache_(NULL),
handle_scope_implementer_(NULL),
unicode_cache_(NULL),
+ runtime_zone_(this),
in_use_list_(0),
free_list_(0),
preallocated_storage_preallocated_(false),
@@ -1960,6 +1963,9 @@
Isolate::~Isolate() {
TRACE_ISOLATE(destructor);
+ // Has to be called while counters_ are still alive
+ runtime_zone_.DeleteKeptSegment();
+
// The entry stack must be empty when we get here,
// except for the default isolate, where it can
// still contain up to one entry stack item
diff --git a/src/isolate.h b/src/isolate.h
index f7d40dd..a0aecd8 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -896,6 +896,7 @@
ASSERT(handle_scope_implementer_);
return handle_scope_implementer_;
}
+ Zone* runtime_zone() { return &runtime_zone_; }
UnicodeCache* unicode_cache() {
return unicode_cache_;
@@ -1270,6 +1271,7 @@
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
HandleScopeImplementer* handle_scope_implementer_;
UnicodeCache* unicode_cache_;
+ Zone runtime_zone_;
PreallocatedStorage in_use_list_;
PreallocatedStorage free_list_;
bool preallocated_storage_preallocated_;
diff --git a/src/log-inl.h b/src/log-inl.h
index 8aebbc7..7f653cb 100644
--- a/src/log-inl.h
+++ b/src/log-inl.h
@@ -29,7 +29,6 @@
#define V8_LOG_INL_H_
#include "log.h"
-#include "cpu-profiler.h"
namespace v8 {
namespace internal {
diff --git a/src/log.cc b/src/log.cc
index 82ce886..e95b963 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -31,6 +31,7 @@
#include "bootstrapper.h"
#include "code-stubs.h"
+#include "cpu-profiler.h"
#include "deoptimizer.h"
#include "global-handles.h"
#include "log.h"
@@ -637,6 +638,16 @@
}
+void Logger::CodeDeoptEvent(Code* code) {
+ if (!log_->IsEnabled()) return;
+ ASSERT(FLAG_log_internal_timer_events);
+ LogMessageBuilder msg(this);
+ int since_epoch = static_cast<int>(OS::Ticks() - epoch_);
+ msg.Append("code-deopt,%ld,%d\n", since_epoch, code->CodeSize());
+ msg.WriteToLogFile();
+}
+
+
void Logger::TimerEvent(StartEnd se, const char* name) {
if (!log_->IsEnabled()) return;
ASSERT(FLAG_log_internal_timer_events);
@@ -1041,7 +1052,7 @@
}
if (!FLAG_log_code || !log_->IsEnabled()) return;
- if (code == Isolate::Current()->builtins()->builtin(
+ if (code == isolate_->builtins()->builtin(
Builtins::kLazyCompile))
return;
@@ -1353,8 +1364,6 @@
LogMessageBuilder msg(this);
msg.Append("%s,", kLogEventsNames[TICK_EVENT]);
msg.AppendAddress(sample->pc);
- msg.Append(',');
- msg.AppendAddress(sample->sp);
msg.Append(",%ld", static_cast<int>(OS::Ticks() - epoch_));
if (sample->has_external_callback) {
msg.Append(",1,");
@@ -1698,7 +1707,7 @@
// During iteration, there can be heap allocation due to
// GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) {
- if (*code_objects[i] == Isolate::Current()->builtins()->builtin(
+ if (*code_objects[i] == isolate_->builtins()->builtin(
Builtins::kLazyCompile))
continue;
LogExistingFunction(sfis[i], code_objects[i]);
@@ -1778,7 +1787,7 @@
code_event_handler_ = event_handler;
if (code_event_handler_ != NULL && (options & kJitCodeEventEnumExisting)) {
- HandleScope scope(Isolate::Current());
+ HandleScope scope(isolate_);
LogCodeObjects();
LogCompiledFunctions();
}
diff --git a/src/log.h b/src/log.h
index f6e2ed9..07ecd0e 100644
--- a/src/log.h
+++ b/src/log.h
@@ -293,6 +293,8 @@
// ==== Events logged by --log-timer-events. ====
enum StartEnd { START, END };
+ void CodeDeoptEvent(Code* code);
+
void TimerEvent(StartEnd se, const char* name);
static void EnterExternal(Isolate* isolate);
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 3940b56..6389685 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -29,6 +29,7 @@
#include "code-stubs.h"
#include "compilation-cache.h"
+#include "cpu-profiler.h"
#include "deoptimizer.h"
#include "execution.h"
#include "gdb-jit.h"
@@ -2181,6 +2182,24 @@
}
+void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor) {
+ for (StackFrameIterator it(isolate(), isolate()->thread_local_top());
+ !it.done(); it.Advance()) {
+ if (it.frame()->type() == StackFrame::JAVA_SCRIPT) {
+ return;
+ }
+ if (it.frame()->type() == StackFrame::OPTIMIZED) {
+ Code* code = it.frame()->LookupCode();
+ if (!code->CanDeoptAt(it.frame()->pc())) {
+ code->CodeIterateBody(visitor);
+ }
+ ProcessMarkingDeque();
+ return;
+ }
+ }
+}
+
+
void MarkCompactCollector::MarkLiveObjects() {
GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_MARK);
// The recursive GC marker detects when it is nearing stack overflow,
@@ -2260,6 +2279,8 @@
RootMarkingVisitor root_visitor(heap());
MarkRoots(&root_visitor);
+ ProcessTopOptimizedFrame(&root_visitor);
+
// The objects reachable from the roots are marked, yet unreachable
// objects are unmarked. Mark objects reachable due to host
// application specific logic or through Harmony weak maps.
@@ -3117,6 +3138,11 @@
Address free_end = object_address + offsets[live_index++] * kPointerSize;
if (free_end != free_start) {
space->Free(free_start, static_cast<int>(free_end - free_start));
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (FLAG_gdbjit && space->identity() == CODE_SPACE) {
+ GDBJITInterface::RemoveCodeRange(free_start, free_end);
+ }
+#endif
}
HeapObject* live_object = HeapObject::FromAddress(free_end);
ASSERT(Marking::IsBlack(Marking::MarkBitFrom(live_object)));
@@ -3143,6 +3169,11 @@
}
if (free_start != p->area_end()) {
space->Free(free_start, static_cast<int>(p->area_end() - free_start));
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (FLAG_gdbjit && space->identity() == CODE_SPACE) {
+ GDBJITInterface::RemoveCodeRange(free_start, p->area_end());
+ }
+#endif
}
p->ResetLiveBytes();
if (FLAG_print_cumulative_gc_stat) {
diff --git a/src/mark-compact.h b/src/mark-compact.h
index db188ed..ab3711a 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -853,6 +853,11 @@
// or implicit references' groups.
void ProcessEphemeralMarking(ObjectVisitor* visitor);
+ // If the call-site of the top optimized code was not prepared for
+ // deoptimization, then treat the maps in the code as strong pointers,
+ // otherwise a map can die and deoptimize the code.
+ void ProcessTopOptimizedFrame(ObjectVisitor* visitor);
+
// Mark objects reachable (transitively) from objects in the marking
// stack. This function empties the marking stack, but may leave
// overflowed objects in the heap, in which case the marking stack's
diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc
old mode 100644
new mode 100755
index fd35a35..35d21f0
--- a/src/mips/builtins-mips.cc
+++ b/src/mips/builtins-mips.cc
@@ -273,7 +273,7 @@
__ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0);
{
FrameScope scope(masm, StackFrame::INTERNAL);
- __ push(v0);
+ __ push(a0);
__ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
}
__ pop(function);
diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc
index 3e55923..8109e8a 100644
--- a/src/mips/lithium-codegen-mips.cc
+++ b/src/mips/lithium-codegen-mips.cc
@@ -1675,13 +1675,6 @@
}
-void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) {
- Register result = ToRegister(instr->result());
- Register array = ToRegister(instr->value());
- __ lw(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset));
-}
-
-
void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->value());
diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc
index e68c722..638eaa4 100644
--- a/src/mips/lithium-mips.cc
+++ b/src/mips/lithium-mips.cc
@@ -1716,13 +1716,6 @@
}
-LInstruction* LChunkBuilder::DoFixedArrayBaseLength(
- HFixedArrayBaseLength* instr) {
- LOperand* array = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
-}
-
-
LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
LOperand* map = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LMapEnumLength(map));
diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h
index 3e94301..06d30d0 100644
--- a/src/mips/lithium-mips.h
+++ b/src/mips/lithium-mips.h
@@ -99,7 +99,6 @@
V(DoubleToSmi) \
V(DummyUse) \
V(ElementsKind) \
- V(FixedArrayBaseLength) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
@@ -1260,20 +1259,6 @@
};
-class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> {
- public:
- explicit LFixedArrayBaseLength(LOperand* value) {
- inputs_[0] = value;
- }
-
- LOperand* value() { return inputs_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength,
- "fixed-array-base-length")
- DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength)
-};
-
-
class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LMapEnumLength(LOperand* value) {
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
index 5890347..47e6ff9 100644
--- a/src/mips/macro-assembler-mips.cc
+++ b/src/mips/macro-assembler-mips.cc
@@ -33,6 +33,7 @@
#include "bootstrapper.h"
#include "codegen.h"
+#include "cpu-profiler.h"
#include "debug.h"
#include "runtime.h"
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 3ac475b..a8d9b35 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -312,6 +312,9 @@
// By default, log code create information in the snapshot.
i::FLAG_log_code = true;
+ // Disable the i18n extension, as it doesn't support being snapshotted yet.
+ i::FLAG_enable_i18n = false;
+
// Print the usage if an error occurs when parsing the command line
// flags or if the help flag is set.
int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
diff --git a/src/natives.h b/src/natives.h
index 5f34420..e3f69d1 100644
--- a/src/natives.h
+++ b/src/natives.h
@@ -36,7 +36,7 @@
int index);
enum NativeType {
- CORE, EXPERIMENTAL, D8, TEST
+ CORE, EXPERIMENTAL, D8, TEST, I18N
};
template <NativeType type>
@@ -61,6 +61,7 @@
typedef NativesCollection<CORE> Natives;
typedef NativesCollection<EXPERIMENTAL> ExperimentalNatives;
+typedef NativesCollection<I18N> I18NNatives;
} } // namespace v8::internal
diff --git a/src/objects-inl.h b/src/objects-inl.h
index ea347fd..fe054da 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -4285,6 +4285,7 @@
MaybeObject* Map::SetPrototypeTransitions(FixedArray* proto_transitions) {
MaybeObject* allow_prototype = EnsureHasTransitionArray(this);
if (allow_prototype->IsFailure()) return allow_prototype;
+ int old_number_of_transitions = NumberOfProtoTransitions();
#ifdef DEBUG
if (HasPrototypeTransitions()) {
ASSERT(GetPrototypeTransitions() != proto_transitions);
@@ -4292,6 +4293,7 @@
}
#endif
transitions()->SetPrototypeTransitions(proto_transitions);
+ SetNumberOfProtoTransitions(old_number_of_transitions);
return this;
}
diff --git a/src/objects.cc b/src/objects.cc
index d1a617e..aa67876 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -39,6 +39,7 @@
#include "execution.h"
#include "full-codegen.h"
#include "hydrogen.h"
+#include "isolate-inl.h"
#include "objects-inl.h"
#include "objects-visiting.h"
#include "objects-visiting-inl.h"
@@ -5838,11 +5839,12 @@
}
-MaybeObject* JSObject::DefineElementAccessor(uint32_t index,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes) {
- switch (GetElementsKind()) {
+void JSObject::DefineElementAccessor(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
+ switch (object->GetElementsKind()) {
case FAST_SMI_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
@@ -5860,21 +5862,21 @@
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
// Ignore getters and setters on pixel and external array elements.
- return GetHeap()->undefined_value();
+ return;
case DICTIONARY_ELEMENTS:
- if (UpdateGetterSetterInDictionary(element_dictionary(),
+ if (UpdateGetterSetterInDictionary(object->element_dictionary(),
index,
- getter,
- setter,
+ *getter,
+ *setter,
attributes)) {
- return GetHeap()->undefined_value();
+ return;
}
break;
case NON_STRICT_ARGUMENTS_ELEMENTS: {
// Ascertain whether we have read-only properties or an existing
// getter/setter pair in an arguments elements dictionary backing
// store.
- FixedArray* parameter_map = FixedArray::cast(elements());
+ FixedArray* parameter_map = FixedArray::cast(object->elements());
uint32_t length = parameter_map->length();
Object* probe =
index < (length - 2) ? parameter_map->get(index + 2) : NULL;
@@ -5885,10 +5887,10 @@
SeededNumberDictionary::cast(arguments);
if (UpdateGetterSetterInDictionary(dictionary,
index,
- getter,
- setter,
+ *getter,
+ *setter,
attributes)) {
- return GetHeap()->undefined_value();
+ return;
}
}
}
@@ -5896,19 +5898,20 @@
}
}
- AccessorPair* accessors;
- { MaybeObject* maybe_accessors = GetHeap()->AllocateAccessorPair();
- if (!maybe_accessors->To(&accessors)) return maybe_accessors;
- }
- accessors->SetComponents(getter, setter);
+ Isolate* isolate = object->GetIsolate();
+ Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair();
+ accessors->SetComponents(*getter, *setter);
- return SetElementCallback(index, accessors, attributes);
+ CALL_HEAP_FUNCTION_VOID(
+ isolate, object->SetElementCallback(index, *accessors, attributes));
}
-MaybeObject* JSObject::CreateAccessorPairFor(Name* name) {
- LookupResult result(GetHeap()->isolate());
- LocalLookupRealNamedProperty(name, &result);
+Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object,
+ Handle<Name> name) {
+ Isolate* isolate = object->GetIsolate();
+ LookupResult result(isolate);
+ object->LocalLookupRealNamedProperty(*name, &result);
if (result.IsPropertyCallbacks()) {
// Note that the result can actually have IsDontDelete() == true when we
// e.g. have to fall back to the slow case while adding a setter after
@@ -5918,47 +5921,37 @@
// DefinePropertyAccessor below.
Object* obj = result.GetCallbackObject();
if (obj->IsAccessorPair()) {
- return AccessorPair::cast(obj)->Copy();
+ return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate));
}
}
- return GetHeap()->AllocateAccessorPair();
+ return isolate->factory()->NewAccessorPair();
}
-MaybeObject* JSObject::DefinePropertyAccessor(Name* name,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes) {
+void JSObject::DefinePropertyAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
// We could assert that the property is configurable here, but we would need
// to do a lookup, which seems to be a bit of overkill.
- Heap* heap = GetHeap();
bool only_attribute_changes = getter->IsNull() && setter->IsNull();
- if (HasFastProperties() && !only_attribute_changes &&
- (map()->NumberOfOwnDescriptors() <
+ if (object->HasFastProperties() && !only_attribute_changes &&
+ (object->map()->NumberOfOwnDescriptors() <
DescriptorArray::kMaxNumberOfDescriptors)) {
- MaybeObject* getterOk = heap->undefined_value();
- if (!getter->IsNull()) {
- getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes);
- if (getterOk->IsFailure()) return getterOk;
- }
-
- MaybeObject* setterOk = heap->undefined_value();
- if (getterOk != heap->null_value() && !setter->IsNull()) {
- setterOk = DefineFastAccessor(name, ACCESSOR_SETTER, setter, attributes);
- if (setterOk->IsFailure()) return setterOk;
- }
-
- if (getterOk != heap->null_value() && setterOk != heap->null_value()) {
- return heap->undefined_value();
- }
+ bool getterOk = getter->IsNull() ||
+ DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes);
+ bool setterOk = !getterOk || setter->IsNull() ||
+ DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes);
+ if (getterOk && setterOk) return;
}
- AccessorPair* accessors;
- MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
- if (!maybe_accessors->To(&accessors)) return maybe_accessors;
+ Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name);
+ accessors->SetComponents(*getter, *setter);
- accessors->SetComponents(getter, setter);
- return SetPropertyCallback(name, accessors, attributes);
+ CALL_HEAP_FUNCTION_VOID(
+ object->GetIsolate(),
+ object->SetPropertyCallback(*name, *accessors, attributes));
}
@@ -6060,29 +6053,21 @@
Handle<Object> getter,
Handle<Object> setter,
PropertyAttributes attributes) {
- CALL_HEAP_FUNCTION_VOID(
- object->GetIsolate(),
- object->DefineAccessor(*name, *getter, *setter, attributes));
-}
-
-MaybeObject* JSObject::DefineAccessor(Name* name_raw,
- Object* getter_raw,
- Object* setter_raw,
- PropertyAttributes attributes) {
- Isolate* isolate = GetIsolate();
+ Isolate* isolate = object->GetIsolate();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
- isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
- return isolate->heap()->undefined_value();
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*object, *name, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(*object, v8::ACCESS_SET);
+ return;
}
- if (IsJSGlobalProxy()) {
- Object* proto = GetPrototype();
- if (proto->IsNull()) return this;
+ if (object->IsJSGlobalProxy()) {
+ Handle<Object> proto(object->GetPrototype(), isolate);
+ if (proto->IsNull()) return;
ASSERT(proto->IsJSGlobalObject());
- return JSObject::cast(proto)->DefineAccessor(
- name_raw, getter_raw, setter_raw, attributes);
+ DefineAccessor(
+ Handle<JSObject>::cast(proto), name, getter, setter, attributes);
+ return;
}
// Make sure that the top context does not change when doing callbacks or
@@ -6090,68 +6075,58 @@
AssertNoContextChange ncc;
// Try to flatten before operating on the string.
- if (name_raw->IsString()) String::cast(name_raw)->TryFlatten();
+ if (name->IsString()) String::cast(*name)->TryFlatten();
- if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value();
-
- // From this point on everything needs to be handlified.
- HandleScope scope(isolate);
- Handle<JSObject> self(this);
- Handle<Name> name(name_raw);
- Handle<Object> getter(getter_raw, isolate);
- Handle<Object> setter(setter_raw, isolate);
+ if (!object->CanSetCallback(*name)) return;
uint32_t index = 0;
bool is_element = name->AsArrayIndex(&index);
Handle<Object> old_value = isolate->factory()->the_hole_value();
- bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
+ bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
bool preexists = false;
if (is_observed) {
if (is_element) {
- preexists = HasLocalElement(index);
- if (preexists && self->GetLocalElementAccessorPair(index) == NULL) {
- old_value = Object::GetElement(self, index);
+ preexists = object->HasLocalElement(index);
+ if (preexists && object->GetLocalElementAccessorPair(index) == NULL) {
+ old_value = Object::GetElement(object, index);
}
} else {
LookupResult lookup(isolate);
- LocalLookup(*name, &lookup, true);
+ object->LocalLookup(*name, &lookup, true);
preexists = lookup.IsProperty();
if (preexists && lookup.IsDataProperty()) {
- old_value = Object::GetProperty(self, name);
+ old_value = Object::GetProperty(object, name);
}
}
}
- MaybeObject* result = is_element ?
- self->DefineElementAccessor(index, *getter, *setter, attributes) :
- self->DefinePropertyAccessor(*name, *getter, *setter, attributes);
-
- Handle<Object> hresult;
- if (!result->ToHandle(&hresult, isolate)) return result;
+ if (is_element) {
+ DefineElementAccessor(object, index, getter, setter, attributes);
+ } else {
+ DefinePropertyAccessor(object, name, getter, setter, attributes);
+ }
if (is_observed) {
const char* type = preexists ? "reconfigured" : "new";
- EnqueueChangeRecord(self, type, name, old_value);
+ EnqueueChangeRecord(object, type, name, old_value);
}
-
- return *hresult;
}
-static MaybeObject* TryAccessorTransition(JSObject* self,
- Map* transitioned_map,
- int target_descriptor,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes) {
+static bool TryAccessorTransition(JSObject* self,
+ Map* transitioned_map,
+ int target_descriptor,
+ AccessorComponent component,
+ Object* accessor,
+ PropertyAttributes attributes) {
DescriptorArray* descs = transitioned_map->instance_descriptors();
PropertyDetails details = descs->GetDetails(target_descriptor);
// If the transition target was not callbacks, fall back to the slow case.
- if (details.type() != CALLBACKS) return self->GetHeap()->null_value();
+ if (details.type() != CALLBACKS) return false;
Object* descriptor = descs->GetCallbacksObject(target_descriptor);
- if (!descriptor->IsAccessorPair()) return self->GetHeap()->null_value();
+ if (!descriptor->IsAccessorPair()) return false;
Object* target_accessor = AccessorPair::cast(descriptor)->get(component);
PropertyAttributes target_attributes = details.attributes();
@@ -6159,25 +6134,46 @@
// Reuse transition if adding same accessor with same attributes.
if (target_accessor == accessor && target_attributes == attributes) {
self->set_map(transitioned_map);
- return self;
+ return true;
}
// If either not the same accessor, or not the same attributes, fall back to
// the slow case.
- return self->GetHeap()->null_value();
+ return false;
}
-MaybeObject* JSObject::DefineFastAccessor(Name* name,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes) {
+static MaybeObject* CopyInsertDescriptor(Map* map,
+ Name* name,
+ AccessorPair* accessors,
+ PropertyAttributes attributes) {
+ CallbacksDescriptor new_accessors_desc(name, accessors, attributes);
+ return map->CopyInsertDescriptor(&new_accessors_desc, INSERT_TRANSITION);
+}
+
+
+static Handle<Map> CopyInsertDescriptor(Handle<Map> map,
+ Handle<Name> name,
+ Handle<AccessorPair> accessors,
+ PropertyAttributes attributes) {
+ CALL_HEAP_FUNCTION(map->GetIsolate(),
+ CopyInsertDescriptor(*map, *name, *accessors, attributes),
+ Map);
+}
+
+
+bool JSObject::DefineFastAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ AccessorComponent component,
+ Handle<Object> accessor,
+ PropertyAttributes attributes) {
ASSERT(accessor->IsSpecFunction() || accessor->IsUndefined());
- LookupResult result(GetIsolate());
- LocalLookup(name, &result);
+ Isolate* isolate = object->GetIsolate();
+ LookupResult result(isolate);
+ object->LocalLookup(*name, &result);
if (result.IsFound() && !result.IsPropertyCallbacks()) {
- return GetHeap()->null_value();
+ return false;
}
// Return success if the same accessor with the same attributes already exist.
@@ -6187,65 +6183,53 @@
if (callback_value->IsAccessorPair()) {
source_accessors = AccessorPair::cast(callback_value);
Object* entry = source_accessors->get(component);
- if (entry == accessor && result.GetAttributes() == attributes) {
- return this;
+ if (entry == *accessor && result.GetAttributes() == attributes) {
+ return true;
}
} else {
- return GetHeap()->null_value();
+ return false;
}
int descriptor_number = result.GetDescriptorIndex();
- map()->LookupTransition(this, name, &result);
+ object->map()->LookupTransition(*object, *name, &result);
if (result.IsFound()) {
Map* target = result.GetTransitionTarget();
ASSERT(target->NumberOfOwnDescriptors() ==
- map()->NumberOfOwnDescriptors());
+ object->map()->NumberOfOwnDescriptors());
// This works since descriptors are sorted in order of addition.
- ASSERT(map()->instance_descriptors()->GetKey(descriptor_number) == name);
- return TryAccessorTransition(
- this, target, descriptor_number, component, accessor, attributes);
+ ASSERT(object->map()->instance_descriptors()->
+ GetKey(descriptor_number) == *name);
+ return TryAccessorTransition(*object, target, descriptor_number,
+ component, *accessor, attributes);
}
} else {
// If not, lookup a transition.
- map()->LookupTransition(this, name, &result);
+ object->map()->LookupTransition(*object, *name, &result);
// If there is a transition, try to follow it.
if (result.IsFound()) {
Map* target = result.GetTransitionTarget();
int descriptor_number = target->LastAdded();
ASSERT(target->instance_descriptors()->GetKey(descriptor_number)
- ->Equals(name));
- return TryAccessorTransition(
- this, target, descriptor_number, component, accessor, attributes);
+ ->Equals(*name));
+ return TryAccessorTransition(*object, target, descriptor_number,
+ component, *accessor, attributes);
}
}
// If there is no transition yet, add a transition to the a new accessor pair
- // containing the accessor.
- AccessorPair* accessors;
- MaybeObject* maybe_accessors;
-
- // Allocate a new pair if there were no source accessors. Otherwise, copy the
- // pair and modify the accessor.
- if (source_accessors != NULL) {
- maybe_accessors = source_accessors->Copy();
- } else {
- maybe_accessors = GetHeap()->AllocateAccessorPair();
- }
- if (!maybe_accessors->To(&accessors)) return maybe_accessors;
- accessors->set(component, accessor);
-
- CallbacksDescriptor new_accessors_desc(name, accessors, attributes);
-
- Map* new_map;
- MaybeObject* maybe_new_map =
- map()->CopyInsertDescriptor(&new_accessors_desc, INSERT_TRANSITION);
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
-
- set_map(new_map);
- return this;
+ // containing the accessor. Allocate a new pair if there were no source
+ // accessors. Otherwise, copy the pair and modify the accessor.
+ Handle<AccessorPair> accessors = source_accessors != NULL
+ ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors))
+ : isolate->factory()->NewAccessorPair();
+ accessors->set(component, *accessor);
+ Handle<Map> new_map = CopyInsertDescriptor(Handle<Map>(object->map()),
+ name, accessors, attributes);
+ object->set_map(*new_map);
+ return true;
}
@@ -6689,6 +6673,11 @@
}
+Handle<Map> Map::Copy(Handle<Map> map) {
+ CALL_HEAP_FUNCTION(map->GetIsolate(), map->Copy(), Map);
+}
+
+
MaybeObject* Map::Copy() {
DescriptorArray* descriptors = instance_descriptors();
DescriptorArray* new_descriptors;
@@ -7820,14 +7809,10 @@
}
-MaybeObject* AccessorPair::Copy() {
- Heap* heap = GetHeap();
- AccessorPair* copy;
- MaybeObject* maybe_copy = heap->AllocateAccessorPair();
- if (!maybe_copy->To(©)) return maybe_copy;
-
- copy->set_getter(getter());
- copy->set_setter(setter());
+Handle<AccessorPair> AccessorPair::Copy(Handle<AccessorPair> pair) {
+ Handle<AccessorPair> copy = pair->GetIsolate()->factory()->NewAccessorPair();
+ copy->set_getter(pair->getter());
+ copy->set_setter(pair->setter());
return copy;
}
@@ -9140,7 +9125,7 @@
void JSFunction::MarkForLazyRecompilation() {
- ASSERT(is_compiled() || GetIsolate()->debugger()->IsDebuggerActive());
+ ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints());
ASSERT(!IsOptimized());
ASSERT(shared()->allows_lazy_compilation() ||
code()->optimizable());
@@ -9151,7 +9136,7 @@
void JSFunction::MarkForParallelRecompilation() {
- ASSERT(is_compiled() || GetIsolate()->debugger()->IsDebuggerActive());
+ ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints());
ASSERT(!IsOptimized());
ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
if (!FLAG_parallel_recompilation) {
@@ -9170,7 +9155,9 @@
void JSFunction::MarkForInstallingRecompiledCode() {
- ASSERT(is_compiled() || GetIsolate()->debugger()->IsDebuggerActive());
+ // The debugger could have switched the builtin to lazy compile.
+ // In that case, simply carry on. It will be dealt with later.
+ ASSERT(IsInRecompileQueue() || GetIsolate()->DebuggerHasBreakPoints());
ASSERT(!IsOptimized());
ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
ASSERT(FLAG_parallel_recompilation);
@@ -9181,8 +9168,10 @@
void JSFunction::MarkInRecompileQueue() {
- ASSERT(is_compiled() || GetIsolate()->debugger()->IsDebuggerActive());
- ASSERT(!IsOptimized());
+ // We can only arrive here via the parallel-recompilation builtin. If
+ // break points were set, the code would point to the lazy-compile builtin.
+ ASSERT(!GetIsolate()->DebuggerHasBreakPoints());
+ ASSERT(IsMarkedForParallelRecompilation() && !IsOptimized());
ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
ASSERT(FLAG_parallel_recompilation);
if (FLAG_trace_parallel_recompilation) {
@@ -9400,6 +9389,11 @@
}
+void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
+ CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), object->OptimizeAsPrototype());
+}
+
+
MaybeObject* JSObject::OptimizeAsPrototype() {
if (IsGlobalObject()) return this;
@@ -10385,6 +10379,19 @@
}
+bool Code::CanDeoptAt(Address pc) {
+ DeoptimizationInputData* deopt_data =
+ DeoptimizationInputData::cast(deoptimization_data());
+ Address code_start_address = instruction_start();
+ for (int i = 0; i < deopt_data->DeoptCount(); i++) {
+ if (deopt_data->Pc(i)->value() == -1) continue;
+ Address address = code_start_address + deopt_data->Pc(i)->value();
+ if (address == pc) return true;
+ }
+ return false;
+}
+
+
// Identify kind of code.
const char* Code::Kind2String(Kind kind) {
switch (kind) {
@@ -11012,63 +11019,61 @@
}
-Map* Map::GetPrototypeTransition(Object* prototype) {
- FixedArray* cache = GetPrototypeTransitions();
- int number_of_transitions = NumberOfProtoTransitions();
+Handle<Map> Map::GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype) {
+ FixedArray* cache = map->GetPrototypeTransitions();
+ int number_of_transitions = map->NumberOfProtoTransitions();
const int proto_offset =
kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
const int step = kProtoTransitionElementsPerEntry;
for (int i = 0; i < number_of_transitions; i++) {
- if (cache->get(proto_offset + i * step) == prototype) {
- Object* map = cache->get(map_offset + i * step);
- return Map::cast(map);
+ if (cache->get(proto_offset + i * step) == *prototype) {
+ Object* result = cache->get(map_offset + i * step);
+ return Handle<Map>(Map::cast(result));
}
}
- return NULL;
+ return Handle<Map>();
}
-MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
- ASSERT(map->IsMap());
- ASSERT(HeapObject::cast(prototype)->map()->IsMap());
+Handle<Map> Map::PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map) {
+ ASSERT(target_map->IsMap());
+ ASSERT(HeapObject::cast(*prototype)->map()->IsMap());
// Don't cache prototype transition if this map is shared.
- if (is_shared() || !FLAG_cache_prototype_transitions) return this;
-
- FixedArray* cache = GetPrototypeTransitions();
+ if (map->is_shared() || !FLAG_cache_prototype_transitions) return map;
const int step = kProtoTransitionElementsPerEntry;
const int header = kProtoTransitionHeaderSize;
+ Handle<FixedArray> cache(map->GetPrototypeTransitions());
int capacity = (cache->length() - header) / step;
-
- int transitions = NumberOfProtoTransitions() + 1;
+ int transitions = map->NumberOfProtoTransitions() + 1;
if (transitions > capacity) {
- if (capacity > kMaxCachedPrototypeTransitions) return this;
+ if (capacity > kMaxCachedPrototypeTransitions) return map;
- FixedArray* new_cache;
// Grow array by factor 2 over and above what we need.
- { MaybeObject* maybe_cache =
- GetHeap()->AllocateFixedArray(transitions * 2 * step + header);
- if (!maybe_cache->To(&new_cache)) return maybe_cache;
- }
+ Factory* factory = map->GetIsolate()->factory();
+ cache = factory->CopySizeFixedArray(cache, transitions * 2 * step + header);
- for (int i = 0; i < capacity * step; i++) {
- new_cache->set(i + header, cache->get(i + header));
- }
- cache = new_cache;
- MaybeObject* set_result = SetPrototypeTransitions(cache);
- if (set_result->IsFailure()) return set_result;
+ CALL_AND_RETRY_OR_DIE(map->GetIsolate(),
+ map->SetPrototypeTransitions(*cache),
+ break,
+ return Handle<Map>());
}
- int last = transitions - 1;
+ // Reload number of transitions as GC might shrink them.
+ int last = map->NumberOfProtoTransitions();
+ int entry = header + last * step;
- cache->set(header + last * step + kProtoTransitionPrototypeOffset, prototype);
- cache->set(header + last * step + kProtoTransitionMapOffset, map);
- SetNumberOfProtoTransitions(transitions);
+ cache->set(entry + kProtoTransitionPrototypeOffset, *prototype);
+ cache->set(entry + kProtoTransitionMapOffset, *target_map);
+ map->SetNumberOfProtoTransitions(transitions);
- return cache;
+ return map;
}
@@ -11291,13 +11296,14 @@
}
-MaybeObject* JSReceiver::SetPrototype(Object* value,
+Handle<Object> JSObject::SetPrototype(Handle<JSObject> object,
+ Handle<Object> value,
bool skip_hidden_prototypes) {
#ifdef DEBUG
- int size = Size();
+ int size = object->Size();
#endif
- Isolate* isolate = GetIsolate();
+ Isolate* isolate = object->GetIsolate();
Heap* heap = isolate->heap();
// Silently ignore the change if value is not a JSObject or null.
// SpiderMonkey behaves this way.
@@ -11311,70 +11317,64 @@
// Implementation specific extensions that modify [[Class]], [[Prototype]]
// or [[Extensible]] must not violate the invariants defined in the preceding
// paragraph.
- if (!this->map()->is_extensible()) {
- HandleScope scope(isolate);
- Handle<Object> handle(this, isolate);
- return isolate->Throw(
- *isolate->factory()->NewTypeError("non_extensible_proto",
- HandleVector<Object>(&handle, 1)));
+ if (!object->map()->is_extensible()) {
+ Handle<Object> args[] = { object };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "non_extensible_proto", HandleVector(args, ARRAY_SIZE(args)));
+ isolate->Throw(*error);
+ return Handle<Object>();
}
// Before we can set the prototype we need to be sure
// prototype cycles are prevented.
// It is sufficient to validate that the receiver is not in the new prototype
// chain.
- for (Object* pt = value;
+ for (Object* pt = *value;
pt != heap->null_value();
pt = pt->GetPrototype(isolate)) {
- if (JSReceiver::cast(pt) == this) {
+ if (JSReceiver::cast(pt) == *object) {
// Cycle detected.
- HandleScope scope(isolate);
- return isolate->Throw(
- *isolate->factory()->NewError("cyclic_proto",
- HandleVector<Object>(NULL, 0)));
+ Handle<Object> error = isolate->factory()->NewError(
+ "cyclic_proto", HandleVector<Object>(NULL, 0));
+ isolate->Throw(*error);
+ return Handle<Object>();
}
}
- JSReceiver* real_receiver = this;
+ Handle<JSObject> real_receiver = object;
if (skip_hidden_prototypes) {
// Find the first object in the chain whose prototype object is not
// hidden and set the new prototype on that object.
Object* current_proto = real_receiver->GetPrototype();
while (current_proto->IsJSObject() &&
- JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) {
- real_receiver = JSReceiver::cast(current_proto);
+ JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
+ real_receiver = handle(JSObject::cast(current_proto), isolate);
current_proto = current_proto->GetPrototype(isolate);
}
}
// Set the new prototype of the object.
- Map* map = real_receiver->map();
+ Handle<Map> map(real_receiver->map());
// Nothing to do if prototype is already set.
- if (map->prototype() == value) return value;
+ if (map->prototype() == *value) return value;
if (value->IsJSObject()) {
- MaybeObject* ok = JSObject::cast(value)->OptimizeAsPrototype();
- if (ok->IsFailure()) return ok;
+ JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}
- Map* new_map = map->GetPrototypeTransition(value);
- if (new_map == NULL) {
- MaybeObject* maybe_new_map = map->Copy();
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
-
- MaybeObject* maybe_new_cache =
- map->PutPrototypeTransition(value, new_map);
- if (maybe_new_cache->IsFailure()) return maybe_new_cache;
-
- new_map->set_prototype(value);
+ Handle<Map> new_map = Map::GetPrototypeTransition(map, value);
+ if (new_map.is_null()) {
+ new_map = Map::Copy(map);
+ Map::PutPrototypeTransition(map, value, new_map);
+ new_map->set_prototype(*value);
}
- ASSERT(new_map->prototype() == value);
- real_receiver->set_map(new_map);
+ ASSERT(new_map->prototype() == *value);
+ real_receiver->set_map(*new_map);
heap->ClearInstanceofCache();
- ASSERT(size == Size());
+ ASSERT(size == object->Size());
return value;
}
diff --git a/src/objects.h b/src/objects.h
index adaa9cf..416ed7f 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -32,6 +32,7 @@
#include "assert-scope.h"
#include "builtins.h"
#include "elements-kind.h"
+#include "flags.h"
#include "list.h"
#include "property-details.h"
#include "smart-pointers.h"
@@ -1703,10 +1704,6 @@
// Return the constructor function (may be Heap::null_value()).
inline Object* GetConstructor();
- // Set the object's prototype (only JSReceiver and null are allowed).
- MUST_USE_RESULT MaybeObject* SetPrototype(Object* value,
- bool skip_hidden_prototypes);
-
// Retrieves a permanent object identity hash code. The undefined value might
// be returned in case no hash was created yet and OMIT_CREATION was used.
inline MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag);
@@ -1908,6 +1905,7 @@
MUST_USE_RESULT MaybeObject* DeleteNormalizedProperty(Name* name,
DeleteMode mode);
+ static void OptimizeAsPrototype(Handle<JSObject> object);
MUST_USE_RESULT MaybeObject* OptimizeAsPrototype();
// Retrieve interceptors.
@@ -1935,19 +1933,7 @@
Handle<Object> getter,
Handle<Object> setter,
PropertyAttributes attributes);
- // Can cause GC.
- MUST_USE_RESULT MaybeObject* DefineAccessor(Name* name,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes);
- // Try to define a single accessor paying attention to map transitions.
- // Returns a JavaScript null if this was not possible and we have to use the
- // slow case. Note that we can fail due to allocations, too.
- MUST_USE_RESULT MaybeObject* DefineFastAccessor(
- Name* name,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes);
+
Object* LookupAccessor(Name* name, AccessorComponent component);
MUST_USE_RESULT MaybeObject* DefineAccessor(AccessorInfo* info);
@@ -2311,6 +2297,11 @@
WriteBarrierMode mode
= UPDATE_WRITE_BARRIER);
+ // Set the object's prototype (only JSReceiver and null are allowed values).
+ static Handle<Object> SetPrototype(Handle<JSObject> object,
+ Handle<Object> value,
+ bool skip_hidden_prototypes = false);
+
// Initializes the body after properties slot, properties slot is
// initialized by set_properties. Fill the pre-allocated fields with
// pre_allocated_value and the rest with filler_value.
@@ -2513,18 +2504,26 @@
Name* name,
Object* structure,
PropertyAttributes attributes);
- MUST_USE_RESULT MaybeObject* DefineElementAccessor(
- uint32_t index,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes);
- MUST_USE_RESULT MaybeObject* CreateAccessorPairFor(Name* name);
- MUST_USE_RESULT MaybeObject* DefinePropertyAccessor(
- Name* name,
- Object* getter,
- Object* setter,
- PropertyAttributes attributes);
+ static void DefineElementAccessor(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes);
+ static Handle<AccessorPair> CreateAccessorPairFor(Handle<JSObject> object,
+ Handle<Name> name);
+ static void DefinePropertyAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes);
+ // Try to define a single accessor paying attention to map transitions.
+ // Returns false if this was not possible and we have to use the slow case.
+ static bool DefineFastAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ AccessorComponent component,
+ Handle<Object> accessor,
+ PropertyAttributes attributes);
enum InitializeHiddenProperties {
CREATE_NEW_IF_ABSENT,
@@ -4841,6 +4840,7 @@
int GetAge();
void PrintDeoptLocation(int bailout_id);
+ bool CanDeoptAt(Address pc);
#ifdef VERIFY_HEAP
void VerifyEmbeddedMapsDependency();
@@ -5482,6 +5482,7 @@
// Returns a copy of the map, with all transitions dropped from the
// instance descriptors.
+ static Handle<Map> Copy(Handle<Map> map);
MUST_USE_RESULT MaybeObject* Copy();
// Returns the next free property index (only valid for FAST MODE).
@@ -5608,11 +5609,11 @@
// transitions are in the form of a map where the keys are prototype objects
// and the values are the maps the are transitioned to.
static const int kMaxCachedPrototypeTransitions = 256;
-
- Map* GetPrototypeTransition(Object* prototype);
-
- MUST_USE_RESULT MaybeObject* PutPrototypeTransition(Object* prototype,
- Map* map);
+ static Handle<Map> GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype);
+ static Handle<Map> PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map);
static const int kMaxPreAllocatedPropertyFields = 255;
@@ -9255,7 +9256,7 @@
static inline AccessorPair* cast(Object* obj);
- MUST_USE_RESULT MaybeObject* Copy();
+ static Handle<AccessorPair> Copy(Handle<AccessorPair> pair);
Object* get(AccessorComponent component) {
return component == ACCESSOR_GETTER ? getter() : setter();
diff --git a/src/optimizing-compiler-thread.h b/src/optimizing-compiler-thread.h
index 699c76d..275ceb4 100644
--- a/src/optimizing-compiler-thread.h
+++ b/src/optimizing-compiler-thread.h
@@ -31,7 +31,7 @@
#include "atomicops.h"
#include "flags.h"
#include "platform.h"
-#include "unbound-queue.h"
+#include "unbound-queue-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index aeb80a3..78b05c5 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -30,11 +30,12 @@
#include "profile-generator-inl.h"
#include "compiler.h"
+#include "debug.h"
+#include "sampler.h"
#include "global-handles.h"
#include "scopeinfo.h"
#include "unicode.h"
#include "zone-inl.h"
-#include "debug.h"
namespace v8 {
namespace internal {
@@ -906,11 +907,16 @@
}
*entry++ = pc_entry;
- if (pc_entry->builtin_id() == Builtins::kFunctionCall) {
- // When current function is FunctionCall builtin tos is sometimes
- // address of the function that invoked it but sometimes it's one
- // of the arguments. We simply replace the frame with 'unknown' entry.
- *entry++ = unresolved_entry_;
+ if (pc_entry->builtin_id() == Builtins::kFunctionCall ||
+ pc_entry->builtin_id() == Builtins::kFunctionApply) {
+ // When current function is FunctionCall or FunctionApply builtin the
+ // top frame is either frame of the calling JS function or internal
+ // frame. In the latter case we know the caller for sure but in the
+ // former case we don't so we simply replace the frame with
+ // 'unresolved' entry.
+ if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) {
+ *entry++ = unresolved_entry_;
+ }
}
}
}
diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc
index 00f7de4..bd02a69 100644
--- a/src/runtime-profiler.cc
+++ b/src/runtime-profiler.cc
@@ -227,6 +227,8 @@
void RuntimeProfiler::OptimizeNow() {
HandleScope scope(isolate_);
+ if (isolate_->DebuggerHasBreakPoints()) return;
+
if (FLAG_parallel_recompilation) {
// Take this as opportunity to process the optimizing compiler thread's
// output queue so that it does not unnecessarily keep objects alive.
diff --git a/src/runtime.cc b/src/runtime.cc
index 4691e18..e3ee6d5 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -38,6 +38,7 @@
#include "compilation-cache.h"
#include "compiler.h"
#include "cpu.h"
+#include "cpu-profiler.h"
#include "dateparser-inl.h"
#include "debug.h"
#include "deoptimizer.h"
@@ -1450,31 +1451,29 @@
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetPrototype) {
- SealHandleScope shs(isolate);
+ HandleScope scope(isolate);
ASSERT(args.length() == 2);
- CONVERT_ARG_CHECKED(JSObject, obj, 0);
- CONVERT_ARG_CHECKED(Object, prototype, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
if (FLAG_harmony_observation && obj->map()->is_observed()) {
- HandleScope scope(isolate);
- Handle<JSObject> receiver(obj);
- Handle<Object> value(prototype, isolate);
Handle<Object> old_value(
- GetPrototypeSkipHiddenPrototypes(isolate, *receiver), isolate);
+ GetPrototypeSkipHiddenPrototypes(isolate, *obj), isolate);
- MaybeObject* result = receiver->SetPrototype(*value, true);
- Handle<Object> hresult;
- if (!result->ToHandle(&hresult, isolate)) return result;
+ Handle<Object> result = JSObject::SetPrototype(obj, prototype, true);
+ if (result.is_null()) return Failure::Exception();
Handle<Object> new_value(
- GetPrototypeSkipHiddenPrototypes(isolate, *receiver), isolate);
+ GetPrototypeSkipHiddenPrototypes(isolate, *obj), isolate);
if (!new_value->SameValue(*old_value)) {
- JSObject::EnqueueChangeRecord(receiver, "prototype",
+ JSObject::EnqueueChangeRecord(obj, "prototype",
isolate->factory()->proto_string(),
old_value);
}
- return *hresult;
+ return *result;
}
- return obj->SetPrototype(prototype, true);
+ Handle<Object> result = JSObject::SetPrototype(obj, prototype, true);
+ if (result.is_null()) return Failure::Exception();
+ return *result;
}
@@ -3600,8 +3599,8 @@
ASSERT(subject->IsFlat());
ASSERT(replacement->IsFlat());
- Zone zone(isolate);
- ZoneList<int> indices(8, &zone);
+ ZoneScope zone_scope(isolate->runtime_zone());
+ ZoneList<int> indices(8, zone_scope.zone());
ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
String* pattern =
String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
@@ -3610,7 +3609,7 @@
int replacement_len = replacement->length();
FindStringIndicesDispatch(
- isolate, *subject, pattern, &indices, 0xffffffff, &zone);
+ isolate, *subject, pattern, &indices, 0xffffffff, zone_scope.zone());
int matches = indices.length();
if (matches == 0) return *subject;
@@ -3686,8 +3685,8 @@
int subject_length = subject->length();
// CompiledReplacement uses zone allocation.
- Zone zone(isolate);
- CompiledReplacement compiled_replacement(&zone);
+ ZoneScope zone_scope(isolate->runtime_zone());
+ CompiledReplacement compiled_replacement(zone_scope.zone());
bool simple_replace = compiled_replacement.Compile(replacement,
capture_count,
subject_length);
@@ -4220,14 +4219,14 @@
int capture_count = regexp->CaptureCount();
- Zone zone(isolate);
- ZoneList<int> offsets(8, &zone);
+ ZoneScope zone_scope(isolate->runtime_zone());
+ ZoneList<int> offsets(8, zone_scope.zone());
while (true) {
int32_t* match = global_cache.FetchNext();
if (match == NULL) break;
- offsets.Add(match[0], &zone); // start
- offsets.Add(match[1], &zone); // end
+ offsets.Add(match[0], zone_scope.zone()); // start
+ offsets.Add(match[1], zone_scope.zone()); // end
}
if (global_cache.HasException()) return Failure::Exception();
@@ -6312,18 +6311,18 @@
static const int kMaxInitialListCapacity = 16;
- Zone zone(isolate);
+ ZoneScope zone_scope(isolate->runtime_zone());
// Find (up to limit) indices of separator and end-of-string in subject
int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
- ZoneList<int> indices(initial_capacity, &zone);
+ ZoneList<int> indices(initial_capacity, zone_scope.zone());
if (!pattern->IsFlat()) FlattenString(pattern);
FindStringIndicesDispatch(isolate, *subject, *pattern,
- &indices, limit, &zone);
+ &indices, limit, zone_scope.zone());
if (static_cast<uint32_t>(indices.length()) < limit) {
- indices.Add(subject_length, &zone);
+ indices.Add(subject_length, zone_scope.zone());
}
// The list indices now contains the end of each part to create.
diff --git a/src/sampler.cc b/src/sampler.cc
index 6d97110..982f252 100644
--- a/src/sampler.cc
+++ b/src/sampler.cc
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "sampler.h"
+
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
|| defined(__NetBSD__) || defined(__sun) || defined(__ANDROID__) \
|| defined(__native_client__)
@@ -60,6 +62,8 @@
#include "v8.h"
+#include "cpu-profiler.h"
+#include "flags.h"
#include "frames-inl.h"
#include "log.h"
#include "platform.h"
@@ -227,44 +231,36 @@
#endif
-class SampleHelper {
- public:
- inline TickSample* Init(Sampler* sampler, Isolate* isolate) {
#if defined(USE_SIMULATOR)
+class SimulatorHelper {
+ public:
+ inline bool Init(Sampler* sampler, Isolate* isolate) {
ThreadId thread_id = sampler->platform_data()->profiled_thread_id();
Isolate::PerIsolateThreadData* per_thread_data = isolate->
FindPerThreadDataForThread(thread_id);
- if (!per_thread_data) return NULL;
+ if (!per_thread_data) return false;
simulator_ = per_thread_data->simulator();
- // Check if there is active simulator before allocating TickSample.
- if (!simulator_) return NULL;
-#endif // USE_SIMULATOR
- TickSample* sample = isolate->cpu_profiler()->TickSampleEvent();
- if (sample == NULL) sample = &sample_obj;
- return sample;
+ // Check if there is active simulator.
+ return simulator_ != NULL;
}
-#if defined(USE_SIMULATOR)
- inline void FillRegisters(TickSample* sample) {
- sample->pc = reinterpret_cast<Address>(simulator_->get_pc());
- sample->sp = reinterpret_cast<Address>(simulator_->get_register(
+ inline void FillRegisters(RegisterState* state) {
+ state->pc = reinterpret_cast<Address>(simulator_->get_pc());
+ state->sp = reinterpret_cast<Address>(simulator_->get_register(
Simulator::sp));
#if V8_TARGET_ARCH_ARM
- sample->fp = reinterpret_cast<Address>(simulator_->get_register(
+ state->fp = reinterpret_cast<Address>(simulator_->get_register(
Simulator::r11));
#elif V8_TARGET_ARCH_MIPS
- sample->fp = reinterpret_cast<Address>(simulator_->get_register(
+ state->fp = reinterpret_cast<Address>(simulator_->get_register(
Simulator::fp));
#endif
}
-#endif // USE_SIMULATOR
private:
-#if defined(USE_SIMULATOR)
Simulator* simulator_;
-#endif
- TickSample sample_obj;
};
+#endif // USE_SIMULATOR
#if defined(USE_SIGNALS)
@@ -324,89 +320,86 @@
Sampler* sampler = isolate->logger()->sampler();
if (sampler == NULL || !sampler->IsActive()) return;
- SampleHelper helper;
- TickSample* sample = helper.Init(sampler, isolate);
- if (sample == NULL) return;
+ RegisterState state;
#if defined(USE_SIMULATOR)
- helper.FillRegisters(sample);
+ SimulatorHelper helper;
+ if (!helper.Init(sampler, isolate)) return;
+ helper.FillRegisters(&state);
#else
// Extracting the sample from the context is extremely machine dependent.
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
mcontext_t& mcontext = ucontext->uc_mcontext;
- sample->state = isolate->current_vm_state();
#if defined(__linux__) || defined(__ANDROID__)
#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
#elif V8_HOST_ARCH_ARM
#if defined(__GLIBC__) && !defined(__UCLIBC__) && \
(__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
// Old GLibc ARM versions used a gregs[] array to access the register
// values from mcontext_t.
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
#else
- sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
- sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
- sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
+ state.pc = reinterpret_cast<Address>(mcontext.arm_pc);
+ state.sp = reinterpret_cast<Address>(mcontext.arm_sp);
+ state.fp = reinterpret_cast<Address>(mcontext.arm_fp);
#endif // defined(__GLIBC__) && !defined(__UCLIBC__) &&
// (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
#elif V8_HOST_ARCH_MIPS
- sample->pc = reinterpret_cast<Address>(mcontext.pc);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[29]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[30]);
+ state.pc = reinterpret_cast<Address>(mcontext.pc);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[29]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[30]);
#endif // V8_HOST_ARCH_*
#elif defined(__FreeBSD__)
#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.mc_eip);
- sample->sp = reinterpret_cast<Address>(mcontext.mc_esp);
- sample->fp = reinterpret_cast<Address>(mcontext.mc_ebp);
+ state.pc = reinterpret_cast<Address>(mcontext.mc_eip);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_esp);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_ebp);
#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.mc_rip);
- sample->sp = reinterpret_cast<Address>(mcontext.mc_rsp);
- sample->fp = reinterpret_cast<Address>(mcontext.mc_rbp);
+ state.pc = reinterpret_cast<Address>(mcontext.mc_rip);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_rsp);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_rbp);
#elif V8_HOST_ARCH_ARM
- sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
- sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
- sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
+ state.pc = reinterpret_cast<Address>(mcontext.mc_r15);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_r13);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_r11);
#endif // V8_HOST_ARCH_*
#elif defined(__NetBSD__)
#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
+ state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
+ state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
+ state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
+ state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
#endif // V8_HOST_ARCH_*
#elif defined(__OpenBSD__)
USE(mcontext);
#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(ucontext->sc_eip);
- sample->sp = reinterpret_cast<Address>(ucontext->sc_esp);
- sample->fp = reinterpret_cast<Address>(ucontext->sc_ebp);
+ state.pc = reinterpret_cast<Address>(ucontext->sc_eip);
+ state.sp = reinterpret_cast<Address>(ucontext->sc_esp);
+ state.fp = reinterpret_cast<Address>(ucontext->sc_ebp);
#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(ucontext->sc_rip);
- sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp);
- sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp);
+ state.pc = reinterpret_cast<Address>(ucontext->sc_rip);
+ state.sp = reinterpret_cast<Address>(ucontext->sc_rsp);
+ state.fp = reinterpret_cast<Address>(ucontext->sc_rbp);
#endif // V8_HOST_ARCH_*
#elif defined(__sun)
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
#endif // __sun
#endif // USE_SIMULATOR
-
- sampler->SampleStack(sample);
- sampler->Tick(sample);
+ sampler->SampleStack(state);
#endif // __native_client__
}
@@ -503,17 +496,18 @@
void SampleContext(Sampler* sampler) {
thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
- Isolate* isolate = sampler->isolate();
- SampleHelper helper;
- TickSample* sample = helper.Init(sampler, isolate);
- if (sample == NULL) return;
+#if defined(USE_SIMULATOR)
+ SimulatorHelper helper;
+ Isolate* isolate = sampler->isolate();
+ if (!helper.Init(sampler, isolate)) return;
+#endif
if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
#if V8_HOST_ARCH_X64
thread_state_flavor_t flavor = x86_THREAD_STATE64;
- x86_thread_state64_t state;
+ x86_thread_state64_t thread_state;
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __r ## name
@@ -522,7 +516,7 @@
#endif // __DARWIN_UNIX03
#elif V8_HOST_ARCH_IA32
thread_state_flavor_t flavor = i386_THREAD_STATE;
- i386_thread_state_t state;
+ i386_thread_state_t thread_state;
mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __e ## name
@@ -535,19 +529,18 @@
if (thread_get_state(profiled_thread,
flavor,
- reinterpret_cast<natural_t*>(&state),
+ reinterpret_cast<natural_t*>(&thread_state),
&count) == KERN_SUCCESS) {
- sample->state = isolate->current_vm_state();
+ RegisterState state;
#if defined(USE_SIMULATOR)
- helper.FillRegisters(sample);
+ helper.FillRegisters(&state);
#else
- sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
- sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
- sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
+ state.pc = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(ip));
+ state.sp = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(sp));
+ state.fp = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(bp));
#endif // USE_SIMULATOR
#undef REGISTER_FIELD
- sampler->SampleStack(sample);
- sampler->Tick(sample);
+ sampler->SampleStack(state);
}
thread_resume(profiled_thread);
}
@@ -559,34 +552,34 @@
if (profiled_thread == NULL) return;
Isolate* isolate = sampler->isolate();
- SampleHelper helper;
- TickSample* sample = helper.Init(sampler, isolate);
- if (sample == NULL) return;
+#if defined(USE_SIMULATOR)
+ SimulatorHelper helper;
+ if (!helper.Init(sampler, isolate)) return;
+#endif
const DWORD kSuspendFailed = static_cast<DWORD>(-1);
if (SuspendThread(profiled_thread) == kSuspendFailed) return;
- sample->state = isolate->current_vm_state();
// Context used for sampling the register state of the profiled thread.
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(profiled_thread, &context) != 0) {
+ RegisterState state;
#if defined(USE_SIMULATOR)
- helper.FillRegisters(sample);
+ helper.FillRegisters(&state);
#else
#if V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(context.Rip);
- sample->sp = reinterpret_cast<Address>(context.Rsp);
- sample->fp = reinterpret_cast<Address>(context.Rbp);
+ state.pc = reinterpret_cast<Address>(context.Rip);
+ state.sp = reinterpret_cast<Address>(context.Rsp);
+ state.fp = reinterpret_cast<Address>(context.Rbp);
#else
- sample->pc = reinterpret_cast<Address>(context.Eip);
- sample->sp = reinterpret_cast<Address>(context.Esp);
- sample->fp = reinterpret_cast<Address>(context.Ebp);
+ state.pc = reinterpret_cast<Address>(context.Eip);
+ state.sp = reinterpret_cast<Address>(context.Esp);
+ state.fp = reinterpret_cast<Address>(context.Ebp);
#endif
#endif // USE_SIMULATOR
- sampler->SampleStack(sample);
- sampler->Tick(sample);
+ sampler->SampleStack(state);
}
ResumeThread(profiled_thread);
}
@@ -612,8 +605,11 @@
//
// StackTracer implementation
//
-DISABLE_ASAN void TickSample::Trace(Isolate* isolate) {
+DISABLE_ASAN void TickSample::Init(Isolate* isolate,
+ const RegisterState& regs) {
ASSERT(isolate->IsInitialized());
+ pc = regs.pc;
+ state = isolate->current_vm_state();
// Avoid collecting traces while doing GC.
if (state == GC) return;
@@ -632,11 +628,12 @@
} else {
// Sample potential return address value for frameless invocation of
// stubs (we'll figure out later, if this value makes sense).
- tos = Memory::Address_at(sp);
+ tos = Memory::Address_at(regs.sp);
has_external_callback = false;
}
- SafeStackFrameIterator it(isolate, fp, sp, sp, js_entry_sp);
+ SafeStackFrameIterator it(isolate, regs.fp, regs.sp, js_entry_sp);
+ top_frame_type = it.top_frame_type();
int i = 0;
while (!it.done() && i < TickSample::kMaxFramesCount) {
stack[i++] = it.frame()->pc();
@@ -684,9 +681,13 @@
SetActive(false);
}
-void Sampler::SampleStack(TickSample* sample) {
- sample->Trace(isolate_);
+void Sampler::SampleStack(const RegisterState& state) {
+ TickSample* sample = isolate_->cpu_profiler()->TickSampleEvent();
+ TickSample sample_obj;
+ if (sample == NULL) sample = &sample_obj;
+ sample->Init(isolate_, state);
if (++samples_taken_ < 0) samples_taken_ = 0;
+ Tick(sample);
}
} } // namespace v8::internal
diff --git a/src/sampler.h b/src/sampler.h
index 1d9ac87..a47a363 100644
--- a/src/sampler.h
+++ b/src/sampler.h
@@ -29,6 +29,7 @@
#define V8_SAMPLER_H_
#include "atomicops.h"
+#include "frames.h"
#include "v8globals.h"
namespace v8 {
@@ -43,21 +44,25 @@
// (if used for profiling) the program counter and stack pointer for
// the thread that created it.
+struct RegisterState {
+ RegisterState() : pc(NULL), sp(NULL), fp(NULL) {}
+ Address pc; // Instruction pointer.
+ Address sp; // Stack pointer.
+ Address fp; // Frame pointer.
+};
+
// TickSample captures the information collected for each sample.
struct TickSample {
TickSample()
: state(OTHER),
pc(NULL),
- sp(NULL),
- fp(NULL),
external_callback(NULL),
frames_count(0),
- has_external_callback(false) {}
- void Trace(Isolate* isolate);
+ has_external_callback(false),
+ top_frame_type(StackFrame::NONE) {}
+ void Init(Isolate* isolate, const RegisterState& state);
StateTag state; // The state of the VM.
Address pc; // Instruction pointer.
- Address sp; // Stack pointer.
- Address fp; // Frame pointer.
union {
Address tos; // Top stack value (*sp).
Address external_callback;
@@ -66,6 +71,7 @@
Address stack[kMaxFramesCount]; // Call stack.
int frames_count : 8; // Number of captured frames.
bool has_external_callback : 1;
+ StackFrame::Type top_frame_type : 4;
};
class Sampler {
@@ -82,11 +88,7 @@
int interval() const { return interval_; }
// Performs stack sampling.
- void SampleStack(TickSample* sample);
-
- // This method is called for each sampling period with the current
- // program counter.
- virtual void Tick(TickSample* sample) = 0;
+ void SampleStack(const RegisterState& regs);
// Start and stop sampler.
void Start();
@@ -107,6 +109,11 @@
class PlatformData;
PlatformData* platform_data() const { return data_; }
+ protected:
+ // This method is called for each sampling period with the current
+ // program counter.
+ virtual void Tick(TickSample* sample) = 0;
+
private:
void SetActive(bool value) { NoBarrier_Store(&active_, value); }
diff --git a/src/spaces.h b/src/spaces.h
index 5b27542..dda5591 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -32,6 +32,7 @@
#include "hashmap.h"
#include "list.h"
#include "log.h"
+#include "v8utils.h"
namespace v8 {
namespace internal {
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index e0839f1..62ac2c8 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -31,6 +31,7 @@
#include "arguments.h"
#include "ast.h"
#include "code-stubs.h"
+#include "cpu-profiler.h"
#include "gdb-jit.h"
#include "ic-inl.h"
#include "stub-cache.h"
diff --git a/src/type-info.cc b/src/type-info.cc
index ae5bf97..83eb9c4 100644
--- a/src/type-info.cc
+++ b/src/type-info.cc
@@ -356,9 +356,12 @@
Handle<Type>* left_type,
Handle<Type>* right_type,
Handle<Type>* combined_type) {
- *left_type = *right_type = *combined_type = handle(Type::Any(), isolate_);
Handle<Object> info = GetInfo(id);
- if (!info->IsCode()) return;
+ if (!info->IsCode()) {
+ // For some comparisons we don't have ICs, e.g. LiteralCompareTypeof.
+ *left_type = *right_type = *combined_type = handle(Type::None(), isolate_);
+ return;
+ }
Handle<Code> code = Handle<Code>::cast(info);
Handle<Map> map;
@@ -387,7 +390,9 @@
Handle<Type> TypeFeedbackOracle::UnaryType(TypeFeedbackId id) {
Handle<Object> object = GetInfo(id);
- if (!object->IsCode()) return handle(Type::Any(), isolate());
+ if (!object->IsCode()) {
+ return handle(Type::None(), isolate());
+ }
Handle<Code> code = Handle<Code>::cast(object);
ASSERT(code->is_unary_op_stub());
return UnaryOpIC::TypeInfoToType(
@@ -401,10 +406,13 @@
Handle<Type>* result,
Maybe<int>* fixed_right_arg) {
Handle<Object> object = GetInfo(id);
- *left = *right = *result = handle(Type::Any(), isolate_);
- if (!object->IsCode()) return;
+ if (!object->IsCode()) {
+ // For some binary ops we don't have ICs, e.g. Token::COMMA.
+ *left = *right = *result = handle(Type::None(), isolate_);
+ return;
+ }
Handle<Code> code = Handle<Code>::cast(object);
- if (!code->is_binary_op_stub()) return;
+ ASSERT(code->is_binary_op_stub());
int minor_key = code->stub_info();
BinaryOpIC::StubInfoToType(minor_key, left, right, result, isolate());
diff --git a/src/v8.h b/src/v8.h
index 52fb98a..47893e8 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -64,9 +64,7 @@
#include "incremental-marking-inl.h"
#include "mark-compact-inl.h"
#include "log-inl.h"
-#include "cpu-profiler-inl.h"
#include "handles-inl.h"
-#include "heap-snapshot-generator-inl.h"
#include "zone-inl.h"
namespace v8 {
diff --git a/src/version.cc b/src/version.cc
index c634509..0041c67 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// system so their names cannot be changed without changing the scripts.
#define MAJOR_VERSION 3
#define MINOR_VERSION 20
-#define BUILD_NUMBER 1
+#define BUILD_NUMBER 2
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 378217b..2b44a77 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -44,15 +44,15 @@
CFunctionId id,
BuiltinExtraArguments extra_args) {
// ----------- S t a t e -------------
- // -- rax : number of arguments excluding receiver
- // -- rdi : called function (only guaranteed when
- // extra_args requires it)
- // -- rsi : context
- // -- rsp[0] : return address
- // -- rsp[8] : last argument
+ // -- rax : number of arguments excluding receiver
+ // -- rdi : called function (only guaranteed when
+ // extra_args requires it)
+ // -- rsi : context
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
// -- ...
- // -- rsp[8 * argc] : first argument (argc == rax)
- // -- rsp[8 * (argc +1)] : receiver
+ // -- rsp[8 * argc] : first argument (argc == rax)
+ // -- rsp[8 * (argc + 1)] : receiver
// -----------------------------------
// Insert extra arguments.
@@ -475,10 +475,10 @@
#ifdef _WIN64
// MSVC parameters in:
- // rcx : entry (ignored)
- // rdx : function
- // r8 : receiver
- // r9 : argc
+ // rcx : entry (ignored)
+ // rdx : function
+ // r8 : receiver
+ // r9 : argc
// [rsp+0x20] : argv
// Clear the context before we push it when entering the internal frame.
@@ -527,9 +527,9 @@
#endif // _WIN64
// Current stack contents:
- // [rsp + 2 * kPointerSize ... ]: Internal frame
- // [rsp + kPointerSize] : function
- // [rsp] : receiver
+ // [rsp + 2 * kPointerSize ... ] : Internal frame
+ // [rsp + kPointerSize] : function
+ // [rsp] : receiver
// Current register contents:
// rax : argc
// rbx : argv
@@ -758,12 +758,12 @@
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout:
- // rsp[0]: Return address
- // rsp[1]: Argument n
- // rsp[2]: Argument n-1
+ // rsp[0] : Return address
+ // rsp[8] : Argument n
+ // rsp[16] : Argument n-1
// ...
- // rsp[n]: Argument 1
- // rsp[n+1]: Receiver (function to call)
+ // rsp[8 * n] : Argument 1
+ // rsp[8 * (n + 1)] : Receiver (function to call)
//
// rax contains the number of arguments, n, not counting the receiver.
//
@@ -931,18 +931,18 @@
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// Stack at entry:
- // rsp: return address
- // rsp+8: arguments
- // rsp+16: receiver ("this")
- // rsp+24: function
+ // rsp : return address
+ // rsp[8] : arguments
+ // rsp[16] : receiver ("this")
+ // rsp[24] : function
{
FrameScope frame_scope(masm, StackFrame::INTERNAL);
// Stack frame:
- // rbp: Old base pointer
- // rbp[1]: return address
- // rbp[2]: function arguments
- // rbp[3]: receiver
- // rbp[4]: function
+ // rbp : Old base pointer
+ // rbp[8] : return address
+ // rbp[16] : function arguments
+ // rbp[24] : receiver
+ // rbp[32] : function
static const int kArgumentsOffset = 2 * kPointerSize;
static const int kReceiverOffset = 3 * kPointerSize;
static const int kFunctionOffset = 4 * kPointerSize;
@@ -1097,7 +1097,7 @@
void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : argc
+ // -- rax : argc
// -- rsp[0] : return address
// -- rsp[8] : last argument
// -----------------------------------
@@ -1127,7 +1127,7 @@
void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : argc
+ // -- rax : argc
// -- rsp[0] : return address
// -- rsp[8] : last argument
// -----------------------------------
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 9774638..9233848 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -254,7 +254,7 @@
void ToNumberStub::Generate(MacroAssembler* masm) {
- // The ToNumber stub takes one argument in eax.
+ // The ToNumber stub takes one argument in rax.
Label check_heap_number, call_builtin;
__ SmiTest(rax);
__ j(not_zero, &check_heap_number, Label::kNear);
@@ -333,7 +333,7 @@
__ IncrementCounter(counters->fast_new_closure_try_optimized(), 1);
- // rcx holds native context, ebx points to fixed array of 3-element entries
+ // rcx holds native context, rbx points to fixed array of 3-element entries
// (native context, optimized code, literals).
// The optimized code map must never be empty, so check the first elements.
Label install_optimized;
@@ -452,8 +452,8 @@
void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
// Stack layout on entry:
//
- // [rsp + (1 * kPointerSize)]: function
- // [rsp + (2 * kPointerSize)]: serialized scope info
+ // [rsp + (1 * kPointerSize)] : function
+ // [rsp + (2 * kPointerSize)] : serialized scope info
// Try to allocate the context in new space.
Label gc;
@@ -1424,7 +1424,7 @@
// If the argument in rdx is already an object, we skip the
// allocation of a heap number.
__ JumpIfNotSmi(rdx, &skip_allocation);
- // Allocate a heap number for the result. Keep eax and edx intact
+ // Allocate a heap number for the result. Keep rax and rdx intact
// for the possible runtime call.
__ AllocateHeapNumber(rbx, rcx, alloc_failure);
// Now rdx can be overwritten losing one of the arguments as we are
@@ -1463,16 +1463,16 @@
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
// TAGGED case:
// Input:
- // rsp[8]: argument (should be number).
- // rsp[0]: return address.
+ // rsp[8] : argument (should be number).
+ // rsp[0] : return address.
// Output:
// rax: tagged double result.
// UNTAGGED case:
// Input::
- // rsp[0]: return address.
- // xmm1: untagged double input argument
+ // rsp[0] : return address.
+ // xmm1 : untagged double input argument
// Output:
- // xmm1: untagged double result.
+ // xmm1 : untagged double result.
Label runtime_call;
Label runtime_call_clear_stack;
@@ -2213,7 +2213,7 @@
__ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
// The stub is called from non-optimized code, which expects the result
- // as heap number in eax.
+ // as heap number in rax.
__ bind(&done);
__ AllocateHeapNumber(rax, rcx, &call_runtime);
__ movsd(FieldOperand(rax, HeapNumber::kValueOffset), double_result);
@@ -2424,8 +2424,8 @@
void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
// Stack layout:
- // rsp[0] : return address
- // rsp[8] : number of parameters (tagged)
+ // rsp[0] : return address
+ // rsp[8] : number of parameters (tagged)
// rsp[16] : receiver displacement
// rsp[24] : function
// Registers used over the whole function:
@@ -2640,10 +2640,10 @@
void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
- // esp[0] : return address
- // esp[8] : number of parameters
- // esp[16] : receiver displacement
- // esp[24] : function
+ // rsp[0] : return address
+ // rsp[8] : number of parameters
+ // rsp[16] : receiver displacement
+ // rsp[24] : function
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
@@ -2666,8 +2666,8 @@
void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
- // rsp[0] : return address
- // rsp[8] : number of parameters
+ // rsp[0] : return address
+ // rsp[8] : number of parameters
// rsp[16] : receiver displacement
// rsp[24] : function
@@ -2774,11 +2774,11 @@
#else // V8_INTERPRETED_REGEXP
// Stack frame on entry.
- // rsp[0]: return address
- // rsp[8]: last_match_info (expected JSArray)
- // rsp[16]: previous index
- // rsp[24]: subject string
- // rsp[32]: JSRegExp object
+ // rsp[0] : return address
+ // rsp[8] : last_match_info (expected JSArray)
+ // rsp[16] : previous index
+ // rsp[24] : subject string
+ // rsp[32] : JSRegExp object
static const int kLastMatchInfoOffset = 1 * kPointerSize;
static const int kPreviousIndexOffset = 2 * kPointerSize;
@@ -4383,14 +4383,14 @@
void InstanceofStub::Generate(MacroAssembler* masm) {
// Implements "value instanceof function" operator.
// Expected input state with no inline cache:
- // rsp[0] : return address
- // rsp[1] : function pointer
- // rsp[2] : value
+ // rsp[0] : return address
+ // rsp[8] : function pointer
+ // rsp[16] : value
// Expected input state with an inline one-element cache:
- // rsp[0] : return address
- // rsp[1] : offset from return address to location of inline cache
- // rsp[2] : function pointer
- // rsp[3] : value
+ // rsp[0] : return address
+ // rsp[8] : offset from return address to location of inline cache
+ // rsp[16] : function pointer
+ // rsp[24] : value
// Returns a bitwise zero to indicate that the value
// is and instance of the function and anything else to
// indicate that the value is not an instance.
@@ -5332,10 +5332,10 @@
Label runtime;
// Stack frame on entry.
- // rsp[0]: return address
- // rsp[8]: to
- // rsp[16]: from
- // rsp[24]: string
+ // rsp[0] : return address
+ // rsp[8] : to
+ // rsp[16] : from
+ // rsp[24] : string
const int kToOffset = 1 * kPointerSize;
const int kFromOffset = kToOffset + kPointerSize;
@@ -5694,9 +5694,9 @@
Label runtime;
// Stack frame on entry.
- // rsp[0]: return address
- // rsp[8]: right string
- // rsp[16]: left string
+ // rsp[0] : return address
+ // rsp[8] : right string
+ // rsp[16] : left string
__ movq(rdx, Operand(rsp, 2 * kPointerSize)); // left
__ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
@@ -6193,9 +6193,9 @@
// This stub overrides SometimesSetsUpAFrame() to return false. That means
// we cannot call anything that could cause a GC from this stub.
// Stack frame on entry:
- // esp[0 * kPointerSize]: return address.
- // esp[1 * kPointerSize]: key's hash.
- // esp[2 * kPointerSize]: key.
+ // rsp[0 * kPointerSize] : return address.
+ // rsp[1 * kPointerSize] : key's hash.
+ // rsp[2 * kPointerSize] : key.
// Registers:
// dictionary_: NameDictionary to probe.
// result_: used as scratch.
@@ -6576,11 +6576,11 @@
void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : element value to store
- // -- rcx : element index as smi
- // -- rsp[0] : return address
- // -- rsp[8] : array literal index in function
- // -- rsp[16]: array literal
+ // -- rax : element value to store
+ // -- rcx : element index as smi
+ // -- rsp[0] : return address
+ // -- rsp[8] : array literal index in function
+ // -- rsp[16] : array literal
// clobbers rbx, rdx, rdi
// -----------------------------------
@@ -6742,8 +6742,8 @@
// rdx - kind
// rax - number of arguments
// rdi - constructor?
- // esp[0] - return address
- // esp[4] - last argument
+ // rsp[0] - return address
+ // rsp[8] - last argument
ASSERT(FAST_SMI_ELEMENTS == 0);
ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
ASSERT(FAST_ELEMENTS == 2);
@@ -6841,11 +6841,11 @@
void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : argc
- // -- rbx : type info cell
- // -- rdi : constructor
+ // -- rax : argc
+ // -- rbx : type info cell
+ // -- rdi : constructor
// -- rsp[0] : return address
- // -- rsp[4] : last argument
+ // -- rsp[8] : last argument
// -----------------------------------
Handle<Object> undefined_sentinel(
masm->isolate()->heap()->undefined_value(),
@@ -6864,7 +6864,7 @@
__ CmpObjectType(rcx, MAP_TYPE, rcx);
__ Check(equal, "Unexpected initial map for Array function");
- // We should either have undefined in ebx or a valid cell
+ // We should either have undefined in rbx or a valid cell
Label okay_here;
Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
__ Cmp(rbx, undefined_sentinel);
@@ -6949,11 +6949,11 @@
void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- eax : argc
- // -- ebx : type info cell
- // -- edi : constructor
- // -- esp[0] : return address
- // -- esp[4] : last argument
+ // -- rax : argc
+ // -- rbx : type info cell
+ // -- rdi : constructor
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
// -----------------------------------
if (FLAG_debug_code) {
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index a0f3ead..a8de443 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -712,10 +712,10 @@
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
StrictModeFlag strict_mode) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
Label slow, slow_with_tagged_index, fast_object, fast_object_grow;
Label fast_double, fast_double_grow;
@@ -870,14 +870,14 @@
int argc,
Label* miss) {
// ----------- S t a t e -------------
- // rcx : function name
- // rdi : function
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rdi : function
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
__ JumpIfSmi(rdi, miss);
// Check that the value is a JavaScript function.
@@ -894,13 +894,13 @@
// The generated code falls through if the call should be handled by runtime.
void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Label miss;
@@ -924,13 +924,13 @@
IC::UtilityId id,
Code::ExtraICState extra_state) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Counters* counters = masm->isolate()->counters();
@@ -996,13 +996,13 @@
int argc,
Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
// Get the receiver of the function from the stack; 1 ~ return address.
@@ -1014,13 +1014,13 @@
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
// Get the receiver of the function from the stack; 1 ~ return address.
@@ -1125,13 +1125,13 @@
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
// Check if the name is really a name.
@@ -1230,7 +1230,7 @@
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
Label slow, notin;
Operand mapped_location =
@@ -1253,10 +1253,10 @@
void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
Label slow, notin;
Operand mapped_location = GenerateMappedArgumentsLookup(
@@ -1293,13 +1293,13 @@
void KeyedCallIC::GenerateNonStrictArguments(MacroAssembler* masm,
int argc) {
// ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Label slow, notin;
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
@@ -1385,7 +1385,7 @@
// ----------- S t a t e -------------
// -- rax : receiver
// -- rcx : name
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1402,7 +1402,7 @@
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
Counters* counters = masm->isolate()->counters();
@@ -1426,7 +1426,7 @@
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1527,10 +1527,10 @@
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
StrictModeFlag strict_mode) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1548,10 +1548,10 @@
void StoreIC::GenerateSlow(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1568,10 +1568,10 @@
void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1588,10 +1588,10 @@
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
@@ -1611,9 +1611,9 @@
void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rbx : target map
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rbx : target map
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
@@ -1636,9 +1636,9 @@
void KeyedStoreIC::GenerateTransitionElementsDoubleToObject(
MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rbx : target map
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rbx : target map
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index c97475a..de43f86 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -1553,13 +1553,6 @@
}
-void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) {
- Register result = ToRegister(instr->result());
- Register array = ToRegister(instr->value());
- __ movq(result, FieldOperand(array, FixedArrayBase::kLengthOffset));
-}
-
-
void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->value());
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 9c2373a..95a44f0 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1718,13 +1718,6 @@
}
-LInstruction* LChunkBuilder::DoFixedArrayBaseLength(
- HFixedArrayBaseLength* instr) {
- LOperand* array = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
-}
-
-
LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
LOperand* map = UseRegisterAtStart(instr->value());
return DefineAsRegister(new(zone()) LMapEnumLength(map));
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index d6edd6d..a7530be 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -99,7 +99,6 @@
V(DoubleToSmi) \
V(DummyUse) \
V(ElementsKind) \
- V(FixedArrayBaseLength) \
V(MapEnumLength) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
@@ -1233,20 +1232,6 @@
};
-class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> {
- public:
- explicit LFixedArrayBaseLength(LOperand* value) {
- inputs_[0] = value;
- }
-
- LOperand* value() { return inputs_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength,
- "fixed-array-base-length")
- DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength)
-};
-
-
class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LMapEnumLength(LOperand* value) {
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index fdeee31..e5bee67 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -31,6 +31,7 @@
#include "bootstrapper.h"
#include "codegen.h"
+#include "cpu-profiler.h"
#include "assembler-x64.h"
#include "macro-assembler-x64.h"
#include "serialize.h"
@@ -645,8 +646,8 @@
int num_arguments,
int result_size) {
// ----------- S t a t e -------------
- // -- rsp[0] : return address
- // -- rsp[8] : argument num_arguments - 1
+ // -- rsp[0] : return address
+ // -- rsp[8] : argument num_arguments - 1
// ...
// -- rsp[8 * num_arguments] : argument 0 (receiver)
// -----------------------------------
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 14228cc..124153b 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -1252,7 +1252,7 @@
int return_value_offset_from_rbp);
// Before calling a C-function from generated code, align arguments on stack.
- // After aligning the frame, arguments must be stored in esp[0], esp[4],
+ // After aligning the frame, arguments must be stored in rsp[0], rsp[8],
// etc., not pushed. The argument count assumes all arguments are word sized.
// The number of slots reserved for arguments depends on platform. On Windows
// stack slots are reserved for the arguments passed in registers. On other
diff --git a/src/x64/regexp-macro-assembler-x64.cc b/src/x64/regexp-macro-assembler-x64.cc
index e1d4a2d..106ffb7 100644
--- a/src/x64/regexp-macro-assembler-x64.cc
+++ b/src/x64/regexp-macro-assembler-x64.cc
@@ -29,6 +29,7 @@
#if V8_TARGET_ARCH_X64
+#include "cpu-profiler.h"
#include "serialize.h"
#include "unicode.h"
#include "log.h"
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 1a992b4..4b3ee40 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -423,10 +423,11 @@
// Undoes the effects of ReserveSpaceForFastApiCall.
static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
// ----------- S t a t e -------------
- // -- rsp[0] : return address.
- // -- rsp[8] : last fast api call extra argument.
+ // -- rsp[0] : return address.
+ // -- rsp[8] : last fast api call extra argument.
// -- ...
- // -- rsp[kFastApiCallArguments * 8] : first fast api call extra argument.
+ // -- rsp[kFastApiCallArguments * 8] : first fast api call extra
+ // argument.
// -- rsp[kFastApiCallArguments * 8 + 8] : last argument in the internal
// frame.
// -----------------------------------
@@ -3082,7 +3083,7 @@
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow);
@@ -3090,7 +3091,7 @@
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
TailCallBuiltin(masm, Builtins::kKeyedLoadIC_MissForceGeneric);
}
@@ -3125,10 +3126,10 @@
MacroAssembler* masm,
ElementsKind elements_kind) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
Label slow, miss_force_generic;
@@ -3285,10 +3286,10 @@
__ bind(&slow);
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
// -----------------------------------
TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
diff --git a/src/zone.cc b/src/zone.cc
index 82a2efa..2a0a0e2 100644
--- a/src/zone.cc
+++ b/src/zone.cc
@@ -78,31 +78,82 @@
Zone::~Zone() {
+ DeleteAll();
+ DeleteKeptSegment();
+
+ ASSERT(segment_bytes_allocated_ == 0);
+}
+
+
+void Zone::DeleteAll() {
#ifdef DEBUG
// Constant byte value used for zapping dead memory in debug mode.
static const unsigned char kZapDeadByte = 0xcd;
#endif
- // Traverse the chained list of segments, zapping
- // (in debug mode) and freeing every segment
- Segment* current = segment_head_;
- while (current != NULL) {
+ // Find a segment with a suitable size to keep around.
+ Segment* keep = segment_head_;
+ while (keep != NULL && keep->size() > kMaximumKeptSegmentSize) {
+ keep = keep->next();
+ }
+
+ // Traverse the chained list of segments, zapping (in debug mode)
+ // and freeing every segment except the one we wish to keep.
+ for (Segment* current = segment_head_; current != NULL; ) {
Segment* next = current->next();
- int size = current->size();
+ if (current == keep) {
+ // Unlink the segment we wish to keep from the list.
+ current->clear_next();
+ } else {
+ int size = current->size();
#ifdef DEBUG
- // Zap the entire current segment (including the header).
- memset(current, kZapDeadByte, size);
+ // Zap the entire current segment (including the header).
+ memset(current, kZapDeadByte, size);
#endif
- DeleteSegment(current, size);
+ DeleteSegment(current, size);
+ }
current = next;
}
- // We must clear the position and limit to force
- // a new segment to be allocated on demand.
- position_ = limit_ = 0;
+ // If we have found a segment we want to keep, we must recompute the
+ // variables 'position' and 'limit' to prepare for future allocate
+ // attempts. Otherwise, we must clear the position and limit to
+ // force a new segment to be allocated on demand.
+ if (keep != NULL) {
+ Address start = keep->start();
+ position_ = RoundUp(start, kAlignment);
+ limit_ = keep->end();
+#ifdef DEBUG
+ // Zap the contents of the kept segment (but not the header).
+ memset(start, kZapDeadByte, keep->capacity());
+#endif
+ } else {
+ position_ = limit_ = 0;
+ }
- // Update the head segment.
- segment_head_ = NULL;
+ // Update the head segment to be the kept segment (if any).
+ segment_head_ = keep;
+}
+
+
+void Zone::DeleteKeptSegment() {
+#ifdef DEBUG
+ // Constant byte value used for zapping dead memory in debug mode.
+ static const unsigned char kZapDeadByte = 0xcd;
+#endif
+
+ ASSERT(segment_head_ == NULL || segment_head_->next() == NULL);
+ if (segment_head_ != NULL) {
+ int size = segment_head_->size();
+#ifdef DEBUG
+ // Zap the entire kept segment (including the header).
+ memset(segment_head_, kZapDeadByte, size);
+#endif
+ DeleteSegment(segment_head_, size);
+ segment_head_ = NULL;
+ }
+
+ ASSERT(segment_bytes_allocated_ == 0);
}
diff --git a/src/zone.h b/src/zone.h
index 5545e3c..a12ed79 100644
--- a/src/zone.h
+++ b/src/zone.h
@@ -66,6 +66,14 @@
template <typename T>
inline T* NewArray(int length);
+ // Deletes all objects and free all memory allocated in the Zone. Keeps one
+ // small (size <= kMaximumKeptSegmentSize) segment around if it finds one.
+ void DeleteAll();
+
+ // Deletes the last small segment kept around by DeleteAll(). You
+ // may no longer allocate in the Zone after a call to this method.
+ void DeleteKeptSegment();
+
// Returns true if more memory has been allocated in zones than
// the limit allows.
inline bool excess_allocation();
@@ -90,6 +98,9 @@
// Never allocate segments larger than this size in bytes.
static const int kMaximumSegmentSize = 1 * MB;
+ // Never keep segments larger than this size in bytes around.
+ static const int kMaximumKeptSegmentSize = 64 * KB;
+
// Report zone excess when allocation exceeds this limit.
static const int kExcessLimit = 256 * MB;
@@ -109,10 +120,10 @@
// Creates a new segment, sets it size, and pushes it to the front
// of the segment chain. Returns the new segment.
- Segment* NewSegment(int size);
+ INLINE(Segment* NewSegment(int size));
// Deletes the given segment. Does not touch the segment chain.
- void DeleteSegment(Segment* segment, int size);
+ INLINE(void DeleteSegment(Segment* segment, int size));
// The free region in the current (front) segment is represented as
// the half-open interval [position, limit). The 'position' variable
@@ -145,6 +156,20 @@
};
+// The ZoneScope is used to automatically call DeleteAll() on a
+// Zone when the ZoneScope is destroyed (i.e. goes out of scope)
+struct ZoneScope {
+ public:
+ explicit ZoneScope(Zone* zone) : zone_(zone) { }
+ ~ZoneScope() { zone_->DeleteAll(); }
+
+ Zone* zone() { return zone_; }
+
+ private:
+ Zone* zone_;
+};
+
+
// The ZoneAllocationPolicy is used to specialize generic data
// structures to allocate themselves and their elements in the Zone.
struct ZoneAllocationPolicy {
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index f3a181a..e37f3db 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -78,6 +78,12 @@
# BUG(2657): Test sometimes times out on MIPS simulator.
test-thread-termination/TerminateMultipleV8ThreadsDefaultIsolate: PASS || TIMEOUT
+# BUG(1075): Unresolved crashes on MIPS also.
+test-serialize/Deserialize: SKIP
+test-serialize/DeserializeFromSecondSerializationAndRunScript2: SKIP
+test-serialize/DeserializeAndRunScript2: SKIP
+test-serialize/DeserializeFromSecondSerialization: SKIP
+
##############################################################################
[ $arch == android_arm || $arch == android_ia32 ]
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index f8b492b..34432e3 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -38,16 +38,17 @@
#include "api.h"
#include "arguments.h"
-#include "isolate.h"
-#include "compilation-cache.h"
-#include "execution.h"
-#include "objects.h"
-#include "snapshot.h"
-#include "platform.h"
-#include "utils.h"
#include "cctest.h"
+#include "compilation-cache.h"
+#include "cpu-profiler.h"
+#include "execution.h"
+#include "isolate.h"
+#include "objects.h"
#include "parser.h"
+#include "platform.h"
+#include "snapshot.h"
#include "unicode-inl.h"
+#include "utils.h"
static const bool kLogThreading = false;
@@ -3714,7 +3715,7 @@
static const int K = 1024;
v8::ResourceConstraints constraints;
constraints.set_max_young_space_size(256 * K);
- constraints.set_max_old_space_size(4 * K * K);
+ constraints.set_max_old_space_size(5 * K * K);
v8::SetResourceConstraints(&constraints);
// Execute a script that causes out of memory.
@@ -3755,7 +3756,7 @@
static const int K = 1024;
v8::ResourceConstraints constraints;
constraints.set_max_young_space_size(256 * K);
- constraints.set_max_old_space_size(4 * K * K);
+ constraints.set_max_old_space_size(5 * K * K);
v8::SetResourceConstraints(&constraints);
v8::HandleScope scope(v8::Isolate::GetCurrent());
@@ -3784,7 +3785,7 @@
static const int K = 1024;
v8::ResourceConstraints constraints;
constraints.set_max_young_space_size(256 * K);
- constraints.set_max_old_space_size(3 * K * K);
+ constraints.set_max_old_space_size(4 * K * K);
v8::SetResourceConstraints(&constraints);
// Execute a script that causes out of memory.
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 223f5fc..e59687b 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -1087,10 +1087,16 @@
CheckChildrenNames(root, names);
}
- const v8::CpuProfileNode* startNode = GetChild(root, "start");
- {
- ScopedVector<v8::Handle<v8::String> > names(1);
+ // In case of GC stress tests all samples may be in GC phase and there
+ // won't be |start| node in the profiles.
+ bool is_gc_stress_testing =
+ (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
+ const v8::CpuProfileNode* startNode = FindChild(root, "start");
+ CHECK(is_gc_stress_testing || startNode);
+ if (startNode) {
+ ScopedVector<v8::Handle<v8::String> > names(2);
names[0] = v8::String::New("bar");
+ names[1] = v8::String::New("call");
CheckChildrenNames(startNode, names);
}
@@ -1105,3 +1111,94 @@
cpu_profiler->DeleteAllCpuProfiles();
}
+
+static const char* function_apply_test_source = "function bar(iterations) {\n"
+"}\n"
+"function test() {\n"
+" bar.apply(this, [10 * 1000]);\n"
+"}\n"
+"function start(duration) {\n"
+" var start = Date.now();\n"
+" while (Date.now() - start < duration) {\n"
+" try {\n"
+" test();\n"
+" } catch(e) {}\n"
+" }\n"
+"}";
+
+
+// [Top down]:
+// 94 0 (root) [-1] #0 1
+// 2 2 (garbage collector) [-1] #0 7
+// 82 49 start [-1] #16 3
+// 1 0 (unresolved function) [-1] #0 8
+// 1 1 apply [-1] #0 9
+// 32 21 test [-1] #16 4
+// 2 2 bar [-1] #16 6
+// 9 9 apply [-1] #0 5
+// 10 10 (program) [-1] #0 2
+TEST(FunctionApplySample) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ v8::Script::Compile(v8::String::New(function_apply_test_source))->Run();
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("start")));
+
+ v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
+ v8::Local<v8::String> profile_name = v8::String::New("my_profile");
+
+ cpu_profiler->StartCpuProfiling(profile_name);
+ int32_t duration_ms = 100;
+#if defined(_WIN32) || defined(_WIN64)
+ // 100ms is not enough on Windows. See
+ // https://code.google.com/p/v8/issues/detail?id=2628
+ duration_ms = 400;
+#endif
+ v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
+ function->Call(env->Global(), ARRAY_SIZE(args), args);
+ const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
+
+ CHECK_NE(NULL, profile);
+ // Dump collected profile to have a better diagnostic in case of failure.
+ reinterpret_cast<i::CpuProfile*>(
+ const_cast<v8::CpuProfile*>(profile))->Print();
+
+ const v8::CpuProfileNode* root = profile->GetTopDownRoot();
+ {
+ ScopedVector<v8::Handle<v8::String> > names(3);
+ names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
+ names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
+ names[2] = v8::String::New("start");
+ // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
+ CheckChildrenNames(root, names);
+ }
+
+ const v8::CpuProfileNode* startNode = FindChild(root, "start");
+ if (startNode) {
+ {
+ ScopedVector<v8::Handle<v8::String> > names(2);
+ names[0] = v8::String::New("test");
+ names[1] = v8::String::New(ProfileGenerator::kUnresolvedFunctionName);
+ CheckChildrenNames(startNode, names);
+ }
+
+ const v8::CpuProfileNode* testNode = FindChild(startNode, "test");
+ if (testNode) {
+ ScopedVector<v8::Handle<v8::String> > names(2);
+ names[0] = v8::String::New("bar");
+ names[1] = v8::String::New("apply");
+ CheckChildrenNames(testNode, names);
+ }
+
+ if (const v8::CpuProfileNode* unresolvedNode =
+ FindChild(startNode, ProfileGenerator::kUnresolvedFunctionName)) {
+ ScopedVector<v8::Handle<v8::String> > names(1);
+ names[0] = v8::String::New("apply");
+ CheckChildrenNames(unresolvedNode, names);
+ GetChild(unresolvedNode, "apply");
+ }
+ }
+
+ cpu_profiler->DeleteAllCpuProfiles();
+}
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index afb06fc..5973941 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -1621,10 +1621,10 @@
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("weaks"));
const v8::HeapGraphNode* gc_roots = GetNode(
- snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
+ snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
CHECK_NE(NULL, gc_roots);
const v8::HeapGraphNode* global_handles = GetNode(
- gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
+ gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
CHECK_NE(NULL, global_handles);
return HasWeakEdge(global_handles);
}
@@ -1658,10 +1658,10 @@
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("weaks"));
const v8::HeapGraphNode* gc_roots = GetNode(
- snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
+ snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
CHECK_NE(NULL, gc_roots);
const v8::HeapGraphNode* global_handles = GetNode(
- gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
+ gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
CHECK_NE(NULL, global_handles);
const v8::HeapGraphNode* native_context = GetNode(
global_handles, v8::HeapGraphNode::kHidden, "system / NativeContext");
@@ -1726,10 +1726,10 @@
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
const v8::HeapGraphNode* gc_roots = GetNode(
- snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
+ snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
CHECK_NE(NULL, gc_roots);
const v8::HeapGraphNode* strong_roots = GetNode(
- gc_roots, v8::HeapGraphNode::kObject, "(Strong roots)");
+ gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
CHECK_NE(NULL, strong_roots);
for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index e9b23ed..be1098c 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -1972,10 +1972,10 @@
CHECK(!space->LastPage()->Contains(
map->GetPrototypeTransitions()->address()));
CHECK(space->LastPage()->Contains(prototype->address()));
- baseObject->SetPrototype(*prototype, false)->ToObjectChecked();
- CHECK(map->GetPrototypeTransition(*prototype)->IsMap());
+ JSObject::SetPrototype(baseObject, prototype, false);
+ CHECK(Map::GetPrototypeTransition(map, prototype)->IsMap());
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
- CHECK(map->GetPrototypeTransition(*prototype)->IsMap());
+ CHECK(Map::GetPrototypeTransition(map, prototype)->IsMap());
}
diff --git a/test/cctest/test-log-stack-tracer.cc b/test/cctest/test-log-stack-tracer.cc
index 4333498..20f135d 100644
--- a/test/cctest/test-log-stack-tracer.cc
+++ b/test/cctest/test-log-stack-tracer.cc
@@ -32,11 +32,12 @@
#include "v8.h"
#include "api.h"
-#include "codegen.h"
-#include "log.h"
-#include "isolate.h"
#include "cctest.h"
+#include "codegen.h"
#include "disassembler.h"
+#include "isolate.h"
+#include "log.h"
+#include "sampler.h"
#include "vm-state-inl.h"
using v8::Function;
@@ -51,6 +52,7 @@
using v8::internal::Handle;
using v8::internal::Isolate;
using v8::internal::JSFunction;
+using v8::internal::RegisterState;
using v8::internal::TickSample;
@@ -65,11 +67,12 @@
static void DoTrace(Address fp) {
- trace_env.sample->fp = fp;
+ RegisterState regs;
+ regs.fp = fp;
// sp is only used to define stack high bound
- trace_env.sample->sp =
+ regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240;
- trace_env.sample->Trace(Isolate::Current());
+ trace_env.sample->Init(Isolate::Current(), regs);
}
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index 62a5bcc..9879886 100644
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -1048,8 +1048,9 @@
i::Handle<i::String> FormatMessage(i::ScriptDataImpl* data) {
i::Isolate* isolate = i::Isolate::Current();
i::Factory* factory = isolate->factory();
+ const char* message = data->BuildMessage();
i::Handle<i::String> format = v8::Utils::OpenHandle(
- *v8::String::New(data->BuildMessage()));
+ *v8::String::New(message));
i::Vector<const char*> args = data->BuildArgs();
i::Handle<i::JSArray> args_array = factory->NewJSArray(args.length());
for (int i = 0; i < args.length(); i++) {
@@ -1068,6 +1069,11 @@
i::Execution::Call(format_fun, builtins, 2, arg_handles, &has_exception);
CHECK(!has_exception);
CHECK(result->IsString());
+ for (int i = 0; i < args.length(); i++) {
+ i::DeleteArray(args[i]);
+ }
+ i::DeleteArray(args.start());
+ i::DeleteArray(message);
return i::Handle<i::String>::cast(result);
}
@@ -1279,7 +1285,7 @@
+ kSuffixLen + i::StrLength("label: for (;;) { }");
// Plug the source code pieces together.
- i::Vector<char> program = i::Vector<char>::New(kProgramSize + 1);
+ i::ScopedVector<char> program(kProgramSize + 1);
int length = i::OS::SNPrintF(program,
"label: for (;;) { %s%s%s%s }",
context_data[i][0],
diff --git a/test/cctest/test-profile-generator.cc b/test/cctest/test-profile-generator.cc
index dfcc75c..7472669 100644
--- a/test/cctest/test-profile-generator.cc
+++ b/test/cctest/test-profile-generator.cc
@@ -30,6 +30,7 @@
#include "v8.h"
#include "profile-generator-inl.h"
#include "cctest.h"
+#include "cpu-profiler.h"
#include "../include/v8-profiler.h"
using i::CodeEntry;
diff --git a/test/mjsunit/date.js b/test/mjsunit/date.js
index 5aaa3bb..a1b7871 100644
--- a/test/mjsunit/date.js
+++ b/test/mjsunit/date.js
@@ -150,7 +150,7 @@
// date without the timezone information.
function testToLocaleTimeString() {
var d = new Date();
- var s = d.toLocaleTimeString();
+ var s = d.toLocaleTimeString("en-GB");
assertEquals(8, s.length);
}
@@ -340,4 +340,4 @@
%OptimizeFunctionOnNextCall(Date.prototype.getYear);
assertThrows(function() { Date.prototype.getYear.call(""); }, TypeError);
opt_status = %GetOptimizationStatus(Date.prototype.getYear);
-assertTrue(%GetOptimizationStatus(Date.prototype.getTime) != 1);
\ No newline at end of file
+assertTrue(%GetOptimizationStatus(Date.prototype.getTime) != 1);
diff --git a/test/mjsunit/debug-script.js b/test/mjsunit/debug-script.js
index afaa369..c456e6b 100644
--- a/test/mjsunit/debug-script.js
+++ b/test/mjsunit/debug-script.js
@@ -61,9 +61,8 @@
// This has to be updated if the number of native scripts change.
assertEquals(16, named_native_count);
-// If no snapshot is used, only the 'gc' extension is loaded.
-// If snapshot is used, all extensions are cached in the snapshot.
-assertTrue(extension_count == 1 || extension_count == 5);
+// Only the 'gc' and (depending on flags) the 'i18n' extensions are loaded.
+assertTrue(extension_count == 1 || extension_count == 2);
// This script and mjsunit.js has been loaded. If using d8, d8 loads
// a normal script during startup too.
assertTrue(normal_count == 2 || normal_count == 3);
diff --git a/test/mjsunit/function-call.js b/test/mjsunit/function-call.js
index 92792ac..38be10c 100644
--- a/test/mjsunit/function-call.js
+++ b/test/mjsunit/function-call.js
@@ -151,6 +151,7 @@
function checkExpectedMessage(e) {
assertTrue(e.message.indexOf("called on null or undefined") >= 0 ||
+ e.message.indexOf("invoked on undefined or null value") >= 0 ||
e.message.indexOf("Cannot convert null to object") >= 0);
}
diff --git a/test/mjsunit/regress/regress-crbug-217858.js b/test/mjsunit/regress/regress-crbug-217858.js
index 8563e07..e61cb9f 100644
--- a/test/mjsunit/regress/regress-crbug-217858.js
+++ b/test/mjsunit/regress/regress-crbug-217858.js
@@ -25,16 +25,13 @@
// (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
+// Flags: --noanalyze_environment_liveness
var r = /r/;
-var a = "";
function f() {
- %OptimizeFunctionOnNextCall(f, "osr");
- for (var i = 0; i < 1000000; i++) {
- a += i.toString();
- r[r] = function() {};
- }
+ r[r] = function() {};
}
-f();
+for (var i = 0; i < 300000; i++) {
+ f();
+}
diff --git a/test/mjsunit/tools/tickprocessor-test-func-info.log b/test/mjsunit/tools/tickprocessor-test-func-info.log
index 5e64dc0..94aa56d 100644
--- a/test/mjsunit/tools/tickprocessor-test-func-info.log
+++ b/test/mjsunit/tools/tickprocessor-test-func-info.log
@@ -5,7 +5,7 @@
code-creation,Stub,0,0x424260,348,"CompareStub_GE"
code-creation,LazyCompile,0,0x2a8100,18535,"DrawQube 3d-cube.js:188",0xf43abcac,
code-creation,LazyCompile,0,0x480100,3908,"DrawLine 3d-cube.js:17",0xf43abc50,
-tick,0x424284,0xbfffeea0,0,0,0x480600,0,0x2aaaa5
-tick,0x42429f,0xbfffed88,0,0,0x480600,0,0x2aacb4
-tick,0x48063d,0xbfffec7c,0,0,0x2d0f7c,0,0x2aaec6
+tick,0x424284,0,0,0x480600,0,0x2aaaa5
+tick,0x42429f,0,0,0x480600,0,0x2aacb4
+tick,0x48063d,0,0,0x2d0f7c,0,0x2aaec6
profiler,"end"
diff --git a/test/mjsunit/tools/tickprocessor-test.log b/test/mjsunit/tools/tickprocessor-test.log
index 5ddad89..cf8b90d 100644
--- a/test/mjsunit/tools/tickprocessor-test.log
+++ b/test/mjsunit/tools/tickprocessor-test.log
@@ -9,17 +9,17 @@
function-creation,0xf441d280,0xf541d120
code-creation,LoadIC,0,0xf541d280,117,"j"
code-creation,LoadIC,0,0xf541d360,63,"i"
-tick,0x80f82d1,0xffdfe880,0,0,0,0,0xf541ce5c
-tick,0x80f89a1,0xffdfecf0,0,0,0,0,0xf541ce5c
-tick,0x8123b5c,0xffdff1a0,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0x8123b65,0xffdff1a0,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xf541d2be,0xffdff1e4,0,0,0,0
-tick,0xf541d320,0xffdff1dc,0,0,0,0
-tick,0xf541d384,0xffdff1d8,0,0,0,0
-tick,0xf7db94da,0xffdff0ec,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xf7db951c,0xffdff0f0,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xf7dbc508,0xffdff14c,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xf7dbff21,0xffdff198,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xf7edec90,0xffdff0ec,0,0,0,0,0xf541d1a1,0xf541ceea
-tick,0xffffe402,0xffdff488,0,0,0,0
+tick,0x80f82d1,0,0,0,0,0xf541ce5c
+tick,0x80f89a1,0,0,0,0,0xf541ce5c
+tick,0x8123b5c,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0x8123b65,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xf541d2be,0,0,0,0
+tick,0xf541d320,0,0,0,0
+tick,0xf541d384,0,0,0,0
+tick,0xf7db94da,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xf7db951c,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xf7dbc508,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xf7dbff21,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xf7edec90,0,0,0,0,0xf541d1a1,0xf541ceea
+tick,0xffffe402,0,0,0,0
profiler,"end"
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 26e3299..892e437 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -129,6 +129,11 @@
],
},
}],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ }],
],
'dependencies': [
'v8_base.<(v8_target_arch)',
@@ -192,6 +197,11 @@
'V8_SHARED',
],
}],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ }],
]
},
{
@@ -337,8 +347,12 @@
'../../src/hydrogen-gvn.h',
'../../src/hydrogen-infer-representation.cc',
'../../src/hydrogen-infer-representation.h',
+ '../../src/hydrogen-range-analysis.cc',
+ '../../src/hydrogen-range-analysis.h',
'../../src/hydrogen-uint32-analysis.cc',
'../../src/hydrogen-uint32-analysis.h',
+ '../../src/hydrogen-osr.cc',
+ '../../src/hydrogen-osr.h',
'../../src/ic-inl.h',
'../../src/ic.cc',
'../../src/ic.h',
@@ -781,6 +795,27 @@
'<(SHARED_INTERMEDIATE_DIR)/debug-support.cc',
]
}],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '../../src/extensions/i18n/break-iterator.cc',
+ '../../src/extensions/i18n/break-iterator.h',
+ '../../src/extensions/i18n/collator.cc',
+ '../../src/extensions/i18n/collator.h',
+ '../../src/extensions/i18n/date-format.cc',
+ '../../src/extensions/i18n/date-format.h',
+ '../../src/extensions/i18n/i18n-extension.cc',
+ '../../src/extensions/i18n/i18n-extension.h',
+ '../../src/extensions/i18n/i18n-utils.cc',
+ '../../src/extensions/i18n/i18n-utils.h',
+ '../../src/extensions/i18n/locale.cc',
+ '../../src/extensions/i18n/locale.h',
+ '../../src/extensions/i18n/number-format.cc',
+ '../../src/extensions/i18n/number-format.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:*',
+ ]
+ }],
],
},
{
@@ -792,6 +827,26 @@
}, {
'toolsets': ['target'],
}],
+ ['v8_enable_i18n_support==1', {
+ 'actions': [{
+ 'action_name': 'js2c_i18n',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(i18n_library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'I18N',
+ '<(v8_compress_startup_data)',
+ '<@(i18n_library_files)'
+ ],
+ }],
+ }],
],
'variables': {
'library_files': [
@@ -821,6 +876,18 @@
'../../src/typedarray.js',
'../../src/generator.js'
],
+ 'i18n_library_files': [
+ '../../src/extensions/i18n/header.js',
+ '../../src/extensions/i18n/globals.js',
+ '../../src/extensions/i18n/locale.js',
+ '../../src/extensions/i18n/collator.js',
+ '../../src/extensions/i18n/number-format.js',
+ '../../src/extensions/i18n/date-format.js',
+ '../../src/extensions/i18n/break-iterator.js',
+ '../../src/extensions/i18n/i18n-utils.js',
+ '../../src/extensions/i18n/overrides.js',
+ '../../src/extensions/i18n/footer.js',
+ ],
},
'actions': [
{
diff --git a/tools/perf_tests/chromium_revision b/tools/perf_tests/chromium_revision
index 9f731f9..0cdcc11 100644
--- a/tools/perf_tests/chromium_revision
+++ b/tools/perf_tests/chromium_revision
@@ -1 +1 @@
-209619
+210122
diff --git a/tools/profviz/composer.js b/tools/profviz/composer.js
index 2f78179..bcc17b2 100644
--- a/tools/profviz/composer.js
+++ b/tools/profviz/composer.js
@@ -41,7 +41,9 @@
var kStackFrameWidth = 0.1; // Width of the lower stack frame lines.
var kGapWidth = 0.05; // Gap between stack frame lines.
- var kY1Offset = 10; // Offset for stack frame vs. event lines.
+ var kY1Offset = 11; // Offset for stack frame vs. event lines.
+ var kDeoptRow = 7; // Row displaying deopts.
+ var kMaxDeoptLength = 4; // Draw size of the largest deopt.
var kPauseLabelPadding = 5; // Padding for pause time labels.
var kNumPauseLabels = 7; // Number of biggest pauses to label.
var kCodeKindLabelPadding = 100; // Padding for code kind labels.
@@ -52,6 +54,9 @@
var kNumThreads = 2; // Number of threads.
var kExecutionThreadId = 0; // ID of main thread.
+ // Init values.
+ var num_timer_event = kY1Offset + 0.5;
+
// Data structures.
function TimerEvent(label, color, pause, thread_id) {
assert(thread_id >= 0 && thread_id < kNumThreads, "invalid thread id");
@@ -72,9 +77,13 @@
}
function Range(start, end) {
- // Everthing here are in milliseconds.
- this.start = start;
- this.end = end;
+ this.start = start; // In milliseconds.
+ this.end = end; // In milliseconds.
+ }
+
+ function Deopt(time, size) {
+ this.time = time; // In milliseconds.
+ this.size = size; // In bytes.
}
Range.prototype.duration = function() { return this.end - this.start; }
@@ -83,9 +92,6 @@
this.tick = tick;
}
- // Init values.
- var num_timer_event = kY1Offset + 0.5;
-
var TimerEvents = {
'V8.Execute':
new TimerEvent("execution", "#000000", false, 0),
@@ -127,6 +133,7 @@
var code_map = new CodeMap();
var execution_pauses = [];
+ var deopts = [];
var event_stack = [];
var last_time_stamp = [];
for (var i = 0; i < kNumThreads; i++) {
@@ -312,6 +319,10 @@
code_map.deleteCode(address);
};
+ var processCodeDeoptEvent = function(time, size) {
+ deopts.push(new Deopt(time, size));
+ }
+
var processSharedLibrary = function(name, start, end) {
var code_entry = new CodeMap.CodeEntry(end - start, name);
code_entry.kind = -3; // External code kind.
@@ -352,6 +363,8 @@
processor: processCodeMoveEvent },
'code-delete': { parsers: [parseInt],
processor: processCodeDeleteEvent },
+ 'code-deopt': { parsers: [parseTimeStamp, parseInt],
+ processor: processCodeDeoptEvent },
'tick': { parsers: [parseInt, parseInt, parseTimeStamp,
null, null, parseInt, 'var-args'],
processor: processTickEvent }
@@ -422,18 +435,28 @@
output("set style fill pattern 2 bo 1");
output("set style rect fs solid 1 noborder");
output("set style line 1 lt 1 lw 1 lc rgb \"#000000\"");
+ output("set border 15 lw 0.2"); // Draw thin border box.
+ output("set style line 2 lt 1 lw 1 lc rgb \"#9944CC\"");
output("set xtics out nomirror");
output("unset key");
- function DrawBar(row, color, start, end, width) {
+ function DrawBarBase(color, start, end, top, bottom) {
obj_index++;
command = "set object " + obj_index + " rect";
- command += " from " + start + ", " + (row - width);
- command += " to " + end + ", " + (row + width);
+ command += " from " + start + ", " + top;
+ command += " to " + end + ", " + bottom;
command += " fc rgb \"" + color + "\"";
output(command);
}
+ function DrawBar(row, color, start, end, width) {
+ DrawBarBase(color, start, end, row + width, row - width);
+ }
+
+ function DrawHalfBar(row, color, start, end, width) {
+ DrawBarBase(color, start, end, row, row - width);
+ }
+
var percentages = {};
var total = 0;
for (var name in TimerEvents) {
@@ -446,6 +469,17 @@
percentages[name] = (sum / (range_end - range_start) * 100).toFixed(1);
}
+ // Plot deopts.
+ deopts.sort(function(a, b) { return b.size - a.size; });
+ var max_deopt_size = deopts.length > 0 ? deopts[0].size : Infinity;
+
+ for (var i = 0; i < deopts.length; i++) {
+ var deopt = deopts[i];
+ DrawHalfBar(kDeoptRow, "#9944CC", deopt.time,
+ deopt.time + 10 * pause_tolerance,
+ deopt.size / max_deopt_size * kMaxDeoptLength);
+ }
+
// Name Y-axis.
var ytics = [];
for (name in TimerEvents) {
@@ -458,6 +492,8 @@
ytics.push('"top ' + kStackFrames + ' js stack frames"' + ' ' +
(kY1Offset - 2));
ytics.push('"pause times" 0');
+ ytics.push('"max deopt size: ' + (max_deopt_size / 1024).toFixed(1) +
+ ' kB" ' + kDeoptRow);
output("set ytics out nomirror (" + ytics.join(', ') + ")");
// Plot timeline.
diff --git a/tools/profviz/profviz.css b/tools/profviz/profviz.css
index 4042f15..c583b89 100644
--- a/tools/profviz/profviz.css
+++ b/tools/profviz/profviz.css
@@ -31,40 +31,75 @@
}
#content {
- background-color: #fff;
+ background-color: #fff;
width: 1200px;
margin-left: auto;
margin-right: auto;
padding: 25px;
}
-textarea.log {
- font-family: monospace;
+textarea {
width: 1200px;
- background-color: #ffe;
- color: #000;
resize: none;
+ font-family: monospace;
+ font-size: 12px;
+ color: #000;
+ border: 1px dotted #aaa;
+ padding: 10px;
+ box-sizing: border-box;
}
-button#start {
- width: 100px;
+textarea.log {
+ background-color: #ffe;
}
-button#reset {
- width: 100px;
-}
-
-input.range {
- width: 80px;
- text-align: right;
- padding-right: 5px;
+.display {
+ width: 1200px;
+ height: 600px;
+ background-color: #fff;
+ display: block;
+ box-sizing: border-box;
}
table {
width: 1200px;
}
+button {
+ width: 100px;
+ height: 20px;
+ border: 1px solid #000;
+ border-color: #aaa;
+ font-family: Verdana;
+ font-size: 12px;
+ background-color: #ddd;
+}
+
+button:hover {
+ background-color: #eee;
+}
+
+#file {
+ width: 200px;
+ height: 20px;
+ border: none;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+input.range {
+ width: 70px;
+ height: 16px;
+ text-align: right;
+ padding-right: 5px;
+ border: 0px;
+ background-color: #eee;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
label {
+ height: 20px;
font-family: Verdana;
font-size: 12px;
}
@@ -96,3 +131,8 @@
text-decoration: none;
color: #282;
}
+
+a.unroll {
+ border-bottom: 1px dotted #000;
+ color: #222;
+}
diff --git a/tools/profviz/profviz.html b/tools/profviz/profviz.html
index 1b9fc4d..30494f8 100644
--- a/tools/profviz/profviz.html
+++ b/tools/profviz/profviz.html
@@ -36,24 +36,29 @@
<body onload="onload()">
<div id="content">
- <img src="" id="plot" type="image/svg+xml"
+ <img src="" id="plot" type="image/svg+xml" class="display"
width="1200" height="600" class="float-right"/>
+
+ <textarea id="prof" class="display" disabled=true></textarea>
<br/>
- <table>
+ <table width="1200">
<tr>
- <td width="20%">
- <button type="button" id="start" onclick="start()">
- Start plot
+ <td width="330">
+ <button id="start" onclick="start()">
+ Start
</button>
- <button type="button" id="reset" onclick="ui.reset(); worker.reset();">
+ <button id="reset" onclick="ui.reset(); worker.reset();">
Reset
</button>
+ <button id="toggledisplay" onclick="ui.toggle();">
+ Show profile
+ </button>
</td>
- <td width="20%">
+ <td width="220">
<input type="file" id="file" onchange="ui.reset();"/>
</td>
- <td width="30%">
+ <td width="300">
<label title="You can manually choose the range
to plot only part of the log file.">
<span class="tooltip">Range</span>:
@@ -62,7 +67,7 @@
<label>to</label>
<input type="text" id="range_end" class="range"/>
</td>
- <td width="30%">
+ <td width="350">
<label title="We model profiling overhead by accounting a constant
execution delay to each log entry. Adjust to better suit
your computer's performance.">
@@ -78,7 +83,11 @@
<textarea class="log" id="log" rows="8" disabled=true></textarea>
<div class="text">
- <h1>Instructions</h1>
+ <h1>
+ <a href="javascript:ui.info('instructions');" class="unroll">
+ Instructions
+ </a>
+ </h1>
<div id="instructions">
<ol>
<li>
@@ -96,14 +105,27 @@
on this page. Don't worry, it won't be uploaded anywhere.
</li>
<li>
- Click "Start plot" to starts number crunching. This will take a while.
+ Click "Start" to start number crunching. This will take a while.
</li>
- </ol>
+ <li>
+ Click "Show plot/profile" to switch between the statistical profile and
+ the timeline plot.<br/>
+ C++ items are missing in the statistical profile because symbol
+ information is not available.<br>
+ Consider using the
+ <a href="https://code.google.com/p/v8/wiki/V8Profiler">
+ command-line utility
+ </a> instead.
+ </li>
</div>
</div>
<div class="text">
- <h1>Credits</h1>
+ <h1>
+ <a href="javascript:ui.info('credits');" class="unroll">
+ Credits
+ </a>
+ </h1>
<div id="credits">
<ul>
<li>
diff --git a/tools/profviz/profviz.js b/tools/profviz/profviz.js
index cbfee7a..7af12ad 100644
--- a/tools/profviz/profviz.js
+++ b/tools/profviz/profviz.js
@@ -29,6 +29,7 @@
"../csvparser.js",
"../splaytree.js",
"../codemap.js",
+ "../consarray.js",
"../profile.js",
"../profile_view.js",
"../logreader.js",
@@ -42,11 +43,12 @@
var worker = null;
var delegateList = {
- "log" : log,
- "error" : logError,
- "display" : display,
- "range" : setRange,
- "script" : scriptLoaded
+ "log" : log,
+ "error" : logError,
+ "displayplot" : displayplot,
+ "displayprof" : displayprof,
+ "range" : setRange,
+ "script" : scriptLoaded
}
function initialize() {
@@ -97,15 +99,26 @@
"start",
"file"];
- this.log = document.getElementById("log");
- this.plot = document.getElementById('plot');
+ var other_elements = ["log",
+ "plot",
+ "prof",
+ "instructions",
+ "credits",
+ "toggledisplay"];
+
for (var i in input_elements) {
var id = input_elements[i];
this[id] = document.getElementById(id);
}
+ for (var i in other_elements) {
+ var id = other_elements[i];
+ this[id] = document.getElementById(id);
+ }
+
this.freeze = function() {
this.plot.style.webkitFilter = "grayscale(1)";
+ this.prof.style.color = "#bbb";
for (var i in input_elements) {
this[input_elements[i]].disabled = true;
}
@@ -113,6 +126,7 @@
this.thaw = function() {
this.plot.style.webkitFilter = "";
+ this.prof.style.color = "#000";
for (var i in input_elements) {
this[input_elements[i]].disabled = false;
}
@@ -123,6 +137,34 @@
this.log.value = "";
this.range_start.value = "automatic";
this.range_end.value = "automatic";
+ this.toggle("plot");
+ this.plot.src = "";
+ this.prof.value = "";
+ }
+
+ this.toggle = function(mode) {
+ if (mode) this.toggledisplay.next_mode = mode;
+ if (this.toggledisplay.next_mode == "plot") {
+ this.toggledisplay.next_mode = "prof";
+ this.plot.style.display = "block";
+ this.prof.style.display = "none";
+ this.toggledisplay.innerHTML = "Show profile";
+ } else {
+ this.toggledisplay.next_mode = "plot";
+ this.plot.style.display = "none";
+ this.prof.style.display = "block";
+ this.toggledisplay.innerHTML = "Show plot";
+ }
+ }
+
+ this.info = function(field) {
+ var down_arrow = "\u25bc";
+ var right_arrow = "\u25b6";
+ if (field && this[field].style.display != "none") field = null; // Toggle.
+ this.credits.style.display = "none";
+ this.instructions.style.display = "none";
+ if (!field) return;
+ this[field].style.display = "block";
}
}
@@ -144,7 +186,7 @@
}
-function display(args) {
+function displayplot(args) {
if (error_logged) {
log("Plot failed.\n\n");
} else {
@@ -157,6 +199,15 @@
}
ui.thaw();
+ ui.toggle("plot");
+}
+
+
+function displayprof(args) {
+ if (error_logged) return;
+ ui.prof.value = args;
+ this.prof.style.color = "";
+ ui.toggle("prof");
}
@@ -217,11 +268,12 @@
function onload() {
- kResX = 1600;
- kResY = 900;
+ kResX = 1200;
+ kResY = 600;
error_logged = false;
ui = new UIWrapper();
ui.reset();
+ ui.info(null);
worker = new plotWorker();
worker.reset();
}
diff --git a/tools/profviz/worker.js b/tools/profviz/worker.js
index 5211d85..60a557f 100644
--- a/tools/profviz/worker.js
+++ b/tools/profviz/worker.js
@@ -41,8 +41,13 @@
}
-function display(content) {
- self.postMessage({ "call" : "display", "args" : content});
+function displayplot(content) {
+ self.postMessage({ "call" : "displayplot", "args" : content});
+}
+
+
+function displayprof(content) {
+ self.postMessage({ "call" : "displayprof", "args" : content});
}
@@ -84,13 +89,42 @@
content_lines = content.split("\n");
});
+ time("Producing statistical profile",
+ function() {
+ var profile = "";
+ print = function(text) { profile += text + "\n"; };
+ // Dummy entries provider, as we cannot call nm.
+ var entriesProvider = new UnixCppEntriesProvider("", "");
+ var targetRootFS = "";
+ var separateIc = false;
+ var callGraphSize = 5;
+ var ignoreUnknown = true;
+ var stateFilter = null;
+ var snapshotLogProcessor = null;
+ var range = range_start_override + "," + range_end_override;
+
+ var tickProcessor = new TickProcessor(entriesProvider,
+ separateIc,
+ callGraphSize,
+ ignoreUnknown,
+ stateFilter,
+ snapshotLogProcessor,
+ distortion,
+ range);
+ for (var i = 0; i < content_lines.length; i++) {
+ tickProcessor.processLogLine(content_lines[i]);
+ }
+ tickProcessor.printStatistics();
+ displayprof(profile);
+ });
+
var input_file_name = "input_temp";
var output_file_name = "output.svg";
var psc = new PlotScriptComposer(resx, resy);
var objects = 0;
- time("Analyzing data (" + content_lines.length + " entries)",
+ time("Collecting events (" + content_lines.length + " entries)",
function() {
var line_cursor = 0;
var input = function() { return content_lines[line_cursor++]; };
@@ -103,7 +137,7 @@
time("Assembling plot script",
function() {
var plot_script = "";
- var output = function(output) { plot_script += output + "\n"; };
+ var output = function(text) { plot_script += text + "\n"; };
output("set terminal svg size " + resx + "," + resy +
" enhanced font \"Helvetica,10\"");
output("set output \""+ output_file_name + "\"");
@@ -115,10 +149,10 @@
FS.createDataFile("/", input_file_name, arrc);
});
- time("Running Gnuplot (" + objects + " objects)",
+ time("Running gnuplot (" + objects + " objects)",
function() { Module.run([input_file_name]); });
- display(FS.findObject(output_file_name));
+ displayplot(FS.findObject(output_file_name));
}
@@ -131,4 +165,3 @@
self.postMessage({"call": "error", "args": text});
},
};
-
diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js
index abb2f2d..967bd3c 100644
--- a/tools/tickprocessor.js
+++ b/tools/tickprocessor.js
@@ -169,7 +169,7 @@
'snapshot-pos': { parsers: [parseInt, parseInt],
processor: this.processSnapshotPosition },
'tick': {
- parsers: [parseInt, parseInt, parseInt, parseInt,
+ parsers: [parseInt, parseInt, parseInt,
parseInt, parseInt, 'var-args'],
processor: this.processTick },
'heap-sample-begin': { parsers: [null, null, parseInt],
@@ -204,7 +204,7 @@
// Convert picoseconds to nanoseconds.
this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
this.distortion = 0;
- var rangelimits = range.split(",");
+ var rangelimits = range ? range.split(",") : [];
var range_start = parseInt(rangelimits[0]);
var range_end = parseInt(rangelimits[1]);
// Convert milliseconds to nanoseconds.
@@ -365,7 +365,6 @@
};
TickProcessor.prototype.processTick = function(pc,
- sp,
ns_since_start,
is_external_callback,
tos_or_external_callback,