Version 3.29.93 (based on bleeding_edge revision r24283)
Add a getter for the address and size of the code range to the pulic API (issue 3598).
Convert `obj` ToObject in Object.keys() (issue 3587).
Performance and stability improvements on all platforms.
git-svn-id: https://v8.googlecode.com/svn/trunk@24298 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/BUILD.gn b/BUILD.gn
index 133090a..bfe4439 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -440,6 +440,8 @@
"src/background-parsing-task.h",
"src/bailout-reason.cc",
"src/bailout-reason.h",
+ "src/basic-block-profiler.cc",
+ "src/basic-block-profiler.h",
"src/bignum-dtoa.cc",
"src/bignum-dtoa.h",
"src/bignum.cc",
@@ -471,6 +473,8 @@
"src/compiler/access-builder.h",
"src/compiler/ast-graph-builder.cc",
"src/compiler/ast-graph-builder.h",
+ "src/compiler/basic-block-instrumentor.cc",
+ "src/compiler/basic-block-instrumentor.h",
"src/compiler/change-lowering.cc",
"src/compiler/change-lowering.h",
"src/compiler/code-generator-impl.h",
@@ -820,10 +824,21 @@
"src/rewriter.h",
"src/runtime-profiler.cc",
"src/runtime-profiler.h",
+ "src/runtime/runtime-collections.cc",
+ "src/runtime/runtime-compiler.cc",
"src/runtime/runtime-i18n.cc",
+ "src/runtime/runtime-json.cc",
+ "src/runtime/runtime-maths.cc",
+ "src/runtime/runtime-numbers.cc",
+ "src/runtime/runtime-regexp.cc",
+ "src/runtime/runtime-strings.cc",
+ "src/runtime/runtime-test.cc",
+ "src/runtime/runtime-typedarray.cc",
+ "src/runtime/runtime-uri.cc",
"src/runtime/runtime-utils.h",
"src/runtime/runtime.cc",
"src/runtime/runtime.h",
+ "src/runtime/string-builder.h",
"src/safepoint-table.cc",
"src/safepoint-table.h",
"src/sampler.cc",
diff --git a/ChangeLog b/ChangeLog
index 92b11644..89e5b9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2014-09-30: Version 3.29.93
+
+ Add a getter for the address and size of the code range to the pulic API
+ (issue 3598).
+
+ Convert `obj` ToObject in Object.keys() (issue 3587).
+
+ Performance and stability improvements on all platforms.
+
+
2014-09-29: Version 3.29.92
Performance and stability improvements on all platforms.
diff --git a/include/v8.h b/include/v8.h
index ec1941e..63c6762 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1561,6 +1561,18 @@
bool IsRegExp() const;
/**
+ * Returns true if this value is a Generator function.
+ * This is an experimental feature.
+ */
+ bool IsGeneratorFunction() const;
+
+ /**
+ * Returns true if this value is a Generator object (iterator).
+ * This is an experimental feature.
+ */
+ bool IsGeneratorObject() const;
+
+ /**
* Returns true if this value is a Promise.
* This is an experimental feature.
*/
@@ -4835,6 +4847,18 @@
*/
void SetStackLimit(uintptr_t stack_limit);
+ /**
+ * Returns a memory range that can potentially contain jitted code.
+ *
+ * On Win64, embedders are advised to install function table callbacks for
+ * these ranges, as default SEH won't be able to unwind through jitted code.
+ *
+ * Might be empty on other platforms.
+ *
+ * https://code.google.com/p/v8/issues/detail?id=3598
+ */
+ void GetCodeRange(void** start, size_t* length_in_bytes);
+
private:
template<class K, class V, class Traits> friend class PersistentValueMap;
diff --git a/src/api.cc b/src/api.cc
index bda34bd..0fbdf7b 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2568,6 +2568,19 @@
}
+bool Value::IsGeneratorFunction() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (!obj->IsJSFunction()) return false;
+ i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(obj);
+ return func->shared()->is_generator();
+}
+
+
+bool Value::IsGeneratorObject() const {
+ return Utils::OpenHandle(this)->IsJSGeneratorObject();
+}
+
+
Local<String> Value::ToString() const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::Object> str;
@@ -6813,6 +6826,18 @@
}
+void v8::Isolate::GetCodeRange(void** start, size_t* length_in_bytes) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ if (isolate->code_range()->valid()) {
+ *start = isolate->code_range()->start();
+ *length_in_bytes = isolate->code_range()->size();
+ } else {
+ *start = NULL;
+ *length_in_bytes = 0;
+ }
+}
+
+
String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj)
: str_(NULL), length_(0) {
i::Isolate* isolate = i::Isolate::Current();
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 6108c0b..42a605c 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -1885,13 +1885,19 @@
// Left-hand side can only be a property, a global or a (parameter or local)
// slot.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ enum LhsKind {
+ VARIABLE,
+ NAMED_PROPERTY,
+ KEYED_PROPERTY,
+ NAMED_SUPER_PROPERTY
+ };
LhsKind assign_type = VARIABLE;
Property* property = expr->target()->AsProperty();
if (property != NULL) {
assign_type = (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
+ ? (property->IsSuperAccess() ? NAMED_SUPER_PROPERTY
+ : NAMED_PROPERTY)
+ : KEYED_PROPERTY;
}
// Evaluate LHS expression.
@@ -1908,6 +1914,17 @@
VisitForStackValue(property->obj());
}
break;
+ case NAMED_SUPER_PROPERTY:
+ VisitForStackValue(property->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(property->obj()->AsSuperReference());
+ __ Push(result_register());
+ if (expr->is_compound()) {
+ const Register scratch = r1;
+ __ ldr(scratch, MemOperand(sp, kPointerSize));
+ __ Push(scratch);
+ __ Push(result_register());
+ }
+ break;
case KEYED_PROPERTY:
if (expr->is_compound()) {
VisitForStackValue(property->obj());
@@ -1935,6 +1952,10 @@
EmitNamedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
@@ -1981,6 +2002,9 @@
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyAssignment(expr);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyAssignment(expr);
break;
@@ -2319,6 +2343,7 @@
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
+ DCHECK(!prop->IsSuperAccess());
__ mov(LoadDescriptor::NameRegister(), Operand(key->value()));
if (FLAG_vector_ics) {
@@ -2332,15 +2357,12 @@
void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
+ // Stack: receiver, home_object.
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
DCHECK(prop->IsSuperAccess());
- SuperReference* super_ref = prop->obj()->AsSuperReference();
- EmitLoadHomeObject(super_ref);
- __ Push(r0);
- VisitForStackValue(super_ref->this_var());
__ Push(key->value());
__ CallRuntime(Runtime::kLoadFromSuper, 3);
}
@@ -2612,6 +2634,24 @@
}
+void FullCodeGenerator::EmitNamedSuperPropertyAssignment(Assignment* expr) {
+ // Assignment to named property of super.
+ // r0 : value
+ // stack : receiver ('this'), home_object
+ Property* prop = expr->target()->AsProperty();
+ DCHECK(prop != NULL);
+ Literal* key = prop->key()->AsLiteral();
+ DCHECK(key != NULL);
+
+ __ Push(r0);
+ __ Push(key->value());
+ __ CallRuntime((strict_mode() == STRICT ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy),
+ 4);
+ context()->Plug(r0);
+}
+
+
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
// Assignment to a property, using a keyed store IC.
@@ -2638,6 +2678,9 @@
__ Move(LoadDescriptor::ReceiverRegister(), r0);
EmitNamedPropertyLoad(expr);
} else {
+ VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(expr->obj()->AsSuperReference());
+ __ Push(result_register());
EmitNamedSuperPropertyLoad(expr);
}
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
@@ -2712,16 +2755,16 @@
__ Push(r0);
VisitForAccumulatorValue(super_ref->this_var());
__ Push(r0);
- __ ldr(scratch, MemOperand(sp, kPointerSize));
- __ Push(scratch);
__ Push(r0);
+ __ ldr(scratch, MemOperand(sp, kPointerSize * 2));
+ __ Push(scratch);
__ Push(key->value());
// Stack here:
// - home_object
// - this (receiver)
- // - home_object <-- LoadFromSuper will pop here and below.
- // - this (receiver)
+ // - this (receiver) <-- LoadFromSuper will pop here and below.
+ // - home_object
// - key
__ CallRuntime(Runtime::kLoadFromSuper, 3);
@@ -4339,6 +4382,11 @@
if (prop != NULL) {
assign_type =
(prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ if (prop->IsSuperAccess()) {
+ // throw exception.
+ VisitSuperReference(prop->obj()->AsSuperReference());
+ return;
+ }
}
// Evaluate expression and get value.
diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc
index 619568f..9d54482 100644
--- a/src/arm64/full-codegen-arm64.cc
+++ b/src/arm64/full-codegen-arm64.cc
@@ -1864,13 +1864,19 @@
// Left-hand side can only be a property, a global or a (parameter or local)
// slot.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ enum LhsKind {
+ VARIABLE,
+ NAMED_PROPERTY,
+ KEYED_PROPERTY,
+ NAMED_SUPER_PROPERTY
+ };
LhsKind assign_type = VARIABLE;
Property* property = expr->target()->AsProperty();
if (property != NULL) {
assign_type = (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
+ ? (property->IsSuperAccess() ? NAMED_SUPER_PROPERTY
+ : NAMED_PROPERTY)
+ : KEYED_PROPERTY;
}
// Evaluate LHS expression.
@@ -1887,6 +1893,16 @@
VisitForStackValue(property->obj());
}
break;
+ case NAMED_SUPER_PROPERTY:
+ VisitForStackValue(property->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(property->obj()->AsSuperReference());
+ __ Push(result_register());
+ if (expr->is_compound()) {
+ const Register scratch = x10;
+ __ Peek(scratch, kPointerSize);
+ __ Push(scratch, result_register());
+ }
+ break;
case KEYED_PROPERTY:
if (expr->is_compound()) {
VisitForStackValue(property->obj());
@@ -1913,6 +1929,10 @@
EmitNamedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
@@ -1959,6 +1979,9 @@
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyAssignment(expr);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyAssignment(expr);
break;
@@ -1983,15 +2006,12 @@
void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
+ // Stack: receiver, home_object.
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
DCHECK(prop->IsSuperAccess());
- SuperReference* super_ref = prop->obj()->AsSuperReference();
- EmitLoadHomeObject(super_ref);
- __ Push(x0);
- VisitForStackValue(super_ref->this_var());
__ Push(key->value());
__ CallRuntime(Runtime::kLoadFromSuper, 3);
}
@@ -2277,6 +2297,24 @@
}
+void FullCodeGenerator::EmitNamedSuperPropertyAssignment(Assignment* expr) {
+ // Assignment to named property of super.
+ // x0 : value
+ // stack : receiver ('this'), home_object
+ Property* prop = expr->target()->AsProperty();
+ DCHECK(prop != NULL);
+ Literal* key = prop->key()->AsLiteral();
+ DCHECK(key != NULL);
+
+ __ Push(x0);
+ __ Push(key->value());
+ __ CallRuntime((strict_mode() == STRICT ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy),
+ 4);
+ context()->Plug(x0);
+}
+
+
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
ASM_LOCATION("FullCodeGenerator::EmitKeyedPropertyAssignment");
// Assignment to a property, using a keyed store IC.
@@ -2305,6 +2343,9 @@
__ Move(LoadDescriptor::ReceiverRegister(), x0);
EmitNamedPropertyLoad(expr);
} else {
+ VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(expr->obj()->AsSuperReference());
+ __ Push(result_register());
EmitNamedSuperPropertyLoad(expr);
}
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
@@ -2379,14 +2420,14 @@
VisitForAccumulatorValue(super_ref->this_var());
__ Push(x0);
__ Peek(scratch, kPointerSize);
- __ Push(scratch, x0);
+ __ Push(x0, scratch);
__ Push(key->value());
// Stack here:
// - home_object
// - this (receiver)
- // - home_object <-- LoadFromSuper will pop here and below.
- // - this (receiver)
+ // - this (receiver) <-- LoadFromSuper will pop here and below.
+ // - home_object
// - key
__ CallRuntime(Runtime::kLoadFromSuper, 3);
@@ -4006,6 +4047,11 @@
if (prop != NULL) {
assign_type =
(prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ if (prop->IsSuperAccess()) {
+ // throw exception.
+ VisitSuperReference(prop->obj()->AsSuperReference());
+ return;
+ }
}
// Evaluate expression and get value.
diff --git a/src/assert-scope.cc b/src/assert-scope.cc
index c4aa987..4c10fdd 100644
--- a/src/assert-scope.cc
+++ b/src/assert-scope.cc
@@ -2,20 +2,154 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#include "src/assert-scope.h"
-#include "src/v8.h"
+
+#include "src/base/lazy-instance.h"
+#include "src/base/platform/platform.h"
+#include "src/isolate-inl.h"
+#include "src/utils.h"
namespace v8 {
namespace internal {
-uint32_t PerIsolateAssertBase::GetData(Isolate* isolate) {
- return isolate->per_isolate_assert_data();
+namespace {
+
+struct PerThreadAssertKeyConstructTrait FINAL {
+ static void Construct(base::Thread::LocalStorageKey* key) {
+ *key = base::Thread::CreateThreadLocalKey();
+ }
+};
+
+
+typedef base::LazyStaticInstance<base::Thread::LocalStorageKey,
+ PerThreadAssertKeyConstructTrait>::type
+ PerThreadAssertKey;
+
+
+PerThreadAssertKey kPerThreadAssertKey;
+
+} // namespace
+
+
+class PerThreadAssertData FINAL {
+ public:
+ PerThreadAssertData() : nesting_level_(0) {
+ for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; i++) {
+ assert_states_[i] = true;
+ }
+ }
+
+ ~PerThreadAssertData() {
+ for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; ++i) {
+ DCHECK(assert_states_[i]);
+ }
+ }
+
+ bool Get(PerThreadAssertType type) const { return assert_states_[type]; }
+ void Set(PerThreadAssertType type, bool x) { assert_states_[type] = x; }
+
+ void IncrementLevel() { ++nesting_level_; }
+ bool DecrementLevel() { return --nesting_level_ == 0; }
+
+ static PerThreadAssertData* GetCurrent() {
+ return reinterpret_cast<PerThreadAssertData*>(
+ base::Thread::GetThreadLocal(kPerThreadAssertKey.Get()));
+ }
+ static void SetCurrent(PerThreadAssertData* data) {
+ base::Thread::SetThreadLocal(kPerThreadAssertKey.Get(), data);
+ }
+
+ private:
+ bool assert_states_[LAST_PER_THREAD_ASSERT_TYPE];
+ int nesting_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerThreadAssertData);
+};
+
+
+template <PerThreadAssertType kType, bool kAllow>
+PerThreadAssertScope<kType, kAllow>::PerThreadAssertScope()
+ : data_(PerThreadAssertData::GetCurrent()) {
+ if (data_ == NULL) {
+ data_ = new PerThreadAssertData();
+ PerThreadAssertData::SetCurrent(data_);
+ }
+ data_->IncrementLevel();
+ old_state_ = data_->Get(kType);
+ data_->Set(kType, kAllow);
}
-void PerIsolateAssertBase::SetData(Isolate* isolate, uint32_t data) {
- isolate->set_per_isolate_assert_data(data);
+template <PerThreadAssertType kType, bool kAllow>
+PerThreadAssertScope<kType, kAllow>::~PerThreadAssertScope() {
+ DCHECK_NOT_NULL(data_);
+ data_->Set(kType, old_state_);
+ if (data_->DecrementLevel()) {
+ PerThreadAssertData::SetCurrent(NULL);
+ delete data_;
+ }
}
-} } // namespace v8::internal
+
+// static
+template <PerThreadAssertType kType, bool kAllow>
+bool PerThreadAssertScope<kType, kAllow>::IsAllowed() {
+ PerThreadAssertData* data = PerThreadAssertData::GetCurrent();
+ return data == NULL || data->Get(kType);
+}
+
+
+template <PerIsolateAssertType kType, bool kAllow>
+class PerIsolateAssertScope<kType, kAllow>::DataBit
+ : public BitField<bool, kType, 1> {};
+
+
+template <PerIsolateAssertType kType, bool kAllow>
+PerIsolateAssertScope<kType, kAllow>::PerIsolateAssertScope(Isolate* isolate)
+ : isolate_(isolate), old_data_(isolate->per_isolate_assert_data()) {
+ DCHECK_NOT_NULL(isolate);
+ STATIC_ASSERT(kType < 32);
+ isolate_->set_per_isolate_assert_data(DataBit::update(old_data_, kAllow));
+}
+
+
+template <PerIsolateAssertType kType, bool kAllow>
+PerIsolateAssertScope<kType, kAllow>::~PerIsolateAssertScope() {
+ isolate_->set_per_isolate_assert_data(old_data_);
+}
+
+
+// static
+template <PerIsolateAssertType kType, bool kAllow>
+bool PerIsolateAssertScope<kType, kAllow>::IsAllowed(Isolate* isolate) {
+ return DataBit::decode(isolate->per_isolate_assert_data());
+}
+
+
+// -----------------------------------------------------------------------------
+// Instantiations.
+
+template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, false>;
+template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, true>;
+template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, false>;
+template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, true>;
+template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, false>;
+template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, true>;
+template class PerThreadAssertScope<DEFERRED_HANDLE_DEREFERENCE_ASSERT, false>;
+template class PerThreadAssertScope<DEFERRED_HANDLE_DEREFERENCE_ASSERT, true>;
+template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, false>;
+template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>;
+
+template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, false>;
+template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, true>;
+template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_THROWS, false>;
+template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_THROWS, true>;
+template class PerIsolateAssertScope<ALLOCATION_FAILURE_ASSERT, false>;
+template class PerIsolateAssertScope<ALLOCATION_FAILURE_ASSERT, true>;
+template class PerIsolateAssertScope<DEOPTIMIZATION_ASSERT, false>;
+template class PerIsolateAssertScope<DEOPTIMIZATION_ASSERT, true>;
+template class PerIsolateAssertScope<COMPILATION_ASSERT, false>;
+template class PerIsolateAssertScope<COMPILATION_ASSERT, true>;
+
+} // namespace internal
+} // namespace v8
diff --git a/src/assert-scope.h b/src/assert-scope.h
index 7cfec56..41baa65 100644
--- a/src/assert-scope.h
+++ b/src/assert-scope.h
@@ -5,14 +5,16 @@
#ifndef V8_ASSERT_SCOPE_H_
#define V8_ASSERT_SCOPE_H_
-#include "src/allocation.h"
-#include "src/base/platform/platform.h"
-#include "src/utils.h"
+#include "include/v8stdint.h"
+#include "src/base/macros.h"
namespace v8 {
namespace internal {
+// Forward declarations.
class Isolate;
+class PerThreadAssertData;
+
enum PerThreadAssertType {
HEAP_ALLOCATION_ASSERT,
@@ -33,120 +35,35 @@
};
-class PerThreadAssertData {
+template <PerThreadAssertType kType, bool kAllow>
+class PerThreadAssertScope {
public:
- PerThreadAssertData() : nesting_level_(0) {
- for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; i++) {
- assert_states_[i] = true;
- }
- }
+ PerThreadAssertScope();
+ ~PerThreadAssertScope();
- void set(PerThreadAssertType type, bool allow) {
- assert_states_[type] = allow;
- }
-
- bool get(PerThreadAssertType type) const {
- return assert_states_[type];
- }
-
- void increment_level() { ++nesting_level_; }
- bool decrement_level() { return --nesting_level_ == 0; }
+ static bool IsAllowed();
private:
- bool assert_states_[LAST_PER_THREAD_ASSERT_TYPE];
- int nesting_level_;
-
- DISALLOW_COPY_AND_ASSIGN(PerThreadAssertData);
-};
-
-
-class PerThreadAssertScopeBase {
- protected:
- PerThreadAssertScopeBase() {
- data_ = GetAssertData();
- if (data_ == NULL) {
- data_ = new PerThreadAssertData();
- SetThreadLocalData(data_);
- }
- data_->increment_level();
- }
-
- ~PerThreadAssertScopeBase() {
- if (!data_->decrement_level()) return;
- for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; i++) {
- DCHECK(data_->get(static_cast<PerThreadAssertType>(i)));
- }
- delete data_;
- SetThreadLocalData(NULL);
- }
-
- static PerThreadAssertData* GetAssertData() {
- return reinterpret_cast<PerThreadAssertData*>(
- base::Thread::GetThreadLocal(thread_local_key));
- }
-
- static base::Thread::LocalStorageKey thread_local_key;
PerThreadAssertData* data_;
- friend class Isolate;
-
- private:
- static void SetThreadLocalData(PerThreadAssertData* data) {
- base::Thread::SetThreadLocal(thread_local_key, data);
- }
-};
-
-
-template <PerThreadAssertType type, bool allow>
-class PerThreadAssertScope : public PerThreadAssertScopeBase {
- public:
- PerThreadAssertScope() {
- old_state_ = data_->get(type);
- data_->set(type, allow);
- }
-
- ~PerThreadAssertScope() { data_->set(type, old_state_); }
-
- static bool IsAllowed() {
- PerThreadAssertData* data = GetAssertData();
- return data == NULL || data->get(type);
- }
-
- private:
bool old_state_;
DISALLOW_COPY_AND_ASSIGN(PerThreadAssertScope);
};
-class PerIsolateAssertBase {
- protected:
- static uint32_t GetData(Isolate* isolate);
- static void SetData(Isolate* isolate, uint32_t data);
-};
-
-
template <PerIsolateAssertType type, bool allow>
-class PerIsolateAssertScope : public PerIsolateAssertBase {
+class PerIsolateAssertScope {
public:
- explicit PerIsolateAssertScope(Isolate* isolate) : isolate_(isolate) {
- STATIC_ASSERT(type < 32);
- old_data_ = GetData(isolate_);
- SetData(isolate_, DataBit::update(old_data_, allow));
- }
+ explicit PerIsolateAssertScope(Isolate* isolate);
+ ~PerIsolateAssertScope();
- ~PerIsolateAssertScope() {
- SetData(isolate_, old_data_);
- }
-
- static bool IsAllowed(Isolate* isolate) {
- return DataBit::decode(GetData(isolate));
- }
+ static bool IsAllowed(Isolate* isolate);
private:
- typedef BitField<bool, type, 1> DataBit;
+ class DataBit;
- uint32_t old_data_;
Isolate* isolate_;
+ uint32_t old_data_;
DISALLOW_COPY_AND_ASSIGN(PerIsolateAssertScope);
};
diff --git a/src/basic-block-profiler.cc b/src/basic-block-profiler.cc
new file mode 100644
index 0000000..ef68ac6
--- /dev/null
+++ b/src/basic-block-profiler.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/basic-block-profiler.h"
+
+namespace v8 {
+namespace internal {
+
+BasicBlockProfiler::Data::Data(size_t n_blocks)
+ : n_blocks_(n_blocks), block_ids_(n_blocks_, -1), counts_(n_blocks_, 0) {}
+
+
+BasicBlockProfiler::Data::~Data() {}
+
+
+static void InsertIntoString(OStringStream* os, std::string* string) {
+ string->insert(string->begin(), os->c_str(), &os->c_str()[os->size()]);
+}
+
+
+void BasicBlockProfiler::Data::SetCode(OStringStream* os) {
+ InsertIntoString(os, &code_);
+}
+
+
+void BasicBlockProfiler::Data::SetFunctionName(OStringStream* os) {
+ InsertIntoString(os, &function_name_);
+}
+
+
+void BasicBlockProfiler::Data::SetSchedule(OStringStream* os) {
+ InsertIntoString(os, &schedule_);
+}
+
+
+void BasicBlockProfiler::Data::SetBlockId(size_t offset, int block_id) {
+ DCHECK(offset < n_blocks_);
+ block_ids_[offset] = block_id;
+}
+
+
+uint32_t* BasicBlockProfiler::Data::GetCounterAddress(size_t offset) {
+ DCHECK(offset < n_blocks_);
+ return &counts_[offset];
+}
+
+
+void BasicBlockProfiler::Data::ResetCounts() {
+ for (size_t i = 0; i < n_blocks_; ++i) {
+ counts_[i] = 0;
+ }
+}
+
+
+BasicBlockProfiler::BasicBlockProfiler() {}
+
+
+BasicBlockProfiler::Data* BasicBlockProfiler::NewData(size_t n_blocks) {
+ Data* data = new Data(n_blocks);
+ data_list_.push_back(data);
+ return data;
+}
+
+
+BasicBlockProfiler::~BasicBlockProfiler() {
+ for (DataList::iterator i = data_list_.begin(); i != data_list_.end(); ++i) {
+ delete (*i);
+ }
+}
+
+
+void BasicBlockProfiler::ResetCounts() {
+ for (DataList::iterator i = data_list_.begin(); i != data_list_.end(); ++i) {
+ (*i)->ResetCounts();
+ }
+}
+
+
+OStream& operator<<(OStream& os, const BasicBlockProfiler& p) {
+ os << "---- Start Profiling Data ----" << endl;
+ typedef BasicBlockProfiler::DataList::const_iterator iterator;
+ for (iterator i = p.data_list_.begin(); i != p.data_list_.end(); ++i) {
+ os << **i;
+ }
+ os << "---- End Profiling Data ----" << endl;
+ return os;
+}
+
+
+OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& d) {
+ const char* name = "unknown function";
+ if (!d.function_name_.empty()) {
+ name = d.function_name_.c_str();
+ }
+ if (!d.schedule_.empty()) {
+ os << "schedule for " << name << endl;
+ os << d.schedule_.c_str() << endl;
+ }
+ os << "block counts for " << name << ":" << endl;
+ for (size_t i = 0; i < d.n_blocks_; ++i) {
+ os << "block " << d.block_ids_[i] << " : " << d.counts_[i] << endl;
+ }
+ os << endl;
+ if (!d.code_.empty()) {
+ os << d.code_.c_str() << endl;
+ }
+ return os;
+}
+
+} // namespace internal
+} // namespace v8
diff --git a/src/basic-block-profiler.h b/src/basic-block-profiler.h
new file mode 100644
index 0000000..e625cd2
--- /dev/null
+++ b/src/basic-block-profiler.h
@@ -0,0 +1,73 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_BASIC_BLOCK_PROFILER_H_
+#define V8_BASIC_BLOCK_PROFILER_H_
+
+#include <list>
+
+#include "src/v8.h"
+
+namespace v8 {
+namespace internal {
+
+class Schedule;
+class Graph;
+
+class BasicBlockProfiler {
+ public:
+ class Data {
+ public:
+ size_t n_blocks() const { return n_blocks_; }
+ const uint32_t* counts() const { return &counts_[0]; }
+
+ void SetCode(OStringStream* os);
+ void SetFunctionName(OStringStream* os);
+ void SetSchedule(OStringStream* os);
+ void SetBlockId(size_t offset, int block_id);
+ uint32_t* GetCounterAddress(size_t offset);
+
+ private:
+ friend class BasicBlockProfiler;
+ friend OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& s);
+
+ explicit Data(size_t n_blocks);
+ ~Data();
+
+ void ResetCounts();
+
+ const size_t n_blocks_;
+ std::vector<int> block_ids_;
+ std::vector<uint32_t> counts_;
+ std::string function_name_;
+ std::string schedule_;
+ std::string code_;
+ DISALLOW_COPY_AND_ASSIGN(Data);
+ };
+
+ typedef std::list<Data*> DataList;
+
+ BasicBlockProfiler();
+ ~BasicBlockProfiler();
+
+ Data* NewData(size_t n_blocks);
+ void ResetCounts();
+
+ const DataList* data_list() { return &data_list_; }
+
+ private:
+ friend OStream& operator<<(OStream& os, const BasicBlockProfiler& s);
+
+ DataList data_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(BasicBlockProfiler);
+};
+
+OStream& operator<<(OStream& os, const BasicBlockProfiler& s);
+OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& s);
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_BASIC_BLOCK_PROFILER_H_
diff --git a/src/builtins.cc b/src/builtins.cc
index d0c19e5..c52d228 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -987,12 +987,12 @@
Handle<FixedArrayBase> storage(result_array->elements(), isolate);
ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind);
for (int i = 0; i < n_arguments; i++) {
- // TODO(ishell): It is crucial to keep |array| as a raw pointer to avoid
- // performance degradation. Revisit this later.
+ // It is crucial to keep |array| in a raw pointer form to avoid performance
+ // degradation.
JSArray* array = JSArray::cast(args[i]);
int len = Smi::cast(array->length())->value();
- ElementsKind from_kind = array->GetElementsKind();
if (len > 0) {
+ ElementsKind from_kind = array->GetElementsKind();
accessor->CopyElements(array, 0, from_kind, storage, j, len);
j += len;
}
@@ -1562,7 +1562,7 @@
// Move the code into the object heap.
CodeDesc desc;
masm.GetCode(&desc);
- Code::Flags flags = functions[i].flags;
+ Code::Flags flags = functions[i].flags;
Handle<Code> code =
isolate->factory()->NewCode(desc, flags, masm.CodeObject());
// Log the event and add the code to the builtins array.
diff --git a/src/code-stubs.h b/src/code-stubs.h
index 3b31399..b127782 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -136,10 +136,12 @@
class CodeStub BASE_EMBEDDED {
public:
enum Major {
+ // TODO(mvstanton): eliminate the NoCache key by getting rid
+ // of the non-monomorphic-cache.
+ NoCache = 0, // marker for stubs that do custom caching]
#define DEF_ENUM(name) name,
CODE_STUB_LIST(DEF_ENUM)
#undef DEF_ENUM
- NoCache, // marker for stubs that do custom caching
NUMBER_OF_IDS
};
diff --git a/src/compiler.cc b/src/compiler.cc
index 13a1ec8..d8467e5 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -995,6 +995,8 @@
DCHECK(info->is_eval() || info->is_global());
+ info->MarkAsToplevel();
+
Handle<SharedFunctionInfo> result;
{ VMState<COMPILER> state(info->isolate());
@@ -1264,6 +1266,13 @@
bool allow_lazy = literal->AllowsLazyCompilation() &&
!DebuggerWantsEagerCompilation(&info, allow_lazy_without_ctx);
+
+ if (outer_info->is_toplevel() && outer_info->will_serialize()) {
+ // Make sure that if the toplevel code (possibly to be serialized),
+ // the inner unction must be allowed to be compiled lazily.
+ DCHECK(allow_lazy);
+ }
+
// Generate code
Handle<ScopeInfo> scope_info;
if (FLAG_lazy && allow_lazy && !literal->is_parenthesized()) {
diff --git a/src/compiler.h b/src/compiler.h
index 9617afc..e9176d3 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -85,7 +85,8 @@
kInliningEnabled = 1 << 17,
kTypingEnabled = 1 << 18,
kDisableFutureOptimization = 1 << 19,
- kAbortedDueToDependency = 1 << 20
+ kAbortedDueToDependency = 1 << 20,
+ kToplevel = 1 << 21
};
CompilationInfo(Handle<JSFunction> closure, Zone* zone);
@@ -208,6 +209,10 @@
bool is_typing_enabled() const { return GetFlag(kTypingEnabled); }
+ void MarkAsToplevel() { SetFlag(kToplevel); }
+
+ bool is_toplevel() const { return GetFlag(kToplevel); }
+
bool IsCodePreAgingActive() const {
return FLAG_optimize_for_size && FLAG_age_code && !will_serialize() &&
!is_debug();
diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc
index ac9cfa8..749c04a 100644
--- a/src/compiler/access-builder.cc
+++ b/src/compiler/access-builder.cc
@@ -58,13 +58,6 @@
// static
-ElementAccess AccessBuilder::ForBackingStoreElement(MachineType rep) {
- return {kUntaggedBase, kNonHeapObjectHeaderSize - kHeapObjectTag, Type::Any(),
- rep};
-}
-
-
-// static
ElementAccess AccessBuilder::ForTypedArrayElement(ExternalArrayType type,
bool is_external) {
BaseTaggedness taggedness = is_external ? kUntaggedBase : kTaggedBase;
diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h
index 7d0bda1..72dd023 100644
--- a/src/compiler/access-builder.h
+++ b/src/compiler/access-builder.h
@@ -37,9 +37,6 @@
// Provides access to FixedArray elements.
static ElementAccess ForFixedArrayElement();
- // TODO(mstarzinger): Raw access only for testing, drop me.
- static ElementAccess ForBackingStoreElement(MachineType rep);
-
// Provides access to Fixed{type}TypedArray and External{type}Array elements.
static ElementAccess ForTypedArrayElement(ExternalArrayType type,
bool is_external);
diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc
index a842a6a..a56de20 100644
--- a/src/compiler/arm64/code-generator-arm64.cc
+++ b/src/compiler/arm64/code-generator-arm64.cc
@@ -204,6 +204,28 @@
case kArm64Mul32:
__ Mul(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
break;
+ case kArm64Madd:
+ __ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputRegister(2));
+ break;
+ case kArm64Madd32:
+ __ Madd(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
+ i.InputRegister32(2));
+ break;
+ case kArm64Msub:
+ __ Msub(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputRegister(2));
+ break;
+ case kArm64Msub32:
+ __ Msub(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
+ i.InputRegister32(2));
+ break;
+ case kArm64Mneg:
+ __ Mneg(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ break;
+ case kArm64Mneg32:
+ __ Mneg(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
+ break;
case kArm64Idiv:
__ Sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
break;
diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h
index 91bca81..b8484b7 100644
--- a/src/compiler/arm64/instruction-codes-arm64.h
+++ b/src/compiler/arm64/instruction-codes-arm64.h
@@ -36,6 +36,12 @@
V(Arm64Sub32) \
V(Arm64Mul) \
V(Arm64Mul32) \
+ V(Arm64Madd) \
+ V(Arm64Madd32) \
+ V(Arm64Msub) \
+ V(Arm64Msub32) \
+ V(Arm64Mneg) \
+ V(Arm64Mneg32) \
V(Arm64Idiv) \
V(Arm64Idiv32) \
V(Arm64Udiv) \
diff --git a/src/compiler/arm64/instruction-selector-arm64-unittest.cc b/src/compiler/arm64/instruction-selector-arm64-unittest.cc
index 72a9a19..bd14711 100644
--- a/src/compiler/arm64/instruction-selector-arm64-unittest.cc
+++ b/src/compiler/arm64/instruction-selector-arm64-unittest.cc
@@ -799,10 +799,120 @@
EXPECT_EQ(1U, s[0]->OutputCount());
}
+
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorMulDivTest,
::testing::ValuesIn(kMulDivInstructions));
+namespace {
+
+struct MulDPInst {
+ const char* mul_constructor_name;
+ Node* (RawMachineAssembler::*mul_constructor)(Node*, Node*);
+ Node* (RawMachineAssembler::*add_constructor)(Node*, Node*);
+ Node* (RawMachineAssembler::*sub_constructor)(Node*, Node*);
+ ArchOpcode add_arch_opcode;
+ ArchOpcode sub_arch_opcode;
+ ArchOpcode neg_arch_opcode;
+ MachineType machine_type;
+};
+
+
+std::ostream& operator<<(std::ostream& os, const MulDPInst& inst) {
+ return os << inst.mul_constructor_name;
+}
+
+} // namespace
+
+
+static const MulDPInst kMulDPInstructions[] = {
+ {"Int32Mul", &RawMachineAssembler::Int32Mul, &RawMachineAssembler::Int32Add,
+ &RawMachineAssembler::Int32Sub, kArm64Madd32, kArm64Msub32, kArm64Mneg32,
+ kMachInt32},
+ {"Int64Mul", &RawMachineAssembler::Int64Mul, &RawMachineAssembler::Int64Add,
+ &RawMachineAssembler::Int64Sub, kArm64Madd, kArm64Msub, kArm64Mneg,
+ kMachInt64}};
+
+
+typedef InstructionSelectorTestWithParam<MulDPInst>
+ InstructionSelectorIntDPWithIntMulTest;
+
+
+TEST_P(InstructionSelectorIntDPWithIntMulTest, AddWithMul) {
+ const MulDPInst mdpi = GetParam();
+ const MachineType type = mdpi.machine_type;
+ {
+ StreamBuilder m(this, type, type, type, type);
+ Node* n = (m.*mdpi.mul_constructor)(m.Parameter(1), m.Parameter(2));
+ m.Return((m.*mdpi.add_constructor)(m.Parameter(0), n));
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(mdpi.add_arch_opcode, s[0]->arch_opcode());
+ EXPECT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ }
+ {
+ StreamBuilder m(this, type, type, type, type);
+ Node* n = (m.*mdpi.mul_constructor)(m.Parameter(0), m.Parameter(1));
+ m.Return((m.*mdpi.add_constructor)(n, m.Parameter(2)));
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(mdpi.add_arch_opcode, s[0]->arch_opcode());
+ EXPECT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ }
+}
+
+
+TEST_P(InstructionSelectorIntDPWithIntMulTest, SubWithMul) {
+ const MulDPInst mdpi = GetParam();
+ const MachineType type = mdpi.machine_type;
+ {
+ StreamBuilder m(this, type, type, type, type);
+ Node* n = (m.*mdpi.mul_constructor)(m.Parameter(1), m.Parameter(2));
+ m.Return((m.*mdpi.sub_constructor)(m.Parameter(0), n));
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(mdpi.sub_arch_opcode, s[0]->arch_opcode());
+ EXPECT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ }
+}
+
+
+TEST_P(InstructionSelectorIntDPWithIntMulTest, NegativeMul) {
+ const MulDPInst mdpi = GetParam();
+ const MachineType type = mdpi.machine_type;
+ {
+ StreamBuilder m(this, type, type, type);
+ Node* n =
+ (m.*mdpi.sub_constructor)(BuildConstant(m, type, 0), m.Parameter(0));
+ m.Return((m.*mdpi.mul_constructor)(n, m.Parameter(1)));
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(mdpi.neg_arch_opcode, s[0]->arch_opcode());
+ EXPECT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ }
+ {
+ StreamBuilder m(this, type, type, type);
+ Node* n =
+ (m.*mdpi.sub_constructor)(BuildConstant(m, type, 0), m.Parameter(1));
+ m.Return((m.*mdpi.mul_constructor)(m.Parameter(0), n));
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(mdpi.neg_arch_opcode, s[0]->arch_opcode());
+ EXPECT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ }
+}
+
+
+INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
+ InstructionSelectorIntDPWithIntMulTest,
+ ::testing::ValuesIn(kMulDPInstructions));
+
+
// -----------------------------------------------------------------------------
// Floating point instructions.
diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc
index 730b119..8d7eee5 100644
--- a/src/compiler/arm64/instruction-selector-arm64.cc
+++ b/src/compiler/arm64/instruction-selector-arm64.cc
@@ -424,11 +424,47 @@
void InstructionSelector::VisitInt32Add(Node* node) {
+ Arm64OperandGenerator g(this);
+ Int32BinopMatcher m(node);
+ // Select Madd(x, y, z) for Add(Mul(x, y), z).
+ if (m.left().IsInt32Mul() && CanCover(node, m.left().node())) {
+ Int32BinopMatcher mleft(m.left().node());
+ Emit(kArm64Madd32, g.DefineAsRegister(node),
+ g.UseRegister(mleft.left().node()),
+ g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node()));
+ return;
+ }
+ // Select Madd(x, y, z) for Add(x, Mul(x, y)).
+ if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
+ Int32BinopMatcher mright(m.right().node());
+ Emit(kArm64Madd32, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
+ return;
+ }
VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm);
}
void InstructionSelector::VisitInt64Add(Node* node) {
+ Arm64OperandGenerator g(this);
+ Int64BinopMatcher m(node);
+ // Select Madd(x, y, z) for Add(Mul(x, y), z).
+ if (m.left().IsInt64Mul() && CanCover(node, m.left().node())) {
+ Int64BinopMatcher mleft(m.left().node());
+ Emit(kArm64Madd, g.DefineAsRegister(node),
+ g.UseRegister(mleft.left().node()),
+ g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node()));
+ return;
+ }
+ // Select Madd(x, y, z) for Add(x, Mul(x, y)).
+ if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
+ Int64BinopMatcher mright(m.right().node());
+ Emit(kArm64Madd, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
+ return;
+ }
VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm);
}
@@ -436,6 +472,16 @@
void InstructionSelector::VisitInt32Sub(Node* node) {
Arm64OperandGenerator g(this);
Int32BinopMatcher m(node);
+
+ // Select Msub(a, x, y) for Sub(a, Mul(x, y)).
+ if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
+ Int32BinopMatcher mright(m.right().node());
+ Emit(kArm64Msub32, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
+ return;
+ }
+
if (m.left().Is(0)) {
Emit(kArm64Neg32, g.DefineAsRegister(node),
g.UseRegister(m.right().node()));
@@ -448,6 +494,16 @@
void InstructionSelector::VisitInt64Sub(Node* node) {
Arm64OperandGenerator g(this);
Int64BinopMatcher m(node);
+
+ // Select Msub(a, x, y) for Sub(a, Mul(x, y)).
+ if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
+ Int64BinopMatcher mright(m.right().node());
+ Emit(kArm64Msub, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
+ return;
+ }
+
if (m.left().Is(0)) {
Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
} else {
@@ -457,11 +513,64 @@
void InstructionSelector::VisitInt32Mul(Node* node) {
+ Arm64OperandGenerator g(this);
+ Int32BinopMatcher m(node);
+
+ if (m.left().IsInt32Sub() && CanCover(node, m.left().node())) {
+ Int32BinopMatcher mleft(m.left().node());
+
+ // Select Mneg(x, y) for Mul(Sub(0, x), y).
+ if (mleft.left().Is(0)) {
+ Emit(kArm64Mneg32, g.DefineAsRegister(node),
+ g.UseRegister(mleft.right().node()),
+ g.UseRegister(m.right().node()));
+ return;
+ }
+ }
+
+ if (m.right().IsInt32Sub() && CanCover(node, m.right().node())) {
+ Int32BinopMatcher mright(m.right().node());
+
+ // Select Mneg(x, y) for Mul(x, Sub(0, y)).
+ if (mright.left().Is(0)) {
+ Emit(kArm64Mneg32, g.DefineAsRegister(node),
+ g.UseRegister(m.left().node()),
+ g.UseRegister(mright.right().node()));
+ return;
+ }
+ }
+
VisitRRR(this, kArm64Mul32, node);
}
void InstructionSelector::VisitInt64Mul(Node* node) {
+ Arm64OperandGenerator g(this);
+ Int64BinopMatcher m(node);
+
+ if (m.left().IsInt64Sub() && CanCover(node, m.left().node())) {
+ Int64BinopMatcher mleft(m.left().node());
+
+ // Select Mneg(x, y) for Mul(Sub(0, x), y).
+ if (mleft.left().Is(0)) {
+ Emit(kArm64Mneg, g.DefineAsRegister(node),
+ g.UseRegister(mleft.right().node()),
+ g.UseRegister(m.right().node()));
+ return;
+ }
+ }
+
+ if (m.right().IsInt64Sub() && CanCover(node, m.right().node())) {
+ Int64BinopMatcher mright(m.right().node());
+
+ // Select Mneg(x, y) for Mul(x, Sub(0, y)).
+ if (mright.left().Is(0)) {
+ Emit(kArm64Mneg, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
+ g.UseRegister(mright.right().node()));
+ return;
+ }
+ }
+
VisitRRR(this, kArm64Mul, node);
}
diff --git a/src/compiler/basic-block-instrumentor.cc b/src/compiler/basic-block-instrumentor.cc
new file mode 100644
index 0000000..119a44b
--- /dev/null
+++ b/src/compiler/basic-block-instrumentor.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/basic-block-instrumentor.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/operator-properties-inl.h"
+#include "src/compiler/schedule.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// Find the first place to insert new nodes in a block that's already been
+// scheduled that won't upset the register allocator.
+static NodeVector::iterator FindInsertionPoint(NodeVector* nodes) {
+ NodeVector::iterator i = nodes->begin();
+ for (; i != nodes->end(); ++i) {
+ const Operator* op = (*i)->op();
+ if (OperatorProperties::IsBasicBlockBegin(op)) continue;
+ switch (op->opcode()) {
+ case IrOpcode::kParameter:
+ case IrOpcode::kPhi:
+ case IrOpcode::kEffectPhi:
+ continue;
+ }
+ break;
+ }
+ return i;
+}
+
+
+// TODO(dcarney): need to mark code as non-serializable.
+static const Operator* PointerConstant(CommonOperatorBuilder* common,
+ void* ptr) {
+ return kPointerSize == 8
+ ? common->Int64Constant(reinterpret_cast<intptr_t>(ptr))
+ : common->Int32Constant(
+ static_cast<int32_t>(reinterpret_cast<intptr_t>(ptr)));
+}
+
+
+BasicBlockProfiler::Data* BasicBlockInstrumentor::Instrument(
+ CompilationInfo* info, Graph* graph, Schedule* schedule) {
+ // Skip the exit block in profiles, since the register allocator can't handle
+ // it and entry into it means falling off the end of the function anyway.
+ size_t n_blocks = static_cast<size_t>(schedule->RpoBlockCount()) - 1;
+ BasicBlockProfiler::Data* data =
+ info->isolate()->GetOrCreateBasicBlockProfiler()->NewData(n_blocks);
+ // Set the function name.
+ if (!info->shared_info().is_null() &&
+ info->shared_info()->name()->IsString()) {
+ OStringStream os;
+ String::cast(info->shared_info()->name())->PrintUC16(os);
+ data->SetFunctionName(&os);
+ }
+ // Capture the schedule string before instrumentation.
+ {
+ OStringStream os;
+ os << *schedule;
+ data->SetSchedule(&os);
+ }
+ // Add the increment instructions to the start of every block.
+ CommonOperatorBuilder common(graph->zone());
+ Node* zero = graph->NewNode(common.Int32Constant(0));
+ Node* one = graph->NewNode(common.Int32Constant(1));
+ MachineOperatorBuilder machine;
+ BasicBlockVector* blocks = schedule->rpo_order();
+ size_t block_number = 0;
+ for (BasicBlockVector::iterator it = blocks->begin(); block_number < n_blocks;
+ ++it, ++block_number) {
+ BasicBlock* block = (*it);
+ data->SetBlockId(block_number, block->id());
+ // TODO(dcarney): wire effect and control deps for load and store.
+ // Construct increment operation.
+ Node* base = graph->NewNode(
+ PointerConstant(&common, data->GetCounterAddress(block_number)));
+ Node* load = graph->NewNode(machine.Load(kMachUint32), base, zero);
+ Node* inc = graph->NewNode(machine.Int32Add(), load, one);
+ Node* store = graph->NewNode(
+ machine.Store(StoreRepresentation(kMachUint32, kNoWriteBarrier)), base,
+ zero, inc);
+ // Insert the new nodes.
+ static const int kArraySize = 6;
+ Node* to_insert[kArraySize] = {zero, one, base, load, inc, store};
+ int insertion_start = block_number == 0 ? 0 : 2;
+ NodeVector* nodes = &block->nodes_;
+ NodeVector::iterator insertion_point = FindInsertionPoint(nodes);
+ nodes->insert(insertion_point, &to_insert[insertion_start],
+ &to_insert[kArraySize]);
+ // Tell the scheduler about the new nodes.
+ for (int i = insertion_start; i < kArraySize; ++i) {
+ schedule->SetBlockForNode(block, to_insert[i]);
+ }
+ }
+ return data;
+}
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8
diff --git a/src/compiler/basic-block-instrumentor.h b/src/compiler/basic-block-instrumentor.h
new file mode 100644
index 0000000..7edac0d
--- /dev/null
+++ b/src/compiler/basic-block-instrumentor.h
@@ -0,0 +1,32 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_BASIC_BLOCK_INSTRUMENTOR_H_
+#define V8_COMPILER_BASIC_BLOCK_INSTRUMENTOR_H_
+
+#include "src/v8.h"
+
+#include "src/basic-block-profiler.h"
+
+namespace v8 {
+namespace internal {
+
+class CompilationInfo;
+
+namespace compiler {
+
+class Graph;
+class Schedule;
+
+class BasicBlockInstrumentor : public AllStatic {
+ public:
+ static BasicBlockProfiler::Data* Instrument(CompilationInfo* info,
+ Graph* graph, Schedule* schedule);
+};
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8
+
+#endif
diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc
index 97b3706..d9f8833 100644
--- a/src/compiler/ia32/code-generator-ia32.cc
+++ b/src/compiler/ia32/code-generator-ia32.cc
@@ -76,25 +76,83 @@
return Immediate(-1);
}
- Operand MemoryOperand(int* first_input) {
- const int offset = *first_input;
- switch (AddressingModeField::decode(instr_->opcode())) {
+ static int NextOffset(int* offset) {
+ int i = *offset;
+ (*offset)++;
+ return i;
+ }
+
+ static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
+ STATIC_ASSERT(0 == static_cast<int>(times_1));
+ STATIC_ASSERT(1 == static_cast<int>(times_2));
+ STATIC_ASSERT(2 == static_cast<int>(times_4));
+ STATIC_ASSERT(3 == static_cast<int>(times_8));
+ int scale = static_cast<int>(mode - one);
+ DCHECK(scale >= 0 && scale < 4);
+ return static_cast<ScaleFactor>(scale);
+ }
+
+ Operand MemoryOperand(int* offset) {
+ AddressingMode mode = AddressingModeField::decode(instr_->opcode());
+ switch (mode) {
+ case kMode_MR: {
+ Register base = InputRegister(NextOffset(offset));
+ int32_t disp = 0;
+ return Operand(base, disp);
+ }
+ case kMode_MRI: {
+ Register base = InputRegister(NextOffset(offset));
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(base, disp);
+ }
+ case kMode_MR1:
+ case kMode_MR2:
+ case kMode_MR4:
+ case kMode_MR8: {
+ Register base = InputRegister(NextOffset(offset));
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_MR1, mode);
+ int32_t disp = 0;
+ return Operand(base, index, scale, disp);
+ }
case kMode_MR1I:
- *first_input += 2;
- return Operand(InputRegister(offset + 0), InputRegister(offset + 1),
- times_1,
- 0); // TODO(dcarney): K != 0
- case kMode_MRI:
- *first_input += 2;
- return Operand::ForRegisterPlusImmediate(InputRegister(offset + 0),
- InputImmediate(offset + 1));
- case kMode_MI:
- *first_input += 1;
- return Operand(InputImmediate(offset + 0));
- default:
+ case kMode_MR2I:
+ case kMode_MR4I:
+ case kMode_MR8I: {
+ Register base = InputRegister(NextOffset(offset));
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(base, index, scale, disp);
+ }
+ case kMode_M1:
+ case kMode_M2:
+ case kMode_M4:
+ case kMode_M8: {
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_M1, mode);
+ int32_t disp = 0;
+ return Operand(index, scale, disp);
+ }
+ case kMode_M1I:
+ case kMode_M2I:
+ case kMode_M4I:
+ case kMode_M8I: {
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_M1I, mode);
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(index, scale, disp);
+ }
+ case kMode_MI: {
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(Immediate(disp));
+ }
+ case kMode_None:
UNREACHABLE();
- return Operand(no_reg);
+ return Operand(no_reg, 0);
}
+ UNREACHABLE();
+ return Operand(no_reg, 0);
}
Operand MemoryOperand() {
diff --git a/src/compiler/ia32/instruction-codes-ia32.h b/src/compiler/ia32/instruction-codes-ia32.h
index 3b11000..268a59d 100644
--- a/src/compiler/ia32/instruction-codes-ia32.h
+++ b/src/compiler/ia32/instruction-codes-ia32.h
@@ -61,23 +61,31 @@
//
// We use the following local notation for addressing modes:
//
-// R = register
-// O = register or stack slot
-// D = double register
-// I = immediate (handle, external, int32)
-// MR = [register]
-// MI = [immediate]
-// MRN = [register + register * N in {1, 2, 4, 8}]
-// MRI = [register + immediate]
-// MRNI = [register + register * N in {1, 2, 4, 8} + immediate]
+// M = memory operand
+// R = base register
+// N = index register * N for N in {1, 2, 4, 8}
+// I = immediate displacement (int32_t)
+
#define TARGET_ADDRESSING_MODE_LIST(V) \
- V(MI) /* [K] */ \
- V(MR) /* [%r0] */ \
- V(MRI) /* [%r0 + K] */ \
- V(MR1I) /* [%r0 + %r1 * 1 + K] */ \
- V(MR2I) /* [%r0 + %r1 * 2 + K] */ \
- V(MR4I) /* [%r0 + %r1 * 4 + K] */ \
- V(MR8I) /* [%r0 + %r1 * 8 + K] */
+ V(MR) /* [%r1 ] */ \
+ V(MRI) /* [%r1 + K] */ \
+ V(MR1) /* [%r1 + %r2*1 ] */ \
+ V(MR2) /* [%r1 + %r2*2 ] */ \
+ V(MR4) /* [%r1 + %r2*4 ] */ \
+ V(MR8) /* [%r1 + %r2*8 ] */ \
+ V(MR1I) /* [%r1 + %r2*1 + K] */ \
+ V(MR2I) /* [%r1 + %r2*2 + K] */ \
+ V(MR4I) /* [%r1 + %r2*3 + K] */ \
+ V(MR8I) /* [%r1 + %r2*4 + K] */ \
+ V(M1) /* [ %r2*1 ] */ \
+ V(M2) /* [ %r2*2 ] */ \
+ V(M4) /* [ %r2*4 ] */ \
+ V(M8) /* [ %r2*8 ] */ \
+ V(M1I) /* [ %r2*1 + K] */ \
+ V(M2I) /* [ %r2*2 + K] */ \
+ V(M4I) /* [ %r2*4 + K] */ \
+ V(M8I) /* [ %r2*8 + K] */ \
+ V(MI) /* [ K] */
} // namespace compiler
} // namespace internal
diff --git a/src/compiler/ia32/instruction-selector-ia32-unittest.cc b/src/compiler/ia32/instruction-selector-ia32-unittest.cc
index 50e1358..89d150f 100644
--- a/src/compiler/ia32/instruction-selector-ia32-unittest.cc
+++ b/src/compiler/ia32/instruction-selector-ia32-unittest.cc
@@ -189,9 +189,13 @@
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode());
+ if (base == 0) {
+ ASSERT_EQ(1U, s[0]->InputCount());
+ } else {
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(base, s.ToInt32(s[0]->InputAt(1)));
+ }
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
@@ -205,9 +209,13 @@
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode());
+ if (index == 0) {
+ ASSERT_EQ(1U, s[0]->InputCount());
+ } else {
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
+ }
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
@@ -235,9 +243,13 @@
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
+ if (base == 0) {
+ ASSERT_EQ(2U, s[0]->InputCount());
+ } else {
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(base, s.ToInt32(s[0]->InputAt(1)));
+ }
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
@@ -253,9 +265,13 @@
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
- ASSERT_EQ(3U, s[0]->InputCount());
- ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
- EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
+ if (index == 0) {
+ ASSERT_EQ(2U, s[0]->InputCount());
+ } else {
+ ASSERT_EQ(3U, s[0]->InputCount());
+ ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
+ EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
+ }
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
@@ -265,6 +281,149 @@
InstructionSelectorMemoryAccessTest,
::testing::ValuesIn(kMemoryAccesses));
+
+// -----------------------------------------------------------------------------
+// AddressingMode for loads and stores.
+
+class AddressingModeUnitTest : public InstructionSelectorTest {
+ public:
+ AddressingModeUnitTest() : m(NULL) { Reset(); }
+ ~AddressingModeUnitTest() { delete m; }
+
+ void Run(Node* base, Node* index, AddressingMode mode) {
+ Node* load = m->Load(kMachInt32, base, index);
+ m->Store(kMachInt32, base, index, load);
+ m->Return(m->Int32Constant(0));
+ Stream s = m->Build();
+ ASSERT_EQ(2U, s.size());
+ EXPECT_EQ(mode, s[0]->addressing_mode());
+ EXPECT_EQ(mode, s[1]->addressing_mode());
+ }
+
+ Node* zero;
+ Node* null_ptr;
+ Node* non_zero;
+ Node* base_reg; // opaque value to generate base as register
+ Node* index_reg; // opaque value to generate index as register
+ Node* scales[4];
+ StreamBuilder* m;
+
+ void Reset() {
+ delete m;
+ m = new StreamBuilder(this, kMachInt32, kMachInt32, kMachInt32);
+ zero = m->Int32Constant(0);
+ null_ptr = m->Int32Constant(0);
+ non_zero = m->Int32Constant(127);
+ base_reg = m->Parameter(0);
+ index_reg = m->Parameter(0);
+
+ scales[0] = m->Int32Constant(1);
+ scales[1] = m->Int32Constant(2);
+ scales[2] = m->Int32Constant(4);
+ scales[3] = m->Int32Constant(8);
+ }
+};
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR) {
+ Node* base = base_reg;
+ Node* index = zero;
+ Run(base, index, kMode_MR);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRI) {
+ Node* base = base_reg;
+ Node* index = non_zero;
+ Run(base, index, kMode_MRI);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR1) {
+ Node* base = base_reg;
+ Node* index = index_reg;
+ Run(base, index, kMode_MR1);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRN) {
+ AddressingMode expected[] = {kMode_MR1, kMode_MR2, kMode_MR4, kMode_MR8};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = base_reg;
+ Node* index = m->Int32Mul(index_reg, scales[i]);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR1I) {
+ Node* base = base_reg;
+ Node* index = m->Int32Add(index_reg, non_zero);
+ Run(base, index, kMode_MR1I);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRNI) {
+ AddressingMode expected[] = {kMode_MR1I, kMode_MR2I, kMode_MR4I, kMode_MR8I};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = base_reg;
+ Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_M1) {
+ Node* base = null_ptr;
+ Node* index = index_reg;
+ Run(base, index, kMode_MR);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MN) {
+ AddressingMode expected[] = {kMode_MR, kMode_M2, kMode_M4, kMode_M8};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = null_ptr;
+ Node* index = m->Int32Mul(index_reg, scales[i]);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_M1I) {
+ Node* base = null_ptr;
+ Node* index = m->Int32Add(index_reg, non_zero);
+ Run(base, index, kMode_MRI);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MNI) {
+ AddressingMode expected[] = {kMode_MRI, kMode_M2I, kMode_M4I, kMode_M8I};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = null_ptr;
+ Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MI) {
+ Node* bases[] = {null_ptr, non_zero};
+ Node* indices[] = {zero, non_zero};
+ for (size_t i = 0; i < arraysize(bases); ++i) {
+ for (size_t j = 0; j < arraysize(indices); ++j) {
+ Reset();
+ Node* base = bases[i];
+ Node* index = indices[j];
+ Run(base, index, kMode_MI);
+ }
+ }
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8
diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc
index d72f0dc..70bee35 100644
--- a/src/compiler/ia32/instruction-selector-ia32.cc
+++ b/src/compiler/ia32/instruction-selector-ia32.cc
@@ -44,10 +44,108 @@
};
+class AddressingModeMatcher {
+ public:
+ AddressingModeMatcher(IA32OperandGenerator* g, Node* base, Node* index)
+ : base_operand_(NULL),
+ index_operand_(NULL),
+ displacement_operand_(NULL),
+ mode_(kMode_None) {
+ Int32Matcher index_imm(index);
+ if (index_imm.HasValue()) {
+ int32_t displacement = index_imm.Value();
+ // Compute base operand and fold base immediate into displacement.
+ Int32Matcher base_imm(base);
+ if (!base_imm.HasValue()) {
+ base_operand_ = g->UseRegister(base);
+ } else {
+ displacement += base_imm.Value();
+ }
+ if (displacement != 0 || base_operand_ == NULL) {
+ displacement_operand_ = g->TempImmediate(displacement);
+ }
+ if (base_operand_ == NULL) {
+ mode_ = kMode_MI;
+ } else {
+ if (displacement == 0) {
+ mode_ = kMode_MR;
+ } else {
+ mode_ = kMode_MRI;
+ }
+ }
+ } else {
+ // Compute index and displacement.
+ IndexAndDisplacementMatcher matcher(index);
+ index_operand_ = g->UseRegister(matcher.index_node());
+ int32_t displacement = matcher.displacement();
+ // Compute base operand and fold base immediate into displacement.
+ Int32Matcher base_imm(base);
+ if (!base_imm.HasValue()) {
+ base_operand_ = g->UseRegister(base);
+ } else {
+ displacement += base_imm.Value();
+ }
+ // Compute displacement operand.
+ if (displacement != 0) {
+ displacement_operand_ = g->TempImmediate(displacement);
+ }
+ // Compute mode with scale factor one.
+ if (base_operand_ == NULL) {
+ if (displacement_operand_ == NULL) {
+ mode_ = kMode_M1;
+ } else {
+ mode_ = kMode_M1I;
+ }
+ } else {
+ if (displacement_operand_ == NULL) {
+ mode_ = kMode_MR1;
+ } else {
+ mode_ = kMode_MR1I;
+ }
+ }
+ // Adjust mode to actual scale factor.
+ mode_ = GetMode(mode_, matcher.power());
+ // Don't emit instructions with scale factor 1 if there's no base.
+ if (mode_ == kMode_M1) {
+ mode_ = kMode_MR;
+ } else if (mode_ == kMode_M1I) {
+ mode_ = kMode_MRI;
+ }
+ }
+ DCHECK_NE(kMode_None, mode_);
+ }
+
+ AddressingMode GetMode(AddressingMode one, int power) {
+ return static_cast<AddressingMode>(static_cast<int>(one) + power);
+ }
+
+ size_t SetInputs(InstructionOperand** inputs) {
+ size_t input_count = 0;
+ // Compute inputs_ and input_count.
+ if (base_operand_ != NULL) {
+ inputs[input_count++] = base_operand_;
+ }
+ if (index_operand_ != NULL) {
+ inputs[input_count++] = index_operand_;
+ }
+ if (displacement_operand_ != NULL) {
+ inputs[input_count++] = displacement_operand_;
+ }
+ DCHECK_NE(input_count, 0);
+ return input_count;
+ }
+
+ static const int kMaxInputCount = 3;
+ InstructionOperand* base_operand_;
+ InstructionOperand* index_operand_;
+ InstructionOperand* displacement_operand_;
+ AddressingMode mode_;
+};
+
+
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
- IA32OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
@@ -75,23 +173,14 @@
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- if (Int32Matcher(index).Is(0)) { // load [#base + #0]
- Emit(opcode | AddressingModeField::encode(kMode_MI),
- g.DefineAsRegister(node), g.UseImmediate(base));
- } else { // load [#base + %index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(index),
- g.UseImmediate(base));
- }
- } else if (g.CanBeImmediate(index)) { // load [%base + #index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
- } else { // load [%base + %index + K]
- Emit(opcode | AddressingModeField::encode(kMode_MR1I),
- g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
- }
- // TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
+
+ IA32OperandGenerator g(this);
+ AddressingModeMatcher matcher(&g, base, index);
+ InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
+ InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
+ InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount];
+ size_t input_count = matcher.SetInputs(inputs);
+ Emit(code, 1, outputs, input_count, inputs);
}
@@ -115,14 +204,7 @@
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
- InstructionOperand* val;
- if (g.CanBeImmediate(value)) {
- val = g.UseImmediate(value);
- } else if (rep == kRepWord8 || rep == kRepBit) {
- val = g.UseByteRegister(value);
- } else {
- val = g.UseRegister(value);
- }
+
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
@@ -146,22 +228,22 @@
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- if (Int32Matcher(index).Is(0)) { // store [#base], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MI), NULL,
- g.UseImmediate(base), val);
- } else { // store [#base + %index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- g.UseRegister(index), g.UseImmediate(base), val);
- }
- } else if (g.CanBeImmediate(index)) { // store [%base + #index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- g.UseRegister(base), g.UseImmediate(index), val);
- } else { // store [%base + %index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MR1I), NULL,
- g.UseRegister(base), g.UseRegister(index), val);
+
+ InstructionOperand* val;
+ if (g.CanBeImmediate(value)) {
+ val = g.UseImmediate(value);
+ } else if (rep == kRepWord8 || rep == kRepBit) {
+ val = g.UseByteRegister(value);
+ } else {
+ val = g.UseRegister(value);
}
- // TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
+
+ AddressingModeMatcher matcher(&g, base, index);
+ InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
+ InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount + 1];
+ size_t input_count = matcher.SetInputs(inputs);
+ inputs[input_count++] = val;
+ Emit(code, 0, static_cast<InstructionOperand**>(NULL), input_count, inputs);
}
diff --git a/src/compiler/instruction-codes.h b/src/compiler/instruction-codes.h
index 2d921bd..4b7fe67 100644
--- a/src/compiler/instruction-codes.h
+++ b/src/compiler/instruction-codes.h
@@ -107,10 +107,10 @@
// continuation into a single InstructionCode which is stored as part of
// the instruction.
typedef BitField<ArchOpcode, 0, 7> ArchOpcodeField;
-typedef BitField<AddressingMode, 7, 4> AddressingModeField;
-typedef BitField<FlagsMode, 11, 2> FlagsModeField;
-typedef BitField<FlagsCondition, 13, 5> FlagsConditionField;
-typedef BitField<int, 13, 19> MiscField;
+typedef BitField<AddressingMode, 7, 5> AddressingModeField;
+typedef BitField<FlagsMode, 12, 2> FlagsModeField;
+typedef BitField<FlagsCondition, 14, 5> FlagsConditionField;
+typedef BitField<int, 14, 18> MiscField;
} // namespace compiler
} // namespace internal
diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc
index 9764341..fa31d05 100644
--- a/src/compiler/machine-operator-reducer.cc
+++ b/src/compiler/machine-operator-reducer.cc
@@ -352,6 +352,9 @@
}
case IrOpcode::kFloat64Add: {
Float64BinopMatcher m(node);
+ if (m.right().IsNaN()) { // x + NaN => NaN
+ return Replace(m.right().node());
+ }
if (m.IsFoldable()) { // K + K => K
return ReplaceFloat64(m.left().Value() + m.right().Value());
}
@@ -359,6 +362,15 @@
}
case IrOpcode::kFloat64Sub: {
Float64BinopMatcher m(node);
+ if (m.right().Is(0) && (Double(m.right().Value()).Sign() > 0)) {
+ return Replace(m.left().node()); // x - 0 => x
+ }
+ if (m.right().IsNaN()) { // x - NaN => NaN
+ return Replace(m.right().node());
+ }
+ if (m.left().IsNaN()) { // NaN - x => NaN
+ return Replace(m.left().node());
+ }
if (m.IsFoldable()) { // K - K => K
return ReplaceFloat64(m.left().Value() - m.right().Value());
}
@@ -391,6 +403,9 @@
}
case IrOpcode::kFloat64Mod: {
Float64BinopMatcher m(node);
+ if (m.right().Is(0)) { // x % 0 => NaN
+ return ReplaceFloat64(base::OS::nan_value());
+ }
if (m.right().IsNaN()) { // x % NaN => NaN
return Replace(m.right().node());
}
diff --git a/src/compiler/node-matchers.h b/src/compiler/node-matchers.h
index e62eaee..6019cba 100644
--- a/src/compiler/node-matchers.h
+++ b/src/compiler/node-matchers.h
@@ -139,6 +139,96 @@
typedef BinopMatcher<Uint64Matcher, Uint64Matcher> Uint64BinopMatcher;
typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
+
+// Fairly intel-specify node matcher used for matching scale factors in
+// addressing modes.
+// Matches nodes of form [x * N] for N in {1,2,4,8}
+class ScaleFactorMatcher : public NodeMatcher {
+ public:
+ explicit ScaleFactorMatcher(Node* node)
+ : NodeMatcher(node), left_(NULL), power_(0) {
+ Match();
+ }
+
+ bool Matches() { return left_ != NULL; }
+ int Power() {
+ DCHECK(Matches());
+ return power_;
+ }
+ Node* Left() {
+ DCHECK(Matches());
+ return left_;
+ }
+
+ private:
+ void Match() {
+ if (opcode() != IrOpcode::kInt32Mul) return;
+ Int32BinopMatcher m(node());
+ if (!m.right().HasValue()) return;
+ int32_t value = m.right().Value();
+ switch (value) {
+ case 8:
+ power_++; // Fall through.
+ case 4:
+ power_++; // Fall through.
+ case 2:
+ power_++; // Fall through.
+ case 1:
+ break;
+ default:
+ return;
+ }
+ left_ = m.left().node();
+ }
+
+ Node* left_;
+ int power_;
+};
+
+
+// Fairly intel-specify node matcher used for matching index and displacement
+// operands in addressing modes.
+// Matches nodes of form:
+// [x * N]
+// [x * N + K]
+// [x + K]
+// [x] -- fallback case
+// for N in {1,2,4,8} and K int32_t
+class IndexAndDisplacementMatcher : public NodeMatcher {
+ public:
+ explicit IndexAndDisplacementMatcher(Node* node)
+ : NodeMatcher(node), index_node_(node), displacement_(0), power_(0) {
+ Match();
+ }
+
+ Node* index_node() { return index_node_; }
+ int displacement() { return displacement_; }
+ int power() { return power_; }
+
+ private:
+ void Match() {
+ if (opcode() == IrOpcode::kInt32Add) {
+ // Assume reduction has put constant on the right.
+ Int32BinopMatcher m(node());
+ if (m.right().HasValue()) {
+ displacement_ = m.right().Value();
+ index_node_ = m.left().node();
+ }
+ }
+ // Test scale factor.
+ ScaleFactorMatcher scale_matcher(index_node_);
+ if (scale_matcher.Matches()) {
+ index_node_ = scale_matcher.Left();
+ power_ = scale_matcher.Power();
+ }
+ }
+
+ Node* index_node_;
+ int displacement_;
+ int power_;
+};
+
+
} // namespace compiler
} // namespace internal
} // namespace v8
diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc
index 87a5370..333382a 100644
--- a/src/compiler/pipeline.cc
+++ b/src/compiler/pipeline.cc
@@ -6,6 +6,7 @@
#include "src/base/platform/elapsed-timer.h"
#include "src/compiler/ast-graph-builder.h"
+#include "src/compiler/basic-block-instrumentor.h"
#include "src/compiler/change-lowering.h"
#include "src/compiler/code-generator.h"
#include "src/compiler/graph-replay.h"
@@ -315,6 +316,8 @@
VerifyAndPrintGraph(&graph, "Lowered generic");
}
+ source_positions.RemoveDecorator();
+
Handle<Code> code = Handle<Code>::null();
{
// Compute a schedule.
@@ -381,6 +384,11 @@
DCHECK_NOT_NULL(schedule);
CHECK(SupportedBackend());
+ BasicBlockProfiler::Data* profiler_data = NULL;
+ if (FLAG_turbo_profiling) {
+ profiler_data = BasicBlockInstrumentor::Instrument(info_, graph, schedule);
+ }
+
InstructionSequence sequence(linkage, graph, schedule);
// Select and schedule instructions covering the scheduled graph.
@@ -417,7 +425,15 @@
// Generate native sequence.
CodeGenerator generator(&sequence);
- return generator.GenerateCode();
+ Handle<Code> code = generator.GenerateCode();
+ if (profiler_data != NULL) {
+#if ENABLE_DISASSEMBLER
+ OStringStream os;
+ code->Disassemble(NULL, os);
+ profiler_data->SetCode(&os);
+#endif
+ }
+ return code;
}
diff --git a/src/compiler/schedule.h b/src/compiler/schedule.h
index 0ea499c..0094d57 100644
--- a/src/compiler/schedule.h
+++ b/src/compiler/schedule.h
@@ -22,6 +22,7 @@
namespace compiler {
class BasicBlock;
+class BasicBlockInstrumentor;
class Graph;
class ConstructScheduleData;
class CodeGenerator; // Because of a namespace bug in clang.
@@ -279,6 +280,7 @@
private:
friend class ScheduleVisualizer;
+ friend class BasicBlockInstrumentor;
void SetControlInput(BasicBlock* block, Node* node) {
block->control_input_ = node;
diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc
index 10cafee..fdf6385 100644
--- a/src/compiler/x64/code-generator-x64.cc
+++ b/src/compiler/x64/code-generator-x64.cc
@@ -145,22 +145,79 @@
return result;
}
- Operand MemoryOperand(int* first_input) {
- const int offset = *first_input;
- switch (AddressingModeField::decode(instr_->opcode())) {
- case kMode_MR1I: {
- *first_input += 2;
- Register index = InputRegister(offset + 1);
- return Operand(InputRegister(offset + 0), index, times_1,
- 0); // TODO(dcarney): K != 0
+ static int NextOffset(int* offset) {
+ int i = *offset;
+ (*offset)++;
+ return i;
+ }
+
+ static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
+ STATIC_ASSERT(0 == static_cast<int>(times_1));
+ STATIC_ASSERT(1 == static_cast<int>(times_2));
+ STATIC_ASSERT(2 == static_cast<int>(times_4));
+ STATIC_ASSERT(3 == static_cast<int>(times_8));
+ int scale = static_cast<int>(mode - one);
+ DCHECK(scale >= 0 && scale < 4);
+ return static_cast<ScaleFactor>(scale);
+ }
+
+ Operand MemoryOperand(int* offset) {
+ AddressingMode mode = AddressingModeField::decode(instr_->opcode());
+ switch (mode) {
+ case kMode_MR: {
+ Register base = InputRegister(NextOffset(offset));
+ int32_t disp = 0;
+ return Operand(base, disp);
}
- case kMode_MRI:
- *first_input += 2;
- return Operand(InputRegister(offset + 0), InputInt32(offset + 1));
- default:
+ case kMode_MRI: {
+ Register base = InputRegister(NextOffset(offset));
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(base, disp);
+ }
+ case kMode_MR1:
+ case kMode_MR2:
+ case kMode_MR4:
+ case kMode_MR8: {
+ Register base = InputRegister(NextOffset(offset));
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_MR1, mode);
+ int32_t disp = 0;
+ return Operand(base, index, scale, disp);
+ }
+ case kMode_MR1I:
+ case kMode_MR2I:
+ case kMode_MR4I:
+ case kMode_MR8I: {
+ Register base = InputRegister(NextOffset(offset));
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(base, index, scale, disp);
+ }
+ case kMode_M1:
+ case kMode_M2:
+ case kMode_M4:
+ case kMode_M8: {
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_M1, mode);
+ int32_t disp = 0;
+ return Operand(index, scale, disp);
+ }
+ case kMode_M1I:
+ case kMode_M2I:
+ case kMode_M4I:
+ case kMode_M8I: {
+ Register index = InputRegister(NextOffset(offset));
+ ScaleFactor scale = ScaleFor(kMode_M1I, mode);
+ int32_t disp = InputInt32(NextOffset(offset));
+ return Operand(index, scale, disp);
+ }
+ case kMode_None:
UNREACHABLE();
return Operand(no_reg, 0);
}
+ UNREACHABLE();
+ return Operand(no_reg, 0);
}
Operand MemoryOperand() {
diff --git a/src/compiler/x64/instruction-codes-x64.h b/src/compiler/x64/instruction-codes-x64.h
index 0ab0b1a..336c592 100644
--- a/src/compiler/x64/instruction-codes-x64.h
+++ b/src/compiler/x64/instruction-codes-x64.h
@@ -79,22 +79,30 @@
//
// We use the following local notation for addressing modes:
//
-// R = register
-// O = register or stack slot
-// D = double register
-// I = immediate (handle, external, int32)
-// MR = [register]
-// MI = [immediate]
-// MRN = [register + register * N in {1, 2, 4, 8}]
-// MRI = [register + immediate]
-// MRNI = [register + register * N in {1, 2, 4, 8} + immediate]
+// M = memory operand
+// R = base register
+// N = index register * N for N in {1, 2, 4, 8}
+// I = immediate displacement (int32_t)
+
#define TARGET_ADDRESSING_MODE_LIST(V) \
- V(MR) /* [%r1] */ \
- V(MRI) /* [%r1 + K] */ \
- V(MR1I) /* [%r1 + %r2 + K] */ \
+ V(MR) /* [%r1 ] */ \
+ V(MRI) /* [%r1 + K] */ \
+ V(MR1) /* [%r1 + %r2*1 ] */ \
+ V(MR2) /* [%r1 + %r2*2 ] */ \
+ V(MR4) /* [%r1 + %r2*4 ] */ \
+ V(MR8) /* [%r1 + %r2*8 ] */ \
+ V(MR1I) /* [%r1 + %r2*1 + K] */ \
V(MR2I) /* [%r1 + %r2*2 + K] */ \
- V(MR4I) /* [%r1 + %r2*4 + K] */ \
- V(MR8I) /* [%r1 + %r2*8 + K] */
+ V(MR4I) /* [%r1 + %r2*3 + K] */ \
+ V(MR8I) /* [%r1 + %r2*4 + K] */ \
+ V(M1) /* [ %r2*1 ] */ \
+ V(M2) /* [ %r2*2 ] */ \
+ V(M4) /* [ %r2*4 ] */ \
+ V(M8) /* [ %r2*8 ] */ \
+ V(M1I) /* [ %r2*1 + K] */ \
+ V(M2I) /* [ %r2*2 + K] */ \
+ V(M4I) /* [ %r2*4 + K] */ \
+ V(M8I) /* [ %r2*8 + K] */
} // namespace compiler
} // namespace internal
diff --git a/src/compiler/x64/instruction-selector-x64-unittest.cc b/src/compiler/x64/instruction-selector-x64-unittest.cc
index d94c73f..f5545a7 100644
--- a/src/compiler/x64/instruction-selector-x64-unittest.cc
+++ b/src/compiler/x64/instruction-selector-x64-unittest.cc
@@ -161,6 +161,134 @@
InstructionSelectorMemoryAccessTest,
::testing::ValuesIn(kMemoryAccesses));
+// -----------------------------------------------------------------------------
+// AddressingMode for loads and stores.
+
+class AddressingModeUnitTest : public InstructionSelectorTest {
+ public:
+ AddressingModeUnitTest() : m(NULL) { Reset(); }
+ ~AddressingModeUnitTest() { delete m; }
+
+ void Run(Node* base, Node* index, AddressingMode mode) {
+ Node* load = m->Load(kMachInt32, base, index);
+ m->Store(kMachInt32, base, index, load);
+ m->Return(m->Int32Constant(0));
+ Stream s = m->Build();
+ ASSERT_EQ(2U, s.size());
+ EXPECT_EQ(mode, s[0]->addressing_mode());
+ EXPECT_EQ(mode, s[1]->addressing_mode());
+ }
+
+ Node* zero;
+ Node* null_ptr;
+ Node* non_zero;
+ Node* base_reg; // opaque value to generate base as register
+ Node* index_reg; // opaque value to generate index as register
+ Node* scales[4];
+ StreamBuilder* m;
+
+ void Reset() {
+ delete m;
+ m = new StreamBuilder(this, kMachInt32, kMachInt32, kMachInt32);
+ zero = m->Int32Constant(0);
+ null_ptr = m->Int64Constant(0);
+ non_zero = m->Int32Constant(127);
+ base_reg = m->Parameter(0);
+ index_reg = m->Parameter(0);
+
+ scales[0] = m->Int32Constant(1);
+ scales[1] = m->Int32Constant(2);
+ scales[2] = m->Int32Constant(4);
+ scales[3] = m->Int32Constant(8);
+ }
+};
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR) {
+ Node* base = base_reg;
+ Node* index = zero;
+ Run(base, index, kMode_MR);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRI) {
+ Node* base = base_reg;
+ Node* index = non_zero;
+ Run(base, index, kMode_MRI);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR1) {
+ Node* base = base_reg;
+ Node* index = index_reg;
+ Run(base, index, kMode_MR1);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRN) {
+ AddressingMode expected[] = {kMode_MR1, kMode_MR2, kMode_MR4, kMode_MR8};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = base_reg;
+ Node* index = m->Int32Mul(index_reg, scales[i]);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MR1I) {
+ Node* base = base_reg;
+ Node* index = m->Int32Add(index_reg, non_zero);
+ Run(base, index, kMode_MR1I);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MRNI) {
+ AddressingMode expected[] = {kMode_MR1I, kMode_MR2I, kMode_MR4I, kMode_MR8I};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = base_reg;
+ Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_M1) {
+ Node* base = null_ptr;
+ Node* index = index_reg;
+ Run(base, index, kMode_M1);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MN) {
+ AddressingMode expected[] = {kMode_M1, kMode_M2, kMode_M4, kMode_M8};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = null_ptr;
+ Node* index = m->Int32Mul(index_reg, scales[i]);
+ Run(base, index, expected[i]);
+ }
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_M1I) {
+ Node* base = null_ptr;
+ Node* index = m->Int32Add(index_reg, non_zero);
+ Run(base, index, kMode_M1I);
+}
+
+
+TEST_F(AddressingModeUnitTest, AddressingMode_MNI) {
+ AddressingMode expected[] = {kMode_M1I, kMode_M2I, kMode_M4I, kMode_M8I};
+ for (size_t i = 0; i < arraysize(scales); ++i) {
+ Reset();
+ Node* base = null_ptr;
+ Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
+ Run(base, index, expected[i]);
+ }
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8
diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc
index 0447b79..82d1e33 100644
--- a/src/compiler/x64/instruction-selector-x64.cc
+++ b/src/compiler/x64/instruction-selector-x64.cc
@@ -20,11 +20,6 @@
Register::ToAllocationIndex(reg));
}
- InstructionOperand* UseByteRegister(Node* node) {
- // TODO(dcarney): relax constraint.
- return UseFixed(node, rdx);
- }
-
InstructionOperand* UseImmediate64(Node* node) { return UseImmediate(node); }
bool CanBeImmediate(Node* node) {
@@ -59,10 +54,88 @@
};
+class AddressingModeMatcher {
+ public:
+ AddressingModeMatcher(X64OperandGenerator* g, Node* base, Node* index)
+ : base_operand_(NULL),
+ index_operand_(NULL),
+ displacement_operand_(NULL),
+ mode_(kMode_None) {
+ Int32Matcher index_imm(index);
+ if (index_imm.HasValue()) {
+ int32_t value = index_imm.Value();
+ if (value == 0) {
+ mode_ = kMode_MR;
+ } else {
+ mode_ = kMode_MRI;
+ index_operand_ = g->UseImmediate(index);
+ }
+ base_operand_ = g->UseRegister(base);
+ } else {
+ // Compute base operand.
+ Int64Matcher base_imm(base);
+ if (!base_imm.HasValue() || base_imm.Value() != 0) {
+ base_operand_ = g->UseRegister(base);
+ }
+ // Compute index and displacement.
+ IndexAndDisplacementMatcher matcher(index);
+ index_operand_ = g->UseRegister(matcher.index_node());
+ if (matcher.displacement() != 0) {
+ displacement_operand_ = g->TempImmediate(matcher.displacement());
+ }
+ // Compute mode with scale factor one.
+ if (base_operand_ == NULL) {
+ if (displacement_operand_ == NULL) {
+ mode_ = kMode_M1;
+ } else {
+ mode_ = kMode_M1I;
+ }
+ } else {
+ if (displacement_operand_ == NULL) {
+ mode_ = kMode_MR1;
+ } else {
+ mode_ = kMode_MR1I;
+ }
+ }
+ // Adjust mode to actual scale factor.
+ mode_ = GetMode(mode_, matcher.power());
+ }
+ DCHECK_NE(kMode_None, mode_);
+ }
+
+ AddressingMode GetMode(AddressingMode one, int power) {
+ return static_cast<AddressingMode>(static_cast<int>(one) + power);
+ }
+
+ size_t SetInputs(InstructionOperand** inputs) {
+ size_t input_count = 0;
+ // Compute inputs_ and input_count.
+ if (base_operand_ != NULL) {
+ inputs[input_count++] = base_operand_;
+ }
+ if (index_operand_ != NULL) {
+ inputs[input_count++] = index_operand_;
+ }
+ if (displacement_operand_ != NULL) {
+ // Pure displacement mode not supported by x64.
+ DCHECK_NE(input_count, 0);
+ inputs[input_count++] = displacement_operand_;
+ }
+ DCHECK_NE(input_count, 0);
+ return input_count;
+ }
+
+ static const int kMaxInputCount = 3;
+ InstructionOperand* base_operand_;
+ InstructionOperand* index_operand_;
+ InstructionOperand* displacement_operand_;
+ AddressingMode mode_;
+};
+
+
void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
- X64OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
@@ -93,18 +166,14 @@
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- // load [#base + %index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(index), g.UseImmediate(base));
- } else if (g.CanBeImmediate(index)) { // load [%base + #index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
- } else { // load [%base + %index + K]
- Emit(opcode | AddressingModeField::encode(kMode_MR1I),
- g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
- }
- // TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
+
+ X64OperandGenerator g(this);
+ AddressingModeMatcher matcher(&g, base, index);
+ InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
+ InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
+ InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount];
+ size_t input_count = matcher.SetInputs(inputs);
+ Emit(code, 1, outputs, input_count, inputs);
}
@@ -128,14 +197,6 @@
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
- InstructionOperand* val;
- if (g.CanBeImmediate(value)) {
- val = g.UseImmediate(value);
- } else if (rep == kRepWord8 || rep == kRepBit) {
- val = g.UseByteRegister(value);
- } else {
- val = g.UseRegister(value);
- }
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
@@ -162,18 +223,20 @@
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- // store [#base + %index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- g.UseRegister(index), g.UseImmediate(base), val);
- } else if (g.CanBeImmediate(index)) { // store [%base + #index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- g.UseRegister(base), g.UseImmediate(index), val);
- } else { // store [%base + %index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MR1I), NULL,
- g.UseRegister(base), g.UseRegister(index), val);
+
+ InstructionOperand* val;
+ if (g.CanBeImmediate(value)) {
+ val = g.UseImmediate(value);
+ } else {
+ val = g.UseRegister(value);
}
- // TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
+
+ AddressingModeMatcher matcher(&g, base, index);
+ InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
+ InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount + 1];
+ size_t input_count = matcher.SetInputs(inputs);
+ inputs[input_count++] = val;
+ Emit(code, 0, static_cast<InstructionOperand**>(NULL), input_count, inputs);
}
@@ -702,8 +765,6 @@
// Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(call, &buffer, true, true);
- // TODO(dcarney): stack alignment for c calls.
- // TODO(dcarney): shadow space on window for c calls.
// Push any stack arguments.
for (NodeVectorRIter input = buffer.pushed_nodes.rbegin();
input != buffer.pushed_nodes.rend(); input++) {
diff --git a/src/d8.cc b/src/d8.cc
index 9db7744..d1929b0 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -46,6 +46,7 @@
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
+#include "src/basic-block-profiler.h"
#include "src/d8-debug.h"
#include "src/debug.h"
#include "src/natives.h"
@@ -1700,6 +1701,14 @@
RunShell(isolate);
}
}
+#ifndef V8_SHARED
+ // Dump basic block profiling data.
+ if (i::BasicBlockProfiler* profiler =
+ reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) {
+ i::OFStream os(stdout);
+ os << *profiler;
+ }
+#endif // !V8_SHARED
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
diff --git a/src/elements.cc b/src/elements.cc
index e2127c4..abb0467 100644
--- a/src/elements.cc
+++ b/src/elements.cc
@@ -247,15 +247,18 @@
}
-static void CopyDoubleToObjectElements(Handle<FixedArrayBase> from_base,
+// NOTE: this method violates the handlified function signature convention:
+// raw pointer parameters in the function that allocates.
+// See ElementsAccessorBase::CopyElements() for details.
+static void CopyDoubleToObjectElements(FixedArrayBase* from_base,
uint32_t from_start,
- Handle<FixedArrayBase> to_base,
- ElementsKind to_kind,
- uint32_t to_start,
+ FixedArrayBase* to_base,
+ ElementsKind to_kind, uint32_t to_start,
int raw_copy_size) {
DCHECK(IsFastSmiOrObjectElementsKind(to_kind));
int copy_size = raw_copy_size;
if (raw_copy_size < 0) {
+ DisallowHeapAllocation no_allocation;
DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
copy_size = Min(from_base->length() - from_start,
@@ -268,7 +271,7 @@
int length = to_base->length() - start;
if (length > 0) {
Heap* heap = from_base->GetHeap();
- MemsetPointer(FixedArray::cast(*to_base)->data_start() + start,
+ MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
heap->the_hole_value(), length);
}
}
@@ -276,9 +279,12 @@
DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
(copy_size + static_cast<int>(from_start)) <= from_base->length());
if (copy_size == 0) return;
+
+ // From here on, the code below could actually allocate. Therefore the raw
+ // values are wrapped into handles.
Isolate* isolate = from_base->GetIsolate();
- Handle<FixedDoubleArray> from = Handle<FixedDoubleArray>::cast(from_base);
- Handle<FixedArray> to = Handle<FixedArray>::cast(to_base);
+ Handle<FixedDoubleArray> from(FixedDoubleArray::cast(from_base), isolate);
+ Handle<FixedArray> to(FixedArray::cast(to_base), isolate);
for (int i = 0; i < copy_size; ++i) {
HandleScope scope(isolate);
if (IsFastSmiElementsKind(to_kind)) {
@@ -702,12 +708,9 @@
uint32_t key,
JSReceiver::DeleteMode mode) OVERRIDE = 0;
- static void CopyElementsImpl(Handle<FixedArrayBase> from,
- uint32_t from_start,
- Handle<FixedArrayBase> to,
- ElementsKind from_kind,
- uint32_t to_start,
- int packed_size,
+ static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
+ FixedArrayBase* to, ElementsKind from_kind,
+ uint32_t to_start, int packed_size,
int copy_size) {
UNREACHABLE();
}
@@ -720,9 +723,15 @@
uint32_t to_start,
int copy_size) FINAL OVERRIDE {
DCHECK(!from.is_null());
- ElementsAccessorSubclass::CopyElementsImpl(
- from, from_start, to, from_kind, to_start, kPackedSizeNotKnown,
- copy_size);
+ // NOTE: the ElementsAccessorSubclass::CopyElementsImpl() methods
+ // violate the handlified function signature convention:
+ // raw pointer parameters in the function that allocates. This is done
+ // intentionally to avoid ArrayConcat() builtin performance degradation.
+ // See the comment in another ElementsAccessorBase::CopyElements() for
+ // details.
+ ElementsAccessorSubclass::CopyElementsImpl(*from, from_start, *to,
+ from_kind, to_start,
+ kPackedSizeNotKnown, copy_size);
}
virtual void CopyElements(
@@ -742,9 +751,18 @@
packed_size = copy_size;
}
}
- Handle<FixedArrayBase> from(from_holder->elements());
+ FixedArrayBase* from = from_holder->elements();
+ // NOTE: the ElementsAccessorSubclass::CopyElementsImpl() methods
+ // violate the handlified function signature convention:
+ // raw pointer parameters in the function that allocates. This is done
+ // intentionally to avoid ArrayConcat() builtin performance degradation.
+ //
+ // Details: The idea is that allocations actually happen only in case of
+ // copying from object with fast double elements to object with object
+ // elements. In all the other cases there are no allocations performed and
+ // handle creation causes noticeable performance degradation of the builtin.
ElementsAccessorSubclass::CopyElementsImpl(
- from, from_start, to, from_kind, to_start, packed_size, copy_size);
+ from, from_start, *to, from_kind, to_start, packed_size, copy_size);
}
virtual MaybeHandle<FixedArray> AddElementsToFixedArray(
@@ -1018,7 +1036,7 @@
};
-static inline ElementsKind ElementsKindForArray(Handle<FixedArrayBase> array) {
+static inline ElementsKind ElementsKindForArray(FixedArrayBase* array) {
switch (array->map()->instance_type()) {
case FIXED_ARRAY_TYPE:
if (array->IsDictionary()) {
@@ -1054,38 +1072,42 @@
: FastElementsAccessor<FastElementsAccessorSubclass,
KindTraits>(name) {}
- static void CopyElementsImpl(Handle<FixedArrayBase> from,
- uint32_t from_start,
- Handle<FixedArrayBase> to,
- ElementsKind from_kind,
- uint32_t to_start,
- int packed_size,
+ // NOTE: this method violates the handlified function signature convention:
+ // raw pointer parameters in the function that allocates.
+ // See ElementsAccessor::CopyElements() for details.
+ // This method could actually allocate if copying from double elements to
+ // object elements.
+ static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
+ FixedArrayBase* to, ElementsKind from_kind,
+ uint32_t to_start, int packed_size,
int copy_size) {
+ DisallowHeapAllocation no_gc;
ElementsKind to_kind = KindTraits::Kind;
switch (from_kind) {
case FAST_SMI_ELEMENTS:
case FAST_HOLEY_SMI_ELEMENTS:
case FAST_ELEMENTS:
case FAST_HOLEY_ELEMENTS:
- CopyObjectToObjectElements(*from, from_kind, from_start, *to, to_kind,
+ CopyObjectToObjectElements(from, from_kind, from_start, to, to_kind,
to_start, copy_size);
break;
case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ AllowHeapAllocation allow_allocation;
CopyDoubleToObjectElements(
from, from_start, to, to_kind, to_start, copy_size);
break;
+ }
case DICTIONARY_ELEMENTS:
- CopyDictionaryToObjectElements(*from, from_start, *to, to_kind,
- to_start, copy_size);
+ CopyDictionaryToObjectElements(from, from_start, to, to_kind, to_start,
+ copy_size);
break;
case SLOPPY_ARGUMENTS_ELEMENTS: {
// TODO(verwaest): This is a temporary hack to support extending
// SLOPPY_ARGUMENTS_ELEMENTS in SetFastElementsCapacityAndLength.
// This case should be UNREACHABLE().
- Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(from);
- Handle<FixedArrayBase> arguments(
- FixedArrayBase::cast(parameter_map->get(1)));
+ FixedArray* parameter_map = FixedArray::cast(from);
+ FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1));
ElementsKind from_kind = ElementsKindForArray(arguments);
CopyElementsImpl(arguments, from_start, to, from_kind,
to_start, packed_size, copy_size);
@@ -1179,31 +1201,29 @@
}
protected:
- static void CopyElementsImpl(Handle<FixedArrayBase> from,
- uint32_t from_start,
- Handle<FixedArrayBase> to,
- ElementsKind from_kind,
- uint32_t to_start,
- int packed_size,
+ static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
+ FixedArrayBase* to, ElementsKind from_kind,
+ uint32_t to_start, int packed_size,
int copy_size) {
+ DisallowHeapAllocation no_allocation;
switch (from_kind) {
case FAST_SMI_ELEMENTS:
- CopyPackedSmiToDoubleElements(*from, from_start, *to, to_start,
+ CopyPackedSmiToDoubleElements(from, from_start, to, to_start,
packed_size, copy_size);
break;
case FAST_HOLEY_SMI_ELEMENTS:
- CopySmiToDoubleElements(*from, from_start, *to, to_start, copy_size);
+ CopySmiToDoubleElements(from, from_start, to, to_start, copy_size);
break;
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
- CopyDoubleToDoubleElements(*from, from_start, *to, to_start, copy_size);
+ CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
break;
case FAST_ELEMENTS:
case FAST_HOLEY_ELEMENTS:
- CopyObjectToDoubleElements(*from, from_start, *to, to_start, copy_size);
+ CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
break;
case DICTIONARY_ELEMENTS:
- CopyDictionaryToDoubleElements(*from, from_start, *to, to_start,
+ CopyDictionaryToDoubleElements(from, from_start, to, to_start,
copy_size);
break;
case SLOPPY_ARGUMENTS_ELEMENTS:
@@ -1439,12 +1459,9 @@
return isolate->factory()->true_value();
}
- static void CopyElementsImpl(Handle<FixedArrayBase> from,
- uint32_t from_start,
- Handle<FixedArrayBase> to,
- ElementsKind from_kind,
- uint32_t to_start,
- int packed_size,
+ static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
+ FixedArrayBase* to, ElementsKind from_kind,
+ uint32_t to_start, int packed_size,
int copy_size) {
UNREACHABLE();
}
@@ -1654,12 +1671,9 @@
return isolate->factory()->true_value();
}
- static void CopyElementsImpl(Handle<FixedArrayBase> from,
- uint32_t from_start,
- Handle<FixedArrayBase> to,
- ElementsKind from_kind,
- uint32_t to_start,
- int packed_size,
+ static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
+ FixedArrayBase* to, ElementsKind from_kind,
+ uint32_t to_start, int packed_size,
int copy_size) {
UNREACHABLE();
}
@@ -1715,7 +1729,7 @@
ElementsAccessor* ElementsAccessor::ForArray(Handle<FixedArrayBase> array) {
- return elements_accessors_[ElementsKindForArray(array)];
+ return elements_accessors_[ElementsKindForArray(*array)];
}
diff --git a/src/elements.h b/src/elements.h
index d0bddf9..f4de4bb 100644
--- a/src/elements.h
+++ b/src/elements.h
@@ -146,9 +146,10 @@
uint32_t destination_start,
int copy_size) = 0;
- // TODO(ishell): Keeping |source_holder| parameter in a non-handlified form
- // helps avoiding ArrayConcat() builtin performance degradation.
- // Revisit this later.
+ // NOTE: this method violates the handlified function signature convention:
+ // raw pointer parameter |source_holder| in the function that allocates.
+ // This is done intentionally to avoid ArrayConcat() builtin performance
+ // degradation.
virtual void CopyElements(
JSObject* source_holder,
uint32_t source_start,
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 6c90912..672f2b6 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -345,6 +345,7 @@
DEFINE_BOOL(turbo_inlining, false, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_IMPLICATION(turbo_inlining, turbo_types)
+DEFINE_BOOL(turbo_profiling, false, "enable profiling in TurboFan")
DEFINE_INT(typed_array_max_size_in_heap, 64,
"threshold for in-heap typed array")
diff --git a/src/full-codegen.h b/src/full-codegen.h
index 71e1b608..fdb3293 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -521,6 +521,8 @@
// The receiver is left on the stack by the IC.
void EmitNamedPropertyLoad(Property* expr);
+ // Load a value from super.named prroperty.
+ // Expect receiver ('this' value) and home_object on the stack.
void EmitNamedSuperPropertyLoad(Property* expr);
// Load a value from a keyed property.
@@ -558,6 +560,10 @@
// of the stack and the right-hand-side value in the accumulator.
void EmitNamedPropertyAssignment(Assignment* expr);
+ // Complete a super named property assignment. The right-hand-side value
+ // is expected in accumulator.
+ void EmitNamedSuperPropertyAssignment(Assignment* expr);
+
// Complete a keyed property assignment. The receiver and key are
// expected on top of the stack and the right-hand-side value in the
// accumulator.
diff --git a/src/heap/spaces.h b/src/heap/spaces.h
index 1a89449..ef55357 100644
--- a/src/heap/spaces.h
+++ b/src/heap/spaces.h
@@ -880,6 +880,10 @@
DCHECK(valid());
return static_cast<Address>(code_range_->address());
}
+ size_t size() {
+ DCHECK(valid());
+ return code_range_->size();
+ }
bool contains(Address address) {
if (!valid()) return false;
Address start = static_cast<Address>(code_range_->address());
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index a5065aa..a382dad 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -1816,13 +1816,19 @@
// Left-hand side can only be a property, a global or a (parameter or local)
// slot.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ enum LhsKind {
+ VARIABLE,
+ NAMED_PROPERTY,
+ KEYED_PROPERTY,
+ NAMED_SUPER_PROPERTY
+ };
LhsKind assign_type = VARIABLE;
Property* property = expr->target()->AsProperty();
if (property != NULL) {
assign_type = (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
+ ? (property->IsSuperAccess() ? NAMED_SUPER_PROPERTY
+ : NAMED_PROPERTY)
+ : KEYED_PROPERTY;
}
// Evaluate LHS expression.
@@ -1830,6 +1836,15 @@
case VARIABLE:
// Nothing to do here.
break;
+ case NAMED_SUPER_PROPERTY:
+ VisitForStackValue(property->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(property->obj()->AsSuperReference());
+ __ push(result_register());
+ if (expr->is_compound()) {
+ __ push(MemOperand(esp, kPointerSize));
+ __ push(result_register());
+ }
+ break;
case NAMED_PROPERTY:
if (expr->is_compound()) {
// We need the receiver both on the stack and in the register.
@@ -1863,6 +1878,10 @@
EmitVariableLoad(expr->target()->AsVariableProxy());
PrepareForBailout(expr->target(), TOS_REG);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
case NAMED_PROPERTY:
EmitNamedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
@@ -1912,6 +1931,9 @@
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyAssignment(expr);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyAssignment(expr);
break;
@@ -2239,6 +2261,8 @@
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
+ DCHECK(!prop->IsSuperAccess());
+
__ mov(LoadDescriptor::NameRegister(), Immediate(key->value()));
if (FLAG_vector_ics) {
__ mov(VectorLoadICDescriptor::SlotRegister(),
@@ -2251,15 +2275,12 @@
void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
+ // Stack: receiver, home_object.
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
DCHECK(prop->IsSuperAccess());
- SuperReference* super_ref = prop->obj()->AsSuperReference();
- EmitLoadHomeObject(super_ref);
- __ push(eax);
- VisitForStackValue(super_ref->this_var());
__ push(Immediate(key->value()));
__ CallRuntime(Runtime::kLoadFromSuper, 3);
}
@@ -2530,6 +2551,24 @@
}
+void FullCodeGenerator::EmitNamedSuperPropertyAssignment(Assignment* expr) {
+ // Assignment to named property of super.
+ // eax : value
+ // stack : receiver ('this'), home_object
+ Property* prop = expr->target()->AsProperty();
+ DCHECK(prop != NULL);
+ Literal* key = prop->key()->AsLiteral();
+ DCHECK(key != NULL);
+
+ __ push(eax);
+ __ push(Immediate(key->value()));
+ __ CallRuntime((strict_mode() == STRICT ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy),
+ 4);
+ context()->Plug(eax);
+}
+
+
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
// Assignment to a property, using a keyed store IC.
// eax : value
@@ -2559,6 +2598,9 @@
__ Move(LoadDescriptor::ReceiverRegister(), result_register());
EmitNamedPropertyLoad(expr);
} else {
+ VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(expr->obj()->AsSuperReference());
+ __ push(result_register());
EmitNamedSuperPropertyLoad(expr);
}
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
@@ -2627,14 +2669,14 @@
__ push(eax);
VisitForAccumulatorValue(super_ref->this_var());
__ push(eax);
- __ push(Operand(esp, kPointerSize));
__ push(eax);
+ __ push(Operand(esp, kPointerSize * 2));
__ push(Immediate(key->value()));
// Stack here:
// - home_object
// - this (receiver)
- // - home_object <-- LoadFromSuper will pop here and below.
- // - this (receiver)
+ // - this (receiver) <-- LoadFromSuper will pop here and below.
+ // - home_object
// - key
__ CallRuntime(Runtime::kLoadFromSuper, 3);
@@ -4298,6 +4340,11 @@
if (prop != NULL) {
assign_type =
(prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ if (prop->IsSuperAccess()) {
+ // throw exception.
+ VisitSuperReference(prop->obj()->AsSuperReference());
+ return;
+ }
}
// Evaluate expression and get value.
diff --git a/src/isolate.cc b/src/isolate.cc
index c6a8b81..7d1f835 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -10,6 +10,7 @@
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/base/utils/random-number-generator.h"
+#include "src/basic-block-profiler.h"
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/compilation-cache.h"
@@ -110,9 +111,6 @@
base::Thread::LocalStorageKey Isolate::isolate_key_;
base::Thread::LocalStorageKey Isolate::thread_id_key_;
base::Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_;
-#ifdef DEBUG
-base::Thread::LocalStorageKey PerThreadAssertScopeBase::thread_local_key;
-#endif // DEBUG
base::LazyMutex Isolate::thread_data_table_mutex_ = LAZY_MUTEX_INITIALIZER;
Isolate::ThreadDataTable* Isolate::thread_data_table_ = NULL;
base::Atomic32 Isolate::isolate_counter_ = 0;
@@ -157,10 +155,6 @@
isolate_key_ = base::Thread::CreateThreadLocalKey();
thread_id_key_ = base::Thread::CreateThreadLocalKey();
per_isolate_thread_data_key_ = base::Thread::CreateThreadLocalKey();
-#ifdef DEBUG
- PerThreadAssertScopeBase::thread_local_key =
- base::Thread::CreateThreadLocalKey();
-#endif // DEBUG
thread_data_table_ = new Isolate::ThreadDataTable();
}
@@ -1516,7 +1510,8 @@
num_sweeper_threads_(0),
stress_deopt_count_(0),
next_optimization_id_(0),
- use_counter_callback_(NULL) {
+ use_counter_callback_(NULL),
+ basic_block_profiler_(NULL) {
{
base::LockGuard<base::Mutex> lock_guard(thread_data_table_mutex_.Pointer());
CHECK(thread_data_table_);
@@ -1640,6 +1635,10 @@
delete runtime_profiler_;
runtime_profiler_ = NULL;
}
+
+ delete basic_block_profiler_;
+ basic_block_profiler_ = NULL;
+
heap_.TearDown();
logger_->TearDown();
@@ -2363,6 +2362,14 @@
}
+BasicBlockProfiler* Isolate::GetOrCreateBasicBlockProfiler() {
+ if (basic_block_profiler_ == NULL) {
+ basic_block_profiler_ = new BasicBlockProfiler();
+ }
+ return basic_block_profiler_;
+}
+
+
bool StackLimitCheck::JsHasOverflowed() const {
StackGuard* stack_guard = isolate_->stack_guard();
#ifdef USE_SIMULATOR
diff --git a/src/isolate.h b/src/isolate.h
index a92554e..9ea3074 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -32,6 +32,7 @@
namespace internal {
+class BasicBlockProfiler;
class Bootstrapper;
class CallInterfaceDescriptorData;
class CodeGenerator;
@@ -1107,6 +1108,9 @@
void SetUseCounterCallback(v8::Isolate::UseCounterCallback callback);
void CountUsage(v8::Isolate::UseCounterFeature feature);
+ BasicBlockProfiler* GetOrCreateBasicBlockProfiler();
+ BasicBlockProfiler* basic_block_profiler() { return basic_block_profiler_; }
+
static Isolate* NewForTesting() { return new Isolate(); }
private:
@@ -1327,6 +1331,7 @@
List<CallCompletedCallback> call_completed_callbacks_;
v8::Isolate::UseCounterCallback use_counter_callback_;
+ BasicBlockProfiler* basic_block_profiler_;
friend class ExecutionAccess;
friend class HandleScopeImplementer;
diff --git a/src/objects.cc b/src/objects.cc
index bea2792..482b5be 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -2807,7 +2807,8 @@
MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
Handle<Object> value,
StrictMode strict_mode,
- StoreFromKeyed store_mode) {
+ StoreFromKeyed store_mode,
+ StorePropertyMode data_store_mode) {
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc(it->isolate());
@@ -2902,6 +2903,16 @@
Object);
}
+ if (data_store_mode == SUPER_PROPERTY) {
+ if (strict_mode == STRICT) {
+ Handle<Object> args[1] = {it->name()};
+ THROW_NEW_ERROR(it->isolate(),
+ NewReferenceError("not_defined", HandleVector(args, 1)),
+ Object);
+ }
+ return value;
+ }
+
return AddDataProperty(it, value, NONE, strict_mode, store_mode);
}
@@ -8999,19 +9010,25 @@
}
+inline static uint32_t ObjectAddressForHashing(Object* object) {
+ uint32_t value = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object));
+ return value & MemoryChunk::kAlignmentMask;
+}
+
+
int Map::Hash() {
// For performance reasons we only hash the 3 most variable fields of a map:
- // constructor, prototype and bit_field2.
+ // constructor, prototype and bit_field2. For predictability reasons we
+ // use objects' offsets in respective pages for hashing instead of raw
+ // addresses.
// Shift away the tag.
- int hash = (static_cast<uint32_t>(
- reinterpret_cast<uintptr_t>(constructor())) >> 2);
+ int hash = ObjectAddressForHashing(constructor()) >> 2;
// XOR-ing the prototype and constructor directly yields too many zero bits
// when the two pointers are close (which is fairly common).
- // To avoid this we shift the prototype 4 bits relatively to the constructor.
- hash ^= (static_cast<uint32_t>(
- reinterpret_cast<uintptr_t>(prototype())) << 2);
+ // To avoid this we shift the prototype bits relatively to the constructor.
+ hash ^= ObjectAddressForHashing(prototype()) << (32 - kPageSizeBits);
return hash ^ (hash >> 16) ^ bit_field2();
}
diff --git a/src/objects.h b/src/objects.h
index e232197..f2e17d34 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -1007,6 +1007,8 @@
CERTAINLY_NOT_STORE_FROM_KEYED
};
+ enum StorePropertyMode { NORMAL_PROPERTY, SUPER_PROPERTY };
+
INLINE(bool IsFixedArrayBase() const);
INLINE(bool IsExternal() const);
INLINE(bool IsAccessorInfo() const);
@@ -1116,7 +1118,8 @@
MUST_USE_RESULT static MaybeHandle<Object> SetProperty(
LookupIterator* it, Handle<Object> value, StrictMode strict_mode,
- StoreFromKeyed store_mode);
+ StoreFromKeyed store_mode,
+ StorePropertyMode data_store_mode = NORMAL_PROPERTY);
MUST_USE_RESULT static MaybeHandle<Object> WriteToReadOnlyProperty(
LookupIterator* it, Handle<Object> value, StrictMode strict_mode);
static Handle<Object> SetDataProperty(LookupIterator* it,
diff --git a/src/runtime/runtime-collections.cc b/src/runtime/runtime-collections.cc
new file mode 100644
index 0000000..d0d6aa6
--- /dev/null
+++ b/src/runtime/runtime-collections.cc
@@ -0,0 +1,347 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_SetInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
+ holder->set_table(*table);
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetAdd) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
+ table = OrderedHashSet::Add(table, key);
+ holder->set_table(*table);
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetHas) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
+ return isolate->heap()->ToBoolean(table->Contains(key));
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetDelete) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
+ bool was_present = false;
+ table = OrderedHashSet::Remove(table, key, &was_present);
+ holder->set_table(*table);
+ return isolate->heap()->ToBoolean(was_present);
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetClear) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
+ table = OrderedHashSet::Clear(table);
+ holder->set_table(*table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetGetSize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
+ return Smi::FromInt(table->NumberOfElements());
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, set, 1);
+ CONVERT_SMI_ARG_CHECKED(kind, 2)
+ RUNTIME_ASSERT(kind == JSSetIterator::kKindValues ||
+ kind == JSSetIterator::kKindEntries);
+ Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
+ holder->set_table(*table);
+ holder->set_index(Smi::FromInt(0));
+ holder->set_kind(Smi::FromInt(kind));
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSSetIterator, holder, 0);
+ CONVERT_ARG_CHECKED(JSArray, value_array, 1);
+ return holder->Next(value_array);
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
+ holder->set_table(*table);
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapGet) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ Handle<Object> lookup(table->Lookup(key), isolate);
+ return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapHas) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ Handle<Object> lookup(table->Lookup(key), isolate);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapDelete) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ bool was_present = false;
+ Handle<OrderedHashMap> new_table =
+ OrderedHashMap::Remove(table, key, &was_present);
+ holder->set_table(*new_table);
+ return isolate->heap()->ToBoolean(was_present);
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapClear) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ table = OrderedHashMap::Clear(table);
+ holder->set_table(*table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapSet) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ Handle<OrderedHashMap> new_table = OrderedHashMap::Put(table, key, value);
+ holder->set_table(*new_table);
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapGetSize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
+ return Smi::FromInt(table->NumberOfElements());
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, map, 1);
+ CONVERT_SMI_ARG_CHECKED(kind, 2)
+ RUNTIME_ASSERT(kind == JSMapIterator::kKindKeys ||
+ kind == JSMapIterator::kKindValues ||
+ kind == JSMapIterator::kKindEntries);
+ Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()));
+ holder->set_table(*table);
+ holder->set_index(Smi::FromInt(0));
+ holder->set_kind(Smi::FromInt(kind));
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<FixedArray> entries =
+ isolate->factory()->NewFixedArray(table->NumberOfElements() * 2);
+ {
+ DisallowHeapAllocation no_gc;
+ int number_of_non_hole_elements = 0;
+ for (int i = 0; i < table->Capacity(); i++) {
+ Handle<Object> key(table->KeyAt(i), isolate);
+ if (table->IsKey(*key)) {
+ entries->set(number_of_non_hole_elements++, *key);
+ Object* value = table->Lookup(key);
+ entries->set(number_of_non_hole_elements++, value);
+ }
+ }
+ DCHECK_EQ(table->NumberOfElements() * 2, number_of_non_hole_elements);
+ }
+ return *isolate->factory()->NewJSArrayWithElements(entries);
+}
+
+
+RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSMapIterator, holder, 0);
+ CONVERT_ARG_CHECKED(JSArray, value_array, 1);
+ return holder->Next(value_array);
+}
+
+
+static Handle<JSWeakCollection> WeakCollectionInitialize(
+ Isolate* isolate, Handle<JSWeakCollection> weak_collection) {
+ DCHECK(weak_collection->map()->inobject_properties() == 0);
+ Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0);
+ weak_collection->set_table(*table);
+ return weak_collection;
+}
+
+
+RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ return *WeakCollectionInitialize(isolate, weak_collection);
+}
+
+
+RUNTIME_FUNCTION(Runtime_WeakCollectionGet) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ RUNTIME_ASSERT(table->IsKey(*key));
+ Handle<Object> lookup(table->Lookup(key), isolate);
+ return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
+}
+
+
+RUNTIME_FUNCTION(Runtime_WeakCollectionHas) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ RUNTIME_ASSERT(table->IsKey(*key));
+ Handle<Object> lookup(table->Lookup(key), isolate);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ RUNTIME_ASSERT(table->IsKey(*key));
+ bool was_present = false;
+ Handle<ObjectHashTable> new_table =
+ ObjectHashTable::Remove(table, key, &was_present);
+ weak_collection->set_table(*new_table);
+ return isolate->heap()->ToBoolean(was_present);
+}
+
+
+RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ RUNTIME_ASSERT(table->IsKey(*key));
+ Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
+ weak_collection->set_table(*new_table);
+ return *weak_collection;
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetWeakSetValues) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<FixedArray> values =
+ isolate->factory()->NewFixedArray(table->NumberOfElements());
+ {
+ DisallowHeapAllocation no_gc;
+ int number_of_non_hole_elements = 0;
+ for (int i = 0; i < table->Capacity(); i++) {
+ Handle<Object> key(table->KeyAt(i), isolate);
+ if (table->IsKey(*key)) {
+ values->set(number_of_non_hole_elements++, *key);
+ }
+ }
+ DCHECK_EQ(table->NumberOfElements(), number_of_non_hole_elements);
+ }
+ return *isolate->factory()->NewJSArrayWithElements(values);
+}
+
+
+RUNTIME_FUNCTION(Runtime_ObservationWeakMapCreate) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+ // TODO(adamk): Currently this runtime function is only called three times per
+ // isolate. If it's called more often, the map should be moved into the
+ // strong root list.
+ Handle<Map> map =
+ isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
+ Handle<JSWeakMap> weakmap =
+ Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
+ return *WeakCollectionInitialize(isolate, weakmap);
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-compiler.cc b/src/runtime/runtime-compiler.cc
new file mode 100644
index 0000000..3f7e936
--- /dev/null
+++ b/src/runtime/runtime-compiler.cc
@@ -0,0 +1,441 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/compiler.h"
+#include "src/deoptimizer.h"
+#include "src/frames.h"
+#include "src/full-codegen.h"
+#include "src/isolate.h"
+#include "src/isolate-inl.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+#include "src/v8threads.h"
+#include "src/vm-state.h"
+#include "src/vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_CompileLazy) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+#ifdef DEBUG
+ if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
+ PrintF("[unoptimized: ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+#endif
+
+ // Compile the target function.
+ DCHECK(function->shared()->allows_lazy_compilation());
+
+ Handle<Code> code;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, code,
+ Compiler::GetLazyCode(function));
+ DCHECK(code->kind() == Code::FUNCTION ||
+ code->kind() == Code::OPTIMIZED_FUNCTION);
+ function->ReplaceCode(*code);
+ return *code;
+}
+
+
+RUNTIME_FUNCTION(Runtime_CompileOptimized) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(concurrent, 1);
+
+ Handle<Code> unoptimized(function->shared()->code());
+ if (!isolate->use_crankshaft() ||
+ function->shared()->optimization_disabled() ||
+ isolate->DebuggerHasBreakPoints()) {
+ // If the function is not optimizable or debugger is active continue
+ // using the code from the full compiler.
+ if (FLAG_trace_opt) {
+ PrintF("[failed to optimize ");
+ function->PrintName();
+ PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
+ function->shared()->optimization_disabled() ? "F" : "T",
+ isolate->DebuggerHasBreakPoints() ? "T" : "F");
+ }
+ function->ReplaceCode(*unoptimized);
+ return function->code();
+ }
+
+ Compiler::ConcurrencyMode mode =
+ concurrent ? Compiler::CONCURRENT : Compiler::NOT_CONCURRENT;
+ Handle<Code> code;
+ if (Compiler::GetOptimizedCode(function, unoptimized, mode).ToHandle(&code)) {
+ function->ReplaceCode(*code);
+ } else {
+ function->ReplaceCode(function->shared()->code());
+ }
+
+ DCHECK(function->code()->kind() == Code::FUNCTION ||
+ function->code()->kind() == Code::OPTIMIZED_FUNCTION ||
+ function->IsInOptimizationQueue());
+ return function->code();
+}
+
+
+RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+ Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
+ DCHECK(AllowHeapAllocation::IsAllowed());
+ delete deoptimizer;
+ return isolate->heap()->undefined_value();
+}
+
+
+class ActivationsFinder : public ThreadVisitor {
+ public:
+ Code* code_;
+ bool has_code_activations_;
+
+ explicit ActivationsFinder(Code* code)
+ : code_(code), has_code_activations_(false) {}
+
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ JavaScriptFrameIterator it(isolate, top);
+ VisitFrames(&it);
+ }
+
+ void VisitFrames(JavaScriptFrameIterator* it) {
+ for (; !it->done(); it->Advance()) {
+ JavaScriptFrame* frame = it->frame();
+ if (code_->contains(frame->pc())) has_code_activations_ = true;
+ }
+ }
+};
+
+
+RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_SMI_ARG_CHECKED(type_arg, 0);
+ Deoptimizer::BailoutType type =
+ static_cast<Deoptimizer::BailoutType>(type_arg);
+ Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
+ DCHECK(AllowHeapAllocation::IsAllowed());
+
+ Handle<JSFunction> function = deoptimizer->function();
+ Handle<Code> optimized_code = deoptimizer->compiled_code();
+
+ DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
+ DCHECK(type == deoptimizer->bailout_type());
+
+ // Make sure to materialize objects before causing any allocation.
+ JavaScriptFrameIterator it(isolate);
+ deoptimizer->MaterializeHeapObjects(&it);
+ delete deoptimizer;
+
+ JavaScriptFrame* frame = it.frame();
+ RUNTIME_ASSERT(frame->function()->IsJSFunction());
+ DCHECK(frame->function() == *function);
+
+ // Avoid doing too much work when running with --always-opt and keep
+ // the optimized code around.
+ if (FLAG_always_opt || type == Deoptimizer::LAZY) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Search for other activations of the same function and code.
+ ActivationsFinder activations_finder(*optimized_code);
+ activations_finder.VisitFrames(&it);
+ isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
+
+ if (!activations_finder.has_code_activations_) {
+ if (function->code() == *optimized_code) {
+ if (FLAG_trace_deopt) {
+ PrintF("[removing optimized code for: ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+ function->ReplaceCode(function->shared()->code());
+ // Evict optimized code for this function from the cache so that it
+ // doesn't get used for new closures.
+ function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
+ "notify deoptimized");
+ }
+ } else {
+ // TODO(titzer): we should probably do DeoptimizeCodeList(code)
+ // unconditionally if the code is not already marked for deoptimization.
+ // If there is an index by shared function info, all the better.
+ Deoptimizer::DeoptimizeFunction(*function);
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+static bool IsSuitableForOnStackReplacement(Isolate* isolate,
+ Handle<JSFunction> function,
+ Handle<Code> current_code) {
+ // Keep track of whether we've succeeded in optimizing.
+ if (!isolate->use_crankshaft() || !current_code->optimizable()) return false;
+ // If we are trying to do OSR when there are already optimized
+ // activations of the function, it means (a) the function is directly or
+ // indirectly recursive and (b) an optimized invocation has been
+ // deoptimized so that we are currently in an unoptimized activation.
+ // Check for optimized activations of this function.
+ for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->is_optimized() && frame->function() == *function) return false;
+ }
+
+ return true;
+}
+
+
+RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ Handle<Code> caller_code(function->shared()->code());
+
+ // We're not prepared to handle a function with arguments object.
+ DCHECK(!function->shared()->uses_arguments());
+
+ RUNTIME_ASSERT(FLAG_use_osr);
+
+ // Passing the PC in the javascript frame from the caller directly is
+ // not GC safe, so we walk the stack to get it.
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ if (!caller_code->contains(frame->pc())) {
+ // Code on the stack may not be the code object referenced by the shared
+ // function info. It may have been replaced to include deoptimization data.
+ caller_code = Handle<Code>(frame->LookupCode());
+ }
+
+ uint32_t pc_offset =
+ static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
+
+#ifdef DEBUG
+ DCHECK_EQ(frame->function(), *function);
+ DCHECK_EQ(frame->LookupCode(), *caller_code);
+ DCHECK(caller_code->contains(frame->pc()));
+#endif // DEBUG
+
+
+ BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset);
+ DCHECK(!ast_id.IsNone());
+
+ Compiler::ConcurrencyMode mode =
+ isolate->concurrent_osr_enabled() &&
+ (function->shared()->ast_node_count() > 512)
+ ? Compiler::CONCURRENT
+ : Compiler::NOT_CONCURRENT;
+ Handle<Code> result = Handle<Code>::null();
+
+ OptimizedCompileJob* job = NULL;
+ if (mode == Compiler::CONCURRENT) {
+ // Gate the OSR entry with a stack check.
+ BackEdgeTable::AddStackCheck(caller_code, pc_offset);
+ // Poll already queued compilation jobs.
+ OptimizingCompilerThread* thread = isolate->optimizing_compiler_thread();
+ if (thread->IsQueuedForOSR(function, ast_id)) {
+ if (FLAG_trace_osr) {
+ PrintF("[OSR - Still waiting for queued: ");
+ function->PrintName();
+ PrintF(" at AST id %d]\n", ast_id.ToInt());
+ }
+ return NULL;
+ }
+
+ job = thread->FindReadyOSRCandidate(function, ast_id);
+ }
+
+ if (job != NULL) {
+ if (FLAG_trace_osr) {
+ PrintF("[OSR - Found ready: ");
+ function->PrintName();
+ PrintF(" at AST id %d]\n", ast_id.ToInt());
+ }
+ result = Compiler::GetConcurrentlyOptimizedCode(job);
+ } else if (IsSuitableForOnStackReplacement(isolate, function, caller_code)) {
+ if (FLAG_trace_osr) {
+ PrintF("[OSR - Compiling: ");
+ function->PrintName();
+ PrintF(" at AST id %d]\n", ast_id.ToInt());
+ }
+ MaybeHandle<Code> maybe_result =
+ Compiler::GetOptimizedCode(function, caller_code, mode, ast_id);
+ if (maybe_result.ToHandle(&result) &&
+ result.is_identical_to(isolate->builtins()->InOptimizationQueue())) {
+ // Optimization is queued. Return to check later.
+ return NULL;
+ }
+ }
+
+ // Revert the patched back edge table, regardless of whether OSR succeeds.
+ BackEdgeTable::Revert(isolate, *caller_code);
+
+ // Check whether we ended up with usable optimized code.
+ if (!result.is_null() && result->kind() == Code::OPTIMIZED_FUNCTION) {
+ DeoptimizationInputData* data =
+ DeoptimizationInputData::cast(result->deoptimization_data());
+
+ if (data->OsrPcOffset()->value() >= 0) {
+ DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
+ if (FLAG_trace_osr) {
+ PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
+ ast_id.ToInt(), data->OsrPcOffset()->value());
+ }
+ // TODO(titzer): this is a massive hack to make the deopt counts
+ // match. Fix heuristics for reenabling optimizations!
+ function->shared()->increment_deopt_count();
+
+ // TODO(titzer): Do not install code into the function.
+ function->ReplaceCode(*result);
+ return *result;
+ }
+ }
+
+ // Failed.
+ if (FLAG_trace_osr) {
+ PrintF("[OSR - Failed: ");
+ function->PrintName();
+ PrintF(" at AST id %d]\n", ast_id.ToInt());
+ }
+
+ if (!function->IsOptimized()) {
+ function->ReplaceCode(function->shared()->code());
+ }
+ return NULL;
+}
+
+
+RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+
+ // First check if this is a real stack overflow.
+ StackLimitCheck check(isolate);
+ if (check.JsHasOverflowed()) {
+ SealHandleScope shs(isolate);
+ return isolate->StackOverflow();
+ }
+
+ isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
+ return (function->IsOptimized()) ? function->code()
+ : function->shared()->code();
+}
+
+
+bool CodeGenerationFromStringsAllowed(Isolate* isolate,
+ Handle<Context> context) {
+ DCHECK(context->allow_code_gen_from_strings()->IsFalse());
+ // Check with callback if set.
+ AllowCodeGenerationFromStringsCallback callback =
+ isolate->allow_code_gen_callback();
+ if (callback == NULL) {
+ // No callback set and code generation disallowed.
+ return false;
+ } else {
+ // Callback set. Let it decide if code generation is allowed.
+ VMState<EXTERNAL> state(isolate);
+ return callback(v8::Utils::ToLocal(context));
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_CompileString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
+
+ // Extract native context.
+ Handle<Context> context(isolate->native_context());
+
+ // Check if native context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (context->allow_code_gen_from_strings()->IsFalse() &&
+ !CodeGenerationFromStringsAllowed(isolate, context)) {
+ Handle<Object> error_message =
+ context->ErrorMessageForCodeGenerationFromStrings();
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewEvalError("code_gen_from_strings",
+ HandleVector<Object>(&error_message, 1)));
+ }
+
+ // Compile source string in the native context.
+ ParseRestriction restriction = function_literal_only
+ ? ONLY_SINGLE_FUNCTION_LITERAL
+ : NO_PARSE_RESTRICTION;
+ Handle<JSFunction> fun;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, fun,
+ Compiler::GetFunctionFromEval(source, context, SLOPPY, restriction,
+ RelocInfo::kNoPosition));
+ return *fun;
+}
+
+
+static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
+ Handle<Object> receiver,
+ StrictMode strict_mode,
+ int scope_position) {
+ Handle<Context> context = Handle<Context>(isolate->context());
+ Handle<Context> native_context = Handle<Context>(context->native_context());
+
+ // Check if native context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (native_context->allow_code_gen_from_strings()->IsFalse() &&
+ !CodeGenerationFromStringsAllowed(isolate, native_context)) {
+ Handle<Object> error_message =
+ native_context->ErrorMessageForCodeGenerationFromStrings();
+ Handle<Object> error;
+ MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
+ "code_gen_from_strings", HandleVector<Object>(&error_message, 1));
+ if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
+ return MakePair(isolate->heap()->exception(), NULL);
+ }
+
+ // Deal with a normal eval call with a string argument. Compile it
+ // and return the compiled function bound in the local context.
+ static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
+ Handle<JSFunction> compiled;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate, compiled,
+ Compiler::GetFunctionFromEval(source, context, strict_mode, restriction,
+ scope_position),
+ MakePair(isolate->heap()->exception(), NULL));
+ return MakePair(*compiled, *receiver);
+}
+
+
+RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 5);
+
+ Handle<Object> callee = args.at<Object>(0);
+
+ // If "eval" didn't refer to the original GlobalEval, it's not a
+ // direct call to eval.
+ // (And even if it is, but the first argument isn't a string, just let
+ // execution default to an indirect call to eval, which will also return
+ // the first argument without doing anything).
+ if (*callee != isolate->native_context()->global_eval_fun() ||
+ !args[1]->IsString()) {
+ return MakePair(*callee, isolate->heap()->undefined_value());
+ }
+
+ DCHECK(args[3]->IsSmi());
+ DCHECK(args.smi_at(3) == SLOPPY || args.smi_at(3) == STRICT);
+ StrictMode strict_mode = static_cast<StrictMode>(args.smi_at(3));
+ DCHECK(args[4]->IsSmi());
+ return CompileGlobalEval(isolate, args.at<String>(1), args.at<Object>(2),
+ strict_mode, args.smi_at(4));
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-json.cc b/src/runtime/runtime-json.cc
new file mode 100644
index 0000000..7a89c51
--- /dev/null
+++ b/src/runtime/runtime-json.cc
@@ -0,0 +1,54 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/json-parser.h"
+#include "src/json-stringifier.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_QuoteJSONString) {
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
+ DCHECK(args.length() == 1);
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, BasicJsonStringifier::StringifyString(isolate, string));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_BasicJSONStringify) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
+ BasicJsonStringifier stringifier(isolate);
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
+ stringifier.Stringify(object));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_ParseJson) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+
+ source = String::Flatten(source);
+ // Optimized fast case where we only have Latin1 characters.
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
+ source->IsSeqOneByteString()
+ ? JsonParser<true>::Parse(source)
+ : JsonParser<false>::Parse(source));
+ return *result;
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-maths.cc b/src/runtime/runtime-maths.cc
new file mode 100644
index 0000000..16acb39
--- /dev/null
+++ b/src/runtime/runtime-maths.cc
@@ -0,0 +1,247 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/assembler.h"
+#include "src/codegen.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+#include "third_party/fdlibm/fdlibm.h"
+
+
+namespace v8 {
+namespace internal {
+
+#define RUNTIME_UNARY_MATH(Name, name) \
+ RUNTIME_FUNCTION(Runtime_Math##Name) { \
+ HandleScope scope(isolate); \
+ DCHECK(args.length() == 1); \
+ isolate->counters()->math_##name()->Increment(); \
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0); \
+ return *isolate->factory()->NewHeapNumber(std::name(x)); \
+ }
+
+RUNTIME_UNARY_MATH(Acos, acos)
+RUNTIME_UNARY_MATH(Asin, asin)
+RUNTIME_UNARY_MATH(Atan, atan)
+RUNTIME_UNARY_MATH(LogRT, log)
+#undef RUNTIME_UNARY_MATH
+
+
+RUNTIME_FUNCTION(Runtime_DoubleHi) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ uint64_t integer = double_to_uint64(x);
+ integer = (integer >> 32) & 0xFFFFFFFFu;
+ return *isolate->factory()->NewNumber(static_cast<int32_t>(integer));
+}
+
+
+RUNTIME_FUNCTION(Runtime_DoubleLo) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return *isolate->factory()->NewNumber(
+ static_cast<int32_t>(double_to_uint64(x) & 0xFFFFFFFFu));
+}
+
+
+RUNTIME_FUNCTION(Runtime_ConstructDouble) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_NUMBER_CHECKED(uint32_t, hi, Uint32, args[0]);
+ CONVERT_NUMBER_CHECKED(uint32_t, lo, Uint32, args[1]);
+ uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo;
+ return *isolate->factory()->NewNumber(uint64_to_double(result));
+}
+
+
+RUNTIME_FUNCTION(Runtime_RemPiO2) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ Factory* factory = isolate->factory();
+ double y[2];
+ int n = fdlibm::rempio2(x, y);
+ Handle<FixedArray> array = factory->NewFixedArray(3);
+ Handle<HeapNumber> y0 = factory->NewHeapNumber(y[0]);
+ Handle<HeapNumber> y1 = factory->NewHeapNumber(y[1]);
+ array->set(0, Smi::FromInt(n));
+ array->set(1, *y0);
+ array->set(2, *y1);
+ return *factory->NewJSArrayWithElements(array);
+}
+
+
+static const double kPiDividedBy4 = 0.78539816339744830962;
+
+
+RUNTIME_FUNCTION(Runtime_MathAtan2) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ isolate->counters()->math_atan2()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ double result;
+ if (std::isinf(x) && std::isinf(y)) {
+ // Make sure that the result in case of two infinite arguments
+ // is a multiple of Pi / 4. The sign of the result is determined
+ // by the first argument (x) and the sign of the second argument
+ // determines the multiplier: one or three.
+ int multiplier = (x < 0) ? -1 : 1;
+ if (y < 0) multiplier *= 3;
+ result = multiplier * kPiDividedBy4;
+ } else {
+ result = std::atan2(x, y);
+ }
+ return *isolate->factory()->NewNumber(result);
+}
+
+
+RUNTIME_FUNCTION(Runtime_MathExpRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ isolate->counters()->math_exp()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ lazily_initialize_fast_exp();
+ return *isolate->factory()->NewNumber(fast_exp(x));
+}
+
+
+RUNTIME_FUNCTION(Runtime_MathFloorRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ isolate->counters()->math_floor()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return *isolate->factory()->NewNumber(Floor(x));
+}
+
+
+// Slow version of Math.pow. We check for fast paths for special cases.
+// Used if VFP3 is not available.
+RUNTIME_FUNCTION(Runtime_MathPowSlow) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ isolate->counters()->math_pow()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+
+ // If the second argument is a smi, it is much faster to call the
+ // custom powi() function than the generic pow().
+ if (args[1]->IsSmi()) {
+ int y = args.smi_at(1);
+ return *isolate->factory()->NewNumber(power_double_int(x, y));
+ }
+
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ double result = power_helper(x, y);
+ if (std::isnan(result)) return isolate->heap()->nan_value();
+ return *isolate->factory()->NewNumber(result);
+}
+
+
+// Fast version of Math.pow if we know that y is not an integer and y is not
+// -0.5 or 0.5. Used as slow case from full codegen.
+RUNTIME_FUNCTION(Runtime_MathPowRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ isolate->counters()->math_pow()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ if (y == 0) {
+ return Smi::FromInt(1);
+ } else {
+ double result = power_double_double(x, y);
+ if (std::isnan(result)) return isolate->heap()->nan_value();
+ return *isolate->factory()->NewNumber(result);
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_RoundNumber) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(input, 0);
+ isolate->counters()->math_round()->Increment();
+
+ if (!input->IsHeapNumber()) {
+ DCHECK(input->IsSmi());
+ return *input;
+ }
+
+ Handle<HeapNumber> number = Handle<HeapNumber>::cast(input);
+
+ double value = number->value();
+ int exponent = number->get_exponent();
+ int sign = number->get_sign();
+
+ if (exponent < -1) {
+ // Number in range ]-0.5..0.5[. These always round to +/-zero.
+ if (sign) return isolate->heap()->minus_zero_value();
+ return Smi::FromInt(0);
+ }
+
+ // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
+ // should be rounded to 2^30, which is not smi (for 31-bit smis, similar
+ // argument holds for 32-bit smis).
+ if (!sign && exponent < kSmiValueSize - 2) {
+ return Smi::FromInt(static_cast<int>(value + 0.5));
+ }
+
+ // If the magnitude is big enough, there's no place for fraction part. If we
+ // try to add 0.5 to this number, 1.0 will be added instead.
+ if (exponent >= 52) {
+ return *number;
+ }
+
+ if (sign && value >= -0.5) return isolate->heap()->minus_zero_value();
+
+ // Do not call NumberFromDouble() to avoid extra checks.
+ return *isolate->factory()->NewNumber(Floor(value + 0.5));
+}
+
+
+RUNTIME_FUNCTION(Runtime_MathSqrtRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ isolate->counters()->math_sqrt()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return *isolate->factory()->NewNumber(fast_sqrt(x));
+}
+
+
+RUNTIME_FUNCTION(Runtime_MathFround) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ float xf = DoubleToFloat32(x);
+ return *isolate->factory()->NewNumber(xf);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_MathPow) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_MathPowSlow(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_IsMinusZero) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(Object, obj, 0);
+ if (!obj->IsHeapNumber()) return isolate->heap()->false_value();
+ HeapNumber* number = HeapNumber::cast(obj);
+ return isolate->heap()->ToBoolean(IsMinusZero(number->value()));
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-numbers.cc b/src/runtime/runtime-numbers.cc
new file mode 100644
index 0000000..3286aa6
--- /dev/null
+++ b/src/runtime/runtime-numbers.cc
@@ -0,0 +1,565 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/codegen.h"
+#include "src/misc-intrinsics.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+
+#ifndef _STLP_VENDOR_CSTD
+// STLPort doesn't import fpclassify and isless into the std namespace.
+using std::fpclassify;
+using std::isless;
+#endif
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_NumberToRadixString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_SMI_ARG_CHECKED(radix, 1);
+ RUNTIME_ASSERT(2 <= radix && radix <= 36);
+
+ // Fast case where the result is a one character string.
+ if (args[0]->IsSmi()) {
+ int value = args.smi_at(0);
+ if (value >= 0 && value < radix) {
+ // Character array used for conversion.
+ static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ return *isolate->factory()->LookupSingleCharacterStringFromCode(
+ kCharTable[value]);
+ }
+ }
+
+ // Slow case.
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ if (std::isnan(value)) {
+ return isolate->heap()->nan_string();
+ }
+ if (std::isinf(value)) {
+ if (value < 0) {
+ return isolate->heap()->minus_infinity_string();
+ }
+ return isolate->heap()->infinity_string();
+ }
+ char* str = DoubleToRadixCString(value, radix);
+ Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
+ DeleteArray(str);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToFixed) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ // See DoubleToFixedCString for these constants:
+ RUNTIME_ASSERT(f >= 0 && f <= 20);
+ RUNTIME_ASSERT(!Double(value).IsSpecial());
+ char* str = DoubleToFixedCString(value, f);
+ Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
+ DeleteArray(str);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToExponential) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ RUNTIME_ASSERT(f >= -1 && f <= 20);
+ RUNTIME_ASSERT(!Double(value).IsSpecial());
+ char* str = DoubleToExponentialCString(value, f);
+ Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
+ DeleteArray(str);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToPrecision) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ RUNTIME_ASSERT(f >= 1 && f <= 21);
+ RUNTIME_ASSERT(!Double(value).IsSpecial());
+ char* str = DoubleToPrecisionCString(value, f);
+ Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
+ DeleteArray(str);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_IsValidSmi) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_NUMBER_CHECKED(int32_t, number, Int32, args[0]);
+ return isolate->heap()->ToBoolean(Smi::IsValid(number));
+}
+
+
+static bool AreDigits(const uint8_t* s, int from, int to) {
+ for (int i = from; i < to; i++) {
+ if (s[i] < '0' || s[i] > '9') return false;
+ }
+
+ return true;
+}
+
+
+static int ParseDecimalInteger(const uint8_t* s, int from, int to) {
+ DCHECK(to - from < 10); // Overflow is not possible.
+ DCHECK(from < to);
+ int d = s[from] - '0';
+
+ for (int i = from + 1; i < to; i++) {
+ d = 10 * d + (s[i] - '0');
+ }
+
+ return d;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringToNumber) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ subject = String::Flatten(subject);
+
+ // Fast case: short integer or some sorts of junk values.
+ if (subject->IsSeqOneByteString()) {
+ int len = subject->length();
+ if (len == 0) return Smi::FromInt(0);
+
+ DisallowHeapAllocation no_gc;
+ uint8_t const* data = Handle<SeqOneByteString>::cast(subject)->GetChars();
+ bool minus = (data[0] == '-');
+ int start_pos = (minus ? 1 : 0);
+
+ if (start_pos == len) {
+ return isolate->heap()->nan_value();
+ } else if (data[start_pos] > '9') {
+ // Fast check for a junk value. A valid string may start from a
+ // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit
+ // or the 'I' character ('Infinity'). All of that have codes not greater
+ // than '9' except 'I' and .
+ if (data[start_pos] != 'I' && data[start_pos] != 0xa0) {
+ return isolate->heap()->nan_value();
+ }
+ } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
+ // The maximal/minimal smi has 10 digits. If the string has less digits
+ // we know it will fit into the smi-data type.
+ int d = ParseDecimalInteger(data, start_pos, len);
+ if (minus) {
+ if (d == 0) return isolate->heap()->minus_zero_value();
+ d = -d;
+ } else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize &&
+ (len == 1 || data[0] != '0')) {
+ // String hash is not calculated yet but all the data are present.
+ // Update the hash field to speed up sequential convertions.
+ uint32_t hash = StringHasher::MakeArrayIndexHash(d, len);
+#ifdef DEBUG
+ subject->Hash(); // Force hash calculation.
+ DCHECK_EQ(static_cast<int>(subject->hash_field()),
+ static_cast<int>(hash));
+#endif
+ subject->set_hash_field(hash);
+ }
+ return Smi::FromInt(d);
+ }
+ }
+
+ // Slower case.
+ int flags = ALLOW_HEX;
+ if (FLAG_harmony_numeric_literals) {
+ // The current spec draft has not updated "ToNumber Applied to the String
+ // Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584
+ flags |= ALLOW_OCTAL | ALLOW_BINARY;
+ }
+
+ return *isolate->factory()->NewNumber(
+ StringToDouble(isolate->unicode_cache(), *subject, flags));
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringParseInt) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]);
+ RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
+
+ subject = String::Flatten(subject);
+ double value;
+
+ {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent flat = subject->GetFlatContent();
+
+ // ECMA-262 section 15.1.2.3, empty string is NaN
+ if (flat.IsOneByte()) {
+ value =
+ StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix);
+ } else {
+ value = StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix);
+ }
+ }
+
+ return *isolate->factory()->NewNumber(value);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringParseFloat) {
+ HandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+
+ subject = String::Flatten(subject);
+ double value = StringToDouble(isolate->unicode_cache(), *subject,
+ ALLOW_TRAILING_JUNK, base::OS::nan_value());
+
+ return *isolate->factory()->NewNumber(value);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToStringRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
+
+ return *isolate->factory()->NumberToString(number);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
+
+ return *isolate->factory()->NumberToString(number, false);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToInteger) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+ return *isolate->factory()->NewNumber(DoubleToInteger(number));
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToIntegerMapMinusZero) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+ double double_value = DoubleToInteger(number);
+ // Map both -0 and +0 to +0.
+ if (double_value == 0) double_value = 0;
+
+ return *isolate->factory()->NewNumber(double_value);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToJSUint32) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
+ return *isolate->factory()->NewNumberFromUint(number);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberToJSInt32) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+ return *isolate->factory()->NewNumberFromInt(DoubleToInt32(number));
+}
+
+
+// Converts a Number to a Smi, if possible. Returns NaN if the number is not
+// a small integer.
+RUNTIME_FUNCTION(Runtime_NumberToSmi) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(Object, obj, 0);
+ if (obj->IsSmi()) {
+ return obj;
+ }
+ if (obj->IsHeapNumber()) {
+ double value = HeapNumber::cast(obj)->value();
+ int int_value = FastD2I(value);
+ if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
+ return Smi::FromInt(int_value);
+ }
+ }
+ return isolate->heap()->nan_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberAdd) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return *isolate->factory()->NewNumber(x + y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberSub) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return *isolate->factory()->NewNumber(x - y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberMul) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return *isolate->factory()->NewNumber(x * y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberUnaryMinus) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return *isolate->factory()->NewNumber(-x);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberDiv) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return *isolate->factory()->NewNumber(x / y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberMod) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return *isolate->factory()->NewNumber(modulo(x, y));
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberImul) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ // We rely on implementation-defined behavior below, but at least not on
+ // undefined behavior.
+ CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]);
+ int32_t product = static_cast<int32_t>(x * y);
+ return *isolate->factory()->NewNumberFromInt(product);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberOr) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromInt(x | y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberAnd) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromInt(x & y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberXor) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromInt(x ^ y);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberShl) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromInt(x << (y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberShr) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromUint(x >> (y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberSar) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return *isolate->factory()->NewNumberFromInt(
+ ArithmeticShiftRight(x, y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberEquals) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ if (std::isnan(x)) return Smi::FromInt(NOT_EQUAL);
+ if (std::isnan(y)) return Smi::FromInt(NOT_EQUAL);
+ if (x == y) return Smi::FromInt(EQUAL);
+ Object* result;
+ if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
+ result = Smi::FromInt(EQUAL);
+ } else {
+ result = Smi::FromInt(NOT_EQUAL);
+ }
+ return result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NumberCompare) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, uncomparable_result, 2)
+ if (std::isnan(x) || std::isnan(y)) return *uncomparable_result;
+ if (x == y) return Smi::FromInt(EQUAL);
+ if (isless(x, y)) return Smi::FromInt(LESS);
+ return Smi::FromInt(GREATER);
+}
+
+
+// Compare two Smis as if they were converted to strings and then
+// compared lexicographically.
+RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_SMI_ARG_CHECKED(x_value, 0);
+ CONVERT_SMI_ARG_CHECKED(y_value, 1);
+
+ // If the integers are equal so are the string representations.
+ if (x_value == y_value) return Smi::FromInt(EQUAL);
+
+ // If one of the integers is zero the normal integer order is the
+ // same as the lexicographic order of the string representations.
+ if (x_value == 0 || y_value == 0)
+ return Smi::FromInt(x_value < y_value ? LESS : GREATER);
+
+ // If only one of the integers is negative the negative number is
+ // smallest because the char code of '-' is less than the char code
+ // of any digit. Otherwise, we make both values positive.
+
+ // Use unsigned values otherwise the logic is incorrect for -MIN_INT on
+ // architectures using 32-bit Smis.
+ uint32_t x_scaled = x_value;
+ uint32_t y_scaled = y_value;
+ if (x_value < 0 || y_value < 0) {
+ if (y_value >= 0) return Smi::FromInt(LESS);
+ if (x_value >= 0) return Smi::FromInt(GREATER);
+ x_scaled = -x_value;
+ y_scaled = -y_value;
+ }
+
+ static const uint32_t kPowersOf10[] = {
+ 1, 10, 100, 1000,
+ 10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000,
+ 100 * 1000 * 1000, 1000 * 1000 * 1000};
+
+ // If the integers have the same number of decimal digits they can be
+ // compared directly as the numeric order is the same as the
+ // lexicographic order. If one integer has fewer digits, it is scaled
+ // by some power of 10 to have the same number of digits as the longer
+ // integer. If the scaled integers are equal it means the shorter
+ // integer comes first in the lexicographic order.
+
+ // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+ int x_log2 = IntegerLog2(x_scaled);
+ int x_log10 = ((x_log2 + 1) * 1233) >> 12;
+ x_log10 -= x_scaled < kPowersOf10[x_log10];
+
+ int y_log2 = IntegerLog2(y_scaled);
+ int y_log10 = ((y_log2 + 1) * 1233) >> 12;
+ y_log10 -= y_scaled < kPowersOf10[y_log10];
+
+ int tie = EQUAL;
+
+ if (x_log10 < y_log10) {
+ // X has fewer digits. We would like to simply scale up X but that
+ // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
+ // be scaled up to 9_000_000_000. So we scale up by the next
+ // smallest power and scale down Y to drop one digit. It is OK to
+ // drop one digit from the longer integer since the final digit is
+ // past the length of the shorter integer.
+ x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
+ y_scaled /= 10;
+ tie = LESS;
+ } else if (y_log10 < x_log10) {
+ y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
+ x_scaled /= 10;
+ tie = GREATER;
+ }
+
+ if (x_scaled < y_scaled) return Smi::FromInt(LESS);
+ if (x_scaled > y_scaled) return Smi::FromInt(GREATER);
+ return Smi::FromInt(tie);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_NumberToString) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_NumberToStringRT(args, isolate);
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-regexp.cc b/src/runtime/runtime-regexp.cc
new file mode 100644
index 0000000..e96d501
--- /dev/null
+++ b/src/runtime/runtime-regexp.cc
@@ -0,0 +1,1131 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/jsregexp-inl.h"
+#include "src/jsregexp.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+#include "src/runtime/string-builder.h"
+#include "src/string-search.h"
+
+namespace v8 {
+namespace internal {
+
+class CompiledReplacement {
+ public:
+ explicit CompiledReplacement(Zone* zone)
+ : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {}
+
+ // Return whether the replacement is simple.
+ bool Compile(Handle<String> replacement, int capture_count,
+ int subject_length);
+
+ // Use Apply only if Compile returned false.
+ void Apply(ReplacementStringBuilder* builder, int match_from, int match_to,
+ int32_t* match);
+
+ // Number of distinct parts of the replacement pattern.
+ int parts() { return parts_.length(); }
+
+ Zone* zone() const { return zone_; }
+
+ private:
+ enum PartType {
+ SUBJECT_PREFIX = 1,
+ SUBJECT_SUFFIX,
+ SUBJECT_CAPTURE,
+ REPLACEMENT_SUBSTRING,
+ REPLACEMENT_STRING,
+ NUMBER_OF_PART_TYPES
+ };
+
+ struct ReplacementPart {
+ static inline ReplacementPart SubjectMatch() {
+ return ReplacementPart(SUBJECT_CAPTURE, 0);
+ }
+ static inline ReplacementPart SubjectCapture(int capture_index) {
+ return ReplacementPart(SUBJECT_CAPTURE, capture_index);
+ }
+ static inline ReplacementPart SubjectPrefix() {
+ return ReplacementPart(SUBJECT_PREFIX, 0);
+ }
+ static inline ReplacementPart SubjectSuffix(int subject_length) {
+ return ReplacementPart(SUBJECT_SUFFIX, subject_length);
+ }
+ static inline ReplacementPart ReplacementString() {
+ return ReplacementPart(REPLACEMENT_STRING, 0);
+ }
+ static inline ReplacementPart ReplacementSubString(int from, int to) {
+ DCHECK(from >= 0);
+ DCHECK(to > from);
+ return ReplacementPart(-from, to);
+ }
+
+ // If tag <= 0 then it is the negation of a start index of a substring of
+ // the replacement pattern, otherwise it's a value from PartType.
+ ReplacementPart(int tag, int data) : tag(tag), data(data) {
+ // Must be non-positive or a PartType value.
+ DCHECK(tag < NUMBER_OF_PART_TYPES);
+ }
+ // Either a value of PartType or a non-positive number that is
+ // the negation of an index into the replacement string.
+ int tag;
+ // The data value's interpretation depends on the value of tag:
+ // tag == SUBJECT_PREFIX ||
+ // tag == SUBJECT_SUFFIX: data is unused.
+ // tag == SUBJECT_CAPTURE: data is the number of the capture.
+ // tag == REPLACEMENT_SUBSTRING ||
+ // tag == REPLACEMENT_STRING: data is index into array of substrings
+ // of the replacement string.
+ // tag <= 0: Temporary representation of the substring of the replacement
+ // string ranging over -tag .. data.
+ // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
+ // substring objects.
+ int data;
+ };
+
+ template <typename Char>
+ bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
+ Vector<Char> characters, int capture_count,
+ int subject_length, Zone* zone) {
+ int length = characters.length();
+ int last = 0;
+ for (int i = 0; i < length; i++) {
+ Char c = characters[i];
+ if (c == '$') {
+ int next_index = i + 1;
+ if (next_index == length) { // No next character!
+ break;
+ }
+ Char c2 = characters[next_index];
+ switch (c2) {
+ case '$':
+ if (i > last) {
+ // There is a substring before. Include the first "$".
+ parts->Add(
+ ReplacementPart::ReplacementSubString(last, next_index),
+ zone);
+ last = next_index + 1; // Continue after the second "$".
+ } else {
+ // Let the next substring start with the second "$".
+ last = next_index;
+ }
+ i = next_index;
+ break;
+ case '`':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectPrefix(), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '\'':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectSuffix(subject_length), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '&':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectMatch(), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ int capture_ref = c2 - '0';
+ if (capture_ref > capture_count) {
+ i = next_index;
+ continue;
+ }
+ int second_digit_index = next_index + 1;
+ if (second_digit_index < length) {
+ // Peek ahead to see if we have two digits.
+ Char c3 = characters[second_digit_index];
+ if ('0' <= c3 && c3 <= '9') { // Double digits.
+ int double_digit_ref = capture_ref * 10 + c3 - '0';
+ if (double_digit_ref <= capture_count) {
+ next_index = second_digit_index;
+ capture_ref = double_digit_ref;
+ }
+ }
+ }
+ if (capture_ref > 0) {
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i),
+ zone);
+ }
+ DCHECK(capture_ref <= capture_count);
+ parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone);
+ last = next_index + 1;
+ }
+ i = next_index;
+ break;
+ }
+ default:
+ i = next_index;
+ break;
+ }
+ }
+ }
+ if (length > last) {
+ if (last == 0) {
+ // Replacement is simple. Do not use Apply to do the replacement.
+ return true;
+ } else {
+ parts->Add(ReplacementPart::ReplacementSubString(last, length), zone);
+ }
+ }
+ return false;
+ }
+
+ ZoneList<ReplacementPart> parts_;
+ ZoneList<Handle<String> > replacement_substrings_;
+ Zone* zone_;
+};
+
+
+bool CompiledReplacement::Compile(Handle<String> replacement, int capture_count,
+ int subject_length) {
+ {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent content = replacement->GetFlatContent();
+ DCHECK(content.IsFlat());
+ bool simple = false;
+ if (content.IsOneByte()) {
+ simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(),
+ capture_count, subject_length, zone());
+ } else {
+ DCHECK(content.IsTwoByte());
+ simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(),
+ capture_count, subject_length, zone());
+ }
+ if (simple) return true;
+ }
+
+ Isolate* isolate = replacement->GetIsolate();
+ // Find substrings of replacement string and create them as String objects.
+ int substring_index = 0;
+ for (int i = 0, n = parts_.length(); i < n; i++) {
+ int tag = parts_[i].tag;
+ if (tag <= 0) { // A replacement string slice.
+ int from = -tag;
+ int to = parts_[i].data;
+ replacement_substrings_.Add(
+ isolate->factory()->NewSubString(replacement, from, to), zone());
+ parts_[i].tag = REPLACEMENT_SUBSTRING;
+ parts_[i].data = substring_index;
+ substring_index++;
+ } else if (tag == REPLACEMENT_STRING) {
+ replacement_substrings_.Add(replacement, zone());
+ parts_[i].data = substring_index;
+ substring_index++;
+ }
+ }
+ return false;
+}
+
+
+void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
+ int match_from, int match_to, int32_t* match) {
+ DCHECK_LT(0, parts_.length());
+ for (int i = 0, n = parts_.length(); i < n; i++) {
+ ReplacementPart part = parts_[i];
+ switch (part.tag) {
+ case SUBJECT_PREFIX:
+ if (match_from > 0) builder->AddSubjectSlice(0, match_from);
+ break;
+ case SUBJECT_SUFFIX: {
+ int subject_length = part.data;
+ if (match_to < subject_length) {
+ builder->AddSubjectSlice(match_to, subject_length);
+ }
+ break;
+ }
+ case SUBJECT_CAPTURE: {
+ int capture = part.data;
+ int from = match[capture * 2];
+ int to = match[capture * 2 + 1];
+ if (from >= 0 && to > from) {
+ builder->AddSubjectSlice(from, to);
+ }
+ break;
+ }
+ case REPLACEMENT_SUBSTRING:
+ case REPLACEMENT_STRING:
+ builder->AddString(replacement_substrings_[part.data]);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+
+void FindOneByteStringIndices(Vector<const uint8_t> subject, char pattern,
+ ZoneList<int>* indices, unsigned int limit,
+ Zone* zone) {
+ DCHECK(limit > 0);
+ // Collect indices of pattern in subject using memchr.
+ // Stop after finding at most limit values.
+ const uint8_t* subject_start = subject.start();
+ const uint8_t* subject_end = subject_start + subject.length();
+ const uint8_t* pos = subject_start;
+ while (limit > 0) {
+ pos = reinterpret_cast<const uint8_t*>(
+ memchr(pos, pattern, subject_end - pos));
+ if (pos == NULL) return;
+ indices->Add(static_cast<int>(pos - subject_start), zone);
+ pos++;
+ limit--;
+ }
+}
+
+
+void FindTwoByteStringIndices(const Vector<const uc16> subject, uc16 pattern,
+ ZoneList<int>* indices, unsigned int limit,
+ Zone* zone) {
+ DCHECK(limit > 0);
+ const uc16* subject_start = subject.start();
+ const uc16* subject_end = subject_start + subject.length();
+ for (const uc16* pos = subject_start; pos < subject_end && limit > 0; pos++) {
+ if (*pos == pattern) {
+ indices->Add(static_cast<int>(pos - subject_start), zone);
+ limit--;
+ }
+ }
+}
+
+
+template <typename SubjectChar, typename PatternChar>
+void FindStringIndices(Isolate* isolate, Vector<const SubjectChar> subject,
+ Vector<const PatternChar> pattern,
+ ZoneList<int>* indices, unsigned int limit, Zone* zone) {
+ DCHECK(limit > 0);
+ // Collect indices of pattern in subject.
+ // Stop after finding at most limit values.
+ int pattern_length = pattern.length();
+ int index = 0;
+ StringSearch<PatternChar, SubjectChar> search(isolate, pattern);
+ while (limit > 0) {
+ index = search.Search(subject, index);
+ if (index < 0) return;
+ indices->Add(index, zone);
+ index += pattern_length;
+ limit--;
+ }
+}
+
+
+void FindStringIndicesDispatch(Isolate* isolate, String* subject,
+ String* pattern, ZoneList<int>* indices,
+ unsigned int limit, Zone* zone) {
+ {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent subject_content = subject->GetFlatContent();
+ String::FlatContent pattern_content = pattern->GetFlatContent();
+ DCHECK(subject_content.IsFlat());
+ DCHECK(pattern_content.IsFlat());
+ if (subject_content.IsOneByte()) {
+ Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector();
+ if (pattern_content.IsOneByte()) {
+ Vector<const uint8_t> pattern_vector =
+ pattern_content.ToOneByteVector();
+ if (pattern_vector.length() == 1) {
+ FindOneByteStringIndices(subject_vector, pattern_vector[0], indices,
+ limit, zone);
+ } else {
+ FindStringIndices(isolate, subject_vector, pattern_vector, indices,
+ limit, zone);
+ }
+ } else {
+ FindStringIndices(isolate, subject_vector,
+ pattern_content.ToUC16Vector(), indices, limit, zone);
+ }
+ } else {
+ Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
+ if (pattern_content.IsOneByte()) {
+ Vector<const uint8_t> pattern_vector =
+ pattern_content.ToOneByteVector();
+ if (pattern_vector.length() == 1) {
+ FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
+ limit, zone);
+ } else {
+ FindStringIndices(isolate, subject_vector, pattern_vector, indices,
+ limit, zone);
+ }
+ } else {
+ Vector<const uc16> pattern_vector = pattern_content.ToUC16Vector();
+ if (pattern_vector.length() == 1) {
+ FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
+ limit, zone);
+ } else {
+ FindStringIndices(isolate, subject_vector, pattern_vector, indices,
+ limit, zone);
+ }
+ }
+ }
+ }
+}
+
+
+template <typename ResultSeqString>
+MUST_USE_RESULT static Object* StringReplaceGlobalAtomRegExpWithString(
+ Isolate* isolate, Handle<String> subject, Handle<JSRegExp> pattern_regexp,
+ Handle<String> replacement, Handle<JSArray> last_match_info) {
+ DCHECK(subject->IsFlat());
+ DCHECK(replacement->IsFlat());
+
+ ZoneScope zone_scope(isolate->runtime_zone());
+ ZoneList<int> indices(8, zone_scope.zone());
+ DCHECK_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
+ String* pattern =
+ String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
+ int subject_len = subject->length();
+ int pattern_len = pattern->length();
+ int replacement_len = replacement->length();
+
+ FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff,
+ zone_scope.zone());
+
+ int matches = indices.length();
+ if (matches == 0) return *subject;
+
+ // Detect integer overflow.
+ int64_t result_len_64 = (static_cast<int64_t>(replacement_len) -
+ static_cast<int64_t>(pattern_len)) *
+ static_cast<int64_t>(matches) +
+ static_cast<int64_t>(subject_len);
+ int result_len;
+ if (result_len_64 > static_cast<int64_t>(String::kMaxLength)) {
+ STATIC_ASSERT(String::kMaxLength < kMaxInt);
+ result_len = kMaxInt; // Provoke exception.
+ } else {
+ result_len = static_cast<int>(result_len_64);
+ }
+
+ int subject_pos = 0;
+ int result_pos = 0;
+
+ MaybeHandle<SeqString> maybe_res;
+ if (ResultSeqString::kHasOneByteEncoding) {
+ maybe_res = isolate->factory()->NewRawOneByteString(result_len);
+ } else {
+ maybe_res = isolate->factory()->NewRawTwoByteString(result_len);
+ }
+ Handle<SeqString> untyped_res;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, untyped_res, maybe_res);
+ Handle<ResultSeqString> result = Handle<ResultSeqString>::cast(untyped_res);
+
+ for (int i = 0; i < matches; i++) {
+ // Copy non-matched subject content.
+ if (subject_pos < indices.at(i)) {
+ String::WriteToFlat(*subject, result->GetChars() + result_pos,
+ subject_pos, indices.at(i));
+ result_pos += indices.at(i) - subject_pos;
+ }
+
+ // Replace match.
+ if (replacement_len > 0) {
+ String::WriteToFlat(*replacement, result->GetChars() + result_pos, 0,
+ replacement_len);
+ result_pos += replacement_len;
+ }
+
+ subject_pos = indices.at(i) + pattern_len;
+ }
+ // Add remaining subject content at the end.
+ if (subject_pos < subject_len) {
+ String::WriteToFlat(*subject, result->GetChars() + result_pos, subject_pos,
+ subject_len);
+ }
+
+ int32_t match_indices[] = {indices.at(matches - 1),
+ indices.at(matches - 1) + pattern_len};
+ RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices);
+
+ return *result;
+}
+
+
+MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithString(
+ Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
+ Handle<String> replacement, Handle<JSArray> last_match_info) {
+ DCHECK(subject->IsFlat());
+ DCHECK(replacement->IsFlat());
+
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ // CompiledReplacement uses zone allocation.
+ ZoneScope zone_scope(isolate->runtime_zone());
+ CompiledReplacement compiled_replacement(zone_scope.zone());
+ bool simple_replace =
+ compiled_replacement.Compile(replacement, capture_count, subject_length);
+
+ // Shortcut for simple non-regexp global replacements
+ if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) {
+ if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) {
+ return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
+ isolate, subject, regexp, replacement, last_match_info);
+ } else {
+ return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
+ isolate, subject, regexp, replacement, last_match_info);
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) {
+ if (global_cache.HasException()) return isolate->heap()->exception();
+ return *subject;
+ }
+
+ // Guessing the number of parts that the final result string is built
+ // from. Global regexps can match any number of times, so we guess
+ // conservatively.
+ int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1;
+ ReplacementStringBuilder builder(isolate->heap(), subject, expected_parts);
+
+ // Number of parts added by compiled replacement plus preceeding
+ // string and possibly suffix after last match. It is possible for
+ // all components to use two elements when encoded as two smis.
+ const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
+
+ int prev = 0;
+
+ do {
+ builder.EnsureCapacity(parts_added_per_loop);
+
+ int start = current_match[0];
+ int end = current_match[1];
+
+ if (prev < start) {
+ builder.AddSubjectSlice(prev, start);
+ }
+
+ if (simple_replace) {
+ builder.AddString(replacement);
+ } else {
+ compiled_replacement.Apply(&builder, start, end, current_match);
+ }
+ prev = end;
+
+ current_match = global_cache.FetchNext();
+ } while (current_match != NULL);
+
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ if (prev < subject_length) {
+ builder.EnsureCapacity(2);
+ builder.AddSubjectSlice(prev, subject_length);
+ }
+
+ RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ Handle<String> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, builder.ToString());
+ return *result;
+}
+
+
+template <typename ResultSeqString>
+MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithEmptyString(
+ Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
+ Handle<JSArray> last_match_info) {
+ DCHECK(subject->IsFlat());
+
+ // Shortcut for simple non-regexp global replacements
+ if (regexp->TypeTag() == JSRegExp::ATOM) {
+ Handle<String> empty_string = isolate->factory()->empty_string();
+ if (subject->IsOneByteRepresentation()) {
+ return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
+ isolate, subject, regexp, empty_string, last_match_info);
+ } else {
+ return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
+ isolate, subject, regexp, empty_string, last_match_info);
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) {
+ if (global_cache.HasException()) return isolate->heap()->exception();
+ return *subject;
+ }
+
+ int start = current_match[0];
+ int end = current_match[1];
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ int new_length = subject_length - (end - start);
+ if (new_length == 0) return isolate->heap()->empty_string();
+
+ Handle<ResultSeqString> answer;
+ if (ResultSeqString::kHasOneByteEncoding) {
+ answer = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawOneByteString(new_length).ToHandleChecked());
+ } else {
+ answer = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawTwoByteString(new_length).ToHandleChecked());
+ }
+
+ int prev = 0;
+ int position = 0;
+
+ do {
+ start = current_match[0];
+ end = current_match[1];
+ if (prev < start) {
+ // Add substring subject[prev;start] to answer string.
+ String::WriteToFlat(*subject, answer->GetChars() + position, prev, start);
+ position += start - prev;
+ }
+ prev = end;
+
+ current_match = global_cache.FetchNext();
+ } while (current_match != NULL);
+
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ if (prev < subject_length) {
+ // Add substring subject[prev;length] to answer string.
+ String::WriteToFlat(*subject, answer->GetChars() + position, prev,
+ subject_length);
+ position += subject_length - prev;
+ }
+
+ if (position == 0) return isolate->heap()->empty_string();
+
+ // Shorten string and fill
+ int string_size = ResultSeqString::SizeFor(position);
+ int allocated_string_size = ResultSeqString::SizeFor(new_length);
+ int delta = allocated_string_size - string_size;
+
+ answer->set_length(position);
+ if (delta == 0) return *answer;
+
+ Address end_of_string = answer->address() + string_size;
+ Heap* heap = isolate->heap();
+
+ // The trimming is performed on a newly allocated object, which is on a
+ // fresly allocated page or on an already swept page. Hence, the sweeper
+ // thread can not get confused with the filler creation. No synchronization
+ // needed.
+ heap->CreateFillerObjectAt(end_of_string, delta);
+ heap->AdjustLiveBytes(answer->address(), -delta, Heap::FROM_MUTATOR);
+ return *answer;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringReplaceGlobalRegExpWithString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
+
+ RUNTIME_ASSERT(regexp->GetFlags().is_global());
+ RUNTIME_ASSERT(last_match_info->HasFastObjectElements());
+
+ subject = String::Flatten(subject);
+
+ if (replacement->length() == 0) {
+ if (subject->HasOnlyOneByteChars()) {
+ return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
+ isolate, subject, regexp, last_match_info);
+ } else {
+ return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
+ isolate, subject, regexp, last_match_info);
+ }
+ }
+
+ replacement = String::Flatten(replacement);
+
+ return StringReplaceGlobalRegExpWithString(isolate, subject, regexp,
+ replacement, last_match_info);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringSplit) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
+ CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
+ RUNTIME_ASSERT(limit > 0);
+
+ int subject_length = subject->length();
+ int pattern_length = pattern->length();
+ RUNTIME_ASSERT(pattern_length > 0);
+
+ if (limit == 0xffffffffu) {
+ Handle<Object> cached_answer(
+ RegExpResultsCache::Lookup(isolate->heap(), *subject, *pattern,
+ RegExpResultsCache::STRING_SPLIT_SUBSTRINGS),
+ isolate);
+ if (*cached_answer != Smi::FromInt(0)) {
+ // The cache FixedArray is a COW-array and can therefore be reused.
+ Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(
+ Handle<FixedArray>::cast(cached_answer));
+ return *result;
+ }
+ }
+
+ // The limit can be very large (0xffffffffu), but since the pattern
+ // isn't empty, we can never create more parts than ~half the length
+ // of the subject.
+
+ subject = String::Flatten(subject);
+ pattern = String::Flatten(pattern);
+
+ static const int kMaxInitialListCapacity = 16;
+
+ 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_scope.zone());
+
+ FindStringIndicesDispatch(isolate, *subject, *pattern, &indices, limit,
+ zone_scope.zone());
+
+ if (static_cast<uint32_t>(indices.length()) < limit) {
+ indices.Add(subject_length, zone_scope.zone());
+ }
+
+ // The list indices now contains the end of each part to create.
+
+ // Create JSArray of substrings separated by separator.
+ int part_count = indices.length();
+
+ Handle<JSArray> result = isolate->factory()->NewJSArray(part_count);
+ JSObject::EnsureCanContainHeapObjectElements(result);
+ result->set_length(Smi::FromInt(part_count));
+
+ DCHECK(result->HasFastObjectElements());
+
+ if (part_count == 1 && indices.at(0) == subject_length) {
+ FixedArray::cast(result->elements())->set(0, *subject);
+ return *result;
+ }
+
+ Handle<FixedArray> elements(FixedArray::cast(result->elements()));
+ int part_start = 0;
+ for (int i = 0; i < part_count; i++) {
+ HandleScope local_loop_handle(isolate);
+ int part_end = indices.at(i);
+ Handle<String> substring =
+ isolate->factory()->NewProperSubString(subject, part_start, part_end);
+ elements->set(i, *substring);
+ part_start = part_end + pattern_length;
+ }
+
+ if (limit == 0xffffffffu) {
+ if (result->HasFastObjectElements()) {
+ RegExpResultsCache::Enter(isolate, subject, pattern, elements,
+ RegExpResultsCache::STRING_SPLIT_SUBSTRINGS);
+ }
+ }
+
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_RegExpCompile) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, flags, 2);
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
+ RegExpImpl::Compile(re, pattern, flags));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_RegExpExecRT) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
+ CONVERT_INT32_ARG_CHECKED(index, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
+ // Due to the way the JS calls are constructed this must be less than the
+ // length of a string, i.e. it is always a Smi. We check anyway for security.
+ RUNTIME_ASSERT(index >= 0);
+ RUNTIME_ASSERT(index <= subject->length());
+ isolate->counters()->regexp_entry_runtime()->Increment();
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ RegExpImpl::Exec(regexp, subject, index, last_match_info));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_RegExpConstructResult) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_SMI_ARG_CHECKED(size, 0);
+ RUNTIME_ASSERT(size >= 0 && size <= FixedArray::kMaxLength);
+ CONVERT_ARG_HANDLE_CHECKED(Object, index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, input, 2);
+ Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size);
+ Handle<Map> regexp_map(isolate->native_context()->regexp_result_map());
+ Handle<JSObject> object =
+ isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED, false);
+ Handle<JSArray> array = Handle<JSArray>::cast(object);
+ array->set_elements(*elements);
+ array->set_length(Smi::FromInt(size));
+ // Write in-object properties after the length of the array.
+ array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, *index);
+ array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input);
+ return *array;
+}
+
+
+RUNTIME_FUNCTION(Runtime_RegExpInitializeObject) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 6);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+ // If source is the empty string we set it to "(?:)" instead as
+ // suggested by ECMA-262, 5th, section 15.10.4.1.
+ if (source->length() == 0) source = isolate->factory()->query_colon_string();
+
+ CONVERT_ARG_HANDLE_CHECKED(Object, global, 2);
+ if (!global->IsTrue()) global = isolate->factory()->false_value();
+
+ CONVERT_ARG_HANDLE_CHECKED(Object, ignoreCase, 3);
+ if (!ignoreCase->IsTrue()) ignoreCase = isolate->factory()->false_value();
+
+ CONVERT_ARG_HANDLE_CHECKED(Object, multiline, 4);
+ if (!multiline->IsTrue()) multiline = isolate->factory()->false_value();
+
+ CONVERT_ARG_HANDLE_CHECKED(Object, sticky, 5);
+ if (!sticky->IsTrue()) sticky = isolate->factory()->false_value();
+
+ Map* map = regexp->map();
+ Object* constructor = map->constructor();
+ if (!FLAG_harmony_regexps && constructor->IsJSFunction() &&
+ JSFunction::cast(constructor)->initial_map() == map) {
+ // If we still have the original map, set in-object properties directly.
+ regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, *source);
+ // Both true and false are immovable immortal objects so no need for write
+ // barrier.
+ regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, *global,
+ SKIP_WRITE_BARRIER);
+ regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, *ignoreCase,
+ SKIP_WRITE_BARRIER);
+ regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, *multiline,
+ SKIP_WRITE_BARRIER);
+ regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
+ Smi::FromInt(0), SKIP_WRITE_BARRIER);
+ return *regexp;
+ }
+
+ // Map has changed, so use generic, but slower, method. We also end here if
+ // the --harmony-regexp flag is set, because the initial map does not have
+ // space for the 'sticky' flag, since it is from the snapshot, but must work
+ // both with and without --harmony-regexp. When sticky comes out from under
+ // the flag, we will be able to use the fast initial map.
+ PropertyAttributes final =
+ static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
+ PropertyAttributes writable =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
+ Handle<Object> zero(Smi::FromInt(0), isolate);
+ Factory* factory = isolate->factory();
+ JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->source_string(),
+ source, final).Check();
+ JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->global_string(),
+ global, final).Check();
+ JSObject::SetOwnPropertyIgnoreAttributes(
+ regexp, factory->ignore_case_string(), ignoreCase, final).Check();
+ JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->multiline_string(),
+ multiline, final).Check();
+ if (FLAG_harmony_regexps) {
+ JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->sticky_string(),
+ sticky, final).Check();
+ }
+ JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->last_index_string(),
+ zero, writable).Check();
+ return *regexp;
+}
+
+
+RUNTIME_FUNCTION(Runtime_MaterializeRegExpLiteral) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ CONVERT_SMI_ARG_CHECKED(index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, pattern, 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, flags, 3);
+
+ // Get the RegExp function from the context in the literals array.
+ // This is the RegExp function from the context in which the
+ // function was created. We do not use the RegExp function from the
+ // current native context because this might be the RegExp function
+ // from another context which we should not have access to.
+ Handle<JSFunction> constructor = Handle<JSFunction>(
+ JSFunction::NativeContextFromLiterals(*literals)->regexp_function());
+ // Compute the regular expression literal.
+ Handle<Object> regexp;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, regexp,
+ RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags));
+ literals->set(index, *regexp);
+ return *regexp;
+}
+
+
+// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain
+// separate last match info. See comment on that function.
+template <bool has_capture>
+static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
+ Handle<JSRegExp> regexp,
+ Handle<JSArray> last_match_array,
+ Handle<JSArray> result_array) {
+ DCHECK(subject->IsFlat());
+ DCHECK_NE(has_capture, regexp->CaptureCount() == 0);
+
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ static const int kMinLengthToCache = 0x1000;
+
+ if (subject_length > kMinLengthToCache) {
+ Handle<Object> cached_answer(
+ RegExpResultsCache::Lookup(isolate->heap(), *subject, regexp->data(),
+ RegExpResultsCache::REGEXP_MULTIPLE_INDICES),
+ isolate);
+ if (*cached_answer != Smi::FromInt(0)) {
+ Handle<FixedArray> cached_fixed_array =
+ Handle<FixedArray>(FixedArray::cast(*cached_answer));
+ // The cache FixedArray is a COW-array and can therefore be reused.
+ JSArray::SetContent(result_array, cached_fixed_array);
+ // The actual length of the result array is stored in the last element of
+ // the backing store (the backing FixedArray may have a larger capacity).
+ Object* cached_fixed_array_last_element =
+ cached_fixed_array->get(cached_fixed_array->length() - 1);
+ Smi* js_array_length = Smi::cast(cached_fixed_array_last_element);
+ result_array->set_length(js_array_length);
+ RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count,
+ NULL);
+ return *result_array;
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ // Ensured in Runtime_RegExpExecMultiple.
+ DCHECK(result_array->HasFastObjectElements());
+ Handle<FixedArray> result_elements(
+ FixedArray::cast(result_array->elements()));
+ if (result_elements->length() < 16) {
+ result_elements = isolate->factory()->NewFixedArrayWithHoles(16);
+ }
+
+ FixedArrayBuilder builder(result_elements);
+
+ // Position to search from.
+ int match_start = -1;
+ int match_end = 0;
+ bool first = true;
+
+ // Two smis before and after the match, for very long strings.
+ static const int kMaxBuilderEntriesPerRegExpMatch = 5;
+
+ while (true) {
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) break;
+ match_start = current_match[0];
+ builder.EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
+ if (match_end < match_start) {
+ ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
+ match_start);
+ }
+ match_end = current_match[1];
+ {
+ // Avoid accumulating new handles inside loop.
+ HandleScope temp_scope(isolate);
+ Handle<String> match;
+ if (!first) {
+ match = isolate->factory()->NewProperSubString(subject, match_start,
+ match_end);
+ } else {
+ match =
+ isolate->factory()->NewSubString(subject, match_start, match_end);
+ first = false;
+ }
+
+ if (has_capture) {
+ // Arguments array to replace function is match, captures, index and
+ // subject, i.e., 3 + capture count in total.
+ Handle<FixedArray> elements =
+ isolate->factory()->NewFixedArray(3 + capture_count);
+
+ elements->set(0, *match);
+ for (int i = 1; i <= capture_count; i++) {
+ int start = current_match[i * 2];
+ if (start >= 0) {
+ int end = current_match[i * 2 + 1];
+ DCHECK(start <= end);
+ Handle<String> substring =
+ isolate->factory()->NewSubString(subject, start, end);
+ elements->set(i, *substring);
+ } else {
+ DCHECK(current_match[i * 2 + 1] < 0);
+ elements->set(i, isolate->heap()->undefined_value());
+ }
+ }
+ elements->set(capture_count + 1, Smi::FromInt(match_start));
+ elements->set(capture_count + 2, *subject);
+ builder.Add(*isolate->factory()->NewJSArrayWithElements(elements));
+ } else {
+ builder.Add(*match);
+ }
+ }
+ }
+
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ if (match_start >= 0) {
+ // Finished matching, with at least one match.
+ if (match_end < subject_length) {
+ ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
+ subject_length);
+ }
+
+ RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count,
+ NULL);
+
+ if (subject_length > kMinLengthToCache) {
+ // Store the length of the result array into the last element of the
+ // backing FixedArray.
+ builder.EnsureCapacity(1);
+ Handle<FixedArray> fixed_array = builder.array();
+ fixed_array->set(fixed_array->length() - 1,
+ Smi::FromInt(builder.length()));
+ // Cache the result and turn the FixedArray into a COW array.
+ RegExpResultsCache::Enter(isolate, subject,
+ handle(regexp->data(), isolate), fixed_array,
+ RegExpResultsCache::REGEXP_MULTIPLE_INDICES);
+ }
+ return *builder.ToJSArray(result_array);
+ } else {
+ return isolate->heap()->null_value(); // No matches at all.
+ }
+}
+
+
+// This is only called for StringReplaceGlobalRegExpWithFunction. This sets
+// lastMatchInfoOverride to maintain the last match info, so we don't need to
+// set any other last match array info.
+RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) {
+ HandleScope handles(isolate);
+ DCHECK(args.length() == 4);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3);
+ RUNTIME_ASSERT(last_match_info->HasFastObjectElements());
+ RUNTIME_ASSERT(result_array->HasFastObjectElements());
+
+ subject = String::Flatten(subject);
+ RUNTIME_ASSERT(regexp->GetFlags().is_global());
+
+ if (regexp->CaptureCount() == 0) {
+ return SearchRegExpMultiple<false>(isolate, subject, regexp,
+ last_match_info, result_array);
+ } else {
+ return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info,
+ result_array);
+ }
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_RegExpConstructResult) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_RegExpConstructResult(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_RegExpExec) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_RegExpExecRT(args, isolate);
+}
+
+
+// Perform string match of pattern on subject, starting at start index.
+// Caller must ensure that 0 <= start_index <= sub->length(),
+// and should check that pat->length() + start_index <= sub->length().
+int Runtime::StringMatch(Isolate* isolate, Handle<String> sub,
+ Handle<String> pat, int start_index) {
+ DCHECK(0 <= start_index);
+ DCHECK(start_index <= sub->length());
+
+ int pattern_length = pat->length();
+ if (pattern_length == 0) return start_index;
+
+ int subject_length = sub->length();
+ if (start_index + pattern_length > subject_length) return -1;
+
+ sub = String::Flatten(sub);
+ pat = String::Flatten(pat);
+
+ DisallowHeapAllocation no_gc; // ensure vectors stay valid
+ // Extract flattened substrings of cons strings before getting encoding.
+ String::FlatContent seq_sub = sub->GetFlatContent();
+ String::FlatContent seq_pat = pat->GetFlatContent();
+
+ // dispatch on type of strings
+ if (seq_pat.IsOneByte()) {
+ Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector();
+ if (seq_sub.IsOneByte()) {
+ return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
+ start_index);
+ }
+ return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector,
+ start_index);
+ }
+ Vector<const uc16> pat_vector = seq_pat.ToUC16Vector();
+ if (seq_sub.IsOneByte()) {
+ return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
+ start_index);
+ }
+ return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index);
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc
new file mode 100644
index 0000000..82174e9
--- /dev/null
+++ b/src/runtime/runtime-strings.cc
@@ -0,0 +1,1260 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/jsregexp-inl.h"
+#include "src/jsregexp.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+#include "src/runtime/string-builder.h"
+#include "src/string-search.h"
+
+namespace v8 {
+namespace internal {
+
+
+// This may return an empty MaybeHandle if an exception is thrown or
+// we abort due to reaching the recursion limit.
+MaybeHandle<String> StringReplaceOneCharWithString(
+ Isolate* isolate, Handle<String> subject, Handle<String> search,
+ Handle<String> replace, bool* found, int recursion_limit) {
+ StackLimitCheck stackLimitCheck(isolate);
+ if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
+ return MaybeHandle<String>();
+ }
+ recursion_limit--;
+ if (subject->IsConsString()) {
+ ConsString* cons = ConsString::cast(*subject);
+ Handle<String> first = Handle<String>(cons->first());
+ Handle<String> second = Handle<String>(cons->second());
+ Handle<String> new_first;
+ if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
+ recursion_limit).ToHandle(&new_first)) {
+ return MaybeHandle<String>();
+ }
+ if (*found) return isolate->factory()->NewConsString(new_first, second);
+
+ Handle<String> new_second;
+ if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
+ recursion_limit)
+ .ToHandle(&new_second)) {
+ return MaybeHandle<String>();
+ }
+ if (*found) return isolate->factory()->NewConsString(first, new_second);
+
+ return subject;
+ } else {
+ int index = Runtime::StringMatch(isolate, subject, search, 0);
+ if (index == -1) return subject;
+ *found = true;
+ Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
+ Handle<String> cons1;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, cons1, isolate->factory()->NewConsString(first, replace),
+ String);
+ Handle<String> second =
+ isolate->factory()->NewSubString(subject, index + 1, subject->length());
+ return isolate->factory()->NewConsString(cons1, second);
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, replace, 2);
+
+ // If the cons string tree is too deep, we simply abort the recursion and
+ // retry with a flattened subject string.
+ const int kRecursionLimit = 0x1000;
+ bool found = false;
+ Handle<String> result;
+ if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
+ kRecursionLimit).ToHandle(&result)) {
+ return *result;
+ }
+ if (isolate->has_pending_exception()) return isolate->heap()->exception();
+
+ subject = String::Flatten(subject);
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
+ kRecursionLimit));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringIndexOf) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
+
+ uint32_t start_index;
+ if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
+
+ RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
+ int position = Runtime::StringMatch(isolate, sub, pat, start_index);
+ return Smi::FromInt(position);
+}
+
+
+template <typename schar, typename pchar>
+static int StringMatchBackwards(Vector<const schar> subject,
+ Vector<const pchar> pattern, int idx) {
+ int pattern_length = pattern.length();
+ DCHECK(pattern_length >= 1);
+ DCHECK(idx + pattern_length <= subject.length());
+
+ if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
+ for (int i = 0; i < pattern_length; i++) {
+ uc16 c = pattern[i];
+ if (c > String::kMaxOneByteCharCode) {
+ return -1;
+ }
+ }
+ }
+
+ pchar pattern_first_char = pattern[0];
+ for (int i = idx; i >= 0; i--) {
+ if (subject[i] != pattern_first_char) continue;
+ int j = 1;
+ while (j < pattern_length) {
+ if (pattern[j] != subject[i + j]) {
+ break;
+ }
+ j++;
+ }
+ if (j == pattern_length) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
+
+ uint32_t start_index;
+ if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
+
+ uint32_t pat_length = pat->length();
+ uint32_t sub_length = sub->length();
+
+ if (start_index + pat_length > sub_length) {
+ start_index = sub_length - pat_length;
+ }
+
+ if (pat_length == 0) {
+ return Smi::FromInt(start_index);
+ }
+
+ sub = String::Flatten(sub);
+ pat = String::Flatten(pat);
+
+ int position = -1;
+ DisallowHeapAllocation no_gc; // ensure vectors stay valid
+
+ String::FlatContent sub_content = sub->GetFlatContent();
+ String::FlatContent pat_content = pat->GetFlatContent();
+
+ if (pat_content.IsOneByte()) {
+ Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector();
+ if (sub_content.IsOneByte()) {
+ position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
+ start_index);
+ }
+ } else {
+ Vector<const uc16> pat_vector = pat_content.ToUC16Vector();
+ if (sub_content.IsOneByte()) {
+ position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
+ start_index);
+ }
+ }
+
+ return Smi::FromInt(position);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringLocaleCompare) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
+
+ if (str1.is_identical_to(str2)) return Smi::FromInt(0); // Equal.
+ int str1_length = str1->length();
+ int str2_length = str2->length();
+
+ // Decide trivial cases without flattening.
+ if (str1_length == 0) {
+ if (str2_length == 0) return Smi::FromInt(0); // Equal.
+ return Smi::FromInt(-str2_length);
+ } else {
+ if (str2_length == 0) return Smi::FromInt(str1_length);
+ }
+
+ int end = str1_length < str2_length ? str1_length : str2_length;
+
+ // No need to flatten if we are going to find the answer on the first
+ // character. At this point we know there is at least one character
+ // in each string, due to the trivial case handling above.
+ int d = str1->Get(0) - str2->Get(0);
+ if (d != 0) return Smi::FromInt(d);
+
+ str1 = String::Flatten(str1);
+ str2 = String::Flatten(str2);
+
+ DisallowHeapAllocation no_gc;
+ String::FlatContent flat1 = str1->GetFlatContent();
+ String::FlatContent flat2 = str2->GetFlatContent();
+
+ for (int i = 0; i < end; i++) {
+ if (flat1.Get(i) != flat2.Get(i)) {
+ return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
+ }
+ }
+
+ return Smi::FromInt(str1_length - str2_length);
+}
+
+
+RUNTIME_FUNCTION(Runtime_SubString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
+ int start, end;
+ // We have a fast integer-only case here to avoid a conversion to double in
+ // the common case where from and to are Smis.
+ if (args[1]->IsSmi() && args[2]->IsSmi()) {
+ CONVERT_SMI_ARG_CHECKED(from_number, 1);
+ CONVERT_SMI_ARG_CHECKED(to_number, 2);
+ start = from_number;
+ end = to_number;
+ } else {
+ CONVERT_DOUBLE_ARG_CHECKED(from_number, 1);
+ CONVERT_DOUBLE_ARG_CHECKED(to_number, 2);
+ start = FastD2IChecked(from_number);
+ end = FastD2IChecked(to_number);
+ }
+ RUNTIME_ASSERT(end >= start);
+ RUNTIME_ASSERT(start >= 0);
+ RUNTIME_ASSERT(end <= string->length());
+ isolate->counters()->sub_string_runtime()->Increment();
+
+ return *isolate->factory()->NewSubString(string, start, end);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringAdd) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
+ isolate->counters()->string_add_runtime()->Increment();
+ Handle<String> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewConsString(str1, str2));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_InternalizeString) {
+ HandleScope handles(isolate);
+ RUNTIME_ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
+ return *isolate->factory()->InternalizeString(string);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringMatch) {
+ HandleScope handles(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2);
+
+ RUNTIME_ASSERT(regexp_info->HasFastObjectElements());
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ int capture_count = regexp->CaptureCount();
+
+ 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_scope.zone()); // start
+ offsets.Add(match[1], zone_scope.zone()); // end
+ }
+
+ if (global_cache.HasException()) return isolate->heap()->exception();
+
+ if (offsets.length() == 0) {
+ // Not a single match.
+ return isolate->heap()->null_value();
+ }
+
+ RegExpImpl::SetLastMatchInfo(regexp_info, subject, capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ int matches = offsets.length() / 2;
+ Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches);
+ Handle<String> substring =
+ isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1));
+ elements->set(0, *substring);
+ for (int i = 1; i < matches; i++) {
+ HandleScope temp_scope(isolate);
+ int from = offsets.at(i * 2);
+ int to = offsets.at(i * 2 + 1);
+ Handle<String> substring =
+ isolate->factory()->NewProperSubString(subject, from, to);
+ elements->set(i, *substring);
+ }
+ Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements);
+ result->set_length(Smi::FromInt(matches));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
+
+ // Flatten the string. If someone wants to get a char at an index
+ // in a cons string, it is likely that more indices will be
+ // accessed.
+ subject = String::Flatten(subject);
+
+ if (i >= static_cast<uint32_t>(subject->length())) {
+ return isolate->heap()->nan_value();
+ }
+
+ return Smi::FromInt(subject->Get(i));
+}
+
+
+RUNTIME_FUNCTION(Runtime_CharFromCode) {
+ HandleScope handlescope(isolate);
+ DCHECK(args.length() == 1);
+ if (args[0]->IsNumber()) {
+ CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]);
+ code &= 0xffff;
+ return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
+ }
+ return isolate->heap()->empty_string();
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringCompare) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
+
+ isolate->counters()->string_compare_runtime()->Increment();
+
+ // A few fast case tests before we flatten.
+ if (x.is_identical_to(y)) return Smi::FromInt(EQUAL);
+ if (y->length() == 0) {
+ if (x->length() == 0) return Smi::FromInt(EQUAL);
+ return Smi::FromInt(GREATER);
+ } else if (x->length() == 0) {
+ return Smi::FromInt(LESS);
+ }
+
+ int d = x->Get(0) - y->Get(0);
+ if (d < 0)
+ return Smi::FromInt(LESS);
+ else if (d > 0)
+ return Smi::FromInt(GREATER);
+
+ // Slow case.
+ x = String::Flatten(x);
+ y = String::Flatten(y);
+
+ DisallowHeapAllocation no_gc;
+ Object* equal_prefix_result = Smi::FromInt(EQUAL);
+ int prefix_length = x->length();
+ if (y->length() < prefix_length) {
+ prefix_length = y->length();
+ equal_prefix_result = Smi::FromInt(GREATER);
+ } else if (y->length() > prefix_length) {
+ equal_prefix_result = Smi::FromInt(LESS);
+ }
+ int r;
+ String::FlatContent x_content = x->GetFlatContent();
+ String::FlatContent y_content = y->GetFlatContent();
+ if (x_content.IsOneByte()) {
+ Vector<const uint8_t> x_chars = x_content.ToOneByteVector();
+ if (y_content.IsOneByte()) {
+ Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y_content.ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ } else {
+ Vector<const uc16> x_chars = x_content.ToUC16Vector();
+ if (y_content.IsOneByte()) {
+ Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y_content.ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ }
+ Object* result;
+ if (r == 0) {
+ result = equal_prefix_result;
+ } else {
+ result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
+ }
+ return result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
+ int32_t array_length;
+ if (!args[1]->ToInt32(&array_length)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
+ }
+ CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
+
+ size_t actual_array_length = 0;
+ RUNTIME_ASSERT(
+ TryNumberToSize(isolate, array->length(), &actual_array_length));
+ RUNTIME_ASSERT(array_length >= 0);
+ RUNTIME_ASSERT(static_cast<size_t>(array_length) <= actual_array_length);
+
+ // This assumption is used by the slice encoding in one or two smis.
+ DCHECK(Smi::kMaxValue >= String::kMaxLength);
+
+ RUNTIME_ASSERT(array->HasFastElements());
+ JSObject::EnsureCanContainHeapObjectElements(array);
+
+ int special_length = special->length();
+ if (!array->HasFastObjectElements()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ int length;
+ bool one_byte = special->HasOnlyOneByteChars();
+
+ {
+ DisallowHeapAllocation no_gc;
+ FixedArray* fixed_array = FixedArray::cast(array->elements());
+ if (fixed_array->length() < array_length) {
+ array_length = fixed_array->length();
+ }
+
+ if (array_length == 0) {
+ return isolate->heap()->empty_string();
+ } else if (array_length == 1) {
+ Object* first = fixed_array->get(0);
+ if (first->IsString()) return first;
+ }
+ length = StringBuilderConcatLength(special_length, fixed_array,
+ array_length, &one_byte);
+ }
+
+ if (length == -1) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ if (one_byte) {
+ Handle<SeqOneByteString> answer;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, answer, isolate->factory()->NewRawOneByteString(length));
+ StringBuilderConcatHelper(*special, answer->GetChars(),
+ FixedArray::cast(array->elements()),
+ array_length);
+ return *answer;
+ } else {
+ Handle<SeqTwoByteString> answer;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, answer, isolate->factory()->NewRawTwoByteString(length));
+ StringBuilderConcatHelper(*special, answer->GetChars(),
+ FixedArray::cast(array->elements()),
+ array_length);
+ return *answer;
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
+ int32_t array_length;
+ if (!args[1]->ToInt32(&array_length)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
+ }
+ CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
+ RUNTIME_ASSERT(array->HasFastObjectElements());
+ RUNTIME_ASSERT(array_length >= 0);
+
+ Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()));
+ if (fixed_array->length() < array_length) {
+ array_length = fixed_array->length();
+ }
+
+ if (array_length == 0) {
+ return isolate->heap()->empty_string();
+ } else if (array_length == 1) {
+ Object* first = fixed_array->get(0);
+ RUNTIME_ASSERT(first->IsString());
+ return first;
+ }
+
+ int separator_length = separator->length();
+ RUNTIME_ASSERT(separator_length > 0);
+ int max_nof_separators =
+ (String::kMaxLength + separator_length - 1) / separator_length;
+ if (max_nof_separators < (array_length - 1)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
+ }
+ int length = (array_length - 1) * separator_length;
+ for (int i = 0; i < array_length; i++) {
+ Object* element_obj = fixed_array->get(i);
+ RUNTIME_ASSERT(element_obj->IsString());
+ String* element = String::cast(element_obj);
+ int increment = element->length();
+ if (increment > String::kMaxLength - length) {
+ STATIC_ASSERT(String::kMaxLength < kMaxInt);
+ length = kMaxInt; // Provoke exception;
+ break;
+ }
+ length += increment;
+ }
+
+ Handle<SeqTwoByteString> answer;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, answer, isolate->factory()->NewRawTwoByteString(length));
+
+ DisallowHeapAllocation no_gc;
+
+ uc16* sink = answer->GetChars();
+#ifdef DEBUG
+ uc16* end = sink + length;
+#endif
+
+ RUNTIME_ASSERT(fixed_array->get(0)->IsString());
+ String* first = String::cast(fixed_array->get(0));
+ String* separator_raw = *separator;
+ int first_length = first->length();
+ String::WriteToFlat(first, sink, 0, first_length);
+ sink += first_length;
+
+ for (int i = 1; i < array_length; i++) {
+ DCHECK(sink + separator_length <= end);
+ String::WriteToFlat(separator_raw, sink, 0, separator_length);
+ sink += separator_length;
+
+ RUNTIME_ASSERT(fixed_array->get(i)->IsString());
+ String* element = String::cast(fixed_array->get(i));
+ int element_length = element->length();
+ DCHECK(sink + element_length <= end);
+ String::WriteToFlat(element, sink, 0, element_length);
+ sink += element_length;
+ }
+ DCHECK(sink == end);
+
+ // Use %_FastOneByteArrayJoin instead.
+ DCHECK(!answer->IsOneByteRepresentation());
+ return *answer;
+}
+
+template <typename Char>
+static void JoinSparseArrayWithSeparator(FixedArray* elements,
+ int elements_length,
+ uint32_t array_length,
+ String* separator,
+ Vector<Char> buffer) {
+ DisallowHeapAllocation no_gc;
+ int previous_separator_position = 0;
+ int separator_length = separator->length();
+ int cursor = 0;
+ for (int i = 0; i < elements_length; i += 2) {
+ int position = NumberToInt32(elements->get(i));
+ String* string = String::cast(elements->get(i + 1));
+ int string_length = string->length();
+ if (string->length() > 0) {
+ while (previous_separator_position < position) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor], 0,
+ separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length);
+ cursor += string->length();
+ }
+ }
+ if (separator_length > 0) {
+ // Array length must be representable as a signed 32-bit number,
+ // otherwise the total string length would have been too large.
+ DCHECK(array_length <= 0x7fffffff); // Is int32_t.
+ int last_array_index = static_cast<int>(array_length - 1);
+ while (previous_separator_position < last_array_index) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor], 0,
+ separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ }
+ DCHECK(cursor <= buffer.length());
+}
+
+
+RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
+ CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
+ // elements_array is fast-mode JSarray of alternating positions
+ // (increasing order) and strings.
+ RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements());
+ // array_length is length of original array (used to add separators);
+ // separator is string to put between elements. Assumed to be non-empty.
+ RUNTIME_ASSERT(array_length > 0);
+
+ // Find total length of join result.
+ int string_length = 0;
+ bool is_one_byte = separator->IsOneByteRepresentation();
+ bool overflow = false;
+ CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length());
+ RUNTIME_ASSERT(elements_length <= elements_array->elements()->length());
+ RUNTIME_ASSERT((elements_length & 1) == 0); // Even length.
+ FixedArray* elements = FixedArray::cast(elements_array->elements());
+ for (int i = 0; i < elements_length; i += 2) {
+ RUNTIME_ASSERT(elements->get(i)->IsNumber());
+ CONVERT_NUMBER_CHECKED(uint32_t, position, Uint32, elements->get(i));
+ RUNTIME_ASSERT(position < array_length);
+ RUNTIME_ASSERT(elements->get(i + 1)->IsString());
+ }
+
+ {
+ DisallowHeapAllocation no_gc;
+ for (int i = 0; i < elements_length; i += 2) {
+ String* string = String::cast(elements->get(i + 1));
+ int length = string->length();
+ if (is_one_byte && !string->IsOneByteRepresentation()) {
+ is_one_byte = false;
+ }
+ if (length > String::kMaxLength ||
+ String::kMaxLength - length < string_length) {
+ overflow = true;
+ break;
+ }
+ string_length += length;
+ }
+ }
+
+ int separator_length = separator->length();
+ if (!overflow && separator_length > 0) {
+ if (array_length <= 0x7fffffffu) {
+ int separator_count = static_cast<int>(array_length) - 1;
+ int remaining_length = String::kMaxLength - string_length;
+ if ((remaining_length / separator_length) >= separator_count) {
+ string_length += separator_length * (array_length - 1);
+ } else {
+ // Not room for the separators within the maximal string length.
+ overflow = true;
+ }
+ } else {
+ // Nonempty separator and at least 2^31-1 separators necessary
+ // means that the string is too large to create.
+ STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
+ overflow = true;
+ }
+ }
+ if (overflow) {
+ // Throw an exception if the resulting string is too large. See
+ // https://code.google.com/p/chromium/issues/detail?id=336820
+ // for details.
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
+ }
+
+ if (is_one_byte) {
+ Handle<SeqOneByteString> result = isolate->factory()
+ ->NewRawOneByteString(string_length)
+ .ToHandleChecked();
+ JoinSparseArrayWithSeparator<uint8_t>(
+ FixedArray::cast(elements_array->elements()), elements_length,
+ array_length, *separator,
+ Vector<uint8_t>(result->GetChars(), string_length));
+ return *result;
+ } else {
+ Handle<SeqTwoByteString> result = isolate->factory()
+ ->NewRawTwoByteString(string_length)
+ .ToHandleChecked();
+ JoinSparseArrayWithSeparator<uc16>(
+ FixedArray::cast(elements_array->elements()), elements_length,
+ array_length, *separator,
+ Vector<uc16>(result->GetChars(), string_length));
+ return *result;
+ }
+}
+
+
+// Copies Latin1 characters to the given fixed array looking up
+// one-char strings in the cache. Gives up on the first char that is
+// not in the cache and fills the remainder with smi zeros. Returns
+// the length of the successfully copied prefix.
+static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
+ FixedArray* elements, int length) {
+ DisallowHeapAllocation no_gc;
+ FixedArray* one_byte_cache = heap->single_character_string_cache();
+ Object* undefined = heap->undefined_value();
+ int i;
+ WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
+ for (i = 0; i < length; ++i) {
+ Object* value = one_byte_cache->get(chars[i]);
+ if (value == undefined) break;
+ elements->set(i, value, mode);
+ }
+ if (i < length) {
+ DCHECK(Smi::FromInt(0) == 0);
+ memset(elements->data_start() + i, 0, kPointerSize * (length - i));
+ }
+#ifdef DEBUG
+ for (int j = 0; j < length; ++j) {
+ Object* element = elements->get(j);
+ DCHECK(element == Smi::FromInt(0) ||
+ (element->IsString() && String::cast(element)->LooksValid()));
+ }
+#endif
+ return i;
+}
+
+
+// Converts a String to JSArray.
+// For example, "foo" => ["f", "o", "o"].
+RUNTIME_FUNCTION(Runtime_StringToArray) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
+
+ s = String::Flatten(s);
+ const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
+
+ Handle<FixedArray> elements;
+ int position = 0;
+ if (s->IsFlat() && s->IsOneByteRepresentation()) {
+ // Try using cached chars where possible.
+ elements = isolate->factory()->NewUninitializedFixedArray(length);
+
+ DisallowHeapAllocation no_gc;
+ String::FlatContent content = s->GetFlatContent();
+ if (content.IsOneByte()) {
+ Vector<const uint8_t> chars = content.ToOneByteVector();
+ // Note, this will initialize all elements (not only the prefix)
+ // to prevent GC from seeing partially initialized array.
+ position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(),
+ *elements, length);
+ } else {
+ MemsetPointer(elements->data_start(), isolate->heap()->undefined_value(),
+ length);
+ }
+ } else {
+ elements = isolate->factory()->NewFixedArray(length);
+ }
+ for (int i = position; i < length; ++i) {
+ Handle<Object> str =
+ isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
+ elements->set(i, *str);
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < length; ++i) {
+ DCHECK(String::cast(elements->get(i))->length() == 1);
+ }
+#endif
+
+ return *isolate->factory()->NewJSArrayWithElements(elements);
+}
+
+
+static inline bool ToUpperOverflows(uc32 character) {
+ // y with umlauts and the micro sign are the only characters that stop
+ // fitting into one-byte when converting to uppercase.
+ static const uc32 yuml_code = 0xff;
+ static const uc32 micro_code = 0xb5;
+ return (character == yuml_code || character == micro_code);
+}
+
+
+template <class Converter>
+MUST_USE_RESULT static Object* ConvertCaseHelper(
+ Isolate* isolate, String* string, SeqString* result, int result_length,
+ unibrow::Mapping<Converter, 128>* mapping) {
+ DisallowHeapAllocation no_gc;
+ // We try this twice, once with the assumption that the result is no longer
+ // than the input and, if that assumption breaks, again with the exact
+ // length. This may not be pretty, but it is nicer than what was here before
+ // and I hereby claim my vaffel-is.
+ //
+ // NOTE: This assumes that the upper/lower case of an ASCII
+ // character is also ASCII. This is currently the case, but it
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ bool has_changed_character = false;
+
+ // Convert all characters to upper case, assuming that they will fit
+ // in the buffer
+ Access<ConsStringIteratorOp> op(isolate->runtime_state()->string_iterator());
+ StringCharacterStream stream(string, op.value());
+ unibrow::uchar chars[Converter::kMaxWidth];
+ // We can assume that the string is not empty
+ uc32 current = stream.GetNext();
+ bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
+ for (int i = 0; i < result_length;) {
+ bool has_next = stream.HasMore();
+ uc32 next = has_next ? stream.GetNext() : 0;
+ int char_length = mapping->get(current, next, chars);
+ if (char_length == 0) {
+ // The case conversion of this character is the character itself.
+ result->Set(i, current);
+ i++;
+ } else if (char_length == 1 &&
+ (ignore_overflow || !ToUpperOverflows(current))) {
+ // Common case: converting the letter resulted in one character.
+ DCHECK(static_cast<uc32>(chars[0]) != current);
+ result->Set(i, chars[0]);
+ has_changed_character = true;
+ i++;
+ } else if (result_length == string->length()) {
+ bool overflows = ToUpperOverflows(current);
+ // We've assumed that the result would be as long as the
+ // input but here is a character that converts to several
+ // characters. No matter, we calculate the exact length
+ // of the result and try the whole thing again.
+ //
+ // Note that this leaves room for optimization. We could just
+ // memcpy what we already have to the result string. Also,
+ // the result string is the last object allocated we could
+ // "realloc" it and probably, in the vast majority of cases,
+ // extend the existing string to be able to hold the full
+ // result.
+ int next_length = 0;
+ if (has_next) {
+ next_length = mapping->get(next, 0, chars);
+ if (next_length == 0) next_length = 1;
+ }
+ int current_length = i + char_length + next_length;
+ while (stream.HasMore()) {
+ current = stream.GetNext();
+ overflows |= ToUpperOverflows(current);
+ // NOTE: we use 0 as the next character here because, while
+ // the next character may affect what a character converts to,
+ // it does not in any case affect the length of what it convert
+ // to.
+ int char_length = mapping->get(current, 0, chars);
+ if (char_length == 0) char_length = 1;
+ current_length += char_length;
+ if (current_length > String::kMaxLength) {
+ AllowHeapAllocation allocate_error_and_return;
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate,
+ NewInvalidStringLengthError());
+ }
+ }
+ // Try again with the real length. Return signed if we need
+ // to allocate a two-byte string for to uppercase.
+ return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
+ : Smi::FromInt(current_length);
+ } else {
+ for (int j = 0; j < char_length; j++) {
+ result->Set(i, chars[j]);
+ i++;
+ }
+ has_changed_character = true;
+ }
+ current = next;
+ }
+ if (has_changed_character) {
+ return result;
+ } else {
+ // If we didn't actually change anything in doing the conversion
+ // we simple return the result and let the converted string
+ // become garbage; there is no reason to keep two identical strings
+ // alive.
+ return string;
+ }
+}
+
+
+static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF;
+static const uintptr_t kAsciiMask = kOneInEveryByte << 7;
+
+// Given a word and two range boundaries returns a word with high bit
+// set in every byte iff the corresponding input byte was strictly in
+// the range (m, n). All the other bits in the result are cleared.
+// This function is only useful when it can be inlined and the
+// boundaries are statically known.
+// Requires: all bytes in the input word and the boundaries must be
+// ASCII (less than 0x7F).
+static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) {
+ // Use strict inequalities since in edge cases the function could be
+ // further simplified.
+ DCHECK(0 < m && m < n);
+ // Has high bit set in every w byte less than n.
+ uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w;
+ // Has high bit set in every w byte greater than m.
+ uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m);
+ return (tmp1 & tmp2 & (kOneInEveryByte * 0x80));
+}
+
+
+#ifdef DEBUG
+static bool CheckFastAsciiConvert(char* dst, const char* src, int length,
+ bool changed, bool is_to_lower) {
+ bool expected_changed = false;
+ for (int i = 0; i < length; i++) {
+ if (dst[i] == src[i]) continue;
+ expected_changed = true;
+ if (is_to_lower) {
+ DCHECK('A' <= src[i] && src[i] <= 'Z');
+ DCHECK(dst[i] == src[i] + ('a' - 'A'));
+ } else {
+ DCHECK('a' <= src[i] && src[i] <= 'z');
+ DCHECK(dst[i] == src[i] - ('a' - 'A'));
+ }
+ }
+ return (expected_changed == changed);
+}
+#endif
+
+
+template <class Converter>
+static bool FastAsciiConvert(char* dst, const char* src, int length,
+ bool* changed_out) {
+#ifdef DEBUG
+ char* saved_dst = dst;
+ const char* saved_src = src;
+#endif
+ DisallowHeapAllocation no_gc;
+ // We rely on the distance between upper and lower case letters
+ // being a known power of 2.
+ DCHECK('a' - 'A' == (1 << 5));
+ // Boundaries for the range of input characters than require conversion.
+ static const char lo = Converter::kIsToLower ? 'A' - 1 : 'a' - 1;
+ static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1;
+ bool changed = false;
+ uintptr_t or_acc = 0;
+ const char* const limit = src + length;
+
+ // dst is newly allocated and always aligned.
+ DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t)));
+ // Only attempt processing one word at a time if src is also aligned.
+ if (IsAligned(reinterpret_cast<intptr_t>(src), sizeof(uintptr_t))) {
+ // Process the prefix of the input that requires no conversion one aligned
+ // (machine) word at a time.
+ while (src <= limit - sizeof(uintptr_t)) {
+ const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
+ or_acc |= w;
+ if (AsciiRangeMask(w, lo, hi) != 0) {
+ changed = true;
+ break;
+ }
+ *reinterpret_cast<uintptr_t*>(dst) = w;
+ src += sizeof(uintptr_t);
+ dst += sizeof(uintptr_t);
+ }
+ // Process the remainder of the input performing conversion when
+ // required one word at a time.
+ while (src <= limit - sizeof(uintptr_t)) {
+ const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
+ or_acc |= w;
+ uintptr_t m = AsciiRangeMask(w, lo, hi);
+ // The mask has high (7th) bit set in every byte that needs
+ // conversion and we know that the distance between cases is
+ // 1 << 5.
+ *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2);
+ src += sizeof(uintptr_t);
+ dst += sizeof(uintptr_t);
+ }
+ }
+ // Process the last few bytes of the input (or the whole input if
+ // unaligned access is not supported).
+ while (src < limit) {
+ char c = *src;
+ or_acc |= c;
+ if (lo < c && c < hi) {
+ c ^= (1 << 5);
+ changed = true;
+ }
+ *dst = c;
+ ++src;
+ ++dst;
+ }
+
+ if ((or_acc & kAsciiMask) != 0) return false;
+
+ DCHECK(CheckFastAsciiConvert(saved_dst, saved_src, length, changed,
+ Converter::kIsToLower));
+
+ *changed_out = changed;
+ return true;
+}
+
+
+template <class Converter>
+MUST_USE_RESULT static Object* ConvertCase(
+ Handle<String> s, Isolate* isolate,
+ unibrow::Mapping<Converter, 128>* mapping) {
+ s = String::Flatten(s);
+ int length = s->length();
+ // Assume that the string is not empty; we need this assumption later
+ if (length == 0) return *s;
+
+ // Simpler handling of ASCII strings.
+ //
+ // NOTE: This assumes that the upper/lower case of an ASCII
+ // character is also ASCII. This is currently the case, but it
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ if (s->IsOneByteRepresentationUnderneath()) {
+ // Same length as input.
+ Handle<SeqOneByteString> result =
+ isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
+ DisallowHeapAllocation no_gc;
+ String::FlatContent flat_content = s->GetFlatContent();
+ DCHECK(flat_content.IsFlat());
+ bool has_changed_character = false;
+ bool is_ascii = FastAsciiConvert<Converter>(
+ reinterpret_cast<char*>(result->GetChars()),
+ reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
+ length, &has_changed_character);
+ // If not ASCII, we discard the result and take the 2 byte path.
+ if (is_ascii) return has_changed_character ? *result : *s;
+ }
+
+ Handle<SeqString> result; // Same length as input.
+ if (s->IsOneByteRepresentation()) {
+ result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
+ } else {
+ result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
+ }
+
+ Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
+ if (answer->IsException() || answer->IsString()) return answer;
+
+ DCHECK(answer->IsSmi());
+ length = Smi::cast(answer)->value();
+ if (s->IsOneByteRepresentation() && length > 0) {
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewRawOneByteString(length));
+ } else {
+ if (length < 0) length = -length;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewRawTwoByteString(length));
+ }
+ return ConvertCaseHelper(isolate, *s, *result, length, mapping);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringToLowerCase) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
+ return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping());
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringToUpperCase) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
+ return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping());
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringTrim) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2);
+
+ string = String::Flatten(string);
+ int length = string->length();
+
+ int left = 0;
+ UnicodeCache* unicode_cache = isolate->unicode_cache();
+ if (trimLeft) {
+ while (left < length &&
+ unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) {
+ left++;
+ }
+ }
+
+ int right = length;
+ if (trimRight) {
+ while (
+ right > left &&
+ unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) {
+ right--;
+ }
+ }
+
+ return *isolate->factory()->NewSubString(string, left, right);
+}
+
+
+RUNTIME_FUNCTION(Runtime_TruncateString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0);
+ CONVERT_INT32_ARG_CHECKED(new_length, 1);
+ RUNTIME_ASSERT(new_length >= 0);
+ return *SeqString::Truncate(string, new_length);
+}
+
+
+RUNTIME_FUNCTION(Runtime_NewString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_INT32_ARG_CHECKED(length, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1);
+ if (length == 0) return isolate->heap()->empty_string();
+ Handle<String> result;
+ if (is_one_byte) {
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewRawOneByteString(length));
+ } else {
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, isolate->factory()->NewRawTwoByteString(length));
+ }
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StringEquals) {
+ HandleScope handle_scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
+
+ bool not_equal = !String::Equals(x, y);
+ // This is slightly convoluted because the value that signifies
+ // equality is 0 and inequality is 1 so we have to negate the result
+ // from String::Equals.
+ DCHECK(not_equal == 0 || not_equal == 1);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(NOT_EQUAL == 1);
+ return Smi::FromInt(not_equal);
+}
+
+
+RUNTIME_FUNCTION(Runtime_FlattenString) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
+ return *String::Flatten(str);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_StringCharFromCode) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_CharFromCode(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_StringCharAt) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ if (!args[0]->IsString()) return Smi::FromInt(0);
+ if (!args[1]->IsNumber()) return Smi::FromInt(0);
+ if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string();
+ Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
+ if (code->IsNaN()) return isolate->heap()->empty_string();
+ return __RT_impl_Runtime_CharFromCode(Arguments(1, &code), isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_OneByteSeqStringSetChar) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_INT32_ARG_CHECKED(index, 0);
+ CONVERT_INT32_ARG_CHECKED(value, 1);
+ CONVERT_ARG_CHECKED(SeqOneByteString, string, 2);
+ string->SeqOneByteStringSet(index, value);
+ return string;
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_TwoByteSeqStringSetChar) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_INT32_ARG_CHECKED(index, 0);
+ CONVERT_INT32_ARG_CHECKED(value, 1);
+ CONVERT_ARG_CHECKED(SeqTwoByteString, string, 2);
+ string->SeqTwoByteStringSet(index, value);
+ return string;
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_StringCompare) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_StringCompare(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_StringCharCodeAt) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ if (!args[0]->IsString()) return isolate->heap()->undefined_value();
+ if (!args[1]->IsNumber()) return isolate->heap()->undefined_value();
+ if (std::isinf(args.number_at(1))) return isolate->heap()->nan_value();
+ return __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_SubString) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_SubString(args, isolate);
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_StringAdd) {
+ SealHandleScope shs(isolate);
+ return __RT_impl_Runtime_StringAdd(args, isolate);
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc
new file mode 100644
index 0000000..eac3c61
--- /dev/null
+++ b/src/runtime/runtime-test.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/deoptimizer.h"
+#include "src/full-codegen.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ if (!function->IsOptimized()) return isolate->heap()->undefined_value();
+
+ // TODO(turbofan): Deoptimization is not supported yet.
+ if (function->code()->is_turbofanned() && !FLAG_turbo_deoptimization) {
+ return isolate->heap()->undefined_value();
+ }
+
+ Deoptimizer::DeoptimizeFunction(*function);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+#if defined(USE_SIMULATOR)
+ return isolate->heap()->true_value();
+#else
+ return isolate->heap()->false_value();
+#endif
+}
+
+
+RUNTIME_FUNCTION(Runtime_IsConcurrentRecompilationSupported) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ return isolate->heap()->ToBoolean(
+ isolate->concurrent_recompilation_enabled());
+}
+
+
+RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ // The following two assertions are lifted from the DCHECKs inside
+ // JSFunction::MarkForOptimization().
+ RUNTIME_ASSERT(!function->shared()->is_generator());
+ RUNTIME_ASSERT(function->shared()->allows_lazy_compilation() ||
+ (function->code()->kind() == Code::FUNCTION &&
+ function->code()->optimizable()));
+
+ // If the function is optimized, just return.
+ if (function->IsOptimized()) return isolate->heap()->undefined_value();
+
+ function->MarkForOptimization();
+
+ Code* unoptimized = function->shared()->code();
+ if (args.length() == 2 && unoptimized->kind() == Code::FUNCTION) {
+ CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
+ if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("osr")) && FLAG_use_osr) {
+ // Start patching from the currently patched loop nesting level.
+ DCHECK(BackEdgeTable::Verify(isolate, unoptimized));
+ isolate->runtime_profiler()->AttemptOnStackReplacement(
+ *function, Code::kMaxLoopNestingMarker);
+ } else if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("concurrent")) &&
+ isolate->concurrent_recompilation_enabled()) {
+ function->MarkForConcurrentOptimization();
+ }
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ function->shared()->set_optimization_disabled(true);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
+ if (!isolate->use_crankshaft()) {
+ return Smi::FromInt(4); // 4 == "never".
+ }
+ bool sync_with_compiler_thread = true;
+ if (args.length() == 2) {
+ CONVERT_ARG_HANDLE_CHECKED(String, sync, 1);
+ if (sync->IsOneByteEqualTo(STATIC_CHAR_VECTOR("no sync"))) {
+ sync_with_compiler_thread = false;
+ }
+ }
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ if (isolate->concurrent_recompilation_enabled() &&
+ sync_with_compiler_thread) {
+ while (function->IsInOptimizationQueue()) {
+ isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
+ base::OS::Sleep(50);
+ }
+ }
+ if (FLAG_always_opt) {
+ // We may have always opt, but that is more best-effort than a real
+ // promise, so we still say "no" if it is not optimized.
+ return function->IsOptimized() ? Smi::FromInt(3) // 3 == "always".
+ : Smi::FromInt(2); // 2 == "no".
+ }
+ if (FLAG_deopt_every_n_times) {
+ return Smi::FromInt(6); // 6 == "maybe deopted".
+ }
+ if (function->IsOptimized() && function->code()->is_turbofanned()) {
+ return Smi::FromInt(7); // 7 == "TurboFan compiler".
+ }
+ return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
+ : Smi::FromInt(2); // 2 == "no".
+}
+
+
+RUNTIME_FUNCTION(Runtime_UnblockConcurrentRecompilation) {
+ DCHECK(args.length() == 0);
+ RUNTIME_ASSERT(FLAG_block_concurrent_recompilation);
+ RUNTIME_ASSERT(isolate->concurrent_recompilation_enabled());
+ isolate->optimizing_compiler_thread()->Unblock();
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetOptimizationCount) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ return Smi::FromInt(function->shared()->opt_count());
+}
+
+
+RUNTIME_FUNCTION(Runtime_ClearFunctionTypeFeedback) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ function->shared()->ClearTypeFeedbackInfo();
+ Code* unoptimized = function->shared()->code();
+ if (unoptimized->kind() == Code::FUNCTION) {
+ unoptimized->ClearInlineCaches();
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+ isolate->heap()->NotifyContextDisposed();
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2 || args.length() == 3);
+#ifdef DEBUG
+ CONVERT_SMI_ARG_CHECKED(interval, 0);
+ CONVERT_SMI_ARG_CHECKED(timeout, 1);
+ isolate->heap()->set_allocation_timeout(timeout);
+ FLAG_gc_interval = interval;
+ if (args.length() == 3) {
+ // Enable/disable inline allocation if requested.
+ CONVERT_BOOLEAN_ARG_CHECKED(inline_allocation, 2);
+ if (inline_allocation) {
+ isolate->heap()->EnableInlineAllocation();
+ } else {
+ isolate->heap()->DisableInlineAllocation();
+ }
+ }
+#endif
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugPrint) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+
+ OFStream os(stdout);
+#ifdef DEBUG
+ if (args[0]->IsString()) {
+ // If we have a string, assume it's a code "marker"
+ // and print some interesting cpu debugging info.
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ os << "fp = " << frame->fp() << ", sp = " << frame->sp()
+ << ", caller_sp = " << frame->caller_sp() << ": ";
+ } else {
+ os << "DebugPrint: ";
+ }
+ args[0]->Print(os);
+ if (args[0]->IsHeapObject()) {
+ os << "\n";
+ HeapObject::cast(args[0])->map()->Print(os);
+ }
+#else
+ // ShortPrint is available in release mode. Print is not.
+ os << Brief(args[0]);
+#endif
+ os << endl;
+
+ return args[0]; // return TOS
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugTrace) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ isolate->PrintStack(stdout);
+ return isolate->heap()->undefined_value();
+}
+
+
+// This will not allocate (flatten the string), but it may run
+// very slowly for very deeply nested ConsStrings. For debugging use only.
+RUNTIME_FUNCTION(Runtime_GlobalPrint) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(String, string, 0);
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(string, &op);
+ while (stream.HasMore()) {
+ uint16_t character = stream.GetNext();
+ PrintF("%c", character);
+ }
+ return string;
+}
+
+
+RUNTIME_FUNCTION(Runtime_SystemBreak) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ base::OS::DebugBreak();
+ return isolate->heap()->undefined_value();
+}
+
+
+// Sets a v8 flag.
+RUNTIME_FUNCTION(Runtime_SetFlags) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(String, arg, 0);
+ SmartArrayPointer<char> flags =
+ arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ FlagList::SetFlagsFromString(flags.get(), StrLength(flags.get()));
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_Abort) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_SMI_ARG_CHECKED(message_id, 0);
+ const char* message =
+ GetBailoutReason(static_cast<BailoutReason>(message_id));
+ base::OS::PrintError("abort: %s\n", message);
+ isolate->PrintStack(stderr);
+ base::OS::Abort();
+ UNREACHABLE();
+ return NULL;
+}
+
+
+RUNTIME_FUNCTION(Runtime_AbortJS) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, message, 0);
+ base::OS::PrintError("abort: %s\n", message->ToCString().get());
+ isolate->PrintStack(stderr);
+ base::OS::Abort();
+ UNREACHABLE();
+ return NULL;
+}
+
+
+RUNTIME_FUNCTION(Runtime_HaveSameMap) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSObject, obj1, 0);
+ CONVERT_ARG_CHECKED(JSObject, obj2, 1);
+ return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
+}
+
+
+#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
+ RUNTIME_FUNCTION(Runtime_Has##Name) { \
+ CONVERT_ARG_CHECKED(JSObject, obj, 0); \
+ return isolate->heap()->ToBoolean(obj->Has##Name()); \
+ }
+
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SloppyArgumentsElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements)
+// Properties test sitting with elements tests - not fooling anyone.
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastProperties)
+
+#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc
new file mode 100644
index 0000000..c138a4f
--- /dev/null
+++ b/src/runtime/runtime-typedarray.cc
@@ -0,0 +1,760 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+
+namespace v8 {
+namespace internal {
+
+void Runtime::FreeArrayBuffer(Isolate* isolate,
+ JSArrayBuffer* phantom_array_buffer) {
+ if (phantom_array_buffer->should_be_freed()) {
+ DCHECK(phantom_array_buffer->is_external());
+ free(phantom_array_buffer->backing_store());
+ }
+ if (phantom_array_buffer->is_external()) return;
+
+ size_t allocated_length =
+ NumberToSize(isolate, phantom_array_buffer->byte_length());
+
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(
+ -static_cast<int64_t>(allocated_length));
+ CHECK(V8::ArrayBufferAllocator() != NULL);
+ V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(),
+ allocated_length);
+}
+
+
+void Runtime::SetupArrayBuffer(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ bool is_external, void* data,
+ size_t allocated_length) {
+ DCHECK(array_buffer->GetInternalFieldCount() ==
+ v8::ArrayBuffer::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
+ array_buffer->SetInternalField(i, Smi::FromInt(0));
+ }
+ array_buffer->set_backing_store(data);
+ array_buffer->set_flag(Smi::FromInt(0));
+ array_buffer->set_is_external(is_external);
+
+ Handle<Object> byte_length =
+ isolate->factory()->NewNumberFromSize(allocated_length);
+ CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
+ array_buffer->set_byte_length(*byte_length);
+
+ array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
+ isolate->heap()->set_array_buffers_list(*array_buffer);
+ array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
+}
+
+
+bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t allocated_length,
+ bool initialize) {
+ void* data;
+ CHECK(V8::ArrayBufferAllocator() != NULL);
+ if (allocated_length != 0) {
+ if (initialize) {
+ data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
+ } else {
+ data =
+ V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
+ }
+ if (data == NULL) return false;
+ } else {
+ data = NULL;
+ }
+
+ SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
+
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(allocated_length);
+
+ return true;
+}
+
+
+void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
+ Isolate* isolate = array_buffer->GetIsolate();
+ for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
+ !view_obj->IsUndefined();) {
+ Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
+ if (view->IsJSTypedArray()) {
+ JSTypedArray::cast(*view)->Neuter();
+ } else if (view->IsJSDataView()) {
+ JSDataView::cast(*view)->Neuter();
+ } else {
+ UNREACHABLE();
+ }
+ view_obj = handle(view->weak_next(), isolate);
+ }
+ array_buffer->Neuter();
+}
+
+
+RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1);
+ if (!holder->byte_length()->IsUndefined()) {
+ // ArrayBuffer is already initialized; probably a fuzz test.
+ return *holder;
+ }
+ size_t allocated_length = 0;
+ if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+ if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder,
+ allocated_length)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
+ return holder->byte_length();
+}
+
+
+RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
+ RUNTIME_ASSERT(!source.is_identical_to(target));
+ size_t start = 0;
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start));
+ size_t target_length = NumberToSize(isolate, target->byte_length());
+
+ if (target_length == 0) return isolate->heap()->undefined_value();
+
+ size_t source_byte_length = NumberToSize(isolate, source->byte_length());
+ RUNTIME_ASSERT(start <= source_byte_length);
+ RUNTIME_ASSERT(source_byte_length - start >= target_length);
+ uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
+ uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
+ CopyBytes(target_data, source_data + start, target_length);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_CHECKED(Object, object, 0);
+ return isolate->heap()->ToBoolean(object->IsJSArrayBufferView());
+}
+
+
+RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
+ if (array_buffer->backing_store() == NULL) {
+ CHECK(Smi::FromInt(0) == array_buffer->byte_length());
+ return isolate->heap()->undefined_value();
+ }
+ DCHECK(!array_buffer->is_external());
+ void* backing_store = array_buffer->backing_store();
+ size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
+ array_buffer->set_is_external(true);
+ Runtime::NeuterArrayBuffer(array_buffer);
+ V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
+ return isolate->heap()->undefined_value();
+}
+
+
+void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
+ ElementsKind* external_elements_kind,
+ ElementsKind* fixed_elements_kind,
+ size_t* element_size) {
+ switch (arrayId) {
+#define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \
+ case ARRAY_ID_##TYPE: \
+ *array_type = kExternal##Type##Array; \
+ *external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \
+ *fixed_elements_kind = TYPE##_ELEMENTS; \
+ *element_size = size; \
+ break;
+
+ TYPED_ARRAYS(ARRAY_ID_CASE)
+#undef ARRAY_ID_CASE
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 5);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
+ CONVERT_SMI_ARG_CHECKED(arrayId, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
+
+ RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
+ arrayId <= Runtime::ARRAY_ID_LAST);
+
+ ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
+ size_t element_size = 1; // Bogus initialization.
+ ElementsKind external_elements_kind =
+ EXTERNAL_INT8_ELEMENTS; // Bogus initialization.
+ ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
+ Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
+ &fixed_elements_kind, &element_size);
+ RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
+
+ size_t byte_offset = 0;
+ size_t byte_length = 0;
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset));
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length));
+
+ if (maybe_buffer->IsJSArrayBuffer()) {
+ Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
+ size_t array_buffer_byte_length =
+ NumberToSize(isolate, buffer->byte_length());
+ RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length);
+ RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length);
+ } else {
+ RUNTIME_ASSERT(maybe_buffer->IsNull());
+ }
+
+ RUNTIME_ASSERT(byte_length % element_size == 0);
+ size_t length = byte_length / element_size;
+
+ if (length > static_cast<unsigned>(Smi::kMaxValue)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("invalid_typed_array_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ // All checks are done, now we can modify objects.
+
+ DCHECK(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+ Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
+ holder->set_length(*length_obj);
+ holder->set_byte_offset(*byte_offset_object);
+ holder->set_byte_length(*byte_length_object);
+
+ if (!maybe_buffer->IsNull()) {
+ Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
+ holder->set_buffer(*buffer);
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
+ static_cast<int>(length), array_type,
+ static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
+ Handle<Map> map =
+ JSObject::GetElementsTransitionMap(holder, external_elements_kind);
+ JSObject::SetMapAndElements(holder, map, elements);
+ DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind()));
+ } else {
+ holder->set_buffer(Smi::FromInt(0));
+ holder->set_weak_next(isolate->heap()->undefined_value());
+ Handle<FixedTypedArrayBase> elements =
+ isolate->factory()->NewFixedTypedArray(static_cast<int>(length),
+ array_type);
+ holder->set_elements(*elements);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+// Initializes a typed array from an array-like object.
+// If an array-like object happens to be a typed array of the same type,
+// initializes backing store using memove.
+//
+// Returns true if backing store was initialized or false otherwise.
+RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
+ CONVERT_SMI_ARG_CHECKED(arrayId, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
+
+ RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
+ arrayId <= Runtime::ARRAY_ID_LAST);
+
+ ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
+ size_t element_size = 1; // Bogus initialization.
+ ElementsKind external_elements_kind =
+ EXTERNAL_INT8_ELEMENTS; // Bogus intialization.
+ ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
+ Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
+ &fixed_elements_kind, &element_size);
+
+ RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
+
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ if (source->IsJSTypedArray() &&
+ JSTypedArray::cast(*source)->type() == array_type) {
+ length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate);
+ }
+ size_t length = 0;
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length));
+
+ if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
+ (length > (kMaxInt / element_size))) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("invalid_typed_array_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+ size_t byte_length = length * element_size;
+
+ DCHECK(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+
+ // NOTE: not initializing backing store.
+ // We assume that the caller of this function will initialize holder
+ // with the loop
+ // for(i = 0; i < length; i++) { holder[i] = source[i]; }
+ // We assume that the caller of this function is always a typed array
+ // constructor.
+ // If source is a typed array, this loop will always run to completion,
+ // so we are sure that the backing store will be initialized.
+ // Otherwise, the indexing operation might throw, so the loop will not
+ // run to completion and the typed array might remain partly initialized.
+ // However we further assume that the caller of this function is a typed array
+ // constructor, and the exception will propagate out of the constructor,
+ // therefore uninitialized memory will not be accessible by a user program.
+ //
+ // TODO(dslomov): revise this once we support subclassing.
+
+ if (!Runtime::SetupArrayBufferAllocatingData(isolate, buffer, byte_length,
+ false)) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ holder->set_buffer(*buffer);
+ holder->set_byte_offset(Smi::FromInt(0));
+ Handle<Object> byte_length_obj(
+ isolate->factory()->NewNumberFromSize(byte_length));
+ holder->set_byte_length(*byte_length_obj);
+ holder->set_length(*length_obj);
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
+ static_cast<int>(length), array_type,
+ static_cast<uint8_t*>(buffer->backing_store()));
+ Handle<Map> map =
+ JSObject::GetElementsTransitionMap(holder, external_elements_kind);
+ JSObject::SetMapAndElements(holder, map, elements);
+
+ if (source->IsJSTypedArray()) {
+ Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
+
+ if (typed_array->type() == holder->type()) {
+ uint8_t* backing_store =
+ static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
+ size_t source_byte_offset =
+ NumberToSize(isolate, typed_array->byte_offset());
+ memcpy(buffer->backing_store(), backing_store + source_byte_offset,
+ byte_length);
+ return isolate->heap()->true_value();
+ }
+ }
+
+ return isolate->heap()->false_value();
+}
+
+
+#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
+ RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
+ HandleScope scope(isolate); \
+ DCHECK(args.length() == 1); \
+ CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
+ return holder->accessor(); \
+ }
+
+BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
+BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
+BUFFER_VIEW_GETTER(TypedArray, Length, length)
+BUFFER_VIEW_GETTER(DataView, Buffer, buffer)
+
+#undef BUFFER_VIEW_GETTER
+
+RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
+ return *holder->GetBuffer();
+}
+
+
+// Return codes for Runtime_TypedArraySetFastCases.
+// Should be synchronized with typedarray.js natives.
+enum TypedArraySetResultCodes {
+ // Set from typed array of the same type.
+ // This is processed by TypedArraySetFastCases
+ TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
+ // Set from typed array of the different type, overlapping in memory.
+ TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
+ // Set from typed array of the different type, non-overlapping.
+ TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
+ // Set from non-typed array.
+ TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
+};
+
+
+RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ if (!args[0]->IsJSTypedArray()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate,
+ NewTypeError("not_typed_array", HandleVector<Object>(NULL, 0)));
+ }
+
+ if (!args[1]->IsJSTypedArray())
+ return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
+
+ Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
+ Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
+ size_t offset = 0;
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset));
+ size_t target_length = NumberToSize(isolate, target->length());
+ size_t source_length = NumberToSize(isolate, source->length());
+ size_t target_byte_length = NumberToSize(isolate, target->byte_length());
+ size_t source_byte_length = NumberToSize(isolate, source->byte_length());
+ if (offset > target_length || offset + source_length > target_length ||
+ offset + source_length < offset) { // overflow
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewRangeError("typed_array_set_source_too_large",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ size_t target_offset = NumberToSize(isolate, target->byte_offset());
+ size_t source_offset = NumberToSize(isolate, source->byte_offset());
+ uint8_t* target_base =
+ static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
+ target_offset;
+ uint8_t* source_base =
+ static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
+ source_offset;
+
+ // Typed arrays of the same type: use memmove.
+ if (target->type() == source->type()) {
+ memmove(target_base + offset * target->element_size(), source_base,
+ source_byte_length);
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
+ }
+
+ // Typed arrays of different types over the same backing store
+ if ((source_base <= target_base &&
+ source_base + source_byte_length > target_base) ||
+ (target_base <= source_base &&
+ target_base + target_byte_length > source_base)) {
+ // We do not support overlapping ArrayBuffers
+ DCHECK(target->GetBuffer()->backing_store() ==
+ source->GetBuffer()->backing_store());
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
+ } else { // Non-overlapping typed arrays
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
+ }
+}
+
+
+RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
+ DCHECK(args.length() == 0);
+ DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap +
+ FixedTypedArrayBase::kDataOffset);
+ return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
+}
+
+
+RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
+
+ DCHECK(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+ size_t buffer_length = 0;
+ size_t offset = 0;
+ size_t length = 0;
+ RUNTIME_ASSERT(
+ TryNumberToSize(isolate, buffer->byte_length(), &buffer_length));
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset));
+ RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length));
+
+ // TODO(jkummerow): When we have a "safe numerics" helper class, use it here.
+ // Entire range [offset, offset + length] must be in bounds.
+ RUNTIME_ASSERT(offset <= buffer_length);
+ RUNTIME_ASSERT(offset + length <= buffer_length);
+ // No overflow.
+ RUNTIME_ASSERT(offset + length >= offset);
+
+ holder->set_buffer(*buffer);
+ holder->set_byte_offset(*byte_offset);
+ holder->set_byte_length(*byte_length);
+
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+inline static bool NeedToFlipBytes(bool is_little_endian) {
+#ifdef V8_TARGET_LITTLE_ENDIAN
+ return !is_little_endian;
+#else
+ return is_little_endian;
+#endif
+}
+
+
+template <int n>
+inline void CopyBytes(uint8_t* target, uint8_t* source) {
+ for (int i = 0; i < n; i++) {
+ *(target++) = *(source++);
+ }
+}
+
+
+template <int n>
+inline void FlipBytes(uint8_t* target, uint8_t* source) {
+ source = source + (n - 1);
+ for (int i = 0; i < n; i++) {
+ *(target++) = *(source--);
+ }
+}
+
+
+template <typename T>
+inline static bool DataViewGetValue(Isolate* isolate,
+ Handle<JSDataView> data_view,
+ Handle<Object> byte_offset_obj,
+ bool is_little_endian, T* result) {
+ size_t byte_offset = 0;
+ if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
+ return false;
+ }
+ Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
+
+ size_t data_view_byte_offset =
+ NumberToSize(isolate, data_view->byte_offset());
+ size_t data_view_byte_length =
+ NumberToSize(isolate, data_view->byte_length());
+ if (byte_offset + sizeof(T) > data_view_byte_length ||
+ byte_offset + sizeof(T) < byte_offset) { // overflow
+ return false;
+ }
+
+ union Value {
+ T data;
+ uint8_t bytes[sizeof(T)];
+ };
+
+ Value value;
+ size_t buffer_offset = data_view_byte_offset + byte_offset;
+ DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
+ buffer_offset + sizeof(T));
+ uint8_t* source =
+ static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
+ if (NeedToFlipBytes(is_little_endian)) {
+ FlipBytes<sizeof(T)>(value.bytes, source);
+ } else {
+ CopyBytes<sizeof(T)>(value.bytes, source);
+ }
+ *result = value.data;
+ return true;
+}
+
+
+template <typename T>
+static bool DataViewSetValue(Isolate* isolate, Handle<JSDataView> data_view,
+ Handle<Object> byte_offset_obj,
+ bool is_little_endian, T data) {
+ size_t byte_offset = 0;
+ if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
+ return false;
+ }
+ Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
+
+ size_t data_view_byte_offset =
+ NumberToSize(isolate, data_view->byte_offset());
+ size_t data_view_byte_length =
+ NumberToSize(isolate, data_view->byte_length());
+ if (byte_offset + sizeof(T) > data_view_byte_length ||
+ byte_offset + sizeof(T) < byte_offset) { // overflow
+ return false;
+ }
+
+ union Value {
+ T data;
+ uint8_t bytes[sizeof(T)];
+ };
+
+ Value value;
+ value.data = data;
+ size_t buffer_offset = data_view_byte_offset + byte_offset;
+ DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
+ buffer_offset + sizeof(T));
+ uint8_t* target =
+ static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
+ if (NeedToFlipBytes(is_little_endian)) {
+ FlipBytes<sizeof(T)>(target, value.bytes);
+ } else {
+ CopyBytes<sizeof(T)>(target, value.bytes);
+ }
+ return true;
+}
+
+
+#define DATA_VIEW_GETTER(TypeName, Type, Converter) \
+ RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) { \
+ HandleScope scope(isolate); \
+ DCHECK(args.length() == 3); \
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
+ CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \
+ Type result; \
+ if (DataViewGetValue(isolate, holder, offset, is_little_endian, \
+ &result)) { \
+ return *isolate->factory()->Converter(result); \
+ } else { \
+ THROW_NEW_ERROR_RETURN_FAILURE( \
+ isolate, NewRangeError("invalid_data_view_accessor_offset", \
+ HandleVector<Object>(NULL, 0))); \
+ } \
+ }
+
+DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint)
+DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt)
+DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint)
+DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt)
+DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint)
+DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt)
+DATA_VIEW_GETTER(Float32, float, NewNumber)
+DATA_VIEW_GETTER(Float64, double, NewNumber)
+
+#undef DATA_VIEW_GETTER
+
+
+template <typename T>
+static T DataViewConvertValue(double value);
+
+
+template <>
+int8_t DataViewConvertValue<int8_t>(double value) {
+ return static_cast<int8_t>(DoubleToInt32(value));
+}
+
+
+template <>
+int16_t DataViewConvertValue<int16_t>(double value) {
+ return static_cast<int16_t>(DoubleToInt32(value));
+}
+
+
+template <>
+int32_t DataViewConvertValue<int32_t>(double value) {
+ return DoubleToInt32(value);
+}
+
+
+template <>
+uint8_t DataViewConvertValue<uint8_t>(double value) {
+ return static_cast<uint8_t>(DoubleToUint32(value));
+}
+
+
+template <>
+uint16_t DataViewConvertValue<uint16_t>(double value) {
+ return static_cast<uint16_t>(DoubleToUint32(value));
+}
+
+
+template <>
+uint32_t DataViewConvertValue<uint32_t>(double value) {
+ return DoubleToUint32(value);
+}
+
+
+template <>
+float DataViewConvertValue<float>(double value) {
+ return static_cast<float>(value);
+}
+
+
+template <>
+double DataViewConvertValue<double>(double value) {
+ return value;
+}
+
+
+#define DATA_VIEW_SETTER(TypeName, Type) \
+ RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) { \
+ HandleScope scope(isolate); \
+ DCHECK(args.length() == 4); \
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
+ CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2); \
+ CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \
+ Type v = DataViewConvertValue<Type>(value->Number()); \
+ if (DataViewSetValue(isolate, holder, offset, is_little_endian, v)) { \
+ return isolate->heap()->undefined_value(); \
+ } else { \
+ THROW_NEW_ERROR_RETURN_FAILURE( \
+ isolate, NewRangeError("invalid_data_view_accessor_offset", \
+ HandleVector<Object>(NULL, 0))); \
+ } \
+ }
+
+DATA_VIEW_SETTER(Uint8, uint8_t)
+DATA_VIEW_SETTER(Int8, int8_t)
+DATA_VIEW_SETTER(Uint16, uint16_t)
+DATA_VIEW_SETTER(Int16, int16_t)
+DATA_VIEW_SETTER(Uint32, uint32_t)
+DATA_VIEW_SETTER(Int32, int32_t)
+DATA_VIEW_SETTER(Float32, float)
+DATA_VIEW_SETTER(Float64, double)
+
+#undef DATA_VIEW_SETTER
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime-uri.cc b/src/runtime/runtime-uri.cc
new file mode 100644
index 0000000..10e21be
--- /dev/null
+++ b/src/runtime/runtime-uri.cc
@@ -0,0 +1,309 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/arguments.h"
+#include "src/conversions.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+#include "src/string-search.h"
+#include "src/utils.h"
+
+
+namespace v8 {
+namespace internal {
+
+template <typename Char>
+static INLINE(Vector<const Char> GetCharVector(Handle<String> string));
+
+
+template <>
+Vector<const uint8_t> GetCharVector(Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ DCHECK(flat.IsOneByte());
+ return flat.ToOneByteVector();
+}
+
+
+template <>
+Vector<const uc16> GetCharVector(Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ DCHECK(flat.IsTwoByte());
+ return flat.ToUC16Vector();
+}
+
+
+class URIUnescape : public AllStatic {
+ public:
+ template <typename Char>
+ MUST_USE_RESULT static MaybeHandle<String> Unescape(Isolate* isolate,
+ Handle<String> source);
+
+ private:
+ static const signed char kHexValue['g'];
+
+ template <typename Char>
+ MUST_USE_RESULT static MaybeHandle<String> UnescapeSlow(Isolate* isolate,
+ Handle<String> string,
+ int start_index);
+
+ static INLINE(int TwoDigitHex(uint16_t character1, uint16_t character2));
+
+ template <typename Char>
+ static INLINE(int UnescapeChar(Vector<const Char> vector, int i, int length,
+ int* step));
+};
+
+
+const signed char URIUnescape::kHexValue[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15};
+
+
+template <typename Char>
+MaybeHandle<String> URIUnescape::Unescape(Isolate* isolate,
+ Handle<String> source) {
+ int index;
+ {
+ DisallowHeapAllocation no_allocation;
+ StringSearch<uint8_t, Char> search(isolate, STATIC_CHAR_VECTOR("%"));
+ index = search.Search(GetCharVector<Char>(source), 0);
+ if (index < 0) return source;
+ }
+ return UnescapeSlow<Char>(isolate, source, index);
+}
+
+
+template <typename Char>
+MaybeHandle<String> URIUnescape::UnescapeSlow(Isolate* isolate,
+ Handle<String> string,
+ int start_index) {
+ bool one_byte = true;
+ int length = string->length();
+
+ int unescaped_length = 0;
+ {
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; unescaped_length++) {
+ int step;
+ if (UnescapeChar(vector, i, length, &step) >
+ String::kMaxOneByteCharCode) {
+ one_byte = false;
+ }
+ i += step;
+ }
+ }
+
+ DCHECK(start_index < length);
+ Handle<String> first_part =
+ isolate->factory()->NewProperSubString(string, 0, start_index);
+
+ int dest_position = 0;
+ Handle<String> second_part;
+ DCHECK(unescaped_length <= String::kMaxLength);
+ if (one_byte) {
+ Handle<SeqOneByteString> dest = isolate->factory()
+ ->NewRawOneByteString(unescaped_length)
+ .ToHandleChecked();
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; dest_position++) {
+ int step;
+ dest->SeqOneByteStringSet(dest_position,
+ UnescapeChar(vector, i, length, &step));
+ i += step;
+ }
+ second_part = dest;
+ } else {
+ Handle<SeqTwoByteString> dest = isolate->factory()
+ ->NewRawTwoByteString(unescaped_length)
+ .ToHandleChecked();
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; dest_position++) {
+ int step;
+ dest->SeqTwoByteStringSet(dest_position,
+ UnescapeChar(vector, i, length, &step));
+ i += step;
+ }
+ second_part = dest;
+ }
+ return isolate->factory()->NewConsString(first_part, second_part);
+}
+
+
+int URIUnescape::TwoDigitHex(uint16_t character1, uint16_t character2) {
+ if (character1 > 'f') return -1;
+ int hi = kHexValue[character1];
+ if (hi == -1) return -1;
+ if (character2 > 'f') return -1;
+ int lo = kHexValue[character2];
+ if (lo == -1) return -1;
+ return (hi << 4) + lo;
+}
+
+
+template <typename Char>
+int URIUnescape::UnescapeChar(Vector<const Char> vector, int i, int length,
+ int* step) {
+ uint16_t character = vector[i];
+ int32_t hi = 0;
+ int32_t lo = 0;
+ if (character == '%' && i <= length - 6 && vector[i + 1] == 'u' &&
+ (hi = TwoDigitHex(vector[i + 2], vector[i + 3])) != -1 &&
+ (lo = TwoDigitHex(vector[i + 4], vector[i + 5])) != -1) {
+ *step = 6;
+ return (hi << 8) + lo;
+ } else if (character == '%' && i <= length - 3 &&
+ (lo = TwoDigitHex(vector[i + 1], vector[i + 2])) != -1) {
+ *step = 3;
+ return lo;
+ } else {
+ *step = 1;
+ return character;
+ }
+}
+
+
+class URIEscape : public AllStatic {
+ public:
+ template <typename Char>
+ MUST_USE_RESULT static MaybeHandle<String> Escape(Isolate* isolate,
+ Handle<String> string);
+
+ private:
+ static const char kHexChars[17];
+ static const char kNotEscaped[256];
+
+ static bool IsNotEscaped(uint16_t c) { return kNotEscaped[c] != 0; }
+};
+
+
+const char URIEscape::kHexChars[] = "0123456789ABCDEF";
+
+
+// kNotEscaped is generated by the following:
+//
+// #!/bin/perl
+// for (my $i = 0; $i < 256; $i++) {
+// print "\n" if $i % 16 == 0;
+// my $c = chr($i);
+// my $escaped = 1;
+// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
+// print $escaped ? "0, " : "1, ";
+// }
+
+const char URIEscape::kNotEscaped[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+template <typename Char>
+MaybeHandle<String> URIEscape::Escape(Isolate* isolate, Handle<String> string) {
+ DCHECK(string->IsFlat());
+ int escaped_length = 0;
+ int length = string->length();
+
+ {
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = 0; i < length; i++) {
+ uint16_t c = vector[i];
+ if (c >= 256) {
+ escaped_length += 6;
+ } else if (IsNotEscaped(c)) {
+ escaped_length++;
+ } else {
+ escaped_length += 3;
+ }
+
+ // We don't allow strings that are longer than a maximal length.
+ DCHECK(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
+ if (escaped_length > String::kMaxLength) break; // Provoke exception.
+ }
+ }
+
+ // No length change implies no change. Return original string if no change.
+ if (escaped_length == length) return string;
+
+ Handle<SeqOneByteString> dest;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, dest, isolate->factory()->NewRawOneByteString(escaped_length),
+ String);
+ int dest_position = 0;
+
+ {
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = 0; i < length; i++) {
+ uint16_t c = vector[i];
+ if (c >= 256) {
+ dest->SeqOneByteStringSet(dest_position, '%');
+ dest->SeqOneByteStringSet(dest_position + 1, 'u');
+ dest->SeqOneByteStringSet(dest_position + 2, kHexChars[c >> 12]);
+ dest->SeqOneByteStringSet(dest_position + 3, kHexChars[(c >> 8) & 0xf]);
+ dest->SeqOneByteStringSet(dest_position + 4, kHexChars[(c >> 4) & 0xf]);
+ dest->SeqOneByteStringSet(dest_position + 5, kHexChars[c & 0xf]);
+ dest_position += 6;
+ } else if (IsNotEscaped(c)) {
+ dest->SeqOneByteStringSet(dest_position, c);
+ dest_position++;
+ } else {
+ dest->SeqOneByteStringSet(dest_position, '%');
+ dest->SeqOneByteStringSet(dest_position + 1, kHexChars[c >> 4]);
+ dest->SeqOneByteStringSet(dest_position + 2, kHexChars[c & 0xf]);
+ dest_position += 3;
+ }
+ }
+ }
+
+ return dest;
+}
+
+
+RUNTIME_FUNCTION(Runtime_URIEscape) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ Handle<String> string = String::Flatten(source);
+ DCHECK(string->IsFlat());
+ Handle<String> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, string->IsOneByteRepresentationUnderneath()
+ ? URIEscape::Escape<uint8_t>(isolate, source)
+ : URIEscape::Escape<uc16>(isolate, source));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_URIUnescape) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ Handle<String> string = String::Flatten(source);
+ DCHECK(string->IsFlat());
+ Handle<String> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, string->IsOneByteRepresentationUnderneath()
+ ? URIUnescape::Unescape<uint8_t>(isolate, source)
+ : URIUnescape::Unescape<uc16>(isolate, source));
+ return *result;
+}
+}
+} // namespace v8::internal
diff --git a/src/runtime/runtime.cc b/src/runtime/runtime.cc
index 379a38f7..427b821 100644
--- a/src/runtime/runtime.cc
+++ b/src/runtime/runtime.cc
@@ -28,10 +28,6 @@
#include "src/full-codegen.h"
#include "src/global-handles.h"
#include "src/isolate-inl.h"
-#include "src/json-parser.h"
-#include "src/json-stringifier.h"
-#include "src/jsregexp-inl.h"
-#include "src/jsregexp.h"
#include "src/liveedit.h"
#include "src/misc-intrinsics.h"
#include "src/parser.h"
@@ -41,20 +37,11 @@
#include "src/runtime-profiler.h"
#include "src/scopeinfo.h"
#include "src/smart-pointers.h"
-#include "src/string-search.h"
-#include "src/uri.h"
#include "src/utils.h"
#include "src/v8threads.h"
#include "src/vm-state-inl.h"
-#include "third_party/fdlibm/fdlibm.h"
-#ifndef _STLP_VENDOR_CSTD
-// STLPort doesn't import fpclassify and isless into the std namespace.
-using std::fpclassify;
-using std::isless;
-#endif
-
namespace v8 {
namespace internal {
@@ -653,1071 +640,6 @@
}
-void Runtime::FreeArrayBuffer(Isolate* isolate,
- JSArrayBuffer* phantom_array_buffer) {
- if (phantom_array_buffer->should_be_freed()) {
- DCHECK(phantom_array_buffer->is_external());
- free(phantom_array_buffer->backing_store());
- }
- if (phantom_array_buffer->is_external()) return;
-
- size_t allocated_length =
- NumberToSize(isolate, phantom_array_buffer->byte_length());
-
- reinterpret_cast<v8::Isolate*>(isolate)
- ->AdjustAmountOfExternalAllocatedMemory(
- -static_cast<int64_t>(allocated_length));
- CHECK(V8::ArrayBufferAllocator() != NULL);
- V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(),
- allocated_length);
-}
-
-
-void Runtime::SetupArrayBuffer(Isolate* isolate,
- Handle<JSArrayBuffer> array_buffer,
- bool is_external, void* data,
- size_t allocated_length) {
- DCHECK(array_buffer->GetInternalFieldCount() ==
- v8::ArrayBuffer::kInternalFieldCount);
- for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
- array_buffer->SetInternalField(i, Smi::FromInt(0));
- }
- array_buffer->set_backing_store(data);
- array_buffer->set_flag(Smi::FromInt(0));
- array_buffer->set_is_external(is_external);
-
- Handle<Object> byte_length =
- isolate->factory()->NewNumberFromSize(allocated_length);
- CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
- array_buffer->set_byte_length(*byte_length);
-
- array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
- isolate->heap()->set_array_buffers_list(*array_buffer);
- array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
-}
-
-
-bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate,
- Handle<JSArrayBuffer> array_buffer,
- size_t allocated_length,
- bool initialize) {
- void* data;
- CHECK(V8::ArrayBufferAllocator() != NULL);
- if (allocated_length != 0) {
- if (initialize) {
- data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
- } else {
- data =
- V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
- }
- if (data == NULL) return false;
- } else {
- data = NULL;
- }
-
- SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
-
- reinterpret_cast<v8::Isolate*>(isolate)
- ->AdjustAmountOfExternalAllocatedMemory(allocated_length);
-
- return true;
-}
-
-
-void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
- Isolate* isolate = array_buffer->GetIsolate();
- for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
- !view_obj->IsUndefined();) {
- Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
- if (view->IsJSTypedArray()) {
- JSTypedArray::cast(*view)->Neuter();
- } else if (view->IsJSDataView()) {
- JSDataView::cast(*view)->Neuter();
- } else {
- UNREACHABLE();
- }
- view_obj = handle(view->weak_next(), isolate);
- }
- array_buffer->Neuter();
-}
-
-
-RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1);
- if (!holder->byte_length()->IsUndefined()) {
- // ArrayBuffer is already initialized; probably a fuzz test.
- return *holder;
- }
- size_t allocated_length = 0;
- if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("invalid_array_buffer_length",
- HandleVector<Object>(NULL, 0)));
- }
- if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder,
- allocated_length)) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("invalid_array_buffer_length",
- HandleVector<Object>(NULL, 0)));
- }
- return *holder;
-}
-
-
-RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
- return holder->byte_length();
-}
-
-
-RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
- RUNTIME_ASSERT(!source.is_identical_to(target));
- size_t start = 0;
- RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start));
- size_t target_length = NumberToSize(isolate, target->byte_length());
-
- if (target_length == 0) return isolate->heap()->undefined_value();
-
- size_t source_byte_length = NumberToSize(isolate, source->byte_length());
- RUNTIME_ASSERT(start <= source_byte_length);
- RUNTIME_ASSERT(source_byte_length - start >= target_length);
- uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
- uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
- CopyBytes(target_data, source_data + start, target_length);
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(Object, object, 0);
- return isolate->heap()->ToBoolean(object->IsJSArrayBufferView());
-}
-
-
-RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
- if (array_buffer->backing_store() == NULL) {
- CHECK(Smi::FromInt(0) == array_buffer->byte_length());
- return isolate->heap()->undefined_value();
- }
- DCHECK(!array_buffer->is_external());
- void* backing_store = array_buffer->backing_store();
- size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
- array_buffer->set_is_external(true);
- Runtime::NeuterArrayBuffer(array_buffer);
- V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
- return isolate->heap()->undefined_value();
-}
-
-
-void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
- ElementsKind* external_elements_kind,
- ElementsKind* fixed_elements_kind,
- size_t* element_size) {
- switch (arrayId) {
-#define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \
- case ARRAY_ID_##TYPE: \
- *array_type = kExternal##Type##Array; \
- *external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \
- *fixed_elements_kind = TYPE##_ELEMENTS; \
- *element_size = size; \
- break;
-
- TYPED_ARRAYS(ARRAY_ID_CASE)
-#undef ARRAY_ID_CASE
-
- default:
- UNREACHABLE();
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 5);
- CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
- CONVERT_SMI_ARG_CHECKED(arrayId, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
-
- RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
- arrayId <= Runtime::ARRAY_ID_LAST);
-
- ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
- size_t element_size = 1; // Bogus initialization.
- ElementsKind external_elements_kind =
- EXTERNAL_INT8_ELEMENTS; // Bogus initialization.
- ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
- Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
- &fixed_elements_kind, &element_size);
- RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
-
- size_t byte_offset = 0;
- size_t byte_length = 0;
- RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset));
- RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length));
-
- if (maybe_buffer->IsJSArrayBuffer()) {
- Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
- size_t array_buffer_byte_length =
- NumberToSize(isolate, buffer->byte_length());
- RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length);
- RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length);
- } else {
- RUNTIME_ASSERT(maybe_buffer->IsNull());
- }
-
- RUNTIME_ASSERT(byte_length % element_size == 0);
- size_t length = byte_length / element_size;
-
- if (length > static_cast<unsigned>(Smi::kMaxValue)) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("invalid_typed_array_length",
- HandleVector<Object>(NULL, 0)));
- }
-
- // All checks are done, now we can modify objects.
-
- DCHECK(holder->GetInternalFieldCount() ==
- v8::ArrayBufferView::kInternalFieldCount);
- for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
- holder->SetInternalField(i, Smi::FromInt(0));
- }
- Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
- holder->set_length(*length_obj);
- holder->set_byte_offset(*byte_offset_object);
- holder->set_byte_length(*byte_length_object);
-
- if (!maybe_buffer->IsNull()) {
- Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
- holder->set_buffer(*buffer);
- holder->set_weak_next(buffer->weak_first_view());
- buffer->set_weak_first_view(*holder);
-
- Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
- static_cast<int>(length), array_type,
- static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
- Handle<Map> map =
- JSObject::GetElementsTransitionMap(holder, external_elements_kind);
- JSObject::SetMapAndElements(holder, map, elements);
- DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind()));
- } else {
- holder->set_buffer(Smi::FromInt(0));
- holder->set_weak_next(isolate->heap()->undefined_value());
- Handle<FixedTypedArrayBase> elements =
- isolate->factory()->NewFixedTypedArray(static_cast<int>(length),
- array_type);
- holder->set_elements(*elements);
- }
- return isolate->heap()->undefined_value();
-}
-
-
-// Initializes a typed array from an array-like object.
-// If an array-like object happens to be a typed array of the same type,
-// initializes backing store using memove.
-//
-// Returns true if backing store was initialized or false otherwise.
-RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 4);
- CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
- CONVERT_SMI_ARG_CHECKED(arrayId, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
-
- RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
- arrayId <= Runtime::ARRAY_ID_LAST);
-
- ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
- size_t element_size = 1; // Bogus initialization.
- ElementsKind external_elements_kind =
- EXTERNAL_INT8_ELEMENTS; // Bogus intialization.
- ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
- Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
- &fixed_elements_kind, &element_size);
-
- RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
-
- Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
- if (source->IsJSTypedArray() &&
- JSTypedArray::cast(*source)->type() == array_type) {
- length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate);
- }
- size_t length = 0;
- RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length));
-
- if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
- (length > (kMaxInt / element_size))) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("invalid_typed_array_length",
- HandleVector<Object>(NULL, 0)));
- }
- size_t byte_length = length * element_size;
-
- DCHECK(holder->GetInternalFieldCount() ==
- v8::ArrayBufferView::kInternalFieldCount);
- for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
- holder->SetInternalField(i, Smi::FromInt(0));
- }
-
- // NOTE: not initializing backing store.
- // We assume that the caller of this function will initialize holder
- // with the loop
- // for(i = 0; i < length; i++) { holder[i] = source[i]; }
- // We assume that the caller of this function is always a typed array
- // constructor.
- // If source is a typed array, this loop will always run to completion,
- // so we are sure that the backing store will be initialized.
- // Otherwise, the indexing operation might throw, so the loop will not
- // run to completion and the typed array might remain partly initialized.
- // However we further assume that the caller of this function is a typed array
- // constructor, and the exception will propagate out of the constructor,
- // therefore uninitialized memory will not be accessible by a user program.
- //
- // TODO(dslomov): revise this once we support subclassing.
-
- if (!Runtime::SetupArrayBufferAllocatingData(isolate, buffer, byte_length,
- false)) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("invalid_array_buffer_length",
- HandleVector<Object>(NULL, 0)));
- }
-
- holder->set_buffer(*buffer);
- holder->set_byte_offset(Smi::FromInt(0));
- Handle<Object> byte_length_obj(
- isolate->factory()->NewNumberFromSize(byte_length));
- holder->set_byte_length(*byte_length_obj);
- holder->set_length(*length_obj);
- holder->set_weak_next(buffer->weak_first_view());
- buffer->set_weak_first_view(*holder);
-
- Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
- static_cast<int>(length), array_type,
- static_cast<uint8_t*>(buffer->backing_store()));
- Handle<Map> map =
- JSObject::GetElementsTransitionMap(holder, external_elements_kind);
- JSObject::SetMapAndElements(holder, map, elements);
-
- if (source->IsJSTypedArray()) {
- Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
-
- if (typed_array->type() == holder->type()) {
- uint8_t* backing_store =
- static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
- size_t source_byte_offset =
- NumberToSize(isolate, typed_array->byte_offset());
- memcpy(buffer->backing_store(), backing_store + source_byte_offset,
- byte_length);
- return isolate->heap()->true_value();
- }
- }
-
- return isolate->heap()->false_value();
-}
-
-
-#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
- RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
- HandleScope scope(isolate); \
- DCHECK(args.length() == 1); \
- CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
- return holder->accessor(); \
- }
-
-BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
-BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
-BUFFER_VIEW_GETTER(TypedArray, Length, length)
-BUFFER_VIEW_GETTER(DataView, Buffer, buffer)
-
-#undef BUFFER_VIEW_GETTER
-
-RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
- return *holder->GetBuffer();
-}
-
-
-// Return codes for Runtime_TypedArraySetFastCases.
-// Should be synchronized with typedarray.js natives.
-enum TypedArraySetResultCodes {
- // Set from typed array of the same type.
- // This is processed by TypedArraySetFastCases
- TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
- // Set from typed array of the different type, overlapping in memory.
- TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
- // Set from typed array of the different type, non-overlapping.
- TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
- // Set from non-typed array.
- TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
-};
-
-
-RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- if (!args[0]->IsJSTypedArray()) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate,
- NewTypeError("not_typed_array", HandleVector<Object>(NULL, 0)));
- }
-
- if (!args[1]->IsJSTypedArray())
- return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
-
- CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
-
- Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
- Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
- size_t offset = 0;
- RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset));
- size_t target_length = NumberToSize(isolate, target->length());
- size_t source_length = NumberToSize(isolate, source->length());
- size_t target_byte_length = NumberToSize(isolate, target->byte_length());
- size_t source_byte_length = NumberToSize(isolate, source->byte_length());
- if (offset > target_length || offset + source_length > target_length ||
- offset + source_length < offset) { // overflow
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError("typed_array_set_source_too_large",
- HandleVector<Object>(NULL, 0)));
- }
-
- size_t target_offset = NumberToSize(isolate, target->byte_offset());
- size_t source_offset = NumberToSize(isolate, source->byte_offset());
- uint8_t* target_base =
- static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
- target_offset;
- uint8_t* source_base =
- static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
- source_offset;
-
- // Typed arrays of the same type: use memmove.
- if (target->type() == source->type()) {
- memmove(target_base + offset * target->element_size(), source_base,
- source_byte_length);
- return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
- }
-
- // Typed arrays of different types over the same backing store
- if ((source_base <= target_base &&
- source_base + source_byte_length > target_base) ||
- (target_base <= source_base &&
- target_base + target_byte_length > source_base)) {
- // We do not support overlapping ArrayBuffers
- DCHECK(target->GetBuffer()->backing_store() ==
- source->GetBuffer()->backing_store());
- return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
- } else { // Non-overlapping typed arrays
- return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
- DCHECK(args.length() == 0);
- DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap +
- FixedTypedArrayBase::kDataOffset);
- return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
-}
-
-
-RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 4);
- CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
-
- DCHECK(holder->GetInternalFieldCount() ==
- v8::ArrayBufferView::kInternalFieldCount);
- for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
- holder->SetInternalField(i, Smi::FromInt(0));
- }
- size_t buffer_length = 0;
- size_t offset = 0;
- size_t length = 0;
- RUNTIME_ASSERT(
- TryNumberToSize(isolate, buffer->byte_length(), &buffer_length));
- RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset));
- RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length));
-
- // TODO(jkummerow): When we have a "safe numerics" helper class, use it here.
- // Entire range [offset, offset + length] must be in bounds.
- RUNTIME_ASSERT(offset <= buffer_length);
- RUNTIME_ASSERT(offset + length <= buffer_length);
- // No overflow.
- RUNTIME_ASSERT(offset + length >= offset);
-
- holder->set_buffer(*buffer);
- holder->set_byte_offset(*byte_offset);
- holder->set_byte_length(*byte_length);
-
- holder->set_weak_next(buffer->weak_first_view());
- buffer->set_weak_first_view(*holder);
-
- return isolate->heap()->undefined_value();
-}
-
-
-inline static bool NeedToFlipBytes(bool is_little_endian) {
-#ifdef V8_TARGET_LITTLE_ENDIAN
- return !is_little_endian;
-#else
- return is_little_endian;
-#endif
-}
-
-
-template <int n>
-inline void CopyBytes(uint8_t* target, uint8_t* source) {
- for (int i = 0; i < n; i++) {
- *(target++) = *(source++);
- }
-}
-
-
-template <int n>
-inline void FlipBytes(uint8_t* target, uint8_t* source) {
- source = source + (n - 1);
- for (int i = 0; i < n; i++) {
- *(target++) = *(source--);
- }
-}
-
-
-template <typename T>
-inline static bool DataViewGetValue(Isolate* isolate,
- Handle<JSDataView> data_view,
- Handle<Object> byte_offset_obj,
- bool is_little_endian, T* result) {
- size_t byte_offset = 0;
- if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
- return false;
- }
- Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
-
- size_t data_view_byte_offset =
- NumberToSize(isolate, data_view->byte_offset());
- size_t data_view_byte_length =
- NumberToSize(isolate, data_view->byte_length());
- if (byte_offset + sizeof(T) > data_view_byte_length ||
- byte_offset + sizeof(T) < byte_offset) { // overflow
- return false;
- }
-
- union Value {
- T data;
- uint8_t bytes[sizeof(T)];
- };
-
- Value value;
- size_t buffer_offset = data_view_byte_offset + byte_offset;
- DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
- buffer_offset + sizeof(T));
- uint8_t* source =
- static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
- if (NeedToFlipBytes(is_little_endian)) {
- FlipBytes<sizeof(T)>(value.bytes, source);
- } else {
- CopyBytes<sizeof(T)>(value.bytes, source);
- }
- *result = value.data;
- return true;
-}
-
-
-template <typename T>
-static bool DataViewSetValue(Isolate* isolate, Handle<JSDataView> data_view,
- Handle<Object> byte_offset_obj,
- bool is_little_endian, T data) {
- size_t byte_offset = 0;
- if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
- return false;
- }
- Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
-
- size_t data_view_byte_offset =
- NumberToSize(isolate, data_view->byte_offset());
- size_t data_view_byte_length =
- NumberToSize(isolate, data_view->byte_length());
- if (byte_offset + sizeof(T) > data_view_byte_length ||
- byte_offset + sizeof(T) < byte_offset) { // overflow
- return false;
- }
-
- union Value {
- T data;
- uint8_t bytes[sizeof(T)];
- };
-
- Value value;
- value.data = data;
- size_t buffer_offset = data_view_byte_offset + byte_offset;
- DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
- buffer_offset + sizeof(T));
- uint8_t* target =
- static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
- if (NeedToFlipBytes(is_little_endian)) {
- FlipBytes<sizeof(T)>(target, value.bytes);
- } else {
- CopyBytes<sizeof(T)>(target, value.bytes);
- }
- return true;
-}
-
-
-#define DATA_VIEW_GETTER(TypeName, Type, Converter) \
- RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) { \
- HandleScope scope(isolate); \
- DCHECK(args.length() == 3); \
- CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
- CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \
- Type result; \
- if (DataViewGetValue(isolate, holder, offset, is_little_endian, \
- &result)) { \
- return *isolate->factory()->Converter(result); \
- } else { \
- THROW_NEW_ERROR_RETURN_FAILURE( \
- isolate, NewRangeError("invalid_data_view_accessor_offset", \
- HandleVector<Object>(NULL, 0))); \
- } \
- }
-
-DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint)
-DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt)
-DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint)
-DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt)
-DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint)
-DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt)
-DATA_VIEW_GETTER(Float32, float, NewNumber)
-DATA_VIEW_GETTER(Float64, double, NewNumber)
-
-#undef DATA_VIEW_GETTER
-
-
-template <typename T>
-static T DataViewConvertValue(double value);
-
-
-template <>
-int8_t DataViewConvertValue<int8_t>(double value) {
- return static_cast<int8_t>(DoubleToInt32(value));
-}
-
-
-template <>
-int16_t DataViewConvertValue<int16_t>(double value) {
- return static_cast<int16_t>(DoubleToInt32(value));
-}
-
-
-template <>
-int32_t DataViewConvertValue<int32_t>(double value) {
- return DoubleToInt32(value);
-}
-
-
-template <>
-uint8_t DataViewConvertValue<uint8_t>(double value) {
- return static_cast<uint8_t>(DoubleToUint32(value));
-}
-
-
-template <>
-uint16_t DataViewConvertValue<uint16_t>(double value) {
- return static_cast<uint16_t>(DoubleToUint32(value));
-}
-
-
-template <>
-uint32_t DataViewConvertValue<uint32_t>(double value) {
- return DoubleToUint32(value);
-}
-
-
-template <>
-float DataViewConvertValue<float>(double value) {
- return static_cast<float>(value);
-}
-
-
-template <>
-double DataViewConvertValue<double>(double value) {
- return value;
-}
-
-
-#define DATA_VIEW_SETTER(TypeName, Type) \
- RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) { \
- HandleScope scope(isolate); \
- DCHECK(args.length() == 4); \
- CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2); \
- CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \
- Type v = DataViewConvertValue<Type>(value->Number()); \
- if (DataViewSetValue(isolate, holder, offset, is_little_endian, v)) { \
- return isolate->heap()->undefined_value(); \
- } else { \
- THROW_NEW_ERROR_RETURN_FAILURE( \
- isolate, NewRangeError("invalid_data_view_accessor_offset", \
- HandleVector<Object>(NULL, 0))); \
- } \
- }
-
-DATA_VIEW_SETTER(Uint8, uint8_t)
-DATA_VIEW_SETTER(Int8, int8_t)
-DATA_VIEW_SETTER(Uint16, uint16_t)
-DATA_VIEW_SETTER(Int16, int16_t)
-DATA_VIEW_SETTER(Uint32, uint32_t)
-DATA_VIEW_SETTER(Int32, int32_t)
-DATA_VIEW_SETTER(Float32, float)
-DATA_VIEW_SETTER(Float64, double)
-
-#undef DATA_VIEW_SETTER
-
-
-RUNTIME_FUNCTION(Runtime_SetInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
- holder->set_table(*table);
- return *holder;
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetAdd) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
- table = OrderedHashSet::Add(table, key);
- holder->set_table(*table);
- return *holder;
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetHas) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
- return isolate->heap()->ToBoolean(table->Contains(key));
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetDelete) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
- bool was_present = false;
- table = OrderedHashSet::Remove(table, key, &was_present);
- holder->set_table(*table);
- return isolate->heap()->ToBoolean(was_present);
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetClear) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
- table = OrderedHashSet::Clear(table);
- holder->set_table(*table);
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetGetSize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
- return Smi::FromInt(table->NumberOfElements());
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSSet, set, 1);
- CONVERT_SMI_ARG_CHECKED(kind, 2)
- RUNTIME_ASSERT(kind == JSSetIterator::kKindValues ||
- kind == JSSetIterator::kKindEntries);
- Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
- holder->set_table(*table);
- holder->set_index(Smi::FromInt(0));
- holder->set_kind(Smi::FromInt(kind));
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_CHECKED(JSSetIterator, holder, 0);
- CONVERT_ARG_CHECKED(JSArray, value_array, 1);
- return holder->Next(value_array);
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
- holder->set_table(*table);
- return *holder;
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapGet) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- Handle<Object> lookup(table->Lookup(key), isolate);
- return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapHas) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- Handle<Object> lookup(table->Lookup(key), isolate);
- return isolate->heap()->ToBoolean(!lookup->IsTheHole());
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapDelete) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- bool was_present = false;
- Handle<OrderedHashMap> new_table =
- OrderedHashMap::Remove(table, key, &was_present);
- holder->set_table(*new_table);
- return isolate->heap()->ToBoolean(was_present);
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapClear) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- table = OrderedHashMap::Clear(table);
- holder->set_table(*table);
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapSet) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- Handle<OrderedHashMap> new_table = OrderedHashMap::Put(table, key, value);
- holder->set_table(*new_table);
- return *holder;
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapGetSize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
- return Smi::FromInt(table->NumberOfElements());
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSMap, map, 1);
- CONVERT_SMI_ARG_CHECKED(kind, 2)
- RUNTIME_ASSERT(kind == JSMapIterator::kKindKeys ||
- kind == JSMapIterator::kKindValues ||
- kind == JSMapIterator::kKindEntries);
- Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()));
- holder->set_table(*table);
- holder->set_index(Smi::FromInt(0));
- holder->set_kind(Smi::FromInt(kind));
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
- Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
- Handle<FixedArray> entries =
- isolate->factory()->NewFixedArray(table->NumberOfElements() * 2);
- {
- DisallowHeapAllocation no_gc;
- int number_of_non_hole_elements = 0;
- for (int i = 0; i < table->Capacity(); i++) {
- Handle<Object> key(table->KeyAt(i), isolate);
- if (table->IsKey(*key)) {
- entries->set(number_of_non_hole_elements++, *key);
- Object* value = table->Lookup(key);
- entries->set(number_of_non_hole_elements++, value);
- }
- }
- DCHECK_EQ(table->NumberOfElements() * 2, number_of_non_hole_elements);
- }
- return *isolate->factory()->NewJSArrayWithElements(entries);
-}
-
-
-RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_CHECKED(JSMapIterator, holder, 0);
- CONVERT_ARG_CHECKED(JSArray, value_array, 1);
- return holder->Next(value_array);
-}
-
-
-static Handle<JSWeakCollection> WeakCollectionInitialize(
- Isolate* isolate, Handle<JSWeakCollection> weak_collection) {
- DCHECK(weak_collection->map()->inobject_properties() == 0);
- Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0);
- weak_collection->set_table(*table);
- return weak_collection;
-}
-
-
-RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
- return *WeakCollectionInitialize(isolate, weak_collection);
-}
-
-
-RUNTIME_FUNCTION(Runtime_WeakCollectionGet) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
- Handle<ObjectHashTable> table(
- ObjectHashTable::cast(weak_collection->table()));
- RUNTIME_ASSERT(table->IsKey(*key));
- Handle<Object> lookup(table->Lookup(key), isolate);
- return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
-}
-
-
-RUNTIME_FUNCTION(Runtime_WeakCollectionHas) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
- Handle<ObjectHashTable> table(
- ObjectHashTable::cast(weak_collection->table()));
- RUNTIME_ASSERT(table->IsKey(*key));
- Handle<Object> lookup(table->Lookup(key), isolate);
- return isolate->heap()->ToBoolean(!lookup->IsTheHole());
-}
-
-
-RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
- Handle<ObjectHashTable> table(
- ObjectHashTable::cast(weak_collection->table()));
- RUNTIME_ASSERT(table->IsKey(*key));
- bool was_present = false;
- Handle<ObjectHashTable> new_table =
- ObjectHashTable::Remove(table, key, &was_present);
- weak_collection->set_table(*new_table);
- return isolate->heap()->ToBoolean(was_present);
-}
-
-
-RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
- RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
- CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
- Handle<ObjectHashTable> table(
- ObjectHashTable::cast(weak_collection->table()));
- RUNTIME_ASSERT(table->IsKey(*key));
- Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
- weak_collection->set_table(*new_table);
- return *weak_collection;
-}
-
-
-RUNTIME_FUNCTION(Runtime_GetWeakSetValues) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
- Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
- Handle<FixedArray> values =
- isolate->factory()->NewFixedArray(table->NumberOfElements());
- {
- DisallowHeapAllocation no_gc;
- int number_of_non_hole_elements = 0;
- for (int i = 0; i < table->Capacity(); i++) {
- Handle<Object> key(table->KeyAt(i), isolate);
- if (table->IsKey(*key)) {
- values->set(number_of_non_hole_elements++, *key);
- }
- }
- DCHECK_EQ(table->NumberOfElements(), number_of_non_hole_elements);
- }
- return *isolate->factory()->NewJSArrayWithElements(values);
-}
-
-
RUNTIME_FUNCTION(Runtime_GetPrototype) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@@ -1954,8 +876,8 @@
RUNTIME_FUNCTION(Runtime_LoadFromSuper) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 0);
- CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 2);
if (home_object->IsAccessCheckNeeded() &&
@@ -1975,6 +897,54 @@
}
+static Object* StoreToSuper(Isolate* isolate, Handle<JSObject> home_object,
+ Handle<Object> receiver, Handle<Name> name,
+ Handle<Object> value, StrictMode strict_mode) {
+ if (home_object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(home_object, name, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_SET);
+ RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
+ }
+
+ PrototypeIterator iter(isolate, home_object);
+ Handle<Object> proto = PrototypeIterator::GetCurrent(iter);
+ if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value();
+
+ LookupIterator it(receiver, name, Handle<JSReceiver>::cast(proto));
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ Object::SetProperty(&it, value, strict_mode,
+ Object::CERTAINLY_NOT_STORE_FROM_KEYED,
+ Object::SUPER_PROPERTY));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 3);
+
+ return StoreToSuper(isolate, home_object, receiver, name, value, STRICT);
+}
+
+
+RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 3);
+
+ return StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY);
+}
+
+
RUNTIME_FUNCTION(Runtime_IsExtensible) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
@@ -1989,19 +959,6 @@
}
-RUNTIME_FUNCTION(Runtime_RegExpCompile) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
- CONVERT_ARG_HANDLE_CHECKED(String, flags, 2);
- Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
- RegExpImpl::Compile(re, pattern, flags));
- return *result;
-}
-
-
RUNTIME_FUNCTION(Runtime_CreateApiFunction) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
@@ -2400,116 +1357,6 @@
}
-RUNTIME_FUNCTION(Runtime_RegExpExecRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 4);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
- CONVERT_INT32_ARG_CHECKED(index, 2);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
- // Due to the way the JS calls are constructed this must be less than the
- // length of a string, i.e. it is always a Smi. We check anyway for security.
- RUNTIME_ASSERT(index >= 0);
- RUNTIME_ASSERT(index <= subject->length());
- isolate->counters()->regexp_entry_runtime()->Increment();
- Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result,
- RegExpImpl::Exec(regexp, subject, index, last_match_info));
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_RegExpConstructResult) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_SMI_ARG_CHECKED(size, 0);
- RUNTIME_ASSERT(size >= 0 && size <= FixedArray::kMaxLength);
- CONVERT_ARG_HANDLE_CHECKED(Object, index, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, input, 2);
- Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size);
- Handle<Map> regexp_map(isolate->native_context()->regexp_result_map());
- Handle<JSObject> object =
- isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED, false);
- Handle<JSArray> array = Handle<JSArray>::cast(object);
- array->set_elements(*elements);
- array->set_length(Smi::FromInt(size));
- // Write in-object properties after the length of the array.
- array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, *index);
- array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input);
- return *array;
-}
-
-
-RUNTIME_FUNCTION(Runtime_RegExpInitializeObject) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 6);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
- // If source is the empty string we set it to "(?:)" instead as
- // suggested by ECMA-262, 5th, section 15.10.4.1.
- if (source->length() == 0) source = isolate->factory()->query_colon_string();
-
- CONVERT_ARG_HANDLE_CHECKED(Object, global, 2);
- if (!global->IsTrue()) global = isolate->factory()->false_value();
-
- CONVERT_ARG_HANDLE_CHECKED(Object, ignoreCase, 3);
- if (!ignoreCase->IsTrue()) ignoreCase = isolate->factory()->false_value();
-
- CONVERT_ARG_HANDLE_CHECKED(Object, multiline, 4);
- if (!multiline->IsTrue()) multiline = isolate->factory()->false_value();
-
- CONVERT_ARG_HANDLE_CHECKED(Object, sticky, 5);
- if (!sticky->IsTrue()) sticky = isolate->factory()->false_value();
-
- Map* map = regexp->map();
- Object* constructor = map->constructor();
- if (!FLAG_harmony_regexps && constructor->IsJSFunction() &&
- JSFunction::cast(constructor)->initial_map() == map) {
- // If we still have the original map, set in-object properties directly.
- regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, *source);
- // Both true and false are immovable immortal objects so no need for write
- // barrier.
- regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, *global,
- SKIP_WRITE_BARRIER);
- regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, *ignoreCase,
- SKIP_WRITE_BARRIER);
- regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, *multiline,
- SKIP_WRITE_BARRIER);
- regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
- Smi::FromInt(0), SKIP_WRITE_BARRIER);
- return *regexp;
- }
-
- // Map has changed, so use generic, but slower, method. We also end here if
- // the --harmony-regexp flag is set, because the initial map does not have
- // space for the 'sticky' flag, since it is from the snapshot, but must work
- // both with and without --harmony-regexp. When sticky comes out from under
- // the flag, we will be able to use the fast initial map.
- PropertyAttributes final =
- static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
- PropertyAttributes writable =
- static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
- Handle<Object> zero(Smi::FromInt(0), isolate);
- Factory* factory = isolate->factory();
- JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->source_string(),
- source, final).Check();
- JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->global_string(),
- global, final).Check();
- JSObject::SetOwnPropertyIgnoreAttributes(
- regexp, factory->ignore_case_string(), ignoreCase, final).Check();
- JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->multiline_string(),
- multiline, final).Check();
- if (FLAG_harmony_regexps) {
- JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->sticky_string(),
- sticky, final).Check();
- }
- JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->last_index_string(),
- zero, writable).Check();
- return *regexp;
-}
-
-
RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@@ -2597,31 +1444,6 @@
}
-RUNTIME_FUNCTION(Runtime_MaterializeRegExpLiteral) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 4);
- CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
- CONVERT_SMI_ARG_CHECKED(index, 1);
- CONVERT_ARG_HANDLE_CHECKED(String, pattern, 2);
- CONVERT_ARG_HANDLE_CHECKED(String, flags, 3);
-
- // Get the RegExp function from the context in the literals array.
- // This is the RegExp function from the context in which the
- // function was created. We do not use the RegExp function from the
- // current native context because this might be the RegExp function
- // from another context which we should not have access to.
- Handle<JSFunction> constructor = Handle<JSFunction>(
- JSFunction::NativeContextFromLiterals(*literals)->regexp_function());
- // Compute the regular expression literal.
- Handle<Object> regexp;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, regexp,
- RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags));
- literals->set(index, *regexp);
- return *regexp;
-}
-
-
RUNTIME_FUNCTION(Runtime_FunctionGetName) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
@@ -3013,1512 +1835,6 @@
}
-RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
-
- // Flatten the string. If someone wants to get a char at an index
- // in a cons string, it is likely that more indices will be
- // accessed.
- subject = String::Flatten(subject);
-
- if (i >= static_cast<uint32_t>(subject->length())) {
- return isolate->heap()->nan_value();
- }
-
- return Smi::FromInt(subject->Get(i));
-}
-
-
-RUNTIME_FUNCTION(Runtime_CharFromCode) {
- HandleScope handlescope(isolate);
- DCHECK(args.length() == 1);
- if (args[0]->IsNumber()) {
- CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]);
- code &= 0xffff;
- return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
- }
- return isolate->heap()->empty_string();
-}
-
-
-class FixedArrayBuilder {
- public:
- explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
- : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
- length_(0),
- has_non_smi_elements_(false) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(initial_capacity > 0);
- }
-
- explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
- : array_(backing_store), length_(0), has_non_smi_elements_(false) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(backing_store->length() > 0);
- }
-
- bool HasCapacity(int elements) {
- int length = array_->length();
- int required_length = length_ + elements;
- return (length >= required_length);
- }
-
- void EnsureCapacity(int elements) {
- int length = array_->length();
- int required_length = length_ + elements;
- if (length < required_length) {
- int new_length = length;
- do {
- new_length *= 2;
- } while (new_length < required_length);
- Handle<FixedArray> extended_array =
- array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
- array_->CopyTo(0, *extended_array, 0, length_);
- array_ = extended_array;
- }
- }
-
- void Add(Object* value) {
- DCHECK(!value->IsSmi());
- DCHECK(length_ < capacity());
- array_->set(length_, value);
- length_++;
- has_non_smi_elements_ = true;
- }
-
- void Add(Smi* value) {
- DCHECK(value->IsSmi());
- DCHECK(length_ < capacity());
- array_->set(length_, value);
- length_++;
- }
-
- Handle<FixedArray> array() { return array_; }
-
- int length() { return length_; }
-
- int capacity() { return array_->length(); }
-
- Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
- JSArray::SetContent(target_array, array_);
- target_array->set_length(Smi::FromInt(length_));
- return target_array;
- }
-
-
- private:
- Handle<FixedArray> array_;
- int length_;
- bool has_non_smi_elements_;
-};
-
-
-// Forward declarations.
-const int kStringBuilderConcatHelperLengthBits = 11;
-const int kStringBuilderConcatHelperPositionBits = 19;
-
-template <typename schar>
-static inline void StringBuilderConcatHelper(String*, schar*, FixedArray*, int);
-
-typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
- StringBuilderSubstringLength;
-typedef BitField<int, kStringBuilderConcatHelperLengthBits,
- kStringBuilderConcatHelperPositionBits>
- StringBuilderSubstringPosition;
-
-
-class ReplacementStringBuilder {
- public:
- ReplacementStringBuilder(Heap* heap, Handle<String> subject,
- int estimated_part_count)
- : heap_(heap),
- array_builder_(heap->isolate(), estimated_part_count),
- subject_(subject),
- character_count_(0),
- is_one_byte_(subject->IsOneByteRepresentation()) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(estimated_part_count > 0);
- }
-
- static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
- int to) {
- DCHECK(from >= 0);
- int length = to - from;
- DCHECK(length > 0);
- if (StringBuilderSubstringLength::is_valid(length) &&
- StringBuilderSubstringPosition::is_valid(from)) {
- int encoded_slice = StringBuilderSubstringLength::encode(length) |
- StringBuilderSubstringPosition::encode(from);
- builder->Add(Smi::FromInt(encoded_slice));
- } else {
- // Otherwise encode as two smis.
- builder->Add(Smi::FromInt(-length));
- builder->Add(Smi::FromInt(from));
- }
- }
-
-
- void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); }
-
-
- void AddSubjectSlice(int from, int to) {
- AddSubjectSlice(&array_builder_, from, to);
- IncrementCharacterCount(to - from);
- }
-
-
- void AddString(Handle<String> string) {
- int length = string->length();
- DCHECK(length > 0);
- AddElement(*string);
- if (!string->IsOneByteRepresentation()) {
- is_one_byte_ = false;
- }
- IncrementCharacterCount(length);
- }
-
-
- MaybeHandle<String> ToString() {
- Isolate* isolate = heap_->isolate();
- if (array_builder_.length() == 0) {
- return isolate->factory()->empty_string();
- }
-
- Handle<String> joined_string;
- if (is_one_byte_) {
- Handle<SeqOneByteString> seq;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, seq,
- isolate->factory()->NewRawOneByteString(character_count_), String);
-
- DisallowHeapAllocation no_gc;
- uint8_t* char_buffer = seq->GetChars();
- StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
- array_builder_.length());
- joined_string = Handle<String>::cast(seq);
- } else {
- // Two-byte.
- Handle<SeqTwoByteString> seq;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, seq,
- isolate->factory()->NewRawTwoByteString(character_count_), String);
-
- DisallowHeapAllocation no_gc;
- uc16* char_buffer = seq->GetChars();
- StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
- array_builder_.length());
- joined_string = Handle<String>::cast(seq);
- }
- return joined_string;
- }
-
-
- void IncrementCharacterCount(int by) {
- if (character_count_ > String::kMaxLength - by) {
- STATIC_ASSERT(String::kMaxLength < kMaxInt);
- character_count_ = kMaxInt;
- } else {
- character_count_ += by;
- }
- }
-
- private:
- void AddElement(Object* element) {
- DCHECK(element->IsSmi() || element->IsString());
- DCHECK(array_builder_.capacity() > array_builder_.length());
- array_builder_.Add(element);
- }
-
- Heap* heap_;
- FixedArrayBuilder array_builder_;
- Handle<String> subject_;
- int character_count_;
- bool is_one_byte_;
-};
-
-
-class CompiledReplacement {
- public:
- explicit CompiledReplacement(Zone* zone)
- : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {}
-
- // Return whether the replacement is simple.
- bool Compile(Handle<String> replacement, int capture_count,
- int subject_length);
-
- // Use Apply only if Compile returned false.
- void Apply(ReplacementStringBuilder* builder, int match_from, int match_to,
- int32_t* match);
-
- // Number of distinct parts of the replacement pattern.
- int parts() { return parts_.length(); }
-
- Zone* zone() const { return zone_; }
-
- private:
- enum PartType {
- SUBJECT_PREFIX = 1,
- SUBJECT_SUFFIX,
- SUBJECT_CAPTURE,
- REPLACEMENT_SUBSTRING,
- REPLACEMENT_STRING,
- NUMBER_OF_PART_TYPES
- };
-
- struct ReplacementPart {
- static inline ReplacementPart SubjectMatch() {
- return ReplacementPart(SUBJECT_CAPTURE, 0);
- }
- static inline ReplacementPart SubjectCapture(int capture_index) {
- return ReplacementPart(SUBJECT_CAPTURE, capture_index);
- }
- static inline ReplacementPart SubjectPrefix() {
- return ReplacementPart(SUBJECT_PREFIX, 0);
- }
- static inline ReplacementPart SubjectSuffix(int subject_length) {
- return ReplacementPart(SUBJECT_SUFFIX, subject_length);
- }
- static inline ReplacementPart ReplacementString() {
- return ReplacementPart(REPLACEMENT_STRING, 0);
- }
- static inline ReplacementPart ReplacementSubString(int from, int to) {
- DCHECK(from >= 0);
- DCHECK(to > from);
- return ReplacementPart(-from, to);
- }
-
- // If tag <= 0 then it is the negation of a start index of a substring of
- // the replacement pattern, otherwise it's a value from PartType.
- ReplacementPart(int tag, int data) : tag(tag), data(data) {
- // Must be non-positive or a PartType value.
- DCHECK(tag < NUMBER_OF_PART_TYPES);
- }
- // Either a value of PartType or a non-positive number that is
- // the negation of an index into the replacement string.
- int tag;
- // The data value's interpretation depends on the value of tag:
- // tag == SUBJECT_PREFIX ||
- // tag == SUBJECT_SUFFIX: data is unused.
- // tag == SUBJECT_CAPTURE: data is the number of the capture.
- // tag == REPLACEMENT_SUBSTRING ||
- // tag == REPLACEMENT_STRING: data is index into array of substrings
- // of the replacement string.
- // tag <= 0: Temporary representation of the substring of the replacement
- // string ranging over -tag .. data.
- // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
- // substring objects.
- int data;
- };
-
- template <typename Char>
- bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
- Vector<Char> characters, int capture_count,
- int subject_length, Zone* zone) {
- int length = characters.length();
- int last = 0;
- for (int i = 0; i < length; i++) {
- Char c = characters[i];
- if (c == '$') {
- int next_index = i + 1;
- if (next_index == length) { // No next character!
- break;
- }
- Char c2 = characters[next_index];
- switch (c2) {
- case '$':
- if (i > last) {
- // There is a substring before. Include the first "$".
- parts->Add(
- ReplacementPart::ReplacementSubString(last, next_index),
- zone);
- last = next_index + 1; // Continue after the second "$".
- } else {
- // Let the next substring start with the second "$".
- last = next_index;
- }
- i = next_index;
- break;
- case '`':
- if (i > last) {
- parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
- }
- parts->Add(ReplacementPart::SubjectPrefix(), zone);
- i = next_index;
- last = i + 1;
- break;
- case '\'':
- if (i > last) {
- parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
- }
- parts->Add(ReplacementPart::SubjectSuffix(subject_length), zone);
- i = next_index;
- last = i + 1;
- break;
- case '&':
- if (i > last) {
- parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
- }
- parts->Add(ReplacementPart::SubjectMatch(), zone);
- i = next_index;
- last = i + 1;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': {
- int capture_ref = c2 - '0';
- if (capture_ref > capture_count) {
- i = next_index;
- continue;
- }
- int second_digit_index = next_index + 1;
- if (second_digit_index < length) {
- // Peek ahead to see if we have two digits.
- Char c3 = characters[second_digit_index];
- if ('0' <= c3 && c3 <= '9') { // Double digits.
- int double_digit_ref = capture_ref * 10 + c3 - '0';
- if (double_digit_ref <= capture_count) {
- next_index = second_digit_index;
- capture_ref = double_digit_ref;
- }
- }
- }
- if (capture_ref > 0) {
- if (i > last) {
- parts->Add(ReplacementPart::ReplacementSubString(last, i),
- zone);
- }
- DCHECK(capture_ref <= capture_count);
- parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone);
- last = next_index + 1;
- }
- i = next_index;
- break;
- }
- default:
- i = next_index;
- break;
- }
- }
- }
- if (length > last) {
- if (last == 0) {
- // Replacement is simple. Do not use Apply to do the replacement.
- return true;
- } else {
- parts->Add(ReplacementPart::ReplacementSubString(last, length), zone);
- }
- }
- return false;
- }
-
- ZoneList<ReplacementPart> parts_;
- ZoneList<Handle<String> > replacement_substrings_;
- Zone* zone_;
-};
-
-
-bool CompiledReplacement::Compile(Handle<String> replacement, int capture_count,
- int subject_length) {
- {
- DisallowHeapAllocation no_gc;
- String::FlatContent content = replacement->GetFlatContent();
- DCHECK(content.IsFlat());
- bool simple = false;
- if (content.IsOneByte()) {
- simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(),
- capture_count, subject_length, zone());
- } else {
- DCHECK(content.IsTwoByte());
- simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(),
- capture_count, subject_length, zone());
- }
- if (simple) return true;
- }
-
- Isolate* isolate = replacement->GetIsolate();
- // Find substrings of replacement string and create them as String objects.
- int substring_index = 0;
- for (int i = 0, n = parts_.length(); i < n; i++) {
- int tag = parts_[i].tag;
- if (tag <= 0) { // A replacement string slice.
- int from = -tag;
- int to = parts_[i].data;
- replacement_substrings_.Add(
- isolate->factory()->NewSubString(replacement, from, to), zone());
- parts_[i].tag = REPLACEMENT_SUBSTRING;
- parts_[i].data = substring_index;
- substring_index++;
- } else if (tag == REPLACEMENT_STRING) {
- replacement_substrings_.Add(replacement, zone());
- parts_[i].data = substring_index;
- substring_index++;
- }
- }
- return false;
-}
-
-
-void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
- int match_from, int match_to, int32_t* match) {
- DCHECK_LT(0, parts_.length());
- for (int i = 0, n = parts_.length(); i < n; i++) {
- ReplacementPart part = parts_[i];
- switch (part.tag) {
- case SUBJECT_PREFIX:
- if (match_from > 0) builder->AddSubjectSlice(0, match_from);
- break;
- case SUBJECT_SUFFIX: {
- int subject_length = part.data;
- if (match_to < subject_length) {
- builder->AddSubjectSlice(match_to, subject_length);
- }
- break;
- }
- case SUBJECT_CAPTURE: {
- int capture = part.data;
- int from = match[capture * 2];
- int to = match[capture * 2 + 1];
- if (from >= 0 && to > from) {
- builder->AddSubjectSlice(from, to);
- }
- break;
- }
- case REPLACEMENT_SUBSTRING:
- case REPLACEMENT_STRING:
- builder->AddString(replacement_substrings_[part.data]);
- break;
- default:
- UNREACHABLE();
- }
- }
-}
-
-
-void FindOneByteStringIndices(Vector<const uint8_t> subject, char pattern,
- ZoneList<int>* indices, unsigned int limit,
- Zone* zone) {
- DCHECK(limit > 0);
- // Collect indices of pattern in subject using memchr.
- // Stop after finding at most limit values.
- const uint8_t* subject_start = subject.start();
- const uint8_t* subject_end = subject_start + subject.length();
- const uint8_t* pos = subject_start;
- while (limit > 0) {
- pos = reinterpret_cast<const uint8_t*>(
- memchr(pos, pattern, subject_end - pos));
- if (pos == NULL) return;
- indices->Add(static_cast<int>(pos - subject_start), zone);
- pos++;
- limit--;
- }
-}
-
-
-void FindTwoByteStringIndices(const Vector<const uc16> subject, uc16 pattern,
- ZoneList<int>* indices, unsigned int limit,
- Zone* zone) {
- DCHECK(limit > 0);
- const uc16* subject_start = subject.start();
- const uc16* subject_end = subject_start + subject.length();
- for (const uc16* pos = subject_start; pos < subject_end && limit > 0; pos++) {
- if (*pos == pattern) {
- indices->Add(static_cast<int>(pos - subject_start), zone);
- limit--;
- }
- }
-}
-
-
-template <typename SubjectChar, typename PatternChar>
-void FindStringIndices(Isolate* isolate, Vector<const SubjectChar> subject,
- Vector<const PatternChar> pattern,
- ZoneList<int>* indices, unsigned int limit, Zone* zone) {
- DCHECK(limit > 0);
- // Collect indices of pattern in subject.
- // Stop after finding at most limit values.
- int pattern_length = pattern.length();
- int index = 0;
- StringSearch<PatternChar, SubjectChar> search(isolate, pattern);
- while (limit > 0) {
- index = search.Search(subject, index);
- if (index < 0) return;
- indices->Add(index, zone);
- index += pattern_length;
- limit--;
- }
-}
-
-
-void FindStringIndicesDispatch(Isolate* isolate, String* subject,
- String* pattern, ZoneList<int>* indices,
- unsigned int limit, Zone* zone) {
- {
- DisallowHeapAllocation no_gc;
- String::FlatContent subject_content = subject->GetFlatContent();
- String::FlatContent pattern_content = pattern->GetFlatContent();
- DCHECK(subject_content.IsFlat());
- DCHECK(pattern_content.IsFlat());
- if (subject_content.IsOneByte()) {
- Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector();
- if (pattern_content.IsOneByte()) {
- Vector<const uint8_t> pattern_vector =
- pattern_content.ToOneByteVector();
- if (pattern_vector.length() == 1) {
- FindOneByteStringIndices(subject_vector, pattern_vector[0], indices,
- limit, zone);
- } else {
- FindStringIndices(isolate, subject_vector, pattern_vector, indices,
- limit, zone);
- }
- } else {
- FindStringIndices(isolate, subject_vector,
- pattern_content.ToUC16Vector(), indices, limit, zone);
- }
- } else {
- Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
- if (pattern_content.IsOneByte()) {
- Vector<const uint8_t> pattern_vector =
- pattern_content.ToOneByteVector();
- if (pattern_vector.length() == 1) {
- FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
- limit, zone);
- } else {
- FindStringIndices(isolate, subject_vector, pattern_vector, indices,
- limit, zone);
- }
- } else {
- Vector<const uc16> pattern_vector = pattern_content.ToUC16Vector();
- if (pattern_vector.length() == 1) {
- FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
- limit, zone);
- } else {
- FindStringIndices(isolate, subject_vector, pattern_vector, indices,
- limit, zone);
- }
- }
- }
- }
-}
-
-
-template <typename ResultSeqString>
-MUST_USE_RESULT static Object* StringReplaceGlobalAtomRegExpWithString(
- Isolate* isolate, Handle<String> subject, Handle<JSRegExp> pattern_regexp,
- Handle<String> replacement, Handle<JSArray> last_match_info) {
- DCHECK(subject->IsFlat());
- DCHECK(replacement->IsFlat());
-
- ZoneScope zone_scope(isolate->runtime_zone());
- ZoneList<int> indices(8, zone_scope.zone());
- DCHECK_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
- String* pattern =
- String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
- int subject_len = subject->length();
- int pattern_len = pattern->length();
- int replacement_len = replacement->length();
-
- FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff,
- zone_scope.zone());
-
- int matches = indices.length();
- if (matches == 0) return *subject;
-
- // Detect integer overflow.
- int64_t result_len_64 = (static_cast<int64_t>(replacement_len) -
- static_cast<int64_t>(pattern_len)) *
- static_cast<int64_t>(matches) +
- static_cast<int64_t>(subject_len);
- int result_len;
- if (result_len_64 > static_cast<int64_t>(String::kMaxLength)) {
- STATIC_ASSERT(String::kMaxLength < kMaxInt);
- result_len = kMaxInt; // Provoke exception.
- } else {
- result_len = static_cast<int>(result_len_64);
- }
-
- int subject_pos = 0;
- int result_pos = 0;
-
- MaybeHandle<SeqString> maybe_res;
- if (ResultSeqString::kHasOneByteEncoding) {
- maybe_res = isolate->factory()->NewRawOneByteString(result_len);
- } else {
- maybe_res = isolate->factory()->NewRawTwoByteString(result_len);
- }
- Handle<SeqString> untyped_res;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, untyped_res, maybe_res);
- Handle<ResultSeqString> result = Handle<ResultSeqString>::cast(untyped_res);
-
- for (int i = 0; i < matches; i++) {
- // Copy non-matched subject content.
- if (subject_pos < indices.at(i)) {
- String::WriteToFlat(*subject, result->GetChars() + result_pos,
- subject_pos, indices.at(i));
- result_pos += indices.at(i) - subject_pos;
- }
-
- // Replace match.
- if (replacement_len > 0) {
- String::WriteToFlat(*replacement, result->GetChars() + result_pos, 0,
- replacement_len);
- result_pos += replacement_len;
- }
-
- subject_pos = indices.at(i) + pattern_len;
- }
- // Add remaining subject content at the end.
- if (subject_pos < subject_len) {
- String::WriteToFlat(*subject, result->GetChars() + result_pos, subject_pos,
- subject_len);
- }
-
- int32_t match_indices[] = {indices.at(matches - 1),
- indices.at(matches - 1) + pattern_len};
- RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices);
-
- return *result;
-}
-
-
-MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithString(
- Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
- Handle<String> replacement, Handle<JSArray> last_match_info) {
- DCHECK(subject->IsFlat());
- DCHECK(replacement->IsFlat());
-
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
-
- // CompiledReplacement uses zone allocation.
- ZoneScope zone_scope(isolate->runtime_zone());
- CompiledReplacement compiled_replacement(zone_scope.zone());
- bool simple_replace =
- compiled_replacement.Compile(replacement, capture_count, subject_length);
-
- // Shortcut for simple non-regexp global replacements
- if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) {
- if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) {
- return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
- isolate, subject, regexp, replacement, last_match_info);
- } else {
- return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
- isolate, subject, regexp, replacement, last_match_info);
- }
- }
-
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) {
- if (global_cache.HasException()) return isolate->heap()->exception();
- return *subject;
- }
-
- // Guessing the number of parts that the final result string is built
- // from. Global regexps can match any number of times, so we guess
- // conservatively.
- int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1;
- ReplacementStringBuilder builder(isolate->heap(), subject, expected_parts);
-
- // Number of parts added by compiled replacement plus preceeding
- // string and possibly suffix after last match. It is possible for
- // all components to use two elements when encoded as two smis.
- const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
-
- int prev = 0;
-
- do {
- builder.EnsureCapacity(parts_added_per_loop);
-
- int start = current_match[0];
- int end = current_match[1];
-
- if (prev < start) {
- builder.AddSubjectSlice(prev, start);
- }
-
- if (simple_replace) {
- builder.AddString(replacement);
- } else {
- compiled_replacement.Apply(&builder, start, end, current_match);
- }
- prev = end;
-
- current_match = global_cache.FetchNext();
- } while (current_match != NULL);
-
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- if (prev < subject_length) {
- builder.EnsureCapacity(2);
- builder.AddSubjectSlice(prev, subject_length);
- }
-
- RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count,
- global_cache.LastSuccessfulMatch());
-
- Handle<String> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, builder.ToString());
- return *result;
-}
-
-
-template <typename ResultSeqString>
-MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithEmptyString(
- Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
- Handle<JSArray> last_match_info) {
- DCHECK(subject->IsFlat());
-
- // Shortcut for simple non-regexp global replacements
- if (regexp->TypeTag() == JSRegExp::ATOM) {
- Handle<String> empty_string = isolate->factory()->empty_string();
- if (subject->IsOneByteRepresentation()) {
- return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
- isolate, subject, regexp, empty_string, last_match_info);
- } else {
- return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
- isolate, subject, regexp, empty_string, last_match_info);
- }
- }
-
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) {
- if (global_cache.HasException()) return isolate->heap()->exception();
- return *subject;
- }
-
- int start = current_match[0];
- int end = current_match[1];
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
-
- int new_length = subject_length - (end - start);
- if (new_length == 0) return isolate->heap()->empty_string();
-
- Handle<ResultSeqString> answer;
- if (ResultSeqString::kHasOneByteEncoding) {
- answer = Handle<ResultSeqString>::cast(
- isolate->factory()->NewRawOneByteString(new_length).ToHandleChecked());
- } else {
- answer = Handle<ResultSeqString>::cast(
- isolate->factory()->NewRawTwoByteString(new_length).ToHandleChecked());
- }
-
- int prev = 0;
- int position = 0;
-
- do {
- start = current_match[0];
- end = current_match[1];
- if (prev < start) {
- // Add substring subject[prev;start] to answer string.
- String::WriteToFlat(*subject, answer->GetChars() + position, prev, start);
- position += start - prev;
- }
- prev = end;
-
- current_match = global_cache.FetchNext();
- } while (current_match != NULL);
-
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count,
- global_cache.LastSuccessfulMatch());
-
- if (prev < subject_length) {
- // Add substring subject[prev;length] to answer string.
- String::WriteToFlat(*subject, answer->GetChars() + position, prev,
- subject_length);
- position += subject_length - prev;
- }
-
- if (position == 0) return isolate->heap()->empty_string();
-
- // Shorten string and fill
- int string_size = ResultSeqString::SizeFor(position);
- int allocated_string_size = ResultSeqString::SizeFor(new_length);
- int delta = allocated_string_size - string_size;
-
- answer->set_length(position);
- if (delta == 0) return *answer;
-
- Address end_of_string = answer->address() + string_size;
- Heap* heap = isolate->heap();
-
- // The trimming is performed on a newly allocated object, which is on a
- // fresly allocated page or on an already swept page. Hence, the sweeper
- // thread can not get confused with the filler creation. No synchronization
- // needed.
- heap->CreateFillerObjectAt(end_of_string, delta);
- heap->AdjustLiveBytes(answer->address(), -delta, Heap::FROM_MUTATOR);
- return *answer;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringReplaceGlobalRegExpWithString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 4);
-
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
-
- RUNTIME_ASSERT(regexp->GetFlags().is_global());
- RUNTIME_ASSERT(last_match_info->HasFastObjectElements());
-
- subject = String::Flatten(subject);
-
- if (replacement->length() == 0) {
- if (subject->HasOnlyOneByteChars()) {
- return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
- isolate, subject, regexp, last_match_info);
- } else {
- return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
- isolate, subject, regexp, last_match_info);
- }
- }
-
- replacement = String::Flatten(replacement);
-
- return StringReplaceGlobalRegExpWithString(isolate, subject, regexp,
- replacement, last_match_info);
-}
-
-
-// This may return an empty MaybeHandle if an exception is thrown or
-// we abort due to reaching the recursion limit.
-MaybeHandle<String> StringReplaceOneCharWithString(
- Isolate* isolate, Handle<String> subject, Handle<String> search,
- Handle<String> replace, bool* found, int recursion_limit) {
- StackLimitCheck stackLimitCheck(isolate);
- if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
- return MaybeHandle<String>();
- }
- recursion_limit--;
- if (subject->IsConsString()) {
- ConsString* cons = ConsString::cast(*subject);
- Handle<String> first = Handle<String>(cons->first());
- Handle<String> second = Handle<String>(cons->second());
- Handle<String> new_first;
- if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
- recursion_limit).ToHandle(&new_first)) {
- return MaybeHandle<String>();
- }
- if (*found) return isolate->factory()->NewConsString(new_first, second);
-
- Handle<String> new_second;
- if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
- recursion_limit)
- .ToHandle(&new_second)) {
- return MaybeHandle<String>();
- }
- if (*found) return isolate->factory()->NewConsString(first, new_second);
-
- return subject;
- } else {
- int index = Runtime::StringMatch(isolate, subject, search, 0);
- if (index == -1) return subject;
- *found = true;
- Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
- Handle<String> cons1;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, cons1, isolate->factory()->NewConsString(first, replace),
- String);
- Handle<String> second =
- isolate->factory()->NewSubString(subject, index + 1, subject->length());
- return isolate->factory()->NewConsString(cons1, second);
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
- CONVERT_ARG_HANDLE_CHECKED(String, replace, 2);
-
- // If the cons string tree is too deep, we simply abort the recursion and
- // retry with a flattened subject string.
- const int kRecursionLimit = 0x1000;
- bool found = false;
- Handle<String> result;
- if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
- kRecursionLimit).ToHandle(&result)) {
- return *result;
- }
- if (isolate->has_pending_exception()) return isolate->heap()->exception();
-
- subject = String::Flatten(subject);
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result,
- StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
- kRecursionLimit));
- return *result;
-}
-
-
-// Perform string match of pattern on subject, starting at start index.
-// Caller must ensure that 0 <= start_index <= sub->length(),
-// and should check that pat->length() + start_index <= sub->length().
-int Runtime::StringMatch(Isolate* isolate, Handle<String> sub,
- Handle<String> pat, int start_index) {
- DCHECK(0 <= start_index);
- DCHECK(start_index <= sub->length());
-
- int pattern_length = pat->length();
- if (pattern_length == 0) return start_index;
-
- int subject_length = sub->length();
- if (start_index + pattern_length > subject_length) return -1;
-
- sub = String::Flatten(sub);
- pat = String::Flatten(pat);
-
- DisallowHeapAllocation no_gc; // ensure vectors stay valid
- // Extract flattened substrings of cons strings before getting encoding.
- String::FlatContent seq_sub = sub->GetFlatContent();
- String::FlatContent seq_pat = pat->GetFlatContent();
-
- // dispatch on type of strings
- if (seq_pat.IsOneByte()) {
- Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector();
- if (seq_sub.IsOneByte()) {
- return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
- start_index);
- }
- return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector,
- start_index);
- }
- Vector<const uc16> pat_vector = seq_pat.ToUC16Vector();
- if (seq_sub.IsOneByte()) {
- return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
- start_index);
- }
- return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringIndexOf) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
-
- uint32_t start_index;
- if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
-
- RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
- int position = Runtime::StringMatch(isolate, sub, pat, start_index);
- return Smi::FromInt(position);
-}
-
-
-template <typename schar, typename pchar>
-static int StringMatchBackwards(Vector<const schar> subject,
- Vector<const pchar> pattern, int idx) {
- int pattern_length = pattern.length();
- DCHECK(pattern_length >= 1);
- DCHECK(idx + pattern_length <= subject.length());
-
- if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
- for (int i = 0; i < pattern_length; i++) {
- uc16 c = pattern[i];
- if (c > String::kMaxOneByteCharCode) {
- return -1;
- }
- }
- }
-
- pchar pattern_first_char = pattern[0];
- for (int i = idx; i >= 0; i--) {
- if (subject[i] != pattern_first_char) continue;
- int j = 1;
- while (j < pattern_length) {
- if (pattern[j] != subject[i + j]) {
- break;
- }
- j++;
- }
- if (j == pattern_length) {
- return i;
- }
- }
- return -1;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
-
- uint32_t start_index;
- if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
-
- uint32_t pat_length = pat->length();
- uint32_t sub_length = sub->length();
-
- if (start_index + pat_length > sub_length) {
- start_index = sub_length - pat_length;
- }
-
- if (pat_length == 0) {
- return Smi::FromInt(start_index);
- }
-
- sub = String::Flatten(sub);
- pat = String::Flatten(pat);
-
- int position = -1;
- DisallowHeapAllocation no_gc; // ensure vectors stay valid
-
- String::FlatContent sub_content = sub->GetFlatContent();
- String::FlatContent pat_content = pat->GetFlatContent();
-
- if (pat_content.IsOneByte()) {
- Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector();
- if (sub_content.IsOneByte()) {
- position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
- start_index);
- } else {
- position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
- start_index);
- }
- } else {
- Vector<const uc16> pat_vector = pat_content.ToUC16Vector();
- if (sub_content.IsOneByte()) {
- position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
- start_index);
- } else {
- position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
- start_index);
- }
- }
-
- return Smi::FromInt(position);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringLocaleCompare) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
-
- if (str1.is_identical_to(str2)) return Smi::FromInt(0); // Equal.
- int str1_length = str1->length();
- int str2_length = str2->length();
-
- // Decide trivial cases without flattening.
- if (str1_length == 0) {
- if (str2_length == 0) return Smi::FromInt(0); // Equal.
- return Smi::FromInt(-str2_length);
- } else {
- if (str2_length == 0) return Smi::FromInt(str1_length);
- }
-
- int end = str1_length < str2_length ? str1_length : str2_length;
-
- // No need to flatten if we are going to find the answer on the first
- // character. At this point we know there is at least one character
- // in each string, due to the trivial case handling above.
- int d = str1->Get(0) - str2->Get(0);
- if (d != 0) return Smi::FromInt(d);
-
- str1 = String::Flatten(str1);
- str2 = String::Flatten(str2);
-
- DisallowHeapAllocation no_gc;
- String::FlatContent flat1 = str1->GetFlatContent();
- String::FlatContent flat2 = str2->GetFlatContent();
-
- for (int i = 0; i < end; i++) {
- if (flat1.Get(i) != flat2.Get(i)) {
- return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
- }
- }
-
- return Smi::FromInt(str1_length - str2_length);
-}
-
-
-RUNTIME_FUNCTION(Runtime_SubString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
- int start, end;
- // We have a fast integer-only case here to avoid a conversion to double in
- // the common case where from and to are Smis.
- if (args[1]->IsSmi() && args[2]->IsSmi()) {
- CONVERT_SMI_ARG_CHECKED(from_number, 1);
- CONVERT_SMI_ARG_CHECKED(to_number, 2);
- start = from_number;
- end = to_number;
- } else {
- CONVERT_DOUBLE_ARG_CHECKED(from_number, 1);
- CONVERT_DOUBLE_ARG_CHECKED(to_number, 2);
- start = FastD2IChecked(from_number);
- end = FastD2IChecked(to_number);
- }
- RUNTIME_ASSERT(end >= start);
- RUNTIME_ASSERT(start >= 0);
- RUNTIME_ASSERT(end <= string->length());
- isolate->counters()->sub_string_runtime()->Increment();
-
- return *isolate->factory()->NewSubString(string, start, end);
-}
-
-
-RUNTIME_FUNCTION(Runtime_InternalizeString) {
- HandleScope handles(isolate);
- RUNTIME_ASSERT(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
- return *isolate->factory()->InternalizeString(string);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringMatch) {
- HandleScope handles(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2);
-
- RUNTIME_ASSERT(regexp_info->HasFastObjectElements());
-
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- int capture_count = regexp->CaptureCount();
-
- 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_scope.zone()); // start
- offsets.Add(match[1], zone_scope.zone()); // end
- }
-
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- if (offsets.length() == 0) {
- // Not a single match.
- return isolate->heap()->null_value();
- }
-
- RegExpImpl::SetLastMatchInfo(regexp_info, subject, capture_count,
- global_cache.LastSuccessfulMatch());
-
- int matches = offsets.length() / 2;
- Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches);
- Handle<String> substring =
- isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1));
- elements->set(0, *substring);
- for (int i = 1; i < matches; i++) {
- HandleScope temp_scope(isolate);
- int from = offsets.at(i * 2);
- int to = offsets.at(i * 2 + 1);
- Handle<String> substring =
- isolate->factory()->NewProperSubString(subject, from, to);
- elements->set(i, *substring);
- }
- Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements);
- result->set_length(Smi::FromInt(matches));
- return *result;
-}
-
-
-// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain
-// separate last match info. See comment on that function.
-template <bool has_capture>
-static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
- Handle<JSRegExp> regexp,
- Handle<JSArray> last_match_array,
- Handle<JSArray> result_array) {
- DCHECK(subject->IsFlat());
- DCHECK_NE(has_capture, regexp->CaptureCount() == 0);
-
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
-
- static const int kMinLengthToCache = 0x1000;
-
- if (subject_length > kMinLengthToCache) {
- Handle<Object> cached_answer(
- RegExpResultsCache::Lookup(isolate->heap(), *subject, regexp->data(),
- RegExpResultsCache::REGEXP_MULTIPLE_INDICES),
- isolate);
- if (*cached_answer != Smi::FromInt(0)) {
- Handle<FixedArray> cached_fixed_array =
- Handle<FixedArray>(FixedArray::cast(*cached_answer));
- // The cache FixedArray is a COW-array and can therefore be reused.
- JSArray::SetContent(result_array, cached_fixed_array);
- // The actual length of the result array is stored in the last element of
- // the backing store (the backing FixedArray may have a larger capacity).
- Object* cached_fixed_array_last_element =
- cached_fixed_array->get(cached_fixed_array->length() - 1);
- Smi* js_array_length = Smi::cast(cached_fixed_array_last_element);
- result_array->set_length(js_array_length);
- RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count,
- NULL);
- return *result_array;
- }
- }
-
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- // Ensured in Runtime_RegExpExecMultiple.
- DCHECK(result_array->HasFastObjectElements());
- Handle<FixedArray> result_elements(
- FixedArray::cast(result_array->elements()));
- if (result_elements->length() < 16) {
- result_elements = isolate->factory()->NewFixedArrayWithHoles(16);
- }
-
- FixedArrayBuilder builder(result_elements);
-
- // Position to search from.
- int match_start = -1;
- int match_end = 0;
- bool first = true;
-
- // Two smis before and after the match, for very long strings.
- static const int kMaxBuilderEntriesPerRegExpMatch = 5;
-
- while (true) {
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) break;
- match_start = current_match[0];
- builder.EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
- if (match_end < match_start) {
- ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
- match_start);
- }
- match_end = current_match[1];
- {
- // Avoid accumulating new handles inside loop.
- HandleScope temp_scope(isolate);
- Handle<String> match;
- if (!first) {
- match = isolate->factory()->NewProperSubString(subject, match_start,
- match_end);
- } else {
- match =
- isolate->factory()->NewSubString(subject, match_start, match_end);
- first = false;
- }
-
- if (has_capture) {
- // Arguments array to replace function is match, captures, index and
- // subject, i.e., 3 + capture count in total.
- Handle<FixedArray> elements =
- isolate->factory()->NewFixedArray(3 + capture_count);
-
- elements->set(0, *match);
- for (int i = 1; i <= capture_count; i++) {
- int start = current_match[i * 2];
- if (start >= 0) {
- int end = current_match[i * 2 + 1];
- DCHECK(start <= end);
- Handle<String> substring =
- isolate->factory()->NewSubString(subject, start, end);
- elements->set(i, *substring);
- } else {
- DCHECK(current_match[i * 2 + 1] < 0);
- elements->set(i, isolate->heap()->undefined_value());
- }
- }
- elements->set(capture_count + 1, Smi::FromInt(match_start));
- elements->set(capture_count + 2, *subject);
- builder.Add(*isolate->factory()->NewJSArrayWithElements(elements));
- } else {
- builder.Add(*match);
- }
- }
- }
-
- if (global_cache.HasException()) return isolate->heap()->exception();
-
- if (match_start >= 0) {
- // Finished matching, with at least one match.
- if (match_end < subject_length) {
- ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
- subject_length);
- }
-
- RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count,
- NULL);
-
- if (subject_length > kMinLengthToCache) {
- // Store the length of the result array into the last element of the
- // backing FixedArray.
- builder.EnsureCapacity(1);
- Handle<FixedArray> fixed_array = builder.array();
- fixed_array->set(fixed_array->length() - 1,
- Smi::FromInt(builder.length()));
- // Cache the result and turn the FixedArray into a COW array.
- RegExpResultsCache::Enter(isolate, subject,
- handle(regexp->data(), isolate), fixed_array,
- RegExpResultsCache::REGEXP_MULTIPLE_INDICES);
- }
- return *builder.ToJSArray(result_array);
- } else {
- return isolate->heap()->null_value(); // No matches at all.
- }
-}
-
-
-// This is only called for StringReplaceGlobalRegExpWithFunction. This sets
-// lastMatchInfoOverride to maintain the last match info, so we don't need to
-// set any other last match array info.
-RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) {
- HandleScope handles(isolate);
- DCHECK(args.length() == 4);
-
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3);
- RUNTIME_ASSERT(last_match_info->HasFastObjectElements());
- RUNTIME_ASSERT(result_array->HasFastObjectElements());
-
- subject = String::Flatten(subject);
- RUNTIME_ASSERT(regexp->GetFlags().is_global());
-
- if (regexp->CaptureCount() == 0) {
- return SearchRegExpMultiple<false>(isolate, subject, regexp,
- last_match_info, result_array);
- } else {
- return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info,
- result_array);
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToRadixString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_SMI_ARG_CHECKED(radix, 1);
- RUNTIME_ASSERT(2 <= radix && radix <= 36);
-
- // Fast case where the result is a one character string.
- if (args[0]->IsSmi()) {
- int value = args.smi_at(0);
- if (value >= 0 && value < radix) {
- // Character array used for conversion.
- static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
- return *isolate->factory()->LookupSingleCharacterStringFromCode(
- kCharTable[value]);
- }
- }
-
- // Slow case.
- CONVERT_DOUBLE_ARG_CHECKED(value, 0);
- if (std::isnan(value)) {
- return isolate->heap()->nan_string();
- }
- if (std::isinf(value)) {
- if (value < 0) {
- return isolate->heap()->minus_infinity_string();
- }
- return isolate->heap()->infinity_string();
- }
- char* str = DoubleToRadixCString(value, radix);
- Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
- DeleteArray(str);
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToFixed) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(value, 0);
- CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
- int f = FastD2IChecked(f_number);
- // See DoubleToFixedCString for these constants:
- RUNTIME_ASSERT(f >= 0 && f <= 20);
- RUNTIME_ASSERT(!Double(value).IsSpecial());
- char* str = DoubleToFixedCString(value, f);
- Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
- DeleteArray(str);
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToExponential) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(value, 0);
- CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
- int f = FastD2IChecked(f_number);
- RUNTIME_ASSERT(f >= -1 && f <= 20);
- RUNTIME_ASSERT(!Double(value).IsSpecial());
- char* str = DoubleToExponentialCString(value, f);
- Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
- DeleteArray(str);
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToPrecision) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(value, 0);
- CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
- int f = FastD2IChecked(f_number);
- RUNTIME_ASSERT(f >= 1 && f <= 21);
- RUNTIME_ASSERT(!Double(value).IsSpecial());
- char* str = DoubleToPrecisionCString(value, f);
- Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
- DeleteArray(str);
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_IsValidSmi) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_NUMBER_CHECKED(int32_t, number, Int32, args[0]);
- return isolate->heap()->ToBoolean(Smi::IsValid(number));
-}
-
-
// Returns a single character string where first character equals
// string->Get(index).
static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
@@ -5942,706 +3258,6 @@
}
-static bool AreDigits(const uint8_t* s, int from, int to) {
- for (int i = from; i < to; i++) {
- if (s[i] < '0' || s[i] > '9') return false;
- }
-
- return true;
-}
-
-
-static int ParseDecimalInteger(const uint8_t* s, int from, int to) {
- DCHECK(to - from < 10); // Overflow is not possible.
- DCHECK(from < to);
- int d = s[from] - '0';
-
- for (int i = from + 1; i < to; i++) {
- d = 10 * d + (s[i] - '0');
- }
-
- return d;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringToNumber) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- subject = String::Flatten(subject);
-
- // Fast case: short integer or some sorts of junk values.
- if (subject->IsSeqOneByteString()) {
- int len = subject->length();
- if (len == 0) return Smi::FromInt(0);
-
- DisallowHeapAllocation no_gc;
- uint8_t const* data = Handle<SeqOneByteString>::cast(subject)->GetChars();
- bool minus = (data[0] == '-');
- int start_pos = (minus ? 1 : 0);
-
- if (start_pos == len) {
- return isolate->heap()->nan_value();
- } else if (data[start_pos] > '9') {
- // Fast check for a junk value. A valid string may start from a
- // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit
- // or the 'I' character ('Infinity'). All of that have codes not greater
- // than '9' except 'I' and .
- if (data[start_pos] != 'I' && data[start_pos] != 0xa0) {
- return isolate->heap()->nan_value();
- }
- } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
- // The maximal/minimal smi has 10 digits. If the string has less digits
- // we know it will fit into the smi-data type.
- int d = ParseDecimalInteger(data, start_pos, len);
- if (minus) {
- if (d == 0) return isolate->heap()->minus_zero_value();
- d = -d;
- } else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize &&
- (len == 1 || data[0] != '0')) {
- // String hash is not calculated yet but all the data are present.
- // Update the hash field to speed up sequential convertions.
- uint32_t hash = StringHasher::MakeArrayIndexHash(d, len);
-#ifdef DEBUG
- subject->Hash(); // Force hash calculation.
- DCHECK_EQ(static_cast<int>(subject->hash_field()),
- static_cast<int>(hash));
-#endif
- subject->set_hash_field(hash);
- }
- return Smi::FromInt(d);
- }
- }
-
- // Slower case.
- int flags = ALLOW_HEX;
- if (FLAG_harmony_numeric_literals) {
- // The current spec draft has not updated "ToNumber Applied to the String
- // Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584
- flags |= ALLOW_OCTAL | ALLOW_BINARY;
- }
-
- return *isolate->factory()->NewNumber(
- StringToDouble(isolate->unicode_cache(), *subject, flags));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NewString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_INT32_ARG_CHECKED(length, 0);
- CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1);
- if (length == 0) return isolate->heap()->empty_string();
- Handle<String> result;
- if (is_one_byte) {
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, isolate->factory()->NewRawOneByteString(length));
- } else {
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, isolate->factory()->NewRawTwoByteString(length));
- }
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_TruncateString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0);
- CONVERT_INT32_ARG_CHECKED(new_length, 1);
- RUNTIME_ASSERT(new_length >= 0);
- return *SeqString::Truncate(string, new_length);
-}
-
-
-RUNTIME_FUNCTION(Runtime_URIEscape) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
- Handle<String> string = String::Flatten(source);
- DCHECK(string->IsFlat());
- Handle<String> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, string->IsOneByteRepresentationUnderneath()
- ? URIEscape::Escape<uint8_t>(isolate, source)
- : URIEscape::Escape<uc16>(isolate, source));
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_URIUnescape) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
- Handle<String> string = String::Flatten(source);
- DCHECK(string->IsFlat());
- Handle<String> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, string->IsOneByteRepresentationUnderneath()
- ? URIUnescape::Unescape<uint8_t>(isolate, source)
- : URIUnescape::Unescape<uc16>(isolate, source));
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_QuoteJSONString) {
- HandleScope scope(isolate);
- CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
- DCHECK(args.length() == 1);
- Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, BasicJsonStringifier::StringifyString(isolate, string));
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_BasicJSONStringify) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
- BasicJsonStringifier stringifier(isolate);
- Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
- stringifier.Stringify(object));
- return *result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringParseInt) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]);
- RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
-
- subject = String::Flatten(subject);
- double value;
-
- {
- DisallowHeapAllocation no_gc;
- String::FlatContent flat = subject->GetFlatContent();
-
- // ECMA-262 section 15.1.2.3, empty string is NaN
- if (flat.IsOneByte()) {
- value =
- StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix);
- } else {
- value = StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix);
- }
- }
-
- return *isolate->factory()->NewNumber(value);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringParseFloat) {
- HandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
-
- subject = String::Flatten(subject);
- double value = StringToDouble(isolate->unicode_cache(), *subject,
- ALLOW_TRAILING_JUNK, base::OS::nan_value());
-
- return *isolate->factory()->NewNumber(value);
-}
-
-
-static inline bool ToUpperOverflows(uc32 character) {
- // y with umlauts and the micro sign are the only characters that stop
- // fitting into one-byte when converting to uppercase.
- static const uc32 yuml_code = 0xff;
- static const uc32 micro_code = 0xb5;
- return (character == yuml_code || character == micro_code);
-}
-
-
-template <class Converter>
-MUST_USE_RESULT static Object* ConvertCaseHelper(
- Isolate* isolate, String* string, SeqString* result, int result_length,
- unibrow::Mapping<Converter, 128>* mapping) {
- DisallowHeapAllocation no_gc;
- // We try this twice, once with the assumption that the result is no longer
- // than the input and, if that assumption breaks, again with the exact
- // length. This may not be pretty, but it is nicer than what was here before
- // and I hereby claim my vaffel-is.
- //
- // NOTE: This assumes that the upper/lower case of an ASCII
- // character is also ASCII. This is currently the case, but it
- // might break in the future if we implement more context and locale
- // dependent upper/lower conversions.
- bool has_changed_character = false;
-
- // Convert all characters to upper case, assuming that they will fit
- // in the buffer
- Access<ConsStringIteratorOp> op(isolate->runtime_state()->string_iterator());
- StringCharacterStream stream(string, op.value());
- unibrow::uchar chars[Converter::kMaxWidth];
- // We can assume that the string is not empty
- uc32 current = stream.GetNext();
- bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
- for (int i = 0; i < result_length;) {
- bool has_next = stream.HasMore();
- uc32 next = has_next ? stream.GetNext() : 0;
- int char_length = mapping->get(current, next, chars);
- if (char_length == 0) {
- // The case conversion of this character is the character itself.
- result->Set(i, current);
- i++;
- } else if (char_length == 1 &&
- (ignore_overflow || !ToUpperOverflows(current))) {
- // Common case: converting the letter resulted in one character.
- DCHECK(static_cast<uc32>(chars[0]) != current);
- result->Set(i, chars[0]);
- has_changed_character = true;
- i++;
- } else if (result_length == string->length()) {
- bool overflows = ToUpperOverflows(current);
- // We've assumed that the result would be as long as the
- // input but here is a character that converts to several
- // characters. No matter, we calculate the exact length
- // of the result and try the whole thing again.
- //
- // Note that this leaves room for optimization. We could just
- // memcpy what we already have to the result string. Also,
- // the result string is the last object allocated we could
- // "realloc" it and probably, in the vast majority of cases,
- // extend the existing string to be able to hold the full
- // result.
- int next_length = 0;
- if (has_next) {
- next_length = mapping->get(next, 0, chars);
- if (next_length == 0) next_length = 1;
- }
- int current_length = i + char_length + next_length;
- while (stream.HasMore()) {
- current = stream.GetNext();
- overflows |= ToUpperOverflows(current);
- // NOTE: we use 0 as the next character here because, while
- // the next character may affect what a character converts to,
- // it does not in any case affect the length of what it convert
- // to.
- int char_length = mapping->get(current, 0, chars);
- if (char_length == 0) char_length = 1;
- current_length += char_length;
- if (current_length > String::kMaxLength) {
- AllowHeapAllocation allocate_error_and_return;
- THROW_NEW_ERROR_RETURN_FAILURE(isolate,
- NewInvalidStringLengthError());
- }
- }
- // Try again with the real length. Return signed if we need
- // to allocate a two-byte string for to uppercase.
- return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
- : Smi::FromInt(current_length);
- } else {
- for (int j = 0; j < char_length; j++) {
- result->Set(i, chars[j]);
- i++;
- }
- has_changed_character = true;
- }
- current = next;
- }
- if (has_changed_character) {
- return result;
- } else {
- // If we didn't actually change anything in doing the conversion
- // we simple return the result and let the converted string
- // become garbage; there is no reason to keep two identical strings
- // alive.
- return string;
- }
-}
-
-
-namespace {
-
-static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF;
-static const uintptr_t kAsciiMask = kOneInEveryByte << 7;
-
-// Given a word and two range boundaries returns a word with high bit
-// set in every byte iff the corresponding input byte was strictly in
-// the range (m, n). All the other bits in the result are cleared.
-// This function is only useful when it can be inlined and the
-// boundaries are statically known.
-// Requires: all bytes in the input word and the boundaries must be
-// ASCII (less than 0x7F).
-static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) {
- // Use strict inequalities since in edge cases the function could be
- // further simplified.
- DCHECK(0 < m && m < n);
- // Has high bit set in every w byte less than n.
- uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w;
- // Has high bit set in every w byte greater than m.
- uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m);
- return (tmp1 & tmp2 & (kOneInEveryByte * 0x80));
-}
-
-
-#ifdef DEBUG
-static bool CheckFastAsciiConvert(char* dst, const char* src, int length,
- bool changed, bool is_to_lower) {
- bool expected_changed = false;
- for (int i = 0; i < length; i++) {
- if (dst[i] == src[i]) continue;
- expected_changed = true;
- if (is_to_lower) {
- DCHECK('A' <= src[i] && src[i] <= 'Z');
- DCHECK(dst[i] == src[i] + ('a' - 'A'));
- } else {
- DCHECK('a' <= src[i] && src[i] <= 'z');
- DCHECK(dst[i] == src[i] - ('a' - 'A'));
- }
- }
- return (expected_changed == changed);
-}
-#endif
-
-
-template <class Converter>
-static bool FastAsciiConvert(char* dst, const char* src, int length,
- bool* changed_out) {
-#ifdef DEBUG
- char* saved_dst = dst;
- const char* saved_src = src;
-#endif
- DisallowHeapAllocation no_gc;
- // We rely on the distance between upper and lower case letters
- // being a known power of 2.
- DCHECK('a' - 'A' == (1 << 5));
- // Boundaries for the range of input characters than require conversion.
- static const char lo = Converter::kIsToLower ? 'A' - 1 : 'a' - 1;
- static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1;
- bool changed = false;
- uintptr_t or_acc = 0;
- const char* const limit = src + length;
-
- // dst is newly allocated and always aligned.
- DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t)));
- // Only attempt processing one word at a time if src is also aligned.
- if (IsAligned(reinterpret_cast<intptr_t>(src), sizeof(uintptr_t))) {
- // Process the prefix of the input that requires no conversion one aligned
- // (machine) word at a time.
- while (src <= limit - sizeof(uintptr_t)) {
- const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
- or_acc |= w;
- if (AsciiRangeMask(w, lo, hi) != 0) {
- changed = true;
- break;
- }
- *reinterpret_cast<uintptr_t*>(dst) = w;
- src += sizeof(uintptr_t);
- dst += sizeof(uintptr_t);
- }
- // Process the remainder of the input performing conversion when
- // required one word at a time.
- while (src <= limit - sizeof(uintptr_t)) {
- const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
- or_acc |= w;
- uintptr_t m = AsciiRangeMask(w, lo, hi);
- // The mask has high (7th) bit set in every byte that needs
- // conversion and we know that the distance between cases is
- // 1 << 5.
- *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2);
- src += sizeof(uintptr_t);
- dst += sizeof(uintptr_t);
- }
- }
- // Process the last few bytes of the input (or the whole input if
- // unaligned access is not supported).
- while (src < limit) {
- char c = *src;
- or_acc |= c;
- if (lo < c && c < hi) {
- c ^= (1 << 5);
- changed = true;
- }
- *dst = c;
- ++src;
- ++dst;
- }
-
- if ((or_acc & kAsciiMask) != 0) return false;
-
- DCHECK(CheckFastAsciiConvert(saved_dst, saved_src, length, changed,
- Converter::kIsToLower));
-
- *changed_out = changed;
- return true;
-}
-
-} // namespace
-
-
-template <class Converter>
-MUST_USE_RESULT static Object* ConvertCase(
- Handle<String> s, Isolate* isolate,
- unibrow::Mapping<Converter, 128>* mapping) {
- s = String::Flatten(s);
- int length = s->length();
- // Assume that the string is not empty; we need this assumption later
- if (length == 0) return *s;
-
- // Simpler handling of ASCII strings.
- //
- // NOTE: This assumes that the upper/lower case of an ASCII
- // character is also ASCII. This is currently the case, but it
- // might break in the future if we implement more context and locale
- // dependent upper/lower conversions.
- if (s->IsOneByteRepresentationUnderneath()) {
- // Same length as input.
- Handle<SeqOneByteString> result =
- isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
- DisallowHeapAllocation no_gc;
- String::FlatContent flat_content = s->GetFlatContent();
- DCHECK(flat_content.IsFlat());
- bool has_changed_character = false;
- bool is_ascii = FastAsciiConvert<Converter>(
- reinterpret_cast<char*>(result->GetChars()),
- reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
- length, &has_changed_character);
- // If not ASCII, we discard the result and take the 2 byte path.
- if (is_ascii) return has_changed_character ? *result : *s;
- }
-
- Handle<SeqString> result; // Same length as input.
- if (s->IsOneByteRepresentation()) {
- result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
- } else {
- result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
- }
-
- Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
- if (answer->IsException() || answer->IsString()) return answer;
-
- DCHECK(answer->IsSmi());
- length = Smi::cast(answer)->value();
- if (s->IsOneByteRepresentation() && length > 0) {
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, isolate->factory()->NewRawOneByteString(length));
- } else {
- if (length < 0) length = -length;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, isolate->factory()->NewRawTwoByteString(length));
- }
- return ConvertCaseHelper(isolate, *s, *result, length, mapping);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringToLowerCase) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
- return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping());
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringToUpperCase) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
- return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping());
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringTrim) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
- CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1);
- CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2);
-
- string = String::Flatten(string);
- int length = string->length();
-
- int left = 0;
- UnicodeCache* unicode_cache = isolate->unicode_cache();
- if (trimLeft) {
- while (left < length &&
- unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) {
- left++;
- }
- }
-
- int right = length;
- if (trimRight) {
- while (
- right > left &&
- unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) {
- right--;
- }
- }
-
- return *isolate->factory()->NewSubString(string, left, right);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringSplit) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
- CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
- RUNTIME_ASSERT(limit > 0);
-
- int subject_length = subject->length();
- int pattern_length = pattern->length();
- RUNTIME_ASSERT(pattern_length > 0);
-
- if (limit == 0xffffffffu) {
- Handle<Object> cached_answer(
- RegExpResultsCache::Lookup(isolate->heap(), *subject, *pattern,
- RegExpResultsCache::STRING_SPLIT_SUBSTRINGS),
- isolate);
- if (*cached_answer != Smi::FromInt(0)) {
- // The cache FixedArray is a COW-array and can therefore be reused.
- Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(
- Handle<FixedArray>::cast(cached_answer));
- return *result;
- }
- }
-
- // The limit can be very large (0xffffffffu), but since the pattern
- // isn't empty, we can never create more parts than ~half the length
- // of the subject.
-
- subject = String::Flatten(subject);
- pattern = String::Flatten(pattern);
-
- static const int kMaxInitialListCapacity = 16;
-
- 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_scope.zone());
-
- FindStringIndicesDispatch(isolate, *subject, *pattern, &indices, limit,
- zone_scope.zone());
-
- if (static_cast<uint32_t>(indices.length()) < limit) {
- indices.Add(subject_length, zone_scope.zone());
- }
-
- // The list indices now contains the end of each part to create.
-
- // Create JSArray of substrings separated by separator.
- int part_count = indices.length();
-
- Handle<JSArray> result = isolate->factory()->NewJSArray(part_count);
- JSObject::EnsureCanContainHeapObjectElements(result);
- result->set_length(Smi::FromInt(part_count));
-
- DCHECK(result->HasFastObjectElements());
-
- if (part_count == 1 && indices.at(0) == subject_length) {
- FixedArray::cast(result->elements())->set(0, *subject);
- return *result;
- }
-
- Handle<FixedArray> elements(FixedArray::cast(result->elements()));
- int part_start = 0;
- for (int i = 0; i < part_count; i++) {
- HandleScope local_loop_handle(isolate);
- int part_end = indices.at(i);
- Handle<String> substring =
- isolate->factory()->NewProperSubString(subject, part_start, part_end);
- elements->set(i, *substring);
- part_start = part_end + pattern_length;
- }
-
- if (limit == 0xffffffffu) {
- if (result->HasFastObjectElements()) {
- RegExpResultsCache::Enter(isolate, subject, pattern, elements,
- RegExpResultsCache::STRING_SPLIT_SUBSTRINGS);
- }
- }
-
- return *result;
-}
-
-
-// Copies Latin1 characters to the given fixed array looking up
-// one-char strings in the cache. Gives up on the first char that is
-// not in the cache and fills the remainder with smi zeros. Returns
-// the length of the successfully copied prefix.
-static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
- FixedArray* elements, int length) {
- DisallowHeapAllocation no_gc;
- FixedArray* one_byte_cache = heap->single_character_string_cache();
- Object* undefined = heap->undefined_value();
- int i;
- WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
- for (i = 0; i < length; ++i) {
- Object* value = one_byte_cache->get(chars[i]);
- if (value == undefined) break;
- elements->set(i, value, mode);
- }
- if (i < length) {
- DCHECK(Smi::FromInt(0) == 0);
- memset(elements->data_start() + i, 0, kPointerSize * (length - i));
- }
-#ifdef DEBUG
- for (int j = 0; j < length; ++j) {
- Object* element = elements->get(j);
- DCHECK(element == Smi::FromInt(0) ||
- (element->IsString() && String::cast(element)->LooksValid()));
- }
-#endif
- return i;
-}
-
-
-// Converts a String to JSArray.
-// For example, "foo" => ["f", "o", "o"].
-RUNTIME_FUNCTION(Runtime_StringToArray) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
- CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
-
- s = String::Flatten(s);
- const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
-
- Handle<FixedArray> elements;
- int position = 0;
- if (s->IsFlat() && s->IsOneByteRepresentation()) {
- // Try using cached chars where possible.
- elements = isolate->factory()->NewUninitializedFixedArray(length);
-
- DisallowHeapAllocation no_gc;
- String::FlatContent content = s->GetFlatContent();
- if (content.IsOneByte()) {
- Vector<const uint8_t> chars = content.ToOneByteVector();
- // Note, this will initialize all elements (not only the prefix)
- // to prevent GC from seeing partially initialized array.
- position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(),
- *elements, length);
- } else {
- MemsetPointer(elements->data_start(), isolate->heap()->undefined_value(),
- length);
- }
- } else {
- elements = isolate->factory()->NewFixedArray(length);
- }
- for (int i = position; i < length; ++i) {
- Handle<Object> str =
- isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
- elements->set(i, *str);
- }
-
-#ifdef DEBUG
- for (int i = 0; i < length; ++i) {
- DCHECK(String::cast(elements->get(i))->length() == 1);
- }
-#endif
-
- return *isolate->factory()->NewJSArrayWithElements(elements);
-}
-
-
RUNTIME_FUNCTION(Runtime_NewStringWrapper) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@@ -6650,91 +3266,6 @@
}
-bool Runtime::IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch) {
- unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
- int char_length = runtime_state->to_upper_mapping()->get(ch, 0, chars);
- return char_length == 0;
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToStringRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
-
- return *isolate->factory()->NumberToString(number);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
-
- return *isolate->factory()->NumberToString(number, false);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToInteger) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_DOUBLE_ARG_CHECKED(number, 0);
- return *isolate->factory()->NewNumber(DoubleToInteger(number));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToIntegerMapMinusZero) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_DOUBLE_ARG_CHECKED(number, 0);
- double double_value = DoubleToInteger(number);
- // Map both -0 and +0 to +0.
- if (double_value == 0) double_value = 0;
-
- return *isolate->factory()->NewNumber(double_value);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToJSUint32) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
- return *isolate->factory()->NewNumberFromUint(number);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberToJSInt32) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_DOUBLE_ARG_CHECKED(number, 0);
- return *isolate->factory()->NewNumberFromInt(DoubleToInt32(number));
-}
-
-
-// Converts a Number to a Smi, if possible. Returns NaN if the number is not
-// a small integer.
-RUNTIME_FUNCTION(Runtime_NumberToSmi) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(Object, obj, 0);
- if (obj->IsSmi()) {
- return obj;
- }
- if (obj->IsHeapNumber()) {
- double value = HeapNumber::cast(obj)->value();
- int int_value = FastD2I(value);
- if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
- return Smi::FromInt(int_value);
- }
- }
- return isolate->heap()->nan_value();
-}
-
-
RUNTIME_FUNCTION(Runtime_AllocateHeapNumber) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
@@ -6742,936 +3273,7 @@
}
-RUNTIME_FUNCTION(Runtime_NumberAdd) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- return *isolate->factory()->NewNumber(x + y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberSub) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- return *isolate->factory()->NewNumber(x - y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberMul) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- return *isolate->factory()->NewNumber(x * y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberUnaryMinus) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- return *isolate->factory()->NewNumber(-x);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberDiv) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- return *isolate->factory()->NewNumber(x / y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberMod) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- return *isolate->factory()->NewNumber(modulo(x, y));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberImul) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- // We rely on implementation-defined behavior below, but at least not on
- // undefined behavior.
- CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]);
- int32_t product = static_cast<int32_t>(x * y);
- return *isolate->factory()->NewNumberFromInt(product);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringAdd) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
- isolate->counters()->string_add_runtime()->Increment();
- Handle<String> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, isolate->factory()->NewConsString(str1, str2));
- return *result;
-}
-
-
-template <typename sinkchar>
-static inline void StringBuilderConcatHelper(String* special, sinkchar* sink,
- FixedArray* fixed_array,
- int array_length) {
- DisallowHeapAllocation no_gc;
- int position = 0;
- for (int i = 0; i < array_length; i++) {
- Object* element = fixed_array->get(i);
- if (element->IsSmi()) {
- // Smi encoding of position and length.
- int encoded_slice = Smi::cast(element)->value();
- int pos;
- int len;
- if (encoded_slice > 0) {
- // Position and length encoded in one smi.
- pos = StringBuilderSubstringPosition::decode(encoded_slice);
- len = StringBuilderSubstringLength::decode(encoded_slice);
- } else {
- // Position and length encoded in two smis.
- Object* obj = fixed_array->get(++i);
- DCHECK(obj->IsSmi());
- pos = Smi::cast(obj)->value();
- len = -encoded_slice;
- }
- String::WriteToFlat(special, sink + position, pos, pos + len);
- position += len;
- } else {
- String* string = String::cast(element);
- int element_length = string->length();
- String::WriteToFlat(string, sink + position, 0, element_length);
- position += element_length;
- }
- }
-}
-
-
-// Returns the result length of the concatenation.
-// On illegal argument, -1 is returned.
-static inline int StringBuilderConcatLength(int special_length,
- FixedArray* fixed_array,
- int array_length, bool* one_byte) {
- DisallowHeapAllocation no_gc;
- int position = 0;
- for (int i = 0; i < array_length; i++) {
- int increment = 0;
- Object* elt = fixed_array->get(i);
- if (elt->IsSmi()) {
- // Smi encoding of position and length.
- int smi_value = Smi::cast(elt)->value();
- int pos;
- int len;
- if (smi_value > 0) {
- // Position and length encoded in one smi.
- pos = StringBuilderSubstringPosition::decode(smi_value);
- len = StringBuilderSubstringLength::decode(smi_value);
- } else {
- // Position and length encoded in two smis.
- len = -smi_value;
- // Get the position and check that it is a positive smi.
- i++;
- if (i >= array_length) return -1;
- Object* next_smi = fixed_array->get(i);
- if (!next_smi->IsSmi()) return -1;
- pos = Smi::cast(next_smi)->value();
- if (pos < 0) return -1;
- }
- DCHECK(pos >= 0);
- DCHECK(len >= 0);
- if (pos > special_length || len > special_length - pos) return -1;
- increment = len;
- } else if (elt->IsString()) {
- String* element = String::cast(elt);
- int element_length = element->length();
- increment = element_length;
- if (*one_byte && !element->HasOnlyOneByteChars()) {
- *one_byte = false;
- }
- } else {
- return -1;
- }
- if (increment > String::kMaxLength - position) {
- return kMaxInt; // Provoke throw on allocation.
- }
- position += increment;
- }
- return position;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
- int32_t array_length;
- if (!args[1]->ToInt32(&array_length)) {
- THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
- }
- CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
-
- size_t actual_array_length = 0;
- RUNTIME_ASSERT(
- TryNumberToSize(isolate, array->length(), &actual_array_length));
- RUNTIME_ASSERT(array_length >= 0);
- RUNTIME_ASSERT(static_cast<size_t>(array_length) <= actual_array_length);
-
- // This assumption is used by the slice encoding in one or two smis.
- DCHECK(Smi::kMaxValue >= String::kMaxLength);
-
- RUNTIME_ASSERT(array->HasFastElements());
- JSObject::EnsureCanContainHeapObjectElements(array);
-
- int special_length = special->length();
- if (!array->HasFastObjectElements()) {
- return isolate->Throw(isolate->heap()->illegal_argument_string());
- }
-
- int length;
- bool one_byte = special->HasOnlyOneByteChars();
-
- {
- DisallowHeapAllocation no_gc;
- FixedArray* fixed_array = FixedArray::cast(array->elements());
- if (fixed_array->length() < array_length) {
- array_length = fixed_array->length();
- }
-
- if (array_length == 0) {
- return isolate->heap()->empty_string();
- } else if (array_length == 1) {
- Object* first = fixed_array->get(0);
- if (first->IsString()) return first;
- }
- length = StringBuilderConcatLength(special_length, fixed_array,
- array_length, &one_byte);
- }
-
- if (length == -1) {
- return isolate->Throw(isolate->heap()->illegal_argument_string());
- }
-
- if (one_byte) {
- Handle<SeqOneByteString> answer;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, answer, isolate->factory()->NewRawOneByteString(length));
- StringBuilderConcatHelper(*special, answer->GetChars(),
- FixedArray::cast(array->elements()),
- array_length);
- return *answer;
- } else {
- Handle<SeqTwoByteString> answer;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, answer, isolate->factory()->NewRawTwoByteString(length));
- StringBuilderConcatHelper(*special, answer->GetChars(),
- FixedArray::cast(array->elements()),
- array_length);
- return *answer;
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
- int32_t array_length;
- if (!args[1]->ToInt32(&array_length)) {
- THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
- }
- CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
- RUNTIME_ASSERT(array->HasFastObjectElements());
- RUNTIME_ASSERT(array_length >= 0);
-
- Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()));
- if (fixed_array->length() < array_length) {
- array_length = fixed_array->length();
- }
-
- if (array_length == 0) {
- return isolate->heap()->empty_string();
- } else if (array_length == 1) {
- Object* first = fixed_array->get(0);
- RUNTIME_ASSERT(first->IsString());
- return first;
- }
-
- int separator_length = separator->length();
- RUNTIME_ASSERT(separator_length > 0);
- int max_nof_separators =
- (String::kMaxLength + separator_length - 1) / separator_length;
- if (max_nof_separators < (array_length - 1)) {
- THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
- }
- int length = (array_length - 1) * separator_length;
- for (int i = 0; i < array_length; i++) {
- Object* element_obj = fixed_array->get(i);
- RUNTIME_ASSERT(element_obj->IsString());
- String* element = String::cast(element_obj);
- int increment = element->length();
- if (increment > String::kMaxLength - length) {
- STATIC_ASSERT(String::kMaxLength < kMaxInt);
- length = kMaxInt; // Provoke exception;
- break;
- }
- length += increment;
- }
-
- Handle<SeqTwoByteString> answer;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, answer, isolate->factory()->NewRawTwoByteString(length));
-
- DisallowHeapAllocation no_gc;
-
- uc16* sink = answer->GetChars();
-#ifdef DEBUG
- uc16* end = sink + length;
-#endif
-
- RUNTIME_ASSERT(fixed_array->get(0)->IsString());
- String* first = String::cast(fixed_array->get(0));
- String* separator_raw = *separator;
- int first_length = first->length();
- String::WriteToFlat(first, sink, 0, first_length);
- sink += first_length;
-
- for (int i = 1; i < array_length; i++) {
- DCHECK(sink + separator_length <= end);
- String::WriteToFlat(separator_raw, sink, 0, separator_length);
- sink += separator_length;
-
- RUNTIME_ASSERT(fixed_array->get(i)->IsString());
- String* element = String::cast(fixed_array->get(i));
- int element_length = element->length();
- DCHECK(sink + element_length <= end);
- String::WriteToFlat(element, sink, 0, element_length);
- sink += element_length;
- }
- DCHECK(sink == end);
-
- // Use %_FastOneByteArrayJoin instead.
- DCHECK(!answer->IsOneByteRepresentation());
- return *answer;
-}
-
-template <typename Char>
-static void JoinSparseArrayWithSeparator(FixedArray* elements,
- int elements_length,
- uint32_t array_length,
- String* separator,
- Vector<Char> buffer) {
- DisallowHeapAllocation no_gc;
- int previous_separator_position = 0;
- int separator_length = separator->length();
- int cursor = 0;
- for (int i = 0; i < elements_length; i += 2) {
- int position = NumberToInt32(elements->get(i));
- String* string = String::cast(elements->get(i + 1));
- int string_length = string->length();
- if (string->length() > 0) {
- while (previous_separator_position < position) {
- String::WriteToFlat<Char>(separator, &buffer[cursor], 0,
- separator_length);
- cursor += separator_length;
- previous_separator_position++;
- }
- String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length);
- cursor += string->length();
- }
- }
- if (separator_length > 0) {
- // Array length must be representable as a signed 32-bit number,
- // otherwise the total string length would have been too large.
- DCHECK(array_length <= 0x7fffffff); // Is int32_t.
- int last_array_index = static_cast<int>(array_length - 1);
- while (previous_separator_position < last_array_index) {
- String::WriteToFlat<Char>(separator, &buffer[cursor], 0,
- separator_length);
- cursor += separator_length;
- previous_separator_position++;
- }
- }
- DCHECK(cursor <= buffer.length());
-}
-
-
-RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0);
- CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
- CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
- // elements_array is fast-mode JSarray of alternating positions
- // (increasing order) and strings.
- RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements());
- // array_length is length of original array (used to add separators);
- // separator is string to put between elements. Assumed to be non-empty.
- RUNTIME_ASSERT(array_length > 0);
-
- // Find total length of join result.
- int string_length = 0;
- bool is_one_byte = separator->IsOneByteRepresentation();
- bool overflow = false;
- CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length());
- RUNTIME_ASSERT(elements_length <= elements_array->elements()->length());
- RUNTIME_ASSERT((elements_length & 1) == 0); // Even length.
- FixedArray* elements = FixedArray::cast(elements_array->elements());
- for (int i = 0; i < elements_length; i += 2) {
- RUNTIME_ASSERT(elements->get(i)->IsNumber());
- CONVERT_NUMBER_CHECKED(uint32_t, position, Uint32, elements->get(i));
- RUNTIME_ASSERT(position < array_length);
- RUNTIME_ASSERT(elements->get(i + 1)->IsString());
- }
-
- {
- DisallowHeapAllocation no_gc;
- for (int i = 0; i < elements_length; i += 2) {
- String* string = String::cast(elements->get(i + 1));
- int length = string->length();
- if (is_one_byte && !string->IsOneByteRepresentation()) {
- is_one_byte = false;
- }
- if (length > String::kMaxLength ||
- String::kMaxLength - length < string_length) {
- overflow = true;
- break;
- }
- string_length += length;
- }
- }
-
- int separator_length = separator->length();
- if (!overflow && separator_length > 0) {
- if (array_length <= 0x7fffffffu) {
- int separator_count = static_cast<int>(array_length) - 1;
- int remaining_length = String::kMaxLength - string_length;
- if ((remaining_length / separator_length) >= separator_count) {
- string_length += separator_length * (array_length - 1);
- } else {
- // Not room for the separators within the maximal string length.
- overflow = true;
- }
- } else {
- // Nonempty separator and at least 2^31-1 separators necessary
- // means that the string is too large to create.
- STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
- overflow = true;
- }
- }
- if (overflow) {
- // Throw an exception if the resulting string is too large. See
- // https://code.google.com/p/chromium/issues/detail?id=336820
- // for details.
- THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
- }
-
- if (is_one_byte) {
- Handle<SeqOneByteString> result = isolate->factory()
- ->NewRawOneByteString(string_length)
- .ToHandleChecked();
- JoinSparseArrayWithSeparator<uint8_t>(
- FixedArray::cast(elements_array->elements()), elements_length,
- array_length, *separator,
- Vector<uint8_t>(result->GetChars(), string_length));
- return *result;
- } else {
- Handle<SeqTwoByteString> result = isolate->factory()
- ->NewRawTwoByteString(string_length)
- .ToHandleChecked();
- JoinSparseArrayWithSeparator<uc16>(
- FixedArray::cast(elements_array->elements()), elements_length,
- array_length, *separator,
- Vector<uc16>(result->GetChars(), string_length));
- return *result;
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberOr) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromInt(x | y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberAnd) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromInt(x & y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberXor) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromInt(x ^ y);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberShl) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromInt(x << (y & 0x1f));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberShr) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromUint(x >> (y & 0x1f));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberSar) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
- CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
- return *isolate->factory()->NewNumberFromInt(
- ArithmeticShiftRight(x, y & 0x1f));
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberEquals) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- if (std::isnan(x)) return Smi::FromInt(NOT_EQUAL);
- if (std::isnan(y)) return Smi::FromInt(NOT_EQUAL);
- if (x == y) return Smi::FromInt(EQUAL);
- Object* result;
- if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
- result = Smi::FromInt(EQUAL);
- } else {
- result = Smi::FromInt(NOT_EQUAL);
- }
- return result;
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringEquals) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
-
- bool not_equal = !String::Equals(x, y);
- // This is slightly convoluted because the value that signifies
- // equality is 0 and inequality is 1 so we have to negate the result
- // from String::Equals.
- DCHECK(not_equal == 0 || not_equal == 1);
- STATIC_ASSERT(EQUAL == 0);
- STATIC_ASSERT(NOT_EQUAL == 1);
- return Smi::FromInt(not_equal);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NumberCompare) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 3);
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- CONVERT_ARG_HANDLE_CHECKED(Object, uncomparable_result, 2)
- if (std::isnan(x) || std::isnan(y)) return *uncomparable_result;
- if (x == y) return Smi::FromInt(EQUAL);
- if (isless(x, y)) return Smi::FromInt(LESS);
- return Smi::FromInt(GREATER);
-}
-
-
-// Compare two Smis as if they were converted to strings and then
-// compared lexicographically.
-RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- CONVERT_SMI_ARG_CHECKED(x_value, 0);
- CONVERT_SMI_ARG_CHECKED(y_value, 1);
-
- // If the integers are equal so are the string representations.
- if (x_value == y_value) return Smi::FromInt(EQUAL);
-
- // If one of the integers is zero the normal integer order is the
- // same as the lexicographic order of the string representations.
- if (x_value == 0 || y_value == 0)
- return Smi::FromInt(x_value < y_value ? LESS : GREATER);
-
- // If only one of the integers is negative the negative number is
- // smallest because the char code of '-' is less than the char code
- // of any digit. Otherwise, we make both values positive.
-
- // Use unsigned values otherwise the logic is incorrect for -MIN_INT on
- // architectures using 32-bit Smis.
- uint32_t x_scaled = x_value;
- uint32_t y_scaled = y_value;
- if (x_value < 0 || y_value < 0) {
- if (y_value >= 0) return Smi::FromInt(LESS);
- if (x_value >= 0) return Smi::FromInt(GREATER);
- x_scaled = -x_value;
- y_scaled = -y_value;
- }
-
- static const uint32_t kPowersOf10[] = {
- 1, 10, 100, 1000,
- 10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000,
- 100 * 1000 * 1000, 1000 * 1000 * 1000};
-
- // If the integers have the same number of decimal digits they can be
- // compared directly as the numeric order is the same as the
- // lexicographic order. If one integer has fewer digits, it is scaled
- // by some power of 10 to have the same number of digits as the longer
- // integer. If the scaled integers are equal it means the shorter
- // integer comes first in the lexicographic order.
-
- // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
- int x_log2 = IntegerLog2(x_scaled);
- int x_log10 = ((x_log2 + 1) * 1233) >> 12;
- x_log10 -= x_scaled < kPowersOf10[x_log10];
-
- int y_log2 = IntegerLog2(y_scaled);
- int y_log10 = ((y_log2 + 1) * 1233) >> 12;
- y_log10 -= y_scaled < kPowersOf10[y_log10];
-
- int tie = EQUAL;
-
- if (x_log10 < y_log10) {
- // X has fewer digits. We would like to simply scale up X but that
- // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
- // be scaled up to 9_000_000_000. So we scale up by the next
- // smallest power and scale down Y to drop one digit. It is OK to
- // drop one digit from the longer integer since the final digit is
- // past the length of the shorter integer.
- x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
- y_scaled /= 10;
- tie = LESS;
- } else if (y_log10 < x_log10) {
- y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
- x_scaled /= 10;
- tie = GREATER;
- }
-
- if (x_scaled < y_scaled) return Smi::FromInt(LESS);
- if (x_scaled > y_scaled) return Smi::FromInt(GREATER);
- return Smi::FromInt(tie);
-}
-
-
-RUNTIME_FUNCTION(Runtime_StringCompare) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 2);
-
- CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
-
- isolate->counters()->string_compare_runtime()->Increment();
-
- // A few fast case tests before we flatten.
- if (x.is_identical_to(y)) return Smi::FromInt(EQUAL);
- if (y->length() == 0) {
- if (x->length() == 0) return Smi::FromInt(EQUAL);
- return Smi::FromInt(GREATER);
- } else if (x->length() == 0) {
- return Smi::FromInt(LESS);
- }
-
- int d = x->Get(0) - y->Get(0);
- if (d < 0)
- return Smi::FromInt(LESS);
- else if (d > 0)
- return Smi::FromInt(GREATER);
-
- // Slow case.
- x = String::Flatten(x);
- y = String::Flatten(y);
-
- DisallowHeapAllocation no_gc;
- Object* equal_prefix_result = Smi::FromInt(EQUAL);
- int prefix_length = x->length();
- if (y->length() < prefix_length) {
- prefix_length = y->length();
- equal_prefix_result = Smi::FromInt(GREATER);
- } else if (y->length() > prefix_length) {
- equal_prefix_result = Smi::FromInt(LESS);
- }
- int r;
- String::FlatContent x_content = x->GetFlatContent();
- String::FlatContent y_content = y->GetFlatContent();
- if (x_content.IsOneByte()) {
- Vector<const uint8_t> x_chars = x_content.ToOneByteVector();
- if (y_content.IsOneByte()) {
- Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
- r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
- } else {
- Vector<const uc16> y_chars = y_content.ToUC16Vector();
- r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
- }
- } else {
- Vector<const uc16> x_chars = x_content.ToUC16Vector();
- if (y_content.IsOneByte()) {
- Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
- r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
- } else {
- Vector<const uc16> y_chars = y_content.ToUC16Vector();
- r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
- }
- }
- Object* result;
- if (r == 0) {
- result = equal_prefix_result;
- } else {
- result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
- }
- return result;
-}
-
-
-#define RUNTIME_UNARY_MATH(Name, name) \
- RUNTIME_FUNCTION(Runtime_Math##Name) { \
- HandleScope scope(isolate); \
- DCHECK(args.length() == 1); \
- isolate->counters()->math_##name()->Increment(); \
- CONVERT_DOUBLE_ARG_CHECKED(x, 0); \
- return *isolate->factory()->NewHeapNumber(std::name(x)); \
- }
-
-RUNTIME_UNARY_MATH(Acos, acos)
-RUNTIME_UNARY_MATH(Asin, asin)
-RUNTIME_UNARY_MATH(Atan, atan)
-RUNTIME_UNARY_MATH(LogRT, log)
-#undef RUNTIME_UNARY_MATH
-
-
-RUNTIME_FUNCTION(Runtime_DoubleHi) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- uint64_t integer = double_to_uint64(x);
- integer = (integer >> 32) & 0xFFFFFFFFu;
- return *isolate->factory()->NewNumber(static_cast<int32_t>(integer));
-}
-
-
-RUNTIME_FUNCTION(Runtime_DoubleLo) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- return *isolate->factory()->NewNumber(
- static_cast<int32_t>(double_to_uint64(x) & 0xFFFFFFFFu));
-}
-
-
-RUNTIME_FUNCTION(Runtime_ConstructDouble) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_NUMBER_CHECKED(uint32_t, hi, Uint32, args[0]);
- CONVERT_NUMBER_CHECKED(uint32_t, lo, Uint32, args[1]);
- uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo;
- return *isolate->factory()->NewNumber(uint64_to_double(result));
-}
-
-
-RUNTIME_FUNCTION(Runtime_RemPiO2) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- Factory* factory = isolate->factory();
- double y[2];
- int n = fdlibm::rempio2(x, y);
- Handle<FixedArray> array = factory->NewFixedArray(3);
- Handle<HeapNumber> y0 = factory->NewHeapNumber(y[0]);
- Handle<HeapNumber> y1 = factory->NewHeapNumber(y[1]);
- array->set(0, Smi::FromInt(n));
- array->set(1, *y0);
- array->set(2, *y1);
- return *factory->NewJSArrayWithElements(array);
-}
-
-
-static const double kPiDividedBy4 = 0.78539816339744830962;
-
-
-RUNTIME_FUNCTION(Runtime_MathAtan2) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- isolate->counters()->math_atan2()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- double result;
- if (std::isinf(x) && std::isinf(y)) {
- // Make sure that the result in case of two infinite arguments
- // is a multiple of Pi / 4. The sign of the result is determined
- // by the first argument (x) and the sign of the second argument
- // determines the multiplier: one or three.
- int multiplier = (x < 0) ? -1 : 1;
- if (y < 0) multiplier *= 3;
- result = multiplier * kPiDividedBy4;
- } else {
- result = std::atan2(x, y);
- }
- return *isolate->factory()->NewNumber(result);
-}
-
-
-RUNTIME_FUNCTION(Runtime_MathExpRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- isolate->counters()->math_exp()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- lazily_initialize_fast_exp();
- return *isolate->factory()->NewNumber(fast_exp(x));
-}
-
-
-RUNTIME_FUNCTION(Runtime_MathFloorRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- isolate->counters()->math_floor()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- return *isolate->factory()->NewNumber(Floor(x));
-}
-
-
-// Slow version of Math.pow. We check for fast paths for special cases.
-// Used if VFP3 is not available.
-RUNTIME_FUNCTION(Runtime_MathPowSlow) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- isolate->counters()->math_pow()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
-
- // If the second argument is a smi, it is much faster to call the
- // custom powi() function than the generic pow().
- if (args[1]->IsSmi()) {
- int y = args.smi_at(1);
- return *isolate->factory()->NewNumber(power_double_int(x, y));
- }
-
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- double result = power_helper(x, y);
- if (std::isnan(result)) return isolate->heap()->nan_value();
- return *isolate->factory()->NewNumber(result);
-}
-
-
-// Fast version of Math.pow if we know that y is not an integer and y is not
-// -0.5 or 0.5. Used as slow case from full codegen.
-RUNTIME_FUNCTION(Runtime_MathPowRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- isolate->counters()->math_pow()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- CONVERT_DOUBLE_ARG_CHECKED(y, 1);
- if (y == 0) {
- return Smi::FromInt(1);
- } else {
- double result = power_double_double(x, y);
- if (std::isnan(result)) return isolate->heap()->nan_value();
- return *isolate->factory()->NewNumber(result);
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_RoundNumber) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_NUMBER_ARG_HANDLE_CHECKED(input, 0);
- isolate->counters()->math_round()->Increment();
-
- if (!input->IsHeapNumber()) {
- DCHECK(input->IsSmi());
- return *input;
- }
-
- Handle<HeapNumber> number = Handle<HeapNumber>::cast(input);
-
- double value = number->value();
- int exponent = number->get_exponent();
- int sign = number->get_sign();
-
- if (exponent < -1) {
- // Number in range ]-0.5..0.5[. These always round to +/-zero.
- if (sign) return isolate->heap()->minus_zero_value();
- return Smi::FromInt(0);
- }
-
- // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
- // should be rounded to 2^30, which is not smi (for 31-bit smis, similar
- // argument holds for 32-bit smis).
- if (!sign && exponent < kSmiValueSize - 2) {
- return Smi::FromInt(static_cast<int>(value + 0.5));
- }
-
- // If the magnitude is big enough, there's no place for fraction part. If we
- // try to add 0.5 to this number, 1.0 will be added instead.
- if (exponent >= 52) {
- return *number;
- }
-
- if (sign && value >= -0.5) return isolate->heap()->minus_zero_value();
-
- // Do not call NumberFromDouble() to avoid extra checks.
- return *isolate->factory()->NewNumber(Floor(value + 0.5));
-}
-
-
-RUNTIME_FUNCTION(Runtime_MathSqrtRT) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- isolate->counters()->math_sqrt()->Increment();
-
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- return *isolate->factory()->NewNumber(fast_sqrt(x));
-}
-
-
-RUNTIME_FUNCTION(Runtime_MathFround) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_DOUBLE_ARG_CHECKED(x, 0);
- float xf = DoubleToFloat32(x);
- return *isolate->factory()->NewNumber(xf);
-}
RUNTIME_FUNCTION(Runtime_DateMakeDay) {
@@ -8166,468 +3768,6 @@
}
-RUNTIME_FUNCTION(Runtime_CompileLazy) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
-#ifdef DEBUG
- if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
- PrintF("[unoptimized: ");
- function->PrintName();
- PrintF("]\n");
- }
-#endif
-
- // Compile the target function.
- DCHECK(function->shared()->allows_lazy_compilation());
-
- Handle<Code> code;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, code,
- Compiler::GetLazyCode(function));
- DCHECK(code->kind() == Code::FUNCTION ||
- code->kind() == Code::OPTIMIZED_FUNCTION);
- function->ReplaceCode(*code);
- return *code;
-}
-
-
-RUNTIME_FUNCTION(Runtime_CompileOptimized) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- CONVERT_BOOLEAN_ARG_CHECKED(concurrent, 1);
-
- Handle<Code> unoptimized(function->shared()->code());
- if (!isolate->use_crankshaft() ||
- function->shared()->optimization_disabled() ||
- isolate->DebuggerHasBreakPoints()) {
- // If the function is not optimizable or debugger is active continue
- // using the code from the full compiler.
- if (FLAG_trace_opt) {
- PrintF("[failed to optimize ");
- function->PrintName();
- PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
- function->shared()->optimization_disabled() ? "F" : "T",
- isolate->DebuggerHasBreakPoints() ? "T" : "F");
- }
- function->ReplaceCode(*unoptimized);
- return function->code();
- }
-
- Compiler::ConcurrencyMode mode =
- concurrent ? Compiler::CONCURRENT : Compiler::NOT_CONCURRENT;
- Handle<Code> code;
- if (Compiler::GetOptimizedCode(function, unoptimized, mode).ToHandle(&code)) {
- function->ReplaceCode(*code);
- } else {
- function->ReplaceCode(function->shared()->code());
- }
-
- DCHECK(function->code()->kind() == Code::FUNCTION ||
- function->code()->kind() == Code::OPTIMIZED_FUNCTION ||
- function->IsInOptimizationQueue());
- return function->code();
-}
-
-
-class ActivationsFinder : public ThreadVisitor {
- public:
- Code* code_;
- bool has_code_activations_;
-
- explicit ActivationsFinder(Code* code)
- : code_(code), has_code_activations_(false) {}
-
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
- JavaScriptFrameIterator it(isolate, top);
- VisitFrames(&it);
- }
-
- void VisitFrames(JavaScriptFrameIterator* it) {
- for (; !it->done(); it->Advance()) {
- JavaScriptFrame* frame = it->frame();
- if (code_->contains(frame->pc())) has_code_activations_ = true;
- }
- }
-};
-
-
-RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 0);
- Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
- DCHECK(AllowHeapAllocation::IsAllowed());
- delete deoptimizer;
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_SMI_ARG_CHECKED(type_arg, 0);
- Deoptimizer::BailoutType type =
- static_cast<Deoptimizer::BailoutType>(type_arg);
- Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
- DCHECK(AllowHeapAllocation::IsAllowed());
-
- Handle<JSFunction> function = deoptimizer->function();
- Handle<Code> optimized_code = deoptimizer->compiled_code();
-
- DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
- DCHECK(type == deoptimizer->bailout_type());
-
- // Make sure to materialize objects before causing any allocation.
- JavaScriptFrameIterator it(isolate);
- deoptimizer->MaterializeHeapObjects(&it);
- delete deoptimizer;
-
- JavaScriptFrame* frame = it.frame();
- RUNTIME_ASSERT(frame->function()->IsJSFunction());
- DCHECK(frame->function() == *function);
-
- // Avoid doing too much work when running with --always-opt and keep
- // the optimized code around.
- if (FLAG_always_opt || type == Deoptimizer::LAZY) {
- return isolate->heap()->undefined_value();
- }
-
- // Search for other activations of the same function and code.
- ActivationsFinder activations_finder(*optimized_code);
- activations_finder.VisitFrames(&it);
- isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
-
- if (!activations_finder.has_code_activations_) {
- if (function->code() == *optimized_code) {
- if (FLAG_trace_deopt) {
- PrintF("[removing optimized code for: ");
- function->PrintName();
- PrintF("]\n");
- }
- function->ReplaceCode(function->shared()->code());
- // Evict optimized code for this function from the cache so that it
- // doesn't get used for new closures.
- function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
- "notify deoptimized");
- }
- } else {
- // TODO(titzer): we should probably do DeoptimizeCodeList(code)
- // unconditionally if the code is not already marked for deoptimization.
- // If there is an index by shared function info, all the better.
- Deoptimizer::DeoptimizeFunction(*function);
- }
-
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- if (!function->IsOptimized()) return isolate->heap()->undefined_value();
-
- // TODO(turbofan): Deoptimization is not supported yet.
- if (function->code()->is_turbofanned() && !FLAG_turbo_deoptimization) {
- return isolate->heap()->undefined_value();
- }
-
- Deoptimizer::DeoptimizeFunction(*function);
-
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_ClearFunctionTypeFeedback) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- function->shared()->ClearTypeFeedbackInfo();
- Code* unoptimized = function->shared()->code();
- if (unoptimized->kind() == Code::FUNCTION) {
- unoptimized->ClearInlineCaches();
- }
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 0);
-#if defined(USE_SIMULATOR)
- return isolate->heap()->true_value();
-#else
- return isolate->heap()->false_value();
-#endif
-}
-
-
-RUNTIME_FUNCTION(Runtime_IsConcurrentRecompilationSupported) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 0);
- return isolate->heap()->ToBoolean(
- isolate->concurrent_recompilation_enabled());
-}
-
-
-RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
- HandleScope scope(isolate);
- RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- // The following two assertions are lifted from the DCHECKs inside
- // JSFunction::MarkForOptimization().
- RUNTIME_ASSERT(!function->shared()->is_generator());
- RUNTIME_ASSERT(function->shared()->allows_lazy_compilation() ||
- (function->code()->kind() == Code::FUNCTION &&
- function->code()->optimizable()));
-
- // If the function is optimized, just return.
- if (function->IsOptimized()) return isolate->heap()->undefined_value();
-
- function->MarkForOptimization();
-
- Code* unoptimized = function->shared()->code();
- if (args.length() == 2 && unoptimized->kind() == Code::FUNCTION) {
- CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
- if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("osr")) && FLAG_use_osr) {
- // Start patching from the currently patched loop nesting level.
- DCHECK(BackEdgeTable::Verify(isolate, unoptimized));
- isolate->runtime_profiler()->AttemptOnStackReplacement(
- *function, Code::kMaxLoopNestingMarker);
- } else if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("concurrent")) &&
- isolate->concurrent_recompilation_enabled()) {
- function->MarkForConcurrentOptimization();
- }
- }
-
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(JSFunction, function, 0);
- function->shared()->set_optimization_disabled(true);
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
- HandleScope scope(isolate);
- RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
- if (!isolate->use_crankshaft()) {
- return Smi::FromInt(4); // 4 == "never".
- }
- bool sync_with_compiler_thread = true;
- if (args.length() == 2) {
- CONVERT_ARG_HANDLE_CHECKED(String, sync, 1);
- if (sync->IsOneByteEqualTo(STATIC_CHAR_VECTOR("no sync"))) {
- sync_with_compiler_thread = false;
- }
- }
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- if (isolate->concurrent_recompilation_enabled() &&
- sync_with_compiler_thread) {
- while (function->IsInOptimizationQueue()) {
- isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
- base::OS::Sleep(50);
- }
- }
- if (FLAG_always_opt) {
- // We may have always opt, but that is more best-effort than a real
- // promise, so we still say "no" if it is not optimized.
- return function->IsOptimized() ? Smi::FromInt(3) // 3 == "always".
- : Smi::FromInt(2); // 2 == "no".
- }
- if (FLAG_deopt_every_n_times) {
- return Smi::FromInt(6); // 6 == "maybe deopted".
- }
- if (function->IsOptimized() && function->code()->is_turbofanned()) {
- return Smi::FromInt(7); // 7 == "TurboFan compiler".
- }
- return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
- : Smi::FromInt(2); // 2 == "no".
-}
-
-
-RUNTIME_FUNCTION(Runtime_UnblockConcurrentRecompilation) {
- DCHECK(args.length() == 0);
- RUNTIME_ASSERT(FLAG_block_concurrent_recompilation);
- RUNTIME_ASSERT(isolate->concurrent_recompilation_enabled());
- isolate->optimizing_compiler_thread()->Unblock();
- return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_GetOptimizationCount) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- return Smi::FromInt(function->shared()->opt_count());
-}
-
-
-static bool IsSuitableForOnStackReplacement(Isolate* isolate,
- Handle<JSFunction> function,
- Handle<Code> current_code) {
- // Keep track of whether we've succeeded in optimizing.
- if (!isolate->use_crankshaft() || !current_code->optimizable()) return false;
- // If we are trying to do OSR when there are already optimized
- // activations of the function, it means (a) the function is directly or
- // indirectly recursive and (b) an optimized invocation has been
- // deoptimized so that we are currently in an unoptimized activation.
- // Check for optimized activations of this function.
- for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- if (frame->is_optimized() && frame->function() == *function) return false;
- }
-
- return true;
-}
-
-
-RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
- Handle<Code> caller_code(function->shared()->code());
-
- // We're not prepared to handle a function with arguments object.
- DCHECK(!function->shared()->uses_arguments());
-
- RUNTIME_ASSERT(FLAG_use_osr);
-
- // Passing the PC in the javascript frame from the caller directly is
- // not GC safe, so we walk the stack to get it.
- JavaScriptFrameIterator it(isolate);
- JavaScriptFrame* frame = it.frame();
- if (!caller_code->contains(frame->pc())) {
- // Code on the stack may not be the code object referenced by the shared
- // function info. It may have been replaced to include deoptimization data.
- caller_code = Handle<Code>(frame->LookupCode());
- }
-
- uint32_t pc_offset =
- static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
-
-#ifdef DEBUG
- DCHECK_EQ(frame->function(), *function);
- DCHECK_EQ(frame->LookupCode(), *caller_code);
- DCHECK(caller_code->contains(frame->pc()));
-#endif // DEBUG
-
-
- BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset);
- DCHECK(!ast_id.IsNone());
-
- Compiler::ConcurrencyMode mode =
- isolate->concurrent_osr_enabled() &&
- (function->shared()->ast_node_count() > 512)
- ? Compiler::CONCURRENT
- : Compiler::NOT_CONCURRENT;
- Handle<Code> result = Handle<Code>::null();
-
- OptimizedCompileJob* job = NULL;
- if (mode == Compiler::CONCURRENT) {
- // Gate the OSR entry with a stack check.
- BackEdgeTable::AddStackCheck(caller_code, pc_offset);
- // Poll already queued compilation jobs.
- OptimizingCompilerThread* thread = isolate->optimizing_compiler_thread();
- if (thread->IsQueuedForOSR(function, ast_id)) {
- if (FLAG_trace_osr) {
- PrintF("[OSR - Still waiting for queued: ");
- function->PrintName();
- PrintF(" at AST id %d]\n", ast_id.ToInt());
- }
- return NULL;
- }
-
- job = thread->FindReadyOSRCandidate(function, ast_id);
- }
-
- if (job != NULL) {
- if (FLAG_trace_osr) {
- PrintF("[OSR - Found ready: ");
- function->PrintName();
- PrintF(" at AST id %d]\n", ast_id.ToInt());
- }
- result = Compiler::GetConcurrentlyOptimizedCode(job);
- } else if (IsSuitableForOnStackReplacement(isolate, function, caller_code)) {
- if (FLAG_trace_osr) {
- PrintF("[OSR - Compiling: ");
- function->PrintName();
- PrintF(" at AST id %d]\n", ast_id.ToInt());
- }
- MaybeHandle<Code> maybe_result =
- Compiler::GetOptimizedCode(function, caller_code, mode, ast_id);
- if (maybe_result.ToHandle(&result) &&
- result.is_identical_to(isolate->builtins()->InOptimizationQueue())) {
- // Optimization is queued. Return to check later.
- return NULL;
- }
- }
-
- // Revert the patched back edge table, regardless of whether OSR succeeds.
- BackEdgeTable::Revert(isolate, *caller_code);
-
- // Check whether we ended up with usable optimized code.
- if (!result.is_null() && result->kind() == Code::OPTIMIZED_FUNCTION) {
- DeoptimizationInputData* data =
- DeoptimizationInputData::cast(result->deoptimization_data());
-
- if (data->OsrPcOffset()->value() >= 0) {
- DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
- if (FLAG_trace_osr) {
- PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
- ast_id.ToInt(), data->OsrPcOffset()->value());
- }
- // TODO(titzer): this is a massive hack to make the deopt counts
- // match. Fix heuristics for reenabling optimizations!
- function->shared()->increment_deopt_count();
-
- // TODO(titzer): Do not install code into the function.
- function->ReplaceCode(*result);
- return *result;
- }
- }
-
- // Failed.
- if (FLAG_trace_osr) {
- PrintF("[OSR - Failed: ");
- function->PrintName();
- PrintF(" at AST id %d]\n", ast_id.ToInt());
- }
-
- if (!function->IsOptimized()) {
- function->ReplaceCode(function->shared()->code());
- }
- return NULL;
-}
-
-
-RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2 || args.length() == 3);
-#ifdef DEBUG
- CONVERT_SMI_ARG_CHECKED(interval, 0);
- CONVERT_SMI_ARG_CHECKED(timeout, 1);
- isolate->heap()->set_allocation_timeout(timeout);
- FLAG_gc_interval = interval;
- if (args.length() == 3) {
- // Enable/disable inline allocation if requested.
- CONVERT_BOOLEAN_ARG_CHECKED(inline_allocation, 2);
- if (inline_allocation) {
- isolate->heap()->EnableInlineAllocation();
- } else {
- isolate->heap()->DisableInlineAllocation();
- }
- }
-#endif
- return isolate->heap()->undefined_value();
-}
-
-
RUNTIME_FUNCTION(Runtime_CheckIsBootstrapping) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 0);
@@ -9228,24 +4368,6 @@
}
-RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
-
- // First check if this is a real stack overflow.
- StackLimitCheck check(isolate);
- if (check.JsHasOverflowed()) {
- SealHandleScope shs(isolate);
- return isolate->StackOverflow();
- }
-
- isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
- return (function->IsOptimized()) ? function->code()
- : function->shared()->code();
-}
-
-
RUNTIME_FUNCTION(Runtime_Interrupt) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 0);
@@ -9300,45 +4422,6 @@
}
-RUNTIME_FUNCTION(Runtime_DebugPrint) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
-
- OFStream os(stdout);
-#ifdef DEBUG
- if (args[0]->IsString()) {
- // If we have a string, assume it's a code "marker"
- // and print some interesting cpu debugging info.
- JavaScriptFrameIterator it(isolate);
- JavaScriptFrame* frame = it.frame();
- os << "fp = " << frame->fp() << ", sp = " << frame->sp()
- << ", caller_sp = " << frame->caller_sp() << ": ";
- } else {
- os << "DebugPrint: ";
- }
- args[0]->Print(os);
- if (args[0]->IsHeapObject()) {
- os << "\n";
- HeapObject::cast(args[0])->map()->Print(os);
- }
-#else
- // ShortPrint is available in release mode. Print is not.
- os << Brief(args[0]);
-#endif
- os << endl;
-
- return args[0]; // return TOS
-}
-
-
-RUNTIME_FUNCTION(Runtime_DebugTrace) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 0);
- isolate->PrintStack(stdout);
- return isolate->heap()->undefined_value();
-}
-
-
RUNTIME_FUNCTION(Runtime_DateCurrentTime) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
@@ -9461,130 +4544,6 @@
}
-RUNTIME_FUNCTION(Runtime_ParseJson) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
-
- source = String::Flatten(source);
- // Optimized fast case where we only have Latin1 characters.
- Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
- source->IsSeqOneByteString()
- ? JsonParser<true>::Parse(source)
- : JsonParser<false>::Parse(source));
- return *result;
-}
-
-
-bool CodeGenerationFromStringsAllowed(Isolate* isolate,
- Handle<Context> context) {
- DCHECK(context->allow_code_gen_from_strings()->IsFalse());
- // Check with callback if set.
- AllowCodeGenerationFromStringsCallback callback =
- isolate->allow_code_gen_callback();
- if (callback == NULL) {
- // No callback set and code generation disallowed.
- return false;
- } else {
- // Callback set. Let it decide if code generation is allowed.
- VMState<EXTERNAL> state(isolate);
- return callback(v8::Utils::ToLocal(context));
- }
-}
-
-
-RUNTIME_FUNCTION(Runtime_CompileString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
- CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
-
- // Extract native context.
- Handle<Context> context(isolate->native_context());
-
- // Check if native context allows code generation from
- // strings. Throw an exception if it doesn't.
- if (context->allow_code_gen_from_strings()->IsFalse() &&
- !CodeGenerationFromStringsAllowed(isolate, context)) {
- Handle<Object> error_message =
- context->ErrorMessageForCodeGenerationFromStrings();
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewEvalError("code_gen_from_strings",
- HandleVector<Object>(&error_message, 1)));
- }
-
- // Compile source string in the native context.
- ParseRestriction restriction = function_literal_only
- ? ONLY_SINGLE_FUNCTION_LITERAL
- : NO_PARSE_RESTRICTION;
- Handle<JSFunction> fun;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, fun,
- Compiler::GetFunctionFromEval(source, context, SLOPPY, restriction,
- RelocInfo::kNoPosition));
- return *fun;
-}
-
-
-static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
- Handle<Object> receiver,
- StrictMode strict_mode,
- int scope_position) {
- Handle<Context> context = Handle<Context>(isolate->context());
- Handle<Context> native_context = Handle<Context>(context->native_context());
-
- // Check if native context allows code generation from
- // strings. Throw an exception if it doesn't.
- if (native_context->allow_code_gen_from_strings()->IsFalse() &&
- !CodeGenerationFromStringsAllowed(isolate, native_context)) {
- Handle<Object> error_message =
- native_context->ErrorMessageForCodeGenerationFromStrings();
- Handle<Object> error;
- MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
- "code_gen_from_strings", HandleVector<Object>(&error_message, 1));
- if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
- return MakePair(isolate->heap()->exception(), NULL);
- }
-
- // Deal with a normal eval call with a string argument. Compile it
- // and return the compiled function bound in the local context.
- static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
- Handle<JSFunction> compiled;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, compiled,
- Compiler::GetFunctionFromEval(source, context, strict_mode, restriction,
- scope_position),
- MakePair(isolate->heap()->exception(), NULL));
- return MakePair(*compiled, *receiver);
-}
-
-
-RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 5);
-
- Handle<Object> callee = args.at<Object>(0);
-
- // If "eval" didn't refer to the original GlobalEval, it's not a
- // direct call to eval.
- // (And even if it is, but the first argument isn't a string, just let
- // execution default to an indirect call to eval, which will also return
- // the first argument without doing anything).
- if (*callee != isolate->native_context()->global_eval_fun() ||
- !args[1]->IsString()) {
- return MakePair(*callee, isolate->heap()->undefined_value());
- }
-
- DCHECK(args[3]->IsSmi());
- DCHECK(args.smi_at(3) == SLOPPY || args.smi_at(3) == STRICT);
- StrictMode strict_mode = static_cast<StrictMode>(args.smi_at(3));
- DCHECK(args[4]->IsSmi());
- return CompileGlobalEval(isolate, args.at<String>(1), args.at<Object>(2),
- strict_mode, args.smi_at(4));
-}
-
-
RUNTIME_FUNCTION(Runtime_AllocateInNewSpace) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@@ -10326,23 +5285,6 @@
}
-// This will not allocate (flatten the string), but it may run
-// very slowly for very deeply nested ConsStrings. For debugging use only.
-RUNTIME_FUNCTION(Runtime_GlobalPrint) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_ARG_CHECKED(String, string, 0);
- ConsStringIteratorOp op;
- StringCharacterStream stream(string, &op);
- while (stream.HasMore()) {
- uint16_t character = stream.GetNext();
- PrintF("%c", character);
- }
- return string;
-}
-
-
// Moves all own elements of an object, that are below a limit, to positions
// starting at zero. All undefined values are placed after non-undefined values,
// and are followed by non-existing element. Does not change the length
@@ -12949,14 +7891,6 @@
}
-RUNTIME_FUNCTION(Runtime_SystemBreak) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 0);
- base::OS::DebugBreak();
- return isolate->heap()->undefined_value();
-}
-
-
RUNTIME_FUNCTION(Runtime_DebugDisassembleFunction) {
HandleScope scope(isolate);
#ifdef DEBUG
@@ -13338,18 +8272,6 @@
}
-// Sets a v8 flag.
-RUNTIME_FUNCTION(Runtime_SetFlags) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(String, arg, 0);
- SmartArrayPointer<char> flags =
- arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
- FlagList::SetFlagsFromString(flags.get(), StrLength(flags.get()));
- return isolate->heap()->undefined_value();
-}
-
-
// Performs a GC.
// Presently, it only does a full GC.
RUNTIME_FUNCTION(Runtime_CollectGarbage) {
@@ -13512,48 +8434,6 @@
}
-RUNTIME_FUNCTION(Runtime_Abort) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_SMI_ARG_CHECKED(message_id, 0);
- const char* message =
- GetBailoutReason(static_cast<BailoutReason>(message_id));
- base::OS::PrintError("abort: %s\n", message);
- isolate->PrintStack(stderr);
- base::OS::Abort();
- UNREACHABLE();
- return NULL;
-}
-
-
-RUNTIME_FUNCTION(Runtime_AbortJS) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, message, 0);
- base::OS::PrintError("abort: %s\n", message->ToCString().get());
- isolate->PrintStack(stderr);
- base::OS::Abort();
- UNREACHABLE();
- return NULL;
-}
-
-
-RUNTIME_FUNCTION(Runtime_FlattenString) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
- return *String::Flatten(str);
-}
-
-
-RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 0);
- isolate->heap()->NotifyContextDisposed();
- return isolate->heap()->undefined_value();
-}
-
-
RUNTIME_FUNCTION(Runtime_LoadMutableDouble) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
@@ -13753,26 +8633,6 @@
}
-#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
- RUNTIME_FUNCTION(Runtime_Has##Name) { \
- CONVERT_ARG_CHECKED(JSObject, obj, 0); \
- return isolate->heap()->ToBoolean(obj->Has##Name()); \
- }
-
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SloppyArgumentsElements)
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements)
-// Properties test sitting with elements tests - not fooling anyone.
-ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastProperties)
-
-#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
-
-
#define TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION(Type, type, TYPE, ctype, size) \
RUNTIME_FUNCTION(Runtime_HasExternal##Type##Elements) { \
CONVERT_ARG_CHECKED(JSObject, obj, 0); \
@@ -13795,15 +8655,6 @@
#undef FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
-RUNTIME_FUNCTION(Runtime_HaveSameMap) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- CONVERT_ARG_CHECKED(JSObject, obj1, 0);
- CONVERT_ARG_CHECKED(JSObject, obj2, 1);
- return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
-}
-
-
RUNTIME_FUNCTION(Runtime_IsJSGlobalProxy) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
@@ -13861,20 +8712,6 @@
}
-RUNTIME_FUNCTION(Runtime_ObservationWeakMapCreate) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 0);
- // TODO(adamk): Currently this runtime function is only called three times per
- // isolate. If it's called more often, the map should be moved into the
- // strong root list.
- Handle<Map> map =
- isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
- Handle<JSWeakMap> weakmap =
- Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
- return *WeakCollectionInitialize(isolate, weakmap);
-}
-
-
static bool ContextsHaveSameOrigin(Handle<Context> context1,
Handle<Context> context2) {
return context1->security_token() == context2->security_token();
@@ -14292,46 +9129,6 @@
}
-RUNTIME_FUNCTION(RuntimeReference_StringCharFromCode) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_CharFromCode(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_StringCharAt) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- if (!args[0]->IsString()) return Smi::FromInt(0);
- if (!args[1]->IsNumber()) return Smi::FromInt(0);
- if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string();
- Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
- if (code->IsNaN()) return isolate->heap()->empty_string();
- return __RT_impl_Runtime_CharFromCode(Arguments(1, &code), isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_OneByteSeqStringSetChar) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 3);
- CONVERT_INT32_ARG_CHECKED(index, 0);
- CONVERT_INT32_ARG_CHECKED(value, 1);
- CONVERT_ARG_CHECKED(SeqOneByteString, string, 2);
- string->SeqOneByteStringSet(index, value);
- return string;
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_TwoByteSeqStringSetChar) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 3);
- CONVERT_INT32_ARG_CHECKED(index, 0);
- CONVERT_INT32_ARG_CHECKED(value, 1);
- CONVERT_ARG_CHECKED(SeqTwoByteString, string, 2);
- string->SeqTwoByteStringSet(index, value);
- return string;
-}
-
-
RUNTIME_FUNCTION(RuntimeReference_ObjectEquals) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 2);
@@ -14380,22 +9177,6 @@
}
-RUNTIME_FUNCTION(RuntimeReference_MathPow) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_MathPowSlow(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_IsMinusZero) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 1);
- CONVERT_ARG_CHECKED(Object, obj, 0);
- if (!obj->IsHeapNumber()) return isolate->heap()->false_value();
- HeapNumber* number = HeapNumber::cast(obj);
- return isolate->heap()->ToBoolean(IsMinusZero(number->value()));
-}
-
-
RUNTIME_FUNCTION(RuntimeReference_HasCachedArrayIndex) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
@@ -14438,46 +9219,6 @@
}
-RUNTIME_FUNCTION(RuntimeReference_StringCharCodeAt) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- if (!args[0]->IsString()) return isolate->heap()->undefined_value();
- if (!args[1]->IsNumber()) return isolate->heap()->undefined_value();
- if (std::isinf(args.number_at(1))) return isolate->heap()->nan_value();
- return __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_StringAdd) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_StringAdd(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_SubString) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_SubString(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_StringCompare) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_StringCompare(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_RegExpExec) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_RegExpExecRT(args, isolate);
-}
-
-
-RUNTIME_FUNCTION(RuntimeReference_RegExpConstructResult) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_RegExpConstructResult(args, isolate);
-}
-
-
RUNTIME_FUNCTION(RuntimeReference_GetFromCache) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
@@ -14487,12 +9228,6 @@
}
-RUNTIME_FUNCTION(RuntimeReference_NumberToString) {
- SealHandleScope shs(isolate);
- return __RT_impl_Runtime_NumberToStringRT(args, isolate);
-}
-
-
RUNTIME_FUNCTION(RuntimeReference_DebugIsActive) {
SealHandleScope shs(isolate);
return Smi::FromInt(isolate->debug()->is_active());
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index abd35c0..da8511b 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -186,7 +186,9 @@
F(HomeObjectSymbol, 0, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
- F(LoadFromSuper, 3, 1)
+ F(LoadFromSuper, 3, 1) \
+ F(StoreToSuper_Strict, 4, 1) \
+ F(StoreToSuper_Sloppy, 4, 1)
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
@@ -822,8 +824,6 @@
static int StringMatch(Isolate* isolate, Handle<String> sub,
Handle<String> pat, int index);
- static bool IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch);
-
// TODO(1240886): Some of the following methods are *not* handle safe, but
// accept handle arguments. This seems fragile.
diff --git a/src/runtime/string-builder.h b/src/runtime/string-builder.h
new file mode 100644
index 0000000..37ff7b7
--- /dev/null
+++ b/src/runtime/string-builder.h
@@ -0,0 +1,296 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_STRING_BUILDER_H_
+#define V8_STRING_BUILDER_H_
+
+namespace v8 {
+namespace internal {
+
+const int kStringBuilderConcatHelperLengthBits = 11;
+const int kStringBuilderConcatHelperPositionBits = 19;
+
+typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
+ StringBuilderSubstringLength;
+typedef BitField<int, kStringBuilderConcatHelperLengthBits,
+ kStringBuilderConcatHelperPositionBits>
+ StringBuilderSubstringPosition;
+
+
+template <typename sinkchar>
+static inline void StringBuilderConcatHelper(String* special, sinkchar* sink,
+ FixedArray* fixed_array,
+ int array_length) {
+ DisallowHeapAllocation no_gc;
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ Object* element = fixed_array->get(i);
+ if (element->IsSmi()) {
+ // Smi encoding of position and length.
+ int encoded_slice = Smi::cast(element)->value();
+ int pos;
+ int len;
+ if (encoded_slice > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(encoded_slice);
+ len = StringBuilderSubstringLength::decode(encoded_slice);
+ } else {
+ // Position and length encoded in two smis.
+ Object* obj = fixed_array->get(++i);
+ DCHECK(obj->IsSmi());
+ pos = Smi::cast(obj)->value();
+ len = -encoded_slice;
+ }
+ String::WriteToFlat(special, sink + position, pos, pos + len);
+ position += len;
+ } else {
+ String* string = String::cast(element);
+ int element_length = string->length();
+ String::WriteToFlat(string, sink + position, 0, element_length);
+ position += element_length;
+ }
+ }
+}
+
+
+// Returns the result length of the concatenation.
+// On illegal argument, -1 is returned.
+static inline int StringBuilderConcatLength(int special_length,
+ FixedArray* fixed_array,
+ int array_length, bool* one_byte) {
+ DisallowHeapAllocation no_gc;
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ int increment = 0;
+ Object* elt = fixed_array->get(i);
+ if (elt->IsSmi()) {
+ // Smi encoding of position and length.
+ int smi_value = Smi::cast(elt)->value();
+ int pos;
+ int len;
+ if (smi_value > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(smi_value);
+ len = StringBuilderSubstringLength::decode(smi_value);
+ } else {
+ // Position and length encoded in two smis.
+ len = -smi_value;
+ // Get the position and check that it is a positive smi.
+ i++;
+ if (i >= array_length) return -1;
+ Object* next_smi = fixed_array->get(i);
+ if (!next_smi->IsSmi()) return -1;
+ pos = Smi::cast(next_smi)->value();
+ if (pos < 0) return -1;
+ }
+ DCHECK(pos >= 0);
+ DCHECK(len >= 0);
+ if (pos > special_length || len > special_length - pos) return -1;
+ increment = len;
+ } else if (elt->IsString()) {
+ String* element = String::cast(elt);
+ int element_length = element->length();
+ increment = element_length;
+ if (*one_byte && !element->HasOnlyOneByteChars()) {
+ *one_byte = false;
+ }
+ } else {
+ return -1;
+ }
+ if (increment > String::kMaxLength - position) {
+ return kMaxInt; // Provoke throw on allocation.
+ }
+ position += increment;
+ }
+ return position;
+}
+
+
+class FixedArrayBuilder {
+ public:
+ explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
+ : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
+ length_(0),
+ has_non_smi_elements_(false) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(initial_capacity > 0);
+ }
+
+ explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
+ : array_(backing_store), length_(0), has_non_smi_elements_(false) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(backing_store->length() > 0);
+ }
+
+ bool HasCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ return (length >= required_length);
+ }
+
+ void EnsureCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ if (length < required_length) {
+ int new_length = length;
+ do {
+ new_length *= 2;
+ } while (new_length < required_length);
+ Handle<FixedArray> extended_array =
+ array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
+ array_->CopyTo(0, *extended_array, 0, length_);
+ array_ = extended_array;
+ }
+ }
+
+ void Add(Object* value) {
+ DCHECK(!value->IsSmi());
+ DCHECK(length_ < capacity());
+ array_->set(length_, value);
+ length_++;
+ has_non_smi_elements_ = true;
+ }
+
+ void Add(Smi* value) {
+ DCHECK(value->IsSmi());
+ DCHECK(length_ < capacity());
+ array_->set(length_, value);
+ length_++;
+ }
+
+ Handle<FixedArray> array() { return array_; }
+
+ int length() { return length_; }
+
+ int capacity() { return array_->length(); }
+
+ Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
+ JSArray::SetContent(target_array, array_);
+ target_array->set_length(Smi::FromInt(length_));
+ return target_array;
+ }
+
+
+ private:
+ Handle<FixedArray> array_;
+ int length_;
+ bool has_non_smi_elements_;
+};
+
+
+class ReplacementStringBuilder {
+ public:
+ ReplacementStringBuilder(Heap* heap, Handle<String> subject,
+ int estimated_part_count)
+ : heap_(heap),
+ array_builder_(heap->isolate(), estimated_part_count),
+ subject_(subject),
+ character_count_(0),
+ is_one_byte_(subject->IsOneByteRepresentation()) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(estimated_part_count > 0);
+ }
+
+ static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
+ int to) {
+ DCHECK(from >= 0);
+ int length = to - from;
+ DCHECK(length > 0);
+ if (StringBuilderSubstringLength::is_valid(length) &&
+ StringBuilderSubstringPosition::is_valid(from)) {
+ int encoded_slice = StringBuilderSubstringLength::encode(length) |
+ StringBuilderSubstringPosition::encode(from);
+ builder->Add(Smi::FromInt(encoded_slice));
+ } else {
+ // Otherwise encode as two smis.
+ builder->Add(Smi::FromInt(-length));
+ builder->Add(Smi::FromInt(from));
+ }
+ }
+
+
+ void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); }
+
+
+ void AddSubjectSlice(int from, int to) {
+ AddSubjectSlice(&array_builder_, from, to);
+ IncrementCharacterCount(to - from);
+ }
+
+
+ void AddString(Handle<String> string) {
+ int length = string->length();
+ DCHECK(length > 0);
+ AddElement(*string);
+ if (!string->IsOneByteRepresentation()) {
+ is_one_byte_ = false;
+ }
+ IncrementCharacterCount(length);
+ }
+
+
+ MaybeHandle<String> ToString() {
+ Isolate* isolate = heap_->isolate();
+ if (array_builder_.length() == 0) {
+ return isolate->factory()->empty_string();
+ }
+
+ Handle<String> joined_string;
+ if (is_one_byte_) {
+ Handle<SeqOneByteString> seq;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, seq,
+ isolate->factory()->NewRawOneByteString(character_count_), String);
+
+ DisallowHeapAllocation no_gc;
+ uint8_t* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ } else {
+ // Two-byte.
+ Handle<SeqTwoByteString> seq;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, seq,
+ isolate->factory()->NewRawTwoByteString(character_count_), String);
+
+ DisallowHeapAllocation no_gc;
+ uc16* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ }
+ return joined_string;
+ }
+
+
+ void IncrementCharacterCount(int by) {
+ if (character_count_ > String::kMaxLength - by) {
+ STATIC_ASSERT(String::kMaxLength < kMaxInt);
+ character_count_ = kMaxInt;
+ } else {
+ character_count_ += by;
+ }
+ }
+
+ private:
+ void AddElement(Object* element) {
+ DCHECK(element->IsSmi() || element->IsString());
+ DCHECK(array_builder_.capacity() > array_builder_.length());
+ array_builder_.Add(element);
+ }
+
+ Heap* heap_;
+ FixedArrayBuilder array_builder_;
+ Handle<String> subject_;
+ int character_count_;
+ bool is_one_byte_;
+};
+}
+} // namespace v8::internal
+
+#endif // V8_STRING_BUILDER_H_
diff --git a/src/serialize.cc b/src/serialize.cc
index 894a1be..9d59b6f 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -1846,7 +1846,7 @@
SnapshotByteSink* sink = FLAG_trace_code_serializer
? static_cast<SnapshotByteSink*>(&debug_sink)
: static_cast<SnapshotByteSink*>(&list_sink);
- CodeSerializer cs(isolate, sink, *source);
+ CodeSerializer cs(isolate, sink, *source, info->code());
DisallowHeapAllocation no_gc;
Object** location = Handle<Object>::cast(info).location();
cs.VisitPointer(location);
@@ -1867,57 +1867,78 @@
void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) {
- CHECK(o->IsHeapObject());
HeapObject* heap_object = HeapObject::cast(o);
- // The code-caches link to context-specific code objects, which
- // the startup and context serializes cannot currently handle.
- DCHECK(!heap_object->IsMap() ||
- Map::cast(heap_object)->code_cache() ==
- heap_object->GetHeap()->empty_fixed_array());
-
int root_index;
if ((root_index = RootIndex(heap_object, how_to_code)) != kInvalidRootIndex) {
PutRoot(root_index, heap_object, how_to_code, where_to_point, skip);
return;
}
- // TODO(yangguo) wire up global object.
- // TODO(yangguo) We cannot deal with different hash seeds yet.
- DCHECK(!heap_object->IsHashTable());
-
if (address_mapper_.IsMapped(heap_object)) {
SerializeReferenceToPreviousObject(heap_object, how_to_code, where_to_point,
skip);
return;
}
+ if (skip != 0) {
+ sink_->Put(kSkip, "SkipFromSerializeObject");
+ sink_->PutInt(skip, "SkipDistanceFromSerializeObject");
+ }
+
if (heap_object->IsCode()) {
Code* code_object = Code::cast(heap_object);
- DCHECK(!code_object->is_optimized_code());
- if (code_object->kind() == Code::BUILTIN) {
- SerializeBuiltin(code_object, how_to_code, where_to_point, skip);
- return;
- } else if (code_object->IsCodeStubOrIC()) {
- SerializeCodeStub(code_object, how_to_code, where_to_point, skip);
- return;
+ switch (code_object->kind()) {
+ case Code::OPTIMIZED_FUNCTION: // No optimized code compiled yet.
+ case Code::HANDLER: // No handlers patched in yet.
+ case Code::REGEXP: // No regexp literals initialized yet.
+ case Code::NUMBER_OF_KINDS: // Pseudo enum value.
+ CHECK(false);
+ case Code::BUILTIN:
+ SerializeBuiltin(code_object, how_to_code, where_to_point);
+ return;
+ case Code::STUB:
+ SerializeCodeStub(code_object, how_to_code, where_to_point);
+ return;
+#define IC_KIND_CASE(KIND) case Code::KIND:
+ IC_KIND_LIST(IC_KIND_CASE)
+#undef IC_KIND_CASE
+ SerializeHeapObject(code_object, how_to_code, where_to_point);
+ return;
+ // TODO(yangguo): add special handling to canonicalize ICs.
+ case Code::FUNCTION:
+ // Only serialize the code for the toplevel function. Replace code
+ // of included function literals by the lazy compile builtin.
+ // This is safe, as checked in Compiler::BuildFunctionInfo.
+ if (code_object != main_code_) {
+ Code* lazy = *isolate()->builtins()->CompileLazy();
+ SerializeBuiltin(lazy, how_to_code, where_to_point);
+ } else {
+ SerializeHeapObject(code_object, how_to_code, where_to_point);
+ }
+ return;
}
- code_object->ClearInlineCaches();
}
if (heap_object == source_) {
- SerializeSourceObject(how_to_code, where_to_point, skip);
+ SerializeSourceObject(how_to_code, where_to_point);
return;
}
- SerializeHeapObject(heap_object, how_to_code, where_to_point, skip);
+ // Past this point we should not see any (context-specific) maps anymore.
+ CHECK(!heap_object->IsMap());
+ // There should be no references to the global object embedded.
+ CHECK(!heap_object->IsJSGlobalProxy() && !heap_object->IsGlobalObject());
+ // There should be no hash table embedded. They would require rehashing.
+ CHECK(!heap_object->IsHashTable());
+
+ SerializeHeapObject(heap_object, how_to_code, where_to_point);
}
void CodeSerializer::SerializeHeapObject(HeapObject* heap_object,
HowToCode how_to_code,
- WhereToPoint where_to_point,
- int skip) {
+ WhereToPoint where_to_point) {
if (heap_object->IsScript()) {
// The wrapper cache uses a Foreign object to point to a global handle.
// However, the object visitor expects foreign objects to point to external
@@ -1925,11 +1946,6 @@
Script::cast(heap_object)->ClearWrapperCache();
}
- if (skip != 0) {
- sink_->Put(kSkip, "SkipFromSerializeObject");
- sink_->PutInt(skip, "SkipDistanceFromSerializeObject");
- }
-
if (FLAG_trace_code_serializer) {
PrintF("Encoding heap object: ");
heap_object->ShortPrint();
@@ -1944,12 +1960,7 @@
void CodeSerializer::SerializeBuiltin(Code* builtin, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip) {
- if (skip != 0) {
- sink_->Put(kSkip, "SkipFromSerializeBuiltin");
- sink_->PutInt(skip, "SkipDistanceFromSerializeBuiltin");
- }
-
+ WhereToPoint where_to_point) {
DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
(how_to_code == kPlain && where_to_point == kInnerPointer) ||
(how_to_code == kFromCode && where_to_point == kInnerPointer));
@@ -1967,25 +1978,13 @@
}
-void CodeSerializer::SerializeCodeStub(Code* code, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip) {
+void CodeSerializer::SerializeCodeStub(Code* stub, HowToCode how_to_code,
+ WhereToPoint where_to_point) {
DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
(how_to_code == kPlain && where_to_point == kInnerPointer) ||
(how_to_code == kFromCode && where_to_point == kInnerPointer));
- uint32_t stub_key = code->stub_key();
-
- if (stub_key == CodeStub::NoCacheKey()) {
- if (FLAG_trace_code_serializer) {
- PrintF("Encoding uncacheable code stub as heap object\n");
- }
- SerializeHeapObject(code, how_to_code, where_to_point, skip);
- return;
- }
-
- if (skip != 0) {
- sink_->Put(kSkip, "SkipFromSerializeCodeStub");
- sink_->PutInt(skip, "SkipDistanceFromSerializeCodeStub");
- }
+ uint32_t stub_key = stub->stub_key();
+ DCHECK(CodeStub::MajorKeyFromKey(stub_key) != CodeStub::NoCache);
int index = AddCodeStubKey(stub_key) + kCodeStubsBaseIndex;
@@ -2013,16 +2012,8 @@
void CodeSerializer::SerializeSourceObject(HowToCode how_to_code,
- WhereToPoint where_to_point,
- int skip) {
- if (skip != 0) {
- sink_->Put(kSkip, "SkipFromSerializeSourceObject");
- sink_->PutInt(skip, "SkipDistanceFromSerializeSourceObject");
- }
-
- if (FLAG_trace_code_serializer) {
- PrintF("Encoding source object\n");
- }
+ WhereToPoint where_to_point) {
+ if (FLAG_trace_code_serializer) PrintF("Encoding source object\n");
DCHECK(how_to_code == kPlain && where_to_point == kStartOfObject);
sink_->Put(kAttachedReference + how_to_code + where_to_point, "Source");
diff --git a/src/serialize.h b/src/serialize.h
index 7831536..616f8f1 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -577,19 +577,10 @@
class CodeSerializer : public Serializer {
public:
- CodeSerializer(Isolate* isolate, SnapshotByteSink* sink, String* source)
- : Serializer(isolate, sink), source_(source) {
- set_root_index_wave_front(Heap::kStrongRootListLength);
- InitializeCodeAddressMap();
- }
-
static ScriptData* Serialize(Isolate* isolate,
Handle<SharedFunctionInfo> info,
Handle<String> source);
- virtual void SerializeObject(Object* o, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip);
-
static Handle<SharedFunctionInfo> Deserialize(Isolate* isolate,
ScriptData* data,
Handle<String> source);
@@ -605,18 +596,29 @@
List<uint32_t>* stub_keys() { return &stub_keys_; }
private:
+ CodeSerializer(Isolate* isolate, SnapshotByteSink* sink, String* source,
+ Code* main_code)
+ : Serializer(isolate, sink), source_(source), main_code_(main_code) {
+ set_root_index_wave_front(Heap::kStrongRootListLength);
+ InitializeCodeAddressMap();
+ }
+
+ virtual void SerializeObject(Object* o, HowToCode how_to_code,
+ WhereToPoint where_to_point, int skip);
+
void SerializeBuiltin(Code* builtin, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip);
- void SerializeCodeStub(Code* code, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip);
- void SerializeSourceObject(HowToCode how_to_code, WhereToPoint where_to_point,
- int skip);
+ WhereToPoint where_to_point);
+ void SerializeCodeStub(Code* stub, HowToCode how_to_code,
+ WhereToPoint where_to_point);
+ void SerializeSourceObject(HowToCode how_to_code,
+ WhereToPoint where_to_point);
void SerializeHeapObject(HeapObject* heap_object, HowToCode how_to_code,
- WhereToPoint where_to_point, int skip);
+ WhereToPoint where_to_point);
int AddCodeStubKey(uint32_t stub_key);
DisallowHeapAllocation no_gc_;
String* source_;
+ Code* main_code_;
List<uint32_t> stub_keys_;
DISALLOW_COPY_AND_ASSIGN(CodeSerializer);
};
diff --git a/src/test/run-all-unittests.cc b/src/test/run-all-unittests.cc
index de56365..8c361dd 100644
--- a/src/test/run-all-unittests.cc
+++ b/src/test/run-all-unittests.cc
@@ -5,7 +5,6 @@
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include "src/base/compiler-specific.h"
-#include "src/v8.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace {
@@ -39,9 +38,6 @@
int main(int argc, char** argv) {
- // This forces some thread local key initialization that may be needed to
- // print out the names of the unit tests.
- i::V8::Initialize();
testing::InitGoogleMock(&argc, argv);
testing::AddGlobalTestEnvironment(new DefaultPlatformEnvironment);
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
diff --git a/src/uri.h b/src/uri.h
deleted file mode 100644
index 75f2605..0000000
--- a/src/uri.h
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef V8_URI_H_
-#define V8_URI_H_
-
-#include "src/v8.h"
-
-#include "src/conversions.h"
-#include "src/string-search.h"
-#include "src/utils.h"
-
-namespace v8 {
-namespace internal {
-
-
-template <typename Char>
-static INLINE(Vector<const Char> GetCharVector(Handle<String> string));
-
-
-template <>
-Vector<const uint8_t> GetCharVector(Handle<String> string) {
- String::FlatContent flat = string->GetFlatContent();
- DCHECK(flat.IsOneByte());
- return flat.ToOneByteVector();
-}
-
-
-template <>
-Vector<const uc16> GetCharVector(Handle<String> string) {
- String::FlatContent flat = string->GetFlatContent();
- DCHECK(flat.IsTwoByte());
- return flat.ToUC16Vector();
-}
-
-
-class URIUnescape : public AllStatic {
- public:
- template<typename Char>
- MUST_USE_RESULT static MaybeHandle<String> Unescape(Isolate* isolate,
- Handle<String> source);
-
- private:
- static const signed char kHexValue['g'];
-
- template<typename Char>
- MUST_USE_RESULT static MaybeHandle<String> UnescapeSlow(
- Isolate* isolate, Handle<String> string, int start_index);
-
- static INLINE(int TwoDigitHex(uint16_t character1, uint16_t character2));
-
- template <typename Char>
- static INLINE(int UnescapeChar(Vector<const Char> vector,
- int i,
- int length,
- int* step));
-};
-
-
-const signed char URIUnescape::kHexValue[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15 };
-
-
-template<typename Char>
-MaybeHandle<String> URIUnescape::Unescape(Isolate* isolate,
- Handle<String> source) {
- int index;
- { DisallowHeapAllocation no_allocation;
- StringSearch<uint8_t, Char> search(isolate, STATIC_CHAR_VECTOR("%"));
- index = search.Search(GetCharVector<Char>(source), 0);
- if (index < 0) return source;
- }
- return UnescapeSlow<Char>(isolate, source, index);
-}
-
-
-template <typename Char>
-MaybeHandle<String> URIUnescape::UnescapeSlow(
- Isolate* isolate, Handle<String> string, int start_index) {
- bool one_byte = true;
- int length = string->length();
-
- int unescaped_length = 0;
- { DisallowHeapAllocation no_allocation;
- Vector<const Char> vector = GetCharVector<Char>(string);
- for (int i = start_index; i < length; unescaped_length++) {
- int step;
- if (UnescapeChar(vector, i, length, &step) >
- String::kMaxOneByteCharCode) {
- one_byte = false;
- }
- i += step;
- }
- }
-
- DCHECK(start_index < length);
- Handle<String> first_part =
- isolate->factory()->NewProperSubString(string, 0, start_index);
-
- int dest_position = 0;
- Handle<String> second_part;
- DCHECK(unescaped_length <= String::kMaxLength);
- if (one_byte) {
- Handle<SeqOneByteString> dest = isolate->factory()->NewRawOneByteString(
- unescaped_length).ToHandleChecked();
- DisallowHeapAllocation no_allocation;
- Vector<const Char> vector = GetCharVector<Char>(string);
- for (int i = start_index; i < length; dest_position++) {
- int step;
- dest->SeqOneByteStringSet(dest_position,
- UnescapeChar(vector, i, length, &step));
- i += step;
- }
- second_part = dest;
- } else {
- Handle<SeqTwoByteString> dest = isolate->factory()->NewRawTwoByteString(
- unescaped_length).ToHandleChecked();
- DisallowHeapAllocation no_allocation;
- Vector<const Char> vector = GetCharVector<Char>(string);
- for (int i = start_index; i < length; dest_position++) {
- int step;
- dest->SeqTwoByteStringSet(dest_position,
- UnescapeChar(vector, i, length, &step));
- i += step;
- }
- second_part = dest;
- }
- return isolate->factory()->NewConsString(first_part, second_part);
-}
-
-
-int URIUnescape::TwoDigitHex(uint16_t character1, uint16_t character2) {
- if (character1 > 'f') return -1;
- int hi = kHexValue[character1];
- if (hi == -1) return -1;
- if (character2 > 'f') return -1;
- int lo = kHexValue[character2];
- if (lo == -1) return -1;
- return (hi << 4) + lo;
-}
-
-
-template <typename Char>
-int URIUnescape::UnescapeChar(Vector<const Char> vector,
- int i,
- int length,
- int* step) {
- uint16_t character = vector[i];
- int32_t hi = 0;
- int32_t lo = 0;
- if (character == '%' &&
- i <= length - 6 &&
- vector[i + 1] == 'u' &&
- (hi = TwoDigitHex(vector[i + 2],
- vector[i + 3])) != -1 &&
- (lo = TwoDigitHex(vector[i + 4],
- vector[i + 5])) != -1) {
- *step = 6;
- return (hi << 8) + lo;
- } else if (character == '%' &&
- i <= length - 3 &&
- (lo = TwoDigitHex(vector[i + 1],
- vector[i + 2])) != -1) {
- *step = 3;
- return lo;
- } else {
- *step = 1;
- return character;
- }
-}
-
-
-class URIEscape : public AllStatic {
- public:
- template<typename Char>
- MUST_USE_RESULT static MaybeHandle<String> Escape(Isolate* isolate,
- Handle<String> string);
-
- private:
- static const char kHexChars[17];
- static const char kNotEscaped[256];
-
- static bool IsNotEscaped(uint16_t c) { return kNotEscaped[c] != 0; }
-};
-
-
-const char URIEscape::kHexChars[] = "0123456789ABCDEF";
-
-
-// kNotEscaped is generated by the following:
-//
-// #!/bin/perl
-// for (my $i = 0; $i < 256; $i++) {
-// print "\n" if $i % 16 == 0;
-// my $c = chr($i);
-// my $escaped = 1;
-// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
-// print $escaped ? "0, " : "1, ";
-// }
-
-const char URIEscape::kNotEscaped[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-
-template<typename Char>
-MaybeHandle<String> URIEscape::Escape(Isolate* isolate, Handle<String> string) {
- DCHECK(string->IsFlat());
- int escaped_length = 0;
- int length = string->length();
-
- { DisallowHeapAllocation no_allocation;
- Vector<const Char> vector = GetCharVector<Char>(string);
- for (int i = 0; i < length; i++) {
- uint16_t c = vector[i];
- if (c >= 256) {
- escaped_length += 6;
- } else if (IsNotEscaped(c)) {
- escaped_length++;
- } else {
- escaped_length += 3;
- }
-
- // We don't allow strings that are longer than a maximal length.
- DCHECK(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
- if (escaped_length > String::kMaxLength) break; // Provoke exception.
- }
- }
-
- // No length change implies no change. Return original string if no change.
- if (escaped_length == length) return string;
-
- Handle<SeqOneByteString> dest;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, dest,
- isolate->factory()->NewRawOneByteString(escaped_length),
- String);
- int dest_position = 0;
-
- { DisallowHeapAllocation no_allocation;
- Vector<const Char> vector = GetCharVector<Char>(string);
- for (int i = 0; i < length; i++) {
- uint16_t c = vector[i];
- if (c >= 256) {
- dest->SeqOneByteStringSet(dest_position, '%');
- dest->SeqOneByteStringSet(dest_position+1, 'u');
- dest->SeqOneByteStringSet(dest_position+2, kHexChars[c >> 12]);
- dest->SeqOneByteStringSet(dest_position+3, kHexChars[(c >> 8) & 0xf]);
- dest->SeqOneByteStringSet(dest_position+4, kHexChars[(c >> 4) & 0xf]);
- dest->SeqOneByteStringSet(dest_position+5, kHexChars[c & 0xf]);
- dest_position += 6;
- } else if (IsNotEscaped(c)) {
- dest->SeqOneByteStringSet(dest_position, c);
- dest_position++;
- } else {
- dest->SeqOneByteStringSet(dest_position, '%');
- dest->SeqOneByteStringSet(dest_position+1, kHexChars[c >> 4]);
- dest->SeqOneByteStringSet(dest_position+2, kHexChars[c & 0xf]);
- dest_position += 3;
- }
- }
- }
-
- return dest;
-}
-
-} } // namespace v8::internal
-
-#endif // V8_URI_H_
diff --git a/src/version.cc b/src/version.cc
index 2b8d99c..067f8b3 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 29
-#define BUILD_NUMBER 92
+#define BUILD_NUMBER 93
#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/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 1c7f095..02c2d9c 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1849,13 +1849,19 @@
// Left-hand side can only be a property, a global or a (parameter or local)
// slot.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ enum LhsKind {
+ VARIABLE,
+ NAMED_PROPERTY,
+ KEYED_PROPERTY,
+ NAMED_SUPER_PROPERTY
+ };
LhsKind assign_type = VARIABLE;
Property* property = expr->target()->AsProperty();
if (property != NULL) {
assign_type = (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
+ ? (property->IsSuperAccess() ? NAMED_SUPER_PROPERTY
+ : NAMED_PROPERTY)
+ : KEYED_PROPERTY;
}
// Evaluate LHS expression.
@@ -1872,6 +1878,15 @@
VisitForStackValue(property->obj());
}
break;
+ case NAMED_SUPER_PROPERTY:
+ VisitForStackValue(property->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(property->obj()->AsSuperReference());
+ __ Push(result_register());
+ if (expr->is_compound()) {
+ __ Push(MemOperand(rsp, kPointerSize));
+ __ Push(result_register());
+ }
+ break;
case KEYED_PROPERTY: {
if (expr->is_compound()) {
VisitForStackValue(property->obj());
@@ -1899,6 +1914,10 @@
EmitNamedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
@@ -1944,6 +1963,9 @@
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyAssignment(expr);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyAssignment(expr);
break;
@@ -2271,6 +2293,8 @@
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
+ DCHECK(!prop->IsSuperAccess());
+
__ Move(LoadDescriptor::NameRegister(), key->value());
if (FLAG_vector_ics) {
__ Move(VectorLoadICDescriptor::SlotRegister(),
@@ -2283,15 +2307,12 @@
void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
+ // Stack: receiver, home_object
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
DCHECK(prop->IsSuperAccess());
- SuperReference* super_ref = prop->obj()->AsSuperReference();
- EmitLoadHomeObject(super_ref);
- __ Push(rax);
- VisitForStackValue(super_ref->this_var());
__ Push(key->value());
__ CallRuntime(Runtime::kLoadFromSuper, 3);
}
@@ -2526,6 +2547,24 @@
}
+void FullCodeGenerator::EmitNamedSuperPropertyAssignment(Assignment* expr) {
+ // Assignment to named property of super.
+ // rax : value
+ // stack : receiver ('this'), home_object
+ Property* prop = expr->target()->AsProperty();
+ DCHECK(prop != NULL);
+ Literal* key = prop->key()->AsLiteral();
+ DCHECK(key != NULL);
+
+ __ Push(rax);
+ __ Push(key->value());
+ __ CallRuntime((strict_mode() == STRICT ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy),
+ 4);
+ context()->Plug(rax);
+}
+
+
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
// Assignment to a property, using a keyed store IC.
@@ -2553,6 +2592,9 @@
__ movp(LoadDescriptor::ReceiverRegister(), rax);
EmitNamedPropertyLoad(expr);
} else {
+ VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(expr->obj()->AsSuperReference());
+ __ Push(result_register());
EmitNamedSuperPropertyLoad(expr);
}
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
@@ -2621,15 +2663,15 @@
__ Push(rax);
VisitForAccumulatorValue(super_ref->this_var());
__ Push(rax);
- __ Push(Operand(rsp, kPointerSize));
__ Push(rax);
+ __ Push(Operand(rsp, kPointerSize * 2));
__ Push(key->value());
// Stack here:
// - home_object
// - this (receiver)
- // - home_object <-- LoadFromSuper will pop here and below.
- // - this (receiver)
+ // - this (receiver) <-- LoadFromSuper will pop here and below.
+ // - home_object
// - key
__ CallRuntime(Runtime::kLoadFromSuper, 3);
@@ -4311,6 +4353,11 @@
if (prop != NULL) {
assign_type =
(prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ if (prop->IsSuperAccess()) {
+ // throw exception.
+ VisitSuperReference(prop->obj()->AsSuperReference());
+ return;
+ }
}
// Evaluate expression and get value.
diff --git a/src/x87/full-codegen-x87.cc b/src/x87/full-codegen-x87.cc
index 58328e0..0507604 100644
--- a/src/x87/full-codegen-x87.cc
+++ b/src/x87/full-codegen-x87.cc
@@ -1803,13 +1803,19 @@
// Left-hand side can only be a property, a global or a (parameter or local)
// slot.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ enum LhsKind {
+ VARIABLE,
+ NAMED_PROPERTY,
+ KEYED_PROPERTY,
+ NAMED_SUPER_PROPERTY
+ };
LhsKind assign_type = VARIABLE;
Property* property = expr->target()->AsProperty();
if (property != NULL) {
assign_type = (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
+ ? (property->IsSuperAccess() ? NAMED_SUPER_PROPERTY
+ : NAMED_PROPERTY)
+ : KEYED_PROPERTY;
}
// Evaluate LHS expression.
@@ -1817,6 +1823,15 @@
case VARIABLE:
// Nothing to do here.
break;
+ case NAMED_SUPER_PROPERTY:
+ VisitForStackValue(property->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(property->obj()->AsSuperReference());
+ __ push(result_register());
+ if (expr->is_compound()) {
+ __ push(MemOperand(esp, kPointerSize));
+ __ push(result_register());
+ }
+ break;
case NAMED_PROPERTY:
if (expr->is_compound()) {
// We need the receiver both on the stack and in the register.
@@ -1850,6 +1865,10 @@
EmitVariableLoad(expr->target()->AsVariableProxy());
PrepareForBailout(expr->target(), TOS_REG);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
case NAMED_PROPERTY:
EmitNamedPropertyLoad(property);
PrepareForBailoutForId(property->LoadId(), TOS_REG);
@@ -1899,6 +1918,9 @@
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
break;
+ case NAMED_SUPER_PROPERTY:
+ EmitNamedSuperPropertyAssignment(expr);
+ break;
case KEYED_PROPERTY:
EmitKeyedPropertyAssignment(expr);
break;
@@ -2226,6 +2248,8 @@
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
+ DCHECK(!prop->IsSuperAccess());
+
__ mov(LoadDescriptor::NameRegister(), Immediate(key->value()));
if (FLAG_vector_ics) {
__ mov(VectorLoadICDescriptor::SlotRegister(),
@@ -2238,15 +2262,12 @@
void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
+ // Stack: receiver, home_object.
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
DCHECK(!key->value()->IsSmi());
DCHECK(prop->IsSuperAccess());
- SuperReference* super_ref = prop->obj()->AsSuperReference();
- EmitLoadHomeObject(super_ref);
- __ push(eax);
- VisitForStackValue(super_ref->this_var());
__ push(Immediate(key->value()));
__ CallRuntime(Runtime::kLoadFromSuper, 3);
}
@@ -2517,6 +2538,24 @@
}
+void FullCodeGenerator::EmitNamedSuperPropertyAssignment(Assignment* expr) {
+ // Assignment to named property of super.
+ // eax : value
+ // stack : receiver ('this'), home_object
+ Property* prop = expr->target()->AsProperty();
+ DCHECK(prop != NULL);
+ Literal* key = prop->key()->AsLiteral();
+ DCHECK(key != NULL);
+
+ __ push(eax);
+ __ push(Immediate(key->value()));
+ __ CallRuntime((strict_mode() == STRICT ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy),
+ 4);
+ context()->Plug(eax);
+}
+
+
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
// Assignment to a property, using a keyed store IC.
// eax : value
@@ -2546,6 +2585,9 @@
__ Move(LoadDescriptor::ReceiverRegister(), result_register());
EmitNamedPropertyLoad(expr);
} else {
+ VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
+ EmitLoadHomeObject(expr->obj()->AsSuperReference());
+ __ push(result_register());
EmitNamedSuperPropertyLoad(expr);
}
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
@@ -2614,14 +2656,14 @@
__ push(eax);
VisitForAccumulatorValue(super_ref->this_var());
__ push(eax);
- __ push(Operand(esp, kPointerSize));
__ push(eax);
+ __ push(Operand(esp, kPointerSize * 2));
__ push(Immediate(key->value()));
// Stack here:
// - home_object
// - this (receiver)
- // - home_object <-- LoadFromSuper will pop here and below.
- // - this (receiver)
+ // - this (receiver) <-- LoadFromSuper will pop here and below.
+ // - home_object
// - key
__ CallRuntime(Runtime::kLoadFromSuper, 3);
@@ -4284,6 +4326,11 @@
if (prop != NULL) {
assign_type =
(prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ if (prop->IsSuperAccess()) {
+ // throw exception.
+ VisitSuperReference(prop->obj()->AsSuperReference());
+ return;
+ }
}
// Evaluate expression and get value.
diff --git a/src/x87/lithium-codegen-x87.cc b/src/x87/lithium-codegen-x87.cc
index 8f17258..00bbe5e 100644
--- a/src/x87/lithium-codegen-x87.cc
+++ b/src/x87/lithium-codegen-x87.cc
@@ -4021,12 +4021,65 @@
void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
- X87Register input_reg = ToX87Register(instr->value());
- X87Register output_reg = ToX87Register(instr->result());
- DCHECK(output_reg.is(input_reg));
- USE(output_reg);
- X87Fxch(input_reg);
- __ fsqrt();
+ X87Register input = ToX87Register(instr->value());
+ X87Register result_reg = ToX87Register(instr->result());
+ Register temp_result = ToRegister(instr->temp1());
+ Register temp = ToRegister(instr->temp2());
+ Label slow, done, smi, finish;
+ DCHECK(result_reg.is(input));
+
+ // Store input into Heap number and call runtime function kMathExpRT.
+ if (FLAG_inline_new) {
+ __ AllocateHeapNumber(temp_result, temp, no_reg, &slow);
+ __ jmp(&done, Label::kNear);
+ }
+
+ // Slow case: Call the runtime system to do the number allocation.
+ __ bind(&slow);
+ {
+ // TODO(3095996): Put a valid pointer value in the stack slot where the
+ // result register is stored, as this register is in the pointer map, but
+ // contains an integer value.
+ __ Move(temp_result, Immediate(0));
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ __ StoreToSafepointRegisterSlot(temp_result, eax);
+ }
+ __ bind(&done);
+ X87LoadForUsage(input);
+ __ fstp_d(FieldOperand(temp_result, HeapNumber::kValueOffset));
+
+ {
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ push(temp_result);
+ __ CallRuntimeSaveDoubles(Runtime::kMathSqrtRT);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ __ StoreToSafepointRegisterSlot(temp_result, eax);
+ }
+ X87PrepareToWrite(result_reg);
+ // return value of MathExpRT is Smi or Heap Number.
+ __ JumpIfSmi(temp_result, &smi);
+ // Heap number(double)
+ __ fld_d(FieldOperand(temp_result, HeapNumber::kValueOffset));
+ __ jmp(&finish);
+ // SMI
+ __ bind(&smi);
+ __ SmiUntag(temp_result);
+ __ push(temp_result);
+ __ fild_s(MemOperand(esp, 0));
+ __ pop(temp_result);
+ __ bind(&finish);
+ X87CommitWrite(result_reg);
}
diff --git a/src/x87/lithium-x87.cc b/src/x87/lithium-x87.cc
index f46aef9..9304b89 100644
--- a/src/x87/lithium-x87.cc
+++ b/src/x87/lithium-x87.cc
@@ -1252,8 +1252,10 @@
LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* instr) {
LOperand* input = UseRegisterAtStart(instr->value());
- LMathSqrt* result = new(zone()) LMathSqrt(input);
- return DefineSameAsFirst(result);
+ LOperand* temp1 = FixedTemp(ecx);
+ LOperand* temp2 = FixedTemp(edx);
+ LMathSqrt* result = new(zone()) LMathSqrt(input, temp1, temp2);
+ return MarkAsCall(DefineSameAsFirst(result), instr);
}
diff --git a/src/x87/lithium-x87.h b/src/x87/lithium-x87.h
index cf0e7b2..dbb18ec 100644
--- a/src/x87/lithium-x87.h
+++ b/src/x87/lithium-x87.h
@@ -973,13 +973,19 @@
};
-class LMathSqrt FINAL : public LTemplateInstruction<1, 1, 0> {
+class LMathSqrt FINAL : public LTemplateInstruction<1, 1, 2> {
public:
- explicit LMathSqrt(LOperand* value) {
+ explicit LMathSqrt(LOperand* value,
+ LOperand* temp1,
+ LOperand* temp2) {
inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
}
LOperand* value() { return inputs_[0]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt")
};
diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp
index 6a57763..f993d26 100644
--- a/test/cctest/cctest.gyp
+++ b/test/cctest/cctest.gyp
@@ -53,6 +53,7 @@
'compiler/graph-tester.h',
'compiler/simplified-graph-builder.cc',
'compiler/simplified-graph-builder.h',
+ 'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc',
'compiler/test-changes-lowering.cc',
'compiler/test-codegen-deopt.cc',
diff --git a/test/cctest/compiler/codegen-tester.cc b/test/cctest/compiler/codegen-tester.cc
index b1874f5..be445de 100644
--- a/test/cctest/compiler/codegen-tester.cc
+++ b/test/cctest/compiler/codegen-tester.cc
@@ -4,6 +4,7 @@
#include "src/v8.h"
+#include "src/compiler/generic-node-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"
diff --git a/test/cctest/compiler/test-basic-block-profiler.cc b/test/cctest/compiler/test-basic-block-profiler.cc
new file mode 100644
index 0000000..dd96499
--- /dev/null
+++ b/test/cctest/compiler/test-basic-block-profiler.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/basic-block-profiler.h"
+#include "src/compiler/generic-node-inl.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef RawMachineAssembler::Label MLabel;
+
+class BasicBlockProfilerTest : public RawMachineAssemblerTester<int32_t> {
+ public:
+ BasicBlockProfilerTest() : RawMachineAssemblerTester<int32_t>(kMachInt32) {
+ FLAG_turbo_profiling = true;
+ }
+
+ void ResetCounts() { isolate()->basic_block_profiler()->ResetCounts(); }
+
+ void Expect(size_t size, uint32_t* expected) {
+ CHECK_NE(NULL, isolate()->basic_block_profiler());
+ const BasicBlockProfiler::DataList* l =
+ isolate()->basic_block_profiler()->data_list();
+ CHECK_NE(0, static_cast<int>(l->size()));
+ const BasicBlockProfiler::Data* data = l->back();
+ CHECK_EQ(static_cast<int>(size), static_cast<int>(data->n_blocks()));
+ const uint32_t* counts = data->counts();
+ for (size_t i = 0; i < size; ++i) {
+ CHECK_EQ(static_cast<int>(expected[i]), static_cast<int>(counts[i]));
+ }
+ }
+};
+
+
+TEST(ProfileDiamond) {
+ BasicBlockProfilerTest m;
+
+ MLabel blocka, blockb, end;
+ m.Branch(m.Parameter(0), &blocka, &blockb);
+ m.Bind(&blocka);
+ m.Goto(&end);
+ m.Bind(&blockb);
+ m.Goto(&end);
+ m.Bind(&end);
+ m.Return(m.Int32Constant(0));
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {1, 1, 0, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.ResetCounts();
+
+ m.Call(1);
+ {
+ uint32_t expected[] = {1, 0, 1, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {2, 1, 1, 2};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+
+TEST(ProfileLoop) {
+ BasicBlockProfilerTest m;
+
+ MLabel header, body, end;
+ Node* one = m.Int32Constant(1);
+ m.Goto(&header);
+
+ m.Bind(&header);
+ Node* count = m.Phi(kMachInt32, m.Parameter(0), one);
+ m.Branch(count, &body, &end);
+
+ m.Bind(&body);
+ count->ReplaceInput(1, m.Int32Sub(count, one));
+ m.Goto(&header);
+
+ m.Bind(&end);
+ m.Return(one);
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ uint32_t runs[] = {0, 1, 500, 10000};
+ for (size_t i = 0; i < arraysize(runs); i++) {
+ m.ResetCounts();
+ CHECK_EQ(1, m.Call(static_cast<int>(runs[i])));
+ uint32_t expected[] = {1, runs[i] + 1, runs[i], 1};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-branch-combine.cc b/test/cctest/compiler/test-branch-combine.cc
index cd3472d..52590c0 100644
--- a/test/cctest/compiler/test-branch-combine.cc
+++ b/test/cctest/compiler/test-branch-combine.cc
@@ -4,6 +4,7 @@
#include "src/v8.h"
+#include "src/compiler/generic-node-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"
diff --git a/test/cctest/compiler/test-machine-operator-reducer.cc b/test/cctest/compiler/test-machine-operator-reducer.cc
index eca1f3c..9a41bc5 100644
--- a/test/cctest/compiler/test-machine-operator-reducer.cc
+++ b/test/cctest/compiler/test-machine-operator-reducer.cc
@@ -687,9 +687,9 @@
pr != nans.end(); ++pr) {
Node* nan1 = R->Constant<double>(*pl);
Node* nan2 = R->Constant<double>(*pr);
- R->CheckBinop(nan1, x, nan1); // x % NaN => NaN
- R->CheckBinop(nan1, nan1, x); // NaN % x => NaN
- R->CheckBinop(nan1, nan2, nan1); // NaN % NaN => NaN
+ R->CheckBinop(nan1, x, nan1); // x op NaN => NaN
+ R->CheckBinop(nan1, nan1, x); // NaN op x => NaN
+ R->CheckBinop(nan1, nan2, nan1); // NaN op NaN => NaN
}
}
}
@@ -706,8 +706,15 @@
}
}
- FOR_FLOAT64_INPUTS(i) { R.CheckPutConstantOnRight(*i); }
- // TODO(titzer): CheckNans(&R);
+ FOR_FLOAT64_INPUTS(i) {
+ Double tmp(*i);
+ if (!tmp.IsSpecial() || tmp.IsInfinite()) {
+ // Don't check NaNs as they are reduced more.
+ R.CheckPutConstantOnRight(*i);
+ }
+ }
+
+ CheckNans(&R);
}
@@ -721,7 +728,13 @@
R.CheckFoldBinop<double>(x - y, x, y);
}
}
- // TODO(titzer): CheckNans(&R);
+
+ Node* zero = R.Constant<double>(0.0);
+ Node* x = R.Parameter();
+
+ R.CheckBinop(x, x, zero); // x - 0.0 => x
+
+ CheckNans(&R);
}
@@ -783,6 +796,11 @@
}
}
+ Node* x = R.Parameter();
+ Node* zero = R.Constant<double>(0.0);
+
+ R.CheckFoldBinop<double>(v8::base::OS::nan_value(), x, zero);
+
CheckNans(&R);
}
diff --git a/test/cctest/compiler/test-simplified-lowering.cc b/test/cctest/compiler/test-simplified-lowering.cc
index e67df9b..bafa2d8 100644
--- a/test/cctest/compiler/test-simplified-lowering.cc
+++ b/test/cctest/compiler/test-simplified-lowering.cc
@@ -235,7 +235,7 @@
const int index = 12;
const int array_length = 2 * index;
ElementAccess buffer_access =
- AccessBuilder::ForBackingStoreElement(kMachInt8);
+ AccessBuilder::ForTypedArrayElement(v8::kExternalInt8Array, true);
Node* backing_store = t.LoadField(
AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0));
Node* load =
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 66dc5a0..0330ac8 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -1589,6 +1589,34 @@
}
+THREADED_TEST(IsGeneratorFunctionOrObject) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ CompileRun("function *gen() { yield 1; }\nfunction func() {}");
+ v8::Handle<Value> gen = CompileRun("gen");
+ v8::Handle<Value> genObj = CompileRun("gen()");
+ v8::Handle<Value> object = CompileRun("{a:42}");
+ v8::Handle<Value> func = CompileRun("func");
+
+ CHECK(gen->IsGeneratorFunction());
+ CHECK(gen->IsFunction());
+ CHECK(!gen->IsGeneratorObject());
+
+ CHECK(!genObj->IsGeneratorFunction());
+ CHECK(!genObj->IsFunction());
+ CHECK(genObj->IsGeneratorObject());
+
+ CHECK(!object->IsGeneratorFunction());
+ CHECK(!object->IsFunction());
+ CHECK(!object->IsGeneratorObject());
+
+ CHECK(!func->IsGeneratorFunction());
+ CHECK(func->IsFunction());
+ CHECK(!func->IsGeneratorObject());
+}
+
+
THREADED_TEST(ArgumentsObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
@@ -9656,12 +9684,36 @@
LocalContext env;
env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
- v8::TryCatch try_catch;
- CompileRun(
- "function f() { return super.hasOwnProperty; };"
- "var m = f.toMethod(prohibited);"
- "m();");
- CHECK(try_catch.HasCaught());
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { return super.hasOwnProperty; };"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { super.hasOwnProperty = function () {}; };"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "Object.defineProperty(Object.prototype, 'x', { set : function(){}});"
+ "function f() { "
+ " 'use strict';"
+ " super.x = function () {}; "
+ "};"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
}
diff --git a/test/mjsunit/harmony/super.js b/test/mjsunit/harmony/super.js
index 89fb4b1..809ba10 100644
--- a/test/mjsunit/harmony/super.js
+++ b/test/mjsunit/harmony/super.js
@@ -18,6 +18,8 @@
function fDerived() {
assertEquals("Base this is Derived", super.f());
+ var a = super.x;
+ assertEquals(15, a);
assertEquals(15, super.x);
assertEquals(27, this.x);
@@ -34,6 +36,7 @@
assertEquals("Derived", new Derived().f());
}());
+
(function TestSuperKeywordNonMethod() {
function f() {
super.unknown();
@@ -65,12 +68,17 @@
Derived.prototype.testGetter = function() {
return super.x;
}.toMethod(Derived.prototype);
+ Derived.prototype.testGetterStrict = function() {
+ 'use strict';
+ return super.x;
+ }.toMethod(Derived.prototype);
derived = new Derived();
assertEquals('derived', derived.testGetter());
+ derived = new Derived();
+ assertEquals('derived', derived.testGetterStrict());
}());
-/*
- * TODO[dslomov]: named stores and keyed loads/stores not implemented yet.
+
(function TestSetter() {
function Base() {}
Base.prototype = {
@@ -92,36 +100,135 @@
_x: 'derived'
};
Derived.prototype.testSetter = function() {
- super.x = 'foobar';
- }.toMethod(Derived.prototype);
+ assertEquals('foobar', super.x = 'foobar');
+ assertEquals('foobarabc', super.x += 'abc');
+ }.toMethod(Derived.prototype);
var d = new Derived();
d.testSetter();
assertEquals('base', Base.prototype._x);
- assertEquals('foobar', d._x);
+ assertEquals('foobarabc', d._x);
+ d._x = '';
+ Derived.prototype.testSetterStrict = function() {
+ 'use strict';
+ assertEquals('foobar', super.x = 'foobar');
+ assertEquals('foobarabc', super.x += 'abc');
+ }.toMethod(Derived.prototype);
+ d.testSetterStrict();
+ assertEquals('base', Base.prototype._x);
+ assertEquals('foobarabc', d._x);
}());
-(function TestKeyedGetter() {
+(function TestAccessorsOnPrimitives() {
+ var getCalled = false;
+ var setCalled = false;
function Base() {}
Base.prototype = {
constructor: Base,
- _x: 'base'
+ get x() {
+ getCalled = true;
+ return 1;
+ },
+ set x(v) {
+ setCalled = true;
+ return v;
+ },
};
- Object.defineProperty(Base.prototype, '0',
- { get: function() { return this._x; } });
-
function Derived() {}
- Derived.__proto__ = Base;
Derived.prototype = {
__proto__: Base.prototype,
constructor: Derived,
- _x: 'derived'
};
- Derived.prototype.testGetter = function() {
- return super[0];
- }.toMethod(Derived.prototype);
- assertEquals('derived', new Derived()[0]);
- // assertEquals('derived', new Derived().testGetter());
+ Derived.prototype.testSetter = function() {
+ assertTrue(42 == this);
+ getCalled = false;
+ setCalled = false;
+ assertEquals(1, super.x);
+ assertTrue(getCalled);
+ assertFalse(setCalled);
+
+ setCalled = false;
+ getCalled = false;
+ assertEquals(5, super.x = 5);
+ assertFalse(getCalled);
+ assertTrue(setCalled);
+
+ getCalled = false;
+ setCalled = false;
+ assertEquals(6, super.x += 5);
+ assertTrue(getCalled);
+ assertTrue(setCalled);
+ }.toMethod(Derived.prototype);
+
+ Derived.prototype.testSetterStrict = function() {
+ 'use strict';
+ assertTrue(42 == this);
+ getCalled = false;
+ setCalled = false;
+ assertEquals(1, super.x);
+ assertTrue(getCalled);
+ assertFalse(setCalled);
+
+ setCalled = false;
+ getCalled = false;
+ assertEquals(5, super.x = 5);
+ assertFalse(getCalled);
+ assertTrue(setCalled);
+
+ getCalled = false;
+ setCalled = false;
+ assertEquals(6, super.x += 5);
+ assertTrue(getCalled);
+ assertTrue(setCalled);
+ }.toMethod(Derived.prototype);
+
+ Derived.prototype.testSetter.call(42);
+ Derived.prototype.testSetterStrict.call(42);
+
+ function DerivedFromString() {}
+ DerivedFromString.prototype = Object.create(String.prototype);
+
+ function f() {
+ 'use strict';
+ assertTrue(42 == this);
+ assertEquals(String.prototype.toString, super.toString);
+ var except = false;
+ try {
+ super.toString();
+ } catch(e) { except = true; }
+ assertTrue(except);
+ }
+ f.toMethod(DerivedFromString.prototype).call(42);
}());
-*/
+
+
+(function TestSetterFailures() {
+ function Base() {}
+ function Derived() {}
+ Derived.prototype = { __proto__ : Base.prototype };
+ Derived.prototype.mSloppy = function () {
+ super.x = 10;
+ assertEquals(undefined, super.x);
+ }.toMethod(Derived.prototype);
+
+ Derived.prototype.mStrict = function () {
+ "use strict";
+ super.x = 10;
+ }.toMethod(Derived.prototype);
+ var d = new Derived();
+ d.mSloppy();
+ assertEquals(undefined, d.x);
+ var d1 = new Derived();
+ assertThrows(function() { d.mStrict(); }, ReferenceError);
+ assertEquals(undefined, d.x);
+}());
+
+
+(function TestUnsupportedCases() {
+ function f1(x) { return super[x]; }
+ var o = {}
+ assertThrows(function(){f1.toMethod(o)(x);}, ReferenceError);
+ function f2() { super.x++; }
+ assertThrows(function(){f2.toMethod(o)();}, ReferenceError);
+}());
diff --git a/test/mjsunit/serialize-ic.js b/test/mjsunit/serialize-ic.js
new file mode 100644
index 0000000..8f20b27
--- /dev/null
+++ b/test/mjsunit/serialize-ic.js
@@ -0,0 +1,9 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --cache=code --serialize-toplevel
+
+var foo = [];
+foo[0] = "bar";
+assertEquals(["bar"], foo);
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 9bd9d4b..ee391c3 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -351,6 +351,8 @@
'../../src/background-parsing-task.h',
'../../src/bailout-reason.cc',
'../../src/bailout-reason.h',
+ '../../src/basic-block-profiler.cc',
+ '../../src/basic-block-profiler.h',
'../../src/bignum-dtoa.cc',
'../../src/bignum-dtoa.h',
'../../src/bignum.cc',
@@ -382,6 +384,8 @@
'../../src/compiler/access-builder.h',
'../../src/compiler/ast-graph-builder.cc',
'../../src/compiler/ast-graph-builder.h',
+ '../../src/compiler/basic-block-instrumentor.cc',
+ '../../src/compiler/basic-block-instrumentor.h',
'../../src/compiler/change-lowering.cc',
'../../src/compiler/change-lowering.h',
'../../src/compiler/code-generator-impl.h',
@@ -731,10 +735,21 @@
'../../src/rewriter.h',
'../../src/runtime-profiler.cc',
'../../src/runtime-profiler.h',
+ '../../src/runtime/runtime-collections.cc',
+ '../../src/runtime/runtime-compiler.cc',
'../../src/runtime/runtime-i18n.cc',
+ '../../src/runtime/runtime-json.cc',
+ '../../src/runtime/runtime-maths.cc',
+ '../../src/runtime/runtime-numbers.cc',
+ '../../src/runtime/runtime-regexp.cc',
+ '../../src/runtime/runtime-strings.cc',
+ '../../src/runtime/runtime-test.cc',
+ '../../src/runtime/runtime-typedarray.cc',
+ '../../src/runtime/runtime-uri.cc',
'../../src/runtime/runtime-utils.h',
'../../src/runtime/runtime.cc',
'../../src/runtime/runtime.h',
+ '../../src/runtime/string-builder.h',
'../../src/safepoint-table.cc',
'../../src/safepoint-table.h',
'../../src/sampler.cc',