Version 3.23.3
Fixed compilation with GCC 4.8. (issue 2767, 2149)
Added explicit Isolate parameter to External::New. (Chromium issue 266838)
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@17690 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index a2b4a20..60e5fdc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2013-11-13: Version 3.23.3
+
+ Fixed compilation with GCC 4.8.
+ (issue 2767, 2149)
+
+ Added explicit Isolate parameter to External::New.
+ (Chromium issue 266838)
+
+ Performance and stability improvements on all platforms.
+
+
2013-11-12: Version 3.23.2
Fixed --extra-code flag for snapshot creation.
diff --git a/include/v8.h b/include/v8.h
index 1f8cbfd..f8ef7c1 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -114,6 +114,7 @@
class StringObject;
class Symbol;
class SymbolObject;
+class Private;
class Uint32;
class Utils;
class Value;
@@ -328,6 +329,8 @@
friend Handle<Boolean> False(Isolate* isolate);
friend class Context;
friend class HandleScope;
+ friend class Object;
+ friend class Private;
V8_INLINE static Handle<T> New(Isolate* isolate, T* that);
@@ -1926,11 +1929,9 @@
// Returns the print name string of the symbol, or undefined if none.
Local<Value> Name() const;
- // Create a symbol without a print name.
- static Local<Symbol> New(Isolate* isolate);
-
- // Create a symbol with a print name.
- static Local<Symbol> New(Isolate *isolate, const char* data, int length = -1);
+ // Create a symbol. If data is not NULL, it will be used as a print name.
+ static Local<Symbol> New(
+ Isolate *isolate, const char* data = NULL, int length = -1);
V8_INLINE static Symbol* Cast(v8::Value* obj);
private:
@@ -1940,6 +1941,25 @@
/**
+ * A private symbol
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT Private : public Data {
+ public:
+ // Returns the print name string of the private symbol, or undefined if none.
+ Local<Value> Name() const;
+
+ // Create a private symbol. If data is not NULL, it will be the print name.
+ static Local<Private> New(
+ Isolate *isolate, const char* data = NULL, int length = -1);
+
+ private:
+ Private();
+};
+
+
+/**
* A JavaScript number value (ECMA-262, 4.3.20)
*/
class V8_EXPORT Number : public Primitive {
@@ -2109,6 +2129,17 @@
AccessControl settings = DEFAULT);
/**
+ * Functionality for private properties.
+ * This is an experimental feature, use at your own risk.
+ * Note: Private properties are inherited. Do not rely on this, since it may
+ * change.
+ */
+ bool HasPrivate(Handle<Private> key);
+ bool SetPrivate(Handle<Private> key, Handle<Value> value);
+ bool DeletePrivate(Handle<Private> key);
+ Local<Value> GetPrivate(Handle<Private> key);
+
+ /**
* Returns an array containing the names of the enumerable properties
* of this object, including properties from prototype objects. The
* array returned by this method contains the same values as would
@@ -3042,6 +3073,8 @@
*/
class V8_EXPORT External : public Value {
public:
+ static Local<External> New(Isolate* isolate, void* value);
+ // Deprecated, do not use.
static Local<External> New(void* value);
V8_INLINE static External* Cast(Value* obj);
void* Value() const;
@@ -5819,7 +5852,6 @@
template<typename T>
void ReturnValue<T>::Set(uint32_t i) {
TYPE_CHECK(T, Integer);
- typedef internal::Internals I;
// Can't simply use INT32_MAX here for whatever reason.
bool fits_into_int32_t = (i & (1U << 31)) == 0;
if (V8_LIKELY(fits_into_int32_t)) {
diff --git a/include/v8config.h b/include/v8config.h
index 834f9c5..631ad0d 100644
--- a/include/v8config.h
+++ b/include/v8config.h
@@ -187,6 +187,7 @@
// supported
// V8_HAS_ATTRIBUTE_DEPRECATED - __attribute__((deprecated)) supported
// V8_HAS_ATTRIBUTE_NOINLINE - __attribute__((noinline)) supported
+// V8_HAS_ATTRIBUTE_UNUSED - __attribute__((unused)) supported
// V8_HAS_ATTRIBUTE_VISIBILITY - __attribute__((visibility)) supported
// V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT - __attribute__((warn_unused_result))
// supported
@@ -216,6 +217,7 @@
# define V8_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(always_inline))
# define V8_HAS_ATTRIBUTE_DEPRECATED (__has_attribute(deprecated))
# define V8_HAS_ATTRIBUTE_NOINLINE (__has_attribute(noinline))
+# define V8_HAS_ATTRIBUTE_UNUSED (__has_attribute(unused))
# define V8_HAS_ATTRIBUTE_VISIBILITY (__has_attribute(visibility))
# define V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT \
(__has_attribute(warn_unused_result))
@@ -247,6 +249,7 @@
# define V8_HAS_ATTRIBUTE_DEPRECATED (V8_GNUC_PREREQ(3, 4, 0))
# define V8_HAS_ATTRIBUTE_DEPRECATED_MESSAGE (V8_GNUC_PREREQ(4, 5, 0))
# define V8_HAS_ATTRIBUTE_NOINLINE (V8_GNUC_PREREQ(3, 4, 0))
+# define V8_HAS_ATTRIBUTE_UNUSED (V8_GNUC_PREREQ(2, 95, 0))
# define V8_HAS_ATTRIBUTE_VISIBILITY (V8_GNUC_PREREQ(4, 3, 0))
# define V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT \
(!V8_CC_INTEL && V8_GNUC_PREREQ(4, 1, 0))
@@ -334,6 +337,14 @@
#endif
+// A macro to mark variables or types as unused, avoiding compiler warnings.
+#if V8_HAS_ATTRIBUTE_UNUSED
+# define V8_UNUSED __attribute__((unused))
+#else
+# define V8_UNUSED
+#endif
+
+
// Annotate a function indicating the caller must examine the return value.
// Use like:
// int foo() V8_WARN_UNUSED_RESULT;
diff --git a/samples/process.cc b/samples/process.cc
index e6f2ee3..d1d36ca 100644
--- a/samples/process.cc
+++ b/samples/process.cc
@@ -324,7 +324,7 @@
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
- Handle<External> map_ptr = External::New(obj);
+ Handle<External> map_ptr = External::New(GetIsolate(), obj);
// Store the map pointer in the JavaScript wrapper.
result->SetInternalField(0, map_ptr);
@@ -432,7 +432,7 @@
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
- Handle<External> request_ptr = External::New(request);
+ Handle<External> request_ptr = External::New(GetIsolate(), request);
// Store the request pointer in the JavaScript wrapper.
result->SetInternalField(0, request_ptr);
diff --git a/src/api.cc b/src/api.cc
index 401007b..65e7345 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3191,6 +3191,12 @@
}
+bool v8::Object::SetPrivate(v8::Handle<Private> key, v8::Handle<Value> value) {
+ return Set(v8::Handle<Value>(reinterpret_cast<Value*>(*key)),
+ value, DontEnum);
+}
+
+
bool v8::Object::ForceDelete(v8::Handle<Value> key) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::ForceDelete()", return false);
@@ -3242,6 +3248,11 @@
}
+Local<Value> v8::Object::GetPrivate(v8::Handle<Private> key) {
+ return Get(v8::Handle<Value>(reinterpret_cast<Value*>(*key)));
+}
+
+
PropertyAttribute v8::Object::GetPropertyAttributes(v8::Handle<Value> key) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::GetPropertyAttribute()",
@@ -3441,6 +3452,11 @@
}
+bool v8::Object::DeletePrivate(v8::Handle<Private> key) {
+ return Delete(v8::Handle<Value>(reinterpret_cast<Value*>(*key)));
+}
+
+
bool v8::Object::Has(v8::Handle<Value> key) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::Has()", return false);
@@ -3455,6 +3471,11 @@
}
+bool v8::Object::HasPrivate(v8::Handle<Private> key) {
+ return Has(v8::Handle<Value>(reinterpret_cast<Value*>(*key)));
+}
+
+
bool v8::Object::Delete(uint32_t index) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::DeleteProperty()",
@@ -4876,6 +4897,11 @@
}
+Local<Value> Private::Name() const {
+ return reinterpret_cast<const Symbol*>(this)->Name();
+}
+
+
double Number::Value() const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->Number();
@@ -5369,17 +5395,22 @@
}
-Local<External> v8::External::New(void* value) {
+Local<External> v8::External::New(Isolate* isolate, void* value) {
STATIC_ASSERT(sizeof(value) == sizeof(i::Address));
- i::Isolate* isolate = i::Isolate::Current();
- EnsureInitializedForIsolate(isolate, "v8::External::New()");
- LOG_API(isolate, "External::New");
- ENTER_V8(isolate);
- i::Handle<i::JSObject> external = isolate->factory()->NewExternal(value);
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ EnsureInitializedForIsolate(i_isolate, "v8::External::New()");
+ LOG_API(i_isolate, "External::New");
+ ENTER_V8(i_isolate);
+ i::Handle<i::JSObject> external = i_isolate->factory()->NewExternal(value);
return Utils::ExternalToLocal(external);
}
+Local<External> v8::External::New(void* value) {
+ return v8::External::New(Isolate::GetCurrent(), value);
+}
+
+
void* External::Value() const {
return ExternalValue(*Utils::OpenHandle(this));
}
@@ -6133,27 +6164,37 @@
}
-Local<Symbol> v8::Symbol::New(Isolate* isolate) {
+Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
LOG_API(i_isolate, "Symbol::New()");
ENTER_V8(i_isolate);
i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+ if (data != NULL) {
+ if (length == -1) length = i::StrLength(data);
+ i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+ i::Vector<const char>(data, length));
+ result->set_name(*name);
+ }
return Utils::ToLocal(result);
}
-Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
+Local<Private> v8::Private::New(
+ Isolate* isolate, const char* data, int length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
- EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
- LOG_API(i_isolate, "Symbol::New(char)");
+ EnsureInitializedForIsolate(i_isolate, "v8::Private::New()");
+ LOG_API(i_isolate, "Private::New()");
ENTER_V8(i_isolate);
- if (length == -1) length = i::StrLength(data);
- i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
- i::Vector<const char>(data, length));
- i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
- result->set_name(*name);
- return Utils::ToLocal(result);
+ i::Handle<i::Symbol> symbol = i_isolate->factory()->NewPrivateSymbol();
+ if (data != NULL) {
+ if (length == -1) length = i::StrLength(data);
+ i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+ i::Vector<const char>(data, length));
+ symbol->set_name(*name);
+ }
+ Local<Symbol> result = Utils::ToLocal(symbol);
+ return v8::Handle<Private>(reinterpret_cast<Private*>(*result));
}
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index 95a04f7..94e114f 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -189,14 +189,21 @@
// r0 -- number of arguments
// r1 -- function
// r2 -- type info cell with elements kind
- static Register registers[] = { r1, r2 };
- descriptor->register_param_count_ = 2;
- if (constant_stack_parameter_count != 0) {
+ static Register registers_variable_args[] = { r1, r2, r0 };
+ static Register registers_no_args[] = { r1, r2 };
+
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = r0;
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
@@ -210,15 +217,21 @@
// register state
// r0 -- number of arguments
// r1 -- constructor function
- static Register registers[] = { r1 };
- descriptor->register_param_count_ = 1;
+ static Register registers_variable_args[] = { r1, r0 };
+ static Register registers_no_args[] = { r1 };
- if (constant_stack_parameter_count != 0) {
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = r0;
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
@@ -302,6 +315,17 @@
}
+void NewStringAddStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r1, r0 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kStringAdd)->entry;
+}
+
+
#define __ ACCESS_MASM(masm)
diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc
index 9339c5f..c846f98 100644
--- a/src/arm/deoptimizer-arm.cc
+++ b/src/arm/deoptimizer-arm.cc
@@ -107,7 +107,7 @@
ApiFunction function(descriptor->deoptimization_handler_);
ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
- int params = descriptor->environment_length();
+ int params = descriptor->GetHandlerParameterCount();
output_frame->SetRegister(r0.code(), params);
output_frame->SetRegister(r1.code(), handler);
}
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 8fb1e15..0cb8e46 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -3104,6 +3104,32 @@
}
+void FullCodeGenerator::EmitIsMinusZero(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, if_false, DO_SMI_CHECK);
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ __ ldr(r1, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ cmp(r2, Operand(0x80000000));
+ __ cmp(r1, Operand(0x00000000), eq);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@@ -3705,11 +3731,21 @@
void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
- VisitForStackValue(args->at(0));
- VisitForStackValue(args->at(1));
- StringAddStub stub(STRING_ADD_CHECK_BOTH);
- __ CallStub(&stub);
+ if (FLAG_new_string_add) {
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ __ pop(r1);
+ NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
+ __ CallStub(&stub);
+ } else {
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ }
context()->Plug(r0);
}
@@ -3726,42 +3762,6 @@
}
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(r0);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(r0);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::TAN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(r0);
-}
-
-
void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG,
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index 5fb0e53..787fc81 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1783,6 +1783,16 @@
}
+LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch(
+ HCompareMinusZeroAndBranch* instr) {
+ LInstruction* goto_instr = CheckElideControlInstruction(instr);
+ if (goto_instr != NULL) return goto_instr;
+ LOperand* value = UseRegister(instr->value());
+ LOperand* scratch = TempRegister();
+ return new(zone()) LCompareMinusZeroAndBranch(value, scratch);
+}
+
+
LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -2413,8 +2423,12 @@
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), cp);
- LOperand* left = UseRegisterAtStart(instr->left());
- LOperand* right = UseRegisterAtStart(instr->right());
+ LOperand* left = FLAG_new_string_add
+ ? UseFixed(instr->left(), r1)
+ : UseRegisterAtStart(instr->left());
+ LOperand* right = FLAG_new_string_add
+ ? UseFixed(instr->right(), r0)
+ : UseRegisterAtStart(instr->right());
return MarkAsCall(
DefineFixed(new(zone()) LStringAdd(context, left, right), r0),
instr);
@@ -2485,7 +2499,7 @@
CodeStubInterfaceDescriptor* descriptor =
info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
int index = static_cast<int>(instr->index());
- Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ Register reg = descriptor->GetParameterRegister(index);
return DefineFixed(result, reg);
}
}
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 559f26e..f67747b 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -72,6 +72,7 @@
V(ClampIToUint8) \
V(ClampTToUint8) \
V(ClassOfTestAndBranch) \
+ V(CompareMinusZeroAndBranch) \
V(CompareNumericAndBranch) \
V(CmpObjectEqAndBranch) \
V(CmpHoleAndBranch) \
@@ -928,6 +929,22 @@
};
+class LCompareMinusZeroAndBranch V8_FINAL : public LControlInstruction<1, 1> {
+ public:
+ LCompareMinusZeroAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch,
+ "cmp-minus-zero-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMinusZeroAndBranch)
+};
+
+
class LIsObjectAndBranch V8_FINAL : public LControlInstruction<1, 1> {
public:
LIsObjectAndBranch(LOperand* value, LOperand* temp) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 8049b68..207dd8c 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -2472,6 +2472,33 @@
}
+void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) {
+ Representation rep = instr->hydrogen()->value()->representation();
+ ASSERT(!rep.IsInteger32());
+ Register scratch = ToRegister(instr->temp());
+
+ if (rep.IsDouble()) {
+ DwVfpRegister value = ToDoubleRegister(instr->value());
+ __ VFPCompareAndSetFlags(value, 0.0);
+ EmitFalseBranch(instr, ne);
+ __ VmovHigh(scratch, value);
+ __ cmp(scratch, Operand(0x80000000));
+ } else {
+ Register value = ToRegister(instr->value());
+ __ CheckMap(value,
+ scratch,
+ Heap::kHeapNumberMapRootIndex,
+ instr->FalseLabel(chunk()),
+ DO_SMI_CHECK);
+ __ ldr(scratch, FieldMemOperand(value, HeapNumber::kExponentOffset));
+ __ ldr(ip, FieldMemOperand(value, HeapNumber::kMantissaOffset));
+ __ cmp(scratch, Operand(0x80000000));
+ __ cmp(ip, Operand(0x00000000), eq);
+ }
+ EmitBranch(instr, eq);
+}
+
+
Condition LCodeGen::EmitIsObject(Register input,
Register temp1,
Label* is_not_object,
@@ -4562,10 +4589,18 @@
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(cp));
- __ push(ToRegister(instr->left()));
- __ push(ToRegister(instr->right()));
- StringAddStub stub(instr->hydrogen()->flags());
- CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ if (FLAG_new_string_add) {
+ ASSERT(ToRegister(instr->left()).is(r1));
+ ASSERT(ToRegister(instr->right()).is(r0));
+ NewStringAddStub stub(instr->hydrogen()->flags(),
+ isolate()->heap()->GetPretenureMode());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ }
}
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index cbd6b9c..7390a48 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -297,6 +297,8 @@
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
void EmitGoto(int block);
+
+ // EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition condition);
template<class InstrType>
diff --git a/src/array-iterator.js b/src/array-iterator.js
index e734986..a8c5e00 100644
--- a/src/array-iterator.js
+++ b/src/array-iterator.js
@@ -36,9 +36,9 @@
var ARRAY_ITERATOR_KIND_ENTRIES = 3;
// The spec draft also has "sparse" but it is never used.
-var iteratorObjectSymbol = %CreateSymbol(UNDEFINED);
-var arrayIteratorNextIndexSymbol = %CreateSymbol(UNDEFINED);
-var arrayIterationKindSymbol = %CreateSymbol(UNDEFINED);
+var iteratorObjectSymbol = NEW_PRIVATE("iterator_object");
+var arrayIteratorNextIndexSymbol = NEW_PRIVATE("iterator_next");
+var arrayIterationKindSymbol = NEW_PRIVATE("iterator_kind");
function ArrayIterator() {}
@@ -46,9 +46,9 @@
function CreateArrayIterator(array, kind) {
var object = ToObject(array);
var iterator = new ArrayIterator;
- iterator[iteratorObjectSymbol] = object;
- iterator[arrayIteratorNextIndexSymbol] = 0;
- iterator[arrayIterationKindSymbol] = kind;
+ SET_PRIVATE(iterator, iteratorObjectSymbol, object);
+ SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, 0);
+ SET_PRIVATE(iterator, arrayIterationKindSymbol, kind);
return iterator;
}
@@ -60,24 +60,24 @@
// 15.4.5.2.2 ArrayIterator.prototype.next( )
function ArrayIteratorNext() {
var iterator = ToObject(this);
- var array = iterator[iteratorObjectSymbol];
+ var array = GET_PRIVATE(iterator, iteratorObjectSymbol);
if (!array) {
throw MakeTypeError('incompatible_method_receiver',
['Array Iterator.prototype.next']);
}
- var index = iterator[arrayIteratorNextIndexSymbol];
- var itemKind = iterator[arrayIterationKindSymbol];
+ var index = GET_PRIVATE(iterator, arrayIteratorNextIndexSymbol);
+ var itemKind = GET_PRIVATE(iterator, arrayIterationKindSymbol);
var length = TO_UINT32(array.length);
// "sparse" is never used.
if (index >= length) {
- iterator[arrayIteratorNextIndexSymbol] = 1 / 0; // Infinity
+ SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, INFINITY);
return CreateIteratorResultObject(UNDEFINED, true);
}
- iterator[arrayIteratorNextIndexSymbol] = index + 1;
+ SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);
if (itemKind == ARRAY_ITERATOR_KIND_VALUES)
return CreateIteratorResultObject(array[index], false);
diff --git a/src/checks.h b/src/checks.h
index 9d2db28..f7b145f 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -268,7 +268,7 @@
#define STATIC_CHECK(test) \
typedef \
StaticAssertionHelper<sizeof(StaticAssertion<static_cast<bool>((test))>)> \
- SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)
+ SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__) V8_UNUSED
#endif
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc
index 19b6088..a790179 100644
--- a/src/code-stubs-hydrogen.cc
+++ b/src/code-stubs-hydrogen.cc
@@ -150,26 +150,24 @@
next_block->SetJoinId(BailoutId::StubEntry());
set_current_block(next_block);
+ bool runtime_stack_params = descriptor_->stack_parameter_count_.is_valid();
+ HInstruction* stack_parameter_count = NULL;
for (int i = 0; i < param_count; ++i) {
- HParameter* param =
- Add<HParameter>(i, HParameter::REGISTER_PARAMETER);
+ Representation r = descriptor_->IsParameterCountRegister(i)
+ ? Representation::Integer32()
+ : Representation::Tagged();
+ HParameter* param = Add<HParameter>(i, HParameter::REGISTER_PARAMETER, r);
start_environment->Bind(i, param);
parameters_[i] = param;
+ if (descriptor_->IsParameterCountRegister(i)) {
+ param->set_type(HType::Smi());
+ stack_parameter_count = param;
+ arguments_length_ = stack_parameter_count;
+ }
}
- HInstruction* stack_parameter_count;
- if (descriptor_->stack_parameter_count_.is_valid()) {
- ASSERT(descriptor_->environment_length() == (param_count + 1));
- stack_parameter_count = New<HParameter>(param_count,
- HParameter::REGISTER_PARAMETER,
- Representation::Integer32());
- stack_parameter_count->set_type(HType::Smi());
- // It's essential to bind this value to the environment in case of deopt.
- AddInstruction(stack_parameter_count);
- start_environment->Bind(param_count, stack_parameter_count);
- arguments_length_ = stack_parameter_count;
- } else {
- ASSERT(descriptor_->environment_length() == param_count);
+ ASSERT(!runtime_stack_params || arguments_length_ != NULL);
+ if (!runtime_stack_params) {
stack_parameter_count = graph()->GetConstantMinus1();
arguments_length_ = graph()->GetConstant0();
}
@@ -189,10 +187,11 @@
if (descriptor_->function_mode_ == JS_FUNCTION_STUB_MODE) {
if (!stack_parameter_count->IsConstant() &&
descriptor_->hint_stack_parameter_count_ < 0) {
- HInstruction* amount = graph()->GetConstant1();
- stack_pop_count = Add<HAdd>(stack_parameter_count, amount);
- stack_pop_count->ChangeRepresentation(Representation::Integer32());
+ HInstruction* constant_one = graph()->GetConstant1();
+ stack_pop_count = Add<HAdd>(stack_parameter_count, constant_one);
stack_pop_count->ClearFlag(HValue::kCanOverflow);
+ // TODO(mvstanton): verify that stack_parameter_count+1 really fits in a
+ // smi.
} else {
int count = descriptor_->hint_stack_parameter_count_;
stack_pop_count = Add<HConstant>(count);
@@ -972,6 +971,38 @@
template <>
+HValue* CodeStubGraphBuilder<NewStringAddStub>::BuildCodeInitializedStub() {
+ NewStringAddStub* stub = casted_stub();
+ StringAddFlags flags = stub->flags();
+ PretenureFlag pretenure_flag = stub->pretenure_flag();
+
+ HValue* left = GetParameter(NewStringAddStub::kLeft);
+ HValue* right = GetParameter(NewStringAddStub::kRight);
+
+ // Make sure that both arguments are strings if not known in advance.
+ if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ IfBuilder if_leftnotstring(this);
+ if_leftnotstring.IfNot<HIsStringAndBranch>(left);
+ if_leftnotstring.Then();
+ if_leftnotstring.Deopt("Expected string for LHS of string addition");
+ }
+ if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ IfBuilder if_rightnotstring(this);
+ if_rightnotstring.IfNot<HIsStringAndBranch>(right);
+ if_rightnotstring.Then();
+ if_rightnotstring.Deopt("Expected string for RHS of string addition");
+ }
+
+ return BuildStringAdd(left, right, pretenure_flag);
+}
+
+
+Handle<Code> NewStringAddStub::GenerateCode(Isolate* isolate) {
+ return DoGenerateCode(isolate, this);
+}
+
+
+template <>
HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
ToBooleanStub* stub = casted_stub();
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index afa3cd0..f6e880f 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -46,6 +46,7 @@
function_mode_(NOT_JS_FUNCTION_STUB_MODE),
register_params_(NULL),
deoptimization_handler_(NULL),
+ handler_arguments_mode_(DONT_PASS_ARGUMENTS),
miss_handler_(),
has_miss_handler_(false) { }
@@ -682,6 +683,21 @@
}
+void NewStringAddStub::PrintBaseName(StringStream* stream) {
+ stream->Add("NewStringAddStub");
+ if ((flags() & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
+ stream->Add("_CheckBoth");
+ } else if ((flags() & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ stream->Add("_CheckLeft");
+ } else if ((flags() & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ stream->Add("_CheckRight");
+ }
+ if (pretenure_flag() == TENURED) {
+ stream->Add("_Tenured");
+ }
+}
+
+
InlineCacheState ICCompareStub::GetICState() {
CompareIC::State state = Max(left_, right_);
switch (state) {
@@ -1134,6 +1150,13 @@
}
+// static
+void NewStringAddStub::InstallDescriptors(Isolate* isolate) {
+ NewStringAddStub stub(STRING_ADD_CHECK_NONE, NOT_TENURED);
+ InstallDescriptor(isolate, &stub);
+}
+
+
ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate)
: argument_count_(ANY) {
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
diff --git a/src/code-stubs.h b/src/code-stubs.h
index 3dc32e8..8f1b686 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -43,6 +43,7 @@
V(CallConstruct) \
V(BinaryOp) \
V(StringAdd) \
+ V(NewStringAdd) \
V(SubString) \
V(StringCompare) \
V(Compare) \
@@ -276,23 +277,23 @@
enum StubFunctionMode { NOT_JS_FUNCTION_STUB_MODE, JS_FUNCTION_STUB_MODE };
-
+enum HandlerArgumentsMode { DONT_PASS_ARGUMENTS, PASS_ARGUMENTS };
struct CodeStubInterfaceDescriptor {
CodeStubInterfaceDescriptor();
int register_param_count_;
+
Register stack_parameter_count_;
// if hint_stack_parameter_count_ > 0, the code stub can optimize the
// return sequence. Default value is -1, which means it is ignored.
int hint_stack_parameter_count_;
StubFunctionMode function_mode_;
Register* register_params_;
+
Address deoptimization_handler_;
+ HandlerArgumentsMode handler_arguments_mode_;
int environment_length() const {
- if (stack_parameter_count_.is_valid()) {
- return register_param_count_ + 1;
- }
return register_param_count_;
}
@@ -301,6 +302,9 @@
void SetMissHandler(ExternalReference handler) {
miss_handler_ = handler;
has_miss_handler_ = true;
+ // Our miss handler infrastructure doesn't currently support
+ // variable stack parameter counts.
+ ASSERT(!stack_parameter_count_.is_valid());
}
ExternalReference miss_handler() {
@@ -312,18 +316,27 @@
return has_miss_handler_;
}
+ Register GetParameterRegister(int index) {
+ return register_params_[index];
+ }
+
+ bool IsParameterCountRegister(int index) {
+ return GetParameterRegister(index).is(stack_parameter_count_);
+ }
+
+ int GetHandlerParameterCount() {
+ int params = environment_length();
+ if (handler_arguments_mode_ == PASS_ARGUMENTS) {
+ params += 1;
+ }
+ return params;
+ }
+
private:
ExternalReference miss_handler_;
bool has_miss_handler_;
};
-// A helper to make up for the fact that type Register is not fully
-// defined outside of the platform directories
-#define DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index) \
- ((index) == (descriptor)->register_param_count_) \
- ? (descriptor)->stack_parameter_count_ \
- : (descriptor)->register_params_[(index)]
-
class HydrogenCodeStub : public CodeStub {
public:
@@ -1179,6 +1192,47 @@
};
+// TODO(bmeurer): Rename to StringAddStub once we dropped the old StringAddStub.
+class NewStringAddStub V8_FINAL : public HydrogenCodeStub {
+ public:
+ NewStringAddStub(StringAddFlags flags, PretenureFlag pretenure_flag)
+ : bit_field_(StringAddFlagsBits::encode(flags) |
+ PretenureFlagBits::encode(pretenure_flag)) {}
+
+ StringAddFlags flags() const {
+ return StringAddFlagsBits::decode(bit_field_);
+ }
+
+ PretenureFlag pretenure_flag() const {
+ return PretenureFlagBits::decode(bit_field_);
+ }
+
+ virtual Handle<Code> GenerateCode(Isolate* isolate) V8_OVERRIDE;
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE;
+
+ static void InstallDescriptors(Isolate* isolate);
+
+ // Parameters accessed via CodeStubGraphBuilder::GetParameter()
+ static const int kLeft = 0;
+ static const int kRight = 1;
+
+ private:
+ class StringAddFlagsBits: public BitField<StringAddFlags, 0, 2> {};
+ class PretenureFlagBits: public BitField<PretenureFlag, 2, 1> {};
+ uint32_t bit_field_;
+
+ virtual Major MajorKey() V8_OVERRIDE { return NewStringAdd; }
+ virtual int NotMissMinorKey() V8_OVERRIDE { return bit_field_; }
+
+ virtual void PrintBaseName(StringStream* stream) V8_OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(NewStringAddStub);
+};
+
+
class ICCompareStub: public PlatformCodeStub {
public:
ICCompareStub(Token::Value op,
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc
index 4f2cd03..ef4e39e 100644
--- a/src/deoptimizer.cc
+++ b/src/deoptimizer.cc
@@ -1586,15 +1586,34 @@
}
// Copy the register parameters to the failure frame.
+ int arguments_length_offset = -1;
for (int i = 0; i < descriptor->register_param_count_; ++i) {
output_frame_offset -= kPointerSize;
DoTranslateCommand(iterator, 0, output_frame_offset);
+
+ if (!arg_count_known && descriptor->IsParameterCountRegister(i)) {
+ arguments_length_offset = output_frame_offset;
+ }
}
+ ASSERT(0 == output_frame_offset);
+
if (!arg_count_known) {
- DoTranslateCommand(iterator, 0, length_frame_offset,
- TRANSLATED_VALUE_IS_NATIVE);
- caller_arg_count = output_frame->GetFrameSlot(length_frame_offset);
+ ASSERT(arguments_length_offset >= 0);
+ // We know it's a smi because 1) the code stub guarantees the stack
+ // parameter count is in smi range, and 2) the DoTranslateCommand in the
+ // parameter loop above translated that to a tagged value.
+ Smi* smi_caller_arg_count = reinterpret_cast<Smi*>(
+ output_frame->GetFrameSlot(arguments_length_offset));
+ caller_arg_count = smi_caller_arg_count->value();
+ output_frame->SetFrameSlot(length_frame_offset, caller_arg_count);
+ if (trace_scope_ != NULL) {
+ PrintF(trace_scope_->file(),
+ " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; args.length\n",
+ top_address + length_frame_offset, length_frame_offset,
+ caller_arg_count);
+ }
value = frame_ptr + StandardFrameConstants::kCallerSPOffset +
(caller_arg_count - 1) * kPointerSize;
output_frame->SetFrameSlot(args_arguments_offset, value);
@@ -1602,12 +1621,11 @@
PrintF(trace_scope_->file(),
" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR " ; args.arguments\n",
- top_address + args_arguments_offset, args_arguments_offset, value);
+ top_address + args_arguments_offset, args_arguments_offset,
+ value);
}
}
- ASSERT(0 == output_frame_offset);
-
// Copy the double registers from the input into the output frame.
CopyDoubleRegisters(output_frame);
@@ -1874,10 +1892,8 @@
#endif
-static const char* TraceValueType(bool is_smi, bool is_native = false) {
- if (is_native) {
- return "native";
- } else if (is_smi) {
+static const char* TraceValueType(bool is_smi) {
+ if (is_smi) {
return "smi";
}
@@ -2146,13 +2162,11 @@
void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
- int frame_index,
- unsigned output_offset,
- DeoptimizerTranslatedValueType value_type) {
+ int frame_index,
+ unsigned output_offset) {
disasm::NameConverter converter;
// A GC-safe temporary placeholder that we can put in the output frame.
const intptr_t kPlaceholder = reinterpret_cast<intptr_t>(Smi::FromInt(0));
- bool is_native = value_type == TRANSLATED_VALUE_IS_NATIVE;
Translation::Opcode opcode =
static_cast<Translation::Opcode>(iterator->Next());
@@ -2190,8 +2204,7 @@
case Translation::INT32_REGISTER: {
int input_reg = iterator->Next();
intptr_t value = input_->GetRegister(input_reg);
- bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
- Smi::IsValid(value);
+ bool is_smi = Smi::IsValid(value);
if (trace_scope_ != NULL) {
PrintF(
trace_scope_->file(),
@@ -2200,18 +2213,15 @@
output_offset,
value,
converter.NameOfCPURegister(input_reg),
- TraceValueType(is_smi, is_native));
+ TraceValueType(is_smi));
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
- } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
- output_[frame_index]->SetFrameSlot(output_offset, value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<int32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
@@ -2222,8 +2232,7 @@
case Translation::UINT32_REGISTER: {
int input_reg = iterator->Next();
uintptr_t value = static_cast<uintptr_t>(input_->GetRegister(input_reg));
- bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
- (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ bool is_smi = value <= static_cast<uintptr_t>(Smi::kMaxValue);
if (trace_scope_ != NULL) {
PrintF(
trace_scope_->file(),
@@ -2233,18 +2242,15 @@
output_offset,
value,
converter.NameOfCPURegister(input_reg),
- TraceValueType(is_smi, is_native));
+ TraceValueType(is_smi));
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
- } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
- output_[frame_index]->SetFrameSlot(output_offset, value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<uint32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
@@ -2295,8 +2301,7 @@
int input_slot_index = iterator->Next();
unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
intptr_t value = input_->GetFrameSlot(input_offset);
- bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
- Smi::IsValid(value);
+ bool is_smi = Smi::IsValid(value);
if (trace_scope_ != NULL) {
PrintF(trace_scope_->file(),
" 0x%08" V8PRIxPTR ": ",
@@ -2306,18 +2311,15 @@
output_offset,
value,
input_offset,
- TraceValueType(is_smi, is_native));
+ TraceValueType(is_smi));
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
- } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
- output_[frame_index]->SetFrameSlot(output_offset, value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<int32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
@@ -2330,8 +2332,7 @@
unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
uintptr_t value =
static_cast<uintptr_t>(input_->GetFrameSlot(input_offset));
- bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
- (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ bool is_smi = value <= static_cast<uintptr_t>(Smi::kMaxValue);
if (trace_scope_ != NULL) {
PrintF(trace_scope_->file(),
" 0x%08" V8PRIxPTR ": ",
@@ -2341,18 +2342,15 @@
output_offset,
value,
input_offset,
- TraceValueType(is_smi, is_native));
+ TraceValueType(is_smi));
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
- } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
- output_[frame_index]->SetFrameSlot(output_offset, value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<uint32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
diff --git a/src/deoptimizer.h b/src/deoptimizer.h
index f5530a8..52e4e24 100644
--- a/src/deoptimizer.h
+++ b/src/deoptimizer.h
@@ -333,15 +333,9 @@
int object_index,
int field_index);
- enum DeoptimizerTranslatedValueType {
- TRANSLATED_VALUE_IS_NATIVE,
- TRANSLATED_VALUE_IS_TAGGED
- };
-
void DoTranslateCommand(TranslationIterator* iterator,
- int frame_index,
- unsigned output_offset,
- DeoptimizerTranslatedValueType value_type = TRANSLATED_VALUE_IS_TAGGED);
+ int frame_index,
+ unsigned output_offset);
unsigned ComputeInputFrameSize() const;
unsigned ComputeFixedSize(JSFunction* function) const;
diff --git a/src/factory.cc b/src/factory.cc
index da85b12..6da9a2e 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -365,6 +365,14 @@
}
+Handle<Symbol> Factory::NewPrivateSymbol() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocatePrivateSymbol(),
+ Symbol);
+}
+
+
Handle<Context> Factory::NewNativeContext() {
CALL_HEAP_FUNCTION(
isolate(),
diff --git a/src/factory.h b/src/factory.h
index 2b4ae7b..663f56f 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -186,6 +186,7 @@
// Create a symbol.
Handle<Symbol> NewSymbol();
+ Handle<Symbol> NewPrivateSymbol();
// Create a global (but otherwise uninitialized) context.
Handle<Context> NewNativeContext();
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index f0444d1..1781f7f 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -338,6 +338,8 @@
"do not emit check maps for constant values that have a leaf map, "
"deoptimize the optimized code if the layout of the maps changes.")
+DEFINE_bool(new_string_add, false, "enable new string addition")
+
// Experimental profiler changes.
DEFINE_bool(experimental_profiler, true, "enable all profiler experiments")
DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability")
diff --git a/src/heap.cc b/src/heap.cc
index 2ba3919..b5d03bc 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -3311,11 +3311,13 @@
{ MaybeObject* maybe_obj = AllocateSymbol();
if (!maybe_obj->ToObject(&obj)) return false;
}
+ Symbol::cast(obj)->set_is_private(true);
set_frozen_symbol(Symbol::cast(obj));
{ MaybeObject* maybe_obj = AllocateSymbol();
if (!maybe_obj->ToObject(&obj)) return false;
}
+ Symbol::cast(obj)->set_is_private(true);
set_elements_transition_symbol(Symbol::cast(obj));
{ MaybeObject* maybe_obj = SeededNumberDictionary::Allocate(this, 0, TENURED);
@@ -3327,6 +3329,7 @@
{ MaybeObject* maybe_obj = AllocateSymbol();
if (!maybe_obj->ToObject(&obj)) return false;
}
+ Symbol::cast(obj)->set_is_private(true);
set_observed_symbol(Symbol::cast(obj));
// Handling of script id generation is in Factory::NewScript.
@@ -5516,12 +5519,22 @@
Symbol::cast(result)->set_hash_field(
Name::kIsNotArrayIndexMask | (hash << Name::kHashShift));
Symbol::cast(result)->set_name(undefined_value());
+ Symbol::cast(result)->set_flags(Smi::FromInt(0));
- ASSERT(result->IsSymbol());
+ ASSERT(!Symbol::cast(result)->is_private());
return result;
}
+MaybeObject* Heap::AllocatePrivateSymbol() {
+ MaybeObject* maybe = AllocateSymbol();
+ Symbol* symbol;
+ if (!maybe->To(&symbol)) return maybe;
+ symbol->set_is_private(true);
+ return symbol;
+}
+
+
MaybeObject* Heap::AllocateNativeContext() {
Object* result;
{ MaybeObject* maybe_result =
diff --git a/src/heap.h b/src/heap.h
index eea94f8..a0c85e5 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -881,6 +881,7 @@
// failed.
// Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateSymbol();
+ MUST_USE_RESULT MaybeObject* AllocatePrivateSymbol();
// Allocate a tenured AllocationSite. It's payload is null
MUST_USE_RESULT MaybeObject* AllocateAllocationSite();
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc
index 79962db..39d5958 100644
--- a/src/hydrogen-instructions.cc
+++ b/src/hydrogen-instructions.cc
@@ -2944,6 +2944,24 @@
}
+bool HCompareMinusZeroAndBranch::KnownSuccessorBlock(HBasicBlock** block) {
+ if (value()->representation().IsSmiOrInteger32()) {
+ // A Smi or Integer32 cannot contain minus zero.
+ *block = SecondSuccessor();
+ return true;
+ }
+ *block = NULL;
+ return false;
+}
+
+
+void HCompareMinusZeroAndBranch::InferRepresentation(
+ HInferRepresentationPhase* h_infer) {
+ ChangeRepresentation(value()->representation());
+}
+
+
+
void HGoto::PrintDataTo(StringStream* stream) {
stream->Add("B%d", SuccessorAt(0)->block_id());
}
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index b9c0ca9..dd7781b 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -100,6 +100,7 @@
V(CompareNumericAndBranch) \
V(CompareHoleAndBranch) \
V(CompareGeneric) \
+ V(CompareMinusZeroAndBranch) \
V(CompareObjectEqAndBranch) \
V(CompareMap) \
V(Constant) \
@@ -4165,6 +4166,28 @@
};
+class HCompareMinusZeroAndBranch V8_FINAL : public HUnaryControlInstruction {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HCompareMinusZeroAndBranch, HValue*);
+
+ virtual void InferRepresentation(
+ HInferRepresentationPhase* h_infer) V8_OVERRIDE;
+
+ virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
+ return representation();
+ }
+
+ virtual bool KnownSuccessorBlock(HBasicBlock** block) V8_OVERRIDE;
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch)
+
+ private:
+ explicit HCompareMinusZeroAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) {
+ }
+};
+
+
class HCompareObjectEqAndBranch : public HTemplateControlInstruction<2, 2> {
public:
HCompareObjectEqAndBranch(HValue* left,
@@ -5808,6 +5831,12 @@
FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
}
+ static HObjectAccess ForStringHashField() {
+ return HObjectAccess(kInobject,
+ String::kHashFieldOffset,
+ Representation::Integer32());
+ }
+
static HObjectAccess ForStringLength() {
STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
return HObjectAccess(
@@ -5816,6 +5845,14 @@
FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
}
+ static HObjectAccess ForConsStringFirst() {
+ return HObjectAccess(kInobject, ConsString::kFirstOffset);
+ }
+
+ static HObjectAccess ForConsStringSecond() {
+ return HObjectAccess(kInobject, ConsString::kSecondOffset);
+ }
+
static HObjectAccess ForPropertiesPointer() {
return HObjectAccess(kInobject, JSObject::kPropertiesOffset);
}
@@ -5863,6 +5900,12 @@
Representation::UInteger8());
}
+ static HObjectAccess ForMapInstanceType() {
+ return HObjectAccess(kInobject,
+ Map::kInstanceTypeOffset,
+ Representation::UInteger8());
+ }
+
static HObjectAccess ForPropertyCellValue() {
return HObjectAccess(kInobject, PropertyCell::kValueOffset);
}
diff --git a/src/hydrogen-minus-zero.cc b/src/hydrogen-minus-zero.cc
index 28ae6eb..316e0f5 100644
--- a/src/hydrogen-minus-zero.cc
+++ b/src/hydrogen-minus-zero.cc
@@ -49,6 +49,14 @@
PropagateMinusZeroChecks(change->value());
visited_.Clear();
}
+ } else if (current->IsCompareMinusZeroAndBranch()) {
+ HCompareMinusZeroAndBranch* check =
+ HCompareMinusZeroAndBranch::cast(current);
+ if (check->value()->representation().IsSmiOrInteger32()) {
+ ASSERT(visited_.IsEmpty());
+ PropagateMinusZeroChecks(check->value());
+ visited_.Clear();
+ }
}
}
}
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 147563e..7c42a3b 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -1469,6 +1469,342 @@
}
+HValue* HGraphBuilder::BuildSeqStringSizeFor(HValue* length,
+ String::Encoding encoding) {
+ STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0);
+ HValue* size = length;
+ if (encoding == String::TWO_BYTE_ENCODING) {
+ size = Add<HShl>(length, graph()->GetConstant1());
+ size->ClearFlag(HValue::kCanOverflow);
+ size->SetFlag(HValue::kUint32);
+ }
+ size = Add<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
+ SeqString::kHeaderSize + kObjectAlignmentMask)));
+ size->ClearFlag(HValue::kCanOverflow);
+ size = Add<HBitwise>(
+ Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
+ ~kObjectAlignmentMask)));
+ return size;
+}
+
+
+void HGraphBuilder::BuildCopySeqStringChars(HValue* src,
+ HValue* src_offset,
+ String::Encoding src_encoding,
+ HValue* dst,
+ HValue* dst_offset,
+ String::Encoding dst_encoding,
+ HValue* length) {
+ ASSERT(dst_encoding != String::ONE_BYTE_ENCODING ||
+ src_encoding == String::ONE_BYTE_ENCODING);
+ LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
+ HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT);
+ {
+ HValue* src_index = Add<HAdd>(src_offset, index);
+ HValue* value = Add<HSeqStringGetChar>(src_encoding, src, src_index);
+ HValue* dst_index = Add<HAdd>(dst_offset, index);
+ Add<HSeqStringSetChar>(dst_encoding, dst, dst_index, value);
+ }
+ loop.EndBody();
+}
+
+
+HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
+ HValue* right,
+ PretenureFlag pretenure_flag) {
+ // Determine the string lengths.
+ HValue* left_length = Add<HLoadNamedField>(
+ left, HObjectAccess::ForStringLength());
+ HValue* right_length = Add<HLoadNamedField>(
+ right, HObjectAccess::ForStringLength());
+
+ // Check if we concatenated the strings here, or if we have to resort to the
+ // runtime function.
+ HIfContinuation handled(graph()->CreateBasicBlock(),
+ graph()->CreateBasicBlock());
+
+ // Check if both parameters do not exceed half the max string length, because
+ // exceptionally long strings should be handled in the runtime. Unfortunately
+ // we cannot actually check whether the combined length of both strings
+ // exceeds String::kMaxLength (because of unclear results from the
+ // representation inference phase), so we use a pessimistic approach here
+ // instead, checking that the length of either substring does not exceed half
+ // of String::kMaxLength.
+ HConstant* max_length = Add<HConstant>(String::kMaxLength / 2);
+ IfBuilder if_nooverflow(this);
+ if_nooverflow.If<HCompareNumericAndBranch>(
+ left_length, max_length, Token::LTE);
+ if_nooverflow.AndIf<HCompareNumericAndBranch>(
+ right_length, max_length, Token::LTE);
+ if_nooverflow.Then();
+ {
+ // Determine the string instance types.
+ HLoadNamedField* left_instance_type = Add<HLoadNamedField>(
+ Add<HLoadNamedField>(left, HObjectAccess::ForMap()),
+ HObjectAccess::ForMapInstanceType());
+ HLoadNamedField* right_instance_type = Add<HLoadNamedField>(
+ Add<HLoadNamedField>(right, HObjectAccess::ForMap()),
+ HObjectAccess::ForMapInstanceType());
+
+ // Compute difference of instance types.
+ HValue* xored_instance_types = Add<HBitwise>(
+ Token::BIT_XOR, left_instance_type, right_instance_type);
+
+ // Compute the length of the resulting string.
+ HValue* length = Add<HAdd>(left_length, right_length);
+
+ // Check if we should create a cons string.
+ IfBuilder if_createcons(this);
+ if_createcons.If<HCompareNumericAndBranch>(
+ length, Add<HConstant>(ConsString::kMinLength), Token::GTE);
+ if_createcons.Then();
+ {
+ // Allocate the cons string object. HAllocate does not care whether we
+ // pass CONS_STRING_TYPE or CONS_ASCII_STRING_TYPE here, so we just use
+ // CONS_STRING_TYPE here. Below we decide whether the cons string is
+ // one-byte or two-byte and set the appropriate map.
+ HAllocate* string = Add<HAllocate>(Add<HConstant>(ConsString::kSize),
+ HType::String(), pretenure_flag,
+ CONS_STRING_TYPE);
+
+ // Compute the intersection of instance types.
+ HValue* anded_instance_types = Add<HBitwise>(
+ Token::BIT_AND, left_instance_type, right_instance_type);
+
+ // We create a one-byte cons string if
+ // 1. both strings are one-byte, or
+ // 2. at least one of the strings is two-byte, but happens to contain only
+ // one-byte characters.
+ // To do this, we check
+ // 1. if both strings are one-byte, or if the one-byte data hint is set in
+ // both strings, or
+ // 2. if one of the strings has the one-byte data hint set and the other
+ // string is one-byte.
+ IfBuilder if_onebyte(this);
+ STATIC_ASSERT(kOneByteStringTag != 0);
+ STATIC_ASSERT(kOneByteDataHintMask != 0);
+ if_onebyte.If<HCompareNumericAndBranch>(
+ Add<HBitwise>(
+ Token::BIT_AND, anded_instance_types,
+ Add<HConstant>(static_cast<int32_t>(
+ kStringEncodingMask | kOneByteDataHintMask))),
+ graph()->GetConstant0(), Token::NE);
+ if_onebyte.Or();
+ STATIC_ASSERT(kOneByteStringTag != 0 &&
+ kOneByteDataHintTag != 0 &&
+ kOneByteDataHintTag != kOneByteStringTag);
+ if_onebyte.If<HCompareNumericAndBranch>(
+ Add<HBitwise>(
+ Token::BIT_AND, xored_instance_types,
+ Add<HConstant>(static_cast<int32_t>(
+ kOneByteStringTag | kOneByteDataHintTag))),
+ Add<HConstant>(static_cast<int32_t>(
+ kOneByteStringTag | kOneByteDataHintTag)), Token::EQ);
+ if_onebyte.Then();
+ {
+ // We can safely skip the write barrier for storing the map here.
+ Handle<Map> map = isolate()->factory()->cons_ascii_string_map();
+ AddStoreMapConstantNoWriteBarrier(string, map);
+ }
+ if_onebyte.Else();
+ {
+ // We can safely skip the write barrier for storing the map here.
+ Handle<Map> map = isolate()->factory()->cons_string_map();
+ AddStoreMapConstantNoWriteBarrier(string, map);
+ }
+ if_onebyte.End();
+
+ // Initialize the cons string fields.
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
+ Add<HConstant>(String::kEmptyHashField));
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length);
+ Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left);
+ Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(),
+ right);
+
+ // Cons string is result.
+ Push(string);
+ }
+ if_createcons.Else();
+ {
+ // Compute union of instance types.
+ HValue* ored_instance_types = Add<HBitwise>(
+ Token::BIT_OR, left_instance_type, right_instance_type);
+
+ // Check if both strings have the same encoding and both are
+ // sequential.
+ IfBuilder if_sameencodingandsequential(this);
+ if_sameencodingandsequential.If<HCompareNumericAndBranch>(
+ Add<HBitwise>(
+ Token::BIT_AND, xored_instance_types,
+ Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
+ graph()->GetConstant0(), Token::EQ);
+ if_sameencodingandsequential.And();
+ STATIC_ASSERT(kSeqStringTag == 0);
+ if_sameencodingandsequential.If<HCompareNumericAndBranch>(
+ Add<HBitwise>(
+ Token::BIT_AND, ored_instance_types,
+ Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))),
+ graph()->GetConstant0(), Token::EQ);
+ if_sameencodingandsequential.Then();
+ {
+ // Check if the result is a one-byte string.
+ IfBuilder if_onebyte(this);
+ STATIC_ASSERT(kOneByteStringTag != 0);
+ if_onebyte.If<HCompareNumericAndBranch>(
+ Add<HBitwise>(
+ Token::BIT_AND, ored_instance_types,
+ Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
+ graph()->GetConstant0(), Token::NE);
+ if_onebyte.Then();
+ {
+ // Calculate the number of bytes needed for the characters in the
+ // string while observing object alignment.
+ HValue* size = BuildSeqStringSizeFor(
+ length, String::ONE_BYTE_ENCODING);
+
+ // Allocate the ASCII string object.
+ Handle<Map> map = isolate()->factory()->ascii_string_map();
+ HAllocate* string = Add<HAllocate>(size, HType::String(),
+ pretenure_flag, ASCII_STRING_TYPE);
+ string->set_known_initial_map(map);
+
+ // We can safely skip the write barrier for storing map here.
+ AddStoreMapConstantNoWriteBarrier(string, map);
+
+ // Copy bytes from the left string.
+ BuildCopySeqStringChars(
+ left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
+ string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
+ left_length);
+
+ // Copy bytes from the right string.
+ BuildCopySeqStringChars(
+ right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
+ string, left_length, String::ONE_BYTE_ENCODING,
+ right_length);
+
+ // Return the string.
+ Push(string);
+ }
+ if_onebyte.Else();
+ {
+ // Calculate the number of bytes needed for the characters in the
+ // string while observing object alignment.
+ HValue* size = BuildSeqStringSizeFor(
+ length, String::TWO_BYTE_ENCODING);
+
+ // Allocate the two-byte string object.
+ Handle<Map> map = isolate()->factory()->string_map();
+ HAllocate* string = Add<HAllocate>(size, HType::String(),
+ pretenure_flag, STRING_TYPE);
+ string->set_known_initial_map(map);
+
+ // We can safely skip the write barrier for storing map here.
+ AddStoreMapConstantNoWriteBarrier(string, map);
+
+ // Copy bytes from the left string.
+ BuildCopySeqStringChars(
+ left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
+ string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
+ left_length);
+
+ // Copy bytes from the right string.
+ BuildCopySeqStringChars(
+ right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
+ string, left_length, String::TWO_BYTE_ENCODING,
+ right_length);
+
+ // Return the string.
+ Push(string);
+ }
+ if_onebyte.End();
+
+ // Initialize the (common) string fields.
+ HValue* string = Pop();
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
+ Add<HConstant>(String::kEmptyHashField));
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
+ length);
+ Push(string);
+ }
+ if_sameencodingandsequential.JoinContinuation(&handled);
+ }
+ if_createcons.JoinContinuation(&handled);
+ }
+ if_nooverflow.JoinContinuation(&handled);
+
+ // Check if the strings were concatenated successfully, otherwise fallback to
+ // add the strings in the runtime.
+ IfBuilder if_handled(this, &handled);
+ if_handled.Then();
+ {
+ // Count the native string addition.
+ AddIncrementCounter(isolate()->counters()->string_add_native());
+ }
+ if_handled.Else();
+ {
+ // Fallback to the runtime to add the two strings.
+ Add<HPushArgument>(left);
+ Add<HPushArgument>(right);
+ Push(Add<HCallRuntime>(isolate()->factory()->empty_string(),
+ Runtime::FunctionForId(Runtime::kStringAdd),
+ 2));
+ }
+ if_handled.End();
+
+ return Pop();
+}
+
+
+HValue* HGraphBuilder::BuildStringAdd(HValue* left,
+ HValue* right,
+ PretenureFlag pretenure_flag) {
+ // Determine the string lengths.
+ HValue* left_length = Add<HLoadNamedField>(
+ left, HObjectAccess::ForStringLength());
+ HValue* right_length = Add<HLoadNamedField>(
+ right, HObjectAccess::ForStringLength());
+
+ // Check if left string is empty.
+ IfBuilder if_leftisempty(this);
+ if_leftisempty.If<HCompareNumericAndBranch>(
+ left_length, graph()->GetConstant0(), Token::EQ);
+ if_leftisempty.Then();
+ {
+ // Count the native string addition.
+ AddIncrementCounter(isolate()->counters()->string_add_native());
+
+ // Just return the right string.
+ Push(right);
+ }
+ if_leftisempty.Else();
+ {
+ // Check if right string is empty.
+ IfBuilder if_rightisempty(this);
+ if_rightisempty.If<HCompareNumericAndBranch>(
+ right_length, graph()->GetConstant0(), Token::EQ);
+ if_rightisempty.Then();
+ {
+ // Count the native string addition.
+ AddIncrementCounter(isolate()->counters()->string_add_native());
+
+ // Just return the left string.
+ Push(left);
+ }
+ if_rightisempty.Else();
+ {
+ // Concatenate the two non-empty strings.
+ Push(BuildUncheckedStringAdd(left, right, pretenure_flag));
+ }
+ if_rightisempty.End();
+ }
+ if_leftisempty.End();
+
+ return Pop();
+}
+
+
HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* checked_object,
HValue* key,
@@ -6369,6 +6705,11 @@
Handle<JSFunction> caller = current_info()->closure();
Handle<SharedFunctionInfo> target_shared(target->shared());
+ // Always inline builtins marked for inlining.
+ if (target->IsBuiltin()) {
+ return target_shared->inline_builtin() ? 0 : kNotInlinable;
+ }
+
// Do a quick check on source code length to avoid parsing large
// inlining candidates.
if (target_shared->SourceSize() >
@@ -6378,7 +6719,7 @@
}
// Target must be inlineable.
- if (!target->IsInlineable()) {
+ if (!target_shared->IsInlineable()) {
TraceInline(target, caller, "target not inlineable");
return kNotInlinable;
}
@@ -6758,9 +7099,6 @@
case kMathAbs:
case kMathSqrt:
case kMathLog:
- case kMathSin:
- case kMathCos:
- case kMathTan:
if (expr->arguments()->length() == 1) {
HValue* argument = Pop();
Drop(1); // Receiver.
@@ -6839,9 +7177,6 @@
case kMathAbs:
case kMathSqrt:
case kMathLog:
- case kMathSin:
- case kMathCos:
- case kMathTan:
if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
HValue* argument = Pop();
@@ -8778,6 +9113,15 @@
}
+void HOptimizedGraphBuilder::GenerateIsMinusZero(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HCompareMinusZeroAndBranch* result = New<HCompareMinusZeroAndBranch>(value);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
void HOptimizedGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
@@ -9161,36 +9505,6 @@
}
-void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
- ASSERT_EQ(1, call->arguments()->length());
- CHECK_ALIVE(VisitArgumentList(call->arguments()));
- HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
- result->set_transcendental_type(TranscendentalCache::SIN);
- Drop(1);
- return ast_context()->ReturnInstruction(result, call->id());
-}
-
-
-void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
- ASSERT_EQ(1, call->arguments()->length());
- CHECK_ALIVE(VisitArgumentList(call->arguments()));
- HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
- result->set_transcendental_type(TranscendentalCache::COS);
- Drop(1);
- return ast_context()->ReturnInstruction(result, call->id());
-}
-
-
-void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
- ASSERT_EQ(1, call->arguments()->length());
- CHECK_ALIVE(VisitArgumentList(call->arguments()));
- HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
- result->set_transcendental_type(TranscendentalCache::TAN);
- Drop(1);
- return ast_context()->ReturnInstruction(result, call->id());
-}
-
-
void HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
CHECK_ALIVE(VisitArgumentList(call->arguments()));
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 8f4878d..b3cb8ff 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -1276,6 +1276,26 @@
HValue* BuildNumberToString(HValue* object, Handle<Type> type);
+ // Computes the size for a sequential string of the given length and encoding.
+ HValue* BuildSeqStringSizeFor(HValue* length,
+ String::Encoding encoding);
+ // Copies characters from one sequential string to another.
+ void BuildCopySeqStringChars(HValue* src,
+ HValue* src_offset,
+ String::Encoding src_encoding,
+ HValue* dst,
+ HValue* dst_offset,
+ String::Encoding dst_encoding,
+ HValue* length);
+ // Both operands are non-empty strings.
+ HValue* BuildUncheckedStringAdd(HValue* left,
+ HValue* right,
+ PretenureFlag pretenure_flag);
+ // Both operands are strings.
+ HValue* BuildStringAdd(HValue* left,
+ HValue* right,
+ PretenureFlag pretenure_flag);
+
HInstruction* BuildUncheckedMonomorphicElementAccess(
HValue* checked_object,
HValue* key,
@@ -1298,7 +1318,13 @@
HLoadNamedField* BuildLoadNamedField(HValue* object, HObjectAccess access);
HInstruction* AddLoadNamedField(HValue* object, HObjectAccess access);
HInstruction* BuildLoadStringLength(HValue* object, HValue* checked_value);
- HStoreNamedField* AddStoreMapConstant(HValue* object, Handle<Map>);
+ HStoreNamedField* AddStoreMapConstant(HValue* object, Handle<Map> map);
+ HStoreNamedField* AddStoreMapConstantNoWriteBarrier(HValue* object,
+ Handle<Map> map) {
+ HStoreNamedField* store_map = AddStoreMapConstant(object, map);
+ store_map->SkipWriteBarrier();
+ return store_map;
+ }
HLoadNamedField* AddLoadElements(HValue* object);
bool MatchRotateRight(HValue* left,
diff --git a/src/i18n.js b/src/i18n.js
index a64c7e6..6b563a0 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1302,10 +1302,7 @@
*/
function formatNumber(formatter, value) {
// Spec treats -0 and +0 as 0.
- var number = $Number(value);
- if (number === -0) {
- number = 0;
- }
+ var number = $Number(value) + 0;
return %InternalNumberFormat(formatter.formatter, number);
}
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 941bb32..adc2d50 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -168,15 +168,21 @@
// eax -- number of arguments
// edi -- function
// ebx -- type info cell with elements kind
- static Register registers[] = { edi, ebx };
- descriptor->register_param_count_ = 2;
+ static Register registers_variable_args[] = { edi, ebx, eax };
+ static Register registers_no_args[] = { edi, ebx };
- if (constant_stack_parameter_count != 0) {
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = eax;
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
@@ -190,15 +196,21 @@
// register state
// eax -- number of arguments
// edi -- constructor function
- static Register registers[] = { edi };
- descriptor->register_param_count_ = 1;
+ static Register registers_variable_args[] = { edi, eax };
+ static Register registers_no_args[] = { edi };
- if (constant_stack_parameter_count != 0) {
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = eax;
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
@@ -306,6 +318,17 @@
}
+void NewStringAddStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx, eax };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kStringAdd)->entry;
+}
+
+
#define __ ACCESS_MASM(masm)
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index e339b3a..e043aa4 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -202,7 +202,7 @@
FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
intptr_t handler =
reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
- int params = descriptor->environment_length();
+ int params = descriptor->GetHandlerParameterCount();
output_frame->SetRegister(eax.code(), params);
output_frame->SetRegister(ebx.code(), handler);
}
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index b7ddeac..8a51fbc 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -3052,6 +3052,32 @@
}
+void FullCodeGenerator::EmitIsMinusZero(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Handle<Map> map = masm()->isolate()->factory()->heap_number_map();
+ __ CheckMap(eax, map, if_false, DO_SMI_CHECK);
+ __ cmp(FieldOperand(eax, HeapNumber::kExponentOffset), Immediate(0x80000000));
+ __ j(not_equal, if_false);
+ __ cmp(FieldOperand(eax, HeapNumber::kMantissaOffset), Immediate(0x00000000));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+
void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@@ -3671,11 +3697,20 @@
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
- VisitForStackValue(args->at(0));
- VisitForStackValue(args->at(1));
+ if (FLAG_new_string_add) {
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
- StringAddStub stub(STRING_ADD_CHECK_BOTH);
- __ CallStub(&stub);
+ __ pop(edx);
+ NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
+ __ CallStub(&stub);
+ } else {
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ }
context()->Plug(eax);
}
@@ -3693,42 +3728,6 @@
}
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(eax);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(eax);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::TAN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(eax);
-}
-
-
void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG,
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 89df802..de52fad 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -2670,6 +2670,35 @@
}
+void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) {
+ Representation rep = instr->hydrogen()->value()->representation();
+ ASSERT(!rep.IsInteger32());
+ Register scratch = ToRegister(instr->temp());
+
+ if (rep.IsDouble()) {
+ CpuFeatureScope use_sse2(masm(), SSE2);
+ XMMRegister value = ToDoubleRegister(instr->value());
+ XMMRegister xmm_scratch = double_scratch0();
+ __ xorps(xmm_scratch, xmm_scratch);
+ __ ucomisd(xmm_scratch, value);
+ EmitFalseBranch(instr, not_equal);
+ __ movmskpd(scratch, value);
+ __ test(scratch, Immediate(1));
+ EmitBranch(instr, not_zero);
+ } else {
+ Register value = ToRegister(instr->value());
+ Handle<Map> map = masm()->isolate()->factory()->heap_number_map();
+ __ CheckMap(value, map, instr->FalseLabel(chunk()), DO_SMI_CHECK);
+ __ cmp(FieldOperand(value, HeapNumber::kExponentOffset),
+ Immediate(0x80000000));
+ EmitFalseBranch(instr, not_equal);
+ __ cmp(FieldOperand(value, HeapNumber::kMantissaOffset),
+ Immediate(0x00000000));
+ EmitBranch(instr, equal);
+ }
+}
+
+
Condition LCodeGen::EmitIsObject(Register input,
Register temp1,
Label* is_not_object,
@@ -4551,6 +4580,10 @@
if (operand_value->IsRegister()) {
Register value = ToRegister(operand_value);
__ Store(value, operand, representation);
+ } else if (representation.IsInteger32()) {
+ Immediate immediate = ToImmediate(operand_value, representation);
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ __ mov(operand, immediate);
} else {
Handle<Object> handle_value = ToHandle(operand_value);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
@@ -4997,10 +5030,18 @@
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
- EmitPushTaggedOperand(instr->left());
- EmitPushTaggedOperand(instr->right());
- StringAddStub stub(instr->hydrogen()->flags());
- CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ if (FLAG_new_string_add) {
+ ASSERT(ToRegister(instr->left()).is(edx));
+ ASSERT(ToRegister(instr->right()).is(eax));
+ NewStringAddStub stub(instr->hydrogen()->flags(),
+ isolate()->heap()->GetPretenureMode());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ }
}
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index 6c9cde3..514b42e 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -320,6 +320,8 @@
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
void EmitGoto(int block);
+
+ // EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition cc);
template<class InstrType>
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 632b065..29b42fe 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1768,6 +1768,16 @@
}
+LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch(
+ HCompareMinusZeroAndBranch* instr) {
+ LInstruction* goto_instr = CheckElideControlInstruction(instr);
+ if (goto_instr != NULL) return goto_instr;
+ LOperand* value = UseRegister(instr->value());
+ LOperand* scratch = TempRegister();
+ return new(zone()) LCompareMinusZeroAndBranch(value, scratch);
+}
+
+
LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
ASSERT(instr->value()->representation().IsSmiOrTagged());
LOperand* temp = TempRegister();
@@ -2485,8 +2495,12 @@
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), esi);
- LOperand* left = UseOrConstantAtStart(instr->left());
- LOperand* right = UseOrConstantAtStart(instr->right());
+ LOperand* left = FLAG_new_string_add
+ ? UseFixed(instr->left(), edx)
+ : UseOrConstantAtStart(instr->left());
+ LOperand* right = FLAG_new_string_add
+ ? UseFixed(instr->right(), eax)
+ : UseOrConstantAtStart(instr->right());
LStringAdd* string_add = new(zone()) LStringAdd(context, left, right);
return MarkAsCall(DefineFixed(string_add, eax), instr);
}
@@ -2555,7 +2569,7 @@
CodeStubInterfaceDescriptor* descriptor =
info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
int index = static_cast<int>(instr->index());
- Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ Register reg = descriptor->GetParameterRegister(index);
return DefineFixed(result, reg);
}
}
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index 600c40f..b0d031a 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -74,6 +74,7 @@
V(ClampTToUint8NoSSE2) \
V(ClassOfTestAndBranch) \
V(ClobberDoubles) \
+ V(CompareMinusZeroAndBranch) \
V(CompareNumericAndBranch) \
V(CmpObjectEqAndBranch) \
V(CmpHoleAndBranch) \
@@ -900,6 +901,22 @@
};
+class LCompareMinusZeroAndBranch V8_FINAL : public LControlInstruction<1, 1> {
+ public:
+ LCompareMinusZeroAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch,
+ "cmp-minus-zero-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMinusZeroAndBranch)
+};
+
+
class LIsObjectAndBranch V8_FINAL : public LControlInstruction<1, 1> {
public:
LIsObjectAndBranch(LOperand* value, LOperand* temp) {
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index c673727..1bdd382 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -2010,30 +2010,48 @@
Register destination,
Register length,
Register scratch) {
- Label loop, done, short_string, short_loop;
- // Experimentation shows that the short string loop is faster if length < 10.
- cmp(length, Immediate(10));
- j(less_equal, &short_string);
-
+ Label short_loop, len4, len8, len12, done, short_string;
ASSERT(source.is(esi));
ASSERT(destination.is(edi));
ASSERT(length.is(ecx));
+ cmp(length, Immediate(4));
+ j(below, &short_string, Label::kNear);
// Because source is 4-byte aligned in our uses of this function,
// we keep source aligned for the rep_movs call by copying the odd bytes
// at the end of the ranges.
mov(scratch, Operand(source, length, times_1, -4));
mov(Operand(destination, length, times_1, -4), scratch);
+
+ cmp(length, Immediate(8));
+ j(below_equal, &len4, Label::kNear);
+ cmp(length, Immediate(12));
+ j(below_equal, &len8, Label::kNear);
+ cmp(length, Immediate(16));
+ j(below_equal, &len12, Label::kNear);
+
mov(scratch, ecx);
shr(ecx, 2);
rep_movs();
and_(scratch, Immediate(0x3));
add(destination, scratch);
- jmp(&done);
+ jmp(&done, Label::kNear);
+
+ bind(&len12);
+ mov(scratch, Operand(source, 8));
+ mov(Operand(destination, 8), scratch);
+ bind(&len8);
+ mov(scratch, Operand(source, 4));
+ mov(Operand(destination, 4), scratch);
+ bind(&len4);
+ mov(scratch, Operand(source, 0));
+ mov(Operand(destination, 0), scratch);
+ add(destination, length);
+ jmp(&done, Label::kNear);
bind(&short_string);
test(length, length);
- j(zero, &done);
+ j(zero, &done, Label::kNear);
bind(&short_loop);
mov_b(scratch, Operand(source, 0));
diff --git a/src/isolate.cc b/src/isolate.cc
index 0e764bf..740cce0 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -2328,6 +2328,7 @@
InternalArrayConstructorStubBase::InstallDescriptors(this);
FastNewClosureStub::InstallDescriptors(this);
NumberToStringStub::InstallDescriptors(this);
+ NewStringAddStub::InstallDescriptors(this);
}
if (FLAG_sweeper_threads > 0) {
diff --git a/src/macros.py b/src/macros.py
index 1785d44..7bad23b 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -157,6 +157,13 @@
macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
+# Private names.
+macro NEW_PRIVATE(name) = (%CreatePrivateSymbol(name));
+macro HAS_PRIVATE(obj, sym) = (sym in obj);
+macro GET_PRIVATE(obj, sym) = (obj[sym]);
+macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
+macro DELETE_PRIVATE(obj, sym) = (delete obj[sym]);
+
# Constants. The compiler constant folds them.
const NAN = $NaN;
const INFINITY = (1/0);
diff --git a/src/math.js b/src/math.js
index efab63a..385b3c2 100644
--- a/src/math.js
+++ b/src/math.js
@@ -79,7 +79,7 @@
// ECMA 262 - 15.8.2.7
function MathCos(x) {
- return %_MathCos(TO_NUMBER_INLINE(x));
+ return MathCosImpl(x);
}
// ECMA 262 - 15.8.2.8
@@ -117,9 +117,8 @@
if (arg2 > arg1) return arg2;
if (arg1 > arg2) return arg1;
if (arg1 == arg2) {
- // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be
- // a Smi or a heap number.
- return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg2 : arg1;
+ // Make sure -0 is considered less than +0.
+ return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
}
// All comparisons failed, one of the arguments must be NaN.
return NAN;
@@ -128,10 +127,8 @@
for (var i = 0; i < length; i++) {
var n = %_Arguments(i);
if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
- // Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be
- // a Smi or heap number.
- if (NUMBER_IS_NAN(n) || n > r ||
- (r == 0 && n == 0 && !%_IsSmi(r) && 1 / r < 0)) {
+ // Make sure +0 is considered greater than -0.
+ if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
r = n;
}
}
@@ -147,9 +144,8 @@
if (arg2 > arg1) return arg1;
if (arg1 > arg2) return arg2;
if (arg1 == arg2) {
- // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be
- // a Smi or a heap number.
- return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg1 : arg2;
+ // Make sure -0 is considered less than +0.
+ return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
}
// All comparisons failed, one of the arguments must be NaN.
return NAN;
@@ -158,10 +154,8 @@
for (var i = 0; i < length; i++) {
var n = %_Arguments(i);
if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
- // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be a
- // Smi or a heap number.
- if (NUMBER_IS_NAN(n) || n < r ||
- (r == 0 && n == 0 && !%_IsSmi(n) && 1 / n < 0)) {
+ // Make sure -0 is considered less than +0.
+ if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
r = n;
}
}
@@ -185,7 +179,7 @@
// ECMA 262 - 15.8.2.16
function MathSin(x) {
- return %_MathSin(TO_NUMBER_INLINE(x));
+ return MathSinImpl(x);
}
// ECMA 262 - 15.8.2.17
@@ -195,7 +189,7 @@
// ECMA 262 - 15.8.2.18
function MathTan(x) {
- return %_MathTan(TO_NUMBER_INLINE(x));
+ return MathSinImpl(x) / MathCosImpl(x);
}
// Non-standard extension.
@@ -204,6 +198,92 @@
}
+var MathSinImpl = function(x) {
+ InitTrigonometricFunctions();
+ return MathSinImpl(x);
+}
+
+
+var MathCosImpl = function(x) {
+ InitTrigonometricFunctions();
+ return MathCosImpl(x);
+}
+
+
+var InitTrigonometricFunctions;
+
+
+// Define constants and interpolation functions.
+// Also define the initialization function that populates the lookup table
+// and then wires up the function definitions.
+function SetupTrigonometricFunctions() {
+ var samples = 1800; // Table size.
+ var pi = 3.1415926535897932;
+ var pi_half = pi / 2;
+ var inverse_pi_half = 1 / pi_half;
+ var two_pi = pi * 2;
+ var interval = pi_half / samples;
+ var inverse_interval = samples / pi_half;
+ var table_sin;
+ var table_cos_interval;
+
+ // This implements sine using the following algorithm.
+ // 1) Multiplication takes care of to-number conversion.
+ // 2) Reduce x to the first quadrant [0, pi/2].
+ // Conveniently enough, in case of +/-Infinity, we get NaN.
+ // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
+ // 4) Do a table lookup for the closest samples to the left and right of x.
+ // 5) Find the derivatives at those sampling points by table lookup:
+ // dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
+ // 6) Use cubic spline interpolation to approximate sin(x).
+ // 7) Negate the result if x was in the 3rd or 4th quadrant.
+ // 8) Get rid of -0 by adding 0.
+ var Interpolation = function(x) {
+ var double_index = x * inverse_interval;
+ var index = double_index | 0;
+ var t1 = double_index - index;
+ var t2 = 1 - t1;
+ var y1 = table_sin[index];
+ var y2 = table_sin[index + 1];
+ var dy = y2 - y1;
+ return (t2 * y1 + t1 * y2 +
+ t1 * t2 * ((table_cos_interval[index] - dy) * t2 +
+ (dy - table_cos_interval[index + 1]) * t1));
+ }
+
+ var MathSinInterpolation = function(x) {
+ var multiple = MathFloor(x * inverse_pi_half);
+ if (%_IsMinusZero(multiple)) return multiple;
+ x = (multiple & 1) * pi_half +
+ (1 - ((multiple & 1) << 1)) * (x - multiple * pi_half);
+ return Interpolation(x) * (1 - (multiple & 2)) + 0;
+ }
+
+ // Cosine is sine with a phase offset of pi/2.
+ var MathCosInterpolation = function(x) {
+ var multiple = MathFloor(x * inverse_pi_half);
+ var phase = multiple + 1;
+ x = (phase & 1) * pi_half +
+ (1 - ((phase & 1) << 1)) * (x - multiple * pi_half);
+ return Interpolation(x) * (1 - (phase & 2)) + 0;
+ };
+
+ %SetInlineBuiltinFlag(Interpolation);
+ %SetInlineBuiltinFlag(MathSinInterpolation);
+ %SetInlineBuiltinFlag(MathCosInterpolation);
+
+ InitTrigonometricFunctions = function() {
+ table_sin = new global.Float64Array(samples + 2);
+ table_cos_interval = new global.Float64Array(samples + 2);
+ %PopulateTrigonometricTable(table_sin, table_cos_interval, samples);
+ MathSinImpl = MathSinInterpolation;
+ MathCosImpl = MathCosInterpolation;
+ }
+}
+
+SetupTrigonometricFunctions();
+
+
// -------------------------------------------------------------------
function SetUpMath() {
@@ -276,6 +356,10 @@
"min", MathMin,
"imul", MathImul
));
+
+ %SetInlineBuiltinFlag(MathSin);
+ %SetInlineBuiltinFlag(MathCos);
+ %SetInlineBuiltinFlag(MathTan);
}
SetUpMath();
diff --git a/src/messages.js b/src/messages.js
index bfd42de..926f294 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -783,64 +783,67 @@
// ----------------------------------------------------------------------------
// Error implementation
-var CallSiteReceiverKey = %CreateSymbol("receiver");
-var CallSiteFunctionKey = %CreateSymbol("function");
-var CallSitePositionKey = %CreateSymbol("position");
-var CallSiteStrictModeKey = %CreateSymbol("strict mode");
+//TODO(rossberg)
+var CallSiteReceiverKey = NEW_PRIVATE("receiver");
+var CallSiteFunctionKey = NEW_PRIVATE("function");
+var CallSitePositionKey = NEW_PRIVATE("position");
+var CallSiteStrictModeKey = NEW_PRIVATE("strict mode");
function CallSite(receiver, fun, pos, strict_mode) {
- this[CallSiteReceiverKey] = receiver;
- this[CallSiteFunctionKey] = fun;
- this[CallSitePositionKey] = pos;
- this[CallSiteStrictModeKey] = strict_mode;
+ SET_PRIVATE(this, CallSiteReceiverKey, receiver);
+ SET_PRIVATE(this, CallSiteFunctionKey, fun);
+ SET_PRIVATE(this, CallSitePositionKey, pos);
+ SET_PRIVATE(this, CallSiteStrictModeKey, strict_mode);
}
function CallSiteGetThis() {
- return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteReceiverKey];
+ return GET_PRIVATE(this, CallSiteStrictModeKey)
+ ? UNDEFINED : GET_PRIVATE(this, CallSiteReceiverKey);
}
function CallSiteGetTypeName() {
- return GetTypeName(this[CallSiteReceiverKey], false);
+ return GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), false);
}
function CallSiteIsToplevel() {
- if (this[CallSiteReceiverKey] == null) {
+ if (GET_PRIVATE(this, CallSiteReceiverKey) == null) {
return true;
}
- return IS_GLOBAL(this[CallSiteReceiverKey]);
+ return IS_GLOBAL(GET_PRIVATE(this, CallSiteReceiverKey));
}
function CallSiteIsEval() {
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
return script && script.compilation_type == COMPILATION_TYPE_EVAL;
}
function CallSiteGetEvalOrigin() {
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
return FormatEvalOrigin(script);
}
function CallSiteGetScriptNameOrSourceURL() {
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
return script ? script.nameOrSourceURL() : null;
}
function CallSiteGetFunction() {
- return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteFunctionKey];
+ return GET_PRIVATE(this, CallSiteStrictModeKey)
+ ? UNDEFINED : GET_PRIVATE(this, CallSiteFunctionKey);
}
function CallSiteGetFunctionName() {
// See if the function knows its own name
- var name = this[CallSiteFunctionKey].name;
+ var name = GET_PRIVATE(this, CallSiteFunctionKey).name;
if (name) {
return name;
}
- name = %FunctionGetInferredName(this[CallSiteFunctionKey]);
+ name = %FunctionGetInferredName(GET_PRIVATE(this, CallSiteFunctionKey));
if (name) {
return name;
}
// Maybe this is an evaluation?
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
return "eval";
}
@@ -850,8 +853,8 @@
function CallSiteGetMethodName() {
// See if we can find a unique property on the receiver that holds
// this function.
- var receiver = this[CallSiteReceiverKey];
- var fun = this[CallSiteFunctionKey];
+ var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
+ var fun = GET_PRIVATE(this, CallSiteFunctionKey);
var ownName = fun.name;
if (ownName && receiver &&
(%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
@@ -880,49 +883,51 @@
}
function CallSiteGetFileName() {
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
return script ? script.name : null;
}
function CallSiteGetLineNumber() {
- if (this[CallSitePositionKey] == -1) {
+ if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
return null;
}
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
var location = null;
if (script) {
- location = script.locationFromPosition(this[CallSitePositionKey], true);
+ location = script.locationFromPosition(
+ GET_PRIVATE(this, CallSitePositionKey), true);
}
return location ? location.line + 1 : null;
}
function CallSiteGetColumnNumber() {
- if (this[CallSitePositionKey] == -1) {
+ if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
return null;
}
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
var location = null;
if (script) {
- location = script.locationFromPosition(this[CallSitePositionKey], true);
+ location = script.locationFromPosition(
+ GET_PRIVATE(this, CallSitePositionKey), true);
}
return location ? location.column + 1: null;
}
function CallSiteIsNative() {
- var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
return script ? (script.type == TYPE_NATIVE) : false;
}
function CallSiteGetPosition() {
- return this[CallSitePositionKey];
+ return GET_PRIVATE(this, CallSitePositionKey);
}
function CallSiteIsConstructor() {
- var receiver = this[CallSiteReceiverKey];
+ var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
var constructor = (receiver != null && IS_OBJECT(receiver))
? %GetDataProperty(receiver, "constructor") : null;
if (!constructor) return false;
- return this[CallSiteFunctionKey] === constructor;
+ return GET_PRIVATE(this, CallSiteFunctionKey) === constructor;
}
function CallSiteToString() {
@@ -965,7 +970,7 @@
var isConstructor = this.isConstructor();
var isMethodCall = !(this.isToplevel() || isConstructor);
if (isMethodCall) {
- var typeName = GetTypeName(this[CallSiteReceiverKey], true);
+ var typeName = GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), true);
var methodName = this.getMethodName();
if (functionName) {
if (typeName &&
diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc
index a0e990c..f053c79 100644
--- a/src/mips/code-stubs-mips.cc
+++ b/src/mips/code-stubs-mips.cc
@@ -291,6 +291,17 @@
}
+void NewStringAddStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a1, a0 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kStringAdd)->entry;
+}
+
+
#define __ ACCESS_MASM(masm)
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index caa7352..a9cf791 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -3133,6 +3133,36 @@
}
+void FullCodeGenerator::EmitIsMinusZero(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, if_false, DO_SMI_CHECK);
+ __ lw(a2, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ lw(a1, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ li(t0, 0x80000000);
+ Label not_nan;
+ __ Branch(¬_nan, ne, a2, Operand(t0));
+ __ mov(t0, zero_reg);
+ __ mov(a2, a1);
+ __ bind(¬_nan);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, a2, Operand(t0), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@@ -3740,11 +3770,20 @@
void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
- VisitForStackValue(args->at(0));
- VisitForStackValue(args->at(1));
+ if (FLAG_new_string_add) {
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
- StringAddStub stub(STRING_ADD_CHECK_BOTH);
- __ CallStub(&stub);
+ __ pop(a1);
+ NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
+ __ CallStub(&stub);
+ } else {
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ }
context()->Plug(v0);
}
@@ -3762,45 +3801,6 @@
}
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
- __ CallStub(&stub);
- context()->Plug(v0);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
- __ CallStub(&stub);
- context()->Plug(v0);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::TAN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
- __ CallStub(&stub);
- context()->Plug(v0);
-}
-
-
void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG,
diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc
index 5c2d636..7f6e869 100644
--- a/src/mips/lithium-codegen-mips.cc
+++ b/src/mips/lithium-codegen-mips.cc
@@ -2039,6 +2039,16 @@
template<class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr,
+ Condition condition,
+ Register src1,
+ const Operand& src2) {
+ int false_block = instr->FalseDestination(chunk_);
+ __ Branch(chunk_->GetAssemblyLabel(false_block), condition, src1, src2);
+}
+
+
+template<class InstrType>
void LCodeGen::EmitFalseBranchF(InstrType instr,
Condition condition,
FPURegister src1,
@@ -2311,6 +2321,32 @@
}
+void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) {
+ Representation rep = instr->hydrogen()->value()->representation();
+ ASSERT(!rep.IsInteger32());
+ Register scratch = ToRegister(instr->temp());
+
+ if (rep.IsDouble()) {
+ DoubleRegister value = ToDoubleRegister(instr->value());
+ EmitFalseBranchF(instr, ne, value, kDoubleRegZero);
+ __ FmoveHigh(scratch, value);
+ __ li(at, 0x80000000);
+ } else {
+ Register value = ToRegister(instr->value());
+ __ CheckMap(value,
+ scratch,
+ Heap::kHeapNumberMapRootIndex,
+ instr->FalseLabel(chunk()),
+ DO_SMI_CHECK);
+ __ lw(scratch, FieldMemOperand(value, HeapNumber::kExponentOffset));
+ EmitFalseBranch(instr, ne, scratch, Operand(0x80000000));
+ __ lw(scratch, FieldMemOperand(value, HeapNumber::kMantissaOffset));
+ __ mov(at, zero_reg);
+ }
+ EmitBranch(instr, eq, scratch, Operand(at));
+}
+
+
Condition LCodeGen::EmitIsObject(Register input,
Register temp1,
Register temp2,
@@ -4480,10 +4516,18 @@
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(cp));
- __ push(ToRegister(instr->left()));
- __ push(ToRegister(instr->right()));
- StringAddStub stub(instr->hydrogen()->flags());
- CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ if (FLAG_new_string_add) {
+ ASSERT(ToRegister(instr->left()).is(a1));
+ ASSERT(ToRegister(instr->right()).is(a0));
+ NewStringAddStub stub(instr->hydrogen()->flags(),
+ isolate()->heap()->GetPretenureMode());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ }
}
diff --git a/src/mips/lithium-codegen-mips.h b/src/mips/lithium-codegen-mips.h
index f41adbf..c15352f 100644
--- a/src/mips/lithium-codegen-mips.h
+++ b/src/mips/lithium-codegen-mips.h
@@ -301,6 +301,8 @@
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
void EmitGoto(int block);
+
+ // EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr,
Condition condition,
@@ -312,6 +314,11 @@
FPURegister src1,
FPURegister src2);
template<class InstrType>
+ void EmitFalseBranch(InstrType instr,
+ Condition condition,
+ Register src1,
+ const Operand& src2);
+ template<class InstrType>
void EmitFalseBranchF(InstrType instr,
Condition condition,
FPURegister src1,
diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc
index edb1206..70dd60d 100644
--- a/src/mips/lithium-mips.cc
+++ b/src/mips/lithium-mips.cc
@@ -1703,6 +1703,16 @@
}
+LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch(
+ HCompareMinusZeroAndBranch* instr) {
+ LInstruction* goto_instr = CheckElideControlInstruction(instr);
+ if (goto_instr != NULL) return goto_instr;
+ LOperand* value = UseRegister(instr->value());
+ LOperand* scratch = TempRegister();
+ return new(zone()) LCompareMinusZeroAndBranch(value, scratch);
+}
+
+
LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* temp = TempRegister();
@@ -2335,8 +2345,12 @@
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), cp);
- LOperand* left = UseRegisterAtStart(instr->left());
- LOperand* right = UseRegisterAtStart(instr->right());
+ LOperand* left = FLAG_new_string_add
+ ? UseFixed(instr->left(), a1)
+ : UseRegisterAtStart(instr->left());
+ LOperand* right = FLAG_new_string_add
+ ? UseFixed(instr->right(), a0)
+ : UseRegisterAtStart(instr->right());
return MarkAsCall(
DefineFixed(new(zone()) LStringAdd(context, left, right), v0),
instr);
diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h
index 5678bb7..8c82a9d 100644
--- a/src/mips/lithium-mips.h
+++ b/src/mips/lithium-mips.h
@@ -72,6 +72,7 @@
V(ClampIToUint8) \
V(ClampTToUint8) \
V(ClassOfTestAndBranch) \
+ V(CompareMinusZeroAndBranch) \
V(CompareNumericAndBranch) \
V(CmpObjectEqAndBranch) \
V(CmpHoleAndBranch) \
@@ -923,6 +924,22 @@
};
+class LCompareMinusZeroAndBranch V8_FINAL : public LControlInstruction<1, 1> {
+ public:
+ LCompareMinusZeroAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch,
+ "cmp-minus-zero-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMinusZeroAndBranch)
+};
+
+
class LIsObjectAndBranch V8_FINAL : public LControlInstruction<1, 1> {
public:
LIsObjectAndBranch(LOperand* value, LOperand* temp) {
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 6ab2ddf..f59687a 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -243,6 +243,7 @@
CHECK(HasHashCode());
CHECK_GT(Hash(), 0);
CHECK(name()->IsUndefined() || name()->IsString());
+ CHECK(flags()->IsSmi());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index bef807e..a89c049 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2692,6 +2692,8 @@
ACCESSORS(Symbol, name, Object, kNameOffset)
+ACCESSORS(Symbol, flags, Smi, kFlagsOffset)
+BOOL_ACCESSORS(Symbol, flags, is_private, kPrivateBit)
bool String::Equals(String* other) {
@@ -4807,6 +4809,8 @@
BOOL_GETTER(SharedFunctionInfo, compiler_hints, is_extended_mode,
kExtendedModeFunction)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, native, kNative)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, inline_builtin,
+ kInlineBuiltin)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints,
name_should_print_as_anonymous,
kNameShouldPrintAsAnonymous)
@@ -4867,6 +4871,7 @@
void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
+ ASSERT(value->kind() != Code::OPTIMIZED_FUNCTION);
WRITE_FIELD(this, kCodeOffset, value);
CONDITIONAL_WRITE_BARRIER(value->GetHeap(), this, kCodeOffset, value, mode);
}
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index 37ae9b6..6d2811b 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -523,6 +523,7 @@
PrintF(out, " - hash: %d\n", Hash());
PrintF(out, " - name: ");
name()->ShortPrint();
+ PrintF(out, " - private: %d\n", is_private());
PrintF(out, "\n");
}
diff --git a/src/objects.cc b/src/objects.cc
index e5b4763..935e875 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -9742,20 +9742,6 @@
}
-bool JSFunction::IsInlineable() {
- if (IsBuiltin()) return false;
- SharedFunctionInfo* shared_info = shared();
- // Check that the function has a script associated with it.
- if (!shared_info->script()->IsScript()) return false;
- if (shared_info->optimization_disabled()) return false;
- Code* code = shared_info->code();
- if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
- // If we never ran this (unlikely) then lets try to optimize it.
- if (code->kind() != Code::FUNCTION) return true;
- return code->optimizable();
-}
-
-
void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
if (object->IsGlobalObject()) return;
@@ -10024,6 +10010,16 @@
}
+bool SharedFunctionInfo::IsInlineable() {
+ // Check that the function has a script associated with it.
+ if (!script()->IsScript()) return false;
+ if (optimization_disabled()) return false;
+ // If we never ran this (unlikely) then lets try to optimize it.
+ if (code()->kind() != Code::FUNCTION) return true;
+ return code()->optimizable();
+}
+
+
int SharedFunctionInfo::SourceSize() {
return end_position() - start_position();
}
diff --git a/src/objects.h b/src/objects.h
index 0099cb2..2fc1d23 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -352,6 +352,7 @@
V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE) \
\
V(SYMBOL_TYPE) \
+ \
V(MAP_TYPE) \
V(CODE_TYPE) \
V(ODDBALL_TYPE) \
@@ -684,7 +685,7 @@
| kNotInternalizedTag,
// Non-string names
- SYMBOL_TYPE = kNotStringTag, // LAST_NAME_TYPE, FIRST_NONSTRING_TYPE
+ SYMBOL_TYPE = kNotStringTag, // FIRST_NONSTRING_TYPE, LAST_NAME_TYPE
// Objects allocated in their own spaces (never in new space).
MAP_TYPE,
@@ -6781,6 +6782,9 @@
// global object.
DECL_BOOLEAN_ACCESSORS(native)
+ // Indicate that this builtin needs to be inlined in crankshaft.
+ DECL_BOOLEAN_ACCESSORS(inline_builtin)
+
// Indicates that the function was created by the Function function.
// Though it's anonymous, toString should treat it as if it had the name
// "anonymous". We don't set the name itself so that the system does not
@@ -6870,6 +6874,9 @@
set_dont_optimize(reason != kNoReason);
}
+ // Check whether or not this function is inlineable.
+ bool IsInlineable();
+
// Source size of this function.
int SourceSize();
@@ -7020,6 +7027,7 @@
kUsesArguments,
kHasDuplicateParameters,
kNative,
+ kInlineBuiltin,
kBoundFunction,
kIsAnonymous,
kNameShouldPrintAsAnonymous,
@@ -7246,9 +7254,6 @@
// Tells whether or not the function is on the concurrent recompilation queue.
inline bool IsInRecompileQueue();
- // Check whether or not this function is inlineable.
- bool IsInlineable();
-
// [literals_or_bindings]: Fixed array holding either
// the materialized literals or the bindings of a bound function.
//
@@ -8376,6 +8381,11 @@
// [name]: the print name of a symbol, or undefined if none.
DECL_ACCESSORS(name, Object)
+ DECL_ACCESSORS(flags, Smi)
+
+ // [is_private]: whether this is a private symbol.
+ DECL_BOOLEAN_ACCESSORS(is_private)
+
// Casting.
static inline Symbol* cast(Object* obj);
@@ -8385,12 +8395,14 @@
// Layout description.
static const int kNameOffset = Name::kSize;
- static const int kSize = kNameOffset + kPointerSize;
+ static const int kFlagsOffset = kNameOffset + kPointerSize;
+ static const int kSize = kFlagsOffset + kPointerSize;
- typedef FixedBodyDescriptor<kNameOffset, kNameOffset + kPointerSize, kSize>
- BodyDescriptor;
+ typedef FixedBodyDescriptor<kNameOffset, kFlagsOffset, kSize> BodyDescriptor;
private:
+ static const int kPrivateBit = 0;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol);
};
diff --git a/src/parser.cc b/src/parser.cc
index 71c6f67..b168919 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -4114,8 +4114,7 @@
while (!done) {
bool is_strict_reserved = false;
Handle<String> param_name =
- ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
- CHECK_OK);
+ ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
// Store locations for possible future error reports.
if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) {
diff --git a/src/runtime.cc b/src/runtime.cc
index bf4fa4e..9d317ca 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -588,6 +588,19 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreatePrivateSymbol) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ Handle<Object> name(args[0], isolate);
+ RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
+ Symbol* symbol;
+ MaybeObject* maybe = isolate->heap()->AllocatePrivateSymbol();
+ if (!maybe->To(&symbol)) return maybe;
+ if (name->IsString()) symbol->set_name(*name);
+ return symbol;
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolName) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
@@ -596,6 +609,14 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolIsPrivate) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(Symbol, symbol, 0);
+ return isolate->heap()->ToBoolean(symbol->is_private());
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 2);
@@ -4790,6 +4811,19 @@
}
+static Handle<Name> ToName(Isolate* isolate, Handle<Object> key) {
+ if (key->IsName()) {
+ return Handle<Name>::cast(key);
+ } else {
+ bool has_pending_exception = false;
+ Handle<Object> converted =
+ Execution::ToString(isolate, key, &has_pending_exception);
+ if (has_pending_exception) return Handle<Name>();
+ return Handle<Name>::cast(converted);
+ }
+}
+
+
MaybeObject* Runtime::HasObjectProperty(Isolate* isolate,
Handle<JSReceiver> object,
Handle<Object> key) {
@@ -4802,16 +4836,8 @@
}
// Convert the key to a name - possibly by calling back into JavaScript.
- Handle<Name> name;
- if (key->IsName()) {
- name = Handle<Name>::cast(key);
- } else {
- bool has_pending_exception = false;
- Handle<Object> converted =
- Execution::ToString(isolate, key, &has_pending_exception);
- if (has_pending_exception) return Failure::Exception();
- name = Handle<Name>::cast(converted);
- }
+ Handle<Name> name = ToName(isolate, key);
+ RETURN_IF_EMPTY_HANDLE(isolate, name);
return isolate->heap()->ToBoolean(JSReceiver::HasProperty(object, name));
}
@@ -4844,16 +4870,8 @@
}
// Convert the key to a name - possibly by calling back into JavaScript.
- Handle<Name> name;
- if (key->IsName()) {
- name = Handle<Name>::cast(key);
- } else {
- bool has_pending_exception = false;
- Handle<Object> converted =
- Execution::ToString(isolate, key, &has_pending_exception);
- if (has_pending_exception) return Failure::Exception();
- name = Handle<Name>::cast(converted);
- }
+ Handle<Name> name = ToName(isolate, key);
+ RETURN_IF_EMPTY_HANDLE(isolate, name);
// Check if the name is trivially convertible to an index and get
// the element if so.
@@ -5379,6 +5397,20 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInlineBuiltinFlag) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(args.length() == 1);
+
+ Handle<Object> object = args.at<Object>(0);
+
+ if (object->IsJSFunction()) {
+ JSFunction* func = JSFunction::cast(*object);
+ func->shared()->set_inline_builtin(true);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) {
HandleScope scope(isolate);
RUNTIME_ASSERT(args.length() == 5);
@@ -7802,6 +7834,35 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PopulateTrigonometricTable) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sin_table, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, cos_table, 1);
+ CONVERT_SMI_ARG_CHECKED(samples, 2);
+ RUNTIME_ASSERT(sin_table->type() == kExternalDoubleArray);
+ RUNTIME_ASSERT(cos_table->type() == kExternalDoubleArray);
+ double* sin_buffer = reinterpret_cast<double*>(
+ JSArrayBuffer::cast(sin_table->buffer())->backing_store());
+ double* cos_buffer = reinterpret_cast<double*>(
+ JSArrayBuffer::cast(cos_table->buffer())->backing_store());
+
+ static const double pi_half = 3.1415926535897932 / 2;
+ double interval = pi_half / samples;
+ for (int i = 0; i < samples + 1; i++) {
+ double sample = sin(i * interval);
+ sin_buffer[i] = sample;
+ cos_buffer[samples - i] = sample * interval;
+ }
+
+ // Fill this to catch out of bound accesses when calculating Math.sin(pi/2).
+ sin_buffer[samples + 1] = sin(pi_half + interval);
+ cos_buffer[samples + 1] = cos(pi_half + interval) * interval;
+
+ return isolate->heap()->undefined_value();
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 2);
@@ -14691,18 +14752,25 @@
RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConstructor) {
HandleScope scope(isolate);
// If we get 2 arguments then they are the stub parameters (constructor, type
- // info). If we get 3, then the first one is a pointer to the arguments
- // passed by the caller.
+ // info). If we get 4, then the first one is a pointer to the arguments
+ // passed by the caller, and the last one is the length of the arguments
+ // passed to the caller (redundant, but useful to check on the deoptimizer
+ // with an assert).
Arguments empty_args(0, NULL);
bool no_caller_args = args.length() == 2;
- ASSERT(no_caller_args || args.length() == 3);
+ ASSERT(no_caller_args || args.length() == 4);
int parameters_start = no_caller_args ? 0 : 1;
Arguments* caller_args = no_caller_args
? &empty_args
: reinterpret_cast<Arguments*>(args[0]);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
CONVERT_ARG_HANDLE_CHECKED(Object, type_info, parameters_start + 1);
-
+#ifdef DEBUG
+ if (!no_caller_args) {
+ CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 2);
+ ASSERT(arg_count == caller_args->length());
+ }
+#endif
return ArrayConstructorCommon(isolate,
constructor,
type_info,
@@ -14714,13 +14782,18 @@
HandleScope scope(isolate);
Arguments empty_args(0, NULL);
bool no_caller_args = args.length() == 1;
- ASSERT(no_caller_args || args.length() == 2);
+ ASSERT(no_caller_args || args.length() == 3);
int parameters_start = no_caller_args ? 0 : 1;
Arguments* caller_args = no_caller_args
? &empty_args
: reinterpret_cast<Arguments*>(args[0]);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
-
+#ifdef DEBUG
+ if (!no_caller_args) {
+ CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 1);
+ ASSERT(arg_count == caller_args->length());
+ }
+#endif
return ArrayConstructorCommon(isolate,
constructor,
Handle<Object>::null(),
diff --git a/src/runtime.h b/src/runtime.h
index c316d40..734875f 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -106,6 +106,7 @@
F(AllocateInOldPointerSpace, 1, 1) \
F(AllocateInOldDataSpace, 1, 1) \
F(SetNativeFlag, 1, 1) \
+ F(SetInlineBuiltinFlag, 1, 1) \
F(StoreArrayLiteralElement, 5, 1) \
F(DebugCallbackSupportsStepping, 1, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \
@@ -189,6 +190,7 @@
F(Math_sin, 1, 1) \
F(Math_sqrt, 1, 1) \
F(Math_tan, 1, 1) \
+ F(PopulateTrigonometricTable, 3, 1) \
\
/* Regular expressions */ \
F(RegExpCompile, 3, 1) \
@@ -315,7 +317,9 @@
\
/* Harmony symbols */ \
F(CreateSymbol, 1, 1) \
+ F(CreatePrivateSymbol, 1, 1) \
F(SymbolName, 1, 1) \
+ F(SymbolIsPrivate, 1, 1) \
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \
@@ -625,11 +629,9 @@
F(IsSpecObject, 1, 1) \
F(IsStringWrapperSafeForDefaultValueOf, 1, 1) \
F(MathPow, 2, 1) \
- F(MathSin, 1, 1) \
- F(MathCos, 1, 1) \
- F(MathTan, 1, 1) \
F(MathSqrt, 1, 1) \
F(MathLog, 1, 1) \
+ F(IsMinusZero, 1, 1) \
F(IsRegExpEquivalent, 2, 1) \
F(HasCachedArrayIndex, 1, 1) \
F(GetCachedArrayIndex, 1, 1) \
diff --git a/src/runtime.js b/src/runtime.js
index 875b6b0..19b858b 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -606,7 +606,9 @@
if (IS_NUMBER(x)) {
if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
// x is +0 and y is -0 or vice versa.
- if (x === 0 && y === 0 && (1 / x) != (1 / y)) return false;
+ if (x === 0 && y === 0 && %_IsMinusZero(x) != %_IsMinusZero(y)) {
+ return false;
+ }
}
return x === y;
}
diff --git a/src/spaces.cc b/src/spaces.cc
index f35db69..1e0d9bc 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -2504,29 +2504,6 @@
// -----------------------------------------------------------------------------
// OldSpace implementation
-bool NewSpace::ReserveSpace(int bytes) {
- // We can't reliably unpack a partial snapshot that needs more new space
- // space than the minimum NewSpace size. The limit can be set lower than
- // the end of new space either because there is more space on the next page
- // or because we have lowered the limit in order to get periodic incremental
- // marking. The most reliable way to ensure that there is linear space is
- // to do the allocation, then rewind the limit.
- ASSERT(bytes <= InitialCapacity());
- MaybeObject* maybe = AllocateRaw(bytes);
- Object* object = NULL;
- if (!maybe->ToObject(&object)) return false;
- HeapObject* allocation = HeapObject::cast(object);
- Address top = allocation_info_.top();
- if ((top - bytes) == allocation->address()) {
- allocation_info_.set_top(allocation->address());
- return true;
- }
- // There may be a borderline case here where the allocation succeeded, but
- // the limit and top have moved on to a new page. In that case we try again.
- return ReserveSpace(bytes);
-}
-
-
void PagedSpace::PrepareForMarkCompact() {
// We don't have a linear allocation area while sweeping. It will be restored
// on the first allocation after the sweep.
@@ -2561,28 +2538,6 @@
}
-bool PagedSpace::ReserveSpace(int size_in_bytes) {
- ASSERT(size_in_bytes <= AreaSize());
- ASSERT(size_in_bytes == RoundSizeDownToObjectAlignment(size_in_bytes));
- Address current_top = allocation_info_.top();
- Address new_top = current_top + size_in_bytes;
- if (new_top <= allocation_info_.limit()) return true;
-
- HeapObject* new_area = free_list_.Allocate(size_in_bytes);
- if (new_area == NULL) new_area = SlowAllocateRaw(size_in_bytes);
- if (new_area == NULL) return false;
-
- int old_linear_size = static_cast<int>(limit() - top());
- // Mark the old linear allocation area with a free space so it can be
- // skipped when scanning the heap. This also puts it back in the free list
- // if it is big enough.
- Free(top(), old_linear_size);
-
- SetTop(new_area->address(), new_area->address() + size_in_bytes);
- return true;
-}
-
-
intptr_t PagedSpace::SizeOfObjects() {
ASSERT(!heap()->IsSweepingComplete() || (unswept_free_bytes_ == 0));
return Size() - unswept_free_bytes_ - (limit() - top());
@@ -2598,15 +2553,6 @@
}
-// You have to call this last, since the implementation from PagedSpace
-// doesn't know that memory was 'promised' to large object space.
-bool LargeObjectSpace::ReserveSpace(int bytes) {
- return heap()->OldGenerationCapacityAvailable() >= bytes &&
- (!heap()->incremental_marking()->IsStopped() ||
- heap()->OldGenerationSpaceAvailable() >= bytes);
-}
-
-
bool PagedSpace::AdvanceSweeper(intptr_t bytes_to_sweep) {
if (IsLazySweepingComplete()) return true;
@@ -2861,23 +2807,6 @@
}
#endif
-// -----------------------------------------------------------------------------
-// FixedSpace implementation
-
-void FixedSpace::PrepareForMarkCompact() {
- // Call prepare of the super class.
- PagedSpace::PrepareForMarkCompact();
-
- // During a non-compacting collection, everything below the linear
- // allocation pointer except wasted top-of-page blocks is considered
- // allocated and we will rediscover available bytes during the
- // collection.
- accounting_stats_.AllocateBytes(free_list_.available());
-
- // Clear the free list before a full GC---it will be rebuilt afterward.
- free_list_.Reset();
-}
-
// -----------------------------------------------------------------------------
// MapSpace implementation
diff --git a/src/spaces.h b/src/spaces.h
index 06f444d..83793e3 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -1683,10 +1683,10 @@
// During boot the free_space_map is created, and afterwards we may need
// to write it into the free list nodes that were already created.
- virtual void RepairFreeListsAfterBoot();
+ void RepairFreeListsAfterBoot();
// Prepares for a mark-compact GC.
- virtual void PrepareForMarkCompact();
+ void PrepareForMarkCompact();
// Current capacity without growing (Size() + Available()).
intptr_t Capacity() { return accounting_stats_.Capacity(); }
@@ -1768,8 +1768,6 @@
// failure object if not.
MUST_USE_RESULT inline MaybeObject* AllocateRaw(int size_in_bytes);
- virtual bool ReserveSpace(int bytes);
-
// Give a block of memory to the space's free list. It might be added to
// the free list or accounted as waste.
// If add_to_freelist is false then just accounting stats are updated and
@@ -1914,12 +1912,6 @@
// Normal allocation information.
AllocationInfo allocation_info_;
- // Bytes of each page that cannot be allocated. Possibly non-zero
- // for pages in spaces with only fixed-size objects. Always zero
- // for pages in spaces with variable sized objects (those pages are
- // padded with free-list nodes).
- int page_extra_;
-
bool was_swept_conservatively_;
// The first page to be swept when the lazy sweeper advances. Is set
@@ -2167,11 +2159,6 @@
return 0;
}
- virtual bool ReserveSpace(int bytes) {
- UNREACHABLE();
- return false;
- }
-
bool is_committed() { return committed_; }
bool Commit();
bool Uncommit();
@@ -2535,8 +2522,6 @@
// if successful.
bool AddFreshPage();
- virtual bool ReserveSpace(int bytes);
-
#ifdef VERIFY_HEAP
// Verify the active semispace.
virtual void Verify();
@@ -2632,12 +2617,6 @@
AllocationSpace id,
Executability executable)
: PagedSpace(heap, max_capacity, id, executable) {
- page_extra_ = 0;
- }
-
- // The limit of allocation for a page in this space.
- virtual Address PageAllocationLimit(Page* page) {
- return page->area_end();
}
public:
@@ -2654,43 +2633,13 @@
// -----------------------------------------------------------------------------
-// Old space for objects of a fixed size
-
-class FixedSpace : public PagedSpace {
- public:
- FixedSpace(Heap* heap,
- intptr_t max_capacity,
- AllocationSpace id,
- int object_size_in_bytes)
- : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE),
- object_size_in_bytes_(object_size_in_bytes) {
- page_extra_ = Page::kNonCodeObjectAreaSize % object_size_in_bytes;
- }
-
- // The limit of allocation for a page in this space.
- virtual Address PageAllocationLimit(Page* page) {
- return page->area_end() - page_extra_;
- }
-
- int object_size_in_bytes() { return object_size_in_bytes_; }
-
- // Prepares for a mark-compact GC.
- virtual void PrepareForMarkCompact();
-
- private:
- // The size of objects in this space.
- int object_size_in_bytes_;
-};
-
-
-// -----------------------------------------------------------------------------
// Old space for all map objects
-class MapSpace : public FixedSpace {
+class MapSpace : public PagedSpace {
public:
// Creates a map space object with a maximum capacity.
MapSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id)
- : FixedSpace(heap, max_capacity, id, Map::kSize),
+ : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE),
max_map_space_pages_(kMaxMapPageIndex - 1) {
}
@@ -2727,12 +2676,12 @@
// -----------------------------------------------------------------------------
// Old space for simple property cell objects
-class CellSpace : public FixedSpace {
+class CellSpace : public PagedSpace {
public:
// Creates a property cell space object with a maximum capacity.
CellSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id)
- : FixedSpace(heap, max_capacity, id, Cell::kSize)
- {}
+ : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE) {
+ }
virtual int RoundSizeDownToObjectAlignment(int size) {
if (IsPowerOf2(Cell::kSize)) {
@@ -2753,13 +2702,13 @@
// -----------------------------------------------------------------------------
// Old space for all global object property cell objects
-class PropertyCellSpace : public FixedSpace {
+class PropertyCellSpace : public PagedSpace {
public:
// Creates a property cell space object with a maximum capacity.
PropertyCellSpace(Heap* heap, intptr_t max_capacity,
AllocationSpace id)
- : FixedSpace(heap, max_capacity, id, PropertyCell::kSize)
- {}
+ : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE) {
+ }
virtual int RoundSizeDownToObjectAlignment(int size) {
if (IsPowerOf2(PropertyCell::kSize)) {
@@ -2849,11 +2798,6 @@
// Checks whether the space is empty.
bool IsEmpty() { return first_page_ == NULL; }
- // See the comments for ReserveSpace in the Space class. This has to be
- // called after ReserveSpace has been called on the paged spaces, since they
- // may use some memory, leaving less for large objects.
- virtual bool ReserveSpace(int bytes);
-
LargePage* first_page() { return first_page_; }
#ifdef VERIFY_HEAP
diff --git a/src/version.cc b/src/version.cc
index 164d08d..eef1029 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 23
-#define BUILD_NUMBER 2
+#define BUILD_NUMBER 3
#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/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 7a7c0a7..9d95fc4 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -176,14 +176,21 @@
// rax -- number of arguments
// rdi -- function
// rbx -- type info cell with elements kind
- static Register registers[] = { rdi, rbx };
- descriptor->register_param_count_ = 2;
- if (constant_stack_parameter_count != 0) {
+ static Register registers_variable_args[] = { rdi, rbx, rax };
+ static Register registers_no_args[] = { rdi, rbx };
+
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = rax;
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
@@ -197,15 +204,21 @@
// register state
// rax -- number of arguments
// rdi -- constructor function
- static Register registers[] = { rdi };
- descriptor->register_param_count_ = 1;
+ static Register registers_variable_args[] = { rdi, rax };
+ static Register registers_no_args[] = { rdi };
- if (constant_stack_parameter_count != 0) {
+ if (constant_stack_parameter_count == 0) {
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers_no_args;
+ } else {
// stack param count needs (constructor pointer, and single argument)
+ descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
descriptor->stack_parameter_count_ = rax;
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers_variable_args;
}
+
descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
- descriptor->register_params_ = registers;
descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
@@ -302,6 +315,17 @@
}
+void NewStringAddStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rdx, rax };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kStringAdd)->entry;
+}
+
+
#define __ ACCESS_MASM(masm)
diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc
index bf11e08..4528077 100644
--- a/src/x64/deoptimizer-x64.cc
+++ b/src/x64/deoptimizer-x64.cc
@@ -106,7 +106,7 @@
FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
intptr_t handler =
reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
- int params = descriptor->environment_length();
+ int params = descriptor->GetHandlerParameterCount();
output_frame->SetRegister(rax.code(), params);
output_frame->SetRegister(rbx.code(), handler);
}
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index cc0d3d9..d5fa5e5 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -3026,6 +3026,33 @@
}
+void FullCodeGenerator::EmitIsMinusZero(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Handle<Map> map = masm()->isolate()->factory()->heap_number_map();
+ __ CheckMap(rax, map, if_false, DO_SMI_CHECK);
+ __ cmpl(FieldOperand(rax, HeapNumber::kExponentOffset),
+ Immediate(0x80000000));
+ __ j(not_equal, if_false);
+ __ cmpl(FieldOperand(rax, HeapNumber::kMantissaOffset),
+ Immediate(0x00000000));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@@ -3628,11 +3655,20 @@
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
- VisitForStackValue(args->at(0));
- VisitForStackValue(args->at(1));
+ if (FLAG_new_string_add) {
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
- StringAddStub stub(STRING_ADD_CHECK_BOTH);
- __ CallStub(&stub);
+ __ pop(rdx);
+ NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
+ __ CallStub(&stub);
+ } else {
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ }
context()->Plug(rax);
}
@@ -3650,42 +3686,6 @@
}
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(rax);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(rax);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
- // Load the argument on the stack and call the stub.
- TranscendentalCacheStub stub(TranscendentalCache::TAN,
- TranscendentalCacheStub::TAGGED);
- ZoneList<Expression*>* args = expr->arguments();
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallStub(&stub);
- context()->Plug(rax);
-}
-
-
void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG,
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 20b1897..dc6ddf9 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -2246,6 +2246,33 @@
}
+void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) {
+ Representation rep = instr->hydrogen()->value()->representation();
+ ASSERT(!rep.IsInteger32());
+
+ if (rep.IsDouble()) {
+ XMMRegister value = ToDoubleRegister(instr->value());
+ XMMRegister xmm_scratch = double_scratch0();
+ __ xorps(xmm_scratch, xmm_scratch);
+ __ ucomisd(xmm_scratch, value);
+ EmitFalseBranch(instr, not_equal);
+ __ movmskpd(kScratchRegister, value);
+ __ testl(kScratchRegister, Immediate(1));
+ EmitBranch(instr, not_zero);
+ } else {
+ Register value = ToRegister(instr->value());
+ Handle<Map> map = masm()->isolate()->factory()->heap_number_map();
+ __ CheckMap(value, map, instr->FalseLabel(chunk()), DO_SMI_CHECK);
+ __ cmpl(FieldOperand(value, HeapNumber::kExponentOffset),
+ Immediate(0x80000000));
+ EmitFalseBranch(instr, not_equal);
+ __ cmpl(FieldOperand(value, HeapNumber::kMantissaOffset),
+ Immediate(0x00000000));
+ EmitBranch(instr, equal);
+ }
+}
+
+
Condition LCodeGen::EmitIsObject(Register input,
Label* is_not_object,
Label* is_object) {
@@ -4077,6 +4104,10 @@
if (operand_value->IsRegister()) {
Register value = ToRegister(operand_value);
__ Store(FieldOperand(write_register, offset), value, representation);
+ } else if (representation.IsInteger32()) {
+ int32_t value = ToInteger32(operand_value);
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ __ movl(FieldOperand(write_register, offset), Immediate(value));
} else {
Handle<Object> handle_value = ToHandle(operand_value);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
@@ -4400,10 +4431,18 @@
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(rsi));
- EmitPushTaggedOperand(instr->left());
- EmitPushTaggedOperand(instr->right());
- StringAddStub stub(instr->hydrogen()->flags());
- CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ if (FLAG_new_string_add) {
+ ASSERT(ToRegister(instr->left()).is(rdx));
+ ASSERT(ToRegister(instr->right()).is(rax));
+ NewStringAddStub stub(instr->hydrogen()->flags(),
+ isolate()->heap()->GetPretenureMode());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ }
}
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index 060cbe8..ac238d0 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -270,6 +270,8 @@
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
void EmitGoto(int block);
+
+ // EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition cc);
template<class InstrType>
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 281589e..a121646 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1672,6 +1672,15 @@
}
+LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch(
+ HCompareMinusZeroAndBranch* instr) {
+ LInstruction* goto_instr = CheckElideControlInstruction(instr);
+ if (goto_instr != NULL) return goto_instr;
+ LOperand* value = UseRegister(instr->value());
+ return new(zone()) LCompareMinusZeroAndBranch(value);
+}
+
+
LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new(zone()) LIsObjectAndBranch(UseRegisterAtStart(instr->value()));
@@ -2339,8 +2348,12 @@
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), rsi);
- LOperand* left = UseOrConstantAtStart(instr->left());
- LOperand* right = UseOrConstantAtStart(instr->right());
+ LOperand* left = FLAG_new_string_add
+ ? UseFixed(instr->left(), rdx)
+ : UseOrConstantAtStart(instr->left());
+ LOperand* right = FLAG_new_string_add
+ ? UseFixed(instr->right(), rax)
+ : UseOrConstantAtStart(instr->right());
return MarkAsCall(
DefineFixed(new(zone()) LStringAdd(context, left, right), rax), instr);
}
@@ -2409,7 +2422,7 @@
CodeStubInterfaceDescriptor* descriptor =
info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
int index = static_cast<int>(instr->index());
- Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ Register reg = descriptor->GetParameterRegister(index);
return DefineFixed(result, reg);
}
}
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index 17e74aa..6e236c7 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -72,6 +72,7 @@
V(ClampIToUint8) \
V(ClampTToUint8) \
V(ClassOfTestAndBranch) \
+ V(CompareMinusZeroAndBranch) \
V(CompareNumericAndBranch) \
V(CmpObjectEqAndBranch) \
V(CmpHoleAndBranch) \
@@ -874,6 +875,21 @@
};
+class LCompareMinusZeroAndBranch V8_FINAL : public LControlInstruction<1, 0> {
+ public:
+ explicit LCompareMinusZeroAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch,
+ "cmp-minus-zero-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMinusZeroAndBranch)
+};
+
+
+
class LIsObjectAndBranch V8_FINAL : public LControlInstruction<1, 0> {
public:
explicit LIsObjectAndBranch(LOperand* value) {
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index df984fc..2f7166e 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -4415,18 +4415,27 @@
cmpl(length, Immediate(min_length));
Assert(greater_equal, kInvalidMinLength);
}
- Label loop, done, short_string, short_loop;
+ Label short_loop, len8, len16, len24, done, short_string;
- const int kLongStringLimit = 20;
+ const int kLongStringLimit = 4 * kPointerSize;
if (min_length <= kLongStringLimit) {
- cmpl(length, Immediate(kLongStringLimit));
- j(less_equal, &short_string);
+ cmpl(length, Immediate(kPointerSize));
+ j(below, &short_string, Label::kNear);
}
ASSERT(source.is(rsi));
ASSERT(destination.is(rdi));
ASSERT(length.is(rcx));
+ if (min_length <= kLongStringLimit) {
+ cmpl(length, Immediate(2 * kPointerSize));
+ j(below_equal, &len8, Label::kNear);
+ cmpl(length, Immediate(3 * kPointerSize));
+ j(below_equal, &len16, Label::kNear);
+ cmpl(length, Immediate(4 * kPointerSize));
+ j(below_equal, &len24, Label::kNear);
+ }
+
// Because source is 8-byte aligned in our uses of this function,
// we keep source aligned for the rep movs operation by copying the odd bytes
// at the end of the ranges.
@@ -4440,25 +4449,38 @@
addq(destination, scratch);
if (min_length <= kLongStringLimit) {
- jmp(&done);
+ jmp(&done, Label::kNear);
+ bind(&len24);
+ movq(scratch, Operand(source, 2 * kPointerSize));
+ movq(Operand(destination, 2 * kPointerSize), scratch);
+ bind(&len16);
+ movq(scratch, Operand(source, kPointerSize));
+ movq(Operand(destination, kPointerSize), scratch);
+ bind(&len8);
+ movq(scratch, Operand(source, 0));
+ movq(Operand(destination, 0), scratch);
+ // Move remaining bytes of length.
+ movq(scratch, Operand(source, length, times_1, -kPointerSize));
+ movq(Operand(destination, length, times_1, -kPointerSize), scratch);
+ addq(destination, length);
+ jmp(&done, Label::kNear);
bind(&short_string);
if (min_length == 0) {
testl(length, length);
- j(zero, &done);
+ j(zero, &done, Label::kNear);
}
- lea(scratch, Operand(destination, length, times_1, 0));
bind(&short_loop);
- movb(length, Operand(source, 0));
- movb(Operand(destination, 0), length);
+ movb(scratch, Operand(source, 0));
+ movb(Operand(destination, 0), scratch);
incq(source);
incq(destination);
- cmpq(destination, scratch);
- j(not_equal, &short_loop);
-
- bind(&done);
+ decl(length);
+ j(not_zero, &short_loop);
}
+
+ bind(&done);
}
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 59bf826..58550d8 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -63,6 +63,13 @@
}], # ALWAYS
##############################################################################
+['system == windows', {
+
+ # BUG(2999).
+ 'test-cpu-profiler/CollectCpuProfile': [PASS, FAIL],
+}], # 'system == windows'
+
+##############################################################################
['arch == arm', {
# We cannot assume that we can throw OutOfMemory exceptions in all situations.
@@ -80,6 +87,7 @@
'test-serialize/DeserializeAndRunScript2': [SKIP],
'test-serialize/DeserializeFromSecondSerialization': [SKIP],
}], # 'arch == arm'
+
##############################################################################
['arch == mipsel', {
@@ -132,5 +140,8 @@
# Fails since 16322 (new test).
'test-code-stubs-arm/ConvertDToI': [SKIP],
+
+ # BUG(2998).
+ 'test-macro-assembler-arm/LoadAndStoreWithRepresentation': [SKIP],
}], # 'arch == nacl_ia32 or arch == nacl_x64'
]
diff --git a/test/cctest/test-accessors.cc b/test/cctest/test-accessors.cc
index df4937e..4920ac2 100644
--- a/test/cctest/test-accessors.cc
+++ b/test/cctest/test-accessors.cc
@@ -122,18 +122,15 @@
baz = 10;
v8::HandleScope scope(CcTest::isolate());
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
- templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&foo));
- templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&bar));
- templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&baz));
+ templ->InstanceTemplate()->SetAccessor(
+ v8_str("foo"), GetIntValue, SetIntValue,
+ v8::External::New(CcTest::isolate(), &foo));
+ templ->InstanceTemplate()->SetAccessor(
+ v8_str("bar"), GetIntValue, SetIntValue,
+ v8::External::New(CcTest::isolate(), &bar));
+ templ->InstanceTemplate()->SetAccessor(
+ v8_str("baz"), GetIntValue, SetIntValue,
+ v8::External::New(CcTest::isolate(), &baz));
LocalContext env(0, templ->InstanceTemplate());
v8_compile("foo = (++bar) + baz")->Run();
CHECK_EQ(bar, -3);
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index f33d02b..15c7263 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -1306,7 +1306,8 @@
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
- v8::Handle<v8::Value> data = v8::External::New(expected_ptr);
+ v8::Handle<v8::Value> data =
+ v8::External::New(env->GetIsolate(), expected_ptr);
v8::Handle<v8::Object> obj = v8::Object::New();
obj->Set(v8_str("func"),
@@ -2743,6 +2744,70 @@
CHECK(!obj->Has(sym2));
CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+ // Symbol properties are inherited.
+ v8::Local<v8::Object> child = v8::Object::New();
+ child->SetPrototype(obj);
+ CHECK(child->Has(sym1));
+ CHECK_EQ(2002, child->Get(sym1)->Int32Value());
+ CHECK_EQ(0, child->GetOwnPropertyNames()->Length());
+}
+
+
+THREADED_TEST(PrivateProperties) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+
+ v8::Local<v8::Object> obj = v8::Object::New();
+ v8::Local<v8::Private> priv1 = v8::Private::New(isolate);
+ v8::Local<v8::Private> priv2 = v8::Private::New(isolate, "my-private");
+
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+ CHECK(priv2->Name()->Equals(v8::String::New("my-private")));
+
+ // Make sure delete of a non-existent private symbol property works.
+ CHECK(obj->DeletePrivate(priv1));
+ CHECK(!obj->HasPrivate(priv1));
+
+ CHECK(obj->SetPrivate(priv1, v8::Integer::New(1503)));
+ CHECK(obj->HasPrivate(priv1));
+ CHECK_EQ(1503, obj->GetPrivate(priv1)->Int32Value());
+ CHECK(obj->SetPrivate(priv1, v8::Integer::New(2002)));
+ CHECK(obj->HasPrivate(priv1));
+ CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+
+ CHECK_EQ(0, obj->GetOwnPropertyNames()->Length());
+ int num_props = obj->GetPropertyNames()->Length();
+ CHECK(obj->Set(v8::String::New("bla"), v8::Integer::New(20)));
+ CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+ CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length());
+
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+ // Add another property and delete it afterwards to force the object in
+ // slow case.
+ CHECK(obj->SetPrivate(priv2, v8::Integer::New(2008)));
+ CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+ CHECK_EQ(2008, obj->GetPrivate(priv2)->Int32Value());
+ CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+ CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+ CHECK(obj->HasPrivate(priv1));
+ CHECK(obj->HasPrivate(priv2));
+ CHECK(obj->DeletePrivate(priv2));
+ CHECK(obj->HasPrivate(priv1));
+ CHECK(!obj->HasPrivate(priv2));
+ CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+ CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+ // Private properties are inherited (for the time being).
+ v8::Local<v8::Object> child = v8::Object::New();
+ child->SetPrototype(obj);
+ CHECK(child->HasPrivate(priv1));
+ CHECK_EQ(2002, child->GetPrivate(priv1)->Int32Value());
+ CHECK_EQ(0, child->GetOwnPropertyNames()->Length());
}
@@ -3119,7 +3184,7 @@
THREADED_TEST(External) {
v8::HandleScope scope(CcTest::isolate());
int x = 3;
- Local<v8::External> ext = v8::External::New(&x);
+ Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x);
LocalContext env;
env->Global()->Set(v8_str("ext"), ext);
Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
@@ -3131,10 +3196,10 @@
// Make sure unaligned pointers are wrapped properly.
char* data = i::StrDup("0123456789");
- Local<v8::Value> zero = v8::External::New(&data[0]);
- Local<v8::Value> one = v8::External::New(&data[1]);
- Local<v8::Value> two = v8::External::New(&data[2]);
- Local<v8::Value> three = v8::External::New(&data[3]);
+ Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]);
+ Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]);
+ Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]);
+ Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]);
char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value());
CHECK_EQ('0', *char_ptr);
@@ -6874,7 +6939,7 @@
Whammy* whammy = new Whammy(CcTest::isolate());
templ->SetNamedPropertyHandler(WhammyPropertyGetter,
0, 0, 0, 0,
- v8::External::New(whammy));
+ v8::External::New(CcTest::isolate(), whammy));
const char* extension_list[] = { "v8/gc" };
v8::ExtensionConfiguration extensions(1, extension_list);
v8::Handle<Context> context =
@@ -11569,9 +11634,9 @@
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -11598,9 +11663,9 @@
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -11630,9 +11695,9 @@
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -11668,9 +11733,9 @@
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -11706,9 +11771,9 @@
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -11747,9 +11812,9 @@
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
- NULL, NULL, NULL, NULL,
- v8::External::New(&interceptor_call_count));
+ templ->SetNamedPropertyHandler(
+ InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
+ v8::External::New(CcTest::isolate(), &interceptor_call_count));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 9ef307c..33444ea 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -718,7 +718,8 @@
func_template->InstanceTemplate();
TestApiCallbacks accessors(100);
- v8::Local<v8::External> data = v8::External::New(&accessors);
+ v8::Local<v8::External> data =
+ v8::External::New(env->GetIsolate(), &accessors);
instance_template->SetAccessor(
v8::String::New("foo"), &TestApiCallbacks::Getter,
&TestApiCallbacks::Setter, data);
@@ -758,7 +759,8 @@
func_template->InstanceTemplate();
TestApiCallbacks accessors(1);
- v8::Local<v8::External> data = v8::External::New(&accessors);
+ v8::Local<v8::External> data =
+ v8::External::New(env->GetIsolate(), &accessors);
instance_template->SetAccessor(
v8::String::New("foo"), &TestApiCallbacks::Getter,
&TestApiCallbacks::Setter, data);
@@ -807,7 +809,8 @@
v8::HandleScope scope(env->GetIsolate());
TestApiCallbacks callbacks(100);
- v8::Local<v8::External> data = v8::External::New(&callbacks);
+ v8::Local<v8::External> data =
+ v8::External::New(env->GetIsolate(), &callbacks);
v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
func_template->SetClassName(v8::String::New("Test_InstanceCostructor"));
@@ -844,7 +847,8 @@
v8::HandleScope scope(env->GetIsolate());
TestApiCallbacks callbacks(1);
- v8::Local<v8::External> data = v8::External::New(&callbacks);
+ v8::Local<v8::External> data =
+ v8::External::New(env->GetIsolate(), &callbacks);
v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
func_template->SetClassName(v8::String::New("Test_InstanceCostructor"));
diff --git a/test/cctest/test-decls.cc b/test/cctest/test-decls.cc
index de27286..1d09f9c 100644
--- a/test/cctest/test-decls.cc
+++ b/test/cctest/test-decls.cc
@@ -119,7 +119,7 @@
Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate);
Local<FunctionTemplate> function = FunctionTemplate::New();
- Local<Value> data = External::New(this);
+ Local<Value> data = External::New(CcTest::isolate(), this);
GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
&HandleSet,
&HandleQuery,
diff --git a/test/cctest/test-macro-assembler-x64.cc b/test/cctest/test-macro-assembler-x64.cc
index 3d28fc0..a85d8e0 100644
--- a/test/cctest/test-macro-assembler-x64.cc
+++ b/test/cctest/test-macro-assembler-x64.cc
@@ -2327,8 +2327,8 @@
TEST(OperandOffset) {
i::V8::Initialize(NULL);
- int data[256];
- for (int i = 0; i < 256; i++) { data[i] = i * 0x01010101; }
+ uint32_t data[256];
+ for (uint32_t i = 0; i < 256; i++) { data[i] = i * 0x01010101; }
// Allocate an executable page of memory.
size_t actual_size;
diff --git a/test/mjsunit/compiler/minus-zero.js b/test/mjsunit/compiler/minus-zero.js
index 6efceb5..c161257 100644
--- a/test/mjsunit/compiler/minus-zero.js
+++ b/test/mjsunit/compiler/minus-zero.js
@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Flags: --allow-natives-syntax
+// Flags: --allow-natives-syntax --no-fold-constants
function add(x, y) {
return x + y;
@@ -35,3 +35,59 @@
assertEquals(0, add(0, 0));
%OptimizeFunctionOnNextCall(add);
assertEquals(-0, add(-0, -0));
+
+
+function test(x, y) {
+ assertTrue(%_IsMinusZero(-0));
+ assertTrue(%_IsMinusZero(1/(-Infinity)));
+ assertTrue(%_IsMinusZero(x));
+
+ assertFalse(%_IsMinusZero(0));
+ assertFalse(%_IsMinusZero(1/Infinity));
+ assertFalse(%_IsMinusZero(0.1));
+ assertFalse(%_IsMinusZero(-0.2));
+ assertFalse(%_IsMinusZero({}));
+ assertFalse(%_IsMinusZero(""));
+ assertFalse(%_IsMinusZero("-0"));
+ assertFalse(%_IsMinusZero(function() {}));
+ assertFalse(%_IsMinusZero(y));
+}
+
+test(-0, 1.2);
+test(-0, 1.2);
+%OptimizeFunctionOnNextCall(test);
+test(-0, 1.2);
+assertOptimized(test);
+
+
+function testsin() {
+ assertTrue(%_IsMinusZero(Math.sin(-0)));
+}
+
+testsin();
+testsin();
+%OptimizeFunctionOnNextCall(testsin);
+testsin();
+
+
+function testfloor() {
+ assertTrue(%_IsMinusZero(Math.floor(-0)));
+ assertFalse(%_IsMinusZero(Math.floor(2)));
+}
+
+testfloor();
+testfloor();
+%OptimizeFunctionOnNextCall(testfloor);
+testfloor();
+
+
+var double_one = Math.cos(0);
+
+function add(a, b) {
+ return a + b;
+}
+
+assertEquals(1, 1/add(double_one, 0));
+assertEquals(1, 1/add(0, double_one));
+%OptimizeFunctionOnNextCall(add);
+assertEquals(1/(-0 + -0), 1/add(-0, -0));
diff --git a/test/mjsunit/constant-folding-2.js b/test/mjsunit/constant-folding-2.js
index 9e6b2c6..f429c6c 100644
--- a/test/mjsunit/constant-folding-2.js
+++ b/test/mjsunit/constant-folding-2.js
@@ -128,30 +128,6 @@
assertEquals("Infinity", String(1 / Math.max(0.0, -0.0)));
});
-test(function mathSin() {
- assertEquals(0.0, Math.sin(0.0));
- assertTrue(0.8 < Math.sin(1) && Math.sin(1) < 0.9);
- assertEquals("NaN", String(Math.sin(Infinity)));
- assertEquals("NaN", String(Math.sin(-Infinity)));
- assertEquals("NaN", String(Math.sin(NaN)));
-});
-
-test(function mathCos() {
- assertEquals(1.0, Math.cos(0.0));
- assertTrue(0.5 < Math.cos(1) && Math.cos(1) < 0.6);
- assertEquals("NaN", String(Math.cos(Infinity)));
- assertEquals("NaN", String(Math.cos(-Infinity)));
- assertEquals("NaN", String(Math.cos(NaN)));
-});
-
-test(function mathTan() {
- assertEquals(0.0, Math.tan(0.0));
- assertTrue(1.5 < Math.tan(1) && Math.tan(1) < 1.6);
- assertEquals("NaN", String(Math.tan(Infinity)));
- assertEquals("NaN", String(Math.tan(-Infinity)));
- assertEquals("NaN", String(Math.tan(NaN)));
-});
-
test(function mathExp() {
assertEquals(1.0, Math.exp(0.0));
assertTrue(2.7 < Math.exp(1) && Math.exp(1) < 2.8);
diff --git a/test/mjsunit/harmony/private.js b/test/mjsunit/harmony/private.js
new file mode 100644
index 0000000..884e31b
--- /dev/null
+++ b/test/mjsunit/harmony/private.js
@@ -0,0 +1,324 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-symbols --harmony-collections
+// Flags: --expose-gc --allow-natives-syntax
+
+var symbols = []
+
+// Test different forms of constructor calls, all equivalent.
+function TestNew() {
+ for (var i = 0; i < 2; ++i) {
+ for (var j = 0; j < 5; ++j) {
+ symbols.push(%CreatePrivateSymbol("66"))
+ symbols.push(Object(%CreatePrivateSymbol("66")).valueOf())
+ }
+ gc() // Promote existing symbols and then allocate some more.
+ }
+}
+TestNew()
+
+
+function TestType() {
+ for (var i in symbols) {
+ assertEquals("symbol", typeof symbols[i])
+ assertTrue(typeof symbols[i] === "symbol")
+ assertTrue(%SymbolIsPrivate(symbols[i]))
+ assertEquals(null, %_ClassOf(symbols[i]))
+ assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
+ assertEquals("Symbol", %_ClassOf(Object(symbols[i])))
+ }
+}
+TestType()
+
+
+function TestPrototype() {
+ for (var i in symbols) {
+ assertSame(Symbol.prototype, symbols[i].__proto__)
+ }
+}
+TestPrototype()
+
+
+function TestConstructor() {
+ for (var i in symbols) {
+ assertSame(Symbol, symbols[i].__proto__.constructor)
+ }
+}
+TestConstructor()
+
+
+function TestName() {
+ for (var i in symbols) {
+ var name = symbols[i].name
+ assertTrue(name === "66")
+ }
+}
+TestName()
+
+
+function TestToString() {
+ for (var i in symbols) {
+ assertThrows(function() { String(symbols[i]) }, TypeError)
+ assertThrows(function() { symbols[i] + "" }, TypeError)
+ assertThrows(function() { symbols[i].toString() }, TypeError)
+ assertThrows(function() { (new Symbol(symbols[i])).toString() }, TypeError)
+ assertThrows(function() { Object(symbols[i]).toString() }, TypeError)
+ assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
+ }
+}
+TestToString()
+
+
+function TestToBoolean() {
+ for (var i in symbols) {
+ assertTrue(Boolean(symbols[i]).valueOf())
+ assertFalse(!symbols[i])
+ assertTrue(!!symbols[i])
+ assertTrue(symbols[i] && true)
+ assertFalse(!symbols[i] && false)
+ assertTrue(!symbols[i] || true)
+ assertEquals(1, symbols[i] ? 1 : 2)
+ assertEquals(2, !symbols[i] ? 1 : 2)
+ if (!symbols[i]) assertUnreachable();
+ if (symbols[i]) {} else assertUnreachable();
+ }
+}
+TestToBoolean()
+
+
+function TestToNumber() {
+ for (var i in symbols) {
+ assertSame(NaN, Number(symbols[i]).valueOf())
+ assertSame(NaN, symbols[i] + 0)
+ }
+}
+TestToNumber()
+
+
+function TestEquality() {
+ // Every symbol should equal itself, and non-strictly equal its wrapper.
+ for (var i in symbols) {
+ assertSame(symbols[i], symbols[i])
+ assertEquals(symbols[i], symbols[i])
+ assertTrue(Object.is(symbols[i], symbols[i]))
+ assertTrue(symbols[i] === symbols[i])
+ assertTrue(symbols[i] == symbols[i])
+ assertFalse(symbols[i] === new Symbol(symbols[i]))
+ assertFalse(new Symbol(symbols[i]) === symbols[i])
+ assertTrue(symbols[i] == new Symbol(symbols[i]))
+ assertTrue(new Symbol(symbols[i]) == symbols[i])
+ }
+
+ // All symbols should be distinct.
+ for (var i = 0; i < symbols.length; ++i) {
+ for (var j = i + 1; j < symbols.length; ++j) {
+ assertFalse(Object.is(symbols[i], symbols[j]))
+ assertFalse(symbols[i] === symbols[j])
+ assertFalse(symbols[i] == symbols[j])
+ }
+ }
+
+ // Symbols should not be equal to any other value (and the test terminates).
+ var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}]
+ for (var i in symbols) {
+ for (var j in values) {
+ assertFalse(symbols[i] === values[j])
+ assertFalse(values[j] === symbols[i])
+ assertFalse(symbols[i] == values[j])
+ assertFalse(values[j] == symbols[i])
+ }
+ }
+}
+TestEquality()
+
+
+function TestGet() {
+ for (var i in symbols) {
+ assertThrows(function() { symbols[i].toString() }, TypeError)
+ assertEquals(symbols[i], symbols[i].valueOf())
+ assertEquals(undefined, symbols[i].a)
+ assertEquals(undefined, symbols[i]["a" + "b"])
+ assertEquals(undefined, symbols[i]["" + "1"])
+ assertEquals(undefined, symbols[i][62])
+ }
+}
+TestGet()
+
+
+function TestSet() {
+ for (var i in symbols) {
+ symbols[i].toString = 0
+ assertThrows(function() { symbols[i].toString() }, TypeError)
+ symbols[i].valueOf = 0
+ assertEquals(symbols[i], symbols[i].valueOf())
+ symbols[i].a = 0
+ assertEquals(undefined, symbols[i].a)
+ symbols[i]["a" + "b"] = 0
+ assertEquals(undefined, symbols[i]["a" + "b"])
+ symbols[i][62] = 0
+ assertEquals(undefined, symbols[i][62])
+ }
+}
+TestSet()
+
+
+function TestCollections() {
+ var set = new Set
+ var map = new Map
+ var weakmap = new WeakMap
+ for (var i in symbols) {
+ set.add(symbols[i])
+ map.set(symbols[i], i)
+ weakmap.set(symbols[i], i)
+ }
+ assertEquals(symbols.length, set.size)
+ assertEquals(symbols.length, map.size)
+ for (var i in symbols) {
+ assertTrue(set.has(symbols[i]))
+ assertTrue(map.has(symbols[i]))
+ assertTrue(weakmap.has(symbols[i]))
+ assertEquals(i, map.get(symbols[i]))
+ assertEquals(i, weakmap.get(symbols[i]))
+ }
+ for (var i in symbols) {
+ assertTrue(set.delete(symbols[i]))
+ assertTrue(map.delete(symbols[i]))
+ assertTrue(weakmap.delete(symbols[i]))
+ }
+ assertEquals(0, set.size)
+ assertEquals(0, map.size)
+}
+TestCollections()
+
+
+
+function TestKeySet(obj) {
+ assertTrue(%HasFastProperties(obj))
+ // Set the even symbols via assignment.
+ for (var i = 0; i < symbols.length; i += 2) {
+ obj[symbols[i]] = i
+ // Object should remain in fast mode until too many properties were added.
+ assertTrue(%HasFastProperties(obj) || i >= 30)
+ }
+}
+
+
+function TestKeyDefine(obj) {
+ // Set the odd symbols via defineProperty (as non-enumerable).
+ for (var i = 1; i < symbols.length; i += 2) {
+ Object.defineProperty(obj, symbols[i], {value: i, configurable: true})
+ }
+}
+
+
+function TestKeyGet(obj) {
+ var obj2 = Object.create(obj)
+ for (var i in symbols) {
+ assertEquals(i|0, obj[symbols[i]])
+ assertEquals(i|0, obj2[symbols[i]])
+ }
+}
+
+
+function TestKeyHas() {
+ for (var i in symbols) {
+ assertTrue(symbols[i] in obj)
+ assertTrue(Object.hasOwnProperty.call(obj, symbols[i]))
+ }
+}
+
+
+function TestKeyEnum(obj) {
+ for (var name in obj) {
+ assertEquals("string", typeof name)
+ }
+}
+
+
+function TestKeyNames(obj) {
+ assertEquals(0, Object.keys(obj).length)
+
+ var names = Object.getOwnPropertyNames(obj)
+ for (var i in names) {
+ assertEquals("string", typeof names[i])
+ }
+}
+
+
+function TestKeyDescriptor(obj) {
+ for (var i in symbols) {
+ var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]);
+ assertEquals(i|0, desc.value)
+ assertTrue(desc.configurable)
+ assertEquals(i % 2 == 0, desc.writable)
+ assertEquals(i % 2 == 0, desc.enumerable)
+ assertEquals(i % 2 == 0,
+ Object.prototype.propertyIsEnumerable.call(obj, symbols[i]))
+ }
+}
+
+
+function TestKeyDelete(obj) {
+ for (var i in symbols) {
+ delete obj[symbols[i]]
+ }
+ for (var i in symbols) {
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i]))
+ }
+}
+
+
+var objs = [{}, [], Object.create(null), Object(1), new Map, function(){}]
+
+for (var i in objs) {
+ var obj = objs[i]
+ TestKeySet(obj)
+ TestKeyDefine(obj)
+ TestKeyGet(obj)
+ TestKeyHas(obj)
+ TestKeyEnum(obj)
+ TestKeyNames(obj)
+ TestKeyDescriptor(obj)
+ TestKeyDelete(obj)
+}
+
+
+function TestCachedKeyAfterScavenge() {
+ gc();
+ // Keyed property lookup are cached. Hereby we assume that the keys are
+ // tenured, so that we only have to clear the cache between mark compacts,
+ // but not between scavenges. This must also apply for symbol keys.
+ var key = Symbol("key");
+ var a = {};
+ a[key] = "abc";
+
+ for (var i = 0; i < 1000000; i++) {
+ a[key] += "a"; // Allocations cause a scavenge.
+ }
+}
+TestCachedKeyAfterScavenge();
diff --git a/test/mjsunit/harmony/symbols.js b/test/mjsunit/harmony/symbols.js
index 5eaa1a3..1fc3945 100644
--- a/test/mjsunit/harmony/symbols.js
+++ b/test/mjsunit/harmony/symbols.js
@@ -59,6 +59,7 @@
for (var i in symbols) {
assertEquals("symbol", typeof symbols[i])
assertTrue(typeof symbols[i] === "symbol")
+ assertFalse(%SymbolIsPrivate(symbols[i]))
assertEquals(null, %_ClassOf(symbols[i]))
assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
assertEquals("Symbol", %_ClassOf(Object(symbols[i])))
diff --git a/test/mjsunit/new-string-add.js b/test/mjsunit/new-string-add.js
new file mode 100644
index 0000000..f5b7cbf
--- /dev/null
+++ b/test/mjsunit/new-string-add.js
@@ -0,0 +1,197 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --new-string-add
+
+assertEquals("ab", "a" + "b", "ll");
+
+assertEquals("12", "1" + "2", "dd");
+assertEquals("123", "1" + "2" + "3", "ddd");
+assertEquals("123", 1 + "2" + "3", "ndd");
+assertEquals("123", "1" + 2 + "3", "dnd");
+assertEquals("123", "1" + "2" + 3, "ddn");
+
+assertEquals("123", "1" + 2 + 3, "dnn");
+assertEquals("123", 1 + "2" + 3, "ndn");
+assertEquals("33", 1 + 2 + "3", "nnd");
+
+var x = "1";
+assertEquals("12", x + 2, "vn");
+assertEquals("12", x + "2", "vd");
+assertEquals("21", 2 + x, "nv");
+assertEquals("21", "2" + x, "dv");
+
+var y = "2";
+assertEquals("12", x + y, "vdvd");
+
+x = 1;
+assertEquals("12", x + y, "vnvd");
+
+y = 2;
+assertEquals(3, x + y, "vnvn");
+
+x = "1";
+assertEquals("12", x + y, "vdvn");
+
+y = "2";
+assertEquals("12", x + y, "vdvd2");
+
+(function(x, y) {
+ var z = "3";
+ var w = "4";
+
+ assertEquals("11", x + x, "xx");
+ assertEquals("12", x + y, "xy");
+ assertEquals("13", x + z, "xz");
+ assertEquals("14", x + w, "xw");
+
+ assertEquals("21", y + x, "yx");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals("31", z + x, "zx");
+ assertEquals("32", z + y, "zy");
+ assertEquals("33", z + z, "zz");
+ assertEquals("34", z + w, "zw");
+
+ assertEquals("41", w + x, "wx");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz");
+ assertEquals("44", w + w, "ww");
+
+ (function(){x = 1; z = 3;})();
+
+ assertEquals(2, x + x, "x'x");
+ assertEquals("12", x + y, "x'y");
+ assertEquals(4, x + z, "x'z'");
+ assertEquals("14", x + w, "x'w");
+
+ assertEquals("21", y + x, "yx'");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz'");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals(4, z + x, "z'x'");
+ assertEquals("32", z + y, "z'y");
+ assertEquals(6, z + z, "z'z'");
+ assertEquals("34", z + w, "z'w");
+
+ assertEquals("41", w + x, "wx'");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz'");
+ assertEquals("44", w + w, "ww");
+})("1", "2");
+
+assertEquals("142", "1" + new Number(42), "sN");
+assertEquals("421", new Number(42) + "1", "Ns");
+assertEquals(84, new Number(42) + new Number(42), "NN");
+
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("421", new String("42") + "1", "Ss");
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("4242", new String("42") + new String("42"), "SS");
+
+assertEquals("1true", "1" + true, "sb");
+assertEquals("true1", true + "1", "bs");
+assertEquals(2, true + true, "bs");
+
+assertEquals("1true", "1" + new Boolean(true), "sB");
+assertEquals("true1", new Boolean(true) + "1", "Bs");
+assertEquals(2, new Boolean(true) + new Boolean(true), "Bs");
+
+assertEquals("1undefined", "1" + void 0, "sv");
+assertEquals("undefined1", (void 0) + "1", "vs");
+assertTrue(isNaN(void 0 + void 0), "vv");
+
+assertEquals("1null", "1" + null, "su");
+assertEquals("null1", null + "1", "us");
+assertEquals(0, null + null, "uu");
+
+(function (i) {
+ // Check that incoming frames are merged correctly.
+ var x;
+ var y;
+ var z;
+ var w;
+ switch (i) {
+ case 1: x = 42; y = "stry"; z = "strz"; w = 42; break;
+ default: x = "strx", y = 42; z = "strz"; w = 42; break;
+ }
+ var resxx = x + x;
+ var resxy = x + y;
+ var resxz = x + z;
+ var resxw = x + w;
+ var resyx = y + x;
+ var resyy = y + y;
+ var resyz = y + z;
+ var resyw = y + w;
+ var reszx = z + x;
+ var reszy = z + y;
+ var reszz = z + z;
+ var reszw = z + w;
+ var reswx = w + x;
+ var reswy = w + y;
+ var reswz = w + z;
+ var resww = w + w;
+ assertEquals(84, resxx, "swxx");
+ assertEquals("42stry", resxy, "swxy");
+ assertEquals("42strz", resxz, "swxz");
+ assertEquals(84, resxw, "swxw");
+ assertEquals("stry42", resyx, "swyx");
+ assertEquals("strystry", resyy, "swyy");
+ assertEquals("strystrz", resyz, "swyz");
+ assertEquals("stry42", resyw, "swyw");
+ assertEquals("strz42", reszx, "swzx");
+ assertEquals("strzstry", reszy, "swzy");
+ assertEquals("strzstrz", reszz, "swzz");
+ assertEquals("strz42", reszw, "swzw");
+ assertEquals(84, reswx, "swwx");
+ assertEquals("42stry", reswy, "swwy");
+ assertEquals("42strz", reswz, "swwz");
+ assertEquals(84, resww, "swww");
+})(1);
+
+// Generate ascii and non ascii strings from length 0 to 20.
+var ascii = 'aaaaaaaaaaaaaaaaaaaa';
+var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';
+assertEquals(20, ascii.length);
+assertEquals(20, non_ascii.length);
+var a = Array(21);
+var b = Array(21);
+for (var i = 0; i <= 20; i++) {
+ a[i] = ascii.substring(0, i);
+ b[i] = non_ascii.substring(0, i);
+}
+
+// Add ascii and non-ascii strings generating strings with length from 0 to 20.
+for (var i = 0; i <= 20; i++) {
+ for (var j = 0; j < i; j++) {
+ assertEquals(a[i], a[j] + a[i - j])
+ assertEquals(b[i], b[j] + b[i - j])
+ }
+}
diff --git a/test/mjsunit/sin-cos.js b/test/mjsunit/sin-cos.js
index e38dfdf..1176b6c 100644
--- a/test/mjsunit/sin-cos.js
+++ b/test/mjsunit/sin-cos.js
@@ -42,9 +42,102 @@
// By accident, the slow case for sine and cosine were both sine at
// some point. This is a regression test for that issue.
-var x = Math.pow(2, 70);
+var x = Math.pow(2, 30);
assertTrue(Math.sin(x) != Math.cos(x));
// Ensure that sine and log are not the same.
x = 0.5;
assertTrue(Math.sin(x) != Math.log(x));
+
+// Test against approximation by series.
+var factorial = [1];
+var accuracy = 50;
+for (var i = 1; i < accuracy; i++) {
+ factorial[i] = factorial[i-1] * i;
+}
+
+// We sum up in the reverse order for higher precision, as we expect the terms
+// to grow smaller for x reasonably close to 0.
+function precision_sum(array) {
+ var result = 0;
+ while (array.length > 0) {
+ result += array.pop();
+ }
+ return result;
+}
+
+function sin(x) {
+ var sign = 1;
+ var x2 = x*x;
+ var terms = [];
+ for (var i = 1; i < accuracy; i += 2) {
+ terms.push(sign * x / factorial[i]);
+ x *= x2;
+ sign *= -1;
+ }
+ return precision_sum(terms);
+}
+
+function cos(x) {
+ var sign = -1;
+ var x2 = x*x;
+ x = x2;
+ var terms = [1];
+ for (var i = 2; i < accuracy; i += 2) {
+ terms.push(sign * x / factorial[i]);
+ x *= x2;
+ sign *= -1;
+ }
+ return precision_sum(terms);
+}
+
+function abs_error(fun, ref, x) {
+ return Math.abs(ref(x) - fun(x));
+}
+
+var test_inputs = [];
+for (var i = -10000; i < 10000; i += 177) test_inputs.push(i/1257);
+var epsilon = 0.000001;
+
+test_inputs.push(0);
+test_inputs.push(0 + epsilon);
+test_inputs.push(0 - epsilon);
+test_inputs.push(Math.PI/2);
+test_inputs.push(Math.PI/2 + epsilon);
+test_inputs.push(Math.PI/2 - epsilon);
+test_inputs.push(Math.PI);
+test_inputs.push(Math.PI + epsilon);
+test_inputs.push(Math.PI - epsilon);
+test_inputs.push(- 2*Math.PI);
+test_inputs.push(- 2*Math.PI + epsilon);
+test_inputs.push(- 2*Math.PI - epsilon);
+
+var squares = [];
+for (var i = 0; i < test_inputs.length; i++) {
+ var x = test_inputs[i];
+ var err_sin = abs_error(Math.sin, sin, x);
+ var err_cos = abs_error(Math.cos, cos, x)
+ assertTrue(err_sin < 1E-13);
+ assertTrue(err_cos < 1E-13);
+ squares.push(err_sin*err_sin + err_cos*err_cos);
+}
+
+// Sum squares up by adding them pairwise, to avoid losing precision.
+while (squares.length > 1) {
+ var reduced = [];
+ if (squares.length % 2 == 1) reduced.push(squares.pop());
+ // Remaining number of elements is even.
+ while(squares.length > 1) reduced.push(squares.pop() + squares.pop());
+ squares = reduced;
+}
+
+var err_rms = Math.sqrt(squares[0] / test_inputs.length / 2);
+assertTrue(err_rms < 1E-14);
+
+assertEquals(-1, Math.cos({ valueOf: function() { return Math.PI; } }));
+assertEquals(0, Math.sin("0x00000"));
+assertEquals(1, Math.cos("0x00000"));
+assertTrue(isNaN(Math.sin(Infinity)));
+assertTrue(isNaN(Math.cos("-Infinity")));
+assertEquals("Infinity", String(Math.tan(Math.PI/2)));
+assertEquals("-Infinity", String(Math.tan(-Math.PI/2)));
diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status
index b27e991..9e23dce 100644
--- a/test/mozilla/mozilla.status
+++ b/test/mozilla/mozilla.status
@@ -600,6 +600,9 @@
'ecma/TypeConversion/9.3.1-3': [FAIL_OK],
+ # Math.tan expectations are more strict than the spec.
+ 'ecma/Math/15.8.2.18': [FAIL_OK],
+
##################### FAILING TESTS #####################
# This section is for tests that fail in V8 and pass in JSC.
diff --git a/test/test262/test262.status b/test/test262/test262.status
index e546266..80a302b 100644
--- a/test/test262/test262.status
+++ b/test/test262/test262.status
@@ -73,6 +73,7 @@
# trigonometric functions are platform/compiler dependent. Furthermore, the
# expectation values by far deviates from the actual result given by an
# arbitrary-precision calculator, making those tests partly bogus.
+ 'S15.8.2.7_A7': [PASS, FAIL_OK], # Math.cos
'S15.8.2.8_A6': [PASS, FAIL_OK], # Math.exp (less precise with --fast-math)
'S15.8.2.16_A7': [PASS, FAIL_OK], # Math.sin
'S15.8.2.18_A7': [PASS, FAIL_OK], # Math.tan
diff --git a/test/webkit/webkit.status b/test/webkit/webkit.status
index bd22741..98935f7 100644
--- a/test/webkit/webkit.status
+++ b/test/webkit/webkit.status
@@ -30,8 +30,10 @@
##############################################################################
# Flaky tests.
# BUG(v8:2989).
+ 'dfg-inline-arguments-become-double': [PASS, FLAKY],
'dfg-inline-arguments-become-int32': [PASS, FLAKY],
'dfg-inline-arguments-reset': [PASS, FLAKY],
+ 'dfg-inline-arguments-reset-changetype': [PASS, FLAKY],
}], # ALWAYS
['mode == debug', {
# Too slow in debug mode.
diff --git a/tools/push-to-trunk/common_includes.py b/tools/push-to-trunk/common_includes.py
index 3b2eab0..eb2bfb0 100644
--- a/tools/push-to-trunk/common_includes.py
+++ b/tools/push-to-trunk/common_includes.py
@@ -67,6 +67,14 @@
return re.sub(rexp, replacement, text, flags=re.MULTILINE)
+def GetLastChangeLogEntries(change_log_file):
+ result = []
+ for line in LinesInFile(change_log_file):
+ if re.search(r"^\d{4}-\d{2}-\d{2}:", line) and result: break
+ result.append(line)
+ return "".join(result)
+
+
# Some commands don't like the pipe, e.g. calling vi from within the script or
# from subscripts like git cl upload.
def Command(cmd, args="", prefix="", pipe=True):
diff --git a/tools/push-to-trunk/push_to_trunk.py b/tools/push-to-trunk/push_to_trunk.py
index 6ec03cb..9f72cd2 100755
--- a/tools/push-to-trunk/push_to_trunk.py
+++ b/tools/push-to-trunk/push_to_trunk.py
@@ -221,18 +221,10 @@
self.WaitForLGTM()
# Re-read the ChangeLog entry (to pick up possible changes).
# FIXME(machenbach): This was hanging once with a broken pipe.
- TextToFile(Command("cat %s | awk --posix '{\
- if ($0 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}:/) {\
- if (in_firstblock == 1) {\
- exit 0;\
- } else {\
- in_firstblock = 1;\
- }\
- };\
- print $0;\
- }'" % self.Config(CHANGELOG_FILE)), self.Config(CHANGELOG_ENTRY_FILE))
+ TextToFile(GetLastChangeLogEntries(self.Config(CHANGELOG_FILE)),
+ self.Config(CHANGELOG_ENTRY_FILE))
- if self.Git("cl dcommit", "PRESUBMIT_TREE_CHECK=\"skip\"") is None:
+ if self.Git("cl dcommit -v", "PRESUBMIT_TREE_CHECK=\"skip\"") is None:
self.Die("'git cl dcommit' failed, please try again.")
diff --git a/tools/push-to-trunk/test_scripts.py b/tools/push-to-trunk/test_scripts.py
index 9865ada..0975b0d 100644
--- a/tools/push-to-trunk/test_scripts.py
+++ b/tools/push-to-trunk/test_scripts.py
@@ -287,6 +287,22 @@
self.assertEquals("6", self.MakeStep().Restore("new_build"))
self.assertEquals("0", self.MakeStep().Restore("new_patch"))
+ def testLastChangeLogEntries(self):
+ TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
+ l = """
+ Fixed something.
+ (issue 1234)\n"""
+ for _ in xrange(10): l = l + l
+
+ cl_chunk = """2013-11-12: Version 3.23.2\n%s
+ Performance and stability improvements on all platforms.\n\n\n""" % l
+
+ cl_chunk_full = cl_chunk + cl_chunk + cl_chunk
+ TextToFile(cl_chunk_full, TEST_CONFIG[CHANGELOG_FILE])
+
+ cl = GetLastChangeLogEntries(TEST_CONFIG[CHANGELOG_FILE])
+ self.assertEquals(cl_chunk, cl)
+
def testSquashCommits(self):
TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
with open(TEST_CONFIG[CHANGELOG_ENTRY_FILE], "w") as f:
@@ -368,7 +384,7 @@
" 2 files changed\n",
CheckPreparePush],
["cl upload -r \"reviewer@chromium.org\" --send-mail", "done\n"],
- ["cl dcommit", "Closing issue\n"],
+ ["cl dcommit -v", "Closing issue\n"],
["svn fetch", "fetch result\n"],
["checkout svn/bleeding_edge", ""],
[("log -1 --format=%H --grep=\"Prepare push to trunk. "