diff options
author | Feng Qian <fqian@google.com> | 2009-04-17 10:56:30 -0700 |
---|---|---|
committer | Feng Qian <fqian@google.com> | 2009-04-17 10:56:30 -0700 |
commit | 9d6c0fe1a576c623a6db3b95e0460519f984da64 (patch) | |
tree | b7eb0f703cb467f01b29b67633532072c34536cf | |
parent | e62a68bd8f6c9cb81ff8757c55d64ae13a277eab (diff) | |
download | external_webkit-9d6c0fe1a576c623a6db3b95e0460519f984da64.zip external_webkit-9d6c0fe1a576c623a6db3b95e0460519f984da64.tar.gz external_webkit-9d6c0fe1a576c623a6db3b95e0460519f984da64.tar.bz2 |
Update v8 source to bleeding_edge 1738.
Git thinks bug-269.js and func-name-inferrer.cc are similar because both has copyright info and small code size.
100 files changed, 3348 insertions, 708 deletions
diff --git a/v8/ChangeLog b/v8/ChangeLog index 5433a70..d5c30c7 100644 --- a/v8/ChangeLog +++ b/v8/ChangeLog @@ -1,3 +1,43 @@ +2009-04-15: Version 1.1.10 + + Fixed crash bug that occurred when loading a const variable in the + presence of eval. + + Allowed using with and eval in registered extensions in debug mode + by fixing bogus assert. + + Fixed the source position for function returns to enable the + debugger to break there. + + +2009-04-14: Version 1.1.9 + + Made the stack traversal code in the profiler robust by avoiding + to look into the heap. + + Added name inferencing for anonymous functions to facilitate + debugging and profiling. + + Re-enabled stats timers in the developer shell (d8). + + Fixed issue 303 by avoiding to shortcut cons-symbols. + + +2009-04-11: Version 1.1.8 + + Changed test-debug/ThreadedDebugging to be non-flaky (issue 96). + + Fixed step-in handling for Function.prototype.apply and call in + the debugger (issue 269). + + Fixed v8::Object::DeleteHiddenValue to not bail out when there + are no hidden properties. + + Added workaround for crash bug, where external symbol table + entries with deleted resources would lead to NPEs when looking + up in the symbol table. + + 2009-04-07: Version 1.1.7 Added support for easily importing additional environment diff --git a/v8/REVISION b/v8/REVISION index 7ea9da8..2996207 100644 --- a/v8/REVISION +++ b/v8/REVISION @@ -1 +1 @@ -http://v8.googlecode.com/svn/trunk@1682 +https://v8.googlecode.com/svn/branches/bleeding_edge@1738 diff --git a/v8/SConstruct b/v8/SConstruct index 9654ebd..1024a5b 100644 --- a/v8/SConstruct +++ b/v8/SConstruct @@ -108,7 +108,7 @@ LIBRARY_FLAGS = { 'os:linux': { 'CCFLAGS': ['-ansi'], 'library:shared': { - 'LIBS': ['pthread', 'rt'] + 'LIBS': ['pthread'] } }, 'os:macos': { @@ -142,7 +142,7 @@ LIBRARY_FLAGS = { 'DIALECTFLAGS': ['/nologo'], 'CCFLAGS': ['$DIALECTFLAGS', '$WARNINGFLAGS'], 'CXXFLAGS': ['$CCFLAGS', '/GR-', '/Gy'], - 'CPPDEFINES': ['WIN32', '_USE_32BIT_TIME_T', 'PCRE_STATIC'], + 'CPPDEFINES': ['WIN32', '_USE_32BIT_TIME_T'], 'LINKFLAGS': ['/NOLOGO', '/MACHINE:X86', '/INCREMENTAL:NO', '/NXCOMPAT', '/IGNORE:4221'], 'ARFLAGS': ['/NOLOGO'], @@ -224,7 +224,7 @@ V8_EXTRA_FLAGS = { MKSNAPSHOT_EXTRA_FLAGS = { 'gcc': { 'os:linux': { - 'LIBS': ['pthread', 'rt'], + 'LIBS': ['pthread'], }, 'os:macos': { 'LIBS': ['pthread'], @@ -238,6 +238,7 @@ MKSNAPSHOT_EXTRA_FLAGS = { }, 'msvc': { 'all': { + 'CPPDEFINES': ['_HAS_EXCEPTIONS=0'], 'LIBS': ['winmm', 'ws2_32'] } } @@ -268,7 +269,7 @@ CCTEST_EXTRA_FLAGS = { 'LIBPATH': [abspath('.')] }, 'os:linux': { - 'LIBS': ['pthread', 'rt'], + 'LIBS': ['pthread'], }, 'os:macos': { 'LIBS': ['pthread'], @@ -307,7 +308,7 @@ SAMPLE_FLAGS = { 'CCFLAGS': ['-fno-rtti', '-fno-exceptions'] }, 'os:linux': { - 'LIBS': ['pthread', 'rt'], + 'LIBS': ['pthread'], }, 'os:macos': { 'LIBS': ['pthread'], @@ -387,7 +388,7 @@ D8_FLAGS = { 'LIBS': ['readline'] }, 'os:linux': { - 'LIBS': ['pthread', 'rt'], + 'LIBS': ['pthread'], }, 'os:macos': { 'LIBS': ['pthread'], diff --git a/v8/samples/process.cc b/v8/samples/process.cc index 6567f08..511e21a 100644 --- a/v8/samples/process.cc +++ b/v8/samples/process.cc @@ -27,8 +27,6 @@ #include <v8.h> -// To avoid warnings from <map> on windows we disable exceptions. -#define _HAS_EXCEPTIONS 0 #include <string> #include <map> diff --git a/v8/src/SConscript b/v8/src/SConscript index 9b7ff02..97fb7d6 100644 --- a/v8/src/SConscript +++ b/v8/src/SConscript @@ -40,7 +40,8 @@ SOURCES = { 'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc', 'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc', 'debug-agent.cc', 'disassembler.cc', 'execution.cc', 'factory.cc', - 'flags.cc', 'frames.cc', 'global-handles.cc', 'handles.cc', 'hashmap.cc', + 'flags.cc', 'frames.cc', 'func-name-inferrer.cc', + 'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc', 'jump-target.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc', 'oprofile-agent.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc', diff --git a/v8/src/api.cc b/v8/src/api.cc index 1ddb2c6..a5af312 100644 --- a/v8/src/api.cc +++ b/v8/src/api.cc @@ -2077,13 +2077,13 @@ bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) { ON_BAILOUT("v8::DeleteHiddenValue()", return false); ENTER_V8; i::Handle<i::JSObject> self = Utils::OpenHandle(this); - i::Handle<i::JSObject> hidden_props( - i::JSObject::cast(*i::GetHiddenProperties(self, false))); + i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, false)); if (hidden_props->IsUndefined()) { - return false; + return true; } + i::Handle<i::JSObject> js_obj(i::JSObject::cast(*hidden_props)); i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - return i::DeleteProperty(hidden_props, key_obj)->IsTrue(); + return i::DeleteProperty(js_obj, key_obj)->IsTrue(); } @@ -2373,7 +2373,7 @@ bool v8::V8::Dispose() { const char* v8::V8::GetVersion() { - return "1.1.7"; + return "1.1.11 (candidate)"; } diff --git a/v8/src/assembler.cc b/v8/src/assembler.cc index ad4b24c..977be7d 100644 --- a/v8/src/assembler.cc +++ b/v8/src/assembler.cc @@ -582,4 +582,40 @@ ExternalReference ExternalReference::debug_step_in_fp_address() { return ExternalReference(Debug::step_in_fp_addr()); } + +static double add_two_doubles(double x, double y) { + return x + y; +} + + +static double sub_two_doubles(double x, double y) { + return x - y; +} + + +static double mul_two_doubles(double x, double y) { + return x * y; +} + + +ExternalReference ExternalReference::double_fp_operation( + Token::Value operation) { + typedef double BinaryFPOperation(double x, double y); + BinaryFPOperation* function = NULL; + switch (operation) { + case Token::ADD: + function = &add_two_doubles; + break; + case Token::SUB: + function = &sub_two_doubles; + break; + case Token::MUL: + function = &mul_two_doubles; + break; + default: + UNREACHABLE(); + } + return ExternalReference(FUNCTION_ADDR(function)); +} + } } // namespace v8::internal diff --git a/v8/src/assembler.h b/v8/src/assembler.h index 49c9b90..0352a70 100644 --- a/v8/src/assembler.h +++ b/v8/src/assembler.h @@ -38,6 +38,7 @@ #include "runtime.h" #include "top.h" #include "zone-inl.h" +#include "token.h" namespace v8 { namespace internal { @@ -340,22 +341,6 @@ class RelocIterator: public Malloced { }; -// A stack-allocated code region logs a name for the code generated -// while the region is in effect. This information is used by the -// profiler to categorize ticks within generated code. -class CodeRegion BASE_EMBEDDED { - public: - inline CodeRegion(Assembler* assm, const char *name) : assm_(assm) { - LOG(BeginCodeRegionEvent(this, assm, name)); - } - inline ~CodeRegion() { - LOG(EndCodeRegionEvent(this, assm_)); - } - private: - Assembler* assm_; -}; - - //------------------------------------------------------------------------------ // External function @@ -417,6 +402,8 @@ class ExternalReference BASE_EMBEDDED { // Used to check if single stepping is enabled in generated code. static ExternalReference debug_step_in_fp_address(); + static ExternalReference double_fp_operation(Token::Value operation); + Address address() const {return address_;} private: diff --git a/v8/src/ast.h b/v8/src/ast.h index 5dfd21d..b496816 100644 --- a/v8/src/ast.h +++ b/v8/src/ast.h @@ -1223,7 +1223,8 @@ class FunctionLiteral: public Expression { end_position_(end_position), is_expression_(is_expression), loop_nesting_(0), - function_token_position_(RelocInfo::kNoPosition) { + function_token_position_(RelocInfo::kNoPosition), + inferred_name_(Heap::empty_string()) { #ifdef DEBUG already_compiled_ = false; #endif @@ -1253,6 +1254,11 @@ class FunctionLiteral: public Expression { bool loop_nesting() const { return loop_nesting_; } void set_loop_nesting(int nesting) { loop_nesting_ = nesting; } + Handle<String> inferred_name() const { return inferred_name_; } + void set_inferred_name(Handle<String> inferred_name) { + inferred_name_ = inferred_name; + } + #ifdef DEBUG void mark_as_compiled() { ASSERT(!already_compiled_); @@ -1273,6 +1279,7 @@ class FunctionLiteral: public Expression { bool is_expression_; int loop_nesting_; int function_token_position_; + Handle<String> inferred_name_; #ifdef DEBUG bool already_compiled_; #endif diff --git a/v8/src/builtins-arm.cc b/v8/src/builtins-arm.cc index 2e6f255..26ed45b 100644 --- a/v8/src/builtins-arm.cc +++ b/v8/src/builtins-arm.cc @@ -34,7 +34,7 @@ namespace v8 { namespace internal { -#define __ masm-> +#define __ DEFINE_MASM(masm) void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) { @@ -218,8 +218,9 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ mov(r5, Operand(r4)); __ mov(r6, Operand(r4)); __ mov(r7, Operand(r4)); - if (kR9Available == 1) + if (kR9Available == 1) { __ mov(r9, Operand(r4)); + } // Invoke the code and pass argc as r0. __ mov(r0, Operand(r3)); diff --git a/v8/src/codegen-arm.cc b/v8/src/codegen-arm.cc index 37a7cf2..2386b97 100644 --- a/v8/src/codegen-arm.cc +++ b/v8/src/codegen-arm.cc @@ -38,7 +38,8 @@ namespace v8 { namespace internal { -#define __ masm_-> +#define __ DEFINE_MASM(masm_) + // ------------------------------------------------------------------------- // CodeGenState implementation. @@ -653,37 +654,27 @@ void CodeGenerator::ToBoolean(JumpTarget* true_target, } -class GetPropertyStub : public CodeStub { - public: - GetPropertyStub() { } - - private: - Major MajorKey() { return GetProperty; } - int MinorKey() { return 0; } - void Generate(MacroAssembler* masm); -}; - - -class SetPropertyStub : public CodeStub { - public: - SetPropertyStub() { } - - private: - Major MajorKey() { return SetProperty; } - int MinorKey() { return 0; } - void Generate(MacroAssembler* masm); -}; - - class GenericBinaryOpStub : public CodeStub { public: - explicit GenericBinaryOpStub(Token::Value op) : op_(op) { } + GenericBinaryOpStub(Token::Value op, + OverwriteMode mode) + : op_(op), mode_(mode) { } private: Token::Value op_; + OverwriteMode mode_; + + // Minor key encoding in 16 bits. + class ModeBits: public BitField<OverwriteMode, 0, 2> {}; + class OpBits: public BitField<Token::Value, 2, 14> {}; Major MajorKey() { return GenericBinaryOp; } - int MinorKey() { return static_cast<int>(op_); } + int MinorKey() { + // Encode the parameters in a unique 16 bit value. + return OpBits::encode(op_) + | ModeBits::encode(mode_); + } + void Generate(MacroAssembler* masm); const char* GetName() { @@ -708,7 +699,8 @@ class GenericBinaryOpStub : public CodeStub { }; -void CodeGenerator::GenericBinaryOperation(Token::Value op) { +void CodeGenerator::GenericBinaryOperation(Token::Value op, + OverwriteMode overwrite_mode) { VirtualFrame::SpilledScope spilled_scope(this); // sp[0] : y // sp[1] : x @@ -727,7 +719,7 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op) { case Token::SAR: { frame_->EmitPop(r0); // r0 : y frame_->EmitPop(r1); // r1 : x - GenericBinaryOpStub stub(op); + GenericBinaryOpStub stub(op, overwrite_mode); frame_->CallStub(&stub, 0); break; } @@ -767,11 +759,13 @@ class DeferredInlineSmiOperation: public DeferredCode { DeferredInlineSmiOperation(CodeGenerator* generator, Token::Value op, int value, - bool reversed) + bool reversed, + OverwriteMode overwrite_mode) : DeferredCode(generator), op_(op), value_(value), - reversed_(reversed) { + reversed_(reversed), + overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlinedSmiOperation"); } @@ -781,6 +775,7 @@ class DeferredInlineSmiOperation: public DeferredCode { Token::Value op_; int value_; bool reversed_; + OverwriteMode overwrite_mode_; }; @@ -844,7 +839,7 @@ void DeferredInlineSmiOperation::Generate() { break; } - GenericBinaryOpStub igostub(op_); + GenericBinaryOpStub igostub(op_, overwrite_mode_); Result arg0 = generator()->allocator()->Allocate(r1); ASSERT(arg0.is_valid()); Result arg1 = generator()->allocator()->Allocate(r0); @@ -856,7 +851,8 @@ void DeferredInlineSmiOperation::Generate() { void CodeGenerator::SmiOperation(Token::Value op, Handle<Object> value, - bool reversed) { + bool reversed, + OverwriteMode mode) { VirtualFrame::SpilledScope spilled_scope(this); // NOTE: This is an attempt to inline (a bit) more of the code for // some possible smi operations (like + and -) when (at least) one @@ -875,7 +871,7 @@ void CodeGenerator::SmiOperation(Token::Value op, switch (op) { case Token::ADD: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed); + new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); __ add(r0, r0, Operand(value), SetCC); deferred->enter()->Branch(vs); @@ -887,7 +883,7 @@ void CodeGenerator::SmiOperation(Token::Value op, case Token::SUB: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed); + new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); if (!reversed) { __ sub(r0, r0, Operand(value), SetCC); @@ -905,7 +901,7 @@ void CodeGenerator::SmiOperation(Token::Value op, case Token::BIT_XOR: case Token::BIT_AND: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed); + new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); __ tst(r0, Operand(kSmiTagMask)); deferred->enter()->Branch(ne); switch (op) { @@ -925,12 +921,12 @@ void CodeGenerator::SmiOperation(Token::Value op, __ mov(ip, Operand(value)); frame_->EmitPush(ip); frame_->EmitPush(r0); - GenericBinaryOperation(op); + GenericBinaryOperation(op, mode); } else { int shift_value = int_value & 0x1f; // least significant 5 bits DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, shift_value, false); + new DeferredInlineSmiOperation(this, op, shift_value, false, mode); __ tst(r0, Operand(kSmiTagMask)); deferred->enter()->Branch(ne); __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags @@ -982,7 +978,7 @@ void CodeGenerator::SmiOperation(Token::Value op, frame_->EmitPush(ip); frame_->EmitPush(r0); } - GenericBinaryOperation(op); + GenericBinaryOperation(op, mode); break; } @@ -1487,8 +1483,8 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( // Test for a Smi value in a HeapNumber. __ tst(r0, Operand(kSmiTagMask)); is_smi.Branch(eq); - __ ldr(r1, MemOperand(r0, HeapObject::kMapOffset - kHeapObjectTag)); - __ ldrb(r1, MemOperand(r1, Map::kInstanceTypeOffset - kHeapObjectTag)); + __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); + __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); __ cmp(r1, Operand(HEAP_NUMBER_TYPE)); default_target->Branch(ne); frame_->EmitPush(r0); @@ -2446,8 +2442,13 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { r1, r2, &slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ cmp(r0, Operand(Factory::the_hole_value())); + __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq); + } // There is always control flow to slow from - // ContextSlotOperandCheckExtensions. + // ContextSlotOperandCheckExtensions so we have to jump around + // it. done.Jump(); } } @@ -2518,7 +2519,9 @@ void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot, if (s->is_eval_scope()) { Label next, fast; - if (!context.is(tmp)) __ mov(tmp, Operand(context)); + if (!context.is(tmp)) { + __ mov(tmp, Operand(context)); + } __ bind(&next); // Terminate at global context. __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset)); @@ -2929,15 +2932,24 @@ void CodeGenerator::VisitAssignment(Assignment* node) { LoadAndSpill(node->value()); } else { + // +=, *= and similar binary assignments. + // Get the old value of the lhs. target.GetValueAndSpill(NOT_INSIDE_TYPEOF); Literal* literal = node->value()->AsLiteral(); + bool overwrite = + (node->value()->AsBinaryOperation() != NULL && + node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); if (literal != NULL && literal->handle()->IsSmi()) { - SmiOperation(node->binary_op(), literal->handle(), false); + SmiOperation(node->binary_op(), + literal->handle(), + false, + overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE); frame_->EmitPush(r0); } else { LoadAndSpill(node->value()); - GenericBinaryOperation(node->binary_op()); + GenericBinaryOperation(node->binary_op(), + overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE); frame_->EmitPush(r0); } } @@ -3817,19 +3829,39 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { // is a literal small integer. Literal* lliteral = node->left()->AsLiteral(); Literal* rliteral = node->right()->AsLiteral(); + // NOTE: The code below assumes that the slow cases (calls to runtime) + // never return a constant/immutable object. + bool overwrite_left = + (node->left()->AsBinaryOperation() != NULL && + node->left()->AsBinaryOperation()->ResultOverwriteAllowed()); + bool overwrite_right = + (node->right()->AsBinaryOperation() != NULL && + node->right()->AsBinaryOperation()->ResultOverwriteAllowed()); if (rliteral != NULL && rliteral->handle()->IsSmi()) { LoadAndSpill(node->left()); - SmiOperation(node->op(), rliteral->handle(), false); + SmiOperation(node->op(), + rliteral->handle(), + false, + overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE); } else if (lliteral != NULL && lliteral->handle()->IsSmi()) { LoadAndSpill(node->right()); - SmiOperation(node->op(), lliteral->handle(), true); + SmiOperation(node->op(), + lliteral->handle(), + true, + overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE); } else { + OverwriteMode overwrite_mode = NO_OVERWRITE; + if (overwrite_left) { + overwrite_mode = OVERWRITE_LEFT; + } else if (overwrite_right) { + overwrite_mode = OVERWRITE_RIGHT; + } LoadAndSpill(node->left()); LoadAndSpill(node->right()); - GenericBinaryOperation(node->op()); + GenericBinaryOperation(node->op(), overwrite_mode); } frame_->EmitPush(r0); } @@ -4062,7 +4094,8 @@ bool CodeGenerator::HasValidEntryRegisters() { return true; } #undef __ -#define __ masm-> +#define __ DEFINE_MASM(masm) + Handle<String> Reference::GetName() { ASSERT(type_ == NAMED); @@ -4300,167 +4333,80 @@ void Reference::SetValue(InitState init_state) { } -void GetPropertyStub::Generate(MacroAssembler* masm) { - // sp[0]: key - // sp[1]: receiver - Label slow, fast; - // Get the key and receiver object from the stack. - __ ldm(ia, sp, r0.bit() | r1.bit()); - // Check that the key is a smi. - __ tst(r0, Operand(kSmiTagMask)); - __ b(ne, &slow); - __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - // Check that the object isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &slow); - - // Check that the object is some kind of JS object EXCEPT JS Value type. - // In the case that the object is a value-wrapper object, - // we enter the runtime system to make sure that indexing into string - // objects work as intended. - ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_OBJECT_TYPE)); - __ b(lt, &slow); - - // Get the elements array of the object. - __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); - // Check that the object is in fast mode (not dictionary). - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ cmp(r3, Operand(Factory::hash_table_map())); - __ b(eq, &slow); - // Check that the key (index) is within bounds. - __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset)); - __ cmp(r0, Operand(r3)); - __ b(lo, &fast); - - // Slow case: Push extra copies of the arguments (2). - __ bind(&slow); - __ ldm(ia, sp, r0.bit() | r1.bit()); - __ stm(db_w, sp, r0.bit() | r1.bit()); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2); - - // Fast case: Do the load. - __ bind(&fast); - __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag)); - __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2)); - __ cmp(r0, Operand(Factory::the_hole_value())); - // In case the loaded value is the_hole we have to consult GetProperty - // to ensure the prototype chain is searched. - __ b(eq, &slow); - - __ StubReturn(1); -} - - -void SetPropertyStub::Generate(MacroAssembler* masm) { - // r0 : value - // sp[0] : key - // sp[1] : receiver - - Label slow, fast, array, extra, exit; - // Get the key and the object from the stack. - __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver - // Check that the key is a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(ne, &slow); - // Check that the object isn't a smi. - __ tst(r3, Operand(kSmiTagMask)); - __ b(eq, &slow); - // Get the type of the object from its map. - __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - // Check if the object is a JS array or not. - __ cmp(r2, Operand(JS_ARRAY_TYPE)); - __ b(eq, &array); - // Check that the object is some kind of JS object. - __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE)); - __ b(lt, &slow); - - - // Object case: Check key against length in the elements array. - __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset)); - // Check that the object is in fast mode (not dictionary). - __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ cmp(r2, Operand(Factory::hash_table_map())); - __ b(eq, &slow); - // Untag the key (for checking against untagged length in the fixed array). - __ mov(r1, Operand(r1, ASR, kSmiTagSize)); - // Compute address to store into and check array bounds. - __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag)); - __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2)); - __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset)); - __ cmp(r1, Operand(ip)); - __ b(lo, &fast); - - - // Slow case: Push extra copies of the arguments (3). +static void HandleBinaryOpSlowCases(MacroAssembler* masm, + Label* not_smi, + const Builtins::JavaScript& builtin, + Token::Value operation, + int swi_number, + OverwriteMode mode) { + Label slow; + if (mode == NO_OVERWRITE) { + __ bind(not_smi); + } __ bind(&slow); - __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object - __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); - - - // Extra capacity case: Check if there is extra capacity to - // perform the store and update the length. Used for adding one - // element to the array by writing to array[array.length]. - // r0 == value, r1 == key, r2 == elements, r3 == object - __ bind(&extra); - __ b(ne, &slow); // do not leave holes in the array - __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag - __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset)); - __ cmp(r1, Operand(ip)); - __ b(hs, &slow); - __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag - __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment - __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset)); - __ mov(r3, Operand(r2)); - // NOTE: Computing the address to store into must take the fact - // that the key has been incremented into account. - int displacement = Array::kHeaderSize - kHeapObjectTag - - ((1 << kSmiTagSize) * 2); - __ add(r2, r2, Operand(displacement)); - __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); - __ b(&fast); - - - // Array case: Get the length and the elements array from the JS - // array. Check that the array is in fast mode; if it is the - // length is always a smi. - // r0 == value, r3 == object - __ bind(&array); - __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset)); - __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); - __ cmp(r1, Operand(Factory::hash_table_map())); - __ b(eq, &slow); + __ push(r1); + __ push(r0); + __ mov(r0, Operand(1)); // Set number of arguments. + __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. - // Check the key against the length in the array, compute the - // address to store into and fall through to fast case. - __ ldr(r1, MemOperand(sp)); - // r0 == value, r1 == key, r2 == elements, r3 == object. - __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset)); - __ cmp(r1, Operand(ip)); - __ b(hs, &extra); - __ mov(r3, Operand(r2)); - __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag)); - __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); - - - // Fast case: Do the store. - // r0 == value, r2 == address to store into, r3 == elements - __ bind(&fast); - __ str(r0, MemOperand(r2)); - // Skip write barrier if the written value is a smi. - __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &exit); - // Update write barrier for the elements array address. - __ sub(r1, r2, Operand(r3)); - __ RecordWrite(r3, r1, r2); - __ bind(&exit); - __ StubReturn(1); + // Could it be a double-double op? If we already have a place to put + // the answer then we can do the op and skip the builtin and runtime call. + if (mode != NO_OVERWRITE) { + __ bind(not_smi); + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &slow); // We can't handle a Smi-double combination yet. + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &slow); // We can't handle a Smi-double combination yet. + // Get map of r0 into r2. + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + // Get type of r0 into r3. + __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); + __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); + __ b(ne, &slow); + // Get type of r1 into r3. + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + // Check they are both the same map (heap number map). + __ cmp(r2, r3); + __ b(ne, &slow); + // Both are doubles. + // Calling convention says that second double is in r2 and r3. + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset)); + __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize)); + __ push(lr); + if (mode == OVERWRITE_LEFT) { + __ push(r1); + } else { + __ push(r0); + } + // Calling convention says that first double is in r0 and r1. + __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset)); + __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize)); + // Call C routine that may not cause GC or other trouble. + __ mov(r5, Operand(ExternalReference::double_fp_operation(operation))); +#if !defined(__arm__) + // Notify the simulator that we are calling an add routine in C. + __ swi(swi_number); +#else + // Actually call the add routine written in C. + __ Call(r5); +#endif + // Store answer in the overwritable heap number. + __ pop(r4); +#if !defined(__ARM_EABI__) && defined(__arm__) + // Double returned in fp coprocessor register 0 and 1, encoded as register + // cr8. Offsets must be divisible by 4 for coprocessor so we need to + // substract the tag from r4. + __ sub(r5, r4, Operand(kHeapObjectTag)); + __ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset)); +#else + // Double returned in fp coprocessor register 0 and 1. + __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset)); + __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize)); +#endif + __ mov(r0, Operand(r4)); + // And we are done. + __ pop(pc); + } } @@ -4469,89 +4415,84 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // r0 : y // result : r0 + // All ops need to know whether we are dealing with two Smis. Set up r2 to + // tell us that. + __ orr(r2, r1, Operand(r0)); // r2 = x | y; + switch (op_) { case Token::ADD: { - Label slow, exit; - // fast path - __ orr(r2, r1, Operand(r0)); // r2 = x | y; - __ add(r0, r1, Operand(r0), SetCC); // add y optimistically - // go slow-path in case of overflow - __ b(vs, &slow); - // go slow-path in case of non-smi operands - ASSERT(kSmiTag == 0); // adjust code below + Label not_smi; + // Fast path. + ASSERT(kSmiTag == 0); // Adjust code below. __ tst(r2, Operand(kSmiTagMask)); - __ b(eq, &exit); - // slow path - __ bind(&slow); - __ sub(r0, r0, Operand(r1)); // revert optimistic add - __ push(r1); - __ push(r0); - __ mov(r0, Operand(1)); // set number of arguments - __ InvokeBuiltin(Builtins::ADD, JUMP_JS); - // done - __ bind(&exit); + __ b(ne, ¬_smi); + __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically. + // Return if no overflow. + __ Ret(vc); + __ sub(r0, r0, Operand(r1)); // Revert optimistic add. + + HandleBinaryOpSlowCases(masm, + ¬_smi, + Builtins::ADD, + Token::ADD, + assembler::arm::simulator_fp_add, + mode_); break; } case Token::SUB: { - Label slow, exit; - // fast path - __ orr(r2, r1, Operand(r0)); // r2 = x | y; - __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically - // go slow-path in case of overflow - __ b(vs, &slow); - // go slow-path in case of non-smi operands - ASSERT(kSmiTag == 0); // adjust code below + Label not_smi; + // Fast path. + ASSERT(kSmiTag == 0); // Adjust code below. __ tst(r2, Operand(kSmiTagMask)); - __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result - __ b(eq, &exit); - // slow path - __ bind(&slow); - __ push(r1); - __ push(r0); - __ mov(r0, Operand(1)); // set number of arguments - __ InvokeBuiltin(Builtins::SUB, JUMP_JS); - // done - __ bind(&exit); + __ b(ne, ¬_smi); + __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically. + // Return if no overflow. + __ Ret(vc); + __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract. + + HandleBinaryOpSlowCases(masm, + ¬_smi, + Builtins::SUB, + Token::SUB, + assembler::arm::simulator_fp_sub, + mode_); break; } case Token::MUL: { - Label slow, exit; - // tag check - __ orr(r2, r1, Operand(r0)); // r2 = x | y; + Label not_smi, slow; ASSERT(kSmiTag == 0); // adjust code below __ tst(r2, Operand(kSmiTagMask)); - __ b(ne, &slow); - // remove tag from one operand (but keep sign), so that result is smi + __ b(ne, ¬_smi); + // Remove tag from one operand (but keep sign), so that result is Smi. __ mov(ip, Operand(r0, ASR, kSmiTagSize)); - // do multiplication - __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1 - // go slow on overflows (overflow bit is not set) + // Do multiplication + __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1. + // Go slow on overflows (overflow bit is not set). __ mov(ip, Operand(r3, ASR, 31)); __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical __ b(ne, &slow); - // go slow on zero result to handle -0 + // Go slow on zero result to handle -0. __ tst(r3, Operand(r3)); __ mov(r0, Operand(r3), LeaveCC, ne); - __ b(ne, &exit); - // slow case + __ Ret(ne); + // Slow case. __ bind(&slow); - __ push(r1); - __ push(r0); - __ mov(r0, Operand(1)); // set number of arguments - __ InvokeBuiltin(Builtins::MUL, JUMP_JS); - // done - __ bind(&exit); + + HandleBinaryOpSlowCases(masm, + ¬_smi, + Builtins::MUL, + Token::MUL, + assembler::arm::simulator_fp_mul, + mode_); break; } case Token::BIT_OR: case Token::BIT_AND: case Token::BIT_XOR: { - Label slow, exit; - // tag check - __ orr(r2, r1, Operand(r0)); // r2 = x | y; + Label slow; ASSERT(kSmiTag == 0); // adjust code below __ tst(r2, Operand(kSmiTagMask)); __ b(ne, &slow); @@ -4561,7 +4502,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; default: UNREACHABLE(); } - __ b(&exit); + __ Ret(); __ bind(&slow); __ push(r1); // restore stack __ push(r0); @@ -4579,16 +4520,13 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { default: UNREACHABLE(); } - __ bind(&exit); break; } case Token::SHL: case Token::SHR: case Token::SAR: { - Label slow, exit; - // tag check - __ orr(r2, r1, Operand(r0)); // r2 = x | y; + Label slow; ASSERT(kSmiTag == 0); // adjust code below __ tst(r2, Operand(kSmiTagMask)); __ b(ne, &slow); @@ -4628,7 +4566,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // tag result and store it in r0 ASSERT(kSmiTag == 0); // adjust code below __ mov(r0, Operand(r3, LSL, kSmiTagSize)); - __ b(&exit); + __ Ret(); // slow case __ bind(&slow); __ push(r1); // restore stack @@ -4640,13 +4578,13 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; default: UNREACHABLE(); } - __ bind(&exit); break; } default: UNREACHABLE(); } - __ Ret(); + // This code should be unreachable. + __ stop("Unreachable"); } @@ -4716,7 +4654,9 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { __ mov(cp, Operand(0), LeaveCC, eq); // Restore cp otherwise. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); - if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); + if (kDebug && FLAG_debug_code) { + __ mov(lr, Operand(pc)); + } __ pop(pc); } @@ -4779,7 +4719,9 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { __ mov(cp, Operand(0), LeaveCC, eq); // Restore cp otherwise. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); - if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); + if (kDebug && FLAG_debug_code) { + __ mov(lr, Operand(pc)); + } __ pop(pc); } @@ -5038,9 +4980,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { } __ ldr(ip, MemOperand(ip)); // deref address - // Branch and link to JSEntryTrampoline + // Branch and link to JSEntryTrampoline. We don't use the double underscore + // macro for the add instruction because we don't want the coverage tool + // inserting instructions here after we read the pc. __ mov(lr, Operand(pc)); - __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); + masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); // Unlink this frame from the handler chain. When reading the // address of the next handler, there is no need to use the address @@ -5052,6 +4996,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // No need to restore registers __ add(sp, sp, Operand(StackHandlerConstants::kSize)); + __ bind(&exit); // r0 holds result // Restore the top frame descriptors from the stack. __ pop(r3); @@ -5063,7 +5008,9 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Restore callee-saved registers and return. #ifdef DEBUG - if (FLAG_debug_code) __ mov(lr, Operand(pc)); + if (FLAG_debug_code) { + __ mov(lr, Operand(pc)); + } #endif __ ldm(ia_w, sp, kCalleeSaved | pc.bit()); } diff --git a/v8/src/codegen-arm.h b/v8/src/codegen-arm.h index 54c5e88..48b92ea 100644 --- a/v8/src/codegen-arm.h +++ b/v8/src/codegen-arm.h @@ -35,9 +35,6 @@ class DeferredCode; class RegisterAllocator; class RegisterFile; -// Mode to overwrite BinaryExpression values. -enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; - enum InitState { CONST_INIT, NOT_CONST_INIT }; enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; @@ -160,7 +157,8 @@ class CodeGenerator: public AstVisitor { int end_position, bool is_expression, bool is_toplevel, - Handle<Script> script); + Handle<Script> script, + Handle<String> inferred_name); // Accessors MacroAssembler* masm() { return masm_; } @@ -291,10 +289,13 @@ class CodeGenerator: public AstVisitor { void ToBoolean(JumpTarget* true_target, JumpTarget* false_target); - void GenericBinaryOperation(Token::Value op); + void GenericBinaryOperation(Token::Value op, OverwriteMode overwrite_mode); void Comparison(Condition cc, bool strict = false); - void SmiOperation(Token::Value op, Handle<Object> value, bool reversed); + void SmiOperation(Token::Value op, + Handle<Object> value, + bool reversed, + OverwriteMode mode); void CallWithArguments(ZoneList<Expression*>* arguments, int position); diff --git a/v8/src/codegen-ia32.cc b/v8/src/codegen-ia32.cc index 714e5bd..47c3538 100644 --- a/v8/src/codegen-ia32.cc +++ b/v8/src/codegen-ia32.cc @@ -3286,8 +3286,14 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { ContextSlotOperandCheckExtensions(potential_slot, value, &slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ cmp(value.reg(), Factory::the_hole_value()); + done.Branch(not_equal, &value); + __ mov(value.reg(), Factory::undefined_value()); + } // There is always control flow to slow from - // ContextSlotOperandCheckExtensions. + // ContextSlotOperandCheckExtensions so we have to jump around + // it. done.Jump(&value); } } @@ -3927,6 +3933,9 @@ void CodeGenerator::VisitAssignment(Assignment* node) { } else { Literal* literal = node->value()->AsLiteral(); + bool overwrite_value = + (node->value()->AsBinaryOperation() != NULL && + node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); Variable* right_var = node->value()->AsVariableProxy()->AsVariable(); // There are two cases where the target is not read in the right hand // side, that are easy to test for: the right hand side is a literal, @@ -3939,7 +3948,9 @@ void CodeGenerator::VisitAssignment(Assignment* node) { target.GetValue(NOT_INSIDE_TYPEOF); } Load(node->value()); - GenericBinaryOperation(node->binary_op(), node->type()); + GenericBinaryOperation(node->binary_op(), + node->type(), + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); } if (var != NULL && diff --git a/v8/src/codegen-ia32.h b/v8/src/codegen-ia32.h index 165f877..a39bc58 100644 --- a/v8/src/codegen-ia32.h +++ b/v8/src/codegen-ia32.h @@ -35,9 +35,6 @@ class DeferredCode; class RegisterAllocator; class RegisterFile; -// Mode to overwrite BinaryExpression values. -enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; - enum InitState { CONST_INIT, NOT_CONST_INIT }; enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; @@ -299,7 +296,8 @@ class CodeGenerator: public AstVisitor { int end_position, bool is_expression, bool is_toplevel, - Handle<Script> script); + Handle<Script> script, + Handle<String> inferred_name); // Accessors MacroAssembler* masm() { return masm_; } @@ -434,7 +432,7 @@ class CodeGenerator: public AstVisitor { void GenericBinaryOperation( Token::Value op, SmiAnalysis* type, - const OverwriteMode overwrite_mode = NO_OVERWRITE); + OverwriteMode overwrite_mode); // If possible, combine two constant smi values using op to produce // a smi result, and push it on the virtual frame, all at compile time. diff --git a/v8/src/codegen.cc b/v8/src/codegen.cc index c8f69c7..edc498d 100644 --- a/v8/src/codegen.cc +++ b/v8/src/codegen.cc @@ -230,7 +230,8 @@ void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun, int end_position, bool is_expression, bool is_toplevel, - Handle<Script> script) { + Handle<Script> script, + Handle<String> inferred_name) { fun->shared()->set_length(length); fun->shared()->set_formal_parameter_count(length); fun->shared()->set_script(*script); @@ -239,6 +240,7 @@ void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun, fun->shared()->set_end_position(end_position); fun->shared()->set_is_expression(is_expression); fun->shared()->set_is_toplevel(is_toplevel); + fun->shared()->set_inferred_name(*inferred_name); } @@ -299,7 +301,8 @@ Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) { CodeGenerator::SetFunctionInfo(function, node->num_parameters(), node->function_token_position(), node->start_position(), node->end_position(), - node->is_expression(), false, script_); + node->is_expression(), false, script_, + node->inferred_name()); // Notify debugger that a new function has been added. Debugger::OnNewFunction(function); @@ -568,7 +571,7 @@ void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) { void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) { if (FLAG_debug_info) { - int pos = fun->start_position(); + int pos = fun->end_position(); if (pos != RelocInfo::kNoPosition) { masm()->RecordStatementPosition(pos); masm()->RecordPosition(pos); diff --git a/v8/src/codegen.h b/v8/src/codegen.h index dd43cc0..54fe330 100644 --- a/v8/src/codegen.h +++ b/v8/src/codegen.h @@ -71,6 +71,11 @@ // CodeForStatementPosition // CodeForSourcePosition + +// Mode to overwrite BinaryExpression values. +enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; + + #ifdef ARM #include "codegen-arm.h" #else diff --git a/v8/src/compiler.cc b/v8/src/compiler.cc index ced094c..63fed4a 100644 --- a/v8/src/compiler.cc +++ b/v8/src/compiler.cc @@ -152,7 +152,8 @@ static Handle<JSFunction> MakeFunction(bool is_global, CodeGenerator::SetFunctionInfo(fun, lit->scope()->num_parameters(), RelocInfo::kNoPosition, lit->start_position(), lit->end_position(), - lit->is_expression(), true, script); + lit->is_expression(), true, script, + lit->inferred_name()); // Hint to the runtime system used when allocating space for initial // property space by setting the expected number of properties for @@ -316,20 +317,22 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared, // name and line number. Check explicit whether logging is enabled as finding // the line number is not for free. if (Logger::is_enabled() || OProfileAgent::is_enabled()) { + Handle<String> func_name(lit->name()->length() > 0 ? + *lit->name() : shared->inferred_name()); if (script->name()->IsString()) { int line_num = GetScriptLineNumber(script, start_position); if (line_num > 0) { line_num += script->line_offset()->value() + 1; } - LOG(CodeCreateEvent("LazyCompile", *code, *lit->name(), + LOG(CodeCreateEvent("LazyCompile", *code, *func_name, String::cast(script->name()), line_num)); - OProfileAgent::CreateNativeCodeRegion(*lit->name(), + OProfileAgent::CreateNativeCodeRegion(*func_name, String::cast(script->name()), line_num, code->address(), code->ExecutableSize()); } else { - LOG(CodeCreateEvent("LazyCompile", *code, *lit->name())); - OProfileAgent::CreateNativeCodeRegion(*lit->name(), code->address(), + LOG(CodeCreateEvent("LazyCompile", *code, *func_name)); + OProfileAgent::CreateNativeCodeRegion(*func_name, code->address(), code->ExecutableSize()); } } diff --git a/v8/src/constants-arm.h b/v8/src/constants-arm.h index f553963..919a892 100644 --- a/v8/src/constants-arm.h +++ b/v8/src/constants-arm.h @@ -106,7 +106,12 @@ enum SoftwareInterruptCodes { call_rt_r5 = 0x10, call_rt_r2 = 0x11, // break point - break_point = 0x20 + break_point = 0x20, + // FP operations. These simulate calling into C for a moment to do fp ops. + // They should trash all caller-save registers. + simulator_fp_add = 0x21, + simulator_fp_sub = 0x22, + simulator_fp_mul = 0x23 }; diff --git a/v8/src/d8-posix.cc b/v8/src/d8-posix.cc index d7ea2ec..c2dc531 100644 --- a/v8/src/d8-posix.cc +++ b/v8/src/d8-posix.cc @@ -105,17 +105,17 @@ static int LengthWithoutIncompleteUtf8(char* buffer, int len) { // Returns false on timeout, true on data ready. static bool WaitOnFD(int fd, int read_timeout, - int* total_timeout, + int total_timeout, struct timeval& start_time) { fd_set readfds, writefds, exceptfds; struct timeval timeout; - if (*total_timeout != -1) { + int gone = 0; + if (total_timeout != -1) { struct timeval time_now; gettimeofday(&time_now, NULL); int seconds = time_now.tv_sec - start_time.tv_sec; - int gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000; - if (gone >= *total_timeout) return false; - *total_timeout -= gone; + gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000; + if (gone >= total_timeout) return false; } FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -123,8 +123,8 @@ static bool WaitOnFD(int fd, FD_SET(fd, &readfds); FD_SET(fd, &exceptfds); if (read_timeout == -1 || - (*total_timeout != -1 && *total_timeout < read_timeout)) { - read_timeout = *total_timeout; + (total_timeout != -1 && total_timeout - gone < read_timeout)) { + read_timeout = total_timeout - gone; } timeout.tv_usec = (read_timeout % 1000) * 1000; timeout.tv_sec = read_timeout / 1000; @@ -306,7 +306,7 @@ static bool ChildLaunchedOK(int* exec_error_fds) { static Handle<Value> GetStdout(int child_fd, struct timeval& start_time, int read_timeout, - int* total_timeout) { + int total_timeout) { Handle<String> accumulator = String::Empty(); const char* source = "function(a, b) { return a + b; }"; Handle<Value> cons_as_obj(Script::Compile(String::New(source))->Run()); @@ -332,7 +332,7 @@ static Handle<Value> GetStdout(int child_fd, read_timeout, total_timeout, start_time) || - (TimeIsOut(start_time, *total_timeout))) { + (TimeIsOut(start_time, total_timeout))) { return ThrowException(String::New("Timed out waiting for output")); } continue; @@ -502,7 +502,7 @@ Handle<Value> Shell::System(const Arguments& args) { Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD], start_time, read_timeout, - &total_timeout); + total_timeout); if (accumulator->IsUndefined()) { kill(pid, SIGINT); // On timeout, kill the subprocess. return accumulator; diff --git a/v8/src/d8.cc b/v8/src/d8.cc index 934bcbd..9648168 100644 --- a/v8/src/d8.cc +++ b/v8/src/d8.cc @@ -268,12 +268,19 @@ Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) { } -int32_t* Counter::Bind(const char* name) { +int32_t* Counter::Bind(const char* name, bool is_histogram) { int i; for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) name_[i] = static_cast<char>(name[i]); name_[i] = '\0'; - return &counter_; + is_histogram_ = is_histogram; + return ptr(); +} + + +void Counter::AddSample(int32_t sample) { + count_++; + sample_total_ += sample; } @@ -302,6 +309,8 @@ void Shell::MapCounters(const char* name) { } counters_ = static_cast<CounterCollection*>(memory); V8::SetCounterFunction(LookupCounter); + V8::SetCreateHistogramFunction(CreateHistogram); + V8::SetAddHistogramSampleFunction(AddHistogramSample); } @@ -316,15 +325,44 @@ int CounterMap::Hash(const char* name) { } -int* Shell::LookupCounter(const char* name) { +Counter* Shell::GetCounter(const char* name, bool is_histogram) { Counter* counter = counter_map_->Lookup(name); + + if (counter == NULL) { + counter = counters_->GetNextCounter(); + if (counter != NULL) { + counter_map_->Set(name, counter); + counter->Bind(name, is_histogram); + } + } else { + ASSERT(counter->is_histogram() == is_histogram); + } + return counter; +} + + +int* Shell::LookupCounter(const char* name) { + Counter* counter = GetCounter(name, false); + if (counter != NULL) { return counter->ptr(); + } else { + return NULL; } - Counter* result = counters_->GetNextCounter(); - if (result == NULL) return NULL; - counter_map_->Set(name, result); - return result->Bind(name); +} + + +void* Shell::CreateHistogram(const char* name, + int min, + int max, + size_t buckets) { + return GetCounter(name, true); +} + + +void Shell::AddHistogramSample(void* histogram, int sample) { + Counter* counter = reinterpret_cast<Counter*>(histogram); + counter->AddSample(sample); } @@ -333,8 +371,12 @@ void Shell::Initialize() { // Set up counters if (i::FLAG_map_counters != NULL) MapCounters(i::FLAG_map_counters); - if (i::FLAG_dump_counters) + if (i::FLAG_dump_counters) { V8::SetCounterFunction(LookupCounter); + V8::SetCreateHistogramFunction(CreateHistogram); + V8::SetAddHistogramSampleFunction(AddHistogramSample); + } + // Initialize the global objects HandleScope scope; Handle<ObjectTemplate> global_template = ObjectTemplate::New(); @@ -406,7 +448,14 @@ void Shell::OnExit() { ::printf("+----------------------------------------+-------------+\n"); for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { Counter* counter = i.CurrentValue(); - ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->value()); + if (counter->is_histogram()) { + ::printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count()); + ::printf("| t:%-36s | %11i |\n", + i.CurrentKey(), + counter->sample_total()); + } else { + ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count()); + } } ::printf("+----------------------------------------+-------------+\n"); } diff --git a/v8/src/d8.h b/v8/src/d8.h index 54ae612..342a0d2 100644 --- a/v8/src/d8.h +++ b/v8/src/d8.h @@ -42,11 +42,16 @@ namespace i = v8::internal; class Counter { public: static const int kMaxNameSize = 64; - int32_t* Bind(const char* name); - int32_t* ptr() { return &counter_; } - int32_t value() { return counter_; } + int32_t* Bind(const char* name, bool histogram); + int32_t* ptr() { return &count_; } + int32_t count() { return count_; } + int32_t sample_total() { return sample_total_; } + bool is_histogram() { return is_histogram_; } + void AddSample(int32_t sample); private: - int32_t counter_; + int32_t count_; + int32_t sample_total_; + bool is_histogram_; uint8_t name_[kMaxNameSize]; }; @@ -116,6 +121,11 @@ class Shell: public i::AllStatic { static void Initialize(); static void OnExit(); static int* LookupCounter(const char* name); + static void* CreateHistogram(const char* name, + int min, + int max, + size_t buckets); + static void AddHistogramSample(void* histogram, int sample); static void MapCounters(const char* name); static Handle<String> ReadFile(const char* name); static void RunShell(); @@ -179,6 +189,7 @@ class Shell: public i::AllStatic { static CounterCollection local_counters_; static CounterCollection* counters_; static i::OS::MemoryMappedFile* counters_file_; + static Counter* GetCounter(const char* name, bool is_histogram); }; diff --git a/v8/src/debug-arm.cc b/v8/src/debug-arm.cc index 9fb77b7..f935a8f 100644 --- a/v8/src/debug-arm.cc +++ b/v8/src/debug-arm.cc @@ -58,7 +58,7 @@ bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { } -#define __ masm-> +#define __ DEFINE_MASM(masm) static void Generate_DebugBreakCallHelper(MacroAssembler* masm, diff --git a/v8/src/debug-delay.js b/v8/src/debug-delay.js index 9944fb3..10d0614 100644 --- a/v8/src/debug-delay.js +++ b/v8/src/debug-delay.js @@ -1547,20 +1547,24 @@ DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { } // Pull out arguments. - var handle = request.arguments.handle; + var handles = request.arguments.handles; // Check for legal arguments. - if (IS_UNDEFINED(handle)) { - return response.failed('Argument "handle" missing'); + if (IS_UNDEFINED(handles)) { + return response.failed('Argument "handles" missing'); } - // Lookup handle. - var mirror = LookupMirror(handle); - if (mirror) { - response.body = mirror; - } else { - return response.failed('Object #' + handle + '# not found'); + // Lookup handles. + var mirrors = {}; + for (var i = 0; i < handles.length; i++) { + var handle = handles[i]; + var mirror = LookupMirror(handle); + if (!mirror) { + return response.failed('Object #' + handle + '# not found'); + } + mirrors[handle] = mirror; } + response.body = mirrors; }; diff --git a/v8/src/debug.cc b/v8/src/debug.cc index 4901f5f..a4bb04d 100644 --- a/v8/src/debug.cc +++ b/v8/src/debug.cc @@ -1160,7 +1160,31 @@ void Debug::HandleStepIn(Handle<JSFunction> function, if (fp == Debug::step_in_fp()) { // Don't allow step into functions in the native context. if (function->context()->global() != Top::context()->builtins()) { - Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared())); + if (function->shared()->code() == + Builtins::builtin(Builtins::FunctionApply) || + function->shared()->code() == + Builtins::builtin(Builtins::FunctionCall)) { + // Handle function.apply and function.call separately to flood the + // function to be called and not the code for Builtins::FunctionApply or + // Builtins::FunctionCall. At the point of the call IC to call either + // Builtins::FunctionApply or Builtins::FunctionCall the expression + // stack has the following content: + // symbol "apply" or "call" + // function apply or call was called on + // receiver for apply or call (first parameter to apply or call) + // ... further arguments to apply or call. + JavaScriptFrameIterator it; + ASSERT(it.frame()->fp() == fp); + ASSERT(it.frame()->GetExpression(1)->IsJSFunction()); + if (it.frame()->GetExpression(1)->IsJSFunction()) { + Handle<JSFunction> + actual_function(JSFunction::cast(it.frame()->GetExpression(1))); + Handle<SharedFunctionInfo> actual_shared(actual_function->shared()); + Debug::FloodWithOneShot(actual_shared); + } + } else { + Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared())); + } } } } @@ -1801,6 +1825,9 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, Debugger::host_dispatch_handler_(reinterpret_cast<void*>(dispatch), Debugger::host_dispatch_handler_data_); } + if (auto_continue && !HasCommands()) { + return; + } continue; } @@ -2006,6 +2033,8 @@ void Debugger::ProcessCommand(Vector<const uint16_t> command) { Logger::DebugTag("Put command on command_queue."); command_queue_.Put(command_copy); command_received_->Signal(); + + // Set the debug command break flag to have the command processed. if (!Debug::InDebugger()) { StackGuard::DebugCommand(); } @@ -2018,7 +2047,7 @@ bool Debugger::HasCommands() { void Debugger::ProcessHostDispatch(void* dispatch) { -// Puts a host dispatch comming from the public API on the queue. + // Puts a host dispatch comming from the public API on the queue. uint16_t hack[3]; hack[0] = 0; hack[1] = reinterpret_cast<uint32_t>(dispatch) >> 16; @@ -2026,6 +2055,11 @@ void Debugger::ProcessHostDispatch(void* dispatch) { Logger::DebugTag("Put dispatch on command_queue."); command_queue_.Put(Vector<uint16_t>(hack, 3).Clone()); command_received_->Signal(); + + // Set the debug command break flag to have the host dispatch processed. + if (!Debug::InDebugger()) { + StackGuard::DebugCommand(); + } } diff --git a/v8/src/disasm-arm.cc b/v8/src/disasm-arm.cc index d19e042..3b7474d 100644 --- a/v8/src/disasm-arm.cc +++ b/v8/src/disasm-arm.cc @@ -261,6 +261,15 @@ void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) { case break_point: Print("break_point"); return; + case simulator_fp_add: + Print("simulator_fp_add"); + return; + case simulator_fp_mul: + Print("simulator_fp_mul"); + return; + case simulator_fp_sub: + Print("simulator_fp_sub"); + return; default: out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", diff --git a/v8/src/frames-arm.cc b/v8/src/frames-arm.cc index fe850a8..121fb75 100644 --- a/v8/src/frames-arm.cc +++ b/v8/src/frames-arm.cc @@ -80,7 +80,7 @@ int JavaScriptFrame::GetProvidedParametersCount() const { Address JavaScriptFrame::GetCallerStackPointer() const { int arguments; - if (Heap::gc_state() != Heap::NOT_IN_GC) { + if (Heap::gc_state() != Heap::NOT_IN_GC || disable_heap_access_) { // The arguments for cooked frames are traversed as if they were // expression stack elements of the calling frame. The reason for // this rather strange decision is that we cannot access the diff --git a/v8/src/frames-ia32.cc b/v8/src/frames-ia32.cc index 2b24777..1bc62ec 100644 --- a/v8/src/frames-ia32.cc +++ b/v8/src/frames-ia32.cc @@ -78,7 +78,7 @@ int JavaScriptFrame::GetProvidedParametersCount() const { Address JavaScriptFrame::GetCallerStackPointer() const { int arguments; - if (Heap::gc_state() != Heap::NOT_IN_GC) { + if (Heap::gc_state() != Heap::NOT_IN_GC || disable_heap_access_) { // The arguments for cooked frames are traversed as if they were // expression stack elements of the calling frame. The reason for // this rather strange decision is that we cannot access the diff --git a/v8/src/frames-inl.h b/v8/src/frames-inl.h index 07c8e4e..cb03e2f 100644 --- a/v8/src/frames-inl.h +++ b/v8/src/frames-inl.h @@ -169,19 +169,6 @@ inline bool JavaScriptFrame::has_adapted_arguments() const { } -inline bool JavaScriptFrame::is_at_function() const { - Object* result = function_slot_object(); - // Verify that frame points at correct JS function object. - // We are verifying that function object address and - // the underlying map object address are valid, and that - // function is really a function. - return Heap::Contains(reinterpret_cast<Address>(result)) && - result->IsHeapObject() && - Heap::Contains(HeapObject::cast(result)->map()) && - result->IsJSFunction(); -} - - inline Object* JavaScriptFrame::function() const { Object* result = function_slot_object(); ASSERT(result->IsJSFunction()); diff --git a/v8/src/frames.cc b/v8/src/frames.cc index a7da25a..88c723d 100644 --- a/v8/src/frames.cc +++ b/v8/src/frames.cc @@ -86,6 +86,7 @@ StackFrameIterator::StackFrameIterator(bool use_top, Address fp, Address sp) if (use_top || fp != NULL) { Reset(); } + JavaScriptFrame_.DisableHeapAccess(); } #undef INITIALIZE_SINGLETON @@ -208,7 +209,9 @@ void SafeStackFrameIterator::Advance() { StackFrame* last_frame = iterator_.frame(); Address last_sp = last_frame->sp(), last_fp = last_frame->fp(); // Before advancing to the next stack frame, perform pointer validity tests - iteration_done_ = !IsValidFrame(last_frame) || !IsValidCaller(last_frame); + iteration_done_ = !IsValidFrame(last_frame) || + !CanIterateHandles(last_frame, iterator_.handler()) || + !IsValidCaller(last_frame); if (iteration_done_) return; iterator_.Advance(); @@ -219,12 +222,17 @@ void SafeStackFrameIterator::Advance() { } +bool SafeStackFrameIterator::CanIterateHandles(StackFrame* frame, + StackHandler* handler) { + // If StackIterator iterates over StackHandles, verify that + // StackHandlerIterator can be instantiated (see StackHandlerIterator + // constructor.) + return !is_valid_top_ || (frame->sp() <= handler->address()); +} + + bool SafeStackFrameIterator::IsValidFrame(StackFrame* frame) const { - return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp()) && - // JavaScriptFrame uses function shared info to advance, hence it must - // point to a valid function object. - (!frame->is_java_script() || - reinterpret_cast<JavaScriptFrame*>(frame)->is_at_function()); + return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp()); } @@ -270,7 +278,7 @@ void SafeStackFrameIterator::Reset() { SafeStackTraceFrameIterator::SafeStackTraceFrameIterator( Address fp, Address sp, Address low_bound, Address high_bound) : SafeJavaScriptFrameIterator(fp, sp, low_bound, high_bound) { - if (!done() && !frame()->is_at_function()) Advance(); + if (!done() && !frame()->is_java_script()) Advance(); } @@ -278,7 +286,7 @@ void SafeStackTraceFrameIterator::Advance() { while (true) { SafeJavaScriptFrameIterator::Advance(); if (done()) return; - if (frame()->is_at_function()) return; + if (frame()->is_java_script()) return; } } #endif diff --git a/v8/src/frames.h b/v8/src/frames.h index 78d8e72..8ab4be9 100644 --- a/v8/src/frames.h +++ b/v8/src/frames.h @@ -373,7 +373,6 @@ class JavaScriptFrame: public StandardFrame { virtual Type type() const { return JAVA_SCRIPT; } // Accessors. - inline bool is_at_function() const; inline Object* function() const; inline Object* receiver() const; inline void set_receiver(Object* value); @@ -414,11 +413,19 @@ class JavaScriptFrame: public StandardFrame { protected: explicit JavaScriptFrame(StackFrameIterator* iterator) - : StandardFrame(iterator) { } + : StandardFrame(iterator), disable_heap_access_(false) { } virtual Address GetCallerStackPointer() const; + // When this mode is enabled it is not allowed to access heap objects. + // This is a special mode used when gathering stack samples in profiler. + // A shortcoming is that caller's SP value will be calculated incorrectly + // (see GetCallerStackPointer implementation), but it is not used for stack + // sampling. + void DisableHeapAccess() { disable_heap_access_ = true; } + private: + bool disable_heap_access_; inline Object* function_slot_object() const; friend class StackFrameIterator; @@ -638,6 +645,7 @@ class SafeStackFrameIterator BASE_EMBEDDED { bool IsValidStackAddress(Address addr) const { return IsWithinBounds(low_bound_, high_bound_, addr); } + bool CanIterateHandles(StackFrame* frame, StackHandler* handler); bool IsValidFrame(StackFrame* frame) const; bool IsValidCaller(StackFrame* frame); diff --git a/v8/src/func-name-inferrer.cc b/v8/src/func-name-inferrer.cc new file mode 100644 index 0000000..75f7a99 --- /dev/null +++ b/v8/src/func-name-inferrer.cc @@ -0,0 +1,75 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "ast.h" +#include "func-name-inferrer.h" + +namespace v8 { namespace internal { + + +void FuncNameInferrer::PushEnclosingName(Handle<String> name) { + // Enclosing name is a name of a constructor function. To check + // that it is really a constructor, we check that it is not empty + // and starts with a capital letter. + if (name->length() > 0 && Runtime::IsUpperCaseChar(name->Get(0))) { + names_stack_.Add(name); + } +} + + +Handle<String> FuncNameInferrer::MakeNameFromStack() { + if (names_stack_.is_empty()) { + return Factory::empty_string(); + } else { + return MakeNameFromStackHelper(1, names_stack_.at(0)); + } +} + + +Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos, + Handle<String> prev) { + if (pos >= names_stack_.length()) { + return prev; + } else { + Handle<String> curr = Factory::NewConsString(dot_, names_stack_.at(pos)); + return MakeNameFromStackHelper(pos + 1, Factory::NewConsString(prev, curr)); + } +} + + +void FuncNameInferrer::InferFunctionsNames() { + Handle<String> func_name = MakeNameFromStack(); + for (int i = 0; i < funcs_to_infer_.length(); ++i) { + funcs_to_infer_[i]->set_inferred_name(func_name); + } + funcs_to_infer_.Rewind(0); +} + + +} } // namespace v8::internal diff --git a/v8/src/func-name-inferrer.h b/v8/src/func-name-inferrer.h new file mode 100644 index 0000000..d8270c3 --- /dev/null +++ b/v8/src/func-name-inferrer.h @@ -0,0 +1,122 @@ +// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_FUNC_NAME_INFERRER_H_ +#define V8_FUNC_NAME_INFERRER_H_ + +namespace v8 { namespace internal { + +// FuncNameInferrer is a stateful class that is used to perform name +// inference for anonymous functions during static analysis of source code. +// Inference is performed in cases when an anonymous function is assigned +// to a variable or a property (see test-func-name-inference.cc for examples.) + +// The basic idea is that during AST traversal LHSs of expressions are +// always visited before RHSs. Thus, during visiting the LHS, a name can be +// collected, and during visiting the RHS, a function literal can be collected. +// Inference is performed while leaving the assignment node. + +class FuncNameInferrer BASE_EMBEDDED { + public: + FuncNameInferrer() : + entries_stack_(10), + names_stack_(5), + funcs_to_infer_(4), + dot_(Factory::NewStringFromAscii(CStrVector("."))) { + } + + bool IsOpen() const { return !entries_stack_.is_empty(); } + + void PushEnclosingName(Handle<String> name); + + void Enter() { + entries_stack_.Add(names_stack_.length()); + } + + void PushName(Handle<String> name) { + if (IsOpen()) { + names_stack_.Add(name); + } + } + + void AddFunction(FunctionLiteral* func_to_infer) { + if (IsOpen()) { + funcs_to_infer_.Add(func_to_infer); + } + } + + void InferAndLeave() { + ASSERT(IsOpen()); + if (!funcs_to_infer_.is_empty()) { + InferFunctionsNames(); + } + names_stack_.Rewind(entries_stack_.RemoveLast()); + } + + private: + Handle<String> MakeNameFromStack(); + Handle<String> MakeNameFromStackHelper(int pos, Handle<String> prev); + void InferFunctionsNames(); + + List<int> entries_stack_; + List<Handle<String> > names_stack_; + List<FunctionLiteral*> funcs_to_infer_; + Handle<String> dot_; + + DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer); +}; + + +// A wrapper class that automatically calls InferAndLeave when +// leaving scope. +class ScopedFuncNameInferrer BASE_EMBEDDED { + public: + explicit ScopedFuncNameInferrer(FuncNameInferrer* inferrer) : + inferrer_(inferrer), + is_entered_(false) {} + ~ScopedFuncNameInferrer() { + if (is_entered_) { + inferrer_->InferAndLeave(); + } + } + + void Enter() { + inferrer_->Enter(); + is_entered_ = true; + } + + private: + FuncNameInferrer* inferrer_; + bool is_entered_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFuncNameInferrer); +}; + + +} } // namespace v8::internal + +#endif // V8_FUNC_NAME_INFERRER_H_ diff --git a/v8/src/globals.h b/v8/src/globals.h index 1579c3d..09af766 100644 --- a/v8/src/globals.h +++ b/v8/src/globals.h @@ -146,7 +146,6 @@ class Assembler; class BreakableStatement; class Code; class CodeGenerator; -class CodeRegion; class CodeStub; class Context; class Debug; @@ -514,6 +513,16 @@ inline Dest bit_cast(const Source& source) { } +#ifdef ARM_GENERATED_CODE_COVERAGE +#define CODE_COVERAGE_STRINGIFY(x) #x +#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) +#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__) +#define DEFINE_MASM(masm) masm->stop(__FILE_LINE__); masm-> +#else +#define DEFINE_MASM(masm) masm-> +#endif + + } } // namespace v8::internal #endif // V8_GLOBALS_H_ diff --git a/v8/src/handles.cc b/v8/src/handles.cc index 60d8236..a834564 100644 --- a/v8/src/handles.cc +++ b/v8/src/handles.cc @@ -212,10 +212,11 @@ Handle<Object> SetProperty(Handle<Object> object, } -Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes) { +Handle<Object> IgnoreAttributesAndSetLocalProperty( + Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes) { CALL_HEAP_FUNCTION(object-> IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object); } @@ -491,17 +492,6 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object) { break; } - // Compute the property keys. - content = UnionOfKeys(content, GetEnumPropertyKeys(current)); - - // Add the property keys from the interceptor. - if (current->HasNamedInterceptor()) { - v8::Handle<v8::Array> result = - GetKeysForNamedInterceptor(object, current); - if (!result.IsEmpty()) - content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); - } - // Compute the element keys. Handle<FixedArray> element_keys = Factory::NewFixedArray(current->NumberOfEnumElements()); @@ -515,6 +505,17 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object) { if (!result.IsEmpty()) content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); } + + // Compute the property keys. + content = UnionOfKeys(content, GetEnumPropertyKeys(current)); + + // Add the property keys from the interceptor. + if (current->HasNamedInterceptor()) { + v8::Handle<v8::Array> result = + GetKeysForNamedInterceptor(object, current); + if (!result.IsEmpty()) + content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); + } } } return content; @@ -549,7 +550,7 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) { index++; } } - (*storage)->SortPairs(*sort_array); + (*storage)->SortPairs(*sort_array, sort_array->length()); Handle<FixedArray> bridge_storage = Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength); DescriptorArray* desc = object->map()->instance_descriptors(); diff --git a/v8/src/heap.cc b/v8/src/heap.cc index 26be5a4..7a8b728 100644 --- a/v8/src/heap.cc +++ b/v8/src/heap.cc @@ -374,9 +374,37 @@ void Heap::PerformScavenge() { } +#ifdef DEBUG +// Helper class for verifying the symbol table. +class SymbolTableVerifier : public ObjectVisitor { + public: + SymbolTableVerifier() { } + void VisitPointers(Object** start, Object** end) { + // Visit all HeapObject pointers in [start, end). + for (Object** p = start; p < end; p++) { + if ((*p)->IsHeapObject()) { + // Check that the symbol is actually a symbol. + ASSERT((*p)->IsNull() || (*p)->IsUndefined() || (*p)->IsSymbol()); + } + } + } +}; +#endif // DEBUG + + +static void VerifySymbolTable() { +#ifdef DEBUG + SymbolTableVerifier verifier; + SymbolTable* symbol_table = SymbolTable::cast(Heap::symbol_table()); + symbol_table->IterateElements(&verifier); +#endif // DEBUG +} + + void Heap::PerformGarbageCollection(AllocationSpace space, GarbageCollector collector, GCTracer* tracer) { + VerifySymbolTable(); if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) { ASSERT(!allocation_allowed_); global_gc_prologue_callback_(); @@ -421,6 +449,7 @@ void Heap::PerformGarbageCollection(AllocationSpace space, ASSERT(!allocation_allowed_); global_gc_epilogue_callback_(); } + VerifySymbolTable(); } @@ -813,12 +842,12 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { static inline bool IsShortcutCandidate(HeapObject* object, Map* map) { - // A ConsString object with Heap::empty_string() as the right side - // is a candidate for being shortcut by the scavenger. + STATIC_ASSERT(kNotStringTag != 0 && kSymbolTag != 0); ASSERT(object->map() == map); - if (map->instance_type() >= FIRST_NONSTRING_TYPE) return false; - return (StringShape(map).representation_tag() == kConsStringTag) && - (ConsString::cast(object)->unchecked_second() == Heap::empty_string()); + InstanceType type = map->instance_type(); + if ((type & kShortcutTypeMask) != kShortcutTypeTag) return false; + ASSERT(object->IsString() && !object->IsSymbol()); + return ConsString::cast(object)->unchecked_second() == Heap::empty_string(); } @@ -1384,6 +1413,7 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) { share->set_script(undefined_value()); share->set_start_position_and_type(0); share->set_debug_info(undefined_value()); + share->set_inferred_name(empty_string()); return result; } diff --git a/v8/src/ic-arm.cc b/v8/src/ic-arm.cc index 4db3980..d407f85 100644 --- a/v8/src/ic-arm.cc +++ b/v8/src/ic-arm.cc @@ -39,7 +39,7 @@ namespace v8 { namespace internal { // Static IC stub generators. // -#define __ masm-> +#define __ DEFINE_MASM(masm) // Helper function used from LoadIC/CallIC GenerateNormal. @@ -96,7 +96,9 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // Compute the masked index: (hash + i + i * i) & mask. __ ldr(t1, FieldMemOperand(r2, String::kLengthOffset)); __ mov(t1, Operand(t1, LSR, String::kHashShift)); - if (i > 0) __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i))); + if (i > 0) { + __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i))); + } __ and_(t1, t1, Operand(r3)); // Scale the index by multiplying by the element size. diff --git a/v8/src/log.cc b/v8/src/log.cc index d9e304d..f23b73b 100644 --- a/v8/src/log.cc +++ b/v8/src/log.cc @@ -814,35 +814,6 @@ void Logger::CodeDeleteEvent(Address from) { } -void Logger::BeginCodeRegionEvent(CodeRegion* region, - Assembler* masm, - const char* name) { -#ifdef ENABLE_LOGGING_AND_PROFILING - if (logfile_ == NULL || !FLAG_log_code) return; - LogMessageBuilder msg; - msg.Append("begin-code-region,0x%x,0x%x,0x%x,%s\n", - reinterpret_cast<unsigned int>(region), - reinterpret_cast<unsigned int>(masm), - masm->pc_offset(), - name); - msg.WriteToLogFile(); -#endif -} - - -void Logger::EndCodeRegionEvent(CodeRegion* region, Assembler* masm) { -#ifdef ENABLE_LOGGING_AND_PROFILING - if (logfile_ == NULL || !FLAG_log_code) return; - LogMessageBuilder msg; - msg.Append("end-code-region,0x%x,0x%x,0x%x\n", - reinterpret_cast<unsigned int>(region), - reinterpret_cast<unsigned int>(masm), - masm->pc_offset()); - msg.WriteToLogFile(); -#endif -} - - void Logger::ResourceEvent(const char* name, const char* tag) { #ifdef ENABLE_LOGGING_AND_PROFILING if (logfile_ == NULL || !FLAG_log) return; diff --git a/v8/src/log.h b/v8/src/log.h index bbcfa42..44c1957 100644 --- a/v8/src/log.h +++ b/v8/src/log.h @@ -174,11 +174,6 @@ class Logger { static void CodeMoveEvent(Address from, Address to); // Emits a code delete event. static void CodeDeleteEvent(Address from); - // Emits region delimiters - static void BeginCodeRegionEvent(CodeRegion* region, - Assembler* masm, - const char* name); - static void EndCodeRegionEvent(CodeRegion* region, Assembler* masm); // ==== Events logged by --log-gc. ==== // Heap sampling events: start, end, and individual types. diff --git a/v8/src/macro-assembler-arm.cc b/v8/src/macro-assembler-arm.cc index 88a300b..ef2ffab 100644 --- a/v8/src/macro-assembler-arm.cc +++ b/v8/src/macro-assembler-arm.cc @@ -168,11 +168,11 @@ void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode, } -void MacroAssembler::Ret() { +void MacroAssembler::Ret(Condition cond) { #if USE_BX - bx(lr); + bx(lr, cond); #else - mov(pc, Operand(lr)); + mov(pc, Operand(lr), LeaveCC, cond); #endif } diff --git a/v8/src/macro-assembler-arm.h b/v8/src/macro-assembler-arm.h index 4b999fd..60ef77a 100644 --- a/v8/src/macro-assembler-arm.h +++ b/v8/src/macro-assembler-arm.h @@ -86,7 +86,7 @@ class MacroAssembler: public Assembler { void Call(Register target, Condition cond = al); void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al); void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al); - void Ret(); + void Ret(Condition cond = al); // Jumps to the label at the index given by the Smi in "index". void SmiJumpTable(Register index, Vector<Label*> targets); diff --git a/v8/src/mark-compact.cc b/v8/src/mark-compact.cc index b369cac..c55345d 100644 --- a/v8/src/mark-compact.cc +++ b/v8/src/mark-compact.cc @@ -220,13 +220,7 @@ static inline HeapObject* ShortCircuitConsString(Object** p) { MapWord map_word = object->map_word(); map_word.ClearMark(); InstanceType type = map_word.ToMap()->instance_type(); - if (type >= FIRST_NONSTRING_TYPE || (type & kIsSymbolMask) != 0) { - return object; - } - - StringRepresentationTag rep = - static_cast<StringRepresentationTag>(type & kStringRepresentationMask); - if (rep != kConsStringTag) return object; + if ((type & kShortcutTypeMask) != kShortcutTypeTag) return object; Object* second = reinterpret_cast<ConsString*>(object)->unchecked_second(); if (reinterpret_cast<String*>(second) != Heap::empty_string()) return object; @@ -410,15 +404,19 @@ class SymbolTableCleaner : public ObjectVisitor { ExternalString::kResourceOffset - kHeapObjectTag; if (is_two_byte) { - v8::String::ExternalStringResource* resource = - *reinterpret_cast<v8::String::ExternalStringResource**> + v8::String::ExternalStringResource** resource = + reinterpret_cast<v8::String::ExternalStringResource**> (resource_addr); - delete resource; + delete *resource; + // Clear the resource pointer in the symbol. + *resource = NULL; } else { - v8::String::ExternalAsciiStringResource* resource = - *reinterpret_cast<v8::String::ExternalAsciiStringResource**> + v8::String::ExternalAsciiStringResource** resource = + reinterpret_cast<v8::String::ExternalAsciiStringResource**> (resource_addr); - delete resource; + delete *resource; + // Clear the resource pointer in the symbol. + *resource = NULL; } } // Set the entry to null_value (as deleted). diff --git a/v8/src/mirror-delay.js b/v8/src/mirror-delay.js index 916bc27..9c9d713 100644 --- a/v8/src/mirror-delay.js +++ b/v8/src/mirror-delay.js @@ -1715,8 +1715,8 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, // Collect the JSON property/value pairs in an array. var content = new Array(); - // Add the handle for value mirrors. - if (mirror.isValue()) { + // Add the mirror handle. + if (mirror.isValue() || mirror.isScript()) { content.push(MakeJSONPair_('handle', NumberToJSON_(mirror.handle()))); } @@ -1771,10 +1771,11 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, break; case SCRIPT_TYPE: - // Script is represented by name and source attributes. + // Script is represented by id, name and source attributes. if (mirror.name()) { content.push(MakeJSONPair_('name', StringToJSON_(mirror.name()))); } + content.push(MakeJSONPair_('id', NumberToJSON_(mirror.id()))); content.push(MakeJSONPair_('lineOffset', NumberToJSON_(mirror.lineOffset()))); content.push(MakeJSONPair_('columnOffset', @@ -1908,7 +1909,12 @@ JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { content.push(MakeJSONPair_('index', NumberToJSON_(mirror.index()))); content.push(MakeJSONPair_('receiver', this.serializeReference(mirror.receiver()))); - content.push(MakeJSONPair_('func', this.serializeReference(mirror.func()))); + var func = mirror.func(); + content.push(MakeJSONPair_('func', this.serializeReference(func))); + if (func.script()) { + content.push(MakeJSONPair_('script', + this.serializeReference(func.script()))); + } content.push(MakeJSONPair_('constructCall', BooleanToJSON_(mirror.isConstructCall()))); content.push(MakeJSONPair_('debuggerFrame', diff --git a/v8/src/mksnapshot.cc b/v8/src/mksnapshot.cc index 40a9019..4891f37 100644 --- a/v8/src/mksnapshot.cc +++ b/v8/src/mksnapshot.cc @@ -25,8 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// To avoid warnings from <map> on windows we disable exceptions. -#define _HAS_EXCEPTIONS 0 #include <signal.h> #include <string> #include <map> diff --git a/v8/src/objects-inl.h b/v8/src/objects-inl.h index 762bb63..c2143ea 100644 --- a/v8/src/objects-inl.h +++ b/v8/src/objects-inl.h @@ -263,8 +263,7 @@ bool StringShape::IsSequentialAscii() { bool StringShape::IsSequentialTwoByte() { - return (type_ & (kStringRepresentationMask | kStringEncodingMask)) == - (kSeqStringTag | kTwoByteStringTag); + return full_representation_tag() == (kSeqStringTag | kTwoByteStringTag); } @@ -274,8 +273,7 @@ bool StringShape::IsExternalAscii() { bool StringShape::IsExternalTwoByte() { - return (type_ & (kStringRepresentationMask | kStringEncodingMask)) == - (kExternalStringTag | kTwoByteStringTag); + return full_representation_tag() == (kExternalStringTag | kTwoByteStringTag); } @@ -2077,6 +2075,7 @@ ACCESSORS(SharedFunctionInfo, function_data, Object, ACCESSORS(SharedFunctionInfo, lazy_load_data, Object, kLazyLoadDataOffset) ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset) ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset) +ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset) BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype, kHiddenPrototypeBit) diff --git a/v8/src/objects.cc b/v8/src/objects.cc index 6806829..3273291 100644 --- a/v8/src/objects.cc +++ b/v8/src/objects.cc @@ -43,22 +43,6 @@ namespace v8 { namespace internal { -#define FIELD_ADDR(p, offset) \ - (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag) - - -#define WRITE_FIELD(p, offset, value) \ - (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value) - - -#define WRITE_INT_FIELD(p, offset, value) \ - (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value) - - -#define WRITE_BARRIER(object, offset) \ - Heap::RecordWrite(object->address(), offset); - - // Getters and setters are stored in a fixed array property. These are // constants for their indices. const int kGetterIndex = 0; @@ -679,7 +663,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { SmartPointer<uc16> smart_chars = this->ToWideCString(); ASSERT(memcmp(*smart_chars, resource->data(), - resource->length()*sizeof(**smart_chars)) == 0); + resource->length() * sizeof(**smart_chars)) == 0); } #endif // DEBUG @@ -3301,6 +3285,13 @@ Vector<const uc16> String::ToUC16Vector() { } ASSERT(string_tag == kExternalStringTag); ExternalTwoByteString* ext = ExternalTwoByteString::cast(string); + // This is a workaround for Chromium bug 9746: http://crbug.com/9746 + // For external strings with a deleted resource we return a special + // Vector which will not compare to any string when doing SymbolTable + // lookups. + if (ext->resource() == NULL) { + return Vector<const uc16>(NULL, length); + } const uc16* start = reinterpret_cast<const uc16*>(ext->resource()->data()); return Vector<const uc16>(start + offset, length); @@ -3602,7 +3593,7 @@ void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( while (chars_read < max_chars) { uint16_t c = data[offset]; if (c <= kMaxAsciiCharCode) { - // Fast case for ASCII characters. Cursor is an input output argument. + // Fast case for ASCII characters. Cursor is an input output argument. if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, rbb->util_buffer, rbb->capacity, @@ -4117,6 +4108,18 @@ static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) { } +// This is a workaround for Chromium bug 9746: http://crbug.com/9746 +// Returns true if this Vector matches the problem exposed in the bug. +template <typename T> +static bool CheckVectorForBug9746(Vector<T> vec) { + // The problem is that somehow external string entries in the symbol + // table can have their resources collected while they are still in the + // table. This should not happen according to the test in the function + // DisposeExternalString in api.cc, but we have evidence that it does. + return (vec.start() == NULL) ? true : false; +} + + static StringInputBuffer string_compare_buffer_b; @@ -4127,7 +4130,9 @@ static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) { VectorIterator<char> ib(b->ToAsciiVector()); return CompareStringContents(ia, &ib); } else { - VectorIterator<uc16> ib(b->ToUC16Vector()); + Vector<const uc16> vb = b->ToUC16Vector(); + if (CheckVectorForBug9746(vb)) return false; + VectorIterator<uc16> ib(vb); return CompareStringContents(ia, &ib); } } else { @@ -4169,7 +4174,9 @@ bool String::SlowEquals(String* other) { return CompareRawStringContents(vec1, vec2); } else { VectorIterator<char> buf1(vec1); - VectorIterator<uc16> ib(other->ToUC16Vector()); + Vector<const uc16> vec2 = other->ToUC16Vector(); + if (CheckVectorForBug9746(vec2)) return false; + VectorIterator<uc16> ib(vec2); return CompareStringContents(&buf1, &ib); } } else { @@ -4179,13 +4186,15 @@ bool String::SlowEquals(String* other) { } } else { Vector<const uc16> vec1 = this->ToUC16Vector(); + if (CheckVectorForBug9746(vec1)) return false; if (other->IsFlat()) { if (StringShape(other).IsAsciiRepresentation()) { VectorIterator<uc16> buf1(vec1); VectorIterator<char> ib(other->ToAsciiVector()); return CompareStringContents(&buf1, &ib); } else { - Vector<const uc16> vec2(other->ToUC16Vector()); + Vector<const uc16> vec2 = other->ToUC16Vector(); + if (CheckVectorForBug9746(vec2)) return false; return CompareRawStringContents(vec1, vec2); } } else { @@ -4230,6 +4239,18 @@ bool String::MarkAsUndetectable() { bool String::IsEqualTo(Vector<const char> str) { + // This is a workaround for Chromium bug 9746: http://crbug.com/9746 + // The problem is that somehow external string entries in the symbol + // table can have their resources deleted while they are still in the + // table. This should not happen according to the test in the function + // DisposeExternalString in api.cc but we have evidence that it does. + // Thus we add this bailout here. + StringShape shape(this); + if (shape.IsExternalTwoByte()) { + ExternalTwoByteString* ext = ExternalTwoByteString::cast(this); + if (ext->resource() == NULL) return false; + } + int slen = length(); Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); decoder->Reset(str.start(), str.length()); @@ -4604,7 +4625,7 @@ void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) { IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize); IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize); - IteratePointer(v, kDebugInfoOffset); + IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize); } @@ -5100,7 +5121,7 @@ bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) { VMState state(EXTERNAL); result = getter(index, info); } - if (!result.IsEmpty()) return !result->IsUndefined(); + if (!result.IsEmpty()) return true; } return holder_handle->HasElementPostInterceptor(*receiver_handle, index); } @@ -5827,43 +5848,46 @@ int JSObject::NumberOfEnumProperties() { } -void FixedArray::Swap(int i, int j) { +void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { Object* temp = get(i); set(i, get(j)); set(j, temp); + if (this != numbers) { + temp = numbers->get(i); + numbers->set(i, numbers->get(j)); + numbers->set(j, temp); + } } -static void InsertionSortPairs(FixedArray* content, FixedArray* smis) { - int len = smis->length(); +static void InsertionSortPairs(FixedArray* content, + FixedArray* numbers, + int len) { for (int i = 1; i < len; i++) { int j = i; while (j > 0 && - Smi::cast(smis->get(j-1))->value() > - Smi::cast(smis->get(j))->value()) { - smis->Swap(j-1, j); - content->Swap(j-1, j); + (NumberToUint32(numbers->get(j - 1)) > + NumberToUint32(numbers->get(j)))) { + content->SwapPairs(numbers, j - 1, j); j--; } } } -void HeapSortPairs(FixedArray* content, FixedArray* smis) { +void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) { // In-place heap sort. - ASSERT(content->length() == smis->length()); - int len = smis->length(); + ASSERT(content->length() == numbers->length()); // Bottom-up max-heap construction. for (int i = 1; i < len; ++i) { int child_index = i; while (child_index > 0) { int parent_index = ((child_index + 1) >> 1) - 1; - int parent_value = Smi::cast(smis->get(parent_index))->value(); - int child_value = Smi::cast(smis->get(child_index))->value(); + uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); + uint32_t child_value = NumberToUint32(numbers->get(child_index)); if (parent_value < child_value) { - content->Swap(parent_index, child_index); - smis->Swap(parent_index, child_index); + content->SwapPairs(numbers, parent_index, child_index); } else { break; } @@ -5874,25 +5898,22 @@ void HeapSortPairs(FixedArray* content, FixedArray* smis) { // Extract elements and create sorted array. for (int i = len - 1; i > 0; --i) { // Put max element at the back of the array. - content->Swap(0, i); - smis->Swap(0, i); + content->SwapPairs(numbers, 0, i); // Sift down the new top element. int parent_index = 0; while (true) { int child_index = ((parent_index + 1) << 1) - 1; if (child_index >= i) break; - uint32_t child1_value = Smi::cast(smis->get(child_index))->value(); - uint32_t child2_value = Smi::cast(smis->get(child_index + 1))->value(); - uint32_t parent_value = Smi::cast(smis->get(parent_index))->value(); + uint32_t child1_value = NumberToUint32(numbers->get(child_index)); + uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1)); + uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); if (child_index + 1 >= i || child1_value > child2_value) { if (parent_value > child1_value) break; - content->Swap(parent_index, child_index); - smis->Swap(parent_index, child_index); + content->SwapPairs(numbers, parent_index, child_index); parent_index = child_index; } else { if (parent_value > child2_value) break; - content->Swap(parent_index, child_index + 1); - smis->Swap(parent_index, child_index + 1); + content->SwapPairs(numbers, parent_index, child_index + 1); parent_index = child_index + 1; } } @@ -5900,43 +5921,41 @@ void HeapSortPairs(FixedArray* content, FixedArray* smis) { } -// Sort this array and the smis as pairs wrt. the (distinct) smis. -void FixedArray::SortPairs(FixedArray* smis) { - ASSERT(this->length() == smis->length()); - int len = smis->length(); +// Sort this array and the numbers as pairs wrt. the (distinct) numbers. +void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { + ASSERT(this->length() == numbers->length()); // For small arrays, simply use insertion sort. if (len <= 10) { - InsertionSortPairs(this, smis); + InsertionSortPairs(this, numbers, len); return; } // Check the range of indices. - int min_index = Smi::cast(smis->get(0))->value(); - int max_index = min_index; - int i; + uint32_t min_index = NumberToUint32(numbers->get(0)); + uint32_t max_index = min_index; + uint32_t i; for (i = 1; i < len; i++) { - if (Smi::cast(smis->get(i))->value() < min_index) { - min_index = Smi::cast(smis->get(i))->value(); - } else if (Smi::cast(smis->get(i))->value() > max_index) { - max_index = Smi::cast(smis->get(i))->value(); + if (NumberToUint32(numbers->get(i)) < min_index) { + min_index = NumberToUint32(numbers->get(i)); + } else if (NumberToUint32(numbers->get(i)) > max_index) { + max_index = NumberToUint32(numbers->get(i)); } } if (max_index - min_index + 1 == len) { // Indices form a contiguous range, unless there are duplicates. - // Do an in-place linear time sort assuming distinct smis, but + // Do an in-place linear time sort assuming distinct numbers, but // avoid hanging in case they are not. for (i = 0; i < len; i++) { - int p; - int j = 0; + uint32_t p; + uint32_t j = 0; // While the current element at i is not at its correct position p, // swap the elements at these two positions. - while ((p = Smi::cast(smis->get(i))->value() - min_index) != i && + while ((p = NumberToUint32(numbers->get(i)) - min_index) != i && j++ < len) { - this->Swap(i, p); - smis->Swap(i, p); + SwapPairs(numbers, i, p); } } } else { - HeapSortPairs(this, smis); + HeapSortPairs(this, numbers, len); return; } } @@ -6267,7 +6286,7 @@ class SymbolKey : public HashTableKey { if (StringShape(string_).IsCons()) { ConsString* cons_string = ConsString::cast(string_); cons_string->TryFlatten(); - if (cons_string->second() == Heap::empty_string()) { + if (cons_string->second()->length() == 0) { string_ = cons_string->first(); } } @@ -6275,6 +6294,7 @@ class SymbolKey : public HashTableKey { Map* map = Heap::SymbolMapForString(string_); if (map != NULL) { string_->set_map(map); + ASSERT(string_->IsSymbol()); return string_; } // Otherwise allocate a new symbol. @@ -6723,7 +6743,7 @@ Object* Dictionary::GenerateNewEnumerationIndices() { } // Sort the arrays wrt. enumeration order. - iteration_order->SortPairs(enumeration_order); + iteration_order->SortPairs(enumeration_order, enumeration_order->length()); // Overwrite the enumeration_order with the enumeration indices. for (int i = 0; i < length; i++) { @@ -6975,6 +6995,7 @@ void Dictionary::CopyKeysTo(FixedArray* storage, PropertyAttributes filter) { if ((attr & filter) == 0) storage->set(index++, k); } } + storage->SortPairs(storage, index); ASSERT(storage->length() >= index); } @@ -6996,7 +7017,7 @@ void Dictionary::CopyEnumKeysTo(FixedArray* storage, FixedArray* sort_array) { } } } - storage->SortPairs(sort_array); + storage->SortPairs(sort_array, sort_array->length()); ASSERT(storage->length() >= index); } diff --git a/v8/src/objects.h b/v8/src/objects.h index 63480eb..b12875e 100644 --- a/v8/src/objects.h +++ b/v8/src/objects.h @@ -441,6 +441,19 @@ enum StringRepresentationTag { kExternalStringTag = 0x3 }; + +// A ConsString with an empty string as the right side is a candidate +// for being shortcut by the garbage collector unless it is a +// symbol. It's not common to have non-flat symbols, so we do not +// shortcut them thereby avoiding turning symbols into strings. See +// heap.cc and mark-compact.cc. +const uint32_t kShortcutTypeMask = + kIsNotStringMask | + kIsSymbolMask | + kStringRepresentationMask; +const uint32_t kShortcutTypeTag = kConsStringTag; + + enum InstanceType { SHORT_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSeqStringTag, MEDIUM_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSeqStringTag, @@ -1572,11 +1585,15 @@ class FixedArray: public Array { bool IsEqualTo(FixedArray* other); #endif - // Swap two elements. - void Swap(int i, int j); + // Swap two elements in a pair of arrays. If this array and the + // numbers array are the same object, the elements are only swapped + // once. + void SwapPairs(FixedArray* numbers, int i, int j); - // Sort this array and the smis as pairs wrt. the smis. - void SortPairs(FixedArray* smis); + // Sort prefix of this array and the numbers array as pairs wrt. the + // numbers. If the numbers array and the this array are the same + // object, the prefix of this array is sorted. + void SortPairs(FixedArray* numbers, uint32_t len); protected: // Set operation on FixedArray without using write barriers. @@ -2665,6 +2682,13 @@ class SharedFunctionInfo: public HeapObject { // [debug info]: Debug information. DECL_ACCESSORS(debug_info, Object) + // [inferred name]: Name inferred from variable or property + // assignment of this function. Used to facilitate debugging and + // profiling of JavaScript code written in OO style, where almost + // all functions are anonymous but are assigned to object + // properties. + DECL_ACCESSORS(inferred_name, String) + // Position of the 'function' token in the script source. inline int function_token_position(); inline void set_function_token_position(int function_token_position); @@ -2724,7 +2748,8 @@ class SharedFunctionInfo: public HeapObject { static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize; static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize; static const int kDebugInfoOffset = kFunctionTokenPositionOffset + kIntSize; - static const int kSize = kDebugInfoOffset + kPointerSize; + static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize; + static const int kSize = kInferredNameOffset + kPointerSize; private: // Bit positions in length_and_flg. diff --git a/v8/src/parser.cc b/v8/src/parser.cc index fc0ca4d..9db10cf 100644 --- a/v8/src/parser.cc +++ b/v8/src/parser.cc @@ -2092,7 +2092,7 @@ Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { // code. If 'with' statements were allowed, the simplified setup of // the runtime context chain would allow access to properties in the // global object from within a 'with' statement. - ASSERT(!Bootstrapper::IsActive()); + ASSERT(extension_ != NULL || !Bootstrapper::IsActive()); Expect(Token::WITH, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); @@ -2761,7 +2761,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { if (var == NULL) { // We do not allow direct calls to 'eval' in our internal // JS files. Use builtin functions instead. - ASSERT(!Bootstrapper::IsActive()); + ASSERT(extension_ != NULL || !Bootstrapper::IsActive()); top_scope_->RecordEvalCall(); is_potentially_direct_eval = true; } diff --git a/v8/src/platform-freebsd.cc b/v8/src/platform-freebsd.cc index 9209990..1e71704 100644 --- a/v8/src/platform-freebsd.cc +++ b/v8/src/platform-freebsd.cc @@ -262,7 +262,8 @@ void OS::LogSharedLibraryAddresses() { } -int OS::StackWalk(OS::StackFrame* frames, int frames_size) { +int OS::StackWalk(Vector<OS::StackFrame> frames) { + int frames_size = frames.length(); void** addresses = NewArray<void*>(frames_size); int frames_count = backtrace(addresses, frames_size); @@ -502,27 +503,24 @@ void FreeBSDSemaphore::Wait() { bool FreeBSDSemaphore::Wait(int timeout) { const long kOneSecondMicros = 1000000; // NOLINT - const long kOneSecondNanos = 1000000000; // NOLINT // Split timeout into second and nanosecond parts. - long nanos = (timeout % kOneSecondMicros) * 1000; // NOLINT - time_t secs = timeout / kOneSecondMicros; + struct timeval delta; + delta.tv_usec = timeout % kOneSecondMicros; + delta.tv_sec = timeout / kOneSecondMicros; - // Get the current real time clock. - struct timespec ts; - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + struct timeval current_time; + // Get the current time. + if (gettimeofday(¤t_time, NULL) == -1) { return false; } - // Calculate realtime for end of timeout. - ts.tv_nsec += nanos; - if (ts.tv_nsec >= kOneSecondNanos) { - ts.tv_nsec -= kOneSecondNanos; - ts.tv_nsec++; - } - ts.tv_sec += secs; + // Calculate time for end of timeout. + struct timeval end_time; + timeradd(¤t_time, &delta, &end_time); - // Wait for semaphore signalled or timeout. + struct timespec ts; + TIMEVAL_TO_TIMESPEC(&end_time, &ts); while (true) { int result = sem_timedwait(&sem_, &ts); if (result == 0) return true; // Successfully got semaphore. diff --git a/v8/src/platform-linux.cc b/v8/src/platform-linux.cc index 5bbc895..9f5b9ac 100644 --- a/v8/src/platform-linux.cc +++ b/v8/src/platform-linux.cc @@ -262,9 +262,10 @@ void OS::LogSharedLibraryAddresses() { } -int OS::StackWalk(OS::StackFrame* frames, int frames_size) { +int OS::StackWalk(Vector<OS::StackFrame> frames) { // backtrace is a glibc extension. #ifdef __GLIBC__ + int frames_size = frames.length(); void** addresses = NewArray<void*>(frames_size); int frames_count = backtrace(addresses, frames_size); @@ -506,28 +507,34 @@ void LinuxSemaphore::Wait() { } +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} while (false) +#endif + + bool LinuxSemaphore::Wait(int timeout) { const long kOneSecondMicros = 1000000; // NOLINT - const long kOneSecondNanos = 1000000000; // NOLINT // Split timeout into second and nanosecond parts. - long nanos = (timeout % kOneSecondMicros) * 1000; // NOLINT - time_t secs = timeout / kOneSecondMicros; + struct timeval delta; + delta.tv_usec = timeout % kOneSecondMicros; + delta.tv_sec = timeout / kOneSecondMicros; - // Get the current realtime clock. - struct timespec ts; - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + struct timeval current_time; + // Get the current time. + if (gettimeofday(¤t_time, NULL) == -1) { return false; } - // Calculate real time for end of timeout. - ts.tv_nsec += nanos; - if (ts.tv_nsec >= kOneSecondNanos) { - ts.tv_nsec -= kOneSecondNanos; - ts.tv_nsec++; - } - ts.tv_sec += secs; + // Calculate time for end of timeout. + struct timeval end_time; + timeradd(¤t_time, &delta, &end_time); + struct timespec ts; + TIMEVAL_TO_TIMESPEC(&end_time, &ts); // Wait for semaphore signalled or timeout. while (true) { int result = sem_timedwait(&sem_, &ts); @@ -552,9 +559,34 @@ Semaphore* OS::CreateSemaphore(int count) { static Sampler* active_sampler_ = NULL; + +#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__)) +// Android runs a fairly new Linux kernel, so signal info is there, +// but the C library doesn't have the structs defined. + +struct sigcontext { + uint32_t trap_no; + uint32_t error_code; + uint32_t oldmask; + uint32_t gregs[16]; + uint32_t arm_cpsr; + uint32_t fault_address; +}; +typedef uint32_t __sigset_t; +typedef struct sigcontext mcontext_t; +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + __sigset_t uc_sigmask; +} ucontext_t; +enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11}; + +#endif + + static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { - // Ucontext is a glibc extension - no profiling on Android at the moment. -#ifdef __GLIBC__ USE(info); if (signal != SIGPROF) return; if (active_sampler_ == NULL) return; @@ -581,7 +613,6 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample.state = Logger::state(); active_sampler_->Tick(&sample); -#endif } diff --git a/v8/src/platform-macos.cc b/v8/src/platform-macos.cc index b13122f..7951543 100644 --- a/v8/src/platform-macos.cc +++ b/v8/src/platform-macos.cc @@ -212,10 +212,11 @@ int OS::ActivationFrameAlignment() { } -int OS::StackWalk(StackFrame* frames, int frames_size) { +int OS::StackWalk(Vector<StackFrame> frames) { #ifndef MAC_OS_X_VERSION_10_5 return 0; #else + int frames_size = frames.length(); void** addresses = NewArray<void*>(frames_size); int frames_count = backtrace(addresses, frames_size); diff --git a/v8/src/platform-nullos.cc b/v8/src/platform-nullos.cc index 3a50b9d..42583f1 100644 --- a/v8/src/platform-nullos.cc +++ b/v8/src/platform-nullos.cc @@ -215,7 +215,7 @@ void OS::LogSharedLibraryAddresses() { } -int OS::StackWalk(OS::StackFrame* frames, int frames_size) { +int OS::StackWalk(Vector<OS::StackFrame> frames) { UNIMPLEMENTED(); return 0; } diff --git a/v8/src/platform-win32.cc b/v8/src/platform-win32.cc index 7177870..597a217 100644 --- a/v8/src/platform-win32.cc +++ b/v8/src/platform-win32.cc @@ -1161,7 +1161,7 @@ void OS::LogSharedLibraryAddresses() { // it is triggered by the use of inline assembler. #pragma warning(push) #pragma warning(disable : 4748) -int OS::StackWalk(OS::StackFrame* frames, int frames_size) { +int OS::StackWalk(Vector<OS::StackFrame> frames) { BOOL ok; // Load the required functions from DLL's. @@ -1201,6 +1201,7 @@ int OS::StackWalk(OS::StackFrame* frames, int frames_size) { int frames_count = 0; // Collect stack frames. + int frames_size = frames.length(); while (frames_count < frames_size) { ok = _StackWalk64( IMAGE_FILE_MACHINE_I386, // MachineType @@ -1284,7 +1285,7 @@ int OS::StackWalk(OS::StackFrame* frames, int frames_size) { #else // __MINGW32__ void OS::LogSharedLibraryAddresses() { } -int OS::StackWalk(OS::StackFrame* frames, int frames_size) { return 0; } +int OS::StackWalk(Vector<OS::StackFrame> frames) { return 0; } #endif // __MINGW32__ diff --git a/v8/src/platform.h b/v8/src/platform.h index db4f2fd..b70095b 100644 --- a/v8/src/platform.h +++ b/v8/src/platform.h @@ -207,7 +207,7 @@ class OS { char text[kStackWalkMaxTextLen]; }; - static int StackWalk(StackFrame* frames, int frames_size); + static int StackWalk(Vector<StackFrame> frames); // Factory method for creating platform dependent Mutex. // Please use delete to reclaim the storage for the returned Mutex. diff --git a/v8/src/prettyprinter.cc b/v8/src/prettyprinter.cc index 7f8c567..b58000a 100644 --- a/v8/src/prettyprinter.cc +++ b/v8/src/prettyprinter.cc @@ -646,7 +646,7 @@ AstPrinter::~AstPrinter() { void AstPrinter::PrintIndented(const char* txt) { for (int i = 0; i < indent_; i++) { - Print(". "); + Print(". "); } Print(txt); } @@ -709,6 +709,7 @@ const char* AstPrinter::PrintProgram(FunctionLiteral* program) { Init(); { IndentedScope indent("FUNC"); PrintLiteralIndented("NAME", program->name(), true); + PrintLiteralIndented("INFERRED NAME", program->inferred_name(), true); PrintParameters(program->scope()); PrintDeclarations(program->scope()->declarations()); PrintStatements(program->body()); @@ -731,7 +732,7 @@ void AstPrinter::PrintParameters(Scope* scope) { if (scope->num_parameters() > 0) { IndentedScope indent("PARAMS"); for (int i = 0; i < scope->num_parameters(); i++) { - PrintLiteralWithModeIndented("VAR ", scope->parameter(i), + PrintLiteralWithModeIndented("VAR", scope->parameter(i), scope->parameter(i)->name(), scope->parameter(i)->type()); } @@ -885,6 +886,7 @@ void AstPrinter::VisitDebuggerStatement(DebuggerStatement* node) { void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) { IndentedScope indent("FUNC LITERAL"); PrintLiteralIndented("NAME", node->name(), false); + PrintLiteralIndented("INFERRED NAME", node->inferred_name(), false); PrintParameters(node->scope()); // We don't want to see the function literal in this case: it // will be printed via PrintProgram when the code for it is @@ -1022,7 +1024,7 @@ void AstPrinter::VisitProperty(Property* node) { Visit(node->obj()); Literal* literal = node->key()->AsLiteral(); if (literal != NULL && literal->handle()->IsSymbol()) { - PrintLiteralIndented("LITERAL", literal->handle(), false); + PrintLiteralIndented("NAME", literal->handle(), false); } else { PrintIndentedVisit("KEY", node->key()); } diff --git a/v8/src/rewriter.cc b/v8/src/rewriter.cc index 1aa24aa..4e3676b 100644 --- a/v8/src/rewriter.cc +++ b/v8/src/rewriter.cc @@ -28,6 +28,7 @@ #include "v8.h" #include "ast.h" +#include "func-name-inferrer.h" #include "scopes.h" #include "rewriter.h" @@ -36,7 +37,9 @@ namespace v8 { namespace internal { class AstOptimizer: public AstVisitor { public: - explicit AstOptimizer() { + explicit AstOptimizer() {} + explicit AstOptimizer(Handle<String> enclosing_name) { + func_name_inferrer_.PushEnclosingName(enclosing_name); } void Optimize(ZoneList<Statement*>* statements); @@ -45,6 +48,8 @@ class AstOptimizer: public AstVisitor { // Used for loop condition analysis. Cleared before visiting a loop // condition, set when a function literal is visited. bool has_function_literal_; + // Helper object for function name inferring. + FuncNameInferrer func_name_inferrer_; // Helpers void OptimizeArguments(ZoneList<Expression*>* arguments); @@ -185,8 +190,12 @@ void AstOptimizer::VisitDebuggerStatement(DebuggerStatement* node) { void AstOptimizer::VisitFunctionLiteral(FunctionLiteral* node) { - USE(node); has_function_literal_ = true; + + if (node->name()->length() == 0) { + // Anonymous function. + func_name_inferrer_.AddFunction(node); + } } @@ -216,6 +225,11 @@ void AstOptimizer::VisitVariableProxy(VariableProxy* node) { } else if (node->type()->IsLikelySmi()) { var->type()->SetAsLikelySmi(); } + + if (!var->is_this() && + !Heap::result_symbol()->Equals(*var->name())) { + func_name_inferrer_.PushName(var->name()); + } } } @@ -224,6 +238,11 @@ void AstOptimizer::VisitLiteral(Literal* node) { Handle<Object> literal = node->handle(); if (literal->IsSmi()) { node->type()->SetAsLikelySmi(); + } else if (literal->IsString()) { + Handle<String> lit_str(Handle<String>::cast(literal)); + if (!Heap::prototype_symbol()->Equals(*lit_str)) { + func_name_inferrer_.PushName(lit_str); + } } } @@ -239,9 +258,10 @@ void AstOptimizer::VisitArrayLiteral(ArrayLiteral* node) { } } - void AstOptimizer::VisitObjectLiteral(ObjectLiteral* node) { for (int i = 0; i < node->properties()->length(); i++) { + ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_); + scoped_fni.Enter(); Visit(node->properties()->at(i)->key()); Visit(node->properties()->at(i)->value()); } @@ -255,11 +275,14 @@ void AstOptimizer::VisitCatchExtensionObject(CatchExtensionObject* node) { void AstOptimizer::VisitAssignment(Assignment* node) { + ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_); switch (node->op()) { case Token::INIT_VAR: case Token::INIT_CONST: case Token::ASSIGN: // No type can be infered from the general assignment. + + scoped_fni.Enter(); break; case Token::ASSIGN_BIT_OR: case Token::ASSIGN_BIT_XOR: @@ -368,6 +391,12 @@ void AstOptimizer::VisitCallNew(CallNew* node) { void AstOptimizer::VisitCallRuntime(CallRuntime* node) { + ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_); + if (Factory::InitializeVarGlobal_symbol()->Equals(*node->name()) && + node->arguments()->length() >= 2 && + node->arguments()->at(1)->AsFunctionLiteral() != NULL) { + scoped_fni.Enter(); + } OptimizeArguments(node->arguments()); } @@ -794,13 +823,10 @@ bool Rewriter::Optimize(FunctionLiteral* function) { ZoneList<Statement*>* body = function->body(); if (FLAG_optimize_ast && !body->is_empty()) { - Scope* scope = function->scope(); - if (!scope->is_global_scope()) { - AstOptimizer optimizer; - optimizer.Optimize(body); - if (optimizer.HasStackOverflow()) { - return false; - } + AstOptimizer optimizer(function->name()); + optimizer.Optimize(body); + if (optimizer.HasStackOverflow()) { + return false; } } return true; diff --git a/v8/src/runtime.cc b/v8/src/runtime.cc index 5962b2a..93c4b7a 100644 --- a/v8/src/runtime.cc +++ b/v8/src/runtime.cc @@ -3391,6 +3391,13 @@ static Object* Runtime_StringToUpperCase(Arguments args) { } +bool Runtime::IsUpperCaseChar(uint16_t ch) { + unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth]; + int char_length = to_upper_mapping.get(ch, 0, chars); + return char_length == 0; +} + + static Object* Runtime_NumberToString(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); @@ -3475,6 +3482,7 @@ static Object* Runtime_NumberToSmi(Arguments args) { return Heap::nan_value(); } + static Object* Runtime_NumberAdd(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 2); @@ -5921,8 +5929,8 @@ static Object* Runtime_GetCFrames(Arguments args) { if (result->IsFailure()) return result; static const int kMaxCFramesSize = 200; - OS::StackFrame frames[kMaxCFramesSize]; - int frames_count = OS::StackWalk(frames, kMaxCFramesSize); + ScopedVector<OS::StackFrame> frames(kMaxCFramesSize); + int frames_count = OS::StackWalk(frames); if (frames_count == OS::kStackWalkError) { return Heap::undefined_value(); } @@ -6061,8 +6069,8 @@ static Object* Runtime_SetFunctionBreakPoint(Arguments args) { } -static Object* FindSharedFunctionInfoInScript(Handle<Script> script, - int position) { +Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script, + int position) { // Iterate the heap looking for SharedFunctionInfo generated from the // script. The inner most SharedFunctionInfo containing the source position // for the requested break point is found. @@ -6159,7 +6167,8 @@ static Object* Runtime_SetScriptBreakPoint(Arguments args) { RUNTIME_ASSERT(wrapper->value()->IsScript()); Handle<Script> script(Script::cast(wrapper->value())); - Object* result = FindSharedFunctionInfoInScript(script, source_position); + Object* result = Runtime::FindSharedFunctionInfoInScript( + script, source_position); if (!result->IsUndefined()) { Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result)); // Find position within function. The script position might be before the @@ -6516,9 +6525,9 @@ static bool IsExternalStringValid(Object* str) { return true; } if (StringShape(String::cast(str)).IsAsciiRepresentation()) { - return ExternalAsciiString::cast(str)->resource() != 0; + return ExternalAsciiString::cast(str)->resource() != NULL; } else if (StringShape(String::cast(str)).IsTwoByteRepresentation()) { - return ExternalTwoByteString::cast(str)->resource() != 0; + return ExternalTwoByteString::cast(str)->resource() != NULL; } else { return true; } diff --git a/v8/src/runtime.h b/v8/src/runtime.h index 486ffb3..657e5c5 100644 --- a/v8/src/runtime.h +++ b/v8/src/runtime.h @@ -355,6 +355,8 @@ class Runtime : public AllStatic { static int StringMatch(Handle<String> sub, Handle<String> pat, int index); + static bool IsUpperCaseChar(uint16_t ch); + // TODO(1240886): The following three methods are *not* handle safe, // but accept handle arguments. This seems fragile. @@ -369,6 +371,10 @@ class Runtime : public AllStatic { static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key); + // This function is used in FunctionNameUsing* tests. + static Object* FindSharedFunctionInfoInScript(Handle<Script> script, + int position); + // Helper functions used stubs. static void PerformGC(Object* result); }; diff --git a/v8/src/serialize.cc b/v8/src/serialize.cc index 9a2be23..ef2517c 100644 --- a/v8/src/serialize.cc +++ b/v8/src/serialize.cc @@ -683,6 +683,18 @@ void ExternalReferenceTable::PopulateTable() { UNCLASSIFIED, 10, "Debug::step_in_fp_addr()"); + Add(ExternalReference::double_fp_operation(Token::ADD).address(), + UNCLASSIFIED, + 11, + "add_two_doubles"); + Add(ExternalReference::double_fp_operation(Token::SUB).address(), + UNCLASSIFIED, + 12, + "sub_two_doubles"); + Add(ExternalReference::double_fp_operation(Token::MUL).address(), + UNCLASSIFIED, + 13, + "mul_two_doubles"); } diff --git a/v8/src/simulator-arm.cc b/v8/src/simulator-arm.cc index 5a61107..c35e229 100644 --- a/v8/src/simulator-arm.cc +++ b/v8/src/simulator-arm.cc @@ -90,12 +90,44 @@ Debugger::~Debugger() { } + +#ifdef ARM_GENERATED_CODE_COVERAGE +static FILE* coverage_log = NULL; + + +static void InitializeCoverage() { + char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG"); + if (file_name != NULL) { + coverage_log = fopen(file_name, "aw+"); + } +} + + +void Debugger::Stop(Instr* instr) { + char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff); + if (strlen(str) > 0) { + if (coverage_log != NULL) { + fprintf(coverage_log, "Simulator hit %s\n", str); + fflush(coverage_log); + } + instr->SetInstructionBits(0xe1a00000); // Overwrite with nop. + } + sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); +} + +#else // ndef ARM_GENERATED_CODE_COVERAGE + +static void InitializeCoverage() { +} + + void Debugger::Stop(Instr* instr) { const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff); PrintF("Simulator hit %s\n", str); sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); Debug(); } +#endif static const char* reg_names[] = { "r0", "r1", "r2", "r3", @@ -375,6 +407,7 @@ Simulator::Simulator() { // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; + InitializeCoverage(); } @@ -427,6 +460,37 @@ int32_t Simulator::get_pc() const { } +// For use in calls that take two double values, constructed from r0, r1, r2 +// and r3. +void Simulator::GetFpArgs(double* x, double* y) { + // We use a char buffer to get around the strict-aliasing rules which + // otherwise allow the compiler to optimize away the copy. + char buffer[2 * sizeof(registers_[0])]; + // Registers 0 and 1 -> x. + memcpy(buffer, registers_, sizeof(buffer)); + memcpy(x, buffer, sizeof(buffer)); + // Registers 2 and 3 -> y. + memcpy(buffer, registers_ + 2, sizeof(buffer)); + memcpy(y, buffer, sizeof(buffer)); +} + + +void Simulator::SetFpResult(const double& result) { + char buffer[2 * sizeof(registers_[0])]; + memcpy(buffer, &result, sizeof(buffer)); + // result -> registers 0 and 1. + memcpy(registers_, buffer, sizeof(buffer)); +} + + +void Simulator::TrashCallerSaveRegisters() { + // We don't trash the registers with the return value. + registers_[2] = 0x50Bad4U; + registers_[3] = 0x50Bad4U; + registers_[12] = 0x50Bad4U; +} + + // The ARM cannot do unaligned reads and writes. On some ARM platforms an // interrupt is caused. On others it does a funky rotation thing. For now we // simply disallow unaligned reads, but at some point we may want to move to @@ -862,7 +926,8 @@ typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1); // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instr* instr) { - switch (instr->SwiField()) { + int swi = instr->SwiField(); + switch (swi) { case call_rt_r5: { SimulatorRuntimeCall target = reinterpret_cast<SimulatorRuntimeCall>(get_register(r5)); @@ -894,6 +959,30 @@ void Simulator::SoftwareInterrupt(Instr* instr) { dbg.Debug(); break; } + { + double x, y, z; + case simulator_fp_add: + GetFpArgs(&x, &y); + z = x + y; + SetFpResult(z); + TrashCallerSaveRegisters(); + set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); + break; + case simulator_fp_sub: + GetFpArgs(&x, &y); + z = x - y; + SetFpResult(z); + TrashCallerSaveRegisters(); + set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); + break; + case simulator_fp_mul: + GetFpArgs(&x, &y); + z = x * y; + SetFpResult(z); + TrashCallerSaveRegisters(); + set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); + break; + } default: { UNREACHABLE(); break; diff --git a/v8/src/simulator-arm.h b/v8/src/simulator-arm.h index 4f3c53d..5553ab2 100644 --- a/v8/src/simulator-arm.h +++ b/v8/src/simulator-arm.h @@ -174,6 +174,12 @@ class Simulator { // Executes one instruction. void InstructionDecode(Instr* instr); + // For use in calls that take two double values, constructed from r0, r1, r2 + // and r3. + void GetFpArgs(double* x, double* y); + void SetFpResult(const double& result); + void TrashCallerSaveRegisters(); + // architecture state int32_t registers_[16]; bool n_flag_; diff --git a/v8/src/string-stream.cc b/v8/src/string-stream.cc index f311e20..2e0912f 100644 --- a/v8/src/string-stream.cc +++ b/v8/src/string-stream.cc @@ -57,19 +57,26 @@ NoAllocationStringAllocator::NoAllocationStringAllocator(char* memory, bool StringStream::Put(char c) { - if (space() == 0) return false; - if (length_ >= capacity_ - 1) { + if (full()) return false; + ASSERT(length_ < capacity_); + // Since the trailing '\0' is not accounted for in length_ fullness is + // indicated by a difference of 1 between length_ and capacity_. Thus when + // reaching a difference of 2 we need to grow the buffer. + if (length_ == capacity_ - 2) { unsigned new_capacity = capacity_; char* new_buffer = allocator_->grow(&new_capacity); if (new_capacity > capacity_) { capacity_ = new_capacity; buffer_ = new_buffer; } else { - // Indicate truncation with dots. - memset(cursor(), '.', space()); - length_ = capacity_; - buffer_[length_ - 2] = '\n'; - buffer_[length_ - 1] = '\0'; + // Reached the end of the available buffer. + ASSERT(capacity_ >= 5); + length_ = capacity_ - 1; // Indicate fullness of the stream. + buffer_[length_ - 4] = '.'; + buffer_[length_ - 3] = '.'; + buffer_[length_ - 2] = '.'; + buffer_[length_ - 1] = '\n'; + buffer_[length_] = '\0'; return false; } } @@ -95,8 +102,7 @@ static bool IsControlChar(char c) { void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) { // If we already ran out of space then return immediately. - if (space() == 0) - return; + if (full()) return; int offset = 0; int elm = 0; while (offset < format.length()) { @@ -564,12 +570,10 @@ char* HeapStringAllocator::grow(unsigned* bytes) { } +// Only grow once to the maximum allowable size. char* NoAllocationStringAllocator::grow(unsigned* bytes) { - unsigned new_bytes = *bytes * 2; - if (new_bytes > size_) { - new_bytes = size_; - } - *bytes = new_bytes; + ASSERT(size_ >= *bytes); + *bytes = size_; return space_; } diff --git a/v8/src/string-stream.h b/v8/src/string-stream.h index 37ae93e..901f376 100644 --- a/v8/src/string-stream.h +++ b/v8/src/string-stream.h @@ -162,8 +162,8 @@ class StringStream { unsigned length_; // does not include terminating 0-character char* buffer_; + bool full() const { return (capacity_ - length_) == 1; } int space() const { return capacity_ - length_; } - char* cursor() const { return buffer_ + length_; } DISALLOW_IMPLICIT_CONSTRUCTORS(StringStream); }; diff --git a/v8/src/stub-cache-arm.cc b/v8/src/stub-cache-arm.cc index 211b643..22ebe44 100644 --- a/v8/src/stub-cache-arm.cc +++ b/v8/src/stub-cache-arm.cc @@ -33,7 +33,7 @@ namespace v8 { namespace internal { -#define __ masm-> +#define __ DEFINE_MASM(masm) static void ProbeTable(MacroAssembler* masm, @@ -183,7 +183,7 @@ void StubCompiler::GenerateLoadField(MacroAssembler* masm, // Check that the maps haven't changed. Register reg = - __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); + masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); GenerateFastPropertyLoad(masm, r0, reg, holder, index); __ Ret(); } @@ -203,7 +203,7 @@ void StubCompiler::GenerateLoadConstant(MacroAssembler* masm, // Check that the maps haven't changed. Register reg = - __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); + masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); // Return the constant value. __ mov(r0, Operand(Handle<Object>(value))); @@ -226,7 +226,7 @@ void StubCompiler::GenerateLoadCallback(MacroAssembler* masm, // Check that the maps haven't changed. Register reg = - __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); + masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); // Push the arguments on the JS stack of the caller. __ push(receiver); // receiver @@ -256,7 +256,7 @@ void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, // Check that the maps haven't changed. Register reg = - __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); + masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); // Push the arguments on the JS stack of the caller. __ push(receiver); // receiver @@ -456,8 +456,7 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { #undef __ - -#define __ masm()-> +#define __ DEFINE_MASM(masm()) Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { @@ -511,7 +510,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, // Do the right check and compute the holder register. Register reg = - __ CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss); + masm()->CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss); GenerateFastPropertyLoad(masm(), r1, reg, holder, index); // Check that the function really is a function. diff --git a/v8/src/top.cc b/v8/src/top.cc index a99e9ff..3c4b8dd 100644 --- a/v8/src/top.cc +++ b/v8/src/top.cc @@ -116,15 +116,15 @@ class PreallocatedMemoryThread: public Thread { // When the thread starts running it will allocate a fixed number of bytes // on the stack and publish the location of this memory for others to use. void Run() { - EmbeddedVector<char, 32 * 1024> local_buffer; + EmbeddedVector<char, 15 * 1024> local_buffer; // Initialize the buffer with a known good value. OS::StrNCpy(local_buffer, "Trace data was not generated.\n", local_buffer.length()); // Publish the local buffer and signal its availability. - data_ = &local_buffer[0]; - length_ = sizeof(local_buffer); + data_ = local_buffer.start(); + length_ = local_buffer.length(); data_ready_semaphore_->Signal(); while (keep_running_) { diff --git a/v8/src/utils.h b/v8/src/utils.h index f62b47a..e008c85 100644 --- a/v8/src/utils.h +++ b/v8/src/utils.h @@ -406,6 +406,16 @@ class EmbeddedVector : public Vector<T> { }; +template <typename T> +class ScopedVector : public Vector<T> { + public: + explicit ScopedVector(int length) : Vector<T>(NewArray<T>(length), length) { } + ~ScopedVector() { + DeleteArray(this->start()); + } +}; + + inline Vector<const char> CStrVector(const char* data) { return Vector<const char>(data, strlen(data)); } diff --git a/v8/src/virtual-frame-arm.cc b/v8/src/virtual-frame-arm.cc index baf0814..df442e9 100644 --- a/v8/src/virtual-frame-arm.cc +++ b/v8/src/virtual-frame-arm.cc @@ -36,7 +36,8 @@ namespace v8 { namespace internal { // ------------------------------------------------------------------------- // VirtualFrame implementation. -#define __ masm_-> +#define __ DEFINE_MASM(masm_) + // On entry to a function, the virtual frame already contains the // receiver and the parameters. All initial frame elements are in diff --git a/v8/src/virtual-frame-arm.h b/v8/src/virtual-frame-arm.h index f15eec2..af3c08c 100644 --- a/v8/src/virtual-frame-arm.h +++ b/v8/src/virtual-frame-arm.h @@ -85,7 +85,7 @@ class VirtualFrame : public Malloced { } bool is_used(Register reg) { - return is_used(reg.code()) != kIllegalIndex; + return is_used(reg.code()); } // Add extra in-memory elements to the top of the frame to match an actual diff --git a/v8/test/cctest/SConscript b/v8/test/cctest/SConscript index 091768f..f66f72b 100644 --- a/v8/test/cctest/SConscript +++ b/v8/test/cctest/SConscript @@ -42,6 +42,7 @@ SOURCES = { 'test-debug.cc', 'test-decls.cc', 'test-flags.cc', + 'test-func-name-inference.cc', 'test-hashmap.cc', 'test-heap.cc', 'test-list.cc', diff --git a/v8/test/cctest/test-api.cc b/v8/test/cctest/test-api.cc index dd705d2..13185ab 100644 --- a/v8/test/cctest/test-api.cc +++ b/v8/test/cctest/test-api.cc @@ -1293,6 +1293,9 @@ THREADED_TEST(HiddenProperties) { i::Heap::CollectAllGarbage(); + // Make sure delete of a non-existent hidden value works + CHECK(obj->DeleteHiddenValue(key)); + CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); @@ -2492,6 +2495,44 @@ THREADED_TEST(SimpleExtensions) { } +static const char* kEvalExtensionSource = + "function UseEval() {" + " var x = 42;" + " return eval('x');" + "}"; + + +THREADED_TEST(UseEvalFromExtension) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("evaltest", kEvalExtensionSource)); + const char* extension_names[] = { "evaltest" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + Context::Scope lock(context); + v8::Handle<Value> result = Script::Compile(v8_str("UseEval()"))->Run(); + CHECK_EQ(result, v8::Integer::New(42)); +} + + +static const char* kWithExtensionSource = + "function UseWith() {" + " var x = 42;" + " with({x:87}) { return x; }" + "}"; + + +THREADED_TEST(UseWithFromExtension) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("withtest", kWithExtensionSource)); + const char* extension_names[] = { "withtest" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + Context::Scope lock(context); + v8::Handle<Value> result = Script::Compile(v8_str("UseWith()"))->Run(); + CHECK_EQ(result, v8::Integer::New(87)); +} + + THREADED_TEST(AutoExtensions) { v8::HandleScope handle_scope; Extension* extension = new Extension("autotest", kSimpleExtensionSource); @@ -2884,7 +2925,19 @@ THREADED_TEST(Deleter) { static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { ApiTestFuzzer::Fuzz(); - return v8::Undefined(); + if (name->Equals(v8_str("foo")) || + name->Equals(v8_str("bar")) || + name->Equals(v8_str("baz"))) { + return v8::Undefined(); + } + return v8::Handle<Value>(); +} + + +static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { + ApiTestFuzzer::Fuzz(); + if (index == 0 || index == 1) return v8::Undefined(); + return v8::Handle<Value>(); } @@ -2901,8 +2954,8 @@ static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { ApiTestFuzzer::Fuzz(); v8::Handle<v8::Array> result = v8::Array::New(2); - result->Set(v8::Integer::New(0), v8_str("hat")); - result->Set(v8::Integer::New(1), v8_str("gyt")); + result->Set(v8::Integer::New(0), v8_str("0")); + result->Set(v8::Integer::New(1), v8_str("1")); return result; } @@ -2911,21 +2964,56 @@ THREADED_TEST(Enumerators) { v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); - obj->SetIndexedPropertyHandler(NULL, NULL, NULL, NULL, IndexedEnum); + obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); LocalContext context; context->Global()->Set(v8_str("k"), obj->NewInstance()); v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( + "k[10] = 0;" + "k.a = 0;" + "k[5] = 0;" + "k.b = 0;" + "k[4294967295] = 0;" + "k.c = 0;" + "k[4294967296] = 0;" + "k.d = 0;" + "k[140000] = 0;" + "k.e = 0;" + "k[30000000000] = 0;" + "k.f = 0;" "var result = [];" "for (var prop in k) {" " result.push(prop);" "}" "result")); - CHECK_EQ(5, result->Length()); - CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(0))); - CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(1))); - CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(2))); - CHECK_EQ(v8_str("hat"), result->Get(v8::Integer::New(3))); - CHECK_EQ(v8_str("gyt"), result->Get(v8::Integer::New(4))); + // Check that we get all the property names returned including the + // ones from the enumerators in the right order: indexed properties + // in numerical order, indexed interceptor properties, named + // properties in insertion order, named interceptor properties. + // This order is not mandated by the spec, so this test is just + // documenting our behavior. + CHECK_EQ(17, result->Length()); + // Indexed properties in numerical order. + CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); + CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); + CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); + CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); + // Indexed interceptor properties in the order they are returned + // from the enumerator interceptor. + CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); + CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); + // Named properties in insertion order. + CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); + CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); + CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); + CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); + CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); + CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); + CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); + CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); + // Named interceptor properties. + CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); + CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); + CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); } diff --git a/v8/test/cctest/test-debug.cc b/v8/test/cctest/test-debug.cc index 74d590e..e482c7f 100644 --- a/v8/test/cctest/test-debug.cc +++ b/v8/test/cctest/test-debug.cc @@ -498,7 +498,7 @@ void CheckDebugBreakFunction(DebugLocalContext* env, // --- -// Source for The JavaScript function which picks out the function name on the +// Source for The JavaScript function which picks out the function name of the // top frame. const char* frame_function_name_source = "function frame_function_name(exec_state) {" @@ -507,6 +507,24 @@ const char* frame_function_name_source = v8::Local<v8::Function> frame_function_name; +// Source for The JavaScript function which picks out the source line for the +// top frame. +const char* frame_source_line_source = + "function frame_source_line(exec_state) {" + " return exec_state.frame(0).sourceLine();" + "}"; +v8::Local<v8::Function> frame_source_line; + + +// Source for The JavaScript function which picks out the source column for the +// top frame. +const char* frame_source_column_source = + "function frame_source_column(exec_state) {" + " return exec_state.frame(0).sourceColumn();" + "}"; +v8::Local<v8::Function> frame_source_column; + + // Source for The JavaScript function which returns the number of frames. static const char* frame_count_source = "function frame_count(exec_state) {" @@ -518,6 +536,10 @@ v8::Handle<v8::Function> frame_count; // Global variable to store the last function hit - used by some tests. char last_function_hit[80]; +// Global variables to store the last source position - used by some tests. +int last_source_line = -1; +int last_source_column = -1; + // Debug event handler which counts the break points which have been hit. int break_point_hit_count = 0; static void DebugEventBreakPointHitCount(v8::DebugEvent event, @@ -544,6 +566,26 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event, function_name->WriteAscii(last_function_hit); } } + + if (!frame_source_line.IsEmpty()) { + // Get the source line. + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { exec_state }; + v8::Handle<v8::Value> result = frame_source_line->Call(exec_state, + argc, argv); + CHECK(result->IsNumber()); + last_source_line = result->Int32Value(); + } + + if (!frame_source_column.IsEmpty()) { + // Get the source column. + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { exec_state }; + v8::Handle<v8::Value> result = frame_source_column->Call(exec_state, + argc, argv); + CHECK(result->IsNumber()); + last_source_column = result->Int32Value(); + } } } @@ -994,6 +1036,17 @@ TEST(BreakPointReturn) { break_point_hit_count = 0; v8::HandleScope scope; DebugLocalContext env; + + // Create a functions for checking the source line and column when hitting + // a break point. + frame_source_line = CompileFunction(&env, + frame_source_line_source, + "frame_source_line"); + frame_source_column = CompileFunction(&env, + frame_source_column_source, + "frame_source_column"); + + v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount, v8::Undefined()); v8::Script::Compile(v8::String::New("function foo(){}"))->Run(); @@ -1008,8 +1061,12 @@ TEST(BreakPointReturn) { int bp = SetBreakPoint(foo, 0); foo->Call(env->Global(), 0, NULL); CHECK_EQ(1, break_point_hit_count); + CHECK_EQ(0, last_source_line); + CHECK_EQ(16, last_source_column); foo->Call(env->Global(), 0, NULL); CHECK_EQ(2, break_point_hit_count); + CHECK_EQ(0, last_source_line); + CHECK_EQ(16, last_source_column); // Run without breakpoints. ClearBreakPoint(bp); @@ -2406,6 +2463,96 @@ TEST(DebugStepNatives) { } +// Test that step in works with function.apply. +TEST(DebugStepFunctionApply) { + v8::HandleScope scope; + DebugLocalContext env; + + // Create a function for testing stepping. + v8::Local<v8::Function> foo = CompileFunction( + &env, + "function bar(x, y, z) { if (x == 1) { a = y; b = z; } }" + "function foo(){ debugger; bar.apply(this, [1,2,3]); }", + "foo"); + + // Register a debug event listener which steps and counts. + v8::Debug::SetDebugEventListener(DebugEventStep); + + step_action = StepIn; + break_point_hit_count = 0; + foo->Call(env->Global(), 0, NULL); + + // With stepping all break locations are hit. + CHECK_EQ(6, break_point_hit_count); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); + + // Register a debug event listener which just counts. + v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount); + + break_point_hit_count = 0; + foo->Call(env->Global(), 0, NULL); + + // Without stepping only the debugger statement is hit. + CHECK_EQ(1, break_point_hit_count); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); +} + + +// Test that step in works with function.call. +TEST(DebugStepFunctionCall) { + v8::HandleScope scope; + DebugLocalContext env; + + // Create a function for testing stepping. + v8::Local<v8::Function> foo = CompileFunction( + &env, + "function bar(x, y, z) { if (x == 1) { a = y; b = z; } }" + "function foo(a){ debugger;" + " if (a) {" + " bar.call(this, 1, 2, 3);" + " } else {" + " bar.call(this, 0);" + " }" + "}", + "foo"); + + // Register a debug event listener which steps and counts. + v8::Debug::SetDebugEventListener(DebugEventStep); + step_action = StepIn; + + // Check stepping where the if condition in bar is false. + break_point_hit_count = 0; + foo->Call(env->Global(), 0, NULL); + CHECK_EQ(4, break_point_hit_count); + + // Check stepping where the if condition in bar is true. + break_point_hit_count = 0; + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { v8::True() }; + foo->Call(env->Global(), argc, argv); + CHECK_EQ(6, break_point_hit_count); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); + + // Register a debug event listener which just counts. + v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount); + + break_point_hit_count = 0; + foo->Call(env->Global(), 0, NULL); + + // Without stepping only the debugger statement is hit. + CHECK_EQ(1, break_point_hit_count); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); +} + + // Test break on exceptions. For each exception break combination the number // of debug event exception callbacks and message callbacks are collected. The // number of debug event exception callbacks are used to check that the @@ -3300,6 +3447,12 @@ class DebuggerThread : public v8::internal::Thread { }; +static v8::Handle<v8::Value> ThreadedAtBarrier1(const v8::Arguments& args) { + threaded_debugging_barriers.barrier_1.Wait(); + return v8::Undefined(); +} + + static void ThreadedMessageHandler(const uint16_t* message, int length, void *data) { static char print_buffer[1000]; @@ -3313,7 +3466,7 @@ static void ThreadedMessageHandler(const uint16_t* message, int length, void V8Thread::Run() { - const char* source_1 = + const char* source = "flag = true;\n" "function bar( new_value ) {\n" " flag = new_value;\n" @@ -3323,19 +3476,25 @@ void V8Thread::Run() { "function foo() {\n" " var x = 1;\n" " while ( flag == true ) {\n" + " if ( x == 1 ) {\n" + " ThreadedAtBarrier1();\n" + " }\n" " x = x + 1;\n" " }\n" "}\n" - "\n"; - const char* source_2 = "foo();\n"; + "\n" + "foo();\n"; v8::HandleScope scope; DebugLocalContext env; v8::Debug::SetMessageHandler(&ThreadedMessageHandler); + v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); + global_template->Set(v8::String::New("ThreadedAtBarrier1"), + v8::FunctionTemplate::New(ThreadedAtBarrier1)); + v8::Handle<v8::Context> context = v8::Context::New(NULL, global_template); + v8::Context::Scope context_scope(context); - CompileRun(source_1); - threaded_debugging_barriers.barrier_1.Wait(); - CompileRun(source_2); + CompileRun(source); } void DebuggerThread::Run() { @@ -3561,15 +3720,6 @@ TEST(SendCommandToUninitializedVM) { } -// Source for a JavaScript function which returns the source line for the top -// frame. -static const char* frame_source_line_source = - "function frame_source_line(exec_state) {" - " return exec_state.frame(0).sourceLine();" - "}"; -v8::Handle<v8::Function> frame_source_line; - - // Source for a JavaScript function which returns the data parameter of a // function called in the context of the debugger. If no data parameter is // passed it throws an exception. @@ -3875,19 +4025,33 @@ TEST(DebuggerHostDispatch) { "\"type\":\"request\"," "\"command\":\"continue\"}"; + // Create an empty function to call for processing debug commands + v8::Local<v8::Function> empty = + CompileFunction(&env, "function empty(){}", "empty"); + // Setup message and host dispatch handlers. v8::Debug::SetMessageHandler(DummyMessageHandler); v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount, NULL); - // Fill a host dispatch and a continue command on the command queue before - // running some code. + // Send a host dispatch by itself. + v8::Debug::SendHostDispatch(NULL); + empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. + CHECK_EQ(1, host_dispatch_hit_count); + + // Fill a host dispatch and a continue command on the command queue. v8::Debug::SendHostDispatch(NULL); v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); - CompileRun("void 0"); + empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. - // The host dispatch callback should be called. - CHECK_EQ(1, host_dispatch_hit_count); + // Fill a continue command and a host dispatch on the command queue. + v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); + v8::Debug::SendHostDispatch(NULL); + empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. + empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. + + // All the host dispatch callback should be called. + CHECK_EQ(3, host_dispatch_hit_count); } diff --git a/v8/test/cctest/test-func-name-inference.cc b/v8/test/cctest/test-func-name-inference.cc new file mode 100644 index 0000000..d91f75f --- /dev/null +++ b/v8/test/cctest/test-func-name-inference.cc @@ -0,0 +1,250 @@ +// Copyright 2007-2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "api.h" +#include "runtime.h" +#include "cctest.h" + + +using ::v8::internal::Handle; +using ::v8::internal::JSFunction; +using ::v8::internal::Object; +using ::v8::internal::Script; +using ::v8::internal::SharedFunctionInfo; +using ::v8::internal::String; + +namespace i = ::v8::internal; + + +static v8::Persistent<v8::Context> env; + + +static void InitializeVM() { + if (env.IsEmpty()) { + v8::HandleScope scope; + env = v8::Context::New(); + } + v8::HandleScope scope; + env->Enter(); +} + + +static void CheckFunctionName(v8::Handle<v8::Script> script, + const char* func_pos_src, + const char* ref_inferred_name) { + // Get script source. + Handle<JSFunction> fun = v8::Utils::OpenHandle(*script); + Handle<Script> i_script(Script::cast(fun->shared()->script())); + CHECK(i_script->source()->IsString()); + Handle<String> script_src(String::cast(i_script->source())); + + // Find the position of a given func source substring in the source. + Handle<String> func_pos_str = + i::Factory::NewStringFromAscii(i::CStrVector(func_pos_src)); + int func_pos = i::Runtime::StringMatch(script_src, func_pos_str, 0); + CHECK_NE(0, func_pos); + + // Obtain SharedFunctionInfo for the function. + Object* shared_func_info_ptr = + i::Runtime::FindSharedFunctionInfoInScript(i_script, func_pos); + CHECK(shared_func_info_ptr != i::Heap::undefined_value()); + Handle<SharedFunctionInfo> shared_func_info( + SharedFunctionInfo::cast(shared_func_info_ptr)); + + // Verify inferred function name. + i::SmartPointer<char> inferred_name = + shared_func_info->inferred_name()->ToCString(); + CHECK_EQ(ref_inferred_name, *inferred_name); +} + + +static v8::Handle<v8::Script> Compile(const char* src) { + return v8::Script::Compile(v8::String::New(src)); +} + + +TEST(GlobalProperty) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "fun1 = function() { return 1; }\n" + "fun2 = function() { return 2; }\n"); + CheckFunctionName(script, "return 1", "fun1"); + CheckFunctionName(script, "return 2", "fun2"); +} + + +TEST(GlobalVar) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "var fun1 = function() { return 1; }\n" + "var fun2 = function() { return 2; }\n"); + CheckFunctionName(script, "return 1", "fun1"); + CheckFunctionName(script, "return 2", "fun2"); +} + + +TEST(LocalVar) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function outer() {\n" + " var fun1 = function() { return 1; }\n" + " var fun2 = function() { return 2; }\n" + "}"); + CheckFunctionName(script, "return 1", "fun1"); + CheckFunctionName(script, "return 2", "fun2"); +} + + +TEST(InConstructor) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function MyClass() {\n" + " this.method1 = function() { return 1; }\n" + " this.method2 = function() { return 2; }\n" + "}"); + CheckFunctionName(script, "return 1", "MyClass.method1"); + CheckFunctionName(script, "return 2", "MyClass.method2"); +} + + +TEST(Factory) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function createMyObj() {\n" + " var obj = {};\n" + " obj.method1 = function() { return 1; }\n" + " obj.method2 = function() { return 2; }\n" + " return obj;\n" + "}"); + CheckFunctionName(script, "return 1", "obj.method1"); + CheckFunctionName(script, "return 2", "obj.method2"); +} + + +TEST(Static) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function MyClass() {}\n" + "MyClass.static1 = function() { return 1; }\n" + "MyClass.static2 = function() { return 2; }\n" + "MyClass.MyInnerClass = {}\n" + "MyClass.MyInnerClass.static3 = function() { return 3; }\n" + "MyClass.MyInnerClass.static4 = function() { return 4; }"); + CheckFunctionName(script, "return 1", "MyClass.static1"); + CheckFunctionName(script, "return 2", "MyClass.static2"); + CheckFunctionName(script, "return 3", "MyClass.MyInnerClass.static3"); + CheckFunctionName(script, "return 4", "MyClass.MyInnerClass.static4"); +} + + +TEST(Prototype) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function MyClass() {}\n" + "MyClass.prototype.method1 = function() { return 1; }\n" + "MyClass.prototype.method2 = function() { return 2; }\n" + "MyClass.MyInnerClass = function() {}\n" + "MyClass.MyInnerClass.prototype.method3 = function() { return 3; }\n" + "MyClass.MyInnerClass.prototype.method4 = function() { return 4; }"); + CheckFunctionName(script, "return 1", "MyClass.method1"); + CheckFunctionName(script, "return 2", "MyClass.method2"); + CheckFunctionName(script, "return 3", "MyClass.MyInnerClass.method3"); + CheckFunctionName(script, "return 4", "MyClass.MyInnerClass.method4"); +} + + +TEST(ObjectLiteral) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function MyClass() {}\n" + "MyClass.prototype = {\n" + " method1: function() { return 1; },\n" + " method2: function() { return 2; } }"); + CheckFunctionName(script, "return 1", "MyClass.method1"); + CheckFunctionName(script, "return 2", "MyClass.method2"); +} + + +TEST(AsParameter) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function f1(a) { return a(); }\n" + "function f2(a, b) { return a() + b(); }\n" + "var result1 = f1(function() { return 1; })\n" + "var result2 = f2(function() { return 2; }, function() { return 3; })"); + // Can't infer names here. + CheckFunctionName(script, "return 1", ""); + CheckFunctionName(script, "return 2", ""); + CheckFunctionName(script, "return 3", ""); +} + + +TEST(MultipleFuncsConditional) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "fun1 = 0 ?\n" + " function() { return 1; } :\n" + " function() { return 2; }"); + CheckFunctionName(script, "return 1", "fun1"); + CheckFunctionName(script, "return 2", "fun1"); +} + + +TEST(MultipleFuncsInLiteral) { + InitializeVM(); + v8::HandleScope scope; + + v8::Handle<v8::Script> script = Compile( + "function MyClass() {}\n" + "MyClass.prototype = {\n" + " method1: 0 ? function() { return 1; } :\n" + " function() { return 2; } }"); + CheckFunctionName(script, "return 1", "MyClass.method1"); + CheckFunctionName(script, "return 2", "MyClass.method1"); +} diff --git a/v8/test/cctest/test-strings.cc b/v8/test/cctest/test-strings.cc index b38d645..3be5d62 100644 --- a/v8/test/cctest/test-strings.cc +++ b/v8/test/cctest/test-strings.cc @@ -387,3 +387,60 @@ TEST(Utf8Conversion) { CHECK_EQ(kNoChar, buffer[j]); } } + + +class TwoByteResource: public v8::String::ExternalStringResource { + public: + explicit TwoByteResource(const uint16_t* data, size_t length) + : data_(data), length_(length) { } + virtual ~TwoByteResource() { } + + const uint16_t* data() const { return data_; } + size_t length() const { return length_; } + + private: + const uint16_t* data_; + size_t length_; +}; + + +TEST(ExternalCrBug9746) { + InitializeVM(); + v8::HandleScope handle_scope; + + // This set of tests verifies that the workaround for Chromium bug 9746 + // works correctly. In certain situations the external resource of a symbol + // is collected while the symbol is still part of the symbol table. + static uint16_t two_byte_data[] = { + 't', 'w', 'o', '-', 'b', 'y', 't', 'e', ' ', 'd', 'a', 't', 'a' + }; + static size_t two_byte_length = + sizeof(two_byte_data) / sizeof(two_byte_data[0]); + static const char* one_byte_data = "two-byte data"; + + // Allocate an external string resource and external string. + TwoByteResource* resource = new TwoByteResource(two_byte_data, + two_byte_length); + Handle<String> string = Factory::NewExternalStringFromTwoByte(resource); + Vector<const char> one_byte_vec = CStrVector(one_byte_data); + Handle<String> compare = Factory::NewStringFromAscii(one_byte_vec); + + // Verify the correct behaviour before "collecting" the external resource. + CHECK(string->IsEqualTo(one_byte_vec)); + CHECK(string->Equals(*compare)); + + // "Collect" the external resource manually by setting the external resource + // pointer to NULL. Then redo the comparisons, they should not match AND + // not crash. + Handle<ExternalTwoByteString> external(ExternalTwoByteString::cast(*string)); + external->set_resource(NULL); + CHECK_EQ(false, string->IsEqualTo(one_byte_vec)); +#if !defined(DEBUG) + // These tests only work in non-debug as there are ASSERTs in the code that + // do prevent the ability to even get into the broken code when running the + // debug version of V8. + CHECK_EQ(false, string->Equals(*compare)); + CHECK_EQ(false, compare->Equals(*string)); + CHECK_EQ(false, string->Equals(Heap::empty_string())); +#endif // !defined(DEBUG) +} diff --git a/v8/test/mjsunit/const-declaration.js b/v8/test/mjsunit/const-declaration.js new file mode 100644 index 0000000..48c0cf2 --- /dev/null +++ b/v8/test/mjsunit/const-declaration.js @@ -0,0 +1,172 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test handling of const variables in various settings. + +(function () { + function f() { + function g() { + x = 42; // should be ignored + return x; // force x into context + } + x = 43; // should be ignored + assertEquals(undefined, g()); + x = 44; // should be ignored + const x = 0; + x = 45; // should be ignored + assertEquals(0, g()); + } + f(); +})(); + + +(function () { + function f() { + function g() { + with ({foo: 0}) { + x = 42; // should be ignored + return x; // force x into context + } + } + x = 43; // should be ignored + assertEquals(undefined, g()); + x = 44; // should be ignored + const x = 0; + x = 45; // should be ignored + assertEquals(0, g()); + } + f(); +})(); + + +(function () { + function f() { + function g(s) { + eval(s); + return x; // force x into context + } + x = 43; // should be ignored + assertEquals(undefined, g("x = 42;")); + x = 44; // should be ignored + const x = 0; + x = 45; // should be ignored + assertEquals(0, g("x = 46;")); + } + f(); +})(); + + +(function () { + function f() { + function g(s) { + with ({foo: 0}) { + eval(s); + return x; // force x into context + } + } + x = 43; // should be ignored + assertEquals(undefined, g("x = 42;")); + x = 44; // should be ignored + const x = 0; + x = 45; // should be ignored + assertEquals(0, g("x = 46;")); + } + f(); +})(); + + +(function () { + function f(s) { + function g() { + x = 42; // assign to global x, or to const x + return x; + } + x = 43; // declare global x + assertEquals(42, g()); + x = 44; // assign to global x + eval(s); + x = 45; // should be ignored (assign to const x) + assertEquals(0, g()); + } + f("const x = 0;"); +})(); + + +(function () { + function f(s) { + function g() { + with ({foo: 0}) { + x = 42; // assign to global x, or to const x + return x; + } + } + x = 43; // declare global x + assertEquals(42, g()); + x = 44; // assign to global x + eval(s); + x = 45; // should be ignored (assign to const x) + assertEquals(0, g()); + } + f("const x = 0;"); +})(); + + +(function () { + function f(s) { + function g(s) { + eval(s); + return x; + } + x = 43; // declare global x + assertEquals(42, g("x = 42;")); + x = 44; // assign to global x + eval(s); + x = 45; // should be ignored (assign to const x) + assertEquals(0, g("x = 46;")); + } + f("const x = 0;"); +})(); + + +(function () { + function f(s) { + function g(s) { + with ({foo: 0}) { + eval(s); + return x; + } + } + x = 43; // declare global x + assertEquals(42, g("x = 42;")); + x = 44; // assign to global x + eval(s); + x = 45; // should be ignored (assign to const x) + assertEquals(0, g("x = 46;")); + } + f("const x = 0;"); +})(); + diff --git a/v8/test/mjsunit/constant-folding.js b/v8/test/mjsunit/constant-folding.js index 41b632f..33984c1 100644 --- a/v8/test/mjsunit/constant-folding.js +++ b/v8/test/mjsunit/constant-folding.js @@ -168,4 +168,29 @@ function test() { assertEquals(17, j, "switch with constant value"); } + +function TrueToString() { + return true.toString(); +} + + +function FalseToString() { + return false.toString(); +} + + +function BoolTest() { + assertEquals("true", TrueToString()); + assertEquals("true", TrueToString()); + assertEquals("true", TrueToString()); + assertEquals("false", FalseToString()); + assertEquals("false", FalseToString()); + assertEquals("false", FalseToString()); + Boolean.prototype.toString = function() { return "foo"; } + assertEquals("foo", TrueToString()); + assertEquals("foo", FalseToString()); +} + + test(); +BoolTest(); diff --git a/v8/test/mjsunit/debug-handle.js b/v8/test/mjsunit/debug-handle.js index 64a68a8..c7ab76a 100644 --- a/v8/test/mjsunit/debug-handle.js +++ b/v8/test/mjsunit/debug-handle.js @@ -95,8 +95,8 @@ function listener(event, exec_state, event_data, data) { // Test some illegal lookup requests. lookupRequest(dcp, void 0, false); - lookupRequest(dcp, '{"handle":"a"}', false); - lookupRequest(dcp, '{"handle":-1}', false); + lookupRequest(dcp, '{"handles":["a"]}', false); + lookupRequest(dcp, '{"handles":[-1]}', false); // Evaluate and get some handles. var handle_o = evaluateRequest(dcp, '{"expression":"o"}'); @@ -109,24 +109,28 @@ function listener(event, exec_state, event_data, data) { var response; var count; - response = lookupRequest(dcp, '{"handle":' + handle_o + '}', true); - assertEquals(handle_o, response.body.handle); + response = lookupRequest(dcp, '{"handles":[' + handle_o + ']}', true); + var obj = response.body[handle_o]; + assertTrue(!!obj, 'Object not found: ' + handle_o); + assertEquals(handle_o, obj.handle); count = 0; - for (i in response.body.properties) { - switch (response.body.properties[i].name) { + for (i in obj.properties) { + switch (obj.properties[i].name) { case 'o': - response.body.properties[i].ref = handle_o; + obj.properties[i].ref = handle_o; count++; break; case 'p': - response.body.properties[i].ref = handle_p; + obj.properties[i].ref = handle_p; count++; break; } } assertEquals(2, count, 'Either "o" or "p" not found'); - response = lookupRequest(dcp, '{"handle":' + handle_p + '}', true); - assertEquals(handle_p, response.body.handle); + response = lookupRequest(dcp, '{"handles":[' + handle_p + ']}', true); + obj = response.body[handle_p]; + assertTrue(!!obj, 'Object not found: ' + handle_p); + assertEquals(handle_p, obj.handle); // Check handles for functions on the stack. var handle_f = evaluateRequest(dcp, '{"expression":"f"}'); @@ -136,28 +140,31 @@ function listener(event, exec_state, event_data, data) { assertFalse(handle_f == handle_g, "f and g have he same handle"); assertEquals(handle_g, handle_caller, "caller for f should be g"); - response = lookupRequest(dcp, '{"handle":' + handle_f + '}', true); - assertEquals(handle_f, response.body.handle); + response = lookupRequest(dcp, '{"handles":[' + handle_f + ']}', true); + obj = response.body[handle_f]; + assertEquals(handle_f, obj.handle); + count = 0; - for (i in response.body.properties) { - var arguments = '{"handle":' + response.body.properties[i].ref + '}' - switch (response.body.properties[i].name) { + for (i in obj.properties) { + var ref = obj.properties[i].ref; + var arguments = '{"handles":[' + ref + ']}'; + switch (obj.properties[i].name) { case 'name': var response_name; response_name = lookupRequest(dcp, arguments, true); - assertEquals('string', response_name.body.type); - assertEquals("f", response_name.body.value); + assertEquals('string', response_name.body[ref].type); + assertEquals("f", response_name.body[ref].value); count++; break; case 'length': var response_length; response_length = lookupRequest(dcp, arguments, true); - assertEquals('number', response_length.body.type); - assertEquals(1, response_length.body.value); + assertEquals('number', response_length.body[ref].type); + assertEquals(1, response_length.body[ref].value); count++; break; case 'caller': - assertEquals(handle_g, response.body.properties[i].ref); + assertEquals(handle_g, obj.properties[i].ref); count++; break; } @@ -165,6 +172,49 @@ function listener(event, exec_state, event_data, data) { assertEquals(3, count, 'Either "name", "length" or "caller" not found'); + // Resolve all at once. + var refs = []; + for (i in obj.properties) { + refs.push(obj.properties[i].ref); + } + + var arguments = '{"handles":[' + refs.join(',') + ']}'; + response = lookupRequest(dcp, arguments, true); + count = 0; + for (i in obj.properties) { + var ref = obj.properties[i].ref; + var val = response.body[ref]; + assertTrue(!!val, 'Failed to lookup "' + obj.properties[i].name + '"'); + switch (obj.properties[i].name) { + case 'name': + assertEquals('string', val.type); + assertEquals("f", val.value); + count++; + break; + case 'length': + assertEquals('number', val.type); + assertEquals(1, val.value); + count++; + break; + case 'caller': + assertEquals('function', val.type); + assertEquals(handle_g, ref); + count++; + break; + } + } + assertEquals(3, count, 'Either "name", "length" or "caller" not found'); + + count = 0; + for (var handle in response.body) { + assertTrue(refs.indexOf(parseInt(handle)) != -1, + 'Handle not in the request: ' + handle); + count++; + } + assertEquals(count, obj.properties.length, + 'Unexpected number of resolved objects'); + + // Indicate that all was processed. listenerComplete = true; } @@ -195,5 +245,5 @@ p.p = p; g(o); // Make sure that the debug event listener vas invoked. -assertTrue(listenerComplete, "listener did not run to completion"); +assertTrue(listenerComplete, "listener did not run to completion: " + exception); assertFalse(exception, "exception in listener") diff --git a/v8/test/mjsunit/enumeration-order.js b/v8/test/mjsunit/enumeration-order.js index 699a636..a328121 100644 --- a/v8/test/mjsunit/enumeration-order.js +++ b/v8/test/mjsunit/enumeration-order.js @@ -26,17 +26,17 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function check_enumeration_order(obj) { - var value = 0; + var value = 0; for (var name in obj) assertTrue(value < obj[name]); value = obj[name]; } function make_object(size) { var a = new Object(); - + for (var i = 0; i < size; i++) a["a_" + i] = i + 1; check_enumeration_order(a); - + for (var i = 0; i < size; i +=3) delete a["a_" + i]; check_enumeration_order(a); } @@ -51,9 +51,59 @@ function make_literal_object(size) { code += "a_" + (size - 1) + " : " + size; code += " }"; eval("var a = " + code); - check_enumeration_order(a); + check_enumeration_order(a); } -// Validate the enumeration order for object literals up to 100 named properties. +// Validate the enumeration order for object literals up to 100 named +// properties. for (var j = 1; j< 100; j++) make_literal_object(j); +// We enumerate indexed properties in numerical order followed by +// named properties in insertion order, followed by indexed properties +// of the prototype object in numerical order, followed by named +// properties of the prototype object in insertion order, and so on. +// +// This enumeration order is not required by the specification, so +// this just documents our choice. +var proto2 = {}; +proto2[140000] = 0; +proto2.a = 0; +proto2[2] = 0; +proto2[3] = 0; // also on the 'proto1' object +proto2.b = 0; +proto2[4294967295] = 0; +proto2.c = 0; +proto2[4294967296] = 0; + +var proto1 = {}; +proto1[5] = 0; +proto1.d = 0; +proto1[3] = 0; +proto1.e = 0; +proto1.f = 0; // also on the 'o' object + +var o = {}; +o[-23] = 0; +o[300000000000] = 0; +o[23] = 0; +o.f = 0; +o.g = 0; +o[-4] = 0; +o[42] = 0; + +o.__proto__ = proto1; +proto1.__proto__ = proto2; + +var expected = ['23', '42', // indexed from 'o' + '-23', '300000000000', 'f', 'g', '-4', // named from 'o' + '3', '5', // indexed from 'proto1' + 'd', 'e', // named from 'proto1' + '2', '140000', '4294967295', // indexed from 'proto2' + 'a', 'b', 'c', '4294967296']; // named from 'proto2' +var actual = []; +for (var p in o) actual.push(p); +assertArrayEquals(expected, actual); + + + + diff --git a/v8/test/mjsunit/eval-enclosing-function-name.js b/v8/test/mjsunit/eval-enclosing-function-name.js new file mode 100644 index 0000000..422f03f --- /dev/null +++ b/v8/test/mjsunit/eval-enclosing-function-name.js @@ -0,0 +1,76 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// From within 'eval', the name of the enclosing function should be +// visible. + +var f = function y() { return typeof y; }; +assertEquals("function", f()); + + +f = function y() { return eval('typeof y'); }; +assertEquals("function", f()); + + +f = function y() { y = 3; return typeof y; }; +assertEquals("function", f()); + + +f = function y() { y += 3; return typeof y; }; +assertEquals("function", f()); + + +f = function y() { y &= y; return typeof y; }; +assertEquals("function", f()); + + +f = function y() { y = 3; return eval('typeof y'); } +assertEquals("function", f()); + + +f = function y() { var y = 3; return typeof y; } +assertEquals("number", f()); + + +f = function y() { var y = 3; return eval('typeof y'); } +assertEquals("number", f()); + + +f = function y() { eval('y = 3'); return typeof y; } +assertEquals("function", f()); + + +f = function y() { eval('y = 3'); return eval('typeof y'); } +assertEquals("function", f()); + + +f = function y() { eval('var y = 3'); return typeof y; } +assertEquals("number", f()); + + +f = function y() { eval('var y = 3'); return eval('typeof y'); } +assertEquals("number", f()); diff --git a/v8/test/mjsunit/mjsunit.js b/v8/test/mjsunit/mjsunit.js index 3570d68..370d491 100644 --- a/v8/test/mjsunit/mjsunit.js +++ b/v8/test/mjsunit/mjsunit.js @@ -113,6 +113,20 @@ function assertNaN(value, name_opt) { } +function assertNull(value, name_opt) { + if (value !== null) { + fail("null", value, name_opt); + } +} + + +function assertNotNull(value, name_opt) { + if (value === null) { + fail("not null", value, name_opt); + } +} + + function assertThrows(code) { var threwException = true; try { diff --git a/v8/test/mjsunit/mjsunit.status b/v8/test/mjsunit/mjsunit.status index 276f5a4..5a76078 100644 --- a/v8/test/mjsunit/mjsunit.status +++ b/v8/test/mjsunit/mjsunit.status @@ -36,9 +36,6 @@ fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm) big-object-literal: PASS, SKIP if ($arch == arm) -# Bug realiably triggers a debug assertion and crashed in release mode. -bugs/bug-269: CRASH, FAIL if $mode == debug - [ $arch == arm ] # Slow tests which times out in debug mode. @@ -64,7 +61,7 @@ debug-stepin-constructor: CRASH, FAIL debug-step: SKIP debug-breakpoints: PASS || FAIL debug-handle: CRASH, FAIL if $mode == debug -bugs/bug-269: SKIP +regress/regress-269: SKIP # Bug number 130 http://code.google.com/p/v8/issues/detail?id=130 # Fails on real ARM hardware but not on the simulator. diff --git a/v8/test/mjsunit/negate.js b/v8/test/mjsunit/negate.js index 3bf4111..70daf24 100644 --- a/v8/test/mjsunit/negate.js +++ b/v8/test/mjsunit/negate.js @@ -30,9 +30,9 @@ const SMI_MIN = -(1 << 30); function testmulneg(a, b) { var base = a * b; - assertEquals(-base, a * -b); - assertEquals(-base, -a * b); - assertEquals(base, -a * -b); + assertEquals(-base, a * -b, "a * -b where a = " + a + ", b = " + b); + assertEquals(-base, -a * b, "-a * b where a = " + a + ", b = " + b); + assertEquals(base, -a * -b, "*-a * -b where a = " + a + ", b = " + b); } testmulneg(2, 3); diff --git a/v8/test/mjsunit/number-limits.js b/v8/test/mjsunit/number-limits.js index 1d9a1e5..99ed4e1 100644 --- a/v8/test/mjsunit/number-limits.js +++ b/v8/test/mjsunit/number-limits.js @@ -33,10 +33,14 @@ function testLimits() { var addAboveMax = Number.MAX_VALUE + 1/eps; var mulBelowMin = Number.MIN_VALUE * (1 - eps); var addBelowMin = Number.MIN_VALUE - eps; - assertTrue(mulAboveMax == Number.MAX_VALUE || mulAboveMax == Infinity); - assertTrue(addAboveMax == Number.MAX_VALUE || addAboveMax == Infinity); - assertTrue(mulBelowMin == Number.MIN_VALUE || mulBelowMin <= 0); - assertTrue(addBelowMin == Number.MIN_VALUE || addBelowMin <= 0); + assertTrue(mulAboveMax == Number.MAX_VALUE || + mulAboveMax == Infinity, "mul" + i); + assertTrue(addAboveMax == Number.MAX_VALUE || + addAboveMax == Infinity, "add" + i); + assertTrue(mulBelowMin == Number.MIN_VALUE || + mulBelowMin <= 0, "mul2" + i); + assertTrue(addBelowMin == Number.MIN_VALUE || + addBelowMin <= 0, "add2" + i); } } diff --git a/v8/test/mjsunit/override-read-only-property.js b/v8/test/mjsunit/override-read-only-property.js new file mode 100644 index 0000000..b8fa501 --- /dev/null +++ b/v8/test/mjsunit/override-read-only-property.js @@ -0,0 +1,64 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// According to ECMA-262, sections 8.6.2.2 and 8.6.2.3 you're not +// allowed to override read-only properties, not even if the read-only +// property is in the prototype chain. +// +// However, for compatibility with WebKit/JSC, we allow the overriding +// of read-only properties in prototype chains. + +function F() {}; +F.prototype = Number; + +var original_number_max = Number.MAX_VALUE; + +// Assignment to a property which does not exist on the object itself, +// but is read-only in a prototype takes effect. +var f = new F(); +assertEquals(original_number_max, f.MAX_VALUE); +f.MAX_VALUE = 42; +assertEquals(42, f.MAX_VALUE); + +// Assignment to a property which does not exist on the object itself, +// but is read-only in a prototype takes effect. +f = new F(); +with (f) { + MAX_VALUE = 42; +} +assertEquals(42, f.MAX_VALUE); + +// Assignment to read-only property on the object itself is ignored. +Number.MAX_VALUE = 42; +assertEquals(original_number_max, Number.MAX_VALUE); + +// G should be read-only on the global object and the assignment is +// ignored. +(function G() { + eval("G = 42;"); + assertTrue(typeof G === 'function'); +})(); diff --git a/v8/test/mjsunit/receiver-in-with-calls.js b/v8/test/mjsunit/receiver-in-with-calls.js new file mode 100644 index 0000000..5f2bdac --- /dev/null +++ b/v8/test/mjsunit/receiver-in-with-calls.js @@ -0,0 +1,47 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// When invoking functions from within a 'with' statement, we must set +// the receiver to the object where we found the function. + +(function () { + var x = { get_this: function() { return this; } }; + assertTrue(x === x.get_this()); + with (x) assertTrue(x === get_this()); +})(); + + +assertTrue({ f: function() { + function g() { return this; }; + return eval("g")(); +} }.f() == this); + + +assertTrue({ f: function() { + function g() { return this; }; + return eval("g()"); +} }.f() == this); diff --git a/v8/test/mjsunit/regexp-multiline-stack-trace.js b/v8/test/mjsunit/regexp-multiline-stack-trace.js index aa2de88..fc248ef 100644 --- a/v8/test/mjsunit/regexp-multiline-stack-trace.js +++ b/v8/test/mjsunit/regexp-multiline-stack-trace.js @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// The flags below are to test the trace-calls functionality and the +// preallocated meessage memory. // Flags: --trace-calls --preallocate-message-memory /** diff --git a/v8/test/mjsunit/bugs/bug-269.js b/v8/test/mjsunit/regress/regress-269.js index 49b24c0..49b24c0 100644 --- a/v8/test/mjsunit/bugs/bug-269.js +++ b/v8/test/mjsunit/regress/regress-269.js diff --git a/v8/test/mjsunit/regress/regress-312.js b/v8/test/mjsunit/regress/regress-312.js new file mode 100644 index 0000000..0fb8c21 --- /dev/null +++ b/v8/test/mjsunit/regress/regress-312.js @@ -0,0 +1,31 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Should not trigger debug ASSERT. +// See http://code.google.com/p/v8/issues/detail?id=312 + +var o = { f: "x" ? function () {} : function () {} }; diff --git a/v8/test/mjsunit/smi-ops.js b/v8/test/mjsunit/smi-ops.js index bdd7509..80d2012 100644 --- a/v8/test/mjsunit/smi-ops.js +++ b/v8/test/mjsunit/smi-ops.js @@ -100,3 +100,98 @@ assertEquals(SMI_MIN - ONE_HUNDRED, Sub100(SMI_MIN)); // overflow assertEquals(ONE_HUNDRED - SMI_MIN, Sub100Reversed(SMI_MIN)); // overflow assertEquals(42 - ONE_HUNDRED, Sub100(OBJ_42)); // non-smi assertEquals(ONE_HUNDRED - 42, Sub100Reversed(OBJ_42)); // non-smi + + +function Shr1(x) { + return x >>> 1; +} + +function Shr100(x) { + return x >>> 100; +} + +function Shr1Reversed(x) { + return 1 >>> x; +} + +function Shr100Reversed(x) { + return 100 >>> x; +} + +function Sar1(x) { + return x >> 1; +} + +function Sar100(x) { + return x >> 100; +} + +function Sar1Reversed(x) { + return 1 >> x; +} + +function Sar100Reversed(x) { + return 100 >> x; +} + + +assertEquals(0, Shr1(1)); +assertEquals(0, Sar1(1)); +assertEquals(0, Shr1Reversed(2)); +assertEquals(0, Sar1Reversed(2)); +assertEquals(1610612736, Shr1(SMI_MIN)); +assertEquals(-536870912, Sar1(SMI_MIN)); +assertEquals(1, Shr1Reversed(SMI_MIN)); +assertEquals(1, Sar1Reversed(SMI_MIN)); +assertEquals(21, Shr1(OBJ_42)); +assertEquals(21, Sar1(OBJ_42)); +assertEquals(0, Shr1Reversed(OBJ_42)); +assertEquals(0, Sar1Reversed(OBJ_42)); + +assertEquals(6, Shr100(100)); +assertEquals(6, Sar100(100)); +assertEquals(12, Shr100Reversed(99)); +assertEquals(12, Sar100Reversed(99)); +assertEquals(201326592, Shr100(SMI_MIN)); +assertEquals(-67108864, Sar100(SMI_MIN)); +assertEquals(100, Shr100Reversed(SMI_MIN)); +assertEquals(100, Sar100Reversed(SMI_MIN)); +assertEquals(2, Shr100(OBJ_42)); +assertEquals(2, Sar100(OBJ_42)); +assertEquals(0, Shr100Reversed(OBJ_42)); +assertEquals(0, Sar100Reversed(OBJ_42)); + + +function Xor1(x) { + return x ^ 1; +} + +function Xor100(x) { + return x ^ 100; +} + +function Xor1Reversed(x) { + return 1 ^ x; +} + +function Xor100Reversed(x) { + return 100 ^ x; +} + + +assertEquals(0, Xor1(1)); +assertEquals(3, Xor1Reversed(2)); +assertEquals(SMI_MIN + 1, Xor1(SMI_MIN)); +assertEquals(SMI_MIN + 1, Xor1Reversed(SMI_MIN)); +assertEquals(43, Xor1(OBJ_42)); +assertEquals(43, Xor1Reversed(OBJ_42)); + +assertEquals(0, Xor100(100)); +assertEquals(7, Xor100Reversed(99)); +assertEquals(-1073741724, Xor100(SMI_MIN)); +assertEquals(-1073741724, Xor100Reversed(SMI_MIN)); +assertEquals(78, Xor100(OBJ_42)); +assertEquals(78, Xor100Reversed(OBJ_42)); + +var x = 0x23; var y = 0x35; +assertEquals(0x16, x ^ y); diff --git a/v8/test/mjsunit/string-match.js b/v8/test/mjsunit/string-match.js index 202396d..202396d 100644..100755 --- a/v8/test/mjsunit/string-match.js +++ b/v8/test/mjsunit/string-match.js diff --git a/v8/test/mjsunit/testcfg.py b/v8/test/mjsunit/testcfg.py index f65365d..9c7e028 100644 --- a/v8/test/mjsunit/testcfg.py +++ b/v8/test/mjsunit/testcfg.py @@ -32,6 +32,7 @@ import re FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") +FILES_PATTERN = re.compile(r"//\s+Files:(.*)") class MjsunitTestCase(test.TestCase): @@ -54,6 +55,12 @@ class MjsunitTestCase(test.TestCase): flags_match = FLAGS_PATTERN.search(source) if flags_match: result += flags_match.group(1).strip().split() + files_match = FILES_PATTERN.search(source); + additional_files = [] + if files_match: + additional_files += files_match.group(1).strip().split() + for a_file in additional_files: + result.append(join(dirname(self.config.root), '..', a_file)) framework = join(dirname(self.config.root), 'mjsunit', 'mjsunit.js') result += [framework, self.file] return result @@ -76,7 +83,8 @@ class MjsunitTestConfiguration(test.TestConfiguration): mjsunit = [current_path + [t] for t in self.Ls(self.root)] regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))] bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))] - all_tests = mjsunit + regress + bugs + tools = [current_path + ['tools', t] for t in self.Ls(join(self.root, 'tools'))] + all_tests = mjsunit + regress + bugs + tools result = [] for test in all_tests: if self.Contains(path, test): diff --git a/v8/test/mjsunit/tools/codemap.js b/v8/test/mjsunit/tools/codemap.js new file mode 100644 index 0000000..7f344a6 --- /dev/null +++ b/v8/test/mjsunit/tools/codemap.js @@ -0,0 +1,116 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Load Splay tree and CodeMap implementations from <project root>/tools. +// Files: tools/splaytree.js tools/codemap.js + + +function newCodeEntry(size, name) { + return new devtools.profiler.CodeMap.CodeEntry(size, name); +}; + + +function assertEntry(codeMap, expected_name, addr) { + var entry = codeMap.findEntry(addr); + assertNotNull(entry, 'no entry at ' + addr.toString(16)); + assertEquals(expected_name, entry.name, 'at ' + addr.toString(16)); +}; + + +function assertNoEntry(codeMap, addr) { + assertNull(codeMap.findEntry(addr), 'at ' + addr.toString(16)); +}; + + +(function testStaticCode() { + var codeMap = new devtools.profiler.CodeMap(); + codeMap.addStaticCode(0x1500, newCodeEntry(0x3000, 'lib1')); + codeMap.addStaticCode(0x15500, newCodeEntry(0x5000, 'lib2')); + codeMap.addStaticCode(0x155500, newCodeEntry(0x10000, 'lib3')); + assertNoEntry(codeMap, 0); + assertNoEntry(codeMap, 0x1500 - 1); + assertEntry(codeMap, 'lib1', 0x1500); + assertEntry(codeMap, 'lib1', 0x1500 + 0x100); + assertEntry(codeMap, 'lib1', 0x1500 + 0x1000); + assertEntry(codeMap, 'lib1', 0x1500 + 0x3000 - 1); + assertNoEntry(codeMap, 0x1500 + 0x3000); + assertNoEntry(codeMap, 0x15500 - 1); + assertEntry(codeMap, 'lib2', 0x15500); + assertEntry(codeMap, 'lib2', 0x15500 + 0x100); + assertEntry(codeMap, 'lib2', 0x15500 + 0x1000); + assertEntry(codeMap, 'lib2', 0x15500 + 0x5000 - 1); + assertNoEntry(codeMap, 0x15500 + 0x5000); + assertNoEntry(codeMap, 0x155500 - 1); + assertEntry(codeMap, 'lib3', 0x155500); + assertEntry(codeMap, 'lib3', 0x155500 + 0x100); + assertEntry(codeMap, 'lib3', 0x155500 + 0x1000); + assertEntry(codeMap, 'lib3', 0x155500 + 0x10000 - 1); + assertNoEntry(codeMap, 0x155500 + 0x10000); + assertNoEntry(codeMap, 0xFFFFFFFF); +})(); + + +(function testDynamicCode() { + var codeMap = new devtools.profiler.CodeMap(); + codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1')); + codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2')); + codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3')); + codeMap.addCode(0x1950, newCodeEntry(0x10, 'code4')); + assertNoEntry(codeMap, 0); + assertNoEntry(codeMap, 0x1500 - 1); + assertEntry(codeMap, 'code1', 0x1500); + assertEntry(codeMap, 'code1', 0x1500 + 0x100); + assertEntry(codeMap, 'code1', 0x1500 + 0x200 - 1); + assertEntry(codeMap, 'code2', 0x1700); + assertEntry(codeMap, 'code2', 0x1700 + 0x50); + assertEntry(codeMap, 'code2', 0x1700 + 0x100 - 1); + assertNoEntry(codeMap, 0x1700 + 0x100); + assertNoEntry(codeMap, 0x1900 - 1); + assertEntry(codeMap, 'code3', 0x1900); + assertEntry(codeMap, 'code3', 0x1900 + 0x28); + assertEntry(codeMap, 'code4', 0x1950); + assertEntry(codeMap, 'code4', 0x1950 + 0x7); + assertEntry(codeMap, 'code4', 0x1950 + 0x10 - 1); + assertNoEntry(codeMap, 0x1950 + 0x10); + assertNoEntry(codeMap, 0xFFFFFFFF); +})(); + + +(function testCodeMovesAndDeletions() { + var codeMap = new devtools.profiler.CodeMap(); + codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1')); + codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2')); + assertEntry(codeMap, 'code1', 0x1500); + assertEntry(codeMap, 'code2', 0x1700); + codeMap.moveCode(0x1500, 0x1800); + assertNoEntry(codeMap, 0x1500); + assertEntry(codeMap, 'code2', 0x1700); + assertEntry(codeMap, 'code1', 0x1800); + codeMap.deleteCode(0x1700); + assertNoEntry(codeMap, 0x1700); + assertEntry(codeMap, 'code1', 0x1800); +})(); diff --git a/v8/test/mjsunit/tools/splaytree.js b/v8/test/mjsunit/tools/splaytree.js new file mode 100644 index 0000000..3beba0b --- /dev/null +++ b/v8/test/mjsunit/tools/splaytree.js @@ -0,0 +1,166 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Load the Splay tree implementation from <project root>/tools. +// Files: tools/splaytree.js + + +(function testIsEmpty() { + var tree = new goog.structs.SplayTree(); + assertTrue(tree.isEmpty()); + tree.insert(0, 'value'); + assertFalse(tree.isEmpty()); +})(); + + +(function testExportValues() { + var tree = new goog.structs.SplayTree(); + assertArrayEquals([], tree.exportValues()); + tree.insert(0, 'value'); + assertArrayEquals(['value'], tree.exportValues()); + tree.insert(0, 'value'); + assertArrayEquals(['value'], tree.exportValues()); +})(); + + +function createSampleTree() { + // Creates the following tree: + // 50 + // / \ + // 30 60 + // / \ \ + // 10 40 90 + // \ / \ + // 20 70 100 + // / \ + // 15 80 + // + // We can't use the 'insert' method because it also uses 'splay_'. + return { key: 50, value: 50, + left: { key: 30, value: 30, + left: { key: 10, value: 10, left: null, + right: { key: 20, value: 20, + left: { key: 15, value: 15, + left: null, right: null }, + right: null } }, + right: { key: 40, value: 40, left: null, right: null } }, + right: { key: 60, value: 60, left: null, + right: { key: 90, value: 90, + left: { key: 70, value: 70, left: null, + right: { key: 80, value: 80, + left: null, right: null } }, + right: { key: 100, value: 100, + left: null, right: null } } } }; +}; + + +(function testSplay() { + var tree = new goog.structs.SplayTree(); + tree.root_ = createSampleTree(); + assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'], + tree.exportValues()); + tree.splay_(50); + assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'], + tree.exportValues()); + tree.splay_(80); + assertArrayEquals(['80', '60', '90', '50', '70', '100', '30', '10', '40', '20', '15'], + tree.exportValues()); +})(); + + +(function testInsert() { + var tree = new goog.structs.SplayTree(); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + assertArrayEquals(['left', 'root'], tree.exportValues()); + tree.insert(7, 'right'); + assertArrayEquals(['right', 'root', 'left'], tree.exportValues()); +})(); + + +(function testFind() { + var tree = new goog.structs.SplayTree(); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + tree.insert(7, 'right'); + assertEquals('root', tree.find(5).value); + assertEquals('left', tree.find(3).value); + assertEquals('right', tree.find(7).value); + assertEquals(null, tree.find(0)); + assertEquals(null, tree.find(100)); + assertEquals(null, tree.find(-100)); +})(); + + +(function testFindMin() { + var tree = new goog.structs.SplayTree(); + assertEquals(null, tree.findMin()); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + tree.insert(7, 'right'); + assertEquals('left', tree.findMin().value); +})(); + + +(function testFindMax() { + var tree = new goog.structs.SplayTree(); + assertEquals(null, tree.findMax()); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + tree.insert(7, 'right'); + assertEquals('right', tree.findMax().value); +})(); + + +(function testFindGreatestLessThan() { + var tree = new goog.structs.SplayTree(); + assertEquals(null, tree.findGreatestLessThan(10)); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + tree.insert(7, 'right'); + assertEquals('right', tree.findGreatestLessThan(10).value); + assertEquals('right', tree.findGreatestLessThan(7).value); + assertEquals('root', tree.findGreatestLessThan(6).value); + assertEquals('left', tree.findGreatestLessThan(4).value); + assertEquals(null, tree.findGreatestLessThan(2)); +})(); + + +(function testRemove() { + var tree = new goog.structs.SplayTree(); + assertThrows('tree.remove(5)'); + tree.insert(5, 'root'); + tree.insert(3, 'left'); + tree.insert(7, 'right'); + assertThrows('tree.remove(1)'); + assertThrows('tree.remove(6)'); + assertThrows('tree.remove(10)'); + assertEquals('root', tree.remove(5).value); + assertEquals('left', tree.remove(3).value); + assertEquals('right', tree.remove(7).value); + assertArrayEquals([], tree.exportValues()); +})(); diff --git a/v8/tools/codemap.js b/v8/tools/codemap.js new file mode 100644 index 0000000..f91d525 --- /dev/null +++ b/v8/tools/codemap.js @@ -0,0 +1,205 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Initlialize namespaces +var devtools = devtools || {}; +devtools.profiler = devtools.profiler || {}; + + +/** + * Constructs a mapper that maps addresses into code entries. + * + * @constructor + */ +devtools.profiler.CodeMap = function() { + /** + * Dynamic code entries. Used for JIT compiled code. + */ + this.dynamics_ = new goog.structs.SplayTree(); + + /** + * Deleted code entries. Used for code collected by the GC. + */ + this.deleted_ = []; + + /** + * Static code entries. Used for libraries code. + */ + this.statics_ = new goog.structs.SplayTree(); + + /** + * Map of memory pages occupied with static code. + */ + this.pages_ = []; +}; + + +/** + * The number of alignment bits in a page address. + */ +devtools.profiler.CodeMap.PAGE_ALIGNMENT = 12; + + +/** + * Page size in bytes. + */ +devtools.profiler.CodeMap.PAGE_SIZE = + 1 << devtools.profiler.CodeMap.PAGE_ALIGNMENT; + + +/** + * Adds a dynamic (i.e. moveable and discardable) code entry. + * + * @param {number} start The starting address. + * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object. + */ +devtools.profiler.CodeMap.prototype.addCode = function(start, codeEntry) { + this.dynamics_.insert(start, codeEntry); +}; + + +/** + * Moves a dynamic code entry. Throws an exception if there is no dynamic + * code entry with the specified starting address. + * + * @param {number} from The starting address of the entry being moved. + * @param {number} to The destination address. + */ +devtools.profiler.CodeMap.prototype.moveCode = function(from, to) { + var removedNode = this.dynamics_.remove(from); + this.dynamics_.insert(to, removedNode.value); +}; + + +/** + * Discards a dynamic code entry. Throws an exception if there is no dynamic + * code entry with the specified starting address. The entry will still be + * returned from the 'getAllDynamicEntries' method. + * + * @param {number} start The starting address of the entry being deleted. + */ +devtools.profiler.CodeMap.prototype.deleteCode = function(start) { + var removedNode = this.dynamics_.remove(start); + this.deleted_.push(removedNode.value); +}; + + +/** + * Adds a static code entry. + * + * @param {number} start The starting address. + * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object. + */ +devtools.profiler.CodeMap.prototype.addStaticCode = function( + start, codeEntry) { + this.markPages_(start, start + codeEntry.size); + this.statics_.insert(start, codeEntry); +}; + + +/** + * @private + */ +devtools.profiler.CodeMap.prototype.markPages_ = function(start, end) { + for (var addr = start; addr <= end; + addr += devtools.profiler.CodeMap.PAGE_SIZE) { + this.pages_[addr >> devtools.profiler.CodeMap.PAGE_ALIGNMENT] = 1; + } +}; + + +/** + * @private + */ +devtools.profiler.CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) { + return addr >= node.key && addr < (node.key + node.value.size); +}; + + +/** + * @private + */ +devtools.profiler.CodeMap.prototype.findInTree_ = function(tree, addr) { + var node = tree.findGreatestLessThan(addr); + return node && this.isAddressBelongsTo_(addr, node) ? node.value : null; +}; + + +/** + * Finds a code entry that contains the specified address. Both static and + * dynamic code entries are considered. + * + * @param {number} addr Address. + */ +devtools.profiler.CodeMap.prototype.findEntry = function(addr) { + var pageAddr = addr >> devtools.profiler.CodeMap.PAGE_ALIGNMENT; + if (pageAddr in this.pages_) { + return this.findInTree_(this.statics_, addr); + } + var min = this.dynamics_.findMin(); + var max = this.dynamics_.findMax(); + if (max != null && addr < (max.key + max.value.size) && addr >= min.key) { + return this.findInTree_(this.dynamics_, addr); + } + return null; +}; + + +/** + * Returns an array of all dynamic code entries, including deleted ones. + */ +devtools.profiler.CodeMap.prototype.getAllDynamicEntries = function() { + var dynamicEntries = this.dynamics_.exportValues(); + return dynamicEntries.concat(this.deleted_); +}; + + +/** + * Returns an array of all static code entries. + */ +devtools.profiler.CodeMap.prototype.getAllStaticEntries = function() { + return this.statics_.exportValues(); +}; + + +/** + * Creates a code entry object. + * + * @param {number} size Code entry size in bytes. + * @param {string} opt_name Code entry name. + * @constructor + */ +devtools.profiler.CodeMap.CodeEntry = function(size, opt_name) { + this.size = size; + this.name = opt_name || ''; +}; + + +devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() { + return this.name + ': ' + this.size.toString(16); +}; diff --git a/v8/tools/jsmin.pyc b/v8/tools/jsmin.pyc Binary files differnew file mode 100644 index 0000000..341b5e1 --- /dev/null +++ b/v8/tools/jsmin.pyc diff --git a/v8/tools/presubmit.py b/v8/tools/presubmit.py index 27a3f2c..3e714de 100755 --- a/v8/tools/presubmit.py +++ b/v8/tools/presubmit.py @@ -35,6 +35,9 @@ import re import sys import subprocess +# Disabled LINT rules and reason. +# build/include_what_you_use: Started giving false positives for variables +# named "string" and "map" assuming that you needed to include STL headers. ENABLED_LINT_RULES = """ build/class @@ -42,7 +45,6 @@ build/deprecated build/endif_comment build/forward_decl build/include_order -build/include_what_you_use build/printf_format build/storage_class legal/copyright diff --git a/v8/tools/splaytree.js b/v8/tools/splaytree.js new file mode 100644 index 0000000..f3beb16 --- /dev/null +++ b/v8/tools/splaytree.js @@ -0,0 +1,321 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// A namespace stub. It will become more clear how to declare it properly +// during integration of this script into Dev Tools. +goog = { structs: {} }; + + +/** + * Constructs a Splay tree. A splay tree is a self-balancing binary + * search tree with the additional property that recently accessed + * elements are quick to access again. It performs basic operations + * such as insertion, look-up and removal in O(log(n)) amortized time. + * + * @constructor + */ +goog.structs.SplayTree = function() { +}; + + +/** + * Pointer to the root node of the tree. + * + * @type {goog.structs.SplayTree.Node} + * @private + */ +goog.structs.SplayTree.prototype.root_ = null; + + +/** + * @return {boolean} Whether the tree is empty. + */ +goog.structs.SplayTree.prototype.isEmpty = function() { + return !this.root_; +}; + + + +/** + * Inserts a node into the tree with the specified key and value if + * the tree does not already contain a node with the specified key. If + * the value is inserted, it becomes the root of the tree. + * + * @param {number} key Key to insert into the tree. + * @param {*} value Value to insert into the tree. + */ +goog.structs.SplayTree.prototype.insert = function(key, value) { + if (this.isEmpty()) { + this.root_ = new goog.structs.SplayTree.Node(key, value); + return; + } + // Splay on the key to move the last node on the search path for + // the key to the root of the tree. + this.splay_(key); + if (this.root_.key == key) { + return; + } + var node = new goog.structs.SplayTree.Node(key, value); + if (key > this.root_.key) { + node.left = this.root_; + node.right = this.root_.right; + this.root_.right = null; + } else { + node.right = this.root_; + node.left = this.root_.left; + this.root_.left = null; + } + this.root_ = node; +}; + + +/** + * Removes a node with the specified key from the tree if the tree + * contains a node with this key. The removed node is returned. If the + * key is not found, an exception is thrown. + * + * @param {number} key Key to find and remove from the tree. + * @return {goog.structs.SplayTree.Node} The removed node. + */ +goog.structs.SplayTree.prototype.remove = function(key) { + if (this.isEmpty()) { + throw Error('Key not found: ' + key); + } + this.splay_(key); + if (this.root_.key != key) { + throw Error('Key not found: ' + key); + } + var removed = this.root_; + if (!this.root_.left) { + this.root_ = this.root_.right; + } else { + var right = this.root_.right; + this.root_ = this.root_.left; + // Splay to make sure that the new root has an empty right child. + this.splay_(key); + // Insert the original right child as the right child of the new + // root. + this.root_.right = right; + } + return removed; +}; + + +/** + * Returns the node having the specified key or null if the tree doesn't contain + * a node with the specified key. + * + * @param {number} key Key to find in the tree. + * @return {goog.structs.SplayTree.Node} Node having the specified key. + */ +goog.structs.SplayTree.prototype.find = function(key) { + if (this.isEmpty()) { + return null; + } + this.splay_(key); + return this.root_.key == key ? this.root_ : null; +}; + + +/** + * @return {goog.structs.SplayTree.Node} Node having the minimum key value. + */ +goog.structs.SplayTree.prototype.findMin = function() { + if (this.isEmpty()) { + return null; + } + var current = this.root_; + while (current.left) { + current = current.left; + } + return current; +}; + + +/** + * @return {goog.structs.SplayTree.Node} Node having the maximum key value. + */ +goog.structs.SplayTree.prototype.findMax = function(opt_startNode) { + if (this.isEmpty()) { + return null; + } + var current = opt_startNode || this.root_; + while (current.right) { + current = current.right; + } + return current; +}; + + +/** + * @return {goog.structs.SplayTree.Node} Node having the maximum key value that + * is less or equal to the specified key value. + */ +goog.structs.SplayTree.prototype.findGreatestLessThan = function(key) { + if (this.isEmpty()) { + return null; + } + // Splay on the key to move the node with the given key or the last + // node on the search path to the top of the tree. + this.splay_(key); + // Now the result is either the root node or the greatest node in + // the left subtree. + if (this.root_.key <= key) { + return this.root_; + } else if (this.root_.left) { + return this.findMax(this.root_.left); + } else { + return null; + } +}; + + +/** + * @return {Array<*>} An array containing all the values of tree's nodes. + */ +goog.structs.SplayTree.prototype.exportValues = function() { + var result = []; + this.traverse_(function(node) { result.push(node.value); }); + return result; +}; + + +/** + * Perform the splay operation for the given key. Moves the node with + * the given key to the top of the tree. If no node has the given + * key, the last node on the search path is moved to the top of the + * tree. This is the simplified top-down splaying algorithm from: + * "Self-adjusting Binary Search Trees" by Sleator and Tarjan + * + * @param {number} key Key to splay the tree on. + * @private + */ +goog.structs.SplayTree.prototype.splay_ = function(key) { + if (this.isEmpty()) { + return; + } + // Create a dummy node. The use of the dummy node is a bit + // counter-intuitive: The right child of the dummy node will hold + // the L tree of the algorithm. The left child of the dummy node + // will hold the R tree of the algorithm. Using a dummy node, left + // and right will always be nodes and we avoid special cases. + var dummy, left, right; + dummy = left = right = new goog.structs.SplayTree.Node(null, null); + var current = this.root_; + while (true) { + if (key < current.key) { + if (!current.left) { + break; + } + if (key < current.left.key) { + // Rotate right. + var tmp = current.left; + current.left = tmp.right; + tmp.right = current; + current = tmp; + if (!current.left) { + break; + } + } + // Link right. + right.left = current; + right = current; + current = current.left; + } else if (key > current.key) { + if (!current.right) { + break; + } + if (key > current.right.key) { + // Rotate left. + var tmp = current.right; + current.right = tmp.left; + tmp.left = current; + current = tmp; + if (!current.right) { + break; + } + } + // Link left. + left.right = current; + left = current; + current = current.right; + } else { + break; + } + } + // Assemble. + left.right = current.left; + right.left = current.right; + current.left = dummy.right; + current.right = dummy.left; + this.root_ = current; +}; + + +/** + * Performs a preorder traversal of the tree. + * + * @param {function(goog.structs.SplayTree.Node)} f Visitor function. + * @private + */ +goog.structs.SplayTree.prototype.traverse_ = function(f) { + var nodesToVisit = [this.root_]; + while (nodesToVisit.length > 0) { + var node = nodesToVisit.shift(); + if (node == null) { + continue; + } + f(node); + nodesToVisit.push(node.left); + nodesToVisit.push(node.right); + } +}; + + +/** + * Constructs a Splay tree node. + * + * @param {number} key Key. + * @param {*} value Value. + */ +goog.structs.SplayTree.Node = function(key, value) { + this.key = key; + this.value = value; +}; + + +/** + * @type {goog.structs.SplayTree.Node} + */ +goog.structs.SplayTree.Node.prototype.left = null; + + +/** + * @type {goog.structs.SplayTree.Node} + */ +goog.structs.SplayTree.Node.prototype.right = null; diff --git a/v8/tools/v8.xcodeproj/project.pbxproj b/v8/tools/v8.xcodeproj/project.pbxproj index caa6b33..eba4ec7 100644 --- a/v8/tools/v8.xcodeproj/project.pbxproj +++ b/v8/tools/v8.xcodeproj/project.pbxproj @@ -202,6 +202,9 @@ 89F23C9E0E78D5FD006B2466 /* macro-assembler-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1540E719B8F00D62E90 /* macro-assembler-arm.cc */; }; 89F23C9F0E78D604006B2466 /* simulator-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF17D0E719B8F00D62E90 /* simulator-arm.cc */; }; 89F23CA00E78D609006B2466 /* stub-cache-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF18A0E719B8F00D62E90 /* stub-cache-arm.cc */; }; + 89FB0E3A0F8E533F00B04B3C /* d8-posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89FB0E360F8E531900B04B3C /* d8-posix.cc */; }; + 9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; }; + 9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; }; 9FC86ABD0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; }; 9FC86ABE0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; }; /* End PBXBuildFile section */ @@ -517,6 +520,10 @@ 89B12E8D0E7FF2A40080BA62 /* presubmit.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = presubmit.py; sourceTree = "<group>"; }; 89F23C870E78D5B2006B2466 /* libv8-arm.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libv8-arm.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 89F23C950E78D5B6006B2466 /* v8_shell-arm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "v8_shell-arm"; sourceTree = BUILT_PRODUCTS_DIR; }; + 89FB0E360F8E531900B04B3C /* d8-posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-posix.cc"; path = "../src/d8-posix.cc"; sourceTree = "<group>"; }; + 89FB0E370F8E531900B04B3C /* d8-windows.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-windows.cc"; path = "../src/d8-windows.cc"; sourceTree = "<group>"; }; + 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "func-name-inferrer.cc"; sourceTree = "<group>"; }; + 9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "func-name-inferrer.h"; sourceTree = "<group>"; }; 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "oprofile-agent.cc"; sourceTree = "<group>"; }; 9FC86ABC0F5FEDAC00F22668 /* oprofile-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "oprofile-agent.h"; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -689,6 +696,8 @@ 897FF13B0E719B8F00D62E90 /* frames-inl.h */, 897FF13C0E719B8F00D62E90 /* frames.cc */, 897FF13D0E719B8F00D62E90 /* frames.h */, + 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */, + 9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */, 897FF13E0E719B8F00D62E90 /* global-handles.cc */, 897FF13F0E719B8F00D62E90 /* global-handles.h */, 897FF1400E719B8F00D62E90 /* globals.h */, @@ -872,6 +881,8 @@ 89A15C910EE46A1700B48DEB /* d8-readline.cc */, 893988150F2A3686007D5254 /* d8-debug.cc */, 893A72320F7B4AD700303DD2 /* d8-debug.h */, + 89FB0E360F8E531900B04B3C /* d8-posix.cc */, + 89FB0E370F8E531900B04B3C /* d8-windows.cc */, 89A15C920EE46A1700B48DEB /* d8.cc */, 89A15C930EE46A1700B48DEB /* d8.h */, 89A15C940EE46A1700B48DEB /* d8.js */, @@ -1073,6 +1084,7 @@ 8939880D0F2A362A007D5254 /* d8.cc in Sources */, 893988160F2A3688007D5254 /* d8-debug.cc in Sources */, 893988330F2A3B8F007D5254 /* d8-js.cc in Sources */, + 89FB0E3A0F8E533F00B04B3C /* d8-posix.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1111,6 +1123,7 @@ 89A88E050E71A65D0043BA31 /* flags.cc in Sources */, 89A88E060E71A6600043BA31 /* frames-ia32.cc in Sources */, 89A88E070E71A6610043BA31 /* frames.cc in Sources */, + 9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */, 89A88E080E71A6620043BA31 /* global-handles.cc in Sources */, 89A88E090E71A6640043BA31 /* handles.cc in Sources */, 89A88E0A0E71A6650043BA31 /* hashmap.cc in Sources */, @@ -1211,6 +1224,7 @@ 89F23C580E78D5B2006B2466 /* flags.cc in Sources */, 89F23C9C0E78D5F1006B2466 /* frames-arm.cc in Sources */, 89F23C5A0E78D5B2006B2466 /* frames.cc in Sources */, + 9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */, 89F23C5B0E78D5B2006B2466 /* global-handles.cc in Sources */, 89F23C5C0E78D5B2006B2466 /* handles.cc in Sources */, 89F23C5D0E78D5B2006B2466 /* hashmap.cc in Sources */, diff --git a/v8/tools/visual_studio/common.vsprops b/v8/tools/visual_studio/common.vsprops index 7e18f17..f131a4a 100644 --- a/v8/tools/visual_studio/common.vsprops +++ b/v8/tools/visual_studio/common.vsprops @@ -10,7 +10,7 @@ <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="$(ProjectDir)\..\..\src;$(IntDir)\DerivedSources" - PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_USE_32BIT_TIME_T;_HAS_EXCEPTIONS=0;PCRE_STATIC;ENABLE_LOGGING_AND_PROFILING" + PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_USE_32BIT_TIME_T;_HAS_EXCEPTIONS=0;ENABLE_LOGGING_AND_PROFILING" MinimalRebuild="false" ExceptionHandling="0" RuntimeTypeInfo="false" diff --git a/v8/tools/visual_studio/v8_base.vcproj b/v8/tools/visual_studio/v8_base.vcproj index 81e1f09..776e628 100644 --- a/v8/tools/visual_studio/v8_base.vcproj +++ b/v8/tools/visual_studio/v8_base.vcproj @@ -417,6 +417,14 @@ > </File> <File + RelativePath="..\..\src\func-name-inferrer.cc" + > + </File> + <File + RelativePath="..\..\src\func-name-inferrer.h" + > + </File> + <File RelativePath="..\..\src\global-handles.cc" > </File> diff --git a/v8/tools/visual_studio/v8_base_arm.vcproj b/v8/tools/visual_studio/v8_base_arm.vcproj index a03306d..a91d63a 100644 --- a/v8/tools/visual_studio/v8_base_arm.vcproj +++ b/v8/tools/visual_studio/v8_base_arm.vcproj @@ -413,6 +413,14 @@ > </File> <File + RelativePath="..\..\src\func-name-inferrer.cc" + > + </File> + <File + RelativePath="..\..\src\func-name-inferrer.h" + > + </File> + <File RelativePath="..\..\src\global-handles.cc" > </File> diff --git a/v8/tools/visual_studio/v8_cctest.vcproj b/v8/tools/visual_studio/v8_cctest.vcproj index 859e445..5d49d24 100644 --- a/v8/tools/visual_studio/v8_cctest.vcproj +++ b/v8/tools/visual_studio/v8_cctest.vcproj @@ -186,6 +186,10 @@ > </File> <File + RelativePath="..\..\test\cctest\test-func-name-inference.cc" + > + </File> + <File RelativePath="..\..\test\cctest\test-hashmap.cc" > </File> |