diff options
129 files changed, 3261 insertions, 1527 deletions
diff --git a/deps/v8/.gitignore b/deps/v8/.gitignore index d09889b90e..253639dc5d 100644 --- a/deps/v8/.gitignore +++ b/deps/v8/.gitignore @@ -21,6 +21,7 @@ shell shell_g /build/gyp /obj/ +/out/ /test/es5conform/data/ /test/mozilla/data/ /test/sputnik/sputniktests/ diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index d05b70eae7..6d188b89d1 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,31 @@ +2011-08-17: Version 3.5.6 + + Fixed issue that could cause crashes when running with --heap-stats. + + Fixed compilation on Linux 2.6.9 and older. + + Fixed live-object-list to work with isolates. + + Fixed memory leaks in zones and isolates. + + Fixed a performance regression for TypedArrays on x64. + + Stability improvements on all platforms. + + +2011-08-15: Version 3.5.5 + + Fixed bugs involving negative zero and the optimizing compiler. + + Fixed optimized version of Function.apply(x, arguments). (issue 1592) + + Eliminated uses of deprecated ARM instructions. + + Sped up Math.floor by using SSE 4.1 roundsd instruction on ia32. + + Removed restriction on the size of disassembled code that is printed. + + 2011-08-10: Version 3.5.4 Added a preliminary implementation of ES Harmony weak maps. Weak diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index d1bceca3f6..e722d34e67 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -171,12 +171,12 @@ template <class T> class Handle { /** * Creates an empty handle. */ - inline Handle(); + inline Handle() : val_(0) {} /** * Creates a new handle for the specified value. */ - inline explicit Handle(T* val) : val_(val) { } + inline explicit Handle(T* val) : val_(val) {} /** * Creates a handle for the contents of the specified handle. This @@ -203,14 +203,14 @@ template <class T> class Handle { */ inline bool IsEmpty() const { return val_ == 0; } - inline T* operator->() const { return val_; } - - inline T* operator*() const { return val_; } - /** * Sets the handle to be empty. IsEmpty() will then return true. */ - inline void Clear() { this->val_ = 0; } + inline void Clear() { val_ = 0; } + + inline T* operator->() const { return val_; } + + inline T* operator*() const { return val_; } /** * Checks whether two handles are the same. @@ -3827,10 +3827,6 @@ class Internals { template <class T> -Handle<T>::Handle() : val_(0) { } - - -template <class T> Local<T>::Local() : Handle<T>() { } diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 7355cd61c5..5a5f14dc65 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -35,6 +35,7 @@ #include "debug.h" #include "deoptimizer.h" #include "execution.h" +#include "flags.h" #include "global-handles.h" #include "heap-profiler.h" #include "messages.h" @@ -1405,7 +1406,7 @@ void ObjectTemplate::SetInternalFieldCount(int value) { ScriptData* ScriptData::PreCompile(const char* input, int length) { i::Utf8ToUC16CharacterStream stream( reinterpret_cast<const unsigned char*>(input), length); - return i::ParserApi::PreParse(&stream, NULL); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); } @@ -1414,10 +1415,10 @@ ScriptData* ScriptData::PreCompile(v8::Handle<String> source) { if (str->IsExternalTwoByteString()) { i::ExternalTwoByteStringUC16CharacterStream stream( i::Handle<i::ExternalTwoByteString>::cast(str), 0, str->length()); - return i::ParserApi::PreParse(&stream, NULL); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); } else { i::GenericStringUC16CharacterStream stream(str, 0, str->length()); - return i::ParserApi::PreParse(&stream, NULL); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); } } @@ -3163,10 +3164,9 @@ static i::Context* GetCreationContext(i::JSObject* object) { i::Object* constructor = object->map()->constructor(); i::JSFunction* function; if (!constructor->IsJSFunction()) { - // API functions have null as a constructor, + // Functions have null as a constructor, // but any JSFunction knows its context immediately. - ASSERT(object->IsJSFunction() && - i::JSFunction::cast(object)->shared()->IsApiFunction()); + ASSERT(object->IsJSFunction()); function = i::JSFunction::cast(object); } else { function = i::JSFunction::cast(constructor); diff --git a/deps/v8/src/apinatives.js b/deps/v8/src/apinatives.js index c00195d831..e94da9f065 100644 --- a/deps/v8/src/apinatives.js +++ b/deps/v8/src/apinatives.js @@ -49,7 +49,10 @@ function Instantiate(data, name) { return InstantiateFunction(data, name); case kNewObjectTag: var Constructor = %GetTemplateField(data, kApiConstructorOffset); - var result = Constructor ? new (Instantiate(Constructor))() : {}; + // Note: Do not directly use a function template as a condition, our + // internal ToBoolean doesn't handle that! + var result = typeof Constructor === 'undefined' ? + {} : new (Instantiate(Constructor))(); ConfigureTemplateInstance(result, data); result = %ToFastProperties(result); return result; @@ -74,13 +77,18 @@ function InstantiateFunction(data, name) { cache[serialNumber] = fun; var prototype = %GetTemplateField(data, kApiPrototypeTemplateOffset); var flags = %GetTemplateField(data, kApiFlagOffset); - fun.prototype = prototype ? Instantiate(prototype) : {}; + // Note: Do not directly use an object template as a condition, our + // internal ToBoolean doesn't handle that! + fun.prototype = typeof prototype === 'undefined' ? + {} : Instantiate(prototype); if (flags & (1 << kReadOnlyPrototypeBit)) { %FunctionSetReadOnlyPrototype(fun); } %SetProperty(fun.prototype, "constructor", fun, DONT_ENUM); var parent = %GetTemplateField(data, kApiParentTemplateOffset); - if (parent) { + // Note: Do not directly use a function template as a condition, our + // internal ToBoolean doesn't handle that! + if (!(typeof parent === 'undefined')) { var parent_fun = Instantiate(parent); fun.prototype.__proto__ = parent_fun.prototype; } diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index 161421e346..82fa8ceaf3 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -1613,14 +1613,14 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { const Register map = r9.is(tos_) ? r7 : r9; // undefined -> false. - CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch); + CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false); // Boolean -> its value. - CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch); - CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch); + CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false); + CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true); // 'null' -> false. - CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch); + CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false); if (types_.Contains(SMI)) { // Smis: 0 -> false, all other -> true @@ -1635,12 +1635,13 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { if (types_.NeedsMap()) { __ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); - __ tst(ip, Operand(1 << Map::kIsUndetectable)); - // Undetectable -> false. - __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, ne); - __ Ret(ne); + if (types_.CanBeUndetectable()) { + __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); + __ tst(ip, Operand(1 << Map::kIsUndetectable)); + // Undetectable -> false. + __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, ne); + __ Ret(ne); + } } if (types_.Contains(SPEC_OBJECT)) { @@ -1648,10 +1649,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE); // tos_ contains the correct non-zero return value already. __ Ret(ge); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> patch. - __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &patch); } if (types_.Contains(STRING)) { @@ -1659,10 +1656,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE); __ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset), lt); __ Ret(lt); // the string length is OK as the return value - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a string for the first time -> patch - __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE); - __ b(lt, &patch); } if (types_.Contains(HEAP_NUMBER)) { @@ -1679,30 +1672,17 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, vs); // for FP_NAN __ Ret(); __ bind(¬_heap_number); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> patch - __ CompareRoot(map, Heap::kHeapNumberMapRootIndex); - __ b(eq, &patch); - } - - if (types_.Contains(INTERNAL_OBJECT)) { - // Internal objects -> true. - __ mov(tos_, Operand(1, RelocInfo::NONE)); - __ Ret(); } - if (!types_.IsAll()) { - __ bind(&patch); - GenerateTypeTransition(masm); - } + __ bind(&patch); + GenerateTypeTransition(masm); } void ToBooleanStub::CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, - bool result, - Label* patch) { + bool result) { if (types_.Contains(type)) { // If we see an expected oddball, return its ToBoolean value tos_. __ LoadRoot(ip, value); @@ -1713,12 +1693,6 @@ void ToBooleanStub::CheckOddball(MacroAssembler* masm, __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq); } __ Ret(eq); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // If we see an unexpected oddball and handle internal objects, we must - // patch because the code for internal objects doesn't handle it explictly. - __ LoadRoot(ip, value); - __ cmp(tos_, ip); - __ b(eq, patch); } } @@ -6341,12 +6315,8 @@ void DirectCEntryStub::Generate(MacroAssembler* masm) { void DirectCEntryStub::GenerateCall(MacroAssembler* masm, ExternalReference function) { - __ mov(lr, Operand(reinterpret_cast<intptr_t>(GetCode().location()), - RelocInfo::CODE_TARGET)); __ mov(r2, Operand(function)); - // Push return address (accessible to GC through exit frame pc). - __ str(pc, MemOperand(sp, 0)); - __ Jump(r2); // Call the api function. + GenerateCall(masm, r2); } @@ -6355,8 +6325,14 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm, __ mov(lr, Operand(reinterpret_cast<intptr_t>(GetCode().location()), RelocInfo::CODE_TARGET)); // Push return address (accessible to GC through exit frame pc). - __ str(pc, MemOperand(sp, 0)); + // Note that using pc with str is deprecated. + Label start; + __ bind(&start); + __ add(ip, pc, Operand(Assembler::kInstrSize)); + __ str(ip, MemOperand(sp, 0)); __ Jump(target); // Call the C++ function. + ASSERT_EQ(Assembler::kInstrSize + Assembler::kPcLoadDelta, + masm->SizeOfCodeGeneratedSince(&start)); } diff --git a/deps/v8/src/arm/deoptimizer-arm.cc b/deps/v8/src/arm/deoptimizer-arm.cc index 9d9c045ff1..00357f76db 100644 --- a/deps/v8/src/arm/deoptimizer-arm.cc +++ b/deps/v8/src/arm/deoptimizer-arm.cc @@ -593,6 +593,8 @@ void Deoptimizer::EntryGenerator::Generate() { __ vstm(db_w, sp, first, last); // Push all 16 registers (needed to populate FrameDescription::registers_). + // TODO(1588) Note that using pc with stm is deprecated, so we should perhaps + // handle this a bit differently. __ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit()); const int kSavedRegistersAreaSize = diff --git a/deps/v8/src/arm/frames-arm.h b/deps/v8/src/arm/frames-arm.h index 84e108b3dc..26bbd82d00 100644 --- a/deps/v8/src/arm/frames-arm.h +++ b/deps/v8/src/arm/frames-arm.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -93,10 +93,11 @@ static const int kNumSafepointSavedRegisters = class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kStateOffset = 1 * kPointerSize; - static const int kFPOffset = 2 * kPointerSize; - static const int kPCOffset = 3 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kStateOffset = 1 * kPointerSize; + static const int kContextOffset = 2 * kPointerSize; + static const int kFPOffset = 3 * kPointerSize; + static const int kPCOffset = 4 * kPointerSize; static const int kSize = kPCOffset + kPointerSize; }; diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index cdfc0a9acf..3116ca455b 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -742,9 +742,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ mov(r2, Operand(variable->name())); // Declaration nodes are always introduced in one of two modes. ASSERT(mode == Variable::VAR || - mode == Variable::CONST); - PropertyAttributes attr = - (mode == Variable::VAR) ? NONE : READ_ONLY; + mode == Variable::CONST || + mode == Variable::LET); + PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; __ mov(r1, Operand(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -4030,6 +4030,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, __ b(eq, if_true); __ CompareRoot(r0, Heap::kFalseValueRootIndex); Split(eq, if_true, if_false, fall_through); + } else if (FLAG_harmony_typeof && + check->Equals(isolate()->heap()->null_symbol())) { + __ CompareRoot(r0, Heap::kNullValueRootIndex); + Split(eq, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->undefined_symbol())) { __ CompareRoot(r0, Heap::kUndefinedValueRootIndex); __ b(eq, if_true); @@ -4047,8 +4051,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(r0, if_false); - __ CompareRoot(r0, Heap::kNullValueRootIndex); - __ b(eq, if_true); + if (!FLAG_harmony_typeof) { + __ CompareRoot(r0, Heap::kNullValueRootIndex); + __ b(eq, if_true); + } // Check for JS objects => true. __ CompareObjectType(r0, r0, r1, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); __ b(lt, if_false); diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index 1eb4aff2bc..65ef3e6e1f 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -1039,13 +1039,7 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { : instr->SecondSuccessor(); return new LGoto(successor->block_id()); } - LInstruction* branch = new LBranch(UseRegister(v)); - // When we handle all cases, we never deopt, so we don't need to assign the - // environment then. Note that we map the "empty" case to the "all" case in - // the code generator. - ToBooleanStub::Types types = instr->expected_input_types(); - bool all_cases_handled = types.IsAll() || types.IsEmpty(); - return all_cases_handled ? branch : AssignEnvironment(branch); + return AssignEnvironment(new LBranch(UseRegister(v))); } @@ -1515,16 +1509,10 @@ LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) { } -LInstruction* LChunkBuilder::DoExternalArrayLength( - HExternalArrayLength* instr) { +LInstruction* LChunkBuilder::DoFixedArrayBaseLength( + HFixedArrayBaseLength* instr) { LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LExternalArrayLength(array)); -} - - -LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) { - LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LFixedArrayLength(array)); + return DefineAsRegister(new LFixedArrayBaseLength(array)); } diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h index 70c348de69..3632c8cf48 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -92,8 +92,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ - V(ExternalArrayLength) \ - V(FixedArrayLength) \ + V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ V(GlobalObject) \ @@ -915,25 +914,15 @@ class LJSArrayLength: public LTemplateInstruction<1, 1, 0> { }; -class LExternalArrayLength: public LTemplateInstruction<1, 1, 0> { +class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { public: - explicit LExternalArrayLength(LOperand* value) { + explicit LFixedArrayBaseLength(LOperand* value) { inputs_[0] = value; } - DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength, "external-array-length") - DECLARE_HYDROGEN_ACCESSOR(ExternalArrayLength) -}; - - -class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> { - public: - explicit LFixedArrayLength(LOperand* value) { - inputs_[0] = value; - } - - DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length") - DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength) + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength, + "fixed-array-base-length") + DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength) }; diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index 3f3f31acca..4e135d66b5 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -1378,17 +1378,10 @@ void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) { } -void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) { +void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { Register result = ToRegister(instr->result()); Register array = ToRegister(instr->InputAt(0)); - __ ldr(result, FieldMemOperand(array, ExternalArray::kLengthOffset)); -} - - -void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) { - Register result = ToRegister(instr->result()); - Register array = ToRegister(instr->InputAt(0)); - __ ldr(result, FieldMemOperand(array, FixedArray::kLengthOffset)); + __ ldr(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset)); } @@ -1583,46 +1576,18 @@ void LCodeGen::DoBranch(LBranch* instr) { // undefined -> false. __ CompareRoot(reg, Heap::kUndefinedValueRootIndex); __ b(eq, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen undefined for the first time -> deopt. - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - DeoptimizeIf(eq, instr->environment()); } - if (expected.Contains(ToBooleanStub::BOOLEAN)) { // Boolean -> its value. __ CompareRoot(reg, Heap::kTrueValueRootIndex); __ b(eq, true_label); __ CompareRoot(reg, Heap::kFalseValueRootIndex); __ b(eq, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ CompareRoot(reg, Heap::kTrueValueRootIndex); - DeoptimizeIf(eq, instr->environment()); - __ CompareRoot(reg, Heap::kFalseValueRootIndex); - DeoptimizeIf(eq, instr->environment()); } - -#if 0 - if (expected.Contains(ToBooleanStub::BOOLEAN)) { - // false -> false. - __ CompareRoot(reg, Heap::kFalseValueRootIndex); - __ b(eq, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ CompareRoot(reg, Heap::kFalseValueRootIndex); - DeoptimizeIf(eq, instr->environment()); - } -#endif - if (expected.Contains(ToBooleanStub::NULL_TYPE)) { // 'null' -> false. __ CompareRoot(reg, Heap::kNullValueRootIndex); __ b(eq, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen null for the first time -> deopt. - __ CompareRoot(reg, Heap::kNullValueRootIndex); - DeoptimizeIf(eq, instr->environment()); } if (expected.Contains(ToBooleanStub::SMI)) { @@ -1639,20 +1604,19 @@ void LCodeGen::DoBranch(LBranch* instr) { const Register map = scratch0(); if (expected.NeedsMap()) { __ ldr(map, FieldMemOperand(reg, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); - __ tst(ip, Operand(1 << Map::kIsUndetectable)); - __ b(ne, false_label); + + if (expected.CanBeUndetectable()) { + // Undetectable -> false. + __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); + __ tst(ip, Operand(1 << Map::kIsUndetectable)); + __ b(ne, false_label); + } } if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) { // spec object -> true. __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE); __ b(ge, true_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> deopt. - __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE); - DeoptimizeIf(ge, instr->environment()); } if (expected.Contains(ToBooleanStub::STRING)) { @@ -1665,10 +1629,6 @@ void LCodeGen::DoBranch(LBranch* instr) { __ b(ne, true_label); __ b(false_label); __ bind(¬_string); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a string for the first time -> deopt - __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE); - DeoptimizeIf(lt, instr->environment()); } if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { @@ -1683,19 +1643,10 @@ void LCodeGen::DoBranch(LBranch* instr) { __ b(eq, false_label); // +0, -0 -> false. __ b(true_label); __ bind(¬_heap_number); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> deopt. - __ CompareRoot(map, Heap::kHeapNumberMapRootIndex); - DeoptimizeIf(eq, instr->environment()); } - if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // internal objects -> true - __ b(true_label); - } else { - // We've seen something for the first time -> deopt. - DeoptimizeIf(al, instr->environment()); - } + // We've seen something for the first time -> deopt. + DeoptimizeIf(al, instr->environment()); } } } @@ -3014,19 +2965,18 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); Register result = ToRegister(instr->result()); - Register scratch1 = result; - Register scratch2 = scratch0(); + Register scratch = scratch0(); Label done, check_sign_on_zero; // Extract exponent bits. - __ vmov(scratch1, input.high()); - __ ubfx(scratch2, - scratch1, + __ vmov(result, input.high()); + __ ubfx(scratch, + result, HeapNumber::kExponentShift, HeapNumber::kExponentBits); // If the number is in ]-0.5, +0.5[, the result is +/- 0. - __ cmp(scratch2, Operand(HeapNumber::kExponentBias - 2)); + __ cmp(scratch, Operand(HeapNumber::kExponentBias - 2)); __ mov(result, Operand(0), LeaveCC, le); if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { __ b(le, &check_sign_on_zero); @@ -3036,19 +2986,19 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { // The following conversion will not work with numbers // outside of ]-2^32, 2^32[. - __ cmp(scratch2, Operand(HeapNumber::kExponentBias + 32)); + __ cmp(scratch, Operand(HeapNumber::kExponentBias + 32)); DeoptimizeIf(ge, instr->environment()); // Save the original sign for later comparison. - __ and_(scratch2, scratch1, Operand(HeapNumber::kSignMask)); + __ and_(scratch, result, Operand(HeapNumber::kSignMask)); __ Vmov(double_scratch0(), 0.5); __ vadd(input, input, double_scratch0()); // Check sign of the result: if the sign changed, the input // value was in ]0.5, 0[ and the result should be -0. - __ vmov(scratch1, input.high()); - __ eor(scratch1, scratch1, Operand(scratch2), SetCC); + __ vmov(result, input.high()); + __ eor(result, result, Operand(scratch), SetCC); if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { DeoptimizeIf(mi, instr->environment()); } else { @@ -3059,8 +3009,8 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { __ EmitVFPTruncate(kRoundToMinusInf, double_scratch0().low(), input, - scratch1, - scratch2); + result, + scratch); DeoptimizeIf(ne, instr->environment()); __ vmov(result, double_scratch0().low()); @@ -3069,8 +3019,8 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { __ cmp(result, Operand(0)); __ b(ne, &done); __ bind(&check_sign_on_zero); - __ vmov(scratch1, input.high()); - __ tst(scratch1, Operand(HeapNumber::kSignMask)); + __ vmov(scratch, input.high()); + __ tst(scratch, Operand(HeapNumber::kSignMask)); DeoptimizeIf(ne, instr->environment()); } __ bind(&done); @@ -4395,6 +4345,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, __ CompareRoot(input, Heap::kFalseValueRootIndex); final_branch_condition = eq; + } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_symbol())) { + __ CompareRoot(input, Heap::kNullValueRootIndex); + final_branch_condition = eq; + } else if (type_name->Equals(heap()->undefined_symbol())) { __ CompareRoot(input, Heap::kUndefinedValueRootIndex); __ b(eq, true_label); @@ -4413,8 +4367,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); - __ CompareRoot(input, Heap::kNullValueRootIndex); - __ b(eq, true_label); + if (!FLAG_harmony_typeof) { + __ CompareRoot(input, Heap::kNullValueRootIndex); + __ b(eq, true_label); + } __ CompareObjectType(input, input, scratch, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); __ b(lt, false_label); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index c34a57920a..88477bb7f0 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1102,7 +1102,13 @@ void MacroAssembler::DebugBreak() { void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. - ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); + // The pc (return address) is passed in register lr. if (try_location == IN_JAVASCRIPT) { if (type == TRY_CATCH_HANDLER) { @@ -1110,14 +1116,10 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } else { mov(r3, Operand(StackHandler::TRY_FINALLY)); } - ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize - && StackHandlerConstants::kFPOffset == 2 * kPointerSize - && StackHandlerConstants::kPCOffset == 3 * kPointerSize); - stm(db_w, sp, r3.bit() | fp.bit() | lr.bit()); + stm(db_w, sp, r3.bit() | cp.bit() | fp.bit() | lr.bit()); // Save the current handler as the next handler. mov(r3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); ldr(r1, MemOperand(r3)); - ASSERT(StackHandlerConstants::kNextOffset == 0); push(r1); // Link this handler as the new current one. str(sp, MemOperand(r3)); @@ -1127,16 +1129,13 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, // The frame pointer does not point to a JS frame so we save NULL // for fp. We expect the code throwing an exception to check fp // before dereferencing it to restore the context. - mov(ip, Operand(0, RelocInfo::NONE)); // To save a NULL frame pointer. - mov(r6, Operand(StackHandler::ENTRY)); - ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize - && StackHandlerConstants::kFPOffset == 2 * kPointerSize - && StackHandlerConstants::kPCOffset == 3 * kPointerSize); - stm(db_w, sp, r6.bit() | ip.bit() | lr.bit()); + mov(r5, Operand(StackHandler::ENTRY)); // State. + mov(r6, Operand(Smi::FromInt(0))); // Indicates no context. + mov(r7, Operand(0, RelocInfo::NONE)); // NULL frame pointer. + stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | lr.bit()); // Save the current handler as the next handler. mov(r7, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); ldr(r6, MemOperand(r7)); - ASSERT(StackHandlerConstants::kNextOffset == 0); push(r6); // Link this handler as the new current one. str(sp, MemOperand(r7)); @@ -1145,7 +1144,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PopTryHandler() { - ASSERT_EQ(0, StackHandlerConstants::kNextOffset); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(r1); mov(ip, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize)); @@ -1154,39 +1153,40 @@ void MacroAssembler::PopTryHandler() { void MacroAssembler::Throw(Register value) { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // r0 is expected to hold the exception. if (!value.is(r0)) { mov(r0, value); } - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - // Drop the sp to the top of the handler. mov(r3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); ldr(sp, MemOperand(r3)); - // Restore the next handler and frame pointer, discard handler state. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + // Restore the next handler. pop(r2); str(r2, MemOperand(r3)); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state. - - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - cmp(fp, Operand(0, RelocInfo::NONE)); - // Set cp to NULL if fp is NULL. - mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); - // Restore cp otherwise. - ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); + + // Restore context and frame pointer, discard state (r3). + ldm(ia_w, sp, r3.bit() | cp.bit() | fp.bit()); + + // If the handler is a JS frame, restore the context to the frame. + // (r3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any + // of them. + cmp(r3, Operand(StackHandler::ENTRY)); + str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); + #ifdef DEBUG if (emit_debug_code()) { mov(lr, Operand(pc)); } #endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); pop(pc); } @@ -1194,8 +1194,12 @@ void MacroAssembler::Throw(Register value) { void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Register value) { // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // r0 is expected to hold the exception. if (!value.is(r0)) { mov(r0, value); @@ -1220,7 +1224,6 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(r2); str(r2, MemOperand(r3)); @@ -1242,26 +1245,17 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Stack layout at this point. See also StackHandlerConstants. // sp -> state (ENTRY) + // cp // fp // lr - // Discard handler state (r2 is not used) and restore frame pointer. - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state. - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - cmp(fp, Operand(0, RelocInfo::NONE)); - // Set cp to NULL if fp is NULL. - mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); - // Restore cp otherwise. - ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); + // Restore context and frame pointer, discard state (r2). + ldm(ia_w, sp, r2.bit() | cp.bit() | fp.bit()); #ifdef DEBUG if (emit_debug_code()) { mov(lr, Operand(pc)); } #endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); pop(pc); } diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 2c60b28a5d..1767b9d5b2 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -3489,9 +3489,9 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // Check that the index is in range. __ ldr(ip, FieldMemOperand(r3, ExternalArray::kLengthOffset)); - __ cmp(ip, Operand(key, ASR, kSmiTagSize)); + __ cmp(key, ip); // Unsigned comparison catches both negative and too-large values. - __ b(lo, &miss_force_generic); + __ b(hs, &miss_force_generic); __ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset)); // r3: base pointer of external storage @@ -3811,22 +3811,20 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. - __ ldr(r3, FieldMemOperand(receiver, JSObject::kElementsOffset)); - // Check that the key is a smi. __ JumpIfNotSmi(key, &miss_force_generic); + __ ldr(r3, FieldMemOperand(receiver, JSObject::kElementsOffset)); + // Check that the index is in range - __ SmiUntag(r4, key); __ ldr(ip, FieldMemOperand(r3, ExternalArray::kLengthOffset)); - __ cmp(r4, ip); + __ cmp(key, ip); // Unsigned comparison catches both negative and too-large values. __ b(hs, &miss_force_generic); // Handle both smis and HeapNumbers in the fast path. Go to the // runtime for all other kinds of values. // r3: external array. - // r4: key (integer). if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { // Double to pixel conversion is only implemented in the runtime for now. __ JumpIfNotSmi(value, &slow); @@ -3837,32 +3835,32 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset)); // r3: base pointer of external storage. - // r4: key (integer). // r5: value (integer). switch (elements_kind) { case JSObject::EXTERNAL_PIXEL_ELEMENTS: // Clamp the value to [0..255]. __ Usat(r5, 8, Operand(r5)); - __ strb(r5, MemOperand(r3, r4, LSL, 0)); + __ strb(r5, MemOperand(r3, key, LSR, 1)); break; case JSObject::EXTERNAL_BYTE_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ strb(r5, MemOperand(r3, r4, LSL, 0)); + __ strb(r5, MemOperand(r3, key, LSR, 1)); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ strh(r5, MemOperand(r3, r4, LSL, 1)); + __ strh(r5, MemOperand(r3, key, LSL, 0)); break; case JSObject::EXTERNAL_INT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ str(r5, MemOperand(r3, r4, LSL, 2)); + __ str(r5, MemOperand(r3, key, LSL, 1)); break; case JSObject::EXTERNAL_FLOAT_ELEMENTS: // Perform int-to-float conversion and store to memory. + __ SmiUntag(r4, key); StoreIntAsFloat(masm, r3, r4, r5, r6, r7, r9); break; case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - __ add(r3, r3, Operand(r4, LSL, 3)); + __ add(r3, r3, Operand(key, LSL, 2)); // r3: effective address of the double element FloatingPointHelper::Destination destination; if (CpuFeatures::IsSupported(VFP3)) { @@ -3895,7 +3893,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( if (elements_kind != JSObject::EXTERNAL_PIXEL_ELEMENTS) { // r3: external array. - // r4: index (integer). __ bind(&check_heap_number); __ CompareObjectType(value, r5, r6, HEAP_NUMBER_TYPE); __ b(ne, &slow); @@ -3903,7 +3900,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset)); // r3: base pointer of external storage. - // r4: key (integer). // The WebGL specification leaves the behavior of storing NaN and // +/-Infinity into integer arrays basically undefined. For more @@ -3916,13 +3912,13 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // include -kHeapObjectTag into it. __ sub(r5, r0, Operand(kHeapObjectTag)); __ vldr(d0, r5, HeapNumber::kValueOffset); - __ add(r5, r3, Operand(r4, LSL, 2)); + __ add(r5, r3, Operand(key, LSL, 1)); __ vcvt_f32_f64(s0, d0); __ vstr(s0, r5, 0); } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { __ sub(r5, r0, Operand(kHeapObjectTag)); __ vldr(d0, r5, HeapNumber::kValueOffset); - __ add(r5, r3, Operand(r4, LSL, 3)); + __ add(r5, r3, Operand(key, LSL, 2)); __ vstr(d0, r5, 0); } else { // Hoisted load. vldr requires offset to be a multiple of 4 so we can @@ -3934,15 +3930,15 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( switch (elements_kind) { case JSObject::EXTERNAL_BYTE_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ strb(r5, MemOperand(r3, r4, LSL, 0)); + __ strb(r5, MemOperand(r3, key, LSR, 1)); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ strh(r5, MemOperand(r3, r4, LSL, 1)); + __ strh(r5, MemOperand(r3, key, LSL, 0)); break; case JSObject::EXTERNAL_INT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ str(r5, MemOperand(r3, r4, LSL, 2)); + __ str(r5, MemOperand(r3, key, LSL, 1)); break; case JSObject::EXTERNAL_PIXEL_ELEMENTS: case JSObject::EXTERNAL_FLOAT_ELEMENTS: @@ -4004,7 +4000,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ orr(r5, r7, Operand(r9, LSL, kBinary32ExponentShift)); __ bind(&done); - __ str(r5, MemOperand(r3, r4, LSL, 2)); + __ str(r5, MemOperand(r3, key, LSL, 1)); // Entry registers are intact, r0 holds the value which is the return // value. __ Ret(); @@ -4017,7 +4013,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ orr(r5, r9, Operand(r6, LSR, kMantissaInLoWordShift)); __ b(&done); } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { - __ add(r7, r3, Operand(r4, LSL, 3)); + __ add(r7, r3, Operand(key, LSL, 2)); // r7: effective address of destination element. __ str(r6, MemOperand(r7, 0)); __ str(r5, MemOperand(r7, Register::kSizeInBytes)); @@ -4073,15 +4069,15 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( switch (elements_kind) { case JSObject::EXTERNAL_BYTE_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ strb(r5, MemOperand(r3, r4, LSL, 0)); + __ strb(r5, MemOperand(r3, key, LSR, 1)); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ strh(r5, MemOperand(r3, r4, LSL, 1)); + __ strh(r5, MemOperand(r3, key, LSL, 0)); break; case JSObject::EXTERNAL_INT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ str(r5, MemOperand(r3, r4, LSL, 2)); + __ str(r5, MemOperand(r3, key, LSL, 1)); break; case JSObject::EXTERNAL_PIXEL_ELEMENTS: case JSObject::EXTERNAL_FLOAT_ELEMENTS: diff --git a/deps/v8/src/ast-inl.h b/deps/v8/src/ast-inl.h index c750e6b03c..731ad2ff3f 100644 --- a/deps/v8/src/ast-inl.h +++ b/deps/v8/src/ast-inl.h @@ -50,7 +50,8 @@ Block::Block(Isolate* isolate, bool is_initializer_block) : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY), statements_(capacity), - is_initializer_block_(is_initializer_block) { + is_initializer_block_(is_initializer_block), + block_scope_(NULL) { } diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index 2df62ee96a..3e6d856ce4 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -426,7 +426,7 @@ bool ForInStatement::IsInlineable() const { } -bool EnterWithContextStatement::IsInlineable() const { +bool WithStatement::IsInlineable() const { return false; } diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index b4705f6ab8..4031b7d810 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -60,7 +60,7 @@ namespace internal { V(ContinueStatement) \ V(BreakStatement) \ V(ReturnStatement) \ - V(EnterWithContextStatement) \ + V(WithStatement) \ V(ExitContextStatement) \ V(SwitchStatement) \ V(DoWhileStatement) \ @@ -359,9 +359,13 @@ class Block: public BreakableStatement { ZoneList<Statement*>* statements() { return &statements_; } bool is_initializer_block() const { return is_initializer_block_; } + Scope* block_scope() const { return block_scope_; } + void set_block_scope(Scope* block_scope) { block_scope_ = block_scope; } + private: ZoneList<Statement*> statements_; bool is_initializer_block_; + Scope* block_scope_; }; @@ -371,9 +375,11 @@ class Declaration: public AstNode { : proxy_(proxy), mode_(mode), fun_(fun) { - ASSERT(mode == Variable::VAR || mode == Variable::CONST); + ASSERT(mode == Variable::VAR || + mode == Variable::CONST || + mode == Variable::LET); // At the moment there are no "const functions"'s in JavaScript... - ASSERT(fun == NULL || mode == Variable::VAR); + ASSERT(fun == NULL || mode == Variable::VAR || mode == Variable::LET); } DECLARE_NODE_TYPE(Declaration) @@ -627,19 +633,21 @@ class ReturnStatement: public Statement { }; -class EnterWithContextStatement: public Statement { +class WithStatement: public Statement { public: - explicit EnterWithContextStatement(Expression* expression) - : expression_(expression) { } + WithStatement(Expression* expression, Statement* statement) + : expression_(expression), statement_(statement) { } - DECLARE_NODE_TYPE(EnterWithContextStatement) + DECLARE_NODE_TYPE(WithStatement) Expression* expression() const { return expression_; } + Statement* statement() const { return statement_; } virtual bool IsInlineable() const; private: Expression* expression_; + Statement* statement_; }; diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index a5cb555252..4f7cf40940 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -1198,10 +1198,9 @@ void Genesis::InitializeExperimentalGlobal() { // TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no // longer need to live behind a flag, so WeakMap gets added to the snapshot. if (FLAG_harmony_weakmaps) { // -- W e a k M a p - Handle<JSFunction> weakmap_fun = - InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, - isolate->initial_object_prototype(), - Builtins::kIllegal, true); + InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, + isolate->initial_object_prototype(), + Builtins::kIllegal, true); } } @@ -1587,8 +1586,6 @@ bool Genesis::InstallNatives() { global_context()->set_string_function_prototype_map( HeapObject::cast(string_function->initial_map()->prototype())->map()); - InstallBuiltinFunctionIds(); - // Install Function.prototype.call and apply. { Handle<String> key = factory()->function_class_symbol(); Handle<JSFunction> function = @@ -1622,6 +1619,8 @@ bool Genesis::InstallNatives() { apply->shared()->set_length(2); } + InstallBuiltinFunctionIds(); + // Create a constructor for RegExp results (a variant of Array that // predefines the two properties index and match). { diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 0cba275c3e..5535d171be 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -340,12 +340,11 @@ void ToBooleanStub::Types::Print(StringStream* stream) const { if (IsEmpty()) stream->Add("None"); if (Contains(UNDEFINED)) stream->Add("Undefined"); if (Contains(BOOLEAN)) stream->Add("Bool"); - if (Contains(SMI)) stream->Add("Smi"); if (Contains(NULL_TYPE)) stream->Add("Null"); + if (Contains(SMI)) stream->Add("Smi"); if (Contains(SPEC_OBJECT)) stream->Add("SpecObject"); if (Contains(STRING)) stream->Add("String"); if (Contains(HEAP_NUMBER)) stream->Add("HeapNumber"); - if (Contains(INTERNAL_OBJECT)) stream->Add("InternalObject"); } @@ -385,12 +384,14 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) { return !object->IsUndetectableObject() && String::cast(*object)->length() != 0; } else if (object->IsHeapNumber()) { + ASSERT(!object->IsUndetectableObject()); Add(HEAP_NUMBER); double value = HeapNumber::cast(*object)->value(); - return !object->IsUndetectableObject() && value != 0 && !isnan(value); + return value != 0 && !isnan(value); } else { - Add(INTERNAL_OBJECT); - return !object->IsUndetectableObject(); + // We should never see an internal object at runtime here! + UNREACHABLE(); + return true; } } @@ -398,8 +399,13 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) { bool ToBooleanStub::Types::NeedsMap() const { return Contains(ToBooleanStub::SPEC_OBJECT) || Contains(ToBooleanStub::STRING) - || Contains(ToBooleanStub::HEAP_NUMBER) - || Contains(ToBooleanStub::INTERNAL_OBJECT); + || Contains(ToBooleanStub::HEAP_NUMBER); +} + + +bool ToBooleanStub::Types::CanBeUndetectable() const { + return Contains(ToBooleanStub::SPEC_OBJECT) + || Contains(ToBooleanStub::STRING); } diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 43b958b439..89e99a8d08 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -908,7 +908,6 @@ class ToBooleanStub: public CodeStub { SPEC_OBJECT, STRING, HEAP_NUMBER, - INTERNAL_OBJECT, NUMBER_OF_TYPES }; @@ -922,7 +921,6 @@ class ToBooleanStub: public CodeStub { explicit Types(byte bits) : set_(bits) {} bool IsEmpty() const { return set_.IsEmpty(); } - bool IsAll() const { return ToByte() == ((1 << NUMBER_OF_TYPES) - 1); } bool Contains(Type type) const { return set_.Contains(type); } void Add(Type type) { set_.Add(type); } byte ToByte() const { return set_.ToIntegral(); } @@ -930,6 +928,7 @@ class ToBooleanStub: public CodeStub { void TraceTransition(Types to) const; bool Record(Handle<Object> object); bool NeedsMap() const; + bool CanBeUndetectable() const; private: EnumSet<Type, byte> set_; @@ -956,8 +955,7 @@ class ToBooleanStub: public CodeStub { void CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, - bool result, - Label* patch); + bool result); void GenerateTypeTransition(MacroAssembler* masm); Register tos_; diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index a87eecc3c5..c7e78067cf 100755 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -478,15 +478,21 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, // that would be compiled lazily anyway, so we skip the preparse step // in that case too. ScriptDataImpl* pre_data = input_pre_data; + bool harmony_block_scoping = natives != NATIVES_CODE && + FLAG_harmony_block_scoping; if (pre_data == NULL && source_length >= FLAG_min_preparse_length) { if (source->IsExternalTwoByteString()) { ExternalTwoByteStringUC16CharacterStream stream( Handle<ExternalTwoByteString>::cast(source), 0, source->length()); - pre_data = ParserApi::PartialPreParse(&stream, extension); + pre_data = ParserApi::PartialPreParse(&stream, + extension, + harmony_block_scoping); } else { GenericStringUC16CharacterStream stream(source, 0, source->length()); - pre_data = ParserApi::PartialPreParse(&stream, extension); + pre_data = ParserApi::PartialPreParse(&stream, + extension, + harmony_block_scoping); } } diff --git a/deps/v8/src/contexts.cc b/deps/v8/src/contexts.cc index d066d34769..c0e724253f 100644 --- a/deps/v8/src/contexts.cc +++ b/deps/v8/src/contexts.cc @@ -109,7 +109,7 @@ Handle<Object> Context::Lookup(Handle<String> name, } // Check extension/with/global object. - if (context->has_extension()) { + if (!context->IsBlockContext() && context->has_extension()) { if (context->IsCatchContext()) { // Catch contexts have the variable name in the extension slot. if (name->Equals(String::cast(context->extension()))) { @@ -121,6 +121,9 @@ Handle<Object> Context::Lookup(Handle<String> name, return context; } } else { + ASSERT(context->IsGlobalContext() || + context->IsFunctionContext() || + context->IsWithContext()); // Global, function, and with contexts may have an object in the // extension slot. Handle<JSObject> extension(JSObject::cast(context->extension()), @@ -145,11 +148,20 @@ Handle<Object> Context::Lookup(Handle<String> name, } } - // Only functions can have locals, parameters, and a function name. - if (context->IsFunctionContext()) { + // Check serialized scope information of functions and blocks. Only + // functions can have parameters, and a function name. + if (context->IsFunctionContext() || context->IsBlockContext()) { // We may have context-local slots. Check locals in the context. - Handle<SerializedScopeInfo> scope_info( - context->closure()->shared()->scope_info(), isolate); + Handle<SerializedScopeInfo> scope_info; + if (context->IsFunctionContext()) { + scope_info = Handle<SerializedScopeInfo>( + context->closure()->shared()->scope_info(), isolate); + } else { + ASSERT(context->IsBlockContext()); + scope_info = Handle<SerializedScopeInfo>( + SerializedScopeInfo::cast(context->extension()), isolate); + } + Variable::Mode mode; int index = scope_info->ContextSlotIndex(*name, &mode); ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS); @@ -168,6 +180,7 @@ Handle<Object> Context::Lookup(Handle<String> name, switch (mode) { case Variable::INTERNAL: // Fall through. case Variable::VAR: + case Variable::LET: *attributes = NONE; break; case Variable::CONST: diff --git a/deps/v8/src/contexts.h b/deps/v8/src/contexts.h index 53b40f127d..3d9e7f4bfe 100644 --- a/deps/v8/src/contexts.h +++ b/deps/v8/src/contexts.h @@ -295,6 +295,10 @@ class Context: public FixedArray { Map* map = this->map(); return map == map->GetHeap()->with_context_map(); } + bool IsBlockContext() { + Map* map = this->map(); + return map == map->GetHeap()->block_context_map(); + } // Tells whether the global context is marked with out of memory. inline bool has_out_of_memory(); diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index 3b9c183c2a..780b0c0716 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -740,6 +740,7 @@ Persistent<Context> Shell::CreateEvaluationContext() { // Initialize the global objects Handle<ObjectTemplate> global_template = CreateGlobalTemplate(); Persistent<Context> context = Context::New(NULL, global_template); + ASSERT(!context.IsEmpty()); Context::Scope scope(context); #ifndef V8_SHARED diff --git a/deps/v8/src/d8.h b/deps/v8/src/d8.h index 40cc83b055..28321f56da 100644 --- a/deps/v8/src/d8.h +++ b/deps/v8/src/d8.h @@ -39,10 +39,6 @@ namespace v8 { -#ifndef V8_SHARED -namespace i = v8::internal; -#endif // V8_SHARED - #ifndef V8_SHARED // A single counter in a counter collection. diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js index 033455e9df..3523e54abd 100644 --- a/deps/v8/src/d8.js +++ b/deps/v8/src/d8.js @@ -103,7 +103,8 @@ Debug.ScopeType = { Global: 0, Local: 1, With: 2, Closure: 3, - Catch: 4 }; + Catch: 4, + Block: 5 }; // Current debug state. diff --git a/deps/v8/src/disassembler.cc b/deps/v8/src/disassembler.cc index 368c3a89c1..79076d6abc 100644 --- a/deps/v8/src/disassembler.cc +++ b/deps/v8/src/disassembler.cc @@ -97,14 +97,17 @@ const char* V8NameConverter::NameInCode(byte* addr) const { } -static void DumpBuffer(FILE* f, char* buff) { +static void DumpBuffer(FILE* f, StringBuilder* out) { if (f == NULL) { - PrintF("%s", buff); + PrintF("%s\n", out->Finalize()); } else { - fprintf(f, "%s", buff); + fprintf(f, "%s\n", out->Finalize()); } + out->Reset(); } + + static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength; static const int kRelocInfoPosition = 57; @@ -119,6 +122,7 @@ static int DecodeIt(FILE* f, v8::internal::EmbeddedVector<char, 128> decode_buffer; v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer; + StringBuilder out(out_buffer.start(), out_buffer.length()); byte* pc = begin; disasm::Disassembler d(converter); RelocIterator* it = NULL; @@ -181,17 +185,12 @@ static int DecodeIt(FILE* f, } } - StringBuilder out(out_buffer.start(), out_buffer.length()); - // Comments. for (int i = 0; i < comments.length(); i++) { - out.AddFormatted(" %s\n", comments[i]); + out.AddFormatted(" %s", comments[i]); + DumpBuffer(f, &out); } - // Write out comments, resets outp so that we can format the next line. - DumpBuffer(f, out.Finalize()); - out.Reset(); - // Instruction address and instruction offset. out.AddFormatted("%p %4d ", prev_pc, prev_pc - begin); @@ -209,7 +208,7 @@ static int DecodeIt(FILE* f, out.AddPadding(' ', kRelocInfoPosition - out.position()); } else { // Additional reloc infos are printed on separate lines. - out.AddFormatted("\n"); + DumpBuffer(f, &out); out.AddPadding(' ', kRelocInfoPosition); } @@ -299,9 +298,18 @@ static int DecodeIt(FILE* f, out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode)); } } - out.AddString("\n"); - DumpBuffer(f, out.Finalize()); - out.Reset(); + DumpBuffer(f, &out); + } + + // Emit comments following the last instruction (if any). + if (it != NULL) { + for ( ; !it->done(); it->next()) { + if (RelocInfo::IsComment(it->rinfo()->rmode())) { + out.AddFormatted(" %s", + reinterpret_cast<const char*>(it->rinfo()->data())); + DumpBuffer(f, &out); + } + } } delete it; diff --git a/deps/v8/src/elements.cc b/deps/v8/src/elements.cc index 9cfcceec24..9927cd5ced 100644 --- a/deps/v8/src/elements.cc +++ b/deps/v8/src/elements.cc @@ -75,9 +75,8 @@ class ElementsAccessorBase : public ElementsAccessor { virtual MaybeObject* GetWithReceiver(JSObject* obj, Object* receiver, uint32_t index) { - if (index < ElementsAccessorSubclass::GetLength(obj)) { - BackingStoreClass* backing_store = - ElementsAccessorSubclass::GetBackingStore(obj); + BackingStoreClass* backing_store = BackingStoreClass::cast(obj->elements()); + if (index < ElementsAccessorSubclass::GetLength(backing_store)) { return backing_store->get(index); } return obj->GetHeap()->the_hole_value(); @@ -87,39 +86,39 @@ class ElementsAccessorBase : public ElementsAccessor { uint32_t index, JSReceiver::DeleteMode mode) = 0; - virtual MaybeObject* AddJSArrayKeysToFixedArray(JSArray* other, - FixedArray* keys) { - int len0 = keys->length(); + virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from, + FixedArray* to) { + int len0 = to->length(); #ifdef DEBUG if (FLAG_enable_slow_asserts) { for (int i = 0; i < len0; i++) { - ASSERT(keys->get(i)->IsString() || keys->get(i)->IsNumber()); + ASSERT(!to->get(i)->IsTheHole()); } } #endif - int len1 = ElementsAccessorSubclass::GetCapacity(other); + BackingStoreClass* backing_store = BackingStoreClass::cast(from); + int len1 = ElementsAccessorSubclass::GetCapacity(backing_store); // Optimize if 'other' is empty. - // We cannot optimize if 'this' is empty, as other may have holes - // or non keys. - if (len1 == 0) return keys; + // We cannot optimize if 'this' is empty, as other may have holes. + if (len1 == 0) return to; // Compute how many elements are not in other. int extra = 0; for (int y = 0; y < len1; y++) { Object* value; MaybeObject* maybe_value = - ElementsAccessorSubclass::GetElementAtCapacityIndex(other, y); + ElementsAccessorSubclass::GetElementAtCapacityIndex(backing_store, y); if (!maybe_value->ToObject(&value)) return maybe_value; - if (!value->IsTheHole() && !HasKey(keys, value)) extra++; + if (!value->IsTheHole() && !HasKey(to, value)) extra++; } - if (extra == 0) return keys; + if (extra == 0) return to; // Allocate the result FixedArray* result; MaybeObject* maybe_obj = - other->GetHeap()->AllocateFixedArray(len0 + extra); + backing_store->GetHeap()->AllocateFixedArray(len0 + extra); if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj; // Fill in the content @@ -127,20 +126,19 @@ class ElementsAccessorBase : public ElementsAccessor { AssertNoAllocation no_gc; WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); for (int i = 0; i < len0; i++) { - Object* e = keys->get(i); + Object* e = to->get(i); ASSERT(e->IsString() || e->IsNumber()); result->set(i, e, mode); } } - // Fill in the extra keys. + // Fill in the extra values. int index = 0; for (int y = 0; y < len1; y++) { MaybeObject* maybe_value = - ElementsAccessorSubclass::GetElementAtCapacityIndex(other, y); + ElementsAccessorSubclass::GetElementAtCapacityIndex(backing_store, y); Object* value; if (!maybe_value->ToObject(&value)) return maybe_value; - if (!value->IsTheHole() && !HasKey(keys, value)) { - ASSERT(value->IsString() || value->IsNumber()); + if (!value->IsTheHole() && !HasKey(to, value)) { result->set(len0 + index, value); index++; } @@ -149,23 +147,18 @@ class ElementsAccessorBase : public ElementsAccessor { return result; } - static uint32_t GetCapacity(JSObject* obj) { - return ElementsAccessorSubclass::GetBackingStore(obj)->length(); + static uint32_t GetLength(BackingStoreClass* backing_store) { + return backing_store->length(); } - static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) { - BackingStoreClass* backing_store = - ElementsAccessorSubclass::GetBackingStore(obj); - return backing_store->get(index); - } - - protected: - static BackingStoreClass* GetBackingStore(JSObject* obj) { - return BackingStoreClass::cast(obj->elements()); + static uint32_t GetCapacity(BackingStoreClass* backing_store) { + return GetLength(backing_store); } - static uint32_t GetLength(JSObject* obj) { - return ElementsAccessorSubclass::GetBackingStore(obj)->length(); + static MaybeObject* GetElementAtCapacityIndex( + BackingStoreClass* backing_store, + int index) { + return backing_store->get(index); } private: @@ -255,9 +248,8 @@ class ExternalElementsAccessor virtual MaybeObject* GetWithReceiver(JSObject* obj, Object* receiver, uint32_t index) { - if (index < ExternalElementsAccessorSubclass::GetLength(obj)) { - ExternalArray* backing_store = - ExternalElementsAccessorSubclass::GetBackingStore(obj); + ExternalArray* backing_store = ExternalArray::cast(obj->elements()); + if (index < ExternalElementsAccessorSubclass::GetLength(backing_store)) { return backing_store->get(index); } else { return obj->GetHeap()->undefined_value(); @@ -412,16 +404,16 @@ class DictionaryElementsAccessor index); } - static uint32_t GetCapacity(JSObject* obj) { - return obj->element_dictionary()->Capacity(); + static uint32_t GetCapacity(NumberDictionary* dict) { + return dict->Capacity(); } - static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) { - NumberDictionary* dict = obj->element_dictionary(); + static MaybeObject* GetElementAtCapacityIndex(NumberDictionary* dict, + int index) { if (dict->IsKey(dict->KeyAt(index))) { return dict->ValueAt(index); } else { - return obj->GetHeap()->the_hole_value(); + return dict->GetHeap()->the_hole_value(); } } }; @@ -434,7 +426,7 @@ class NonStrictArgumentsElementsAccessor virtual MaybeObject* GetWithReceiver(JSObject* obj, Object* receiver, uint32_t index) { - FixedArray* parameter_map = GetBackingStore(obj); + FixedArray* parameter_map = FixedArray::cast(obj->elements()); uint32_t length = parameter_map->length(); Object* probe = (index < length - 2) ? parameter_map->get(index + 2) : NULL; @@ -482,13 +474,13 @@ class NonStrictArgumentsElementsAccessor return obj->GetHeap()->true_value(); } - static uint32_t GetCapacity(JSObject* obj) { + static uint32_t GetCapacity(FixedArray* obj) { // TODO(danno): Return max of parameter map length or backing store // capacity. return 0; } - static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) { + static MaybeObject* GetElementAtCapacityIndex(FixedArray* obj, int index) { // TODO(danno): Return either value from parameter map of backing // store value at index. return obj->GetHeap()->the_hole_value(); diff --git a/deps/v8/src/elements.h b/deps/v8/src/elements.h index f64244e958..74e4ad6659 100644 --- a/deps/v8/src/elements.h +++ b/deps/v8/src/elements.h @@ -47,8 +47,8 @@ class ElementsAccessor { uint32_t index, JSReceiver::DeleteMode mode) = 0; - virtual MaybeObject* AddJSArrayKeysToFixedArray(JSArray* other, - FixedArray* keys) = 0; + virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from, + FixedArray* to) = 0; // Returns a shared ElementsAccessor for the specified ElementsKind. static ElementsAccessor* ForKind(JSObject::ElementsKind elements_kind) { diff --git a/deps/v8/src/extensions/experimental/datetime-format.cc b/deps/v8/src/extensions/experimental/datetime-format.cc index 7f46302973..94a29ac0ae 100644 --- a/deps/v8/src/extensions/experimental/datetime-format.cc +++ b/deps/v8/src/extensions/experimental/datetime-format.cc @@ -135,7 +135,7 @@ v8::Handle<v8::Value> DateTimeFormat::GetMonths(const v8::Arguments& args) { v8::Handle<v8::Value> DateTimeFormat::GetWeekdays(const v8::Arguments& args) { icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); if (!date_format) { - ThrowUnexpectedObjectError(); + return ThrowUnexpectedObjectError(); } const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols(); diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 05dd5c9661..ee5c37bf08 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -34,6 +34,7 @@ #include "macro-assembler.h" #include "objects.h" #include "objects-visiting.h" +#include "scopeinfo.h" namespace v8 { namespace internal { @@ -291,6 +292,19 @@ Handle<Context> Factory::NewWithContext(Handle<JSFunction> function, } +Handle<Context> Factory::NewBlockContext( + Handle<JSFunction> function, + Handle<Context> previous, + Handle<SerializedScopeInfo> scope_info) { + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateBlockContext(*function, + *previous, + *scope_info), + Context); +} + + Handle<Struct> Factory::NewStruct(InstanceType type) { CALL_HEAP_FUNCTION( isolate(), @@ -734,6 +748,14 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name, } +Handle<SerializedScopeInfo> Factory::NewSerializedScopeInfo(int length) { + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateSerializedScopeInfo(length), + SerializedScopeInfo); +} + + Handle<Code> Factory::NewCode(const CodeDesc& desc, Code::Flags flags, Handle<Object> self_ref, diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index 3217ca906b..a69b05b38f 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -167,6 +167,11 @@ class Factory { Handle<Context> previous, Handle<JSObject> extension); + // Create a 'block' context. + Handle<Context> NewBlockContext(Handle<JSFunction> function, + Handle<Context> previous, + Handle<SerializedScopeInfo> scope_info); + // Return the Symbol matching the passed in string. Handle<String> SymbolFromString(Handle<String> value); @@ -277,6 +282,8 @@ class Factory { Handle<Context> context, PretenureFlag pretenure = TENURED); + Handle<SerializedScopeInfo> NewSerializedScopeInfo(int length); + Handle<Code> NewCode(const CodeDesc& desc, Code::Flags flags, Handle<Object> self_reference, diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 2ea9651b64..2d8f6fa95a 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -97,8 +97,10 @@ private: #define FLAG FLAG_FULL // Flags for experimental language features. +DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof") DEFINE_bool(harmony_proxies, false, "enable harmony proxies") DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps") +DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping") // Flags for experimental implementation features. DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles") diff --git a/deps/v8/src/frames-inl.h b/deps/v8/src/frames-inl.h index 5951806245..7ba79bf1b5 100644 --- a/deps/v8/src/frames-inl.h +++ b/deps/v8/src/frames-inl.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -67,6 +67,7 @@ inline bool StackHandler::includes(Address address) const { inline void StackHandler::Iterate(ObjectVisitor* v, Code* holder) const { + v->VisitPointer(context_address()); StackFrame::IteratePc(v, pc_address(), holder); } @@ -82,6 +83,12 @@ inline StackHandler::State StackHandler::state() const { } +inline Object** StackHandler::context_address() const { + const int offset = StackHandlerConstants::kContextOffset; + return reinterpret_cast<Object**>(address() + offset); +} + + inline Address* StackHandler::pc_address() const { const int offset = StackHandlerConstants::kPCOffset; return reinterpret_cast<Address*>(address() + offset); diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h index f542a92d96..4f94ebc7d1 100644 --- a/deps/v8/src/frames.h +++ b/deps/v8/src/frames.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -114,6 +114,7 @@ class StackHandler BASE_EMBEDDED { // Accessors. inline State state() const; + inline Object** context_address() const; inline Address* pc_address() const; DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler); diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index e5375fc3ae..732a8fe7d0 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -35,6 +35,7 @@ #include "macro-assembler.h" #include "prettyprinter.h" #include "scopes.h" +#include "scopeinfo.h" #include "stub-cache.h" namespace v8 { @@ -90,8 +91,7 @@ void BreakableStatementChecker::VisitReturnStatement(ReturnStatement* stmt) { } -void BreakableStatementChecker::VisitEnterWithContextStatement( - EnterWithContextStatement* stmt) { +void BreakableStatementChecker::VisitWithStatement(WithStatement* stmt) { Visit(stmt->expression()); } @@ -317,7 +317,6 @@ unsigned FullCodeGenerator::EmitStackCheckTable() { // field, and then a sequence of entries. Each entry is a pair of AST id // and code-relative pc offset. masm()->Align(kIntSize); - masm()->RecordComment("[ Stack check table"); unsigned offset = masm()->pc_offset(); unsigned length = stack_checks_.length(); __ dd(length); @@ -325,7 +324,6 @@ unsigned FullCodeGenerator::EmitStackCheckTable() { __ dd(stack_checks_[i].id); __ dd(stack_checks_[i].pc_and_state); } - masm()->RecordComment("]"); return offset; } @@ -847,8 +845,23 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { Breakable nested_statement(this, stmt); SetStatementPosition(stmt); + Scope* saved_scope = scope(); + if (stmt->block_scope() != NULL) { + { Comment cmnt(masm_, "[ Extend block context"); + scope_ = stmt->block_scope(); + __ Push(scope_->GetSerializedScopeInfo()); + PushFunctionArgumentForContextAllocation(); + __ CallRuntime(Runtime::kPushBlockContext, 2); + StoreToFrameField(StandardFrameConstants::kContextOffset, + context_register()); + } + { Comment cmnt(masm_, "[ Declarations"); + VisitDeclarations(scope_->declarations()); + } + } PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); VisitStatements(stmt->statements()); + scope_ = saved_scope; __ bind(nested_statement.break_target()); PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); } @@ -900,16 +913,24 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; + int context_length = 0; // When continuing, we clobber the unpredictable value in the accumulator // with one that's safe for GC. If we hit an exit from the try block of // try...finally on our way out, we will unconditionally preserve the // accumulator on the stack. ClearAccumulator(); while (!current->IsContinueTarget(stmt->target())) { - stack_depth = current->Exit(stack_depth); - current = current->outer(); + current = current->Exit(&stack_depth, &context_length); } __ Drop(stack_depth); + if (context_length > 0) { + while (context_length > 0) { + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + --context_length; + } + StoreToFrameField(StandardFrameConstants::kContextOffset, + context_register()); + } Iteration* loop = current->AsIteration(); __ jmp(loop->continue_target()); @@ -921,16 +942,24 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; + int context_length = 0; // When breaking, we clobber the unpredictable value in the accumulator // with one that's safe for GC. If we hit an exit from the try block of // try...finally on our way out, we will unconditionally preserve the // accumulator on the stack. ClearAccumulator(); while (!current->IsBreakTarget(stmt->target())) { - stack_depth = current->Exit(stack_depth); - current = current->outer(); + current = current->Exit(&stack_depth, &context_length); } __ Drop(stack_depth); + if (context_length > 0) { + while (context_length > 0) { + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + --context_length; + } + StoreToFrameField(StandardFrameConstants::kContextOffset, + context_register()); + } Breakable* target = current->AsBreakable(); __ jmp(target->break_target()); @@ -946,9 +975,9 @@ void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { // Exit all nested statements. NestedStatement* current = nesting_stack_; int stack_depth = 0; + int context_length = 0; while (current != NULL) { - stack_depth = current->Exit(stack_depth); - current = current->outer(); + current = current->Exit(&stack_depth, &context_length); } __ Drop(stack_depth); @@ -956,9 +985,8 @@ void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { } -void FullCodeGenerator::VisitEnterWithContextStatement( - EnterWithContextStatement* stmt) { - Comment cmnt(masm_, "[ EnterWithContextStatement"); +void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) { + Comment cmnt(masm_, "[ WithStatement"); SetStatementPosition(stmt); VisitForStackValue(stmt->expression()); @@ -966,6 +994,15 @@ void FullCodeGenerator::VisitEnterWithContextStatement( __ CallRuntime(Runtime::kPushWithContext, 2); decrement_stack_height(); StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); + + { WithOrCatch body(this); + Visit(stmt->statement()); + } + + // Pop context. + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + // Update local stack frame context field. + StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); } @@ -1124,7 +1161,9 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { Scope* saved_scope = scope(); scope_ = stmt->scope(); ASSERT(scope_->declarations()->is_empty()); - Visit(stmt->catch_block()); + { WithOrCatch body(this); + Visit(stmt->catch_block()); + } scope_ = saved_scope; __ jmp(&done); @@ -1170,8 +1209,8 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { Label try_handler_setup; const int original_stack_height = stack_height(); const int finally_block_stack_height = original_stack_height + 2; - const int try_block_stack_height = original_stack_height + 4; - STATIC_ASSERT(StackHandlerConstants::kSize / kPointerSize == 4); + const int try_block_stack_height = original_stack_height + 5; + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); // Setup the try-handler chain. Use a call to // Jump to try-handler setup and try-block code. Use call to put try-handler @@ -1300,20 +1339,33 @@ void FullCodeGenerator::VisitThrow(Throw* expr) { } -int FullCodeGenerator::TryFinally::Exit(int stack_depth) { +FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit( + int* stack_depth, + int* context_length) { // The macros used here must preserve the result register. - __ Drop(stack_depth); + __ Drop(*stack_depth); __ PopTryHandler(); + *stack_depth = 0; + + Register context = FullCodeGenerator::context_register(); + while (*context_length > 0) { + codegen_->LoadContextField(context, Context::PREVIOUS_INDEX); + --(*context_length); + } + __ Call(finally_entry_); - return 0; + return previous_; } -int FullCodeGenerator::TryCatch::Exit(int stack_depth) { +FullCodeGenerator::NestedStatement* FullCodeGenerator::TryCatch::Exit( + int* stack_depth, + int* context_length) { // The macros used here must preserve the result register. - __ Drop(stack_depth); + __ Drop(*stack_depth); __ PopTryHandler(); - return 0; + *stack_depth = 0; + return previous_; } diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 9bd6e5e4dc..2fc0553661 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -140,25 +140,19 @@ class FullCodeGenerator: public AstVisitor { virtual bool IsContinueTarget(Statement* target) { return false; } virtual bool IsBreakTarget(Statement* target) { return false; } - // Generate code to leave the nested statement. This includes - // cleaning up any stack elements in use and restoring the - // stack to the expectations of the surrounding statements. - // Takes a number of stack elements currently on top of the - // nested statement's stack, and returns a number of stack - // elements left on top of the surrounding statement's stack. - // The generated code must preserve the result register (which - // contains the value in case of a return). - virtual int Exit(int stack_depth) { - // Default implementation for the case where there is - // nothing to clean up. - return stack_depth; + // Notify the statement that we are exiting it via break, continue, or + // return and give it a chance to generate cleanup code. Return the + // next outer statement in the nesting stack. We accumulate in + // *stack_depth the amount to drop the stack and in *context_length the + // number of context chain links to unwind as we traverse the nesting + // stack from an exit to its target. + virtual NestedStatement* Exit(int* stack_depth, int* context_length) { + return previous_; } - NestedStatement* outer() { return previous_; } protected: MacroAssembler* masm() { return codegen_->masm(); } - private: FullCodeGenerator* codegen_; NestedStatement* previous_; DISALLOW_COPY_AND_ASSIGN(NestedStatement); @@ -207,7 +201,7 @@ class FullCodeGenerator: public AstVisitor { virtual ~TryCatch() {} virtual TryCatch* AsTryCatch() { return this; } Label* catch_entry() { return catch_entry_; } - virtual int Exit(int stack_depth); + virtual NestedStatement* Exit(int* stack_depth, int* context_length); private: Label* catch_entry_; DISALLOW_COPY_AND_ASSIGN(TryCatch); @@ -221,7 +215,7 @@ class FullCodeGenerator: public AstVisitor { virtual ~TryFinally() {} virtual TryFinally* AsTryFinally() { return this; } Label* finally_entry() { return finally_entry_; } - virtual int Exit(int stack_depth); + virtual NestedStatement* Exit(int* stack_depth, int* context_length); private: Label* finally_entry_; DISALLOW_COPY_AND_ASSIGN(TryFinally); @@ -235,8 +229,9 @@ class FullCodeGenerator: public AstVisitor { explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) { } virtual ~Finally() {} virtual Finally* AsFinally() { return this; } - virtual int Exit(int stack_depth) { - return stack_depth + kFinallyStackElementCount; + virtual NestedStatement* Exit(int* stack_depth, int* context_length) { + *stack_depth += kFinallyStackElementCount; + return previous_; } private: // Number of extra stack slots occupied during a finally block. @@ -254,14 +249,32 @@ class FullCodeGenerator: public AstVisitor { : Iteration(codegen, statement) { } virtual ~ForIn() {} virtual ForIn* AsForIn() { return this; } - virtual int Exit(int stack_depth) { - return stack_depth + kForInStackElementCount; + virtual NestedStatement* Exit(int* stack_depth, int* context_length) { + *stack_depth += kForInStackElementCount; + return previous_; } private: static const int kForInStackElementCount = 5; DISALLOW_COPY_AND_ASSIGN(ForIn); }; + + // A WithOrCatch represents being inside the body of a with or catch + // statement. Exiting the body needs to remove a link from the context + // chain. + class WithOrCatch : public NestedStatement { + public: + explicit WithOrCatch(FullCodeGenerator* codegen) + : NestedStatement(codegen) { + } + virtual ~WithOrCatch() {} + + virtual NestedStatement* Exit(int* stack_depth, int* context_length) { + ++(*context_length); + return previous_; + } + }; + // The forward bailout stack keeps track of the expressions that can // bail out to just before the control flow is split in a child // node. The stack elements are linked together through the parent diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index dbf3b95a94..e080cde325 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -1745,6 +1745,12 @@ bool Heap::CreateInitialMaps() { set_fixed_cow_array_map(Map::cast(obj)); ASSERT(fixed_array_map() != fixed_cow_array_map()); + { MaybeObject* maybe_obj = + AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_serialized_scope_info_map(Map::cast(obj)); + { MaybeObject* maybe_obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize); if (!maybe_obj->ToObject(&obj)) return false; } @@ -1910,6 +1916,12 @@ bool Heap::CreateInitialMaps() { AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); if (!maybe_obj->ToObject(&obj)) return false; } + set_block_context_map(Map::cast(obj)); + + { MaybeObject* maybe_obj = + AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); + if (!maybe_obj->ToObject(&obj)) return false; + } Map* global_context_map = Map::cast(obj); global_context_map->set_visitor_id(StaticVisitorBase::kVisitGlobalContext); set_global_context_map(global_context_map); @@ -4017,6 +4029,37 @@ MaybeObject* Heap::AllocateWithContext(JSFunction* function, } +MaybeObject* Heap::AllocateBlockContext(JSFunction* function, + Context* previous, + SerializedScopeInfo* scope_info) { + Object* result; + { MaybeObject* maybe_result = + AllocateFixedArray(scope_info->NumberOfContextSlots()); + if (!maybe_result->ToObject(&result)) return maybe_result; + } + // TODO(keuchel): properly initialize context slots. + Context* context = reinterpret_cast<Context*>(result); + context->set_map(block_context_map()); + context->set_closure(function); + context->set_previous(previous); + context->set_extension(scope_info); + context->set_global(previous->global()); + return context; +} + + +MaybeObject* Heap::AllocateSerializedScopeInfo(int length) { + Object* result; + { MaybeObject* maybe_result = AllocateFixedArray(length, TENURED); + if (!maybe_result->ToObject(&result)) return maybe_result; + } + SerializedScopeInfo* scope_info = + reinterpret_cast<SerializedScopeInfo*>(result); + scope_info->set_map(serialized_scope_info_map()); + return scope_info; +} + + MaybeObject* Heap::AllocateStruct(InstanceType type) { Map* map; switch (type) { diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 4f4ef14ddf..c4ee4dbe2b 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -65,6 +65,7 @@ inline Heap* _inline_get_heap_(); V(Map, heap_number_map, HeapNumberMap) \ V(Map, global_context_map, GlobalContextMap) \ V(Map, fixed_array_map, FixedArrayMap) \ + V(Map, serialized_scope_info_map, SerializedScopeInfoMap) \ V(Map, fixed_cow_array_map, FixedCOWArrayMap) \ V(Map, fixed_double_array_map, FixedDoubleArrayMap) \ V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \ @@ -111,6 +112,7 @@ inline Heap* _inline_get_heap_(); V(Map, function_context_map, FunctionContextMap) \ V(Map, catch_context_map, CatchContextMap) \ V(Map, with_context_map, WithContextMap) \ + V(Map, block_context_map, BlockContextMap) \ V(Map, code_map, CodeMap) \ V(Map, oddball_map, OddballMap) \ V(Map, global_property_cell_map, GlobalPropertyCellMap) \ @@ -160,6 +162,7 @@ inline Heap* _inline_get_heap_(); V(length_symbol, "length") \ V(name_symbol, "name") \ V(native_symbol, "native") \ + V(null_symbol, "null") \ V(number_symbol, "number") \ V(Number_symbol, "Number") \ V(nan_symbol, "NaN") \ @@ -220,7 +223,8 @@ inline Heap* _inline_get_heap_(); V(closure_symbol, "(closure)") \ V(use_strict, "use strict") \ V(dot_symbol, ".") \ - V(anonymous_function_symbol, "(anonymous function)") + V(anonymous_function_symbol, "(anonymous function)") \ + V(block_scope_symbol, ".block") // Forward declarations. class GCTracer; @@ -483,6 +487,9 @@ class Heap { // Allocates an empty code cache. MUST_USE_RESULT MaybeObject* AllocateCodeCache(); + // Allocates a serialized scope info. + MUST_USE_RESULT MaybeObject* AllocateSerializedScopeInfo(int length); + // Allocates an empty PolymorphicCodeCache. MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache(); @@ -668,6 +675,11 @@ class Heap { Context* previous, JSObject* extension); + // Allocate a block context. + MUST_USE_RESULT MaybeObject* AllocateBlockContext(JSFunction* function, + Context* previous, + SerializedScopeInfo* info); + // Allocates a new utility object in the old generation. MUST_USE_RESULT MaybeObject* AllocateStruct(InstanceType type); diff --git a/deps/v8/src/hydrogen-instructions.cc b/deps/v8/src/hydrogen-instructions.cc index 2be2a03257..71c59b5d9e 100644 --- a/deps/v8/src/hydrogen-instructions.cc +++ b/deps/v8/src/hydrogen-instructions.cc @@ -635,6 +635,12 @@ void HBinaryCall::PrintDataTo(StringStream* stream) { } +void HBoundsCheck::PrintDataTo(StringStream* stream) { + index()->PrintNameTo(stream); + stream->Add(" "); + length()->PrintNameTo(stream); +} + void HCallConstantFunction::PrintDataTo(StringStream* stream) { if (IsApplyFunction()) { stream->Add("optimized apply "); @@ -862,19 +868,10 @@ void HInstanceOf::PrintDataTo(StringStream* stream) { Range* HValue::InferRange() { - if (representation().IsTagged()) { - // Tagged values are always in int32 range when converted to integer, - // but they can contain -0. - Range* result = new Range(); - result->set_can_be_minus_zero(true); - return result; - } else if (representation().IsNone()) { - return NULL; - } else { - // Untagged integer32 cannot be -0 and we don't compute ranges for - // untagged doubles. - return new Range(); - } + // Untagged integer32 cannot be -0, all other representations can. + Range* result = new Range(); + result->set_can_be_minus_zero(!representation().IsInteger32()); + return result; } @@ -1373,6 +1370,20 @@ bool HLoadNamedFieldPolymorphic::DataEquals(HValue* value) { } +void HLoadNamedFieldPolymorphic::PrintDataTo(StringStream* stream) { + object()->PrintNameTo(stream); + stream->Add(" ."); + stream->Add(*String::cast(*name())->ToCString()); +} + + +void HLoadNamedGeneric::PrintDataTo(StringStream* stream) { + object()->PrintNameTo(stream); + stream->Add(" ."); + stream->Add(*String::cast(*name())->ToCString()); +} + + void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); stream->Add("["); diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index 23c0ae664c..e7a9104e87 100644 --- a/deps/v8/src/hydrogen-instructions.h +++ b/deps/v8/src/hydrogen-instructions.h @@ -104,8 +104,7 @@ class LChunkBuilder; V(Div) \ V(ElementsKind) \ V(EnterInlined) \ - V(ExternalArrayLength) \ - V(FixedArrayLength) \ + V(FixedArrayBaseLength) \ V(ForceRepresentation) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ @@ -1702,9 +1701,9 @@ class HJSArrayLength: public HTemplateInstruction<2> { }; -class HFixedArrayLength: public HUnaryOperation { +class HFixedArrayBaseLength: public HUnaryOperation { public: - explicit HFixedArrayLength(HValue* value) : HUnaryOperation(value) { + explicit HFixedArrayBaseLength(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetFlag(kDependsOnArrayLengths); @@ -1714,28 +1713,7 @@ class HFixedArrayLength: public HUnaryOperation { return Representation::Tagged(); } - DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength) - - protected: - virtual bool DataEquals(HValue* other) { return true; } -}; - - -class HExternalArrayLength: public HUnaryOperation { - public: - explicit HExternalArrayLength(HValue* value) : HUnaryOperation(value) { - set_representation(Representation::Integer32()); - // The result of this instruction is idempotent as long as its inputs don't - // change. The length of a pixel array cannot change once set, so it's not - // necessary to introduce a kDependsOnArrayLengths or any other dependency. - SetFlag(kUseGVN); - } - - virtual Representation RequiredInputRepresentation(int index) const { - return Representation::Tagged(); - } - - DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength) + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength) protected: virtual bool DataEquals(HValue* other) { return true; } @@ -2471,6 +2449,8 @@ class HBoundsCheck: public HTemplateInstruction<2> { return Representation::Integer32(); } + virtual void PrintDataTo(StringStream* stream); + HValue* index() { return OperandAt(0); } HValue* length() { return OperandAt(1); } @@ -3438,6 +3418,8 @@ class HLoadNamedFieldPolymorphic: public HTemplateInstruction<2> { return Representation::Tagged(); } + virtual void PrintDataTo(StringStream* stream); + DECLARE_CONCRETE_INSTRUCTION(LoadNamedFieldPolymorphic) static const int kMaxLoadPolymorphism = 4; @@ -3471,6 +3453,8 @@ class HLoadNamedGeneric: public HTemplateInstruction<2> { return Representation::Tagged(); } + virtual void PrintDataTo(StringStream* stream); + DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric) private: diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index be56c673b9..ca0aebbbb9 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -2480,6 +2480,9 @@ void HGraphBuilder::VisitBlock(Block* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + if (stmt->block_scope() != NULL) { + return Bailout("ScopedBlock"); + } BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); CHECK_BAILOUT(VisitStatements(stmt->statements())); @@ -2631,12 +2634,11 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { } -void HGraphBuilder::VisitEnterWithContextStatement( - EnterWithContextStatement* stmt) { +void HGraphBuilder::VisitWithStatement(WithStatement* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - return Bailout("EnterWithContextStatement"); + return Bailout("WithStatement"); } @@ -3940,7 +3942,7 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, HInstruction* length = NULL; HInstruction* checked_key = NULL; if (map->has_external_array_elements()) { - length = AddInstruction(new(zone()) HExternalArrayLength(elements)); + length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); HLoadExternalArrayPointer* external_elements = new(zone()) HLoadExternalArrayPointer(elements); @@ -3952,7 +3954,7 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, if (map->instance_type() == JS_ARRAY_TYPE) { length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck)); } else { - length = AddInstruction(new(zone()) HFixedArrayLength(elements)); + length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); } checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); if (is_store) { @@ -4024,7 +4026,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, if (elements_kind == JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && todo_external_array) { HInstruction* length = - AddInstruction(new(zone()) HExternalArrayLength(elements)); + AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); external_elements = new(zone()) HLoadExternalArrayPointer(elements); AddInstruction(external_elements); @@ -4088,7 +4090,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, if_jsarray->Goto(join); set_current_block(if_fastobject); - length = AddInstruction(new(zone()) HFixedArrayLength(elements)); + length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); if (is_store) { if (fast_double_elements) { @@ -4762,10 +4764,17 @@ bool HGraphBuilder::TryCallApply(Call* expr) { Property* prop = callee->AsProperty(); ASSERT(prop != NULL); - if (info()->scope()->arguments() == NULL) return false; + if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) { + return false; + } + Handle<Map> function_map = expr->GetReceiverTypes()->first(); + if (function_map->instance_type() != JS_FUNCTION_TYPE || + !expr->target()->shared()->HasBuiltinFunctionId() || + expr->target()->shared()->builtin_function_id() != kFunctionApply) { + return false; + } - Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); - if (!name->IsEqualTo(CStrVector("apply"))) return false; + if (info()->scope()->arguments() == NULL) return false; ZoneList<Expression*>* args = expr->arguments(); if (args->length() != 2) return false; @@ -4775,9 +4784,6 @@ bool HGraphBuilder::TryCallApply(Call* expr) { HValue* arg_two_value = environment()->Lookup(arg_two->var()); if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false; - if (!expr->IsMonomorphic() || - expr->check_type() != RECEIVER_MAP_CHECK) return false; - // Our implementation of arguments (based on this stack frame or an // adapter below it) does not work for inlined functions. if (function_state()->outer() != NULL) { @@ -4794,10 +4800,7 @@ bool HGraphBuilder::TryCallApply(Call* expr) { HValue* receiver = Pop(); HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements); HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements)); - AddCheckConstantFunction(expr, - function, - expr->GetReceiverTypes()->first(), - true); + AddCheckConstantFunction(expr, function, function_map, true); HInstruction* result = new(zone()) HApplyArguments(function, receiver, length, elements); result->set_position(expr->position()); diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h index 05c42dd7f0..99225511c5 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -874,7 +874,10 @@ class HGraphBuilder: public AstVisitor { bool is_store); bool TryArgumentsAccess(Property* expr); + + // Try to optimize fun.apply(receiver, arguments) pattern. bool TryCallApply(Call* expr); + bool TryInline(Call* expr); bool TryInlineBuiltinFunction(Call* expr, HValue* receiver, diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 0dc519407c..999647487e 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -1957,6 +1957,18 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { } +void Assembler::roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode) { + ASSERT(CpuFeatures::IsEnabled(SSE4_1)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x3A); + EMIT(0x0B); + emit_sse_operand(dst, src); + // Mask precision exeption. + EMIT(static_cast<byte>(mode) | 0x8); +} + void Assembler::movmskpd(Register dst, XMMRegister src) { ASSERT(CpuFeatures::IsEnabled(SSE2)); EnsureSpace ensure_space(this); diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index da38e138da..c186094b39 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -941,6 +941,16 @@ class Assembler : public AssemblerBase { void andpd(XMMRegister dst, XMMRegister src); void ucomisd(XMMRegister dst, XMMRegister src); + + enum RoundingMode { + kRoundToNearest = 0x0, + kRoundDown = 0x1, + kRoundUp = 0x2, + kRoundToZero = 0x3 + }; + + void roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode); + void movmskpd(Register dst, XMMRegister src); void cmpltsd(XMMRegister dst, XMMRegister src); diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index ed7e56c110..e39d114451 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -249,20 +249,20 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } // undefined -> false - CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch); + CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false); // Boolean -> its value - CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch); - CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch); + CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false); + CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true); // 'null' -> false. - CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch); + CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false); if (types_.Contains(SMI)) { // Smis: 0 -> false, all other -> true Label not_smi; __ JumpIfNotSmi(argument, ¬_smi, Label::kNear); - // argument contains the correct return value already + // argument contains the correct return value already. if (!tos_.is(argument)) { __ mov(tos_, argument); } @@ -276,15 +276,16 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { if (types_.NeedsMap()) { __ mov(map, FieldOperand(argument, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ test_b(FieldOperand(map, Map::kBitFieldOffset), - 1 << Map::kIsUndetectable); - // Undetectable -> false. - Label not_undetectable; - __ j(zero, ¬_undetectable, Label::kNear); - __ Set(tos_, Immediate(0)); - __ ret(1 * kPointerSize); - __ bind(¬_undetectable); + if (types_.CanBeUndetectable()) { + __ test_b(FieldOperand(map, Map::kBitFieldOffset), + 1 << Map::kIsUndetectable); + // Undetectable -> false. + Label not_undetectable; + __ j(zero, ¬_undetectable, Label::kNear); + __ Set(tos_, Immediate(0)); + __ ret(1 * kPointerSize); + __ bind(¬_undetectable); + } } if (types_.Contains(SPEC_OBJECT)) { @@ -292,13 +293,12 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { Label not_js_object; __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); __ j(below, ¬_js_object, Label::kNear); - __ Set(tos_, Immediate(1)); + // argument contains the correct return value already. + if (!tos_.is(argument)) { + __ Set(tos_, Immediate(1)); + } __ ret(1 * kPointerSize); __ bind(¬_js_object); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> patch. - __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); - __ j(above_equal, &patch, Label::kNear); } if (types_.Contains(STRING)) { @@ -309,10 +309,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ mov(tos_, FieldOperand(argument, String::kLengthOffset)); __ ret(1 * kPointerSize); // the string length is OK as the return value __ bind(¬_string); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a string for the first time -> patch - __ CmpInstanceType(map, FIRST_NONSTRING_TYPE); - __ j(below, &patch, Label::kNear); } if (types_.Contains(HEAP_NUMBER)) { @@ -324,50 +320,42 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ fld_d(FieldOperand(argument, HeapNumber::kValueOffset)); __ FCmp(); __ j(zero, &false_result, Label::kNear); - __ Set(tos_, Immediate(1)); + // argument contains the correct return value already. + if (!tos_.is(argument)) { + __ Set(tos_, Immediate(1)); + } __ ret(1 * kPointerSize); __ bind(&false_result); __ Set(tos_, Immediate(0)); __ ret(1 * kPointerSize); __ bind(¬_heap_number); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> patch - __ cmp(map, factory->heap_number_map()); - __ j(equal, &patch, Label::kNear); - } - - if (types_.Contains(INTERNAL_OBJECT)) { - // internal objects -> true - __ Set(tos_, Immediate(1)); - __ ret(1 * kPointerSize); } - if (!types_.IsAll()) { - __ bind(&patch); - GenerateTypeTransition(masm); - } + __ bind(&patch); + GenerateTypeTransition(masm); } void ToBooleanStub::CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, - bool result, - Label* patch) { + bool result) { const Register argument = eax; if (types_.Contains(type)) { // If we see an expected oddball, return its ToBoolean value tos_. Label different_value; __ CompareRoot(argument, value); __ j(not_equal, &different_value, Label::kNear); - __ Set(tos_, Immediate(result ? 1 : 0)); + if (!result) { + // If we have to return zero, there is no way around clearing tos_. + __ Set(tos_, Immediate(0)); + } else if (!tos_.is(argument)) { + // If we have to return non-zero, we can re-use the argument if it is the + // same register as the result, because we never see Smi-zero here. + __ Set(tos_, Immediate(1)); + } __ ret(1 * kPointerSize); __ bind(&different_value); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // If we see an unexpected oddball and handle internal objects, we must - // patch because the code for internal objects doesn't handle it explictly. - __ CompareRoot(argument, value); - __ j(equal, patch); } } diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc index 900668fbdc..a936277b2f 100644 --- a/deps/v8/src/ia32/disasm-ia32.cc +++ b/deps/v8/src/ia32/disasm-ia32.cc @@ -1141,7 +1141,17 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, } } else if (*data == 0x3A) { data++; - if (*data == 0x16) { + if (*data == 0x0B) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast<int8_t>(data[1]); + AppendToBuffer("roundsd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm), + static_cast<int>(imm8)); + data += 2; + } else if (*data == 0x16) { data++; int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); diff --git a/deps/v8/src/ia32/frames-ia32.h b/deps/v8/src/ia32/frames-ia32.h index bc65ddfadd..2f1b2a96d3 100644 --- a/deps/v8/src/ia32/frames-ia32.h +++ b/deps/v8/src/ia32/frames-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -58,10 +58,11 @@ static const int kNumSafepointRegisters = 8; class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kFPOffset = 1 * kPointerSize; - static const int kStateOffset = 2 * kPointerSize; - static const int kPCOffset = 3 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kContextOffset = 1 * kPointerSize; + static const int kFPOffset = 2 * kPointerSize; + static const int kStateOffset = 3 * kPointerSize; + static const int kPCOffset = 4 * kPointerSize; static const int kSize = kPCOffset + kPointerSize; }; diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index 994c9ff61e..bb75b1e6ac 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -737,8 +737,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ push(esi); __ push(Immediate(variable->name())); // Declaration nodes are always introduced in one of two modes. - ASSERT(mode == Variable::VAR || mode == Variable::CONST); - PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; + ASSERT(mode == Variable::VAR || + mode == Variable::CONST || + mode == Variable::LET); + PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; __ push(Immediate(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -3830,7 +3832,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr, const char* comment) { - // TODO(svenpanne): Allowing format strings in Comment would be nice here... Comment cmt(masm_, comment); bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); UnaryOverwriteMode overwrite = @@ -4105,6 +4106,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, __ j(equal, if_true); __ cmp(eax, isolate()->factory()->false_value()); Split(equal, if_true, if_false, fall_through); + } else if (FLAG_harmony_typeof && + check->Equals(isolate()->heap()->null_symbol())) { + __ cmp(eax, isolate()->factory()->null_value()); + Split(equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->undefined_symbol())) { __ cmp(eax, isolate()->factory()->undefined_value()); __ j(equal, if_true); @@ -4120,8 +4125,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(above_equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(eax, if_false); - __ cmp(eax, isolate()->factory()->null_value()); - __ j(equal, if_true); + if (!FLAG_harmony_typeof) { + __ cmp(eax, isolate()->factory()->null_value()); + __ j(equal, if_true); + } __ CmpObjectType(eax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, edx); __ j(below, if_false); __ CmpInstanceType(edx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index c0f4e71caa..71fe8d95ed 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -1211,17 +1211,11 @@ void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) { } -void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) { +void LCodeGen::DoFixedArrayBaseLength( + LFixedArrayBaseLength* instr) { Register result = ToRegister(instr->result()); Register array = ToRegister(instr->InputAt(0)); - __ mov(result, FieldOperand(array, FixedArray::kLengthOffset)); -} - - -void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) { - Register result = ToRegister(instr->result()); - Register array = ToRegister(instr->InputAt(0)); - __ mov(result, FieldOperand(array, ExternalArray::kLengthOffset)); + __ mov(result, FieldOperand(array, FixedArrayBase::kLengthOffset)); } @@ -1412,40 +1406,19 @@ void LCodeGen::DoBranch(LBranch* instr) { // undefined -> false. __ cmp(reg, factory()->undefined_value()); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen undefined for the first time -> deopt. - __ cmp(reg, factory()->undefined_value()); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::BOOLEAN)) { // true -> true. __ cmp(reg, factory()->true_value()); __ j(equal, true_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ cmp(reg, factory()->true_value()); - DeoptimizeIf(equal, instr->environment()); - } - - if (expected.Contains(ToBooleanStub::BOOLEAN)) { // false -> false. __ cmp(reg, factory()->false_value()); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ cmp(reg, factory()->false_value()); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::NULL_TYPE)) { // 'null' -> false. __ cmp(reg, factory()->null_value()); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen null for the first time -> deopt. - __ cmp(reg, factory()->null_value()); - DeoptimizeIf(equal, instr->environment()); } if (expected.Contains(ToBooleanStub::SMI)) { @@ -1459,26 +1432,24 @@ void LCodeGen::DoBranch(LBranch* instr) { DeoptimizeIf(zero, instr->environment()); } - Register map = no_reg; + Register map = no_reg; // Keep the compiler happy. if (expected.NeedsMap()) { map = ToRegister(instr->TempAt(0)); ASSERT(!map.is(reg)); __ mov(map, FieldOperand(reg, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ test_b(FieldOperand(map, Map::kBitFieldOffset), - 1 << Map::kIsUndetectable); - // Undetectable -> false. - __ j(not_zero, false_label); + + if (expected.CanBeUndetectable()) { + // Undetectable -> false. + __ test_b(FieldOperand(map, Map::kBitFieldOffset), + 1 << Map::kIsUndetectable); + __ j(not_zero, false_label); + } } if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) { // spec object -> true. __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); __ j(above_equal, true_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> deopt. - __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); - DeoptimizeIf(above_equal, instr->environment()); } if (expected.Contains(ToBooleanStub::STRING)) { @@ -1490,10 +1461,6 @@ void LCodeGen::DoBranch(LBranch* instr) { __ j(not_zero, true_label); __ jmp(false_label); __ bind(¬_string); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a string for the first time -> deopt - __ CmpInstanceType(map, FIRST_NONSTRING_TYPE); - DeoptimizeIf(below, instr->environment()); } if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { @@ -1508,20 +1475,10 @@ void LCodeGen::DoBranch(LBranch* instr) { __ j(zero, false_label); __ jmp(true_label); __ bind(¬_heap_number); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> deopt. - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - factory()->heap_number_map()); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // internal objects -> true - __ jmp(true_label); - } else { - // We've seen something for the first time -> deopt. - DeoptimizeIf(no_condition, instr->environment()); - } + // We've seen something for the first time -> deopt. + DeoptimizeIf(no_condition, instr->environment()); } } } @@ -2302,16 +2259,13 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { - Register elements = ToRegister(instr->elements()); - Register key = ToRegister(instr->key()); Register result = ToRegister(instr->result()); - ASSERT(result.is(elements)); // Load the result. - __ mov(result, FieldOperand(elements, - key, - times_pointer_size, - FixedArray::kHeaderSize)); + __ mov(result, + BuildFastArrayOperand(instr->elements(), instr->key(), + JSObject::FAST_ELEMENTS, + FixedArray::kHeaderSize - kHeapObjectTag)); // Check for the hole value. if (instr->hydrogen()->RequiresHoleCheck()) { @@ -2344,22 +2298,22 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand LCodeGen::BuildFastArrayOperand( - LOperand* external_pointer, + LOperand* elements_pointer, LOperand* key, JSObject::ElementsKind elements_kind, uint32_t offset) { - Register external_pointer_reg = ToRegister(external_pointer); + Register elements_pointer_reg = ToRegister(elements_pointer); int shift_size = ElementsKindToShiftSize(elements_kind); if (key->IsConstantOperand()) { int constant_value = ToInteger32(LConstantOperand::cast(key)); if (constant_value & 0xF0000000) { Abort("array index constant value too big"); } - return Operand(external_pointer_reg, + return Operand(elements_pointer_reg, constant_value * (1 << shift_size) + offset); } else { ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size); - return Operand(external_pointer_reg, ToRegister(key), scale_factor, offset); + return Operand(elements_pointer_reg, ToRegister(key), scale_factor, offset); } } @@ -2756,23 +2710,53 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; Register output_reg = ToRegister(instr->result()); XMMRegister input_reg = ToDoubleRegister(instr->value()); - __ xorps(xmm_scratch, xmm_scratch); // Zero the register. - __ ucomisd(input_reg, xmm_scratch); - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - DeoptimizeIf(below_equal, instr->environment()); + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatures::Scope scope(SSE4_1); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Deoptimize on negative zero. + Label non_zero; + __ xorps(xmm_scratch, xmm_scratch); // Zero the register. + __ ucomisd(input_reg, xmm_scratch); + __ j(not_equal, &non_zero, Label::kNear); + __ movmskpd(output_reg, input_reg); + __ test(output_reg, Immediate(1)); + DeoptimizeIf(not_zero, instr->environment()); + __ bind(&non_zero); + } + __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown); + __ cvttsd2si(output_reg, Operand(xmm_scratch)); + // Overflow is signalled with minint. + __ cmp(output_reg, 0x80000000u); + DeoptimizeIf(equal, instr->environment()); } else { + Label done; + // Deoptimize on negative numbers. + __ xorps(xmm_scratch, xmm_scratch); // Zero the register. + __ ucomisd(input_reg, xmm_scratch); DeoptimizeIf(below, instr->environment()); - } - // Use truncating instruction (OK because input is positive). - __ cvttsd2si(output_reg, Operand(input_reg)); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Check for negative zero. + Label positive_sign; + __ j(above, &positive_sign, Label::kNear); + __ movmskpd(output_reg, input_reg); + __ test(output_reg, Immediate(1)); + DeoptimizeIf(not_zero, instr->environment()); + __ Set(output_reg, Immediate(0)); + __ jmp(&done, Label::kNear); + __ bind(&positive_sign); + } - // Overflow is signalled with minint. - __ cmp(output_reg, 0x80000000u); - DeoptimizeIf(equal, instr->environment()); -} + // Use truncating instruction (OK because input is positive). + __ cvttsd2si(output_reg, Operand(input_reg)); + // Overflow is signalled with minint. + __ cmp(output_reg, 0x80000000u); + DeoptimizeIf(equal, instr->environment()); + __ bind(&done); + } +} void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; @@ -2783,13 +2767,11 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { // xmm_scratch = 0.5 ExternalReference one_half = ExternalReference::address_of_one_half(); __ movdbl(xmm_scratch, Operand::StaticVariable(one_half)); - __ ucomisd(xmm_scratch, input_reg); __ j(above, &below_half); // input = input + 0.5 __ addsd(input_reg, xmm_scratch); - // Compute Math.floor(value + 0.5). // Use truncating instruction (OK because input is positive). __ cvttsd2si(output_reg, Operand(input_reg)); @@ -3108,8 +3090,14 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { - __ cmp(ToRegister(instr->index()), ToOperand(instr->length())); - DeoptimizeIf(above_equal, instr->environment()); + if (instr->index()->IsConstantOperand()) { + __ cmp(ToOperand(instr->length()), + ToImmediate(LConstantOperand::cast(instr->index()))); + DeoptimizeIf(below_equal, instr->environment()); + } else { + __ cmp(ToRegister(instr->index()), ToOperand(instr->length())); + DeoptimizeIf(above_equal, instr->environment()); + } } @@ -4200,6 +4188,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, __ cmp(input, factory()->false_value()); final_branch_condition = equal; + } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_symbol())) { + __ cmp(input, factory()->null_value()); + final_branch_condition = equal; + } else if (type_name->Equals(heap()->undefined_symbol())) { __ cmp(input, factory()->undefined_value()); __ j(equal, true_label); @@ -4218,8 +4210,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); - __ cmp(input, factory()->null_value()); - __ j(equal, true_label); + if (!FLAG_harmony_typeof) { + __ cmp(input, factory()->null_value()); + __ j(equal, true_label); + } __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input); __ j(below, false_label); __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h index c568bef5bb..d26f245553 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -222,7 +222,7 @@ class LCodeGen BASE_EMBEDDED { Register ToRegister(int index) const; XMMRegister ToDoubleRegister(int index) const; int ToInteger32(LConstantOperand* op) const; - Operand BuildFastArrayOperand(LOperand* external_pointer, + Operand BuildFastArrayOperand(LOperand* elements_pointer, LOperand* key, JSObject::ElementsKind elements_kind, uint32_t offset); diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index 07867c70f2..bb92e89d34 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -1047,10 +1047,7 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { // involving maps). bool needs_temp = expected.NeedsMap() || expected.IsEmpty(); LOperand* temp = needs_temp ? TempRegister() : NULL; - LInstruction* branch = new LBranch(UseRegister(v), temp); - // When we handle all cases, we never deopt, so we don't need to assign the - // environment then. - return expected.IsAll() ? branch : AssignEnvironment(branch); + return AssignEnvironment(new LBranch(UseRegister(v), temp)); } @@ -1541,16 +1538,10 @@ LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) { } -LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) { +LInstruction* LChunkBuilder::DoFixedArrayBaseLength( + HFixedArrayBaseLength* instr) { LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LFixedArrayLength(array)); -} - - -LInstruction* LChunkBuilder::DoExternalArrayLength( - HExternalArrayLength* instr) { - LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LExternalArrayLength(array)); + return DefineAsRegister(new LFixedArrayBaseLength(array)); } @@ -1568,8 +1559,9 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { - return AssignEnvironment(new LBoundsCheck(UseRegisterAtStart(instr->index()), - UseAtStart(instr->length()))); + return AssignEnvironment(new LBoundsCheck( + UseRegisterOrConstantAtStart(instr->index()), + UseAtStart(instr->length()))); } @@ -1890,9 +1882,9 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastElement( ASSERT(instr->representation().IsTagged()); ASSERT(instr->key()->representation().IsInteger32()); LOperand* obj = UseRegisterAtStart(instr->object()); - LOperand* key = UseRegisterAtStart(instr->key()); + LOperand* key = UseRegisterOrConstantAtStart(instr->key()); LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); - return AssignEnvironment(DefineSameAsFirst(result)); + return AssignEnvironment(DefineAsRegister(result)); } diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index efa048dd24..e752b714e4 100644 --- a/deps/v8/src/ia32/lithium-ia32.h +++ b/deps/v8/src/ia32/lithium-ia32.h @@ -86,8 +86,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ - V(ExternalArrayLength) \ - V(FixedArrayLength) \ + V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ V(GlobalObject) \ @@ -922,25 +921,15 @@ class LJSArrayLength: public LTemplateInstruction<1, 1, 0> { }; -class LExternalArrayLength: public LTemplateInstruction<1, 1, 0> { +class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { public: - explicit LExternalArrayLength(LOperand* value) { + explicit LFixedArrayBaseLength(LOperand* value) { inputs_[0] = value; } - DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength, "external-array-length") - DECLARE_HYDROGEN_ACCESSOR(ExternalArrayLength) -}; - - -class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> { - public: - explicit LFixedArrayLength(LOperand* value) { - inputs_[0] = value; - } - - DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length") - DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength) + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength, + "fixed-array-base-length") + DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength) }; @@ -2248,14 +2237,18 @@ class LChunkBuilder BASE_EMBEDDED { template<int I, int T> LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr, XMMRegister reg); + // Assigns an environment to an instruction. An instruction which can + // deoptimize must have an environment. LInstruction* AssignEnvironment(LInstruction* instr); + // Assigns a pointer map to an instruction. An instruction which can + // trigger a GC or a lazy deoptimization must have a pointer map. LInstruction* AssignPointerMap(LInstruction* instr); enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY }; - // By default we assume that instruction sequences generated for calls - // cannot deoptimize eagerly and we do not attach environment to this - // instruction. + // Marks a call for the register allocator. Assigns a pointer map to + // support GC and lazy deoptimization. Assigns an environment to support + // eager deoptimization if CAN_DEOPTIMIZE_EAGERLY. LInstruction* MarkAsCall( LInstruction* instr, HInstruction* hinstr, diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index acb670b700..04e6cde4ed 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -542,7 +542,12 @@ void MacroAssembler::LeaveApiExitFrame() { void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. - ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // The pc (return address) is already on TOS. if (try_location == IN_JAVASCRIPT) { if (type == TRY_CATCH_HANDLER) { @@ -551,6 +556,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, push(Immediate(StackHandler::TRY_FINALLY)); } push(ebp); + push(esi); } else { ASSERT(try_location == IN_JS_ENTRY); // The frame pointer does not point to a JS frame so we save NULL @@ -558,6 +564,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, // before dereferencing it to restore the context. push(Immediate(StackHandler::ENTRY)); push(Immediate(0)); // NULL frame pointer. + push(Immediate(Smi::FromInt(0))); // No context. } // Save the current handler as the next handler. push(Operand::StaticVariable(ExternalReference(Isolate::k_handler_address, @@ -570,7 +577,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PopTryHandler() { - ASSERT_EQ(0, StackHandlerConstants::kNextOffset); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(Operand::StaticVariable(ExternalReference(Isolate::k_handler_address, isolate()))); add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize)); @@ -579,8 +586,12 @@ void MacroAssembler::PopTryHandler() { void MacroAssembler::Throw(Register value) { // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // eax must hold the exception. if (!value.is(eax)) { mov(eax, value); @@ -591,24 +602,21 @@ void MacroAssembler::Throw(Register value) { isolate()); mov(esp, Operand::StaticVariable(handler_address)); - // Restore next handler and frame pointer, discard handler state. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + // Restore next handler, context, and frame pointer; discard handler state. pop(Operand::StaticVariable(handler_address)); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); - pop(ebp); - pop(edx); // Remove state. + pop(esi); // Context. + pop(ebp); // Frame pointer. + pop(edx); // State. - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of - // a JS entry frame. - Set(esi, Immediate(0)); // Tentatively set context pointer to NULL. + // If the handler is a JS frame, restore the context to the frame. + // (edx == ENTRY) == (ebp == 0) == (esi == 0), so we could test any + // of them. Label skip; - cmp(ebp, 0); + cmp(Operand(edx), Immediate(StackHandler::ENTRY)); j(equal, &skip, Label::kNear); - mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi); bind(&skip); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); ret(0); } @@ -616,7 +624,12 @@ void MacroAssembler::Throw(Register value) { void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Register value) { // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // eax must hold the exception. if (!value.is(eax)) { @@ -642,7 +655,6 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(Operand::StaticVariable(handler_address)); if (type == OUT_OF_MEMORY) { @@ -660,15 +672,14 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, mov(Operand::StaticVariable(pending_exception), eax); } - // Clear the context pointer. + // Discard the context saved in the handler and clear the context pointer. + pop(edx); Set(esi, Immediate(0)); // Restore fp from handler and discard handler state. - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); pop(ebp); pop(edx); // State. - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); ret(0); } diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 73f42a3d18..4a37b07c71 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -3400,37 +3400,37 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ JumpIfNotSmi(eax, &miss_force_generic); // Check that the index is in range. - __ mov(ecx, eax); - __ SmiUntag(ecx); // Untag the index. __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); - __ cmp(ecx, FieldOperand(ebx, ExternalArray::kLengthOffset)); + __ cmp(eax, FieldOperand(ebx, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ j(above_equal, &miss_force_generic); __ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset)); // ebx: base pointer of external storage switch (elements_kind) { case JSObject::EXTERNAL_BYTE_ELEMENTS: - __ movsx_b(eax, Operand(ebx, ecx, times_1, 0)); + __ SmiUntag(eax); // Untag the index. + __ movsx_b(eax, Operand(ebx, eax, times_1, 0)); break; case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: case JSObject::EXTERNAL_PIXEL_ELEMENTS: - __ movzx_b(eax, Operand(ebx, ecx, times_1, 0)); + __ SmiUntag(eax); // Untag the index. + __ movzx_b(eax, Operand(ebx, eax, times_1, 0)); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: - __ movsx_w(eax, Operand(ebx, ecx, times_2, 0)); + __ movsx_w(eax, Operand(ebx, eax, times_1, 0)); break; case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ movzx_w(eax, Operand(ebx, ecx, times_2, 0)); + __ movzx_w(eax, Operand(ebx, eax, times_1, 0)); break; case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: case JSObject::EXTERNAL_INT_ELEMENTS: - __ mov(ecx, Operand(ebx, ecx, times_4, 0)); + __ mov(ecx, Operand(ebx, eax, times_2, 0)); break; case JSObject::EXTERNAL_FLOAT_ELEMENTS: - __ fld_s(Operand(ebx, ecx, times_4, 0)); + __ fld_s(Operand(ebx, eax, times_2, 0)); break; case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - __ fld_d(Operand(ebx, ecx, times_8, 0)); + __ fld_d(Operand(ebx, eax, times_4, 0)); break; default: UNREACHABLE(); @@ -3556,9 +3556,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Check that the index is in range. __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); - __ mov(ebx, ecx); - __ SmiUntag(ebx); - __ cmp(ebx, FieldOperand(edi, ExternalArray::kLengthOffset)); + __ cmp(ecx, FieldOperand(edi, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ j(above_equal, &slow); @@ -3568,7 +3566,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // edx: receiver // ecx: key // edi: elements array - // ebx: untagged index if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { __ JumpIfNotSmi(eax, &slow); } else { @@ -3576,44 +3573,39 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } // smi case - __ mov(ecx, eax); // Preserve the value in eax. Key is no longer needed. - __ SmiUntag(ecx); + __ mov(ebx, eax); // Preserve the value in eax as the return value. + __ SmiUntag(ebx); __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset)); - // ecx: base pointer of external storage + // edi: base pointer of external storage switch (elements_kind) { case JSObject::EXTERNAL_PIXEL_ELEMENTS: - { // Clamp the value to [0..255]. - Label done; - __ test(ecx, Immediate(0xFFFFFF00)); - __ j(zero, &done, Label::kNear); - __ setcc(negative, ecx); // 1 if negative, 0 if positive. - __ dec_b(ecx); // 0 if negative, 255 if positive. - __ bind(&done); - } - __ mov_b(Operand(edi, ebx, times_1, 0), ecx); + __ ClampUint8(ebx); + __ SmiUntag(ecx); + __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; case JSObject::EXTERNAL_BYTE_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ mov_b(Operand(edi, ebx, times_1, 0), ecx); + __ SmiUntag(ecx); + __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ mov_w(Operand(edi, ebx, times_2, 0), ecx); + __ mov_w(Operand(edi, ecx, times_1, 0), ebx); break; case JSObject::EXTERNAL_INT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ mov(Operand(edi, ebx, times_4, 0), ecx); + __ mov(Operand(edi, ecx, times_2, 0), ebx); break; case JSObject::EXTERNAL_FLOAT_ELEMENTS: case JSObject::EXTERNAL_DOUBLE_ELEMENTS: // Need to perform int-to-float conversion. - __ push(ecx); + __ push(ebx); __ fild_s(Operand(esp, 0)); - __ pop(ecx); + __ pop(ebx); if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { - __ fstp_s(Operand(edi, ebx, times_4, 0)); + __ fstp_s(Operand(edi, ecx, times_2, 0)); } else { // elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS. - __ fstp_d(Operand(edi, ebx, times_8, 0)); + __ fstp_d(Operand(edi, ecx, times_4, 0)); } break; default: @@ -3629,7 +3621,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // edx: receiver // ecx: key // edi: elements array - // ebx: untagged index __ cmp(FieldOperand(eax, HeapObject::kMapOffset), Immediate(masm->isolate()->factory()->heap_number_map())); __ j(not_equal, &slow); @@ -3638,15 +3629,14 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // +/-Infinity into integer arrays basically undefined. For more // reproducible behavior, convert these to zero. __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset)); - // ebx: untagged index // edi: base pointer of external storage if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); - __ fstp_s(Operand(edi, ebx, times_4, 0)); + __ fstp_s(Operand(edi, ecx, times_2, 0)); __ ret(0); } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); - __ fstp_d(Operand(edi, ebx, times_8, 0)); + __ fstp_d(Operand(edi, ecx, times_4, 0)); __ ret(0); } else { // Perform float-to-int conversion with truncation (round-to-zero) @@ -3661,27 +3651,20 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( elements_kind != JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { ASSERT(CpuFeatures::IsSupported(SSE2)); CpuFeatures::Scope scope(SSE2); - __ cvttsd2si(ecx, FieldOperand(eax, HeapNumber::kValueOffset)); + __ cvttsd2si(ebx, FieldOperand(eax, HeapNumber::kValueOffset)); // ecx: untagged integer value switch (elements_kind) { case JSObject::EXTERNAL_PIXEL_ELEMENTS: - { // Clamp the value to [0..255]. - Label done; - __ test(ecx, Immediate(0xFFFFFF00)); - __ j(zero, &done, Label::kNear); - __ setcc(negative, ecx); // 1 if negative, 0 if positive. - __ dec_b(ecx); // 0 if negative, 255 if positive. - __ bind(&done); - } - __ mov_b(Operand(edi, ebx, times_1, 0), ecx); - break; + __ ClampUint8(ebx); + // Fall through. case JSObject::EXTERNAL_BYTE_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ mov_b(Operand(edi, ebx, times_1, 0), ecx); + __ SmiUntag(ecx); + __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; case JSObject::EXTERNAL_SHORT_ELEMENTS: case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ mov_w(Operand(edi, ebx, times_2, 0), ecx); + __ mov_w(Operand(edi, ecx, times_1, 0), ebx); break; default: UNREACHABLE(); @@ -3698,7 +3681,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ sub(Operand(esp), Immediate(2 * kPointerSize)); __ fisttp_d(Operand(esp, 0)); - __ pop(ecx); + __ pop(ebx); __ add(Operand(esp), Immediate(kPointerSize)); } else { ASSERT(CpuFeatures::IsSupported(SSE2)); @@ -3709,15 +3692,15 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Note: we could do better for signed int arrays. __ movd(xmm0, FieldOperand(eax, HeapNumber::kValueOffset)); // We will need the key if we have to make the slow runtime call. - __ push(ecx); - __ LoadPowerOf2(xmm1, ecx, 31); - __ pop(ecx); + __ push(ebx); + __ LoadPowerOf2(xmm1, ebx, 31); + __ pop(ebx); __ ucomisd(xmm1, xmm0); __ j(above_equal, &slow); - __ cvttsd2si(ecx, Operand(xmm0)); + __ cvttsd2si(ebx, Operand(xmm0)); } - // ecx: untagged integer value - __ mov(Operand(edi, ebx, times_4, 0), ecx); + // ebx: untagged integer value + __ mov(Operand(edi, ecx, times_2, 0), ebx); } __ ret(0); // Return original value. } diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index 9ca61177ba..eae812bcd9 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1533,6 +1533,9 @@ void Isolate::SetIsolateThreadLocals(Isolate* isolate, Isolate::~Isolate() { TRACE_ISOLATE(destructor); + // Has to be called while counters_ are still alive. + zone_.DeleteKeptSegment(); + delete unicode_cache_; unicode_cache_ = NULL; @@ -1591,6 +1594,9 @@ Isolate::~Isolate() { delete global_handles_; global_handles_ = NULL; + delete external_reference_table_; + external_reference_table_ = NULL; + #ifdef ENABLE_DEBUGGER_SUPPORT delete debugger_; debugger_ = NULL; diff --git a/deps/v8/src/liveobjectlist.cc b/deps/v8/src/liveobjectlist.cc index e382a06456..451a28ab70 100644 --- a/deps/v8/src/liveobjectlist.cc +++ b/deps/v8/src/liveobjectlist.cc @@ -36,11 +36,12 @@ #include "global-handles.h" #include "heap.h" #include "inspector.h" +#include "isolate.h" #include "list-inl.h" #include "liveobjectlist-inl.h" #include "string-stream.h" -#include "top.h" #include "v8utils.h" +#include "v8conversions.h" namespace v8 { namespace internal { @@ -109,7 +110,7 @@ typedef int (*RawComparer)(const void*, const void*); \ v(Context, "meta: Context") \ v(ByteArray, "meta: ByteArray") \ - v(PixelArray, "meta: PixelArray") \ + v(ExternalPixelArray, "meta: PixelArray") \ v(ExternalArray, "meta: ExternalArray") \ v(FixedArray, "meta: FixedArray") \ v(String, "String") \ @@ -211,8 +212,9 @@ static AllocationSpace FindSpaceFor(String* space_str) { static bool InSpace(AllocationSpace space, HeapObject *heap_obj) { + Heap* heap = ISOLATE->heap(); if (space != LO_SPACE) { - return Heap::InSpace(heap_obj, space); + return heap->InSpace(heap_obj, space); } // This is an optimization to speed up the check for an object in the LO @@ -224,11 +226,11 @@ static bool InSpace(AllocationSpace space, HeapObject *heap_obj) { int first_space = static_cast<int>(FIRST_SPACE); int last_space = static_cast<int>(LO_SPACE); for (int sp = first_space; sp < last_space; sp++) { - if (Heap::InSpace(heap_obj, static_cast<AllocationSpace>(sp))) { + if (heap->InSpace(heap_obj, static_cast<AllocationSpace>(sp))) { return false; } } - SLOW_ASSERT(Heap::InSpace(heap_obj, LO_SPACE)); + SLOW_ASSERT(heap->InSpace(heap_obj, LO_SPACE)); return true; } @@ -285,7 +287,7 @@ LolFilter::LolFilter(Handle<JSObject> filter_obj) void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) { - Handle<String> type_sym = Factory::LookupAsciiSymbol("type"); + Handle<String> type_sym = FACTORY->LookupAsciiSymbol("type"); MaybeObject* maybe_result = filter_obj->GetProperty(*type_sym); Object* type_obj; if (maybe_result->ToObject(&type_obj)) { @@ -301,7 +303,7 @@ void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) { void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) { - Handle<String> space_sym = Factory::LookupAsciiSymbol("space"); + Handle<String> space_sym = FACTORY->LookupAsciiSymbol("space"); MaybeObject* maybe_result = filter_obj->GetProperty(*space_sym); Object* space_obj; if (maybe_result->ToObject(&space_obj)) { @@ -317,7 +319,7 @@ void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) { void LolFilter::InitPropertyFilter(Handle<JSObject> filter_obj) { - Handle<String> prop_sym = Factory::LookupAsciiSymbol("prop"); + Handle<String> prop_sym = FACTORY->LookupAsciiSymbol("prop"); MaybeObject* maybe_result = filter_obj->GetProperty(*prop_sym); Object* prop_obj; if (maybe_result->ToObject(&prop_obj)) { @@ -571,7 +573,9 @@ static bool AddObjDetail(Handle<FixedArray> arr, Handle<JSObject> detail, Handle<String> desc, Handle<Object> error) { - detail = Factory::NewJSObject(Top::object_function()); + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + detail = factory->NewJSObject(isolate->object_function()); if (detail->IsFailure()) { error = detail; return false; @@ -586,7 +590,7 @@ static bool AddObjDetail(Handle<FixedArray> arr, desc_str = buffer; size = obj->Size(); } - desc = Factory::NewStringFromAscii(CStrVector(desc_str)); + desc = factory->NewStringFromAscii(CStrVector(desc_str)); if (desc->IsFailure()) { error = desc; return false; @@ -663,10 +667,13 @@ class LolDumpWriter: public DumpWriter { int index = 0; int count = 0; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + // Prefetch some needed symbols. - Handle<String> id_sym = Factory::LookupAsciiSymbol("id"); - Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc"); - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); + Handle<String> id_sym = factory->LookupAsciiSymbol("id"); + Handle<String> desc_sym = factory->LookupAsciiSymbol("desc"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); // Fill the array with the lol object details. Handle<JSObject> detail; @@ -1089,7 +1096,9 @@ static int CountHeapObjects() { // Captures a current snapshot of all objects in the heap. MaybeObject* LiveObjectList::Capture() { - HandleScope scope; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + HandleScope scope(isolate); // Count the number of objects in the heap. int total_count = CountHeapObjects(); @@ -1139,11 +1148,11 @@ MaybeObject* LiveObjectList::Capture() { #endif } - Handle<String> id_sym = Factory::LookupAsciiSymbol("id"); - Handle<String> count_sym = Factory::LookupAsciiSymbol("count"); - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); + Handle<String> id_sym = factory->LookupAsciiSymbol("id"); + Handle<String> count_sym = factory->LookupAsciiSymbol("count"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); - Handle<JSObject> result = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); if (result->IsFailure()) return Object::cast(*result); { MaybeObject* maybe_result = result->SetProperty(*id_sym, @@ -1259,7 +1268,10 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, int start, int dump_limit, LolFilter* filter) { - HandleScope scope; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + + HandleScope scope(isolate); // Calculate the number of entries of the dump. int count = -1; @@ -1277,7 +1289,7 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, } // Allocate an array to hold the result. - Handle<FixedArray> elements_arr = Factory::NewFixedArray(dump_limit); + Handle<FixedArray> elements_arr = factory->NewFixedArray(dump_limit); if (elements_arr->IsFailure()) return Object::cast(*elements_arr); // Fill in the dump. @@ -1292,11 +1304,11 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, MaybeObject* maybe_result; // Allocate the result body. - Handle<JSObject> body = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> body = factory->NewJSObject(isolate->object_function()); if (body->IsFailure()) return Object::cast(*body); // Set the updated body.count. - Handle<String> count_sym = Factory::LookupAsciiSymbol("count"); + Handle<String> count_sym = factory->LookupAsciiSymbol("count"); maybe_result = body->SetProperty(*count_sym, Smi::FromInt(count), NONE, @@ -1305,7 +1317,7 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, // Set the updated body.size if appropriate. if (size >= 0) { - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); maybe_result = body->SetProperty(*size_sym, Smi::FromInt(size), NONE, @@ -1314,7 +1326,7 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, } // Set body.first_index. - Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index"); + Handle<String> first_sym = factory->LookupAsciiSymbol("first_index"); maybe_result = body->SetProperty(*first_sym, Smi::FromInt(start), NONE, @@ -1322,12 +1334,12 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, if (maybe_result->IsFailure()) return maybe_result; // Allocate the JSArray of the elements. - Handle<JSObject> elements = Factory::NewJSObject(Top::array_function()); + Handle<JSObject> elements = factory->NewJSObject(isolate->array_function()); if (elements->IsFailure()) return Object::cast(*elements); Handle<JSArray>::cast(elements)->SetContent(*elements_arr); // Set body.elements. - Handle<String> elements_sym = Factory::LookupAsciiSymbol("elements"); + Handle<String> elements_sym = factory->LookupAsciiSymbol("elements"); maybe_result = body->SetProperty(*elements_sym, *elements, NONE, @@ -1381,6 +1393,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, LiveObjectSummary summary(filter); writer->Write(&summary); + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + // The result body will look like this: // body: { // count: <total_count>, @@ -1398,21 +1413,21 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, // } // Prefetch some needed symbols. - Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc"); - Handle<String> count_sym = Factory::LookupAsciiSymbol("count"); - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); - Handle<String> summary_sym = Factory::LookupAsciiSymbol("summary"); + Handle<String> desc_sym = factory->LookupAsciiSymbol("desc"); + Handle<String> count_sym = factory->LookupAsciiSymbol("count"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); + Handle<String> summary_sym = factory->LookupAsciiSymbol("summary"); // Allocate the summary array. int entries_count = summary.GetNumberOfEntries(); Handle<FixedArray> summary_arr = - Factory::NewFixedArray(entries_count); + factory->NewFixedArray(entries_count); if (summary_arr->IsFailure()) return Object::cast(*summary_arr); int idx = 0; for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) { // Allocate the summary record. - Handle<JSObject> detail = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> detail = factory->NewJSObject(isolate->object_function()); if (detail->IsFailure()) return Object::cast(*detail); // Fill in the summary record. @@ -1420,7 +1435,7 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, int count = summary.Count(type); if (count) { const char* desc_cstr = GetObjectTypeDesc(type); - Handle<String> desc = Factory::LookupAsciiSymbol(desc_cstr); + Handle<String> desc = factory->LookupAsciiSymbol(desc_cstr); int size = summary.Size(type); maybe_result = detail->SetProperty(*desc_sym, @@ -1444,12 +1459,13 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, } // Wrap the summary fixed array in a JS array. - Handle<JSObject> summary_obj = Factory::NewJSObject(Top::array_function()); + Handle<JSObject> summary_obj = + factory->NewJSObject(isolate->array_function()); if (summary_obj->IsFailure()) return Object::cast(*summary_obj); Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr); // Create the body object. - Handle<JSObject> body = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> body = factory->NewJSObject(isolate->object_function()); if (body->IsFailure()) return Object::cast(*body); // Fill out the body object. @@ -1470,9 +1486,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, if (is_tracking_roots) { int found_root = summary.found_root(); int found_weak_root = summary.found_weak_root(); - Handle<String> root_sym = Factory::LookupAsciiSymbol("found_root"); + Handle<String> root_sym = factory->LookupAsciiSymbol("found_root"); Handle<String> weak_root_sym = - Factory::LookupAsciiSymbol("found_weak_root"); + factory->LookupAsciiSymbol("found_weak_root"); maybe_result = body->SetProperty(*root_sym, Smi::FromInt(found_root), NONE, @@ -1499,7 +1515,10 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, // Note: only dumps the section starting at start_idx and only up to // dump_limit entries. MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { - HandleScope scope; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + + HandleScope scope(isolate); MaybeObject* maybe_result; int total_count = LiveObjectList::list_count(); @@ -1519,13 +1538,13 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { } // Allocate an array to hold the result. - Handle<FixedArray> list = Factory::NewFixedArray(dump_count); + Handle<FixedArray> list = factory->NewFixedArray(dump_count); if (list->IsFailure()) return Object::cast(*list); // Prefetch some needed symbols. - Handle<String> id_sym = Factory::LookupAsciiSymbol("id"); - Handle<String> count_sym = Factory::LookupAsciiSymbol("count"); - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); + Handle<String> id_sym = factory->LookupAsciiSymbol("id"); + Handle<String> count_sym = factory->LookupAsciiSymbol("count"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); // Fill the array with the lol details. int idx = 0; @@ -1543,7 +1562,8 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { int size; count = lol->GetTotalObjCountAndSize(&size); - Handle<JSObject> detail = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> detail = + factory->NewJSObject(isolate->object_function()); if (detail->IsFailure()) return Object::cast(*detail); maybe_result = detail->SetProperty(*id_sym, @@ -1568,10 +1588,10 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { } // Return the result as a JS array. - Handle<JSObject> lols = Factory::NewJSObject(Top::array_function()); + Handle<JSObject> lols = factory->NewJSObject(isolate->array_function()); Handle<JSArray>::cast(lols)->SetContent(*list); - Handle<JSObject> result = Factory::NewJSObject(Top::object_function()); + Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); if (result->IsFailure()) return Object::cast(*result); maybe_result = result->SetProperty(*count_sym, @@ -1580,14 +1600,14 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { kNonStrictMode); if (maybe_result->IsFailure()) return maybe_result; - Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index"); + Handle<String> first_sym = factory->LookupAsciiSymbol("first_index"); maybe_result = result->SetProperty(*first_sym, Smi::FromInt(start_idx), NONE, kNonStrictMode); if (maybe_result->IsFailure()) return maybe_result; - Handle<String> lists_sym = Factory::LookupAsciiSymbol("lists"); + Handle<String> lists_sym = factory->LookupAsciiSymbol("lists"); maybe_result = result->SetProperty(*lists_sym, *lols, NONE, @@ -1618,7 +1638,7 @@ Object* LiveObjectList::GetObj(int obj_id) { if (element != NULL) { return Object::cast(element->obj_); } - return Heap::undefined_value(); + return HEAP->undefined_value(); } @@ -1639,8 +1659,11 @@ Object* LiveObjectList::GetObjId(Handle<String> address) { SmartPointer<char> addr_str = address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + Isolate* isolate = Isolate::Current(); + // Extract the address value from the string. - int value = static_cast<int>(StringToInt(*address, 16)); + int value = + static_cast<int>(StringToInt(isolate->unicode_cache(), *address, 16)); Object* obj = reinterpret_cast<Object*>(value); return Smi::FromInt(GetObjId(obj)); } @@ -1760,10 +1783,13 @@ int LiveObjectList::GetRetainers(Handle<HeapObject> target, Handle<String> desc; Handle<HeapObject> retainer; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + // Prefetch some needed symbols. - Handle<String> id_sym = Factory::LookupAsciiSymbol("id"); - Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc"); - Handle<String> size_sym = Factory::LookupAsciiSymbol("size"); + Handle<String> id_sym = factory->LookupAsciiSymbol("id"); + Handle<String> desc_sym = factory->LookupAsciiSymbol("desc"); + Handle<String> size_sym = factory->LookupAsciiSymbol("size"); NoHandleAllocation ha; int count = 0; @@ -1774,7 +1800,7 @@ int LiveObjectList::GetRetainers(Handle<HeapObject> target, // Iterate roots. LolVisitor lol_visitor(*target, target); - Heap::IterateStrongRoots(&lol_visitor, VISIT_ALL); + isolate->heap()->IterateStrongRoots(&lol_visitor, VISIT_ALL); if (!AddRootRetainerIfFound(lol_visitor, filter, summary, @@ -1794,7 +1820,7 @@ int LiveObjectList::GetRetainers(Handle<HeapObject> target, } lol_visitor.reset(); - Heap::IterateWeakRoots(&lol_visitor, VISIT_ALL); + isolate->heap()->IterateWeakRoots(&lol_visitor, VISIT_ALL); if (!AddRootRetainerIfFound(lol_visitor, filter, summary, @@ -1903,11 +1929,15 @@ MaybeObject* LiveObjectList::GetObjRetainers(int obj_id, int start, int dump_limit, Handle<JSObject> filter_obj) { - HandleScope scope; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + + HandleScope scope(isolate); // Get the target object. HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id)); - if (heap_obj == Heap::undefined_value()) { + if (heap_obj == heap->undefined_value()) { return heap_obj; } @@ -1915,7 +1945,7 @@ MaybeObject* LiveObjectList::GetObjRetainers(int obj_id, // Get the constructor function for context extension and arguments array. JSObject* arguments_boilerplate = - Top::context()->global_context()->arguments_boilerplate(); + isolate->context()->global_context()->arguments_boilerplate(); JSFunction* arguments_function = JSFunction::cast(arguments_boilerplate->map()->constructor()); @@ -1937,7 +1967,7 @@ MaybeObject* LiveObjectList::GetObjRetainers(int obj_id, // Set body.id. Handle<JSObject> body = Handle<JSObject>(JSObject::cast(body_obj)); - Handle<String> id_sym = Factory::LookupAsciiSymbol("id"); + Handle<String> id_sym = factory->LookupAsciiSymbol("id"); maybe_result = body->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE, @@ -1952,13 +1982,17 @@ MaybeObject* LiveObjectList::GetObjRetainers(int obj_id, Object* LiveObjectList::PrintObj(int obj_id) { Object* obj = GetObj(obj_id); if (!obj) { - return Heap::undefined_value(); + return HEAP->undefined_value(); } EmbeddedVector<char, 128> temp_filename; static int temp_count = 0; const char* path_prefix = "."; + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + if (FLAG_lol_workdir) { path_prefix = FLAG_lol_workdir; } @@ -1987,13 +2021,13 @@ Object* LiveObjectList::PrintObj(int obj_id) { if (resource->exists() && !resource->is_empty()) { ASSERT(resource->IsAscii()); Handle<String> dump_string = - Factory::NewExternalStringFromAscii(resource); - ExternalStringTable::AddString(*dump_string); + factory->NewExternalStringFromAscii(resource); + heap->external_string_table()->AddString(*dump_string); return *dump_string; } else { delete resource; } - return Heap::undefined_value(); + return HEAP->undefined_value(); } @@ -2081,6 +2115,10 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { FILE* f = OS::FOpen(temp_filename.start(), "w+"); + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + // Save the previous verbosity. bool prev_verbosity = FLAG_use_verbose_printer; FLAG_use_verbose_printer = false; @@ -2096,15 +2134,14 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { // Check for ObjectGroups that references this object. // TODO(mlam): refactor this to be more modular. { - List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups(); + List<ObjectGroup*>* groups = isolate->global_handles()->object_groups(); for (int i = 0; i < groups->length(); i++) { ObjectGroup* group = groups->at(i); if (group == NULL) continue; bool found_group = false; - List<Object**>& objects = group->objects_; - for (int j = 0; j < objects.length(); j++) { - Object* object = *objects[j]; + for (size_t j = 0; j < group->length_; j++) { + Object* object = *(group->objects_[j]); HeapObject* hobj = HeapObject::cast(object); if (obj2 == hobj) { found_group = true; @@ -2117,8 +2154,8 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { "obj %p is a member of object group %p {\n", reinterpret_cast<void*>(obj2), reinterpret_cast<void*>(group)); - for (int j = 0; j < objects.length(); j++) { - Object* object = *objects[j]; + for (size_t j = 0; j < group->length_; j++) { + Object* object = *(group->objects_[j]); if (!object->IsHeapObject()) continue; HeapObject* hobj = HeapObject::cast(object); @@ -2143,12 +2180,12 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { } PrintF(f, "path from roots to obj %p\n", reinterpret_cast<void*>(obj2)); - Heap::IterateRoots(&tracer, VISIT_ONLY_STRONG); + heap->IterateRoots(&tracer, VISIT_ONLY_STRONG); found = tracer.found(); if (!found) { PrintF(f, " No paths found. Checking symbol tables ...\n"); - SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table(); + SymbolTable* symbol_table = HEAP->raw_unchecked_symbol_table(); tracer.VisitPointers(reinterpret_cast<Object**>(&symbol_table), reinterpret_cast<Object**>(&symbol_table)+1); found = tracer.found(); @@ -2161,7 +2198,7 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { if (!found) { PrintF(f, " No paths found. Checking weak roots ...\n"); // Check weak refs next. - GlobalHandles::IterateWeakRoots(&tracer); + isolate->global_handles()->IterateWeakRoots(&tracer); found = tracer.found(); } @@ -2191,13 +2228,13 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { if (resource->exists() && !resource->is_empty()) { ASSERT(resource->IsAscii()); Handle<String> path_string = - Factory::NewExternalStringFromAscii(resource); - ExternalStringTable::AddString(*path_string); + factory->NewExternalStringFromAscii(resource); + heap->external_string_table()->AddString(*path_string); return *path_string; } else { delete resource; } - return Heap::undefined_value(); + return heap->undefined_value(); } @@ -2210,13 +2247,13 @@ Object* LiveObjectList::GetPath(int obj_id1, HeapObject* obj1 = NULL; if (obj_id1 != 0) { obj1 = HeapObject::cast(GetObj(obj_id1)); - if (obj1 == Heap::undefined_value()) { + if (obj1 == HEAP->undefined_value()) { return obj1; } } HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2)); - if (obj2 == Heap::undefined_value()) { + if (obj2 == HEAP->undefined_value()) { return obj2; } @@ -2570,12 +2607,13 @@ void LiveObjectList::Verify(bool match_heap_exactly) { void LiveObjectList::VerifyNotInFromSpace() { OS::Print("VerifyNotInFromSpace() ...\n"); LolIterator it(NULL, last()); + Heap* heap = ISOLATE->heap(); int i = 0; for (it.Init(); !it.Done(); it.Next()) { HeapObject* heap_obj = it.Obj(); - if (Heap::InFromSpace(heap_obj)) { + if (heap->InFromSpace(heap_obj)) { OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n", - i++, heap_obj, Heap::new_space()->FromSpaceLow()); + i++, heap_obj, heap->new_space()->FromSpaceLow()); } } } diff --git a/deps/v8/src/liveobjectlist.h b/deps/v8/src/liveobjectlist.h index 23e418d6d8..542482d9c5 100644 --- a/deps/v8/src/liveobjectlist.h +++ b/deps/v8/src/liveobjectlist.h @@ -237,10 +237,10 @@ class UpdateLiveObjectListVisitor: public ObjectVisitor { // to live new space objects, and not actually keep them alive. void UpdatePointer(Object** p) { Object* object = *p; - if (!Heap::InNewSpace(object)) return; + if (!HEAP->InNewSpace(object)) return; HeapObject* heap_obj = HeapObject::cast(object); - ASSERT(Heap::InFromSpace(heap_obj)); + ASSERT(HEAP->InFromSpace(heap_obj)); // We use the first word (where the map pointer usually is) of a heap // object to record the forwarding pointer. A forwarding pointer can diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index 6603185a32..58c9a53025 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -247,6 +247,7 @@ function FormatMessage(message) { strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"], strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"], strict_caller: ["Illegal access to a strict mode caller function."], + unprotected_let: ["Illegal let declaration in unprotected statement context."], }; } var message_type = %MessageGetType(message); diff --git a/deps/v8/src/mips/frames-mips.h b/deps/v8/src/mips/frames-mips.h index 2e720fb17e..8c605a39d9 100644 --- a/deps/v8/src/mips/frames-mips.h +++ b/deps/v8/src/mips/frames-mips.h @@ -121,10 +121,11 @@ static const int kSafepointRegisterStackIndexMap[kNumRegs] = { class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kStateOffset = 1 * kPointerSize; - static const int kFPOffset = 2 * kPointerSize; - static const int kPCOffset = 3 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kStateOffset = 1 * kPointerSize; + static const int kContextOffset = 2 * kPointerSize; + static const int kFPOffset = 3 * kPointerSize; + static const int kPCOffset = 4 * kPointerSize; static const int kSize = kPCOffset + kPointerSize; }; diff --git a/deps/v8/src/mips/full-codegen-mips.cc b/deps/v8/src/mips/full-codegen-mips.cc index 4943a03a1a..d8909c9ddd 100644 --- a/deps/v8/src/mips/full-codegen-mips.cc +++ b/deps/v8/src/mips/full-codegen-mips.cc @@ -4052,6 +4052,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, __ Branch(if_true, eq, v0, Operand(at)); __ LoadRoot(at, Heap::kFalseValueRootIndex); Split(eq, v0, Operand(at), if_true, if_false, fall_through); + } else if (FLAG_harmony_typeof && + check->Equals(isolate()->heap()->null_symbol())) { + __ LoadRoot(at, Heap::kNullValueRootIndex); + Split(eq, v0, Operand(at), if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->undefined_symbol())) { __ LoadRoot(at, Heap::kUndefinedValueRootIndex); __ Branch(if_true, eq, v0, Operand(at)); @@ -4069,8 +4073,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(v0, if_false); - __ LoadRoot(at, Heap::kNullValueRootIndex); - __ Branch(if_true, eq, v0, Operand(at)); + if (!FLAG_harmony_typeof) { + __ LoadRoot(at, Heap::kNullValueRootIndex); + __ Branch(if_true, eq, v0, Operand(at)); + } // Check for JS objects => true. __ GetObjectType(v0, v0, a1); __ Branch(if_false, lt, a1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); diff --git a/deps/v8/src/mips/macro-assembler-mips.cc b/deps/v8/src/mips/macro-assembler-mips.cc index 5e8d676ace..76b713f511 100644 --- a/deps/v8/src/mips/macro-assembler-mips.cc +++ b/deps/v8/src/mips/macro-assembler-mips.cc @@ -2244,7 +2244,13 @@ void MacroAssembler::DebugBreak() { void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. - ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); + // The return address is passed in register ra. if (try_location == IN_JAVASCRIPT) { if (type == TRY_CATCH_HANDLER) { @@ -2252,19 +2258,16 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } else { li(t0, Operand(StackHandler::TRY_FINALLY)); } - ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize - && StackHandlerConstants::kFPOffset == 2 * kPointerSize - && StackHandlerConstants::kPCOffset == 3 * kPointerSize - && StackHandlerConstants::kNextOffset == 0 * kPointerSize); // Save the current handler as the next handler. li(t2, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); lw(t1, MemOperand(t2)); addiu(sp, sp, -StackHandlerConstants::kSize); - sw(ra, MemOperand(sp, 12)); - sw(fp, MemOperand(sp, 8)); - sw(t0, MemOperand(sp, 4)); - sw(t1, MemOperand(sp, 0)); + sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset)); + sw(fp, MemOperand(sp, StackHandlerConstants::kFPOffset)); + sw(cp, MemOperand(sp, StackHandlerConstants::kContextOffset)); + sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset)); + sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset)); // Link this handler as the new current one. sw(sp, MemOperand(t2)); @@ -2272,11 +2275,6 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } else { // Must preserve a0-a3, and s0 (argv). ASSERT(try_location == IN_JS_ENTRY); - ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize - && StackHandlerConstants::kFPOffset == 2 * kPointerSize - && StackHandlerConstants::kPCOffset == 3 * kPointerSize - && StackHandlerConstants::kNextOffset == 0 * kPointerSize); - // The frame pointer does not point to a JS frame so we save NULL // for fp. We expect the code throwing an exception to check fp // before dereferencing it to restore the context. @@ -2286,11 +2284,14 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, li(t2, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); lw(t1, MemOperand(t2)); + ASSERT(Smi::FromInt(0) == 0); // Used for no context. + addiu(sp, sp, -StackHandlerConstants::kSize); - sw(ra, MemOperand(sp, 12)); - sw(zero_reg, MemOperand(sp, 8)); - sw(t0, MemOperand(sp, 4)); - sw(t1, MemOperand(sp, 0)); + sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset)); + sw(zero_reg, MemOperand(sp, StackHandlerConstants::kFPOffset)); + sw(zero_reg, MemOperand(sp, StackHandlerConstants::kContextOffset)); + sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset)); + sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset)); // Link this handler as the new current one. sw(sp, MemOperand(t2)); @@ -2299,7 +2300,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PopTryHandler() { - ASSERT_EQ(0, StackHandlerConstants::kNextOffset); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(a1); Addu(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize)); li(at, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); @@ -2312,28 +2313,31 @@ void MacroAssembler::Throw(Register value) { Move(v0, value); // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // Drop the sp to the top of the handler. li(a3, Operand(ExternalReference(Isolate::k_handler_address, - isolate()))); + isolate()))); lw(sp, MemOperand(a3)); - // Restore the next handler and frame pointer, discard handler state. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + // Restore the next handler. pop(a2); sw(a2, MemOperand(a3)); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - MultiPop(a3.bit() | fp.bit()); - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - // Set cp to NULL if fp is NULL. + // Restore context and frame pointer, discard state (a3). + MultiPop(a3.bit() | cp.bit() | fp.bit()); + + // If the handler is a JS frame, restore the context to the frame. + // (a3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any + // of them. Label done; - Branch(USE_DELAY_SLOT, &done, eq, fp, Operand(zero_reg)); - mov(cp, zero_reg); // In branch delay slot. - lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + Branch(&done, eq, fp, Operand(zero_reg)); + sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); bind(&done); #ifdef DEBUG @@ -2355,7 +2359,6 @@ void MacroAssembler::Throw(Register value) { } #endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); pop(t9); // 2 instructions: lw, add sp. Jump(t9); // 2 instructions: jr, nop (in delay slot). @@ -2370,7 +2373,12 @@ void MacroAssembler::Throw(Register value) { void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Register value) { // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // v0 is expected to hold the exception. Move(v0, value); @@ -2393,7 +2401,6 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(a2); sw(a2, MemOperand(a3)); @@ -2415,20 +2422,12 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Stack layout at this point. See also StackHandlerConstants. // sp -> state (ENTRY) + // cp // fp // ra - // Discard handler state (a2 is not used) and restore frame pointer. - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - MultiPop(a2.bit() | fp.bit()); // a2: discarded state. - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - Label cp_null; - Branch(USE_DELAY_SLOT, &cp_null, eq, fp, Operand(zero_reg)); - mov(cp, zero_reg); // In the branch delay slot. - lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - bind(&cp_null); + // Restore context and frame pointer, discard state (r2). + MultiPop(a2.bit() | cp.bit() | fp.bit()); #ifdef DEBUG // When emitting debug_code, set ra as return address for the jump. @@ -2448,7 +2447,6 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, addiu(ra, ra, kOffsetRaBytes); } #endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); pop(t9); // 2 instructions: lw, add sp. Jump(t9); // 2 instructions: jr, nop (in delay slot). diff --git a/deps/v8/src/mips/stub-cache-mips.cc b/deps/v8/src/mips/stub-cache-mips.cc index 84ff991ce2..ec63551b1c 100644 --- a/deps/v8/src/mips/stub-cache-mips.cc +++ b/deps/v8/src/mips/stub-cache-mips.cc @@ -3494,7 +3494,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset)); __ sra(t2, key, kSmiTagSize); // Unsigned comparison catches both negative and too-large values. - __ Branch(&miss_force_generic, Uless, t1, Operand(t2)); + __ Branch(&miss_force_generic, Ugreater_equal, key, Operand(t1)); __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset)); // a3: base pointer of external storage @@ -3822,16 +3822,16 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. - __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset)); - - // Check that the key is a smi. + // Check that the key is a smi. __ JumpIfNotSmi(key, &miss_force_generic); + __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset)); + // Check that the index is in range. __ SmiUntag(t0, key); __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. - __ Branch(&miss_force_generic, Ugreater_equal, t0, Operand(t1)); + __ Branch(&miss_force_generic, Ugreater_equal, key, Operand(t1)); // Handle both smis and HeapNumbers in the fast path. Go to the // runtime for all other kinds of values. diff --git a/deps/v8/src/mirror-debugger.js b/deps/v8/src/mirror-debugger.js index bad08002d7..e3f3c48bb5 100644 --- a/deps/v8/src/mirror-debugger.js +++ b/deps/v8/src/mirror-debugger.js @@ -195,7 +195,8 @@ ScopeType = { Global: 0, Local: 1, With: 2, Closure: 3, - Catch: 4 }; + Catch: 4, + Block: 5 }; // Mirror hierarchy: diff --git a/deps/v8/src/mksnapshot.cc b/deps/v8/src/mksnapshot.cc index c5ce12f0ec..4f5fe96a90 100644 --- a/deps/v8/src/mksnapshot.cc +++ b/deps/v8/src/mksnapshot.cc @@ -40,8 +40,6 @@ #include "serialize.h" #include "list.h" -// use explicit namespace to avoid clashing with types in namespace v8 -namespace i = v8::internal; using namespace v8; static const unsigned int kMaxCounters = 256; diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 70ed47be1f..c5fda89e42 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -552,7 +552,8 @@ bool Object::IsContext() { return (map == heap->function_context_map() || map == heap->catch_context_map() || map == heap->with_context_map() || - map == heap->global_context_map()); + map == heap->global_context_map() || + map == heap->block_context_map()); } return false; } @@ -565,6 +566,13 @@ bool Object::IsGlobalContext() { } +bool Object::IsSerializedScopeInfo() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map() == + HeapObject::cast(this)->GetHeap()->serialized_scope_info_map(); +} + + bool Object::IsJSFunction() { return Object::IsHeapObject() && HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_TYPE; @@ -1335,14 +1343,14 @@ int HeapNumber::get_sign() { ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset) -HeapObject* JSObject::elements() { +FixedArrayBase* JSObject::elements() { Object* array = READ_FIELD(this, kElementsOffset); ASSERT(array->HasValidElements()); - return reinterpret_cast<HeapObject*>(array); + return static_cast<FixedArrayBase*>(array); } -void JSObject::set_elements(HeapObject* value, WriteBarrierMode mode) { +void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) { ASSERT(map()->has_fast_elements() == (value->map() == GetHeap()->fixed_array_map() || value->map() == GetHeap()->fixed_cow_array_map())); @@ -2114,12 +2122,6 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) { SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset) -SMI_ACCESSORS(ByteArray, length, kLengthOffset) - -// TODO(1493): Investigate if it's possible to s/INT/SMI/ here (and -// subsequently unify H{Fixed,External}ArrayLength). -INT_ACCESSORS(ExternalArray, length, kLengthOffset) - SMI_ACCESSORS(String, length, kLengthOffset) diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index e35274d0aa..337dd65db0 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -2318,7 +2318,8 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( if (has_exception) return Failure::Exception(); Object* bool_result = result->ToBoolean(); - if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) { + if (mode == STRICT_DELETION && + bool_result == isolate->heap()->false_value()) { Handle<Object> args[] = { handler, trap_name }; Handle<Object> error = isolate->factory()->NewTypeError( "handler_failed", HandleVector(args, ARRAY_SIZE(args))); @@ -3167,9 +3168,10 @@ MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) { ASSERT(result->IsBoolean()); return *v8::Utils::OpenHandle(*result); } - MaybeObject* raw_result = GetElementsAccessor()->Delete(*this_handle, - index, - NORMAL_DELETION); + MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete( + *this_handle, + index, + NORMAL_DELETION); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return raw_result; } @@ -4637,7 +4639,20 @@ MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps, MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) { - return array->GetElementsAccessor()->AddJSArrayKeysToFixedArray(array, this); + ElementsAccessor* accessor = array->GetElementsAccessor(); + MaybeObject* maybe_result = + accessor->AddElementsToFixedArray(array->elements(), this); + FixedArray* result; + if (!maybe_result->To<FixedArray>(&result)) return maybe_result; +#ifdef DEBUG + if (FLAG_enable_slow_asserts) { + for (int i = 0; i < result->length(); i++) { + Object* current = result->get(i); + ASSERT(current->IsNumber() || current->IsString()); + } + } +#endif + return result; } @@ -6961,12 +6976,16 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count); if (0 == deopt_count) return; - PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", "commands"); + PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", + FLAG_print_code_verbose ? "commands" : ""); for (int i = 0; i < deopt_count; i++) { PrintF(out, "%6d %6d %6d", i, AstId(i)->value(), ArgumentsStackHeight(i)->value()); - if (!FLAG_print_code_verbose) continue; + if (!FLAG_print_code_verbose) { + PrintF(out, "\n"); + continue; + } // Print details of the frame translation. int translation_index = TranslationIndex(i)->value(); TranslationIterator iterator(TranslationByteArray(), translation_index); @@ -8403,14 +8422,14 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, return isolate->Throw(*error); } } - Object* new_dictionary; + FixedArrayBase* new_dictionary; MaybeObject* maybe = dictionary->AtNumberPut(index, value); - if (!maybe->ToObject(&new_dictionary)) return maybe; + if (!maybe->To<FixedArrayBase>(&new_dictionary)) return maybe; if (dictionary != NumberDictionary::cast(new_dictionary)) { if (is_arguments) { elements->set(1, new_dictionary); } else { - set_elements(HeapObject::cast(new_dictionary)); + set_elements(new_dictionary); } dictionary = NumberDictionary::cast(new_dictionary); } diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index ff13aedcce..93f7a1d119 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -62,17 +62,8 @@ // - JSMessageObject // - JSProxy // - JSFunctionProxy -// - ByteArray -// - ExternalArray -// - ExternalPixelArray -// - ExternalByteArray -// - ExternalUnsignedByteArray -// - ExternalShortArray -// - ExternalUnsignedShortArray -// - ExternalIntArray -// - ExternalUnsignedIntArray -// - ExternalFloatArray // - FixedArrayBase +// - ByteArray // - FixedArray // - DescriptorArray // - HashTable @@ -85,6 +76,15 @@ // - JSFunctionResultCache // - SerializedScopeInfo // - FixedDoubleArray +// - ExternalArray +// - ExternalPixelArray +// - ExternalByteArray +// - ExternalUnsignedByteArray +// - ExternalShortArray +// - ExternalUnsignedShortArray +// - ExternalIntArray +// - ExternalUnsignedIntArray +// - ExternalFloatArray // - String // - SeqString // - SeqAsciiString @@ -322,6 +322,7 @@ static const int kVariableSizeSentinel = 0; V(POLYMORPHIC_CODE_CACHE_TYPE) \ \ V(FIXED_ARRAY_TYPE) \ + V(FIXED_DOUBLE_ARRAY_TYPE) \ V(SHARED_FUNCTION_INFO_TYPE) \ \ V(JS_MESSAGE_OBJECT_TYPE) \ @@ -635,10 +636,11 @@ enum CompareResult { WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \ +class DictionaryElementsAccessor; class ElementsAccessor; -class StringStream; +class FixedArrayBase; class ObjectVisitor; -class DictionaryElementsAccessor; +class StringStream; struct ValueInfo : public Malloced { ValueInfo() : type(FIRST_TYPE), ptr(NULL), str(NULL), number(0) { } @@ -743,6 +745,7 @@ class MaybeObject BASE_EMBEDDED { V(FixedDoubleArray) \ V(Context) \ V(GlobalContext) \ + V(SerializedScopeInfo) \ V(JSFunction) \ V(Code) \ V(Oddball) \ @@ -1492,7 +1495,7 @@ class JSObject: public JSReceiver { // In the slow mode the elements is either a NumberDictionary, an // ExternalArray, or a FixedArray parameter map for a (non-strict) // arguments object. - DECL_ACCESSORS(elements, HeapObject) + DECL_ACCESSORS(elements, FixedArrayBase) inline void initialize_elements(); MUST_USE_RESULT inline MaybeObject* ResetElements(); inline ElementsKind GetElementsKind(); @@ -2084,6 +2087,7 @@ class FixedArrayBase: public HeapObject { static const int kHeaderSize = kLengthOffset + kPointerSize; }; + class FixedDoubleArray; // FixedArray describes fixed-sized arrays with element type Object*. @@ -3053,12 +3057,8 @@ class NormalizedMapCache: public FixedArray { // ByteArray represents fixed sized byte arrays. Used by the outside world, // such as PCRE, and also by the memory allocator and garbage collector to // fill in free blocks in the heap. -class ByteArray: public HeapObject { +class ByteArray: public FixedArrayBase { public: - // [length]: length of the array. - inline int length(); - inline void set_length(int value); - // Setter and getter. inline byte get(int index); inline void set(int index, byte value); @@ -3103,10 +3103,6 @@ class ByteArray: public HeapObject { #endif // Layout description. - // Length is smi tagged when it is stored. - static const int kLengthOffset = HeapObject::kHeaderSize; - static const int kHeaderSize = kLengthOffset + kPointerSize; - static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize); // Maximal memory consumption for a single ByteArray. @@ -3130,11 +3126,8 @@ class ByteArray: public HeapObject { // Out-of-range values passed to the setter are converted via a C // cast, not clamping. Out-of-range indices cause exceptions to be // raised rather than being silently ignored. -class ExternalArray: public HeapObject { +class ExternalArray: public FixedArrayBase { public: - // [length]: length of the array. - inline int length(); - inline void set_length(int value); inline bool is_the_hole(int index) { return false; } @@ -3149,9 +3142,8 @@ class ExternalArray: public HeapObject { static const int kMaxLength = 0x3fffffff; // ExternalArray headers are not quadword aligned. - static const int kLengthOffset = HeapObject::kHeaderSize; static const int kExternalPointerOffset = - POINTER_SIZE_ALIGN(kLengthOffset + kIntSize); + POINTER_SIZE_ALIGN(FixedArrayBase::kLengthOffset + kPointerSize); static const int kHeaderSize = kExternalPointerOffset + kPointerSize; static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize); @@ -4444,6 +4436,7 @@ class Script: public Struct { #define FUNCTIONS_WITH_ID_LIST(V) \ V(Array.prototype, push, ArrayPush) \ V(Array.prototype, pop, ArrayPop) \ + V(Function.prototype, apply, FunctionApply) \ V(String.prototype, charCodeAt, StringCharCodeAt) \ V(String.prototype, charAt, StringCharAt) \ V(String, fromCharCode, StringFromCharCode) \ diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index f32e9177b5..844dd7060c 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -584,7 +584,8 @@ Parser::Parser(Handle<Script> script, pre_data_(pre_data), fni_(NULL), stack_overflow_(false), - parenthesized_function_(false) { + parenthesized_function_(false), + harmony_block_scoping_(false) { AstNode::ResetIds(); } @@ -809,6 +810,10 @@ void Parser::ReportMessageAt(Scanner::Location source_location, isolate()->Throw(*result, &location); } +void Parser::SetHarmonyBlockScoping(bool block_scoping) { + scanner().SetHarmonyBlockScoping(block_scoping); + harmony_block_scoping_ = block_scoping; +} // Base class containing common code for the different finder classes used by // the parser. @@ -1089,6 +1094,25 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder { }; +Statement* Parser::ParseSourceElement(ZoneStringList* labels, + bool* ok) { + if (peek() == Token::FUNCTION) { + // FunctionDeclaration is only allowed in the context of SourceElements + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // Common language extension is to allow function declaration in place + // of any statement. This language extension is disabled in strict mode. + return ParseFunctionDeclaration(ok); + } else if (peek() == Token::LET) { + return ParseVariableStatement(kSourceElement, ok); + } else { + return ParseStatement(labels, ok); + } +} + + void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, int end_token, bool* ok) { @@ -1112,21 +1136,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, } Scanner::Location token_loc = scanner().peek_location(); - - Statement* stat; - if (peek() == Token::FUNCTION) { - // FunctionDeclaration is only allowed in the context of SourceElements - // (Ecma 262 5th Edition, clause 14): - // SourceElement: - // Statement - // FunctionDeclaration - // Common language extension is to allow function declaration in place - // of any statement. This language extension is disabled in strict mode. - stat = ParseFunctionDeclaration(CHECK_OK); - } else { - stat = ParseStatement(NULL, CHECK_OK); - } - + Statement* stat = ParseSourceElement(NULL, CHECK_OK); if (stat == NULL || stat->IsEmpty()) { directive_prologue = false; // End of directive prologue. continue; @@ -1214,7 +1224,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { case Token::CONST: // fall through case Token::VAR: - stmt = ParseVariableStatement(ok); + stmt = ParseVariableStatement(kStatement, ok); break; case Token::SEMICOLON: @@ -1309,9 +1319,9 @@ VariableProxy* Parser::Declare(Handle<String> name, bool resolve, bool* ok) { Variable* var = NULL; - // If we are inside a function, a declaration of a variable - // is a truly local variable, and the scope of the variable - // is always the function scope. + // If we are inside a function, a declaration of a var/const variable is a + // truly local variable, and the scope of the variable is always the function + // scope. // If a function scope exists, then we can statically declare this // variable and also set its mode. In any case, a Declaration node @@ -1321,24 +1331,28 @@ VariableProxy* Parser::Declare(Handle<String> name, // to the calling function context. // Similarly, strict mode eval scope does not leak variable declarations to // the caller's scope so we declare all locals, too. - Scope* declaration_scope = top_scope_->DeclarationScope(); + + Scope* declaration_scope = mode == Variable::LET ? top_scope_ + : top_scope_->DeclarationScope(); if (declaration_scope->is_function_scope() || - declaration_scope->is_strict_mode_eval_scope()) { + declaration_scope->is_strict_mode_eval_scope() || + declaration_scope->is_block_scope()) { // Declare the variable in the function scope. var = declaration_scope->LocalLookup(name); if (var == NULL) { // Declare the name. var = declaration_scope->DeclareLocal(name, mode); } else { - // The name was declared before; check for conflicting - // re-declarations. If the previous declaration was a const or the - // current declaration is a const then we have a conflict. There is - // similar code in runtime.cc in the Declare functions. - if ((mode == Variable::CONST) || (var->mode() == Variable::CONST)) { - // We only have vars and consts in declarations. + // The name was declared before; check for conflicting re-declarations. + // We have a conflict if either of the declarations is not a var. There + // is similar code in runtime.cc in the Declare functions. + if ((mode != Variable::VAR) || (var->mode() != Variable::VAR)) { + // We only have vars, consts and lets in declarations. ASSERT(var->mode() == Variable::VAR || - var->mode() == Variable::CONST); - const char* type = (var->mode() == Variable::VAR) ? "var" : "const"; + var->mode() == Variable::CONST || + var->mode() == Variable::LET); + const char* type = (var->mode() == Variable::VAR) ? "var" : + (var->mode() == Variable::CONST) ? "const" : "let"; Handle<String> type_string = isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED); Expression* expression = @@ -1481,12 +1495,15 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) { // Even if we're not at the top-level of the global or a function // scope, we treat is as such and introduce the function with it's // initial value upon entering the corresponding scope. - Declare(name, Variable::VAR, fun, true, CHECK_OK); + Variable::Mode mode = harmony_block_scoping_ ? Variable::LET : Variable::VAR; + Declare(name, mode, fun, true, CHECK_OK); return EmptyStatement(); } Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { + if (harmony_block_scoping_) return ParseScopedBlock(labels, ok); + // Block :: // '{' Statement* '}' @@ -1510,12 +1527,65 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { } -Block* Parser::ParseVariableStatement(bool* ok) { +Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { + // Construct block expecting 16 statements. + Block* body = new(zone()) Block(isolate(), labels, 16, false); + Scope* saved_scope = top_scope_; + Scope* block_scope = NewScope(top_scope_, + Scope::BLOCK_SCOPE, + inside_with()); + body->set_block_scope(block_scope); + block_scope->DeclareLocal(isolate()->factory()->block_scope_symbol(), + Variable::VAR); + if (top_scope_->is_strict_mode()) { + block_scope->EnableStrictMode(); + } + top_scope_ = block_scope; + + // Parse the statements and collect escaping labels. + TargetCollector collector; + Target target(&this->target_stack_, &collector); + Expect(Token::LBRACE, CHECK_OK); + { + Target target_body(&this->target_stack_, body); + InitializationBlockFinder block_finder(top_scope_, target_stack_); + + while (peek() != Token::RBRACE) { + Statement* stat = ParseSourceElement(NULL, CHECK_OK); + if (stat && !stat->IsEmpty()) { + body->AddStatement(stat); + block_finder.Update(stat); + } + } + } + Expect(Token::RBRACE, CHECK_OK); + + // Create exit block. + Block* exit = new(zone()) Block(isolate(), NULL, 1, false); + exit->AddStatement(new(zone()) ExitContextStatement()); + + // Create a try-finally statement. + TryFinallyStatement* try_finally = + new(zone()) TryFinallyStatement(body, exit); + try_finally->set_escaping_targets(collector.targets()); + top_scope_ = saved_scope; + + // Create a result block. + Block* result = new(zone()) Block(isolate(), NULL, 1, false); + result->AddStatement(try_finally); + return result; +} + + +Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context, + bool* ok) { // VariableStatement :: // VariableDeclarations ';' Handle<String> ignore; - Block* result = ParseVariableDeclarations(true, &ignore, CHECK_OK); + Block* result = ParseVariableDeclarations(var_context, + &ignore, + CHECK_OK); ExpectSemicolon(CHECK_OK); return result; } @@ -1532,7 +1602,7 @@ bool Parser::IsEvalOrArguments(Handle<String> string) { // *var is untouched; in particular, it is the caller's responsibility // to initialize it properly. This mechanism is used for the parsing // of 'for-in' loops. -Block* Parser::ParseVariableDeclarations(bool accept_IN, +Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, Handle<String>* out, bool* ok) { // VariableDeclarations :: @@ -1540,25 +1610,36 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, Variable::Mode mode = Variable::VAR; bool is_const = false; - Scope* declaration_scope = top_scope_->DeclarationScope(); if (peek() == Token::VAR) { Consume(Token::VAR); } else if (peek() == Token::CONST) { Consume(Token::CONST); - if (declaration_scope->is_strict_mode()) { + if (top_scope_->is_strict_mode()) { ReportMessage("strict_const", Vector<const char*>::empty()); *ok = false; return NULL; } mode = Variable::CONST; is_const = true; + } else if (peek() == Token::LET) { + Consume(Token::LET); + if (var_context != kSourceElement && + var_context != kForStatement) { + ASSERT(var_context == kStatement); + ReportMessage("unprotected_let", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + mode = Variable::LET; } else { UNREACHABLE(); // by current callers } - // The scope of a variable/const declared anywhere inside a function + Scope* declaration_scope = mode == Variable::LET + ? top_scope_ : top_scope_->DeclarationScope(); + // The scope of a var/const declared variable anywhere inside a function // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can - // transform a source-level variable/const declaration into a (Function) + // transform a source-level var/const declaration into a (Function) // Scope declaration, and rewrite the source-level initialization into an // assignment statement. We use a block to collect multiple assignments. // @@ -1642,7 +1723,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, if (peek() == Token::ASSIGN) { Expect(Token::ASSIGN, CHECK_OK); position = scanner().location().beg_pos; - value = ParseAssignmentExpression(accept_IN, CHECK_OK); + value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); // Don't infer if it is "a = function(){...}();"-like expression. if (fni_ != NULL && value->AsCall() == NULL && @@ -1956,41 +2037,6 @@ Statement* Parser::ParseReturnStatement(bool* ok) { } -Block* Parser::WithHelper(Expression* obj, ZoneStringList* labels, bool* ok) { - // Parse the statement and collect escaping labels. - TargetCollector collector; - Statement* stat; - { Target target(&this->target_stack_, &collector); - with_nesting_level_++; - top_scope_->DeclarationScope()->RecordWithStatement(); - stat = ParseStatement(labels, CHECK_OK); - with_nesting_level_--; - } - // Create resulting block with two statements. - // 1: Evaluate the with expression. - // 2: The try-finally block evaluating the body. - Block* result = new(zone()) Block(isolate(), NULL, 2, false); - - if (result != NULL) { - result->AddStatement(new(zone()) EnterWithContextStatement(obj)); - - // Create body block. - Block* body = new(zone()) Block(isolate(), NULL, 1, false); - body->AddStatement(stat); - - // Create exit block. - Block* exit = new(zone()) Block(isolate(), NULL, 1, false); - exit->AddStatement(new(zone()) ExitContextStatement()); - - // Return a try-finally statement. - TryFinallyStatement* wrapper = new(zone()) TryFinallyStatement(body, exit); - wrapper->set_escaping_targets(collector.targets()); - result->AddStatement(wrapper); - } - return result; -} - - Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { // WithStatement :: // 'with' '(' Expression ')' Statement @@ -2007,7 +2053,11 @@ Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { Expression* expr = ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - return WithHelper(expr, labels, CHECK_OK); + ++with_nesting_level_; + top_scope_->DeclarationScope()->RecordWithStatement(); + Statement* stmt = ParseStatement(labels, CHECK_OK); + --with_nesting_level_; + return new(zone()) WithStatement(expr, stmt); } @@ -2142,39 +2192,22 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Expect(Token::RPAREN, CHECK_OK); if (peek() == Token::LBRACE) { - // Rewrite the catch body B to a single statement block - // { try B finally { PopContext }}. - Block* inner_body; - // We need to collect escapes from the body for both the inner - // try/finally used to pop the catch context and any possible outer - // try/finally. - TargetCollector inner_collector; - { Target target(&this->target_stack_, &catch_collector); - { Target target(&this->target_stack_, &inner_collector); - catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); - if (top_scope_->is_strict_mode()) { - catch_scope->EnableStrictMode(); - } - catch_variable = catch_scope->DeclareLocal(name, Variable::VAR); - - Scope* saved_scope = top_scope_; - top_scope_ = catch_scope; - inner_body = ParseBlock(NULL, CHECK_OK); - top_scope_ = saved_scope; - } + // Rewrite the catch body { B } to a block: + // { { B } ExitContext; }. + Target target(&this->target_stack_, &catch_collector); + catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); + if (top_scope_->is_strict_mode()) { + catch_scope->EnableStrictMode(); } - - // Create exit block. - Block* inner_finally = new(zone()) Block(isolate(), NULL, 1, false); - inner_finally->AddStatement(new(zone()) ExitContextStatement()); - - // Create a try/finally statement. - TryFinallyStatement* inner_try_finally = - new(zone()) TryFinallyStatement(inner_body, inner_finally); - inner_try_finally->set_escaping_targets(inner_collector.targets()); - - catch_block = new(zone()) Block(isolate(), NULL, 1, false); - catch_block->AddStatement(inner_try_finally); + catch_variable = catch_scope->DeclareLocal(name, Variable::VAR); + catch_block = new(zone()) Block(isolate(), NULL, 2, false); + + Scope* saved_scope = top_scope_; + top_scope_ = catch_scope; + Block* catch_body = ParseBlock(NULL, CHECK_OK); + top_scope_ = saved_scope; + catch_block->AddStatement(catch_body); + catch_block->AddStatement(new(zone()) ExitContextStatement()); } else { Expect(Token::LBRACE, CHECK_OK); } @@ -2290,7 +2323,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { if (peek() == Token::VAR || peek() == Token::CONST) { Handle<String> name; Block* variable_statement = - ParseVariableDeclarations(false, &name, CHECK_OK); + ParseVariableDeclarations(kForStatement, &name, CHECK_OK); if (peek() == Token::IN && !name.is_null()) { VariableProxy* each = top_scope_->NewUnresolved(name, inside_with()); @@ -3649,8 +3682,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, } int num_parameters = 0; - // Function declarations are hoisted. - Scope* scope = (type == FunctionLiteral::DECLARATION) + // Function declarations are function scoped in normal mode, so they are + // hoisted. In harmony block scoping mode they are block scoped, so they + // are not hoisted. + Scope* scope = (type == FunctionLiteral::DECLARATION && + !harmony_block_scoping_) ? NewScope(top_scope_->DeclarationScope(), Scope::FUNCTION_SCOPE, false) : NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8); @@ -3952,7 +3988,7 @@ Literal* Parser::GetLiteralNumber(double value) { } -// Parses and identifier that is valid for the current scope, in particular it +// Parses an identifier that is valid for the current scope, in particular it // fails on strict mode future reserved keywords in a strict scope. Handle<String> Parser::ParseIdentifier(bool* ok) { if (top_scope_->is_strict_mode()) { @@ -5033,9 +5069,11 @@ int ScriptDataImpl::ReadNumber(byte** source) { // Create a Scanner for the preparser to use as input, and preparse the source. static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, bool allow_lazy, - ParserRecorder* recorder) { + ParserRecorder* recorder, + bool harmony_block_scoping) { Isolate* isolate = Isolate::Current(); JavaScriptScanner scanner(isolate->unicode_cache()); + scanner.SetHarmonyBlockScoping(harmony_block_scoping); scanner.Initialize(source); intptr_t stack_limit = isolate->stack_guard()->real_climit(); if (!preparser::PreParser::PreParseProgram(&scanner, @@ -5056,7 +5094,8 @@ static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, - v8::Extension* extension) { + v8::Extension* extension, + bool harmony_block_scoping) { bool allow_lazy = FLAG_lazy && (extension == NULL); if (!allow_lazy) { // Partial preparsing is only about lazily compiled functions. @@ -5064,16 +5103,17 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, return NULL; } PartialParserRecorder recorder; - return DoPreParse(source, allow_lazy, &recorder); + return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping); } ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source, - v8::Extension* extension) { + v8::Extension* extension, + bool harmony_block_scoping) { Handle<Script> no_script; bool allow_lazy = FLAG_lazy && (extension == NULL); CompleteParserRecorder recorder; - return DoPreParse(source, allow_lazy, &recorder); + return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping); } @@ -5103,15 +5143,22 @@ bool ParserApi::Parse(CompilationInfo* info) { ASSERT(info->function() == NULL); FunctionLiteral* result = NULL; Handle<Script> script = info->script(); + bool harmony_block_scoping = !info->is_native() && + FLAG_harmony_block_scoping; if (info->is_lazy()) { Parser parser(script, true, NULL, NULL); + parser.SetHarmonyBlockScoping(harmony_block_scoping); result = parser.ParseLazy(info); } else { // Whether we allow %identifier(..) syntax. bool allow_natives_syntax = info->allows_natives_syntax() || FLAG_allow_natives_syntax; ScriptDataImpl* pre_data = info->pre_parse_data(); - Parser parser(script, allow_natives_syntax, info->extension(), pre_data); + Parser parser(script, + allow_natives_syntax, + info->extension(), + pre_data); + parser.SetHarmonyBlockScoping(harmony_block_scoping); if (pre_data != NULL && pre_data->has_error()) { Scanner::Location loc = pre_data->MessageLocation(); const char* message = pre_data->BuildMessage(); @@ -5130,7 +5177,6 @@ bool ParserApi::Parse(CompilationInfo* info) { info->StrictMode()); } } - info->SetFunction(result); return (result != NULL); } diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 535b63945c..686dac85af 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -164,12 +164,14 @@ class ParserApi { // Generic preparser generating full preparse data. static ScriptDataImpl* PreParse(UC16CharacterStream* source, - v8::Extension* extension); + v8::Extension* extension, + bool harmony_block_scoping); // Preparser that only does preprocessing that makes sense if only used // immediately after. static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source, - v8::Extension* extension); + v8::Extension* extension, + bool harmony_block_scoping); }; // ---------------------------------------------------------------------------- @@ -435,6 +437,7 @@ class Parser { void ReportMessageAt(Scanner::Location loc, const char* message, Vector<Handle<String> > args); + void SetHarmonyBlockScoping(bool block_scoping); private: // Limit on number of function parameters is chosen arbitrarily. @@ -451,6 +454,12 @@ class Parser { PARSE_EAGERLY }; + enum VariableDeclarationContext { + kSourceElement, + kStatement, + kForStatement + }; + Isolate* isolate() { return isolate_; } Zone* zone() { return isolate_->zone(); } @@ -479,12 +488,15 @@ class Parser { // for failure at the call sites. void* ParseSourceElements(ZoneList<Statement*>* processor, int end_token, bool* ok); + Statement* ParseSourceElement(ZoneStringList* labels, bool* ok); Statement* ParseStatement(ZoneStringList* labels, bool* ok); Statement* ParseFunctionDeclaration(bool* ok); Statement* ParseNativeDeclaration(bool* ok); Block* ParseBlock(ZoneStringList* labels, bool* ok); - Block* ParseVariableStatement(bool* ok); - Block* ParseVariableDeclarations(bool accept_IN, + Block* ParseScopedBlock(ZoneStringList* labels, bool* ok); + Block* ParseVariableStatement(VariableDeclarationContext var_context, + bool* ok); + Block* ParseVariableDeclarations(VariableDeclarationContext var_context, Handle<String>* out, bool* ok); Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels, @@ -493,7 +505,6 @@ class Parser { Statement* ParseContinueStatement(bool* ok); Statement* ParseBreakStatement(ZoneStringList* labels, bool* ok); Statement* ParseReturnStatement(bool* ok); - Block* WithHelper(Expression* obj, ZoneStringList* labels, bool* ok); Statement* ParseWithStatement(ZoneStringList* labels, bool* ok); CaseClause* ParseCaseClause(bool* default_seen_ptr, bool* ok); SwitchStatement* ParseSwitchStatement(ZoneStringList* labels, bool* ok); @@ -715,6 +726,7 @@ class Parser { // Heuristically that means that the function will be called immediately, // so never lazily compile it. bool parenthesized_function_; + bool harmony_block_scoping_; friend class LexicalScope; }; diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index 37330be82b..362bf47cc1 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -676,9 +676,11 @@ static void* ThreadEntry(void* arg) { // This is also initialized by the first argument to pthread_create() but we // don't know which thread will run first (the original thread or the new // one) so we initialize it here too. +#ifdef PR_SET_NAME prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(thread->name()), // NOLINT 0, 0, 0); +#endif thread->data()->thread_ = pthread_self(); ASSERT(thread->data()->thread_ != kNoThread); thread->Run(); diff --git a/deps/v8/src/preparser-api.cc b/deps/v8/src/preparser-api.cc index e0ab5001f5..80656d5d12 100644 --- a/deps/v8/src/preparser-api.cc +++ b/deps/v8/src/preparser-api.cc @@ -28,6 +28,7 @@ #include "../include/v8-preparser.h" #include "globals.h" +#include "flags.h" #include "checks.h" #include "allocation.h" #include "utils.h" diff --git a/deps/v8/src/preparser.cc b/deps/v8/src/preparser.cc index c741b4655a..1a3dd737c5 100644 --- a/deps/v8/src/preparser.cc +++ b/deps/v8/src/preparser.cc @@ -56,8 +56,6 @@ namespace preparser { // That means that contextual checks (like a label being declared where // it is used) are generally omitted. -namespace i = ::v8::internal; - void PreParser::ReportUnexpectedToken(i::Token::Value token) { // We don't report stack overflows here, to avoid increasing the // stack depth even further. Instead we report it after parsing is @@ -114,6 +112,16 @@ void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) { #undef DUMMY +PreParser::Statement PreParser::ParseSourceElement(bool* ok) { + switch (peek()) { + case i::Token::LET: + return ParseVariableStatement(kSourceElement, ok); + default: + return ParseStatement(ok); + } +} + + PreParser::SourceElements PreParser::ParseSourceElements(int end_token, bool* ok) { // SourceElements :: @@ -121,7 +129,7 @@ PreParser::SourceElements PreParser::ParseSourceElements(int end_token, bool allow_directive_prologue = true; while (peek() != end_token) { - Statement statement = ParseStatement(CHECK_OK); + Statement statement = ParseSourceElement(CHECK_OK); if (allow_directive_prologue) { if (statement.IsUseStrictLiteral()) { set_strict_mode(); @@ -174,7 +182,7 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { case i::Token::CONST: case i::Token::VAR: - return ParseVariableStatement(ok); + return ParseVariableStatement(kStatement, ok); case i::Token::SEMICOLON: Next(); @@ -260,7 +268,7 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) { Expect(i::Token::LBRACE, CHECK_OK); while (peek() != i::Token::RBRACE) { i::Scanner::Location start_location = scanner_->peek_location(); - Statement statement = ParseStatement(CHECK_OK); + Statement statement = ParseSourceElement(CHECK_OK); i::Scanner::Location end_location = scanner_->location(); if (strict_mode() && statement.IsFunctionDeclaration()) { ReportMessageAt(start_location.beg_pos, end_location.end_pos, @@ -274,11 +282,15 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) { } -PreParser::Statement PreParser::ParseVariableStatement(bool* ok) { +PreParser::Statement PreParser::ParseVariableStatement( + VariableDeclarationContext var_context, + bool* ok) { // VariableStatement :: // VariableDeclarations ';' - Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK); + Statement result = ParseVariableDeclarations(var_context, + NULL, + CHECK_OK); ExpectSemicolon(CHECK_OK); return result; } @@ -289,9 +301,10 @@ PreParser::Statement PreParser::ParseVariableStatement(bool* ok) { // *var is untouched; in particular, it is the caller's responsibility // to initialize it properly. This mechanism is also used for the parsing // of 'for-in' loops. -PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN, - int* num_decl, - bool* ok) { +PreParser::Statement PreParser::ParseVariableDeclarations( + VariableDeclarationContext var_context, + int* num_decl, + bool* ok) { // VariableDeclarations :: // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] @@ -306,13 +319,25 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN, return Statement::Default(); } Consume(i::Token::CONST); + } else if (peek() == i::Token::LET) { + if (var_context != kSourceElement && + var_context != kForStatement) { + i::Scanner::Location location = scanner_->peek_location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "unprotected_let", NULL); + *ok = false; + return Statement::Default(); + } + Consume(i::Token::LET); } else { *ok = false; return Statement::Default(); } - // The scope of a variable/const declared anywhere inside a function - // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). . + // The scope of a var/const declared variable anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). The scope + // of a let declared variable is the scope of the immediately enclosing + // block. int nvars = 0; // the number of variables declared do { // Parse variable name. @@ -328,7 +353,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN, nvars++; if (peek() == i::Token::ASSIGN) { Expect(i::Token::ASSIGN, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); + ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); } } while (peek() == i::Token::COMMA); @@ -537,9 +562,10 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { Expect(i::Token::FOR, CHECK_OK); Expect(i::Token::LPAREN, CHECK_OK); if (peek() != i::Token::SEMICOLON) { - if (peek() == i::Token::VAR || peek() == i::Token::CONST) { + if (peek() == i::Token::VAR || peek() == i::Token::CONST || + peek() == i::Token::LET) { int decl_count; - ParseVariableDeclarations(false, &decl_count, CHECK_OK); + ParseVariableDeclarations(kForStatement, &decl_count, CHECK_OK); if (peek() == i::Token::IN && decl_count == 1) { Expect(i::Token::IN, CHECK_OK); ParseExpression(true, CHECK_OK); diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h index 3d72c97e26..cd0a530e8d 100644 --- a/deps/v8/src/preparser.h +++ b/deps/v8/src/preparser.h @@ -77,6 +77,12 @@ class PreParser { kFunctionScope }; + enum VariableDeclarationContext { + kSourceElement, + kStatement, + kForStatement + }; + class Expression; class Identifier { @@ -344,7 +350,8 @@ class PreParser { strict_mode_violation_type_(NULL), stack_overflow_(false), allow_lazy_(true), - parenthesized_function_(false) { } + parenthesized_function_(false), + harmony_block_scoping_(scanner->HarmonyBlockScoping()) { } // Preparse the program. Only called in PreParseProgram after creating // the instance. @@ -377,12 +384,16 @@ class PreParser { // which is set to false if parsing failed; it is unchanged otherwise. // By making the 'exception handling' explicit, we are forced to check // for failure at the call sites. + Statement ParseSourceElement(bool* ok); SourceElements ParseSourceElements(int end_token, bool* ok); Statement ParseStatement(bool* ok); Statement ParseFunctionDeclaration(bool* ok); Statement ParseBlock(bool* ok); - Statement ParseVariableStatement(bool* ok); - Statement ParseVariableDeclarations(bool accept_IN, int* num_decl, bool* ok); + Statement ParseVariableStatement(VariableDeclarationContext var_context, + bool* ok); + Statement ParseVariableDeclarations(VariableDeclarationContext var_context, + int* num_decl, + bool* ok); Statement ParseExpressionOrLabelledStatement(bool* ok); Statement ParseIfStatement(bool* ok); Statement ParseContinueStatement(bool* ok); @@ -496,6 +507,7 @@ class PreParser { bool stack_overflow_; bool allow_lazy_; bool parenthesized_function_; + bool harmony_block_scoping_; }; } } // v8::preparser diff --git a/deps/v8/src/prettyprinter.cc b/deps/v8/src/prettyprinter.cc index f18b3203ed..b03429341e 100644 --- a/deps/v8/src/prettyprinter.cc +++ b/deps/v8/src/prettyprinter.cc @@ -123,11 +123,11 @@ void PrettyPrinter::VisitReturnStatement(ReturnStatement* node) { } -void PrettyPrinter::VisitEnterWithContextStatement( - EnterWithContextStatement* node) { - Print("<enter with context> ("); +void PrettyPrinter::VisitWithStatement(WithStatement* node) { + Print("with ("); Visit(node->expression()); Print(") "); + Visit(node->statement()); } @@ -798,9 +798,10 @@ void AstPrinter::VisitReturnStatement(ReturnStatement* node) { } -void AstPrinter::VisitEnterWithContextStatement( - EnterWithContextStatement* node) { - PrintIndentedVisit("ENTER WITH CONTEXT", node->expression()); +void AstPrinter::VisitWithStatement(WithStatement* node) { + IndentedScope indent(this, "WITH"); + PrintIndentedVisit("OBJECT", node->expression()); + PrintIndentedVisit("BODY", node->statement()); } @@ -1194,10 +1195,10 @@ void JsonAstBuilder::VisitReturnStatement(ReturnStatement* stmt) { } -void JsonAstBuilder::VisitEnterWithContextStatement( - EnterWithContextStatement* stmt) { - TagScope tag(this, "EnterWithContextStatement"); +void JsonAstBuilder::VisitWithStatement(WithStatement* stmt) { + TagScope tag(this, "WithStatement"); Visit(stmt->expression()); + Visit(stmt->statement()); } diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc index e8ca5b9de6..ad6ce056b2 100644 --- a/deps/v8/src/rewriter.cc +++ b/deps/v8/src/rewriter.cc @@ -197,13 +197,17 @@ void Processor::VisitBreakStatement(BreakStatement* node) { } +void Processor::VisitWithStatement(WithStatement* node) { + bool set_after_body = is_set_; + Visit(node->statement()); + is_set_ = is_set_ && set_after_body; +} + + // Do nothing: void Processor::VisitDeclaration(Declaration* node) {} void Processor::VisitEmptyStatement(EmptyStatement* node) {} void Processor::VisitReturnStatement(ReturnStatement* node) {} -void Processor::VisitEnterWithContextStatement( - EnterWithContextStatement* node) { -} void Processor::VisitExitContextStatement(ExitContextStatement* node) {} void Processor::VisitDebuggerStatement(DebuggerStatement* node) {} diff --git a/deps/v8/src/runtime-profiler.h b/deps/v8/src/runtime-profiler.h index 3f3ab0773a..15c209713e 100644 --- a/deps/v8/src/runtime-profiler.h +++ b/deps/v8/src/runtime-profiler.h @@ -94,12 +94,6 @@ class RuntimeProfiler { private: static const int kSamplerWindowSize = 16; - static const int kStateWindowSize = 128; - - enum SamplerState { - IN_NON_JS_STATE = 0, - IN_JS_STATE = 1 - }; static void HandleWakeUp(Isolate* isolate); diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 82733a7c1d..f629970ff2 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -4853,7 +4853,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) { return isolate->heap()->boolean_symbol(); } if (heap_obj->IsNull()) { - return isolate->heap()->object_symbol(); + return FLAG_harmony_typeof + ? isolate->heap()->null_symbol() + : isolate->heap()->object_symbol(); } ASSERT(heap_obj->IsUndefined()); return isolate->heap()->undefined_symbol(); @@ -8314,6 +8316,30 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) { + NoHandleAllocation ha; + ASSERT(args.length() == 2); + SerializedScopeInfo* scope_info = SerializedScopeInfo::cast(args[0]); + JSFunction* function; + if (args[1]->IsSmi()) { + // A smi sentinel indicates a context nested inside global code rather + // than some function. There is a canonical empty function that can be + // gotten from the global context. + function = isolate->context()->global_context()->closure(); + } else { + function = JSFunction::cast(args[1]); + } + Context* context; + MaybeObject* maybe_context = + isolate->heap()->AllocateBlockContext(function, + isolate->context(), + scope_info); + if (!maybe_context->To(&context)) return maybe_context; + isolate->set_context(context); + return context; +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) { HandleScope scope(isolate); ASSERT(args.length() == 2); @@ -9654,7 +9680,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) { ASSERT(args.length() == 2); CONVERT_CHECKED(JSArray, from, args[0]); CONVERT_CHECKED(JSArray, to, args[1]); - HeapObject* new_elements = from->elements(); + FixedArrayBase* new_elements = from->elements(); MaybeObject* maybe_new_map; if (new_elements->map() == isolate->heap()->fixed_array_map() || new_elements->map() == isolate->heap()->fixed_cow_array_map()) { @@ -10639,6 +10665,34 @@ static Handle<JSObject> MaterializeCatchScope(Isolate* isolate, } +// Create a plain JSObject which materializes the block scope for the specified +// block context. +static Handle<JSObject> MaterializeBlockScope( + Isolate* isolate, + Handle<Context> context) { + ASSERT(context->IsBlockContext()); + Handle<SerializedScopeInfo> serialized_scope_info( + SerializedScopeInfo::cast(context->extension())); + ScopeInfo<> scope_info(*serialized_scope_info); + + // Allocate and initialize a JSObject with all the arguments, stack locals + // heap locals and extension properties of the debugged function. + Handle<JSObject> block_scope = + isolate->factory()->NewJSObject(isolate->object_function()); + + // Fill all context locals. + if (scope_info.number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) { + if (!CopyContextLocalsToScopeObject(isolate, + serialized_scope_info, scope_info, + context, block_scope)) { + return Handle<JSObject>(); + } + } + + return block_scope; +} + + // Iterate over the actual scopes visible from a stack frame. All scopes are // backed by an actual context except the local scope, which is inserted // "artifically" in the context chain. @@ -10649,7 +10703,8 @@ class ScopeIterator { ScopeTypeLocal, ScopeTypeWith, ScopeTypeClosure, - ScopeTypeCatch + ScopeTypeCatch, + ScopeTypeBlock }; ScopeIterator(Isolate* isolate, @@ -10675,8 +10730,10 @@ class ScopeIterator { } else if (context_->IsFunctionContext()) { at_local_ = true; } else if (context_->closure() != *function_) { - // The context_ is a with or catch block from the outer function. - ASSERT(context_->IsWithContext() || context_->IsCatchContext()); + // The context_ is a block or with or catch block from the outer function. + ASSERT(context_->IsWithContext() || + context_->IsCatchContext() || + context_->IsBlockContext()); at_local_ = true; } } @@ -10731,6 +10788,9 @@ class ScopeIterator { if (context_->IsCatchContext()) { return ScopeTypeCatch; } + if (context_->IsBlockContext()) { + return ScopeTypeBlock; + } ASSERT(context_->IsWithContext()); return ScopeTypeWith; } @@ -10751,6 +10811,8 @@ class ScopeIterator { case ScopeIterator::ScopeTypeClosure: // Materialize the content of the closure scope into a JSObject. return MaterializeClosure(isolate_, CurrentContext()); + case ScopeIterator::ScopeTypeBlock: + return MaterializeBlockScope(isolate_, CurrentContext()); } UNREACHABLE(); return Handle<JSObject>(); @@ -11307,7 +11369,18 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate, new_previous, name, thrown_object); + } else if (current->IsBlockContext()) { + Handle<SerializedScopeInfo> scope_info( + SerializedScopeInfo::cast(current->extension())); + new_current = + isolate->factory()->NewBlockContext(function, new_previous, scope_info); + // Copy context slots. + int num_context_slots = scope_info->NumberOfContextSlots(); + for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) { + new_current->set(i, current->get(i)); + } } else { + ASSERT(current->IsWithContext()); Handle<JSObject> extension(JSObject::cast(current->extension())); new_current = isolate->factory()->NewWithContext(function, new_previous, extension); diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index a52672ad7d..06d3684541 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -310,6 +310,7 @@ namespace internal { F(NewFunctionContext, 1, 1) \ F(PushWithContext, 2, 1) \ F(PushCatchContext, 3, 1) \ + F(PushBlockContext, 2, 1) \ F(DeleteContextSlot, 2, 1) \ F(LoadContextSlot, 2, 2) \ F(LoadContextSlotNoReferenceError, 2, 2) \ diff --git a/deps/v8/src/scanner-base.cc b/deps/v8/src/scanner-base.cc index e4590b1261..2ecbfd2a95 100644 --- a/deps/v8/src/scanner-base.cc +++ b/deps/v8/src/scanner-base.cc @@ -74,7 +74,9 @@ uc32 Scanner::ScanHexEscape(uc32 c, int length) { // JavaScriptScanner JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants) - : Scanner(scanner_contants), octal_pos_(Location::invalid()) { } + : Scanner(scanner_contants), + octal_pos_(Location::invalid()), + harmony_block_scoping_(false) { } void JavaScriptScanner::Initialize(UC16CharacterStream* source) { @@ -813,71 +815,73 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { // ---------------------------------------------------------------------------- // Keyword Matcher -#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ - KEYWORD_GROUP('b') \ - KEYWORD("break", BREAK) \ - KEYWORD_GROUP('c') \ - KEYWORD("case", CASE) \ - KEYWORD("catch", CATCH) \ - KEYWORD("class", FUTURE_RESERVED_WORD) \ - KEYWORD("const", CONST) \ - KEYWORD("continue", CONTINUE) \ - KEYWORD_GROUP('d') \ - KEYWORD("debugger", DEBUGGER) \ - KEYWORD("default", DEFAULT) \ - KEYWORD("delete", DELETE) \ - KEYWORD("do", DO) \ - KEYWORD_GROUP('e') \ - KEYWORD("else", ELSE) \ - KEYWORD("enum", FUTURE_RESERVED_WORD) \ - KEYWORD("export", FUTURE_RESERVED_WORD) \ - KEYWORD("extends", FUTURE_RESERVED_WORD) \ - KEYWORD_GROUP('f') \ - KEYWORD("false", FALSE_LITERAL) \ - KEYWORD("finally", FINALLY) \ - KEYWORD("for", FOR) \ - KEYWORD("function", FUNCTION) \ - KEYWORD_GROUP('i') \ - KEYWORD("if", IF) \ - KEYWORD("implements", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("import", FUTURE_RESERVED_WORD) \ - KEYWORD("in", IN) \ - KEYWORD("instanceof", INSTANCEOF) \ - KEYWORD("interface", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('l') \ - KEYWORD("let", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('n') \ - KEYWORD("new", NEW) \ - KEYWORD("null", NULL_LITERAL) \ - KEYWORD_GROUP('p') \ - KEYWORD("package", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("protected", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("public", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('r') \ - KEYWORD("return", RETURN) \ - KEYWORD_GROUP('s') \ - KEYWORD("static", FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("super", FUTURE_RESERVED_WORD) \ - KEYWORD("switch", SWITCH) \ - KEYWORD_GROUP('t') \ - KEYWORD("this", THIS) \ - KEYWORD("throw", THROW) \ - KEYWORD("true", TRUE_LITERAL) \ - KEYWORD("try", TRY) \ - KEYWORD("typeof", TYPEOF) \ - KEYWORD_GROUP('v') \ - KEYWORD("var", VAR) \ - KEYWORD("void", VOID) \ - KEYWORD_GROUP('w') \ - KEYWORD("while", WHILE) \ - KEYWORD("with", WITH) \ - KEYWORD_GROUP('y') \ - KEYWORD("yield", FUTURE_STRICT_RESERVED_WORD) +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("class", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("export", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("import", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD("let", harmony_block_scoping \ + ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('n') \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('r') \ + KEYWORD("return", Token::RETURN) \ + KEYWORD_GROUP('s') \ + KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("super", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) \ + KEYWORD_GROUP('y') \ + KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD) static Token::Value KeywordOrIdentifierToken(const char* input, - int input_length) { + int input_length, + bool harmony_block_scoping) { ASSERT(input_length >= 1); const int kMinLength = 2; const int kMaxLength = 10; @@ -906,7 +910,7 @@ static Token::Value KeywordOrIdentifierToken(const char* input, (keyword_length <= 7 || input[7] == keyword[7]) && \ (keyword_length <= 8 || input[8] == keyword[8]) && \ (keyword_length <= 9 || input[9] == keyword[9])) { \ - return Token::token; \ + return token; \ } \ } KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) @@ -947,7 +951,9 @@ Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { if (next_.literal_chars->is_ascii()) { Vector<const char> chars = next_.literal_chars->ascii_literal(); - return KeywordOrIdentifierToken(chars.start(), chars.length()); + return KeywordOrIdentifierToken(chars.start(), + chars.length(), + harmony_block_scoping_); } return Token::IDENTIFIER; diff --git a/deps/v8/src/scanner-base.h b/deps/v8/src/scanner-base.h index d3776e5f89..3e1772a3d2 100644 --- a/deps/v8/src/scanner-base.h +++ b/deps/v8/src/scanner-base.h @@ -507,6 +507,14 @@ class JavaScriptScanner : public Scanner { // tokens, which is what it is used for. void SeekForward(int pos); + bool HarmonyBlockScoping() const { + return harmony_block_scoping_; + } + void SetHarmonyBlockScoping(bool block_scoping) { + harmony_block_scoping_ = block_scoping; + } + + protected: bool SkipWhiteSpace(); Token::Value SkipSingleLineComment(); @@ -540,6 +548,9 @@ class JavaScriptScanner : public Scanner { // Whether there is a multi-line comment that contains a // line-terminator after the current token, and before the next. bool has_multiline_comment_before_next_; + // Whether we scan 'let' as a keyword for harmony block scoped + // let bindings. + bool harmony_block_scoping_; }; } } // namespace v8::internal diff --git a/deps/v8/src/scopeinfo.cc b/deps/v8/src/scopeinfo.cc index 3e18368f74..0eacc83c79 100644 --- a/deps/v8/src/scopeinfo.cc +++ b/deps/v8/src/scopeinfo.cc @@ -313,7 +313,7 @@ Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() { stack_slots_.length(); Handle<SerializedScopeInfo> data( - SerializedScopeInfo::cast(*FACTORY->NewFixedArray(length, TENURED))); + SerializedScopeInfo::cast(*FACTORY->NewSerializedScopeInfo(length))); AssertNoAllocation nogc; Object** p0 = data->data_start(); diff --git a/deps/v8/src/scopeinfo.h b/deps/v8/src/scopeinfo.h index 86c33f61ff..1c61f1115d 100644 --- a/deps/v8/src/scopeinfo.h +++ b/deps/v8/src/scopeinfo.h @@ -107,7 +107,7 @@ class SerializedScopeInfo : public FixedArray { public : static SerializedScopeInfo* cast(Object* object) { - ASSERT(object->IsFixedArray()); + ASSERT(object->IsSerializedScopeInfo()); return reinterpret_cast<SerializedScopeInfo*>(object); } diff --git a/deps/v8/src/scopes.cc b/deps/v8/src/scopes.cc index 390a0b6e11..ddde48a77c 100644 --- a/deps/v8/src/scopes.cc +++ b/deps/v8/src/scopes.cc @@ -146,7 +146,9 @@ Scope::Scope(Scope* outer_scope, Type type) } -Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info) +Scope::Scope(Scope* inner_scope, + Type type, + Handle<SerializedScopeInfo> scope_info) : isolate_(Isolate::Current()), inner_scopes_(4), variables_(), @@ -156,7 +158,7 @@ Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info) decls_(4), already_resolved_(true) { ASSERT(!scope_info.is_null()); - SetDefaults(FUNCTION_SCOPE, NULL, scope_info); + SetDefaults(type, NULL, scope_info); if (scope_info->HasHeapAllocatedLocals()) { num_heap_slots_ = scope_info_->NumberOfContextSlots(); } @@ -232,8 +234,13 @@ Scope* Scope::DeserializeScopeChain(CompilationInfo* info, if (context->IsFunctionContext()) { SerializedScopeInfo* scope_info = context->closure()->shared()->scope_info(); - current_scope = - new Scope(current_scope, Handle<SerializedScopeInfo>(scope_info)); + current_scope = new Scope(current_scope, FUNCTION_SCOPE, + Handle<SerializedScopeInfo>(scope_info)); + } else if (context->IsBlockContext()) { + SerializedScopeInfo* scope_info = + SerializedScopeInfo::cast(context->extension()); + current_scope = new Scope(current_scope, BLOCK_SCOPE, + Handle<SerializedScopeInfo>(scope_info)); } else { ASSERT(context->IsCatchContext()); String* name = String::cast(context->extension()); @@ -294,10 +301,13 @@ void Scope::Initialize(bool inside_with) { // instead load them directly from the stack. Currently, the only // such parameter is 'this' which is passed on the stack when // invoking scripts - if (is_catch_scope()) { + if (is_catch_scope() || is_block_scope()) { ASSERT(outer_scope() != NULL); receiver_ = outer_scope()->receiver(); } else { + ASSERT(is_function_scope() || + is_global_scope() || + is_eval_scope()); Variable* var = variables_.Declare(this, isolate_->factory()->this_symbol(), @@ -387,7 +397,9 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) { // This function handles VAR and CONST modes. DYNAMIC variables are // introduces during variable allocation, INTERNAL variables are allocated // explicitly, and TEMPORARY variables are allocated via NewTemporary(). - ASSERT(mode == Variable::VAR || mode == Variable::CONST); + ASSERT(mode == Variable::VAR || + mode == Variable::CONST || + mode == Variable::LET); ++num_var_or_const_; return variables_.Declare(this, name, mode, true, Variable::NORMAL); } @@ -559,13 +571,22 @@ int Scope::ContextChainLength(Scope* scope) { Scope* Scope::DeclarationScope() { Scope* scope = this; - while (scope->is_catch_scope()) { + while (scope->is_catch_scope() || + scope->is_block_scope()) { scope = scope->outer_scope(); } return scope; } +Handle<SerializedScopeInfo> Scope::GetSerializedScopeInfo() { + if (scope_info_.is_null()) { + scope_info_ = SerializedScopeInfo::Create(this); + } + return scope_info_; +} + + #ifdef DEBUG static const char* Header(Scope::Type type) { switch (type) { @@ -573,6 +594,7 @@ static const char* Header(Scope::Type type) { case Scope::FUNCTION_SCOPE: return "function"; case Scope::GLOBAL_SCOPE: return "global"; case Scope::CATCH_SCOPE: return "catch"; + case Scope::BLOCK_SCOPE: return "block"; } UNREACHABLE(); return NULL; @@ -598,9 +620,11 @@ static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) { PrintF("; // "); if (var->rewrite() != NULL) { PrintF("%s, ", printer->Print(var->rewrite())); - if (var->is_accessed_from_inner_scope()) PrintF(", "); + if (var->is_accessed_from_inner_function_scope()) PrintF(", "); + } + if (var->is_accessed_from_inner_function_scope()) { + PrintF("inner scope access"); } - if (var->is_accessed_from_inner_scope()) PrintF("inner scope access"); PrintF("\n"); } } @@ -721,7 +745,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) { // another variable that is introduced dynamically via an 'eval' call // or a 'with' statement). Variable* Scope::LookupRecursive(Handle<String> name, - bool inner_lookup, + bool from_inner_function, Variable** invalidated_local) { // If we find a variable, but the current scope calls 'eval', the found // variable may not be the correct one (the 'eval' may introduce a @@ -737,7 +761,7 @@ Variable* Scope::LookupRecursive(Handle<String> name, // (Even if there is an 'eval' in this scope which introduces the // same variable again, the resulting variable remains the same. // Note that enclosing 'with' statements are handled at the call site.) - if (!inner_lookup) + if (!from_inner_function) return var; } else { @@ -753,7 +777,10 @@ Variable* Scope::LookupRecursive(Handle<String> name, var = function_; } else if (outer_scope_ != NULL) { - var = outer_scope_->LookupRecursive(name, true, invalidated_local); + var = outer_scope_->LookupRecursive( + name, + is_function_scope() || from_inner_function, + invalidated_local); // We may have found a variable in an outer scope. However, if // the current scope is inside a 'with', the actual variable may // be a property introduced via the 'with' statement. Then, the @@ -770,8 +797,8 @@ Variable* Scope::LookupRecursive(Handle<String> name, ASSERT(var != NULL); // If this is a lookup from an inner scope, mark the variable. - if (inner_lookup) { - var->MarkAsAccessedFromInnerScope(); + if (from_inner_function) { + var->MarkAsAccessedFromInnerFunctionScope(); } // If the variable we have found is just a guess, invalidate the @@ -922,11 +949,12 @@ bool Scope::MustAllocate(Variable* var) { // via an eval() call. This is only possible if the variable has a // visible name. if ((var->is_this() || var->name()->length() > 0) && - (var->is_accessed_from_inner_scope() || + (var->is_accessed_from_inner_function_scope() || scope_calls_eval_ || inner_scope_calls_eval_ || scope_contains_with_ || - is_catch_scope())) { + is_catch_scope() || + is_block_scope())) { var->set_is_used(true); } // Global variables do not need to be allocated. @@ -943,8 +971,8 @@ bool Scope::MustAllocateInContext(Variable* var) { // Exceptions: temporary variables are never allocated in a context; // catch-bound variables are always allocated in a context. if (var->mode() == Variable::TEMPORARY) return false; - if (is_catch_scope()) return true; - return var->is_accessed_from_inner_scope() || + if (is_catch_scope() || is_block_scope()) return true; + return var->is_accessed_from_inner_function_scope() || scope_calls_eval_ || inner_scope_calls_eval_ || scope_contains_with_ || @@ -1010,7 +1038,7 @@ void Scope::AllocateParameterLocals() { if (uses_nonstrict_arguments) { // Give the parameter a use from an inner scope, to force allocation // to the context. - var->MarkAsAccessedFromInnerScope(); + var->MarkAsAccessedFromInnerFunctionScope(); } if (MustAllocate(var)) { diff --git a/deps/v8/src/scopes.h b/deps/v8/src/scopes.h index d4eb17cd56..c2c41799b9 100644 --- a/deps/v8/src/scopes.h +++ b/deps/v8/src/scopes.h @@ -93,7 +93,8 @@ class Scope: public ZoneObject { EVAL_SCOPE, // The top-level scope for an eval source. FUNCTION_SCOPE, // The top-level scope for a function. GLOBAL_SCOPE, // The top-level scope for a program or a top-level eval. - CATCH_SCOPE // The scope introduced by catch. + CATCH_SCOPE, // The scope introduced by catch. + BLOCK_SCOPE // The scope introduced by a new block. }; Scope(Scope* outer_scope, Type type); @@ -204,6 +205,7 @@ class Scope: public ZoneObject { bool is_function_scope() const { return type_ == FUNCTION_SCOPE; } bool is_global_scope() const { return type_ == GLOBAL_SCOPE; } bool is_catch_scope() const { return type_ == CATCH_SCOPE; } + bool is_block_scope() const { return type_ == BLOCK_SCOPE; } bool is_strict_mode() const { return strict_mode_; } bool is_strict_mode_eval_scope() const { return is_eval_scope() && is_strict_mode(); @@ -294,6 +296,8 @@ class Scope: public ZoneObject { // where var declarations will be hoisted to in the implementation. Scope* DeclarationScope(); + Handle<SerializedScopeInfo> GetSerializedScopeInfo(); + // --------------------------------------------------------------------------- // Strict mode support. bool IsDeclared(Handle<String> name) { @@ -397,7 +401,7 @@ class Scope: public ZoneObject { // Variable resolution. Variable* LookupRecursive(Handle<String> name, - bool inner_lookup, + bool from_inner_function, Variable** invalidated_local); void ResolveVariable(Scope* global_scope, Handle<Context> context, @@ -425,8 +429,8 @@ class Scope: public ZoneObject { void AllocateVariablesRecursively(); private: - // Construct a function scope based on the scope info. - Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info); + // Construct a function or block scope based on the scope info. + Scope(Scope* inner_scope, Type type, Handle<SerializedScopeInfo> scope_info); // Construct a catch scope with a binding for the name. Scope(Scope* inner_scope, Handle<String> catch_variable_name); diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index 8cde580fbb..094ad20b22 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -62,57 +62,15 @@ static int* GetInternalPointer(StatsCounter* counter) { } -// ExternalReferenceTable is a helper class that defines the relationship -// between external references and their encodings. It is used to build -// hashmaps in ExternalReferenceEncoder and ExternalReferenceDecoder. -class ExternalReferenceTable { - public: - static ExternalReferenceTable* instance(Isolate* isolate) { - ExternalReferenceTable* external_reference_table = - isolate->external_reference_table(); - if (external_reference_table == NULL) { - external_reference_table = new ExternalReferenceTable(isolate); - isolate->set_external_reference_table(external_reference_table); - } - return external_reference_table; - } - - int size() const { return refs_.length(); } - - Address address(int i) { return refs_[i].address; } - - uint32_t code(int i) { return refs_[i].code; } - - const char* name(int i) { return refs_[i].name; } - - int max_id(int code) { return max_id_[code]; } - - private: - explicit ExternalReferenceTable(Isolate* isolate) : refs_(64) { - PopulateTable(isolate); +ExternalReferenceTable* ExternalReferenceTable::instance(Isolate* isolate) { + ExternalReferenceTable* external_reference_table = + isolate->external_reference_table(); + if (external_reference_table == NULL) { + external_reference_table = new ExternalReferenceTable(isolate); + isolate->set_external_reference_table(external_reference_table); } - ~ExternalReferenceTable() { } - - struct ExternalReferenceEntry { - Address address; - uint32_t code; - const char* name; - }; - - void PopulateTable(Isolate* isolate); - - // For a few types of references, we can get their address from their id. - void AddFromId(TypeCode type, - uint16_t id, - const char* name, - Isolate* isolate); - - // For other types of references, the caller will figure out the address. - void Add(Address address, TypeCode type, uint16_t id, const char* name); - - List<ExternalReferenceEntry> refs_; - int max_id_[kTypeCodeCount]; -}; + return external_reference_table; +} void ExternalReferenceTable::AddFromId(TypeCode type, diff --git a/deps/v8/src/serialize.h b/deps/v8/src/serialize.h index d83722d007..66d6fb5111 100644 --- a/deps/v8/src/serialize.h +++ b/deps/v8/src/serialize.h @@ -60,6 +60,52 @@ const int kDebugRegisterBits = 4; const int kDebugIdShift = kDebugRegisterBits; +// ExternalReferenceTable is a helper class that defines the relationship +// between external references and their encodings. It is used to build +// hashmaps in ExternalReferenceEncoder and ExternalReferenceDecoder. +class ExternalReferenceTable { + public: + static ExternalReferenceTable* instance(Isolate* isolate); + + ~ExternalReferenceTable() { } + + int size() const { return refs_.length(); } + + Address address(int i) { return refs_[i].address; } + + uint32_t code(int i) { return refs_[i].code; } + + const char* name(int i) { return refs_[i].name; } + + int max_id(int code) { return max_id_[code]; } + + private: + explicit ExternalReferenceTable(Isolate* isolate) : refs_(64) { + PopulateTable(isolate); + } + + struct ExternalReferenceEntry { + Address address; + uint32_t code; + const char* name; + }; + + void PopulateTable(Isolate* isolate); + + // For a few types of references, we can get their address from their id. + void AddFromId(TypeCode type, + uint16_t id, + const char* name, + Isolate* isolate); + + // For other types of references, the caller will figure out the address. + void Add(Address address, TypeCode type, uint16_t id, const char* name); + + List<ExternalReferenceEntry> refs_; + int max_id_[kTypeCodeCount]; +}; + + class ExternalReferenceEncoder { public: ExternalReferenceEncoder(); @@ -544,6 +590,7 @@ class PartialSerializer : public Serializer { ASSERT(!o->IsScript()); return o->IsString() || o->IsSharedFunctionInfo() || o->IsHeapNumber() || o->IsCode() || + o->IsSerializedScopeInfo() || o->map() == HEAP->fixed_cow_array_map(); } diff --git a/deps/v8/src/token.h b/deps/v8/src/token.h index 7e6c18cfe6..33af7fe6bf 100644 --- a/deps/v8/src/token.h +++ b/deps/v8/src/token.h @@ -168,6 +168,7 @@ namespace internal { T(FUTURE_RESERVED_WORD, NULL, 0) \ T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ K(CONST, "const", 0) \ + K(LET, "let", 0) \ \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ diff --git a/deps/v8/src/variables.cc b/deps/v8/src/variables.cc index 67150ea13e..69495bb40f 100644 --- a/deps/v8/src/variables.cc +++ b/deps/v8/src/variables.cc @@ -41,6 +41,7 @@ const char* Variable::Mode2String(Mode mode) { switch (mode) { case VAR: return "VAR"; case CONST: return "CONST"; + case LET: return "LET"; case DYNAMIC: return "DYNAMIC"; case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL"; case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL"; @@ -92,7 +93,7 @@ Variable::Variable(Scope* scope, local_if_not_shadowed_(NULL), rewrite_(NULL), is_valid_LHS_(is_valid_LHS), - is_accessed_from_inner_scope_(false), + is_accessed_from_inner_function_scope_(false), is_used_(false) { // names must be canonicalized for fast equality checks ASSERT(name->IsSymbol()); diff --git a/deps/v8/src/variables.h b/deps/v8/src/variables.h index a9c06d1eea..e92ba6d39a 100644 --- a/deps/v8/src/variables.h +++ b/deps/v8/src/variables.h @@ -46,6 +46,8 @@ class Variable: public ZoneObject { CONST, // declared via 'const' declarations + LET, // declared via 'let' declarations + // Variables introduced by the compiler: DYNAMIC, // always require dynamic lookup (we don't know // the declaration) @@ -95,11 +97,12 @@ class Variable: public ZoneObject { Handle<String> name() const { return name_; } Mode mode() const { return mode_; } - bool is_accessed_from_inner_scope() const { - return is_accessed_from_inner_scope_; + bool is_accessed_from_inner_function_scope() const { + return is_accessed_from_inner_function_scope_; } - void MarkAsAccessedFromInnerScope() { - is_accessed_from_inner_scope_ = true; + void MarkAsAccessedFromInnerFunctionScope() { + ASSERT(mode_ != TEMPORARY); + is_accessed_from_inner_function_scope_ = true; } bool is_used() { return is_used_; } void set_is_used(bool flag) { is_used_ = flag; } @@ -156,7 +159,7 @@ class Variable: public ZoneObject { bool is_valid_LHS_; // Usage info. - bool is_accessed_from_inner_scope_; // set by variable resolver + bool is_accessed_from_inner_function_scope_; // set by variable resolver bool is_used_; }; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 83836f6207..c39af566f9 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,8 +34,8 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 3 #define MINOR_VERSION 5 -#define BUILD_NUMBER 4 -#define PATCH_LEVEL 3 +#define BUILD_NUMBER 6 +#define PATCH_LEVEL 0 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/deps/v8/src/weakmap.js b/deps/v8/src/weakmap.js index 15056c7f82..70210b98de 100644 --- a/deps/v8/src/weakmap.js +++ b/deps/v8/src/weakmap.js @@ -33,14 +33,13 @@ const $WeakMap = global.WeakMap; // ------------------------------------------------------------------- -// Set the WeakMap function and constructor. -%SetCode($WeakMap, function(x) { +function WeakMapConstructor() { if (%_IsConstructCall()) { %WeakMapInitialize(this); } else { return new $WeakMap(); } -}); +} function WeakMapGet(key) { @@ -82,6 +81,12 @@ function WeakMapDelete(key) { // ------------------------------------------------------------------- function SetupWeakMap() { + // Setup the WeakMap constructor function. + %SetCode($WeakMap, WeakMapConstructor); + + // Setup the WeakMap prototype object. + %FunctionSetPrototype($WeakMap, new $WeakMap()); + // Setup the non-enumerable functions on the WeakMap prototype object. InstallFunctionsOnHiddenPrototype($WeakMap.prototype, DONT_ENUM, $Array( "get", WeakMapGet, diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 56fbf9a339..94ed0cb55a 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -242,14 +242,14 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } // undefined -> false - CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch); + CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false); // Boolean -> its value - CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch); - CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch); + CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false); + CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true); // 'null' -> false. - CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch); + CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false); if (types_.Contains(SMI)) { // Smis: 0 -> false, all other -> true @@ -269,15 +269,16 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { if (types_.NeedsMap()) { __ movq(map, FieldOperand(argument, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ testb(FieldOperand(map, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - // Undetectable -> false. - Label not_undetectable; - __ j(zero, ¬_undetectable, Label::kNear); - __ Set(tos_, 0); - __ ret(1 * kPointerSize); - __ bind(¬_undetectable); + if (types_.CanBeUndetectable()) { + __ testb(FieldOperand(map, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + // Undetectable -> false. + Label not_undetectable; + __ j(zero, ¬_undetectable, Label::kNear); + __ Set(tos_, 0); + __ ret(1 * kPointerSize); + __ bind(¬_undetectable); + } } if (types_.Contains(SPEC_OBJECT)) { @@ -285,13 +286,12 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { Label not_js_object; __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); __ j(below, ¬_js_object, Label::kNear); - __ Set(tos_, 1); + // argument contains the correct return value already. + if (!tos_.is(argument)) { + __ Set(tos_, 1); + } __ ret(1 * kPointerSize); __ bind(¬_js_object); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> patch. - __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); - __ j(above_equal, &patch, Label::kNear); } if (types_.Contains(STRING)) { @@ -302,10 +302,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ movq(tos_, FieldOperand(argument, String::kLengthOffset)); __ ret(1 * kPointerSize); // the string length is OK as the return value __ bind(¬_string); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a string for the first time -> patch - __ CmpInstanceType(map, FIRST_NONSTRING_TYPE); - __ j(below, &patch, Label::kNear); } if (types_.Contains(HEAP_NUMBER)) { @@ -316,50 +312,42 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ xorps(xmm0, xmm0); __ ucomisd(xmm0, FieldOperand(argument, HeapNumber::kValueOffset)); __ j(zero, &false_result, Label::kNear); - __ Set(tos_, 1); + // argument contains the correct return value already. + if (!tos_.is(argument)) { + __ Set(tos_, 1); + } __ ret(1 * kPointerSize); __ bind(&false_result); __ Set(tos_, 0); __ ret(1 * kPointerSize); __ bind(¬_heap_number); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> patch - __ CompareRoot(map, Heap::kHeapNumberMapRootIndex); - __ j(equal, &patch, Label::kNear); - } - - if (types_.Contains(INTERNAL_OBJECT)) { - // internal objects -> true - __ Set(tos_, 1); - __ ret(1 * kPointerSize); } - if (!types_.IsAll()) { - __ bind(&patch); - GenerateTypeTransition(masm); - } + __ bind(&patch); + GenerateTypeTransition(masm); } void ToBooleanStub::CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, - bool result, - Label* patch) { + bool result) { const Register argument = rax; if (types_.Contains(type)) { // If we see an expected oddball, return its ToBoolean value tos_. Label different_value; __ CompareRoot(argument, value); __ j(not_equal, &different_value, Label::kNear); - __ Set(tos_, result ? 1 : 0); + if (!result) { + // If we have to return zero, there is no way around clearing tos_. + __ Set(tos_, 0); + } else if (!tos_.is(argument)) { + // If we have to return non-zero, we can re-use the argument if it is the + // same register as the result, because we never see Smi-zero here. + __ Set(tos_, 1); + } __ ret(1 * kPointerSize); __ bind(&different_value); - } else if (types_.Contains(INTERNAL_OBJECT)) { - // If we see an unexpected oddball and handle internal objects, we must - // patch because the code for internal objects doesn't handle it explictly. - __ CompareRoot(argument, value); - __ j(equal, patch); } } diff --git a/deps/v8/src/x64/frames-x64.h b/deps/v8/src/x64/frames-x64.h index b14267c82d..7012c76f0b 100644 --- a/deps/v8/src/x64/frames-x64.h +++ b/deps/v8/src/x64/frames-x64.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -50,12 +50,13 @@ static const int kNumSafepointRegisters = 16; class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kFPOffset = 1 * kPointerSize; - static const int kStateOffset = 2 * kPointerSize; - static const int kPCOffset = 3 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kContextOffset = 1 * kPointerSize; + static const int kFPOffset = 2 * kPointerSize; + static const int kStateOffset = 3 * kPointerSize; + static const int kPCOffset = 4 * kPointerSize; - static const int kSize = 4 * kPointerSize; + static const int kSize = kPCOffset + kPointerSize; }; diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index 0f7e5b3d79..d0c71be12f 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -712,8 +712,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ push(rsi); __ Push(variable->name()); // Declaration nodes are always introduced in one of two modes. - ASSERT(mode == Variable::VAR || mode == Variable::CONST); - PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; + ASSERT(mode == Variable::VAR || + mode == Variable::CONST || + mode == Variable::LET); + PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; __ Push(Smi::FromInt(attr)); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -3971,6 +3973,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, __ j(equal, if_true); __ CompareRoot(rax, Heap::kFalseValueRootIndex); Split(equal, if_true, if_false, fall_through); + } else if (FLAG_harmony_typeof && + check->Equals(isolate()->heap()->null_symbol())) { + __ CompareRoot(rax, Heap::kNullValueRootIndex); + Split(equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->undefined_symbol())) { __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); __ j(equal, if_true); @@ -3987,8 +3993,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(above_equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(rax, if_false); - __ CompareRoot(rax, Heap::kNullValueRootIndex); - __ j(equal, if_true); + if (!FLAG_harmony_typeof) { + __ CompareRoot(rax, Heap::kNullValueRootIndex); + __ j(equal, if_true); + } __ CmpObjectType(rax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, rdx); __ j(below, if_false); __ CmpInstanceType(rdx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc index ce6a9105f2..acbac44fc4 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.cc +++ b/deps/v8/src/x64/lithium-codegen-x64.cc @@ -1216,17 +1216,10 @@ void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) { } -void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) { +void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { Register result = ToRegister(instr->result()); Register array = ToRegister(instr->InputAt(0)); - __ movq(result, FieldOperand(array, FixedArray::kLengthOffset)); -} - - -void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) { - Register result = ToRegister(instr->result()); - Register array = ToRegister(instr->InputAt(0)); - __ movl(result, FieldOperand(array, ExternalPixelArray::kLengthOffset)); + __ movq(result, FieldOperand(array, FixedArrayBase::kLengthOffset)); } @@ -1410,40 +1403,19 @@ void LCodeGen::DoBranch(LBranch* instr) { // undefined -> false. __ CompareRoot(reg, Heap::kUndefinedValueRootIndex); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen undefined for the first time -> deopt. - __ CompareRoot(reg, Heap::kUndefinedValueRootIndex); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::BOOLEAN)) { // true -> true. __ CompareRoot(reg, Heap::kTrueValueRootIndex); __ j(equal, true_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ CompareRoot(reg, Heap::kTrueValueRootIndex); - DeoptimizeIf(equal, instr->environment()); - } - - if (expected.Contains(ToBooleanStub::BOOLEAN)) { // false -> false. __ CompareRoot(reg, Heap::kFalseValueRootIndex); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a boolean for the first time -> deopt. - __ CompareRoot(reg, Heap::kFalseValueRootIndex); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::NULL_TYPE)) { // 'null' -> false. __ CompareRoot(reg, Heap::kNullValueRootIndex); __ j(equal, false_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen null for the first time -> deopt. - __ CompareRoot(reg, Heap::kNullValueRootIndex); - DeoptimizeIf(equal, instr->environment()); } if (expected.Contains(ToBooleanStub::SMI)) { @@ -1460,21 +1432,19 @@ void LCodeGen::DoBranch(LBranch* instr) { const Register map = kScratchRegister; if (expected.NeedsMap()) { __ movq(map, FieldOperand(reg, HeapObject::kMapOffset)); - // Everything with a map could be undetectable, so check this now. - __ testb(FieldOperand(map, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - // Undetectable -> false. - __ j(not_zero, false_label); + + if (expected.CanBeUndetectable()) { + // Undetectable -> false. + __ testb(FieldOperand(map, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, false_label); + } } if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) { // spec object -> true. __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); __ j(above_equal, true_label); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a spec object for the first time -> deopt. - __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); - DeoptimizeIf(above_equal, instr->environment()); } if (expected.Contains(ToBooleanStub::STRING)) { @@ -1486,10 +1456,6 @@ void LCodeGen::DoBranch(LBranch* instr) { __ j(not_zero, true_label); __ jmp(false_label); __ bind(¬_string); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a string for the first time -> deopt - __ CmpInstanceType(map, FIRST_NONSTRING_TYPE); - DeoptimizeIf(below, instr->environment()); } if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { @@ -1502,19 +1468,10 @@ void LCodeGen::DoBranch(LBranch* instr) { __ j(zero, false_label); __ jmp(true_label); __ bind(¬_heap_number); - } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // We've seen a heap number for the first time -> deopt. - __ CompareRoot(map, Heap::kHeapNumberMapRootIndex); - DeoptimizeIf(equal, instr->environment()); } - if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) { - // internal objects -> true - __ jmp(true_label); - } else { - // We've seen something for the first time -> deopt. - DeoptimizeIf(no_condition, instr->environment()); - } + // We've seen something for the first time -> deopt. + DeoptimizeIf(no_condition, instr->environment()); } } } @@ -2305,16 +2262,13 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { - Register elements = ToRegister(instr->elements()); - Register key = ToRegister(instr->key()); Register result = ToRegister(instr->result()); - ASSERT(result.is(elements)); // Load the result. - __ movq(result, FieldOperand(elements, - key, - times_pointer_size, - FixedArray::kHeaderSize)); + __ movq(result, + BuildFastArrayOperand(instr->elements(), instr->key(), + JSObject::FAST_ELEMENTS, + FixedArray::kHeaderSize - kHeapObjectTag)); // Check for the hole value. if (instr->hydrogen()->RequiresHoleCheck()) { @@ -2348,22 +2302,22 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand LCodeGen::BuildFastArrayOperand( - LOperand* external_pointer, + LOperand* elements_pointer, LOperand* key, JSObject::ElementsKind elements_kind, uint32_t offset) { - Register external_pointer_reg = ToRegister(external_pointer); + Register elements_pointer_reg = ToRegister(elements_pointer); int shift_size = ElementsKindToShiftSize(elements_kind); if (key->IsConstantOperand()) { int constant_value = ToInteger32(LConstantOperand::cast(key)); if (constant_value & 0xF0000000) { Abort("array index constant value too big"); } - return Operand(external_pointer_reg, + return Operand(elements_pointer_reg, constant_value * (1 << shift_size) + offset); } else { ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size); - return Operand(external_pointer_reg, ToRegister(key), + return Operand(elements_pointer_reg, ToRegister(key), scale_factor, offset); } } @@ -2759,6 +2713,7 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; Register output_reg = ToRegister(instr->result()); XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + Label done; if (CpuFeatures::IsSupported(SSE4_1)) { CpuFeatures::Scope scope(SSE4_1); @@ -2773,13 +2728,20 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { __ cmpl(output_reg, Immediate(0x80000000)); DeoptimizeIf(equal, instr->environment()); } else { + // Deoptimize on negative inputs. __ xorps(xmm_scratch, xmm_scratch); // Zero the register. __ ucomisd(input_reg, xmm_scratch); - + DeoptimizeIf(below, instr->environment()); if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - DeoptimizeIf(below_equal, instr->environment()); - } else { - DeoptimizeIf(below, instr->environment()); + // Check for negative zero. + Label positive_sign; + __ j(above, &positive_sign, Label::kNear); + __ movmskpd(output_reg, input_reg); + __ testq(output_reg, Immediate(1)); + DeoptimizeIf(not_zero, instr->environment()); + __ Set(output_reg, 0); + __ jmp(&done); + __ bind(&positive_sign); } // Use truncating instruction (OK because input is positive). @@ -2789,6 +2751,7 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { __ cmpl(output_reg, Immediate(0x80000000)); DeoptimizeIf(equal, instr->environment()); } + __ bind(&done); } @@ -3137,12 +3100,22 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { - if (instr->length()->IsRegister()) { - __ cmpq(ToRegister(instr->index()), ToRegister(instr->length())); + if (instr->index()->IsConstantOperand()) { + if (instr->length()->IsRegister()) { + __ cmpq(ToRegister(instr->length()), + Immediate(ToInteger32(LConstantOperand::cast(instr->index())))); + } else { + __ cmpq(ToOperand(instr->length()), + Immediate(ToInteger32(LConstantOperand::cast(instr->index())))); + } } else { - __ cmpq(ToRegister(instr->index()), ToOperand(instr->length())); + if (instr->length()->IsRegister()) { + __ cmpq(ToRegister(instr->length()), ToRegister(instr->index())); + } else { + __ cmpq(ToOperand(instr->length()), ToRegister(instr->index())); + } } - DeoptimizeIf(above_equal, instr->environment()); + DeoptimizeIf(below_equal, instr->environment()); } @@ -4008,6 +3981,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, __ CompareRoot(input, Heap::kFalseValueRootIndex); final_branch_condition = equal; + } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_symbol())) { + __ CompareRoot(input, Heap::kNullValueRootIndex); + final_branch_condition = equal; + } else if (type_name->Equals(heap()->undefined_symbol())) { __ CompareRoot(input, Heap::kUndefinedValueRootIndex); __ j(equal, true_label); @@ -4025,8 +4002,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); - __ CompareRoot(input, Heap::kNullValueRootIndex); - __ j(equal, true_label); + if (!FLAG_harmony_typeof) { + __ CompareRoot(input, Heap::kNullValueRootIndex); + __ j(equal, true_label); + } __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input); __ j(below, false_label); __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h index d7c72b544b..0622e9d285 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.h +++ b/deps/v8/src/x64/lithium-codegen-x64.h @@ -216,7 +216,7 @@ class LCodeGen BASE_EMBEDDED { Register ToRegister(int index) const; XMMRegister ToDoubleRegister(int index) const; Operand BuildFastArrayOperand( - LOperand* external_pointer, + LOperand* elements_pointer, LOperand* key, JSObject::ElementsKind elements_kind, uint32_t offset); diff --git a/deps/v8/src/x64/lithium-x64.cc b/deps/v8/src/x64/lithium-x64.cc index 7f4490f6de..9d08d37626 100644 --- a/deps/v8/src/x64/lithium-x64.cc +++ b/deps/v8/src/x64/lithium-x64.cc @@ -1036,11 +1036,7 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { : instr->SecondSuccessor(); return new LGoto(successor->block_id()); } - LInstruction* branch = new LBranch(UseRegister(v)); - // When we handle all cases, we never deopt, so we don't need to assign the - // environment then. - bool all_cases_handled = instr->expected_input_types().IsAll(); - return all_cases_handled ? branch : AssignEnvironment(branch); + return AssignEnvironment(new LBranch(UseRegister(v))); } @@ -1502,16 +1498,10 @@ LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) { } -LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) { +LInstruction* LChunkBuilder::DoFixedArrayBaseLength( + HFixedArrayBaseLength* instr) { LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LFixedArrayLength(array)); -} - - -LInstruction* LChunkBuilder::DoExternalArrayLength( - HExternalArrayLength* instr) { - LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LExternalArrayLength(array)); + return DefineAsRegister(new LFixedArrayBaseLength(array)); } @@ -1529,8 +1519,9 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { - return AssignEnvironment(new LBoundsCheck(UseRegisterAtStart(instr->index()), - Use(instr->length()))); + return AssignEnvironment(new LBoundsCheck( + UseRegisterOrConstantAtStart(instr->index()), + Use(instr->length()))); } @@ -1829,9 +1820,9 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastElement( ASSERT(instr->representation().IsTagged()); ASSERT(instr->key()->representation().IsInteger32()); LOperand* obj = UseRegisterAtStart(instr->object()); - LOperand* key = UseRegisterAtStart(instr->key()); + LOperand* key = UseRegisterOrConstantAtStart(instr->key()); LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); - return AssignEnvironment(DefineSameAsFirst(result)); + return AssignEnvironment(DefineAsRegister(result)); } diff --git a/deps/v8/src/x64/lithium-x64.h b/deps/v8/src/x64/lithium-x64.h index 12bdfb28ae..b570672f68 100644 --- a/deps/v8/src/x64/lithium-x64.h +++ b/deps/v8/src/x64/lithium-x64.h @@ -92,8 +92,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ - V(ExternalArrayLength) \ - V(FixedArrayLength) \ + V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ V(GlobalObject) \ @@ -913,25 +912,15 @@ class LJSArrayLength: public LTemplateInstruction<1, 1, 0> { }; -class LExternalArrayLength: public LTemplateInstruction<1, 1, 0> { +class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { public: - explicit LExternalArrayLength(LOperand* value) { + explicit LFixedArrayBaseLength(LOperand* value) { inputs_[0] = value; } - DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength, "external-array-length") - DECLARE_HYDROGEN_ACCESSOR(ExternalArrayLength) -}; - - -class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> { - public: - explicit LFixedArrayLength(LOperand* value) { - inputs_[0] = value; - } - - DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length") - DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength) + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength, + "fixed-array-base-length") + DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength) }; @@ -2135,14 +2124,18 @@ class LChunkBuilder BASE_EMBEDDED { template<int I, int T> LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr, XMMRegister reg); + // Assigns an environment to an instruction. An instruction which can + // deoptimize must have an environment. LInstruction* AssignEnvironment(LInstruction* instr); + // Assigns a pointer map to an instruction. An instruction which can + // trigger a GC or a lazy deoptimization must have a pointer map. LInstruction* AssignPointerMap(LInstruction* instr); enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY }; - // By default we assume that instruction sequences generated for calls - // cannot deoptimize eagerly and we do not attach environment to this - // instruction. + // Marks a call for the register allocator. Assigns a pointer map to + // support GC and lazy deoptimization. Assigns an environment to support + // eager deoptimization if CAN_DEOPTIMIZE_EAGERLY. LInstruction* MarkAsCall( LInstruction* instr, HInstruction* hinstr, diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 2b15553f17..e4a76270a2 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -2387,18 +2387,15 @@ Operand MacroAssembler::SafepointRegisterSlot(Register reg) { void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. - ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // The pc (return address) is already on TOS. This code pushes state, - // frame pointer and current handler. Check that they are expected - // next on the stack, in that order. - ASSERT_EQ(StackHandlerConstants::kStateOffset, - StackHandlerConstants::kPCOffset - kPointerSize); - ASSERT_EQ(StackHandlerConstants::kFPOffset, - StackHandlerConstants::kStateOffset - kPointerSize); - ASSERT_EQ(StackHandlerConstants::kNextOffset, - StackHandlerConstants::kFPOffset - kPointerSize); - + // frame pointer, context, and current handler. if (try_location == IN_JAVASCRIPT) { if (type == TRY_CATCH_HANDLER) { push(Immediate(StackHandler::TRY_CATCH)); @@ -2406,6 +2403,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, push(Immediate(StackHandler::TRY_FINALLY)); } push(rbp); + push(rsi); } else { ASSERT(try_location == IN_JS_ENTRY); // The frame pointer does not point to a JS frame so we save NULL @@ -2413,6 +2411,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, // before dereferencing it to restore the context. push(Immediate(StackHandler::ENTRY)); push(Immediate(0)); // NULL frame pointer. + Push(Smi::FromInt(0)); // No context. } // Save the current handler. Operand handler_operand = @@ -2435,12 +2434,13 @@ void MacroAssembler::PopTryHandler() { void MacroAssembler::Throw(Register value) { - // Check that stack should contain next handler, frame pointer, state and - // return address in that order. - STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == - StackHandlerConstants::kStateOffset); - STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == - StackHandlerConstants::kPCOffset); + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // Keep thrown value in rax. if (!value.is(rax)) { movq(rax, value); @@ -2451,23 +2451,32 @@ void MacroAssembler::Throw(Register value) { movq(rsp, handler_operand); // get next in chain pop(handler_operand); - pop(rbp); // pop frame pointer - pop(rdx); // remove state + pop(rsi); // Context. + pop(rbp); // Frame pointer. + pop(rdx); // State. - // Before returning we restore the context from the frame pointer if not NULL. - // The frame pointer is NULL in the exception handler of a JS entry frame. - Set(rsi, 0); // Tentatively set context pointer to NULL + // If the handler is a JS frame, restore the context to the frame. + // (rdx == ENTRY) == (rbp == 0) == (rsi == 0), so we could test any + // of them. Label skip; - cmpq(rbp, Immediate(0)); + cmpq(rdx, Immediate(StackHandler::ENTRY)); j(equal, &skip, Label::kNear); - movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); bind(&skip); + ret(0); } void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Register value) { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // Keep thrown value in rax. if (!value.is(rax)) { movq(rax, value); @@ -2507,19 +2516,13 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Store(pending_exception, rax); } - // Clear the context pointer. + // Discard the context saved in the handler and clear the context pointer. + pop(rdx); Set(rsi, 0); - // Restore registers from handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset + kPointerSize == - StackHandlerConstants::kFPOffset); - pop(rbp); // FP - STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == - StackHandlerConstants::kStateOffset); - pop(rdx); // State + pop(rbp); // Restore frame pointer. + pop(rdx); // Discard state. - STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == - StackHandlerConstants::kPCOffset); ret(0); } diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index e195aecc38..cfd19bf146 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -3244,7 +3244,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // Check that the index is in range. __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); __ SmiToInteger32(rcx, rax); - __ cmpl(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset)); + __ cmpq(rax, FieldOperand(rbx, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ j(above_equal, &miss_force_generic); @@ -3379,7 +3379,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Check that the index is in range. __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); __ SmiToInteger32(rdi, rcx); // Untag the index. - __ cmpl(rdi, FieldOperand(rbx, ExternalArray::kLengthOffset)); + __ cmpq(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ j(above_equal, &miss_force_generic); diff --git a/deps/v8/src/zone.cc b/deps/v8/src/zone.cc index 42ce8c5cb7..7574778f53 100644 --- a/deps/v8/src/zone.cc +++ b/deps/v8/src/zone.cc @@ -34,24 +34,6 @@ namespace v8 { namespace internal { -Zone::Zone() - : zone_excess_limit_(256 * MB), - segment_bytes_allocated_(0), - position_(0), - limit_(0), - scope_nesting_(0), - segment_head_(NULL) { -} -unsigned Zone::allocation_size_ = 0; - - -ZoneScope::~ZoneScope() { - ASSERT_EQ(Isolate::Current(), isolate_); - if (ShouldDeleteOnExit()) isolate_->zone()->DeleteAll(); - isolate_->zone()->scope_nesting_--; -} - - // Segments represent chunks of memory: They have starting address // (encoded in the this pointer) and a size in bytes. Segments are // chained together forming a LIFO structure with the newest segment @@ -60,6 +42,11 @@ ZoneScope::~ZoneScope() { class Segment { public: + void Initialize(Segment* next, int size) { + next_ = next; + size_ = size; + } + Segment* next() const { return next_; } void clear_next() { next_ = NULL; } @@ -77,19 +64,33 @@ class Segment { Segment* next_; int size_; - - friend class Zone; }; +Zone::Zone() + : zone_excess_limit_(256 * MB), + segment_bytes_allocated_(0), + position_(0), + limit_(0), + scope_nesting_(0), + segment_head_(NULL) { +} +unsigned Zone::allocation_size_ = 0; + +ZoneScope::~ZoneScope() { + ASSERT_EQ(Isolate::Current(), isolate_); + if (ShouldDeleteOnExit()) isolate_->zone()->DeleteAll(); + isolate_->zone()->scope_nesting_--; +} + + // Creates a new segment, sets it size, and pushes it to the front // of the segment chain. Returns the new segment. Segment* Zone::NewSegment(int size) { Segment* result = reinterpret_cast<Segment*>(Malloced::New(size)); adjust_segment_bytes_allocated(size); if (result != NULL) { - result->next_ = segment_head_; - result->size_ = size; + result->Initialize(segment_head_, size); segment_head_ = result; } return result; @@ -155,6 +156,14 @@ void Zone::DeleteAll() { } +void Zone::DeleteKeptSegment() { + if (segment_head_ != NULL) { + DeleteSegment(segment_head_, segment_head_->size()); + segment_head_ = NULL; + } +} + + Address Zone::NewExpand(int size) { // Make sure the requested size is already properly aligned and that // there isn't enough room in the Zone to satisfy the request. diff --git a/deps/v8/src/zone.h b/deps/v8/src/zone.h index abb53ad46d..faad3b7ccd 100644 --- a/deps/v8/src/zone.h +++ b/deps/v8/src/zone.h @@ -65,9 +65,13 @@ class Zone { template <typename T> inline T* NewArray(int length); - // Delete all objects and free all memory allocated in the Zone. + // Deletes all objects and free all memory allocated in the Zone. Keeps one + // small (size <= kMaximumKeptSegmentSize) segment around if it finds one. void DeleteAll(); + // Deletes the last small segment kept around by DeleteAll(). + void DeleteKeptSegment(); + // Returns true if more memory has been allocated in zones than // the limit allows. inline bool excess_allocation(); diff --git a/deps/v8/test/cctest/test-accessors.cc b/deps/v8/test/cctest/test-accessors.cc index 028f82f3dc..d95536d2d5 100644 --- a/deps/v8/test/cctest/test-accessors.cc +++ b/deps/v8/test/cctest/test-accessors.cc @@ -44,8 +44,6 @@ using ::v8::Function; using ::v8::AccessorInfo; using ::v8::Extension; -namespace i = ::v8::internal; - static v8::Handle<Value> handle_property(Local<String> name, const AccessorInfo&) { ApiTestFuzzer::Fuzz(); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index c694ae38ad..d5e17b89a7 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -72,8 +72,6 @@ using ::v8::Undefined; using ::v8::V8; using ::v8::Value; -namespace i = ::i; - static void ExpectString(const char* code, const char* expected) { Local<Value> result = CompileRun(code); @@ -331,16 +329,14 @@ static uint16_t* AsciiToTwoByteString(const char* source) { class TestResource: public String::ExternalStringResource { public: - static int dispose_count; - - explicit TestResource(uint16_t* data) - : data_(data), length_(0) { + explicit TestResource(uint16_t* data, int* counter = NULL) + : data_(data), length_(0), counter_(counter) { while (data[length_]) ++length_; } ~TestResource() { i::DeleteArray(data_); - ++dispose_count; + if (counter_ != NULL) ++*counter_; } const uint16_t* data() const { @@ -353,23 +349,18 @@ class TestResource: public String::ExternalStringResource { private: uint16_t* data_; size_t length_; + int* counter_; }; -int TestResource::dispose_count = 0; - - class TestAsciiResource: public String::ExternalAsciiStringResource { public: - static int dispose_count; - - explicit TestAsciiResource(const char* data) - : data_(data), - length_(strlen(data)) { } + explicit TestAsciiResource(const char* data, int* counter = NULL) + : data_(data), length_(strlen(data)), counter_(counter) { } ~TestAsciiResource() { i::DeleteArray(data_); - ++dispose_count; + if (counter_ != NULL) ++*counter_; } const char* data() const { @@ -382,20 +373,18 @@ class TestAsciiResource: public String::ExternalAsciiStringResource { private: const char* data_; size_t length_; + int* counter_; }; -int TestAsciiResource::dispose_count = 0; - - THREADED_TEST(ScriptUsingStringResource) { - TestResource::dispose_count = 0; + int dispose_count = 0; const char* c_source = "1 + 2 * 3"; uint16_t* two_byte_source = AsciiToTwoByteString(c_source); { v8::HandleScope scope; LocalContext env; - TestResource* resource = new TestResource(two_byte_source); + TestResource* resource = new TestResource(two_byte_source, &dispose_count); Local<String> source = String::NewExternal(resource); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); @@ -405,37 +394,38 @@ THREADED_TEST(ScriptUsingStringResource) { CHECK_EQ(resource, static_cast<TestResource*>(source->GetExternalStringResource())); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestResource::dispose_count); + CHECK_EQ(0, dispose_count); } v8::internal::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); - CHECK_EQ(1, TestResource::dispose_count); + CHECK_EQ(1, dispose_count); } THREADED_TEST(ScriptUsingAsciiStringResource) { - TestAsciiResource::dispose_count = 0; + int dispose_count = 0; const char* c_source = "1 + 2 * 3"; { v8::HandleScope scope; LocalContext env; Local<String> source = - String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); + String::NewExternal(new TestAsciiResource(i::StrDup(c_source), + &dispose_count)); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); - CHECK_EQ(1, TestAsciiResource::dispose_count); + CHECK_EQ(1, dispose_count); } THREADED_TEST(ScriptMakingExternalString) { - TestResource::dispose_count = 0; + int dispose_count = 0; uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); { v8::HandleScope scope; @@ -444,23 +434,24 @@ THREADED_TEST(ScriptMakingExternalString) { // Trigger GCs so that the newly allocated string moves to old gen. HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now - bool success = source->MakeExternal(new TestResource(two_byte_source)); + bool success = source->MakeExternal(new TestResource(two_byte_source, + &dispose_count)); CHECK(success); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestResource::dispose_count); + CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); - CHECK_EQ(1, TestResource::dispose_count); + CHECK_EQ(1, dispose_count); } THREADED_TEST(ScriptMakingExternalAsciiString) { - TestAsciiResource::dispose_count = 0; + int dispose_count = 0; const char* c_source = "1 + 2 * 3"; { v8::HandleScope scope; @@ -470,18 +461,18 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now bool success = source->MakeExternal( - new TestAsciiResource(i::StrDup(c_source))); + new TestAsciiResource(i::StrDup(c_source), &dispose_count)); CHECK(success); Local<Script> script = Script::Compile(source); Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); - CHECK_EQ(1, TestAsciiResource::dispose_count); + CHECK_EQ(1, dispose_count); } @@ -605,49 +596,52 @@ THREADED_TEST(UsingExternalAsciiString) { THREADED_TEST(ScavengeExternalString) { - TestResource::dispose_count = 0; + int dispose_count = 0; bool in_new_space = false; { v8::HandleScope scope; uint16_t* two_byte_string = AsciiToTwoByteString("test string"); Local<String> string = - String::NewExternal(new TestResource(two_byte_string)); + String::NewExternal(new TestResource(two_byte_string, + &dispose_count)); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); HEAP->CollectGarbage(i::NEW_SPACE); in_new_space = HEAP->InNewSpace(*istring); CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); - CHECK_EQ(0, TestResource::dispose_count); + CHECK_EQ(0, dispose_count); } HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); - CHECK_EQ(1, TestResource::dispose_count); + CHECK_EQ(1, dispose_count); } THREADED_TEST(ScavengeExternalAsciiString) { - TestAsciiResource::dispose_count = 0; + int dispose_count = 0; bool in_new_space = false; { v8::HandleScope scope; const char* one_byte_string = "test string"; Local<String> string = String::NewExternal( - new TestAsciiResource(i::StrDup(one_byte_string))); + new TestAsciiResource(i::StrDup(one_byte_string), &dispose_count)); i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); HEAP->CollectGarbage(i::NEW_SPACE); in_new_space = HEAP->InNewSpace(*istring); CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, dispose_count); } HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); - CHECK_EQ(1, TestAsciiResource::dispose_count); + CHECK_EQ(1, dispose_count); } class TestAsciiResourceWithDisposeControl: public TestAsciiResource { public: + // Only used by non-threaded tests, so it can use static fields. static int dispose_calls; + static int dispose_count; TestAsciiResourceWithDisposeControl(const char* data, bool dispose) - : TestAsciiResource(data), + : TestAsciiResource(data, &dispose_count), dispose_(dispose) { } void Dispose() { @@ -659,6 +653,7 @@ class TestAsciiResourceWithDisposeControl: public TestAsciiResource { }; +int TestAsciiResourceWithDisposeControl::dispose_count = 0; int TestAsciiResourceWithDisposeControl::dispose_calls = 0; @@ -666,7 +661,7 @@ TEST(ExternalStringWithDisposeHandling) { const char* c_source = "1 + 2 * 3"; // Use a stack allocated external string resource allocated object. - TestAsciiResource::dispose_count = 0; + TestAsciiResourceWithDisposeControl::dispose_count = 0; TestAsciiResourceWithDisposeControl::dispose_calls = 0; TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); { @@ -678,15 +673,15 @@ TEST(ExternalStringWithDisposeHandling) { CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); // Use a heap allocated external string resource allocated object. - TestAsciiResource::dispose_count = 0; + TestAsciiResourceWithDisposeControl::dispose_count = 0; TestAsciiResourceWithDisposeControl::dispose_calls = 0; TestAsciiResource* res_heap = new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); @@ -699,12 +694,12 @@ TEST(ExternalStringWithDisposeHandling) { CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, TestAsciiResource::dispose_count); + CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); HEAP->CollectAllGarbage(false); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); - CHECK_EQ(1, TestAsciiResource::dispose_count); + CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_count); } @@ -14595,6 +14590,24 @@ THREADED_TEST(CreationContext) { } +THREADED_TEST(CreationContextOfJsFunction) { + HandleScope handle_scope; + Persistent<Context> context = Context::New(); + InstallContextId(context, 1); + + Local<Object> function; + { + Context::Scope scope(context); + function = CompileRun("function foo() {}; foo").As<Object>(); + } + + CHECK(function->CreationContext() == context); + CheckContextId(function, 1); + + context.Dispose(); +} + + Handle<Value> HasOwnPropertyIndexedPropertyGetter(uint32_t index, const AccessorInfo& info) { if (index == 42) return v8_str("yes"); diff --git a/deps/v8/test/cctest/test-circular-queue.cc b/deps/v8/test/cctest/test-circular-queue.cc index c4e5c4c24d..2861b1f76e 100644 --- a/deps/v8/test/cctest/test-circular-queue.cc +++ b/deps/v8/test/cctest/test-circular-queue.cc @@ -6,8 +6,6 @@ #include "circular-queue-inl.h" #include "cctest.h" -namespace i = v8::internal; - using i::SamplingCircularQueue; diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc index 9ff2a171a3..81c487da8a 100644 --- a/deps/v8/test/cctest/test-cpu-profiler.cc +++ b/deps/v8/test/cctest/test-cpu-profiler.cc @@ -7,8 +7,6 @@ #include "cctest.h" #include "../include/v8-profiler.h" -namespace i = v8::internal; - using i::CodeEntry; using i::CpuProfile; using i::CpuProfiler; diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index 8675a0146c..169e6dcbdb 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -10,8 +10,6 @@ #include "utils-inl.h" #include "../include/v8-profiler.h" -namespace i = v8::internal; - namespace { class NamedEntriesDetector { diff --git a/deps/v8/test/cctest/test-lockers.cc b/deps/v8/test/cctest/test-lockers.cc index 2b184e913d..d61fde250c 100644 --- a/deps/v8/test/cctest/test-lockers.cc +++ b/deps/v8/test/cctest/test-lockers.cc @@ -54,10 +54,6 @@ using ::v8::String; using ::v8::Value; using ::v8::V8; -namespace i = ::i; - - - // Migrating an isolate class KangarooThread : public v8::internal::Thread { diff --git a/deps/v8/test/cctest/test-log-stack-tracer.cc b/deps/v8/test/cctest/test-log-stack-tracer.cc index 2bcb3fe0b6..f536e6b193 100644 --- a/deps/v8/test/cctest/test-log-stack-tracer.cc +++ b/deps/v8/test/cctest/test-log-stack-tracer.cc @@ -54,8 +54,6 @@ using v8::internal::JSFunction; using v8::internal::StackTracer; using v8::internal::TickSample; -namespace i = v8::internal; - static v8::Persistent<v8::Context> env; diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 262e7bb505..dfbc733e0d 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -23,8 +23,6 @@ using v8::internal::EmbeddedVector; using v8::internal::Logger; using v8::internal::StrLength; -namespace i = v8::internal; - namespace { class ScopedLoggerInitializer { diff --git a/deps/v8/test/cctest/test-parsing.cc b/deps/v8/test/cctest/test-parsing.cc index fe738768d6..8b6afdc59c 100755 --- a/deps/v8/test/cctest/test-parsing.cc +++ b/deps/v8/test/cctest/test-parsing.cc @@ -40,8 +40,6 @@ #include "preparser.h" #include "cctest.h" -namespace i = ::v8::internal; - TEST(ScanKeywords) { struct KeywordToken { const char* keyword; @@ -66,6 +64,8 @@ TEST(ScanKeywords) { { i::Utf8ToUC16CharacterStream stream(keyword, length); i::JavaScriptScanner scanner(&unicode_cache); + // The scanner should parse 'let' as Token::LET for this test. + scanner.SetHarmonyBlockScoping(true); scanner.Initialize(&stream); CHECK_EQ(key_token.token, scanner.Next()); CHECK_EQ(i::Token::EOS, scanner.Next()); @@ -289,7 +289,7 @@ TEST(RegressChromium62639) { i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program), static_cast<unsigned>(strlen(program))); i::ScriptDataImpl* data = - i::ParserApi::PreParse(&stream, NULL); + i::ParserApi::PreParse(&stream, NULL, false); CHECK(data->HasError()); delete data; } @@ -313,7 +313,7 @@ TEST(Regress928) { i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program), static_cast<unsigned>(strlen(program))); i::ScriptDataImpl* data = - i::ParserApi::PartialPreParse(&stream, NULL); + i::ParserApi::PartialPreParse(&stream, NULL, false); CHECK(!data->HasError()); data->Initialize(); diff --git a/deps/v8/test/cctest/test-profile-generator.cc b/deps/v8/test/cctest/test-profile-generator.cc index 6d30443178..250ebd4a5f 100644 --- a/deps/v8/test/cctest/test-profile-generator.cc +++ b/deps/v8/test/cctest/test-profile-generator.cc @@ -7,8 +7,6 @@ #include "cctest.h" #include "../include/v8-profiler.h" -namespace i = v8::internal; - using i::CodeEntry; using i::CodeMap; using i::CpuProfile; diff --git a/deps/v8/test/cctest/test-unbound-queue.cc b/deps/v8/test/cctest/test-unbound-queue.cc index df5509ef87..3dc87ae246 100644 --- a/deps/v8/test/cctest/test-unbound-queue.cc +++ b/deps/v8/test/cctest/test-unbound-queue.cc @@ -6,8 +6,6 @@ #include "unbound-queue-inl.h" #include "cctest.h" -namespace i = v8::internal; - using i::UnboundQueue; diff --git a/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js b/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js new file mode 100644 index 0000000000..a407c531a7 --- /dev/null +++ b/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js @@ -0,0 +1,224 @@ +// Copyright 2011 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: --expose-debug-as debug --harmony-block-scoping +// The functions used for testing backtraces. They are at the top to make the +// testing of source line/column easier. + + +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug; + +var test_name; +var listener_delegate; +var listener_called; +var exception; +var begin_test_count = 0; +var end_test_count = 0; +var break_count = 0; + + +// Debug event listener which delegates. +function listener(event, exec_state, event_data, data) { + try { + if (event == Debug.DebugEvent.Break) { + break_count++; + listener_called = true; + listener_delegate(exec_state); + } + } catch (e) { + exception = e; + } +} + +// Add the debug event listener. +Debug.setListener(listener); + + +// Initialize for a new test. +function BeginTest(name) { + test_name = name; + listener_delegate = null; + listener_called = false; + exception = null; + begin_test_count++; +} + + +// Check result of a test. +function EndTest() { + assertTrue(listener_called, "listerner not called for " + test_name); + assertNull(exception, test_name); + end_test_count++; +} + + +// Check that the scope chain contains the expected types of scopes. +function CheckScopeChain(scopes, exec_state) { + assertEquals(scopes.length, exec_state.frame().scopeCount()); + for (var i = 0; i < scopes.length; i++) { + var scope = exec_state.frame().scope(i); + assertTrue(scope.isScope()); + assertEquals(scopes[i], scope.scopeType()); + + // Check the global object when hitting the global scope. + if (scopes[i] == debug.ScopeType.Global) { + // Objects don't have same class (one is "global", other is "Object", + // so just check the properties directly. + assertPropertiesEqual(this, scope.scopeObject().value()); + } + } + + // Get the debug command processor. + var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); + + // Send a scopes request and check the result. + var json; + var request_json = '{"seq":0,"type":"request","command":"scopes"}'; + var response_json = dcp.processDebugJSONRequest(request_json); + var response = JSON.parse(response_json); + assertEquals(scopes.length, response.body.scopes.length); + for (var i = 0; i < scopes.length; i++) { + assertEquals(i, response.body.scopes[i].index); + assertEquals(scopes[i], response.body.scopes[i].type); + if (scopes[i] == debug.ScopeType.Local || + scopes[i] == debug.ScopeType.Closure) { + assertTrue(response.body.scopes[i].object.ref < 0); + } else { + assertTrue(response.body.scopes[i].object.ref >= 0); + } + var found = false; + for (var j = 0; j < response.refs.length && !found; j++) { + found = response.refs[j].handle == response.body.scopes[i].object.ref; + } + assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); + } +} + +// Check that the content of the scope is as expected. For functions just check +// that there is a function. +function CheckScopeContent(content, number, exec_state) { + var scope = exec_state.frame().scope(number); + var count = 0; + for (var p in content) { + var property_mirror = scope.scopeObject().property(p); + if (property_mirror.isUndefined()) { + print('property ' + p + ' not found in scope'); + } + assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); + if (typeof(content[p]) === 'function') { + assertTrue(property_mirror.value().isFunction()); + } else { + assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); + } + count++; + } + + // 'arguments' and might be exposed in the local and closure scope. Just + // ignore this. + var scope_size = scope.scopeObject().properties().length; + if (!scope.scopeObject().property('arguments').isUndefined()) { + scope_size--; + } + // Also ignore synthetic variable from catch block. + if (!scope.scopeObject().property('.catch-var').isUndefined()) { + scope_size--; + } + // Skip property with empty name. + if (!scope.scopeObject().property('').isUndefined()) { + scope_size--; + } + // Also ignore synthetic variable from block scopes. + if (!scope.scopeObject().property('.block').isUndefined()) { + scope_size--; + } + + if (count != scope_size) { + print('Names found in scope:'); + var names = scope.scopeObject().propertyNames(); + for (var i = 0; i < names.length; i++) { + print(names[i]); + } + } + assertEquals(count, scope_size); + + // Get the debug command processor. + var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); + + // Send a scope request for information on a single scope and check the + // result. + var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; + request_json += scope.scopeIndex(); + request_json += '}}'; + var response_json = dcp.processDebugJSONRequest(request_json); + var response = JSON.parse(response_json); + assertEquals(scope.scopeType(), response.body.type); + assertEquals(number, response.body.index); + if (scope.scopeType() == debug.ScopeType.Local || + scope.scopeType() == debug.ScopeType.Closure) { + assertTrue(response.body.object.ref < 0); + } else { + assertTrue(response.body.object.ref >= 0); + } + var found = false; + for (var i = 0; i < response.refs.length && !found; i++) { + found = response.refs[i].handle == response.body.object.ref; + } + assertTrue(found, "Scope object " + response.body.object.ref + " not found"); +} + + +// Simple closure formed by returning an inner function referering to an outer +// block local variable and an outer function's parameter. Due to VM +// optimizations parts of the actual closure is missing from the debugger +// information. +BeginTest("Closure 1"); + +function closure_1(a) { + var x = 2; + let y = 3; + if (true) { + let z = 4; + function f() { + debugger; + return a + x + y + z; + }; + return f; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Local, + debug.ScopeType.Block, + debug.ScopeType.Closure, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({z:4}, 1, exec_state); + CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); +}; +closure_1(1)(); +EndTest(); diff --git a/deps/v8/test/mjsunit/external-array.js b/deps/v8/test/mjsunit/external-array.js index 94105ec9d4..d7e9de0291 100644 --- a/deps/v8/test/mjsunit/external-array.js +++ b/deps/v8/test/mjsunit/external-array.js @@ -190,9 +190,19 @@ function run_test(test_func, array, expected_result) { gc(); // Makes V8 forget about type information for test_func. } +function run_bounds_test(test_func, array, expected_result) { + assertEquals(undefined, a[kElementCount]); + a[kElementCount] = 456; + assertEquals(undefined, a[kElementCount]); + assertEquals(undefined, a[kElementCount+1]); + a[kElementCount+1] = 456; + assertEquals(undefined, a[kElementCount+1]); +} + for (var t = 0; t < types.length; t++) { var type = types[t]; var a = new type(kElementCount); + for (var i = 0; i < kElementCount; i++) { a[i] = i; } @@ -220,6 +230,16 @@ for (var t = 0; t < types.length; t++) { assertTrue(delete a.length); a.length = 2; assertEquals(2, a.length); + + // Make sure bounds checks are handled correctly for external arrays. + run_bounds_test(a); + run_bounds_test(a); + run_bounds_test(a); + %OptimizeFunctionOnNextCall(run_bounds_test); + run_bounds_test(a); + %DeoptimizeFunction(run_bounds_test); + gc(); // Makes V8 forget about type information for test_func. + } function array_load_set_smi_check(a) { diff --git a/deps/v8/test/mjsunit/fuzz-natives.js b/deps/v8/test/mjsunit/fuzz-natives.js index ffa92684dd..f8f0a28e70 100644 --- a/deps/v8/test/mjsunit/fuzz-natives.js +++ b/deps/v8/test/mjsunit/fuzz-natives.js @@ -146,6 +146,7 @@ var knownProblems = { "NewStrictArgumentsFast": true, "PushWithContext": true, "PushCatchContext": true, + "PushBlockContext": true, "LazyCompile": true, "LazyRecompile": true, "NotifyDeoptimized": true, diff --git a/deps/v8/test/mjsunit/harmony/block-lazy-compile.js b/deps/v8/test/mjsunit/harmony/block-lazy-compile.js new file mode 100644 index 0000000000..a6efcbfd10 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-lazy-compile.js @@ -0,0 +1,50 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax +// Test deserialization of block contexts during lazy compilation +// of closures. + +function f() { + var g; + { + // TODO(keuchel): introduce let + var x = 0; + g = function () { + x = x + 1; + return x; + } + } + return g; +} + +var o = f(); +assertEquals(1, o()); +assertEquals(2, o()); +assertEquals(3, o()); +%OptimizeFunctionOnNextCall(o); +assertEquals(4, o()); diff --git a/deps/v8/test/mjsunit/harmony/block-let-declaration.js b/deps/v8/test/mjsunit/harmony/block-let-declaration.js new file mode 100644 index 0000000000..19c943f14c --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-let-declaration.js @@ -0,0 +1,67 @@ +// Copyright 2011 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-block-scoping + +// Test let declarations in various settings. + +// Global +let x; +let y = 2; + +// Block local +{ + let y; + let x = 3; +} + +assertEquals(undefined, x); +assertEquals(2,y); + +if (true) { + let y; + assertEquals(undefined, y); +} + +function TestLocalThrows(str, expect) { + assertThrows("(function(){" + str + "})()", expect); +} + +function TestLocalDoesNotThrow(str) { + assertDoesNotThrow("(function(){" + str + "})()"); +} + +// Unprotected statement +TestLocalThrows("if (true) let x;", SyntaxError); +TestLocalThrows("with ({}) let x;", SyntaxError); +TestLocalThrows("do let x; while (false)", SyntaxError); +TestLocalThrows("while (false) let x;", SyntaxError); + +TestLocalDoesNotThrow("if (true) var x;"); +TestLocalDoesNotThrow("with ({}) var x;"); +TestLocalDoesNotThrow("do var x; while (false)"); +TestLocalDoesNotThrow("while (false) var x;"); diff --git a/deps/v8/test/mjsunit/harmony/block-scoping.js b/deps/v8/test/mjsunit/harmony/block-scoping.js new file mode 100644 index 0000000000..266e380725 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-scoping.js @@ -0,0 +1,216 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --harmony-block-scoping +// Test functionality of block scopes. + +// Hoisting of var declarations. +function f1() { + { + var x = 1; + var y; + } + assertEquals(1, x) + assertEquals(undefined, y) +} +f1(); + + +// Dynamic lookup in and through block contexts. +function f2(one) { + var x = one + 1; + let y = one + 2; + { + let z = one + 3; + assertEquals(1, eval('one')); + assertEquals(2, eval('x')); + assertEquals(3, eval('y')); + assertEquals(4, eval('z')); + } +} +f2(1); + + +// Lookup in and through block contexts. +function f3(one) { + var x = one + 1; + let y = one + 2; + { + let z = one + 3; + assertEquals(1, one); + assertEquals(2, x); + assertEquals(3, y); + assertEquals(4, z); + } +} +f3(1); + + +// Dynamic lookup from closure. +function f4(one) { + var x = one + 1; + let y = one + 2; + { + let z = one + 3; + function f() { + assertEquals(1, eval('one')); + assertEquals(2, eval('x')); + assertEquals(3, eval('y')); + assertEquals(4, eval('z')); + }; + } +} +f4(1); + + +// Lookup from closure. +function f5(one) { + var x = one + 1; + let y = one + 2; + { + let z = one + 3; + function f() { + assertEquals(1, one); + assertEquals(2, x); + assertEquals(3, y); + assertEquals(4, z); + }; + } +} +f5(1); + + +// Return from block. +function f6() { + let x = 1; + { + let y = 2; + return x + y; + } +} +assertEquals(3, f6(6)); + + +// Variable shadowing and lookup. +function f7(a) { + let b = 1; + var c = 1; + var d = 1; + { // let variables shadowing argument, let and var variables + let a = 2; + let b = 2; + let c = 2; + assertEquals(2,a); + assertEquals(2,b); + assertEquals(2,c); + } + try { + throw 'stuff1'; + } catch (a) { + assertEquals('stuff1',a); + // catch variable shadowing argument + a = 2; + assertEquals(2,a); + { + // let variable shadowing catch variable + let a = 3; + assertEquals(3,a); + try { + throw 'stuff2'; + } catch (a) { + assertEquals('stuff2',a); + // catch variable shadowing let variable + a = 4; + assertEquals(4,a); + } + assertEquals(3,a); + } + assertEquals(2,a); + } + try { + throw 'stuff3'; + } catch (c) { + // catch variable shadowing var variable + assertEquals('stuff3',c); + try { + throw 'stuff4'; + } catch(c) { + assertEquals('stuff4',c); + // catch variable shadowing catch variable + c = 3; + assertEquals(3,c); + } + (function(c) { + // argument shadowing catch variable + c = 3; + assertEquals(3,c); + })(); + assertEquals('stuff3', c); + (function() { + // var variable shadowing catch variable + var c = 3; + })(); + assertEquals('stuff3', c); + c = 2; + } + assertEquals(1,c); + (function(a,b,c) { + // arguments shadowing argument, let and var variable + a = 2; + b = 2; + c = 2; + assertEquals(2,a); + assertEquals(2,b); + assertEquals(2,c); + // var variable shadowing var variable + var d = 2; + })(1,1); + assertEquals(1,a); + assertEquals(1,b); + assertEquals(1,c); + assertEquals(1,d); +} +f7(1); + + +// Ensure let variables are block local and var variables function local. +function f8() { + var let_accessors = []; + var var_accessors = []; + for (var i = 0; i < 10; i++) { + let x = i; + var y = i; + let_accessors[i] = function() { return x; } + var_accessors[i] = function() { return y; } + } + for (var j = 0; j < 10; j++) { + y = j + 10; + assertEquals(j, let_accessors[j]()); + assertEquals(y, var_accessors[j]()); + } +} +f8(); diff --git a/deps/v8/test/mjsunit/harmony/debug-blockscopes.js b/deps/v8/test/mjsunit/harmony/debug-blockscopes.js new file mode 100644 index 0000000000..e0df71b2df --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/debug-blockscopes.js @@ -0,0 +1,389 @@ +// Copyright 2011 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: --expose-debug-as debug --harmony-block-scoping +// The functions used for testing backtraces. They are at the top to make the +// testing of source line/column easier. + + +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug; + +var test_name; +var listener_delegate; +var listener_called; +var exception; +var begin_test_count = 0; +var end_test_count = 0; +var break_count = 0; + + +// Debug event listener which delegates. +function listener(event, exec_state, event_data, data) { + try { + if (event == Debug.DebugEvent.Break) { + break_count++; + listener_called = true; + listener_delegate(exec_state); + } + } catch (e) { + exception = e; + } +} + +// Add the debug event listener. +Debug.setListener(listener); + + +// Initialize for a new test. +function BeginTest(name) { + test_name = name; + listener_delegate = null; + listener_called = false; + exception = null; + begin_test_count++; +} + + +// Check result of a test. +function EndTest() { + assertTrue(listener_called, "listerner not called for " + test_name); + assertNull(exception, test_name); + end_test_count++; +} + + +// Check that the scope chain contains the expected types of scopes. +function CheckScopeChain(scopes, exec_state) { + assertEquals(scopes.length, exec_state.frame().scopeCount()); + for (var i = 0; i < scopes.length; i++) { + var scope = exec_state.frame().scope(i); + assertTrue(scope.isScope()); + assertEquals(scopes[i], scope.scopeType()); + + // Check the global object when hitting the global scope. + if (scopes[i] == debug.ScopeType.Global) { + // Objects don't have same class (one is "global", other is "Object", + // so just check the properties directly. + assertPropertiesEqual(this, scope.scopeObject().value()); + } + } + + // Get the debug command processor. + var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); + + // Send a scopes request and check the result. + var json; + var request_json = '{"seq":0,"type":"request","command":"scopes"}'; + var response_json = dcp.processDebugJSONRequest(request_json); + var response = JSON.parse(response_json); + assertEquals(scopes.length, response.body.scopes.length); + for (var i = 0; i < scopes.length; i++) { + assertEquals(i, response.body.scopes[i].index); + assertEquals(scopes[i], response.body.scopes[i].type); + if (scopes[i] == debug.ScopeType.Local || + scopes[i] == debug.ScopeType.Closure) { + assertTrue(response.body.scopes[i].object.ref < 0); + } else { + assertTrue(response.body.scopes[i].object.ref >= 0); + } + var found = false; + for (var j = 0; j < response.refs.length && !found; j++) { + found = response.refs[j].handle == response.body.scopes[i].object.ref; + } + assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); + } +} + +// Check that the content of the scope is as expected. For functions just check +// that there is a function. +function CheckScopeContent(content, number, exec_state) { + var scope = exec_state.frame().scope(number); + var count = 0; + for (var p in content) { + var property_mirror = scope.scopeObject().property(p); + if (property_mirror.isUndefined()) { + print('property ' + p + ' not found in scope'); + } + assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); + if (typeof(content[p]) === 'function') { + assertTrue(property_mirror.value().isFunction()); + } else { + assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); + } + count++; + } + + // 'arguments' and might be exposed in the local and closure scope. Just + // ignore this. + var scope_size = scope.scopeObject().properties().length; + if (!scope.scopeObject().property('arguments').isUndefined()) { + scope_size--; + } + // Also ignore synthetic variable from catch block. + if (!scope.scopeObject().property('.catch-var').isUndefined()) { + scope_size--; + } + // Skip property with empty name. + if (!scope.scopeObject().property('').isUndefined()) { + scope_size--; + } + // Also ignore synthetic variable from block scopes. + if (!scope.scopeObject().property('.block').isUndefined()) { + scope_size--; + } + + if (count != scope_size) { + print('Names found in scope:'); + var names = scope.scopeObject().propertyNames(); + for (var i = 0; i < names.length; i++) { + print(names[i]); + } + } + assertEquals(count, scope_size); + + // Get the debug command processor. + var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); + + // Send a scope request for information on a single scope and check the + // result. + var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; + request_json += scope.scopeIndex(); + request_json += '}}'; + var response_json = dcp.processDebugJSONRequest(request_json); + var response = JSON.parse(response_json); + assertEquals(scope.scopeType(), response.body.type); + assertEquals(number, response.body.index); + if (scope.scopeType() == debug.ScopeType.Local || + scope.scopeType() == debug.ScopeType.Closure) { + assertTrue(response.body.object.ref < 0); + } else { + assertTrue(response.body.object.ref >= 0); + } + var found = false; + for (var i = 0; i < response.refs.length && !found; i++) { + found = response.refs[i].handle == response.body.object.ref; + } + assertTrue(found, "Scope object " + response.body.object.ref + " not found"); +} + + +// Simple empty block scope in local scope. +BeginTest("Local block 1"); + +function local_block_1() { + { + debugger; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({}, 1, exec_state); +}; +local_block_1(); +EndTest(); + + +// Local scope with a parameter. +BeginTest("Local 2"); + +function local_2(a) { + { + debugger; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({a:1}, 1, exec_state); +}; +local_2(1); +EndTest(); + + +// Local scope with a parameter and a local variable. +BeginTest("Local 3"); + +function local_3(a) { + let x = 3; + debugger; +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({a:1,x:3}, 0, exec_state); +}; +local_3(1); +EndTest(); + + +// Local scope with parameters and local variables. +BeginTest("Local 4"); + +function local_4(a, b) { + let x = 3; + let y = 4; + debugger; +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state); +}; +local_4(1, 2); +EndTest(); + + +// Single empty with block. +BeginTest("With block 1"); + +function with_block_1() { + with({}) { + debugger; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({}, 1, exec_state); +}; +with_block_1(); +EndTest(); + + +// Nested empty with blocks. +BeginTest("With block 2"); + +function with_block_2() { + with({}) { + with({}) { + debugger; + } + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({}, 1, exec_state); + CheckScopeContent({}, 2, exec_state); + CheckScopeContent({}, 3, exec_state); +}; +with_block_2(); +EndTest(); + + +// With block using an in-place object literal. +BeginTest("With block 3"); + +function with_block_3() { + with({a:1,b:2}) { + debugger; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({a:1,b:2}, 1, exec_state); +}; +with_block_3(); +EndTest(); + + +// Nested with blocks using in-place object literals. +BeginTest("With block 4"); + +function with_block_4() { + with({a:1,b:2}) { + with({a:2,b:1}) { + debugger; + } + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Block, + debug.ScopeType.With, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({a:2,b:1}, 1, exec_state); + CheckScopeContent({a:1,b:2}, 3, exec_state); +}; +with_block_4(); +EndTest(); + + +// Simple closure formed by returning an inner function referering to an outer +// block local variable and an outer function's parameter. +BeginTest("Closure 1"); + +function closure_1(a) { + var x = 2; + let y = 3; + if (true) { + let z = 4; + function f() { + debugger; + return a + x + y + z; + }; + return f; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Local, + debug.ScopeType.Block, + debug.ScopeType.Closure, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); + CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); +}; +closure_1(1)(); +EndTest(); diff --git a/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js b/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js new file mode 100644 index 0000000000..549960a289 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js @@ -0,0 +1,64 @@ +// Copyright 2011 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: --expose-debug-as debug --harmony-block-scoping + +// Test debug evaluation for functions without local context, but with +// nested catch contexts. + +function f() { + { // Line 1. + let i = 1; // Line 2. + try { // Line 3. + throw 'stuff'; // Line 4. + } catch (e) { // Line 5. + x = 2; // Line 6. + } + } +}; + +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug +// Set breakpoint on line 6. +var bp = Debug.setBreakPoint(f, 6); + +function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + result = exec_state.frame().evaluate("i").value(); + } +}; + +// Add the debug event listener. +Debug.setListener(listener); +result = -1; +f(); +assertEquals(1, result); + +// Clear breakpoint. +Debug.clearBreakPoint(bp); +// Get rid of the debug event listener. +Debug.setListener(null); diff --git a/deps/v8/test/mjsunit/harmony/typeof.js b/deps/v8/test/mjsunit/harmony/typeof.js new file mode 100644 index 0000000000..acde97785f --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/typeof.js @@ -0,0 +1,35 @@ +// Copyright 2011 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-typeof + +assertFalse(typeof null == 'object') +assertFalse(typeof null === 'object') +assertTrue(typeof null == 'null') +assertTrue(typeof null === 'null') +assertEquals("null", typeof null) +assertSame("null", typeof null) diff --git a/deps/v8/test/mjsunit/harmony/weakmaps.js b/deps/v8/test/mjsunit/harmony/weakmaps.js index d98db10df3..97f553cf29 100644 --- a/deps/v8/test/mjsunit/harmony/weakmaps.js +++ b/deps/v8/test/mjsunit/harmony/weakmaps.js @@ -139,6 +139,23 @@ assertTrue(WeakMap.prototype.has instanceof Function) assertTrue(WeakMap.prototype.delete instanceof Function) +// Regression test for issue 1617: The prototype of the WeakMap constructor +// needs to be unique (i.e. different from the one of the Object constructor). +assertFalse(WeakMap.prototype === Object.prototype); +var o = Object.create({}); +assertFalse("get" in o); +assertFalse("set" in o); +assertEquals(undefined, o.get); +assertEquals(undefined, o.set); +var o = Object.create({}, { myValue: { + value: 10, + enumerable: false, + configurable: true, + writable: true +}}); +assertEquals(10, o.myValue); + + // Stress Test // There is a proposed stress-test available at the es-discuss mailing list // which cannot be reasonably automated. Check it out by hand if you like: diff --git a/deps/v8/test/mjsunit/math-floor.js b/deps/v8/test/mjsunit/math-floor.js index 11f4cd789c..f211ce2e56 100644 --- a/deps/v8/test/mjsunit/math-floor.js +++ b/deps/v8/test/mjsunit/math-floor.js @@ -27,10 +27,11 @@ // Flags: --max-new-space-size=256 --allow-natives-syntax +var test_id = 0; + function testFloor(expect, input) { - function test(n) { - return Math.floor(n); - } + var test = new Function('n', + '"' + (test_id++) + '";return Math.floor(n)'); assertEquals(expect, test(input)); assertEquals(expect, test(input)); assertEquals(expect, test(input)); @@ -51,6 +52,17 @@ function test() { testFloor(-Infinity, -Infinity); testFloor(NaN, NaN); + // Ensure that a negative zero coming from Math.floor is properly handled + // by other operations. + function ifloor(x) { + return 1 / Math.floor(x); + } + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + %OptimizeFunctionOnNextCall(ifloor); + assertEquals(-Infinity, ifloor(-0)); + testFloor(0, 0.1); testFloor(0, 0.49999999999999994); testFloor(0, 0.5); @@ -129,3 +141,19 @@ function test() { for (var i = 0; i < 500; i++) { test(); } + + +// Regression test for a bug where a negative zero coming from Math.floor +// was not properly handled by other operations. +function floorsum(i, n) { + var ret = Math.floor(n); + while (--i > 0) { + ret += Math.floor(n); + } + return ret; +} +assertEquals(-0, floorsum(1, -0)); +%OptimizeFunctionOnNextCall(floorsum); +// The optimized function will deopt. Run it with enough iterations to try +// to optimize via OSR (triggering the bug). +assertEquals(-0, floorsum(100000, -0)); diff --git a/deps/v8/test/mjsunit/math-round.js b/deps/v8/test/mjsunit/math-round.js index 1366557f6b..102c970f38 100644 --- a/deps/v8/test/mjsunit/math-round.js +++ b/deps/v8/test/mjsunit/math-round.js @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -27,10 +27,12 @@ // Flags: --allow-natives-syntax +var test_id = 0; function testRound(expect, input) { - function doRound(input) { - return Math.round(input); - } + // Make source code different on each invocation to make + // sure it gets optimized each time. + var doRound = new Function('input', + '"' + (test_id++) + '";return Math.round(input)'); assertEquals(expect, doRound(input)); assertEquals(expect, doRound(input)); assertEquals(expect, doRound(input)); @@ -44,6 +46,21 @@ testRound(Infinity, Infinity); testRound(-Infinity, -Infinity); testRound(NaN, NaN); +// Regression test for a bug where a negative zero coming from Math.round +// was not properly handled by other operations. +function roundsum(i, n) { + var ret = Math.round(n); + while (--i > 0) { + ret += Math.round(n); + } + return ret; +} +assertEquals(-0, roundsum(1, -0)); +%OptimizeFunctionOnNextCall(roundsum); +// The optimized function will deopt. Run it with enough iterations to try +// to optimize via OSR (triggering the bug). +assertEquals(-0, roundsum(100000, -0)); + testRound(1, 0.5); testRound(1, 0.7); testRound(1, 1); diff --git a/deps/v8/test/mjsunit/regress/regress-1592.js b/deps/v8/test/mjsunit/regress/regress-1592.js new file mode 100644 index 0000000000..8f6fba01a9 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1592.js @@ -0,0 +1,45 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +var f = { + apply: function(a, b) {} +}; + +function test(a) { + f.apply(this, arguments); +} + +// Initialize ICs. +test(1); +test(1); + +%OptimizeFunctionOnNextCall(test); + +// Kaboom! +test(1); diff --git a/deps/v8/test/mjsunit/with-leave.js b/deps/v8/test/mjsunit/with-leave.js index ded62caf57..7369faa505 100644 --- a/deps/v8/test/mjsunit/with-leave.js +++ b/deps/v8/test/mjsunit/with-leave.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -59,3 +59,162 @@ try { } assertTrue(caught); + +// We want to test the context chain shape. In each of the tests cases +// below, the outer with is to force a runtime lookup of the identifier 'x' +// to actually verify that the inner context has been discarded. A static +// lookup of 'x' might accidentally succeed. +with ({x: 'outer'}) { + label: { + with ({x: 'inner'}) { + break label; + } + } + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + label: { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + break label; + } + } + } + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + for (var i = 0; i < 10; ++i) { + with ({x: 'inner' + i}) { + continue; + } + } + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + label: for (var i = 0; i < 10; ++i) { + with ({x: 'middle' + i}) { + for (var j = 0; j < 10; ++j) { + with ({x: 'inner' + j}) { + continue label; + } + } + } + } + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + assertEquals('outer', x); + } +} + + +with ({x: 'outer'}) { + try { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + throw 0; + } + } + } catch (e) { + assertEquals('outer', x); + } +} + + +try { + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } finally { + assertEquals('outer', x); + } + } +} catch (e) { + if (e instanceof MjsUnitAssertionError) throw e; +} + + +try { + with ({x: 'outer'}) { + try { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + throw 0; + } + } + } finally { + assertEquals('outer', x); + } + } +} catch (e) { + if (e instanceof MjsUnitAssertionError) throw e; +} + + +// Verify that the context is correctly set in the stack frame after exiting +// from with. +function f() {} + +with ({x: 'outer'}) { + label: { + with ({x: 'inner'}) { + break label; + } + } + f(); // The context could be restored from the stack after the call. + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + for (var i = 0; i < 10; ++i) { + with ({x: 'inner' + i}) { + continue; + } + } + f(); + assertEquals('outer', x); +} + + +with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + f(); + assertEquals('outer', x); + } +} + + +try { + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } finally { + f(); + assertEquals('outer', x); + } + } +} catch (e) { + if (e instanceof MjsUnitAssertionError) throw e; +} diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index e755fcb926..03972463aa 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -497,7 +497,6 @@ '../../src/code-stubs.cc', '../../src/code-stubs.h', '../../src/code.h', - '../../src/codegen-inl.h', '../../src/codegen.cc', '../../src/codegen.h', '../../src/compilation-cache.cc', |