diff options
Diffstat (limited to 'deps/v8')
63 files changed, 3148 insertions, 6407 deletions
diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index c618f202f9..9f2005f40f 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,12 @@ +2010-11-23: Version 2.5.8 + + Removed dependency on Gay's dtoa. + + Improved heap profiler precision and speed. + + Reduced overhead of callback invocations on ARM. + + 2010-11-18: Version 2.5.7 Fixed obscure evaluation order bug (issue 931). diff --git a/deps/v8/LICENSE b/deps/v8/LICENSE index e3ed242d42..c1fcb1a146 100644 --- a/deps/v8/LICENSE +++ b/deps/v8/LICENSE @@ -12,9 +12,6 @@ are: based on layout tests from webkit.org which are copyrighted by Apple Computer, Inc. and released under a 3-clause BSD license. - - Dtoa, located under third_party/dtoa. This code is copyrighted by - David M. Gay and released under an MIT license. - - Strongtalk assembler, the basis of the files assembler-arm-inl.h, assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h. diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index fb492d955c..72195c44f2 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -197,8 +197,13 @@ class V8EXPORT HeapGraphEdge { kContextVariable = 0, // A variable from a function context. kElement = 1, // An element of an array. kProperty = 2, // A named object property. - kInternal = 3 // A link that can't be accessed from JS, - // thus, its name isn't a real property name. + kInternal = 3, // A link that can't be accessed from JS, + // thus, its name isn't a real property name + // (e.g. parts of a ConsString). + kHidden = 4, // A link that is needed for proper sizes + // calculation, but may be hidden from user. + kShortcut = 5 // A link that must not be followed during + // sizes calculation. }; /** Returns edge type (see HeapGraphEdge::Type). */ @@ -240,7 +245,8 @@ class V8EXPORT HeapGraphPath { class V8EXPORT HeapGraphNode { public: enum Type { - kInternal = 0, // Internal node, a virtual one, for housekeeping. + kInternal = 0, // For compatibility, will be removed. + kHidden = 0, // Hidden node, may be filtered when shown to user. kArray = 1, // An array of elements. kString = 2, // A string. kObject = 3, // A JS object (except for arrays and strings). @@ -276,16 +282,19 @@ class V8EXPORT HeapGraphNode { /** Returns node's own size, in bytes. */ int GetSelfSize() const; - /** Returns node's network (self + reachable nodes) size, in bytes. */ - int GetReachableSize() const; - /** * Returns node's retained size, in bytes. That is, self + sizes of * the objects that are reachable only from this object. In other * words, the size of memory that will be reclaimed having this node * collected. + * + * Exact retained size calculation has O(N) (number of nodes) + * computational complexity, while approximate has O(1). It is + * assumed that initially heap profiling tools provide approximate + * sizes for all nodes, and then exact sizes are calculated for the + * most 'interesting' nodes. */ - int GetRetainedSize() const; + int GetRetainedSize(bool exact) const; /** Returns child nodes count of the node. */ int GetChildrenCount() const; @@ -304,6 +313,12 @@ class V8EXPORT HeapGraphNode { /** Returns a retaining path by index. */ const HeapGraphPath* GetRetainingPath(int index) const; + + /** + * Returns a dominator node. This is the node that participates in every + * path from the snapshot root to the current node. + */ + const HeapGraphNode* GetDominatorNode() const; }; diff --git a/deps/v8/samples/shell.cc b/deps/v8/samples/shell.cc index 1a13f5f80b..2bdc5a19da 100644 --- a/deps/v8/samples/shell.cc +++ b/deps/v8/samples/shell.cc @@ -37,6 +37,7 @@ bool ExecuteString(v8::Handle<v8::String> source, v8::Handle<v8::Value> name, bool print_result, bool report_exceptions); +v8::Handle<v8::Value> PrintToInteger(const v8::Arguments& args); v8::Handle<v8::Value> Print(const v8::Arguments& args); v8::Handle<v8::Value> Read(const v8::Arguments& args); v8::Handle<v8::Value> Load(const v8::Arguments& args); @@ -53,7 +54,8 @@ int RunMain(int argc, char* argv[]) { v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); // Bind the global 'print' function to the C++ Print callback. global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); - // Bind the global 'read' function to the C++ Read callback. +global->Set(v8::String::New("print2int"), v8::FunctionTemplate::New(PrintToInteger)); +// Bind the global 'read' function to the C++ Read callback. global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); // Bind the global 'load' function to the C++ Load callback. global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); @@ -138,6 +140,16 @@ v8::Handle<v8::Value> Print(const v8::Arguments& args) { } +v8::Handle<v8::Value> PrintToInteger(const v8::Arguments& args) { + v8::HandleScope handle_scope; + v8::String::Utf8Value str(args[0]); + const char* cstr = ToCString(str); + printf("%s -> %d\n", cstr, args[0]->ToInt32()->Value()); + fflush(stdout); + return v8::Undefined(); +} + + // The callback that is invoked by v8 whenever the JavaScript 'read' // function is called. This function loads the content of the file named in // the argument into a JavaScript string. diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 316387fa23..98e0a0ffa4 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -298,14 +298,8 @@ def ConfigureObjectFiles(): libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE') libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.']) - # Build dtoa. - dtoa_env = env.Copy() - dtoa_env.Replace(**context.flags['dtoa']) - dtoa_files = ['dtoa-config.c'] - dtoa_obj = context.ConfigureObject(dtoa_env, dtoa_files) - source_objs = context.ConfigureObject(env, source_files) - non_snapshot_files = [dtoa_obj, source_objs] + non_snapshot_files = [source_objs] # Create snapshot if necessary. For cross compilation you should either # do without snapshots and take the performance hit or you should build a diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 5912449169..b46cd02a3a 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -4669,9 +4669,11 @@ Handle<Value> HeapGraphEdge::GetName() const { case i::HeapGraphEdge::kContextVariable: case i::HeapGraphEdge::kInternal: case i::HeapGraphEdge::kProperty: + case i::HeapGraphEdge::kShortcut: return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( edge->name()))); case i::HeapGraphEdge::kElement: + case i::HeapGraphEdge::kHidden: return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt( edge->index()))); default: UNREACHABLE(); @@ -4761,15 +4763,9 @@ int HeapGraphNode::GetSelfSize() const { } -int HeapGraphNode::GetReachableSize() const { - IsDeadCheck("v8::HeapSnapshot::GetReachableSize"); - return ToInternal(this)->ReachableSize(); -} - - -int HeapGraphNode::GetRetainedSize() const { +int HeapGraphNode::GetRetainedSize(bool exact) const { IsDeadCheck("v8::HeapSnapshot::GetRetainedSize"); - return ToInternal(this)->RetainedSize(); + return ToInternal(this)->RetainedSize(exact); } @@ -4812,6 +4808,12 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const { } +const HeapGraphNode* HeapGraphNode::GetDominatorNode() const { + IsDeadCheck("v8::HeapSnapshot::GetDominatorNode"); + return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->dominator()); +} + + const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const { IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot"); i::HeapSnapshotsDiff* diff = diff --git a/deps/v8/src/apiutils.h b/deps/v8/src/apiutils.h index 1313ddaabe..9683aa43bb 100644 --- a/deps/v8/src/apiutils.h +++ b/deps/v8/src/apiutils.h @@ -58,6 +58,9 @@ class ImplementationUtilities { static v8::Arguments NewArguments(internal::Object** implicit_args, internal::Object** argv, int argc, bool is_construct_call) { + ASSERT(implicit_args[v8::Arguments::kCalleeIndex]->IsJSFunction()); + ASSERT(implicit_args[v8::Arguments::kHolderIndex]->IsHeapObject()); + return v8::Arguments(implicit_args, argv, argc, is_construct_call); } diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index b3b0766a8f..76a610b7be 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -100,8 +100,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Create a new closure through the slower runtime call. __ bind(&gc); - __ Push(cp, r3); - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ LoadRoot(r4, Heap::kFalseValueRootIndex); + __ Push(cp, r3, r4); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index dd0520feed..3e6743afaf 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -3103,10 +3103,13 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame_->EmitPush(Operand(function_info)); frame_->SpillAll(); @@ -3116,7 +3119,10 @@ void CodeGenerator::InstantiateFunction( // Create a new closure. frame_->EmitPush(cp); frame_->EmitPush(Operand(function_info)); - frame_->CallRuntime(Runtime::kNewClosure, 2); + frame_->EmitPush(Operand(pretenure + ? Factory::true_value() + : Factory::false_value())); + frame_->CallRuntime(Runtime::kNewClosure, 3); frame_->EmitPush(r0); } } @@ -3136,7 +3142,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { ASSERT(frame_->height() == original_height); return; } - InstantiateFunction(function_info); + InstantiateFunction(function_info, node->pretenure()); ASSERT_EQ(original_height + 1, frame_->height()); } @@ -3147,7 +3153,7 @@ void CodeGenerator::VisitSharedFunctionInfoLiteral( int original_height = frame_->height(); #endif Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - InstantiateFunction(node->shared_function_info()); + InstantiateFunction(node->shared_function_info(), false); ASSERT_EQ(original_height + 1, frame_->height()); } @@ -5816,6 +5822,15 @@ void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + Load(args->at(0)); + Register value = frame_->PopToRegister(); + __ LoadRoot(value, Heap::kUndefinedValueRootIndex); + frame_->EmitPush(value); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { #ifdef DEBUG int original_height = frame_->height(); diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index 2e8f46668c..6905d2331e 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -449,7 +449,8 @@ class CodeGenerator: public AstVisitor { void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - void InstantiateFunction(Handle<SharedFunctionInfo> function_info); + void InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for type checks. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -528,6 +529,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index c50f84ad5e..f04015bd78 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -860,18 +860,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ mov(r0, Operand(info)); __ push(r0); __ CallStub(&stub); } else { __ mov(r0, Operand(info)); - __ Push(cp, r0); - __ CallRuntime(Runtime::kNewClosure, 2); + __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex + : Heap::kFalseValueRootIndex); + __ Push(cp, r0, r1); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(r0); } @@ -2772,6 +2777,13 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); + context()->Plug(r0); + return; +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index a0ef80a0fd..f3f7a5d4b3 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -598,8 +598,8 @@ static void GenerateFastApiCall(MacroAssembler* masm, int argc) { // Get the function and setup the context. JSFunction* function = optimization.constant_function(); - __ mov(r7, Operand(Handle<JSFunction>(function))); - __ ldr(cp, FieldMemOperand(r7, JSFunction::kContextOffset)); + __ mov(r5, Operand(Handle<JSFunction>(function))); + __ ldr(cp, FieldMemOperand(r5, JSFunction::kContextOffset)); // Pass the additional arguments FastHandleApiCall expects. bool info_loaded = false; @@ -607,18 +607,18 @@ static void GenerateFastApiCall(MacroAssembler* masm, if (Heap::InNewSpace(callback)) { info_loaded = true; __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info())); - __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kCallbackOffset)); + __ ldr(r7, FieldMemOperand(r0, CallHandlerInfo::kCallbackOffset)); } else { - __ Move(r6, Handle<Object>(callback)); + __ Move(r7, Handle<Object>(callback)); } Object* call_data = optimization.api_call_info()->data(); if (Heap::InNewSpace(call_data)) { if (!info_loaded) { __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info())); } - __ ldr(r5, FieldMemOperand(r0, CallHandlerInfo::kDataOffset)); + __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kDataOffset)); } else { - __ Move(r5, Handle<Object>(call_data)); + __ Move(r6, Handle<Object>(call_data)); } __ add(sp, sp, Operand(1 * kPointerSize)); @@ -1082,10 +1082,9 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, // Push the arguments on the JS stack of the caller. __ push(receiver); // Receiver. - __ push(reg); // Holder. - __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback data - __ ldr(reg, FieldMemOperand(ip, AccessorInfo::kDataOffset)); - __ Push(ip, reg, name_reg); + __ mov(scratch3, Operand(Handle<AccessorInfo>(callback))); // callback data + __ ldr(ip, FieldMemOperand(scratch3, AccessorInfo::kDataOffset)); + __ Push(reg, ip, scratch3, name_reg); // Do tail-call to the runtime system. ExternalReference load_callback_property = @@ -1208,15 +1207,15 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // holder_reg is either receiver or scratch1. if (!receiver.is(holder_reg)) { ASSERT(scratch1.is(holder_reg)); - __ Push(receiver, holder_reg, scratch2); - __ ldr(scratch1, - FieldMemOperand(holder_reg, AccessorInfo::kDataOffset)); - __ Push(scratch1, name_reg); + __ Push(receiver, holder_reg); + __ ldr(scratch3, + FieldMemOperand(scratch2, AccessorInfo::kDataOffset)); + __ Push(scratch3, scratch2, name_reg); } else { __ push(receiver); - __ ldr(scratch1, - FieldMemOperand(holder_reg, AccessorInfo::kDataOffset)); - __ Push(holder_reg, scratch2, scratch1, name_reg); + __ ldr(scratch3, + FieldMemOperand(scratch2, AccessorInfo::kDataOffset)); + __ Push(holder_reg, scratch3, scratch2, name_reg); } ExternalReference ref = @@ -1360,10 +1359,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver // ----------------------------------- - // TODO(639): faster implementation. - // If object is not an array, bail out to regular call. if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); @@ -1371,20 +1371,133 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, GenerateNameCheck(name, &miss); + Register receiver = r1; + // Get the receiver from the stack const int argc = arguments().immediate(); - __ ldr(r1, MemOperand(sp, argc * kPointerSize)); + __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); // Check that the receiver isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &miss); + __ BranchOnSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss); + CheckPrototypes(JSObject::cast(object), receiver, + holder, r3, r0, r4, name, &miss); - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), - argc + 1, - 1); + if (argc == 0) { + // Nothing to do, just return the length. + __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ Drop(argc + 1); + __ Ret(); + } else { + Label call_builtin; + + Register elements = r3; + Register end_elements = r5; + + // Get the elements array of the object. + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); + + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, r0, + Heap::kFixedArrayMapRootIndex, &call_builtin, true); + + if (argc == 1) { // Otherwise fall through to call the builtin. + Label exit, with_write_barrier, attempt_to_grow_elements; + + // Get the array's length into r0 and calculate new length. + __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + STATIC_ASSERT(kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0); + __ add(r0, r0, Operand(Smi::FromInt(argc))); + + // Get the element's length. + __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); + + // Check if we could survive without allocation. + __ cmp(r0, r4); + __ b(gt, &attempt_to_grow_elements); + + // Save new length. + __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Push the element. + __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize)); + // We may need a register containing the address end_elements below, + // so write back the value in end_elements. + __ add(end_elements, elements, + Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + const int kEndElementsOffset = + FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; + __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex)); + + // Check for a smi. + __ BranchOnNotSmi(r4, &with_write_barrier); + __ bind(&exit); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&with_write_barrier); + __ InNewSpace(elements, r4, eq, &exit); + __ RecordWriteHelper(elements, end_elements, r4); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&attempt_to_grow_elements); + // r0: array's length + 1. + // r4: elements' length. + + if (!FLAG_inline_new) { + __ b(&call_builtin); + } + + ExternalReference new_space_allocation_top = + ExternalReference::new_space_allocation_top_address(); + ExternalReference new_space_allocation_limit = + ExternalReference::new_space_allocation_limit_address(); + + const int kAllocationDelta = 4; + // Load top and check if it is the end of elements. + __ add(end_elements, elements, + Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ add(end_elements, end_elements, Operand(kEndElementsOffset)); + __ mov(r7, Operand(new_space_allocation_top)); + __ ldr(r6, MemOperand(r7)); + __ cmp(end_elements, r6); + __ b(ne, &call_builtin); + + __ mov(r9, Operand(new_space_allocation_limit)); + __ ldr(r9, MemOperand(r9)); + __ add(r6, r6, Operand(kAllocationDelta * kPointerSize)); + __ cmp(r6, r9); + __ b(hi, &call_builtin); + + // We fit and could grow elements. + // Update new_space_allocation_top. + __ str(r6, MemOperand(r7)); + // Push the argument. + __ ldr(r6, MemOperand(sp, (argc - 1) * kPointerSize)); + __ str(r6, MemOperand(end_elements)); + // Fill the rest with holes. + __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); + for (int i = 1; i < kAllocationDelta; i++) { + __ str(r6, MemOperand(end_elements, i * kPointerSize)); + } + + // Update elements' and array's sizes. + __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta))); + __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); + + // Elements are in new space, so write barrier is not required. + __ Drop(argc + 1); + __ Ret(); + } + __ bind(&call_builtin); + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), + argc + 1, + 1); + } // Handle call cache miss. __ bind(&miss); @@ -1406,28 +1519,68 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver // ----------------------------------- - // TODO(642): faster implementation. - // If object is not an array, bail out to regular call. if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); - Label miss; + Label miss, return_undefined, call_builtin; + + Register receiver = r1; + Register elements = r3; GenerateNameCheck(name, &miss); // Get the receiver from the stack const int argc = arguments().immediate(); - __ ldr(r1, MemOperand(sp, argc * kPointerSize)); + __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); // Check that the receiver isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &miss); + __ BranchOnSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss); + CheckPrototypes(JSObject::cast(object), + receiver, holder, elements, r4, r0, name, &miss); + + // Get the elements array of the object. + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); + + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, r0, Heap::kFixedArrayMapRootIndex, &call_builtin, true); + + // Get the array's length into r4 and calculate new length. + __ ldr(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ sub(r4, r4, Operand(Smi::FromInt(1)), SetCC); + __ b(lt, &return_undefined); + + // Get the last element. + __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); + STATIC_ASSERT(kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0); + // We can't address the last element in one operation. Compute the more + // expensive shift first, and use an offset later on. + __ add(elements, elements, Operand(r4, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ ldr(r0, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag)); + __ cmp(r0, r6); + __ b(eq, &call_builtin); + + // Set the array's length. + __ str(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Fill with the hole. + __ str(r6, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag)); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&return_undefined); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); + __ Drop(argc + 1); + __ Ret(); + __ bind(&call_builtin); __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop), argc + 1, 1); @@ -2672,7 +2825,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { // -- r1 : receiver // ----------------------------------- Label miss; - __ IncrementCounter(&Counters::keyed_load_string_length, 1, r1, r3); + __ IncrementCounter(&Counters::keyed_load_string_length, 1, r2, r3); // Check the key is the cached one. __ cmp(r0, Operand(Handle<String>(name))); @@ -2680,7 +2833,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { GenerateLoadStringLength(masm(), r1, r2, r3, &miss); __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_string_length, 1, r1, r3); + __ DecrementCounter(&Counters::keyed_load_string_length, 1, r2, r3); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2688,13 +2841,23 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -// TODO(1224671): implement the fast case. MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key // -- r1 : receiver // ----------------------------------- + Label miss; + + __ IncrementCounter(&Counters::keyed_load_function_prototype, 1, r2, r3); + + // Check the name hasn't changed. + __ cmp(r0, Operand(Handle<String>(name))); + __ b(ne, &miss); + + GenerateLoadFunctionPrototype(masm(), r1, r2, r3, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_function_prototype, 1, r2, r3); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); return GetCode(CALLBACKS, name); diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index b2ebece9c1..5ecf5e303b 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -364,6 +364,10 @@ function ArrayJoin(separator) { } else if (!IS_STRING(separator)) { separator = ToString(separator); } + + var result = %_FastAsciiArrayJoin(this, separator); + if (typeof result != "undefined") return result; + var length = TO_UINT32(this.length); return Join(this, length, separator, ConvertToString); } diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index 04c2977570..0846dbc537 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -1416,7 +1416,8 @@ class FunctionLiteral: public Expression { contains_loops_(contains_loops), function_token_position_(RelocInfo::kNoPosition), inferred_name_(Heap::empty_string()), - try_full_codegen_(false) { + try_full_codegen_(false), + pretenure_(false) { #ifdef DEBUG already_compiled_ = false; #endif @@ -1459,6 +1460,9 @@ class FunctionLiteral: public Expression { bool try_full_codegen() { return try_full_codegen_; } void set_try_full_codegen(bool flag) { try_full_codegen_ = flag; } + bool pretenure() { return pretenure_; } + void set_pretenure(bool value) { pretenure_ = value; } + #ifdef DEBUG void mark_as_compiled() { ASSERT(!already_compiled_); @@ -1482,6 +1486,7 @@ class FunctionLiteral: public Expression { int function_token_position_; Handle<String> inferred_name_; bool try_full_codegen_; + bool pretenure_; #ifdef DEBUG bool already_compiled_; #endif diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index aede302035..e88ef6f0e1 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -1081,29 +1081,22 @@ BUILTIN(FastHandleApiCall) { ASSERT(!CalledAsConstructor()); const bool is_construct = false; - // We expect four more arguments: function, callback, call data, and holder. + // We expect four more arguments: callback, function, call data, and holder. const int args_length = args.length() - 4; ASSERT(args_length >= 0); - Handle<JSFunction> function = args.at<JSFunction>(args_length); - Object* callback_obj = args[args_length + 1]; - Handle<Object> data = args.at<Object>(args_length + 2); - Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3); - -#ifdef DEBUG - VerifyTypeCheck(checked_holder, function); -#endif - - CustomArguments custom; - v8::ImplementationUtilities::PrepareArgumentsData(custom.end(), - *data, *function, *checked_holder); + Object* callback_obj = args[args_length]; v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( - custom.end(), + &args[args_length + 1], &args[0] - 1, args_length - 1, is_construct); +#ifdef DEBUG + VerifyTypeCheck(Utils::OpenHandle(*new_args.Holder()), + Utils::OpenHandle(*new_args.Callee())); +#endif HandleScope scope; Object* result; v8::Handle<v8::Value> value; diff --git a/deps/v8/src/conversions.cc b/deps/v8/src/conversions.cc index c0dbf73ad0..19fa7773ab 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -39,16 +39,6 @@ namespace v8 { namespace internal { -int HexValue(uc32 c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; -} - namespace { // C++-style iterator adaptor for StringInputBuffer diff --git a/deps/v8/src/conversions.h b/deps/v8/src/conversions.h index 9e32a0cdb5..312e6aee54 100644 --- a/deps/v8/src/conversions.h +++ b/deps/v8/src/conversions.h @@ -75,11 +75,6 @@ static inline uint32_t DoubleToUint32(double x) { } -// Returns the value (0 .. 15) of a hexadecimal character c. -// If c is not a legal hexadecimal character, returns a value < 0. -int HexValue(uc32 c); - - // Enumeration for allowing octals and ignoring junk when converting // strings to numbers. enum ConversionFlags { diff --git a/deps/v8/src/dtoa-config.c b/deps/v8/src/dtoa-config.c deleted file mode 100644 index 9c5ee33194..0000000000 --- a/deps/v8/src/dtoa-config.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2007-2008 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. - */ - -/** - * Dtoa needs to have a particular environment set up for it so - * instead of using it directly you should use this file. - * - * The way it works is that when you link with it, its definitions - * of dtoa, strtod etc. override the default ones. So if you fail - * to link with this library everything will still work, it's just - * subtly wrong. - */ - -#if !(defined(__APPLE__) && defined(__MACH__)) && \ - !defined(WIN32) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && \ - !defined(__sun) -#include <endian.h> -#endif -#include <math.h> -#include <float.h> - -/* The floating point word order on ARM is big endian when floating point - * emulation is used, even if the byte order is little endian */ -#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(WIN32) && \ - !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sun) && \ - __FLOAT_WORD_ORDER == __BIG_ENDIAN -#define IEEE_MC68k -#else -#define IEEE_8087 -#endif - -#define __MATH_H__ -#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__) || \ - defined(__OpenBSD__) || defined(__sun) -/* stdlib.h on FreeBSD and Apple's 10.5 and later SDKs will mangle the - * name of strtod. If it's included after strtod is redefined as - * gay_strtod, it will mangle the name of gay_strtod, which is - * unwanted. */ -#include <stdlib.h> - -#endif -/* stdlib.h on Windows adds __declspec(dllimport) to all functions when using - * the DLL version of the CRT (compiling with /MD or /MDd). If stdlib.h is - * included after strtod is redefined as gay_strtod, it will add - * __declspec(dllimport) to gay_strtod, which causes the compilation of - * gay_strtod in dtoa.c to fail. -*/ -#if defined(WIN32) && defined(_DLL) -#include "stdlib.h" -#endif - -/* For MinGW, turn on __NO_ISOCEXT so that its strtod doesn't get added */ -#ifdef __MINGW32__ -#define __NO_ISOCEXT -#endif /* __MINGW32__ */ - -/* On 64-bit systems, we need to make sure that a Long is only 32 bits. */ -#ifdef V8_TARGET_ARCH_X64 -#define Long int -#endif /* V8_TARGET_ARCH_X64 */ - -/* Make sure we use the David M. Gay version of strtod(). On Linux, we - * cannot use the same name (maybe the function does not have weak - * linkage?). */ -#define strtod gay_strtod -#include "third_party/dtoa/dtoa.c" diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index 8592472524..a890f15974 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -882,12 +882,12 @@ void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Visit(stmt->body()); // Check stack before looping. + __ bind(loop_statement.continue_target()); __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); // Record the position of the do while condition and make sure it is // possible to break on the condition. - __ bind(loop_statement.continue_target()); SetExpressionPosition(stmt->cond(), stmt->condition_position()); VisitForControl(stmt->cond(), &body, @@ -1168,14 +1168,14 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { SetStackOverflow(); return; } - EmitNewClosure(function_info); + EmitNewClosure(function_info, expr->pretenure()); } void FullCodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* expr) { Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - EmitNewClosure(expr->shared_function_info()); + EmitNewClosure(expr->shared_function_info(), false); } diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 6a1def6ee1..97a56bd908 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -348,7 +348,7 @@ class FullCodeGenerator: public AstVisitor { // Platform-specific support for allocating a new closure based on // the given function info. - void EmitNewClosure(Handle<SharedFunctionInfo> info); + void EmitNewClosure(Handle<SharedFunctionInfo> info, bool pretenure); // Platform-specific support for compiling assignments. diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 1364951b71..3dfb886e99 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -37,6 +37,7 @@ #include "global-handles.h" #include "natives.h" #include "runtime.h" +#include "string-search.h" #include "stub-cache.h" namespace v8 { @@ -508,43 +509,50 @@ void InitScriptLineEnds(Handle<Script> script) { } -Handle<FixedArray> CalculateLineEnds(Handle<String> src, - bool with_imaginary_last_new_line) { - const int src_len = src->length(); - Handle<String> new_line = Factory::NewStringFromAscii(CStrVector("\n")); +template <typename SourceChar> +static void CalculateLineEnds(List<int>* line_ends, + Vector<const SourceChar> src, + bool with_last_line) { + const int src_len = src.length(); + StringSearch<char, SourceChar> search(CStrVector("\n")); - // Pass 1: Identify line count. - int line_count = 0; + // Find and record line ends. int position = 0; while (position != -1 && position < src_len) { - position = Runtime::StringMatch(src, new_line, position); + position = search.Search(src, position); if (position != -1) { + line_ends->Add(position); position++; - } - if (position != -1) { - line_count++; - } else if (with_imaginary_last_new_line) { + } else if (with_last_line) { // Even if the last line misses a line end, it is counted. - line_count++; + line_ends->Add(src_len); + return; } } +} - // Pass 2: Fill in line ends positions - Handle<FixedArray> array = Factory::NewFixedArray(line_count); - int array_index = 0; - position = 0; - while (position != -1 && position < src_len) { - position = Runtime::StringMatch(src, new_line, position); - if (position != -1) { - array->set(array_index++, Smi::FromInt(position++)); - } else if (with_imaginary_last_new_line) { - // If the script does not end with a line ending add the final end - // position as just past the last line ending. - array->set(array_index++, Smi::FromInt(src_len)); + +Handle<FixedArray> CalculateLineEnds(Handle<String> src, + bool with_last_line) { + src = FlattenGetString(src); + // Rough estimate of line count based on a roughly estimated average + // length of (unpacked) code. + int line_count_estimate = src->length() >> 4; + List<int> line_ends(line_count_estimate); + { + AssertNoAllocation no_heap_allocation; // ensure vectors stay valid. + // Dispatch on type of strings. + if (src->IsAsciiRepresentation()) { + CalculateLineEnds(&line_ends, src->ToAsciiVector(), with_last_line); + } else { + CalculateLineEnds(&line_ends, src->ToUC16Vector(), with_last_line); } } - ASSERT(array_index == line_count); - + int line_count = line_ends.length(); + Handle<FixedArray> array = Factory::NewFixedArray(line_count); + for (int i = 0; i < line_count; i++) { + array->set(i, Smi::FromInt(line_ends[i])); + } return array; } @@ -556,11 +564,11 @@ int GetScriptLineNumber(Handle<Script> script, int code_pos) { FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); const int line_ends_len = line_ends_array->length(); - if (!line_ends_len) - return -1; + if (!line_ends_len) return -1; - if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) + if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { return script->line_offset()->value(); + } int left = 0; int right = line_ends_len; diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index d3cc4ab910..3fb1ec1122 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -927,10 +927,16 @@ class AllocatingRetainersIterator { void Call(const JSObjectsCluster& cluster, const NumberAndSizeInfo& number_and_size) { int child_index, retainer_index; - map_->CountReference(ClusterAsHeapObject(cluster), child_, - &child_index, &retainer_index); - map_->Map(ClusterAsHeapObject(cluster))->SetElementReference( - child_index, number_and_size.number(), child_entry_, retainer_index); + map_->CountReference(ClusterAsHeapObject(cluster), + child_, + &child_index, + &retainer_index); + map_->Map(ClusterAsHeapObject(cluster))->SetIndexedReference( + HeapGraphEdge::kElement, + child_index, + number_and_size.number(), + child_entry_, + retainer_index); } private: @@ -1042,7 +1048,7 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { if (agg_snapshot_->info()[i].bytes() > 0) { AddEntryFromAggregatedSnapshot(snapshot, &root_child_index, - HeapEntry::kInternal, + HeapEntry::kHidden, agg_snapshot_->info()[i].name(), agg_snapshot_->info()[i].number(), agg_snapshot_->info()[i].bytes(), diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index 6ac89bfbac..5975ad27db 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -80,8 +80,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ pop(edx); __ push(esi); __ push(edx); + __ push(Immediate(Factory::false_value())); __ push(ecx); // Restore return address. - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index fb7a138d5c..f5ab357ff1 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -4897,7 +4897,8 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { Result CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // The inevitable call will sync frame elements to memory anyway, so // we do it eagerly to allow us to push the arguments directly into // place. @@ -4905,7 +4906,9 @@ Result CodeGenerator::InstantiateFunction( // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame()->EmitPush(Immediate(function_info)); return frame()->CallStub(&stub, 1); @@ -4914,7 +4917,10 @@ Result CodeGenerator::InstantiateFunction( // shared function info. frame()->EmitPush(esi); frame()->EmitPush(Immediate(function_info)); - return frame()->CallRuntime(Runtime::kNewClosure, 2); + frame()->EmitPush(Immediate(pretenure + ? Factory::true_value() + : Factory::false_value())); + return frame()->CallRuntime(Runtime::kNewClosure, 3); } } @@ -4930,7 +4936,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { SetStackOverflow(); return; } - Result result = InstantiateFunction(function_info); + Result result = InstantiateFunction(function_info, node->pretenure()); frame()->Push(&result); } @@ -4939,7 +4945,7 @@ void CodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* node) { ASSERT(!in_safe_int32_mode()); Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - Result result = InstantiateFunction(node->shared_function_info()); + Result result = InstantiateFunction(node->shared_function_info(), false); frame()->Push(&result); } @@ -6642,6 +6648,190 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + Load(args->at(1)); + Load(args->at(0)); + Result array_result = frame_->Pop(); + array_result.ToRegister(eax); + frame_->SpillAll(); + + Label bailout; + Label done; + // All aliases of the same register have disjoint lifetimes. + Register array = eax; + Register result_pos = no_reg; + + Register index = edi; + + Register current_string_length = ecx; // Will be ecx when live. + + Register current_string = edx; + + Register scratch = ebx; + + Register scratch_2 = esi; + Register new_padding_chars = scratch_2; + + Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed. + Operand elements = Operand(esp, 3 * kPointerSize); + Operand result = Operand(esp, 2 * kPointerSize); + Operand padding_chars = Operand(esp, 1 * kPointerSize); + Operand array_length = Operand(esp, 0); + __ sub(Operand(esp), Immediate(4 * kPointerSize)); + + // Check that eax is a JSArray + __ test(array, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); + __ j(not_equal, &bailout); + + // Check that the array has fast elements. + __ test_b(FieldOperand(scratch, Map::kBitField2Offset), + 1 << Map::kHasFastElements); + __ j(zero, &bailout); + + // If the array is empty, return the empty string. + __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset)); + __ sar(scratch, 1); + Label non_trivial; + __ j(not_zero, &non_trivial); + __ mov(result, Factory::empty_string()); + __ jmp(&done); + + __ bind(&non_trivial); + __ mov(array_length, scratch); + + __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset)); + __ mov(elements, scratch); + + // End of array's live range. + result_pos = array; + array = no_reg; + + + // Check that the separator is a flat ascii string. + __ mov(current_string, separator); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + // If the separator is the empty string, replace it with NULL. + // The test for NULL is quicker than the empty string test, in a loop. + __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), + Immediate(0)); + Label separator_checked; + __ j(not_zero, &separator_checked); + __ mov(separator, Immediate(0)); + __ bind(&separator_checked); + + // Check that elements[0] is a flat ascii string, and copy it in new space. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize)); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Allocate space to copy it. Round up the size to the alignment granularity. + __ mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + __ shr(current_string_length, 1); + + // Live registers and stack values: + // current_string_length: length of elements[0]. + + // New string result in new space = elements[0] + __ AllocateAsciiString(result_pos, current_string_length, scratch_2, + index, no_reg, &bailout); + __ mov(result, result_pos); + + // Adjust current_string_length to include padding bytes at end of string. + // Keep track of the number of padding bytes. + __ mov(new_padding_chars, current_string_length); + __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + __ sub(new_padding_chars, Operand(current_string_length)); + __ neg(new_padding_chars); + __ mov(padding_chars, new_padding_chars); + + Label copy_loop_1_done; + Label copy_loop_1; + __ test(current_string_length, Operand(current_string_length)); + __ j(zero, ©_loop_1_done); + __ bind(©_loop_1); + __ sub(Operand(current_string_length), Immediate(kPointerSize)); + __ mov(scratch, FieldOperand(current_string, current_string_length, + times_1, SeqAsciiString::kHeaderSize)); + __ mov(FieldOperand(result_pos, current_string_length, + times_1, SeqAsciiString::kHeaderSize), + scratch); + __ j(not_zero, ©_loop_1); + __ bind(©_loop_1_done); + + __ mov(index, Immediate(1)); + // Loop condition: while (index < length). + Label loop; + __ bind(&loop); + __ cmp(index, array_length); + __ j(greater_equal, &done); + + // If the separator is the empty string, signalled by NULL, skip it. + Label separator_done; + __ mov(current_string, separator); + __ test(current_string, Operand(current_string)); + __ j(zero, &separator_done); + + // Append separator to result. It is known to be a flat ascii string. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ bind(&separator_done); + + // Add next element of array to the end of the result. + // Get current_string = array[index]. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, index, + times_pointer_size, + FixedArray::kHeaderSize)); + // If current != flat ascii string drop result, return undefined. + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Append current to the result. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ add(Operand(index), Immediate(1)); + __ jmp(&loop); // End while (index < length). + + __ bind(&bailout); + __ mov(result, Factory::undefined_value()); + __ bind(&done); + __ mov(eax, result); + // Drop temp values from the stack, and restore context register. + __ add(Operand(esp), Immediate(4 * kPointerSize)); + + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + frame_->Drop(1); + frame_->Push(&array_result); +} + + void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { ASSERT(args->length() == 1); Load(args->at(0)); diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index a945e217ab..d1a2036cb7 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -625,7 +625,8 @@ class CodeGenerator: public AstVisitor { void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - Result InstantiateFunction(Handle<SharedFunctionInfo> function_info); + Result InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for types. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -710,6 +711,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index ad32dc85be..3adc48a739 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -880,17 +880,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ push(Immediate(info)); __ CallStub(&stub); } else { __ push(esi); __ push(Immediate(info)); - __ CallRuntime(Runtime::kNewClosure, 2); + __ push(Immediate(pretenure + ? Factory::true_value() + : Factory::false_value())); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(eax); } @@ -3084,6 +3090,190 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + Label bailout; + Label done; + + ASSERT(args->length() == 2); + // We will leave the separator on the stack until the end of the function. + VisitForStackValue(args->at(1)); + // Load this to eax (= array) + VisitForAccumulatorValue(args->at(0)); + + // All aliases of the same register have disjoint lifetimes. + Register array = eax; + Register result_pos = no_reg; + + Register index = edi; + + Register current_string_length = ecx; // Will be ecx when live. + + Register current_string = edx; + + Register scratch = ebx; + + Register scratch_2 = esi; + Register new_padding_chars = scratch_2; + + Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed. + Operand elements = Operand(esp, 3 * kPointerSize); + Operand result = Operand(esp, 2 * kPointerSize); + Operand padding_chars = Operand(esp, 1 * kPointerSize); + Operand array_length = Operand(esp, 0); + __ sub(Operand(esp), Immediate(4 * kPointerSize)); + + + // Check that eax is a JSArray + __ test(array, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); + __ j(not_equal, &bailout); + + // Check that the array has fast elements. + __ test_b(FieldOperand(scratch, Map::kBitField2Offset), + 1 << Map::kHasFastElements); + __ j(zero, &bailout); + + // If the array is empty, return the empty string. + __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset)); + __ sar(scratch, 1); + Label non_trivial; + __ j(not_zero, &non_trivial); + __ mov(result, Factory::empty_string()); + __ jmp(&done); + + __ bind(&non_trivial); + __ mov(array_length, scratch); + + __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset)); + __ mov(elements, scratch); + + // End of array's live range. + result_pos = array; + array = no_reg; + + + // Check that the separator is a flat ascii string. + __ mov(current_string, separator); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + // If the separator is the empty string, replace it with NULL. + // The test for NULL is quicker than the empty string test, in a loop. + __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), + Immediate(0)); + Label separator_checked; + __ j(not_zero, &separator_checked); + __ mov(separator, Immediate(0)); + __ bind(&separator_checked); + + // Check that elements[0] is a flat ascii string, and copy it in new space. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize)); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Allocate space to copy it. Round up the size to the alignment granularity. + __ mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + __ shr(current_string_length, 1); + + // Live registers and stack values: + // current_string_length: length of elements[0]. + + // New string result in new space = elements[0] + __ AllocateAsciiString(result_pos, current_string_length, scratch_2, + index, no_reg, &bailout); + __ mov(result, result_pos); + + // Adjust current_string_length to include padding bytes at end of string. + // Keep track of the number of padding bytes. + __ mov(new_padding_chars, current_string_length); + __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + __ sub(new_padding_chars, Operand(current_string_length)); + __ neg(new_padding_chars); + __ mov(padding_chars, new_padding_chars); + + Label copy_loop_1_done; + Label copy_loop_1; + __ test(current_string_length, Operand(current_string_length)); + __ j(zero, ©_loop_1_done); + __ bind(©_loop_1); + __ sub(Operand(current_string_length), Immediate(kPointerSize)); + __ mov(scratch, FieldOperand(current_string, current_string_length, + times_1, SeqAsciiString::kHeaderSize)); + __ mov(FieldOperand(result_pos, current_string_length, + times_1, SeqAsciiString::kHeaderSize), + scratch); + __ j(not_zero, ©_loop_1); + __ bind(©_loop_1_done); + + __ mov(index, Immediate(1)); + // Loop condition: while (index < length). + Label loop; + __ bind(&loop); + __ cmp(index, array_length); + __ j(greater_equal, &done); + + // If the separator is the empty string, signalled by NULL, skip it. + Label separator_done; + __ mov(current_string, separator); + __ test(current_string, Operand(current_string)); + __ j(zero, &separator_done); + + // Append separator to result. It is known to be a flat ascii string. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ bind(&separator_done); + + // Add next element of array to the end of the result. + // Get current_string = array[index]. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, index, + times_pointer_size, + FixedArray::kHeaderSize)); + // If current != flat ascii string drop result, return undefined. + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Append current to the result. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ add(Operand(index), Immediate(1)); + __ jmp(&loop); // End while (index < length). + + __ bind(&bailout); + __ mov(result, Factory::undefined_value()); + __ bind(&done); + __ mov(eax, result); + // Drop temp values from the stack, and restore context register. + __ add(Operand(esp), Immediate(5 * kPointerSize)); + + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + context()->Plug(eax); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index db26c10425..61aadf7ebd 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -889,6 +889,57 @@ void MacroAssembler::AllocateAsciiConsString(Register result, Immediate(Factory::cons_ascii_string_map())); } +// All registers must be distinct. Only current_string needs valid contents +// on entry. All registers may be invalid on exit. result_operand is +// unchanged, padding_chars is updated correctly. +void MacroAssembler::AppendStringToTopOfNewSpace( + Register current_string, // Tagged pointer to string to copy. + Register current_string_length, + Register result_pos, + Register scratch, + Register new_padding_chars, + Operand operand_result, + Operand operand_padding_chars, + Label* bailout) { + mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + shr(current_string_length, 1); + sub(current_string_length, operand_padding_chars); + mov(new_padding_chars, current_string_length); + add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + sub(new_padding_chars, Operand(current_string_length)); + neg(new_padding_chars); + // We need an allocation even if current_string_length is 0, to fetch + // result_pos. Consider using a faster fetch of result_pos in that case. + AllocateInNewSpace(current_string_length, result_pos, scratch, no_reg, + bailout, NO_ALLOCATION_FLAGS); + sub(result_pos, operand_padding_chars); + mov(operand_padding_chars, new_padding_chars); + + Register scratch_2 = new_padding_chars; // Used to compute total length. + // Copy string to the end of result. + mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + mov(scratch, operand_result); + mov(scratch_2, current_string_length); + add(scratch_2, FieldOperand(scratch, String::kLengthOffset)); + mov(FieldOperand(scratch, String::kLengthOffset), scratch_2); + shr(current_string_length, 1); + lea(current_string, + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); + // Loop condition: while (--current_string_length >= 0). + Label copy_loop; + Label copy_loop_entry; + jmp(©_loop_entry); + bind(©_loop); + mov_b(scratch, Operand(current_string, current_string_length, times_1, 0)); + mov_b(Operand(result_pos, current_string_length, times_1, 0), scratch); + bind(©_loop_entry); + sub(Operand(current_string_length), Immediate(1)); + j(greater_equal, ©_loop); +} + void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, Register result, diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 0a6e0ee35b..cea7a70183 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -379,6 +379,23 @@ class MacroAssembler: public Assembler { Register scratch2, Label* gc_required); + // All registers must be distinct. Only current_string needs valid contents + // on entry. All registers may be invalid on exit. result_operand is + // unchanged, padding_chars is updated correctly. + // The top of new space must contain a sequential ascii string with + // padding_chars bytes free in its top word. The sequential ascii string + // current_string is concatenated to it, allocating the necessary amount + // of new memory. + void AppendStringToTopOfNewSpace( + Register current_string, // Tagged pointer to string to copy. + Register current_string_length, + Register result_pos, + Register scratch, + Register new_padding_chars, + Operand operand_result, + Operand operand_padding_chars, + Label* bailout); + // --------------------------------------------------------------------------- // Support functions. diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 4184d4cde3..3120ff9da7 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -417,7 +417,7 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, static const int kFastApiCallArguments = 3; -// Reserves space for the extra arguments to FastHandleApiCall in the +// Reserves space for the extra arguments to API function in the // caller's frame. // // These arguments are set by CheckPrototypes and GenerateFastApiCall. @@ -450,7 +450,7 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { } -// Generates call to FastHandleApiCall builtin. +// Generates call to API function. static bool GenerateFastApiCall(MacroAssembler* masm, const CallOptimization& optimization, int argc, @@ -473,7 +473,7 @@ static bool GenerateFastApiCall(MacroAssembler* masm, __ mov(edi, Immediate(Handle<JSFunction>(function))); __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - // Pass the additional arguments FastHandleApiCall expects. + // Pass the additional arguments. __ mov(Operand(esp, 2 * kPointerSize), edi); Object* call_data = optimization.api_call_info()->data(); Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); @@ -1141,9 +1141,8 @@ void StubCompiler::GenerateLoadConstant(JSObject* object, __ j(zero, miss, not_taken); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, scratch3, name, miss); // Return the constant value. __ mov(eax, Handle<Object>(value)); @@ -1263,8 +1262,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ push(receiver); __ push(holder_reg); __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback))); - __ push(holder_reg); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); + __ push(holder_reg); __ push(name_reg); __ push(scratch2); // restore return address @@ -2318,7 +2317,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, &miss, &failure); if (!success) { - return false; + return failure; } // Restore receiver. diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index a9ba0e7169..7e4a51e2fa 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -37,7 +37,6 @@ #include "parser.h" #include "platform.h" #include "preparser.h" -#include "prescanner.h" #include "runtime.h" #include "scopeinfo.h" #include "string-stream.h" @@ -728,7 +727,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, // Initialize parser state. source->TryFlatten(); - scanner_.Initialize(source, JAVASCRIPT); + scanner_.Initialize(source); ASSERT(target_stack_ == NULL); if (pre_data_ != NULL) pre_data_->Initialize(); @@ -791,8 +790,7 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) { // Initialize parser state. source->TryFlatten(); - scanner_.Initialize(source, info->start_position(), info->end_position(), - JAVASCRIPT); + scanner_.Initialize(source, info->start_position(), info->end_position()); ASSERT(target_stack_ == NULL); mode_ = PARSE_EAGERLY; @@ -2278,6 +2276,12 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { temp_scope_->AddProperty(); } + // If we assign a function literal to a property we pretenure the + // literal so it can be added as a constant function property. + if (property != NULL && right->AsFunctionLiteral() != NULL) { + right->AsFunctionLiteral()->set_pretenure(true); + } + if (fni_ != NULL) { // Check if the right hand side is a call to avoid inferring a // name if we're dealing with "a = function(){...}();"-like @@ -3613,7 +3617,7 @@ Expression* Parser::NewThrowError(Handle<String> constructor, Handle<Object> JsonParser::ParseJson(Handle<String> source) { source->TryFlatten(); - scanner_.Initialize(source, JSON); + scanner_.Initialize(source); Handle<Object> result = ParseJsonValue(); if (result.is_null() || scanner_.Next() != Token::EOS) { if (scanner_.stack_overflow()) { @@ -4638,13 +4642,15 @@ int ScriptDataImpl::ReadNumber(byte** source) { } -static ScriptDataImpl* DoPreParse(UTF16Buffer* stream, +// Create a Scanner for the preparser to use as input, and preparse the source. +static ScriptDataImpl* DoPreParse(Handle<String> source, + unibrow::CharacterStream* stream, bool allow_lazy, - PartialParserRecorder* recorder) { - typedef preparser::Scanner<UTF16Buffer, UTF8Buffer> PreScanner; - PreScanner scanner; - scanner.Initialize(stream); - preparser::PreParser<PreScanner, PartialParserRecorder> preparser; + PartialParserRecorder* recorder, + int literal_flags) { + V8JavaScriptScanner scanner; + scanner.Initialize(source, stream, literal_flags); + preparser::PreParser<JavaScriptScanner, PartialParserRecorder> preparser; if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) { Top::StackOverflow(); return NULL; @@ -4657,44 +4663,11 @@ static ScriptDataImpl* DoPreParse(UTF16Buffer* stream, } -// Create an UTF16Buffer for the preparser to use as input, -// and preparse the source. -static ScriptDataImpl* DoPreParse(Handle<String> source, - unibrow::CharacterStream* stream, - bool allow_lazy, - PartialParserRecorder* recorder) { - if (source.is_null()) { - CharacterStreamUTF16Buffer buffer; - int length = stream->Length(); - buffer.Initialize(source, stream, 0, length); - return DoPreParse(&buffer, allow_lazy, recorder); - } else if (source->IsExternalAsciiString()) { - ExternalStringUTF16Buffer<ExternalAsciiString, char> buffer; - int length = source->length(); - buffer.Initialize(Handle<ExternalAsciiString>::cast(source), 0, length); - return DoPreParse(&buffer, allow_lazy, recorder); - } else if (source->IsExternalTwoByteString()) { - ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> buffer; - int length = source->length(); - buffer.Initialize(Handle<ExternalTwoByteString>::cast(source), 0, length); - return DoPreParse(&buffer, allow_lazy, recorder); - } else { - CharacterStreamUTF16Buffer buffer; - SafeStringInputBuffer input; - input.Reset(0, source.location()); - int length = source->length(); - buffer.Initialize(source, &input, 0, length); - return DoPreParse(&buffer, allow_lazy, recorder); - } -} - - // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, unibrow::CharacterStream* stream, v8::Extension* extension) { - Handle<Script> no_script; bool allow_lazy = FLAG_lazy && (extension == NULL); if (!allow_lazy) { // Partial preparsing is only about lazily compiled functions. @@ -4703,7 +4676,8 @@ ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, } PartialParserRecorder recorder; - return DoPreParse(source, stream, allow_lazy, &recorder); + return DoPreParse(source, stream, allow_lazy, &recorder, + JavaScriptScanner::kNoLiterals); } @@ -4713,7 +4687,10 @@ ScriptDataImpl* ParserApi::PreParse(Handle<String> source, Handle<Script> no_script; bool allow_lazy = FLAG_lazy && (extension == NULL); CompleteParserRecorder recorder; - return DoPreParse(source, stream, allow_lazy, &recorder); + int kPreParseLiteralsFlags = + JavaScriptScanner::kLiteralString | JavaScriptScanner::kLiteralIdentifier; + return DoPreParse(source, stream, allow_lazy, + &recorder, kPreParseLiteralsFlags); } diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 64f621c9e2..9a84ab9071 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -181,6 +181,7 @@ class ScriptDataImpl : public ScriptData { class PartialParserRecorder { public: PartialParserRecorder(); + virtual ~PartialParserRecorder() {} void LogFunction(int start, int end, int literals, int properties) { function_store_.Add(start); @@ -189,7 +190,7 @@ class PartialParserRecorder { function_store_.Add(properties); } - void LogSymbol(int start, const char* symbol, int length) { } + virtual void LogSymbol(int start, const char* symbol, int length) { } // Logs an error message and marks the log as containing an error. // Further logging will be ignored, and ExtractData will return a vector @@ -212,7 +213,7 @@ class PartialParserRecorder { const char* message, Vector<const char*> args); - Vector<unsigned> ExtractData(); + virtual Vector<unsigned> ExtractData(); void PauseRecording() { pause_count_++; @@ -253,14 +254,15 @@ class PartialParserRecorder { class CompleteParserRecorder: public PartialParserRecorder { public: CompleteParserRecorder(); + virtual ~CompleteParserRecorder() { } void LogSymbol(int start, Vector<const char> literal); - void LogSymbol(int start, const char* symbol, int length) { + virtual void LogSymbol(int start, const char* symbol, int length) { LogSymbol(start, Vector<const char>(symbol, length)); } - Vector<unsigned> ExtractData(); + virtual Vector<unsigned> ExtractData(); int symbol_position() { return symbol_store_.size(); } int symbol_ids() { return symbol_id_; } @@ -682,7 +684,7 @@ class Parser { Expression* ParseV8Intrinsic(bool* ok); INLINE(Token::Value peek()) { return scanner_.peek(); } - INLINE(Token::Value Next()) { return scanner_.Next(); } + INLINE(Token::Value Next()) { return scanner_.NextCheckStack(); } INLINE(void Consume(Token::Value token)); void Expect(Token::Value token, bool* ok); bool Check(Token::Value token); @@ -760,7 +762,7 @@ class Parser { ZoneList<Handle<String> > symbol_cache_; Handle<Script> script_; - Scanner scanner_; + V8JavaScriptScanner scanner_; Scope* top_scope_; int with_nesting_level_; @@ -852,7 +854,7 @@ class JsonParser BASE_EMBEDDED { // Converts the currently parsed literal to a JavaScript String. Handle<String> GetString(); - Scanner scanner_; + JsonScanner scanner_; }; } } // namespace v8::internal diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h index 547e766752..8e8dfbe052 100644 --- a/deps/v8/src/preparser.h +++ b/deps/v8/src/preparser.h @@ -720,7 +720,7 @@ Statement PreParser<Scanner, Log>::ParseThrowStatement(bool* ok) { ReportMessageAt(pos.beg_pos, pos.end_pos, "newline_after_throw", NULL); *ok = false; - return NULL; + return kUnknownStatement; } ParseExpression(true, CHECK_OK); ExpectSemicolon(CHECK_OK); @@ -1006,7 +1006,7 @@ Expression PreParser<Scanner, Log>::ParseMemberWithNewPrefixesExpression( // ('[' Expression ']' | '.' Identifier | Arguments)* // Parse the initial primary or function expression. - Expression result = NULL; + Expression result = kUnknownExpression; if (peek() == i::Token::FUNCTION) { Consume(i::Token::FUNCTION); if (peek() == i::Token::IDENTIFIER) { diff --git a/deps/v8/src/prescanner.h b/deps/v8/src/prescanner.h deleted file mode 100644 index 7fb8aa5b1d..0000000000 --- a/deps/v8/src/prescanner.h +++ /dev/null @@ -1,1098 +0,0 @@ -// Copyright 2010 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_PRESCANNER_H_ -#define V8_PRESCANNER_H_ - -#include "token.h" -#include "char-predicates-inl.h" -#include "utils.h" -#include "scanner-base.h" - -namespace v8 { -namespace preparser { - -namespace i = v8::internal; - -typedef int uc32; - -int HexValue(uc32 c) { - int res = c | 0x20; // Uppercase letters. - int is_digit = (c & 0x10) >> 4; // 0 if non-digit, 1 if digit. - // What to add to digits to make them consecutive with 'a'-'f' letters. - int kDelta = 'a' - '9' - 1; - // What to subtract to digits and letters to get them back to the range 0..15. - int kStart = '0' + kDelta; - res -= kStart; - res += kDelta * is_digit; - return res; -} - - -class PreScannerStackGuard { - public: - explicit PreScannerStackGuard(int max_size) - : limit_(StackPoint().at() - max_size) { } - bool has_overflowed() { - return StackPoint().at() < limit_; - } - private: - class StackPoint { - public: - char* at() { return reinterpret_cast<char*>(this); } - }; - char* limit_; -}; - - -// Scanner for preparsing. -// InputStream is a source of UC16 characters with limited push-back. -// LiteralsBuffer is a collector of (UTF-8) characters used to capture literals. -template <typename InputStream, typename LiteralsBuffer> -class Scanner { - public: - enum LiteralType { - kLiteralNumber, - kLiteralIdentifier, - kLiteralString, - kLiteralRegExp, - kLiteralRegExpFlags - }; - - class LiteralScope { - public: - explicit LiteralScope(Scanner* self, LiteralType type); - ~LiteralScope(); - void Complete(); - - private: - Scanner* scanner_; - bool complete_; - }; - - Scanner(); - - void Initialize(InputStream* stream); - - // Returns the next token. - i::Token::Value Next(); - - // Returns the current token again. - i::Token::Value current_token() { return current_.token; } - - // One token look-ahead (past the token returned by Next()). - i::Token::Value peek() const { return next_.token; } - - // Returns true if there was a line terminator before the peek'ed token. - bool has_line_terminator_before_next() const { - return has_line_terminator_before_next_; - } - - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - int beg_pos; - int end_pos; - }; - - // Returns the location information for the current token - // (the token returned by Next()). - Location location() const { return current_.location; } - // Returns the location information for the look-ahead token - // (the token returned by peek()). - Location peek_location() const { return next_.location; } - - // Returns the literal string, if any, for the current token (the - // token returned by Next()). The string is 0-terminated and in - // UTF-8 format; they may contain 0-characters. Literal strings are - // collected for identifiers, strings, and numbers. - // These functions only give the correct result if the literal - // was scanned between calls to StartLiteral() and TerminateLiteral(). - const char* literal_string() const { - return current_.literal_chars; - } - - int literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return current_.literal_length - 1; - } - - i::Vector<const char> literal() const { - return i::Vector<const char>(literal_string(), literal_length()); - } - - // Returns the literal string for the next token (the token that - // would be returned if Next() were called). - const char* next_literal_string() const { - return next_.literal_chars; - } - - - // Returns the length of the next token (that would be returned if - // Next() were called). - int next_literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return next_.literal_length - 1; - } - - i::Vector<const char> next_literal() const { - return i::Vector<const char>(next_literal_string(), next_literal_length()); - } - - // Scans the input as a regular expression pattern, previous - // character(s) must be /(=). Returns true if a pattern is scanned. - bool ScanRegExpPattern(bool seen_equal); - // Returns true if regexp flags are scanned (always since flags can - // be empty). - bool ScanRegExpFlags(); - - // Seek forward to the given position. This operation does not - // work in general, for instance when there are pushed back - // characters, but works for seeking forward until simple delimiter - // tokens, which is what it is used for. - void SeekForward(int pos); - - bool stack_overflow() { return stack_overflow_; } - - static const int kCharacterLookaheadBufferSize = 1; - static const int kNoEndPosition = 1; - - private: - // The current and look-ahead token. - struct TokenDesc { - i::Token::Value token; - Location location; - const char* literal_chars; - int literal_length; - }; - - // Default stack limit is 128K pointers. - static const int kMaxStackSize = 128 * 1024 * sizeof(void*); // NOLINT. - - void Init(unibrow::CharacterStream* stream); - - // Literal buffer support - inline void StartLiteral(LiteralType type); - inline void AddLiteralChar(uc32 ch); - inline void AddLiteralCharAdvance(); - inline void TerminateLiteral(); - // Stops scanning of a literal, e.g., due to an encountered error. - inline void DropLiteral(); - - // Low-level scanning support. - void Advance() { c0_ = source_->Advance(); } - void PushBack(uc32 ch) { - source_->PushBack(ch); - c0_ = ch; - } - - bool SkipWhiteSpace(); - - i::Token::Value SkipSingleLineComment(); - i::Token::Value SkipMultiLineComment(); - - inline i::Token::Value Select(i::Token::Value tok); - inline i::Token::Value Select(uc32 next, - i::Token::Value then, - i::Token::Value else_); - - // Scans a single JavaScript token. - void Scan(); - - void ScanDecimalDigits(); - i::Token::Value ScanNumber(bool seen_period); - i::Token::Value ScanIdentifier(); - uc32 ScanHexEscape(uc32 c, int length); - uc32 ScanOctalEscape(uc32 c, int length); - void ScanEscape(); - i::Token::Value ScanString(); - - // Scans a possible HTML comment -- begins with '<!'. - i::Token::Value ScanHtmlComment(); - - // Return the current source position. - int source_pos() { - return source_->pos() - kCharacterLookaheadBufferSize; - } - - // Decodes a unicode escape-sequence which is part of an identifier. - // If the escape sequence cannot be decoded the result is kBadRune. - uc32 ScanIdentifierUnicodeEscape(); - - PreScannerStackGuard stack_guard_; - - TokenDesc current_; // desc for current token (as returned by Next()) - TokenDesc next_; // desc for next token (one token look-ahead) - bool has_line_terminator_before_next_; - - // Source. - InputStream* source_; - - // Buffer to hold literal values (identifiers, strings, numerals, regexps and - // regexp flags) using '\x00'-terminated UTF-8 encoding. - // Handles allocation internally. - // Notice that the '\x00' termination is meaningless for strings and regexps - // which may contain the zero-character, but can be used as terminator for - // identifiers, numerals and regexp flags. - LiteralsBuffer literal_buffer_; - - bool stack_overflow_; - - // One Unicode character look-ahead; c0_ < 0 at the end of the input. - uc32 c0_; -}; - - -// ---------------------------------------------------------------------------- -// Scanner::LiteralScope - -template <typename InputStream, typename LiteralsBuffer> -Scanner<InputStream, LiteralsBuffer>::LiteralScope::LiteralScope( - Scanner* self, LiteralType type) - : scanner_(self), complete_(false) { - self->StartLiteral(type); -} - - -template <typename InputStream, typename LiteralsBuffer> -Scanner<InputStream, LiteralsBuffer>::LiteralScope::~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); -} - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::LiteralScope::Complete() { - scanner_->TerminateLiteral(); - complete_ = true; -} - - -// ---------------------------------------------------------------------------- -// Scanner. -template <typename InputStream, typename LiteralsBuffer> -Scanner<InputStream, LiteralsBuffer>::Scanner() - : stack_guard_(kMaxStackSize), - has_line_terminator_before_next_(false), - source_(NULL), - stack_overflow_(false) {} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::Initialize(InputStream* stream) { - source_ = stream; - - // Initialize current_ to not refer to a literal. - current_.literal_length = 0; - // Reset literal buffer. - literal_buffer_.Reset(); - - // Set c0_ (one character ahead) - ASSERT(kCharacterLookaheadBufferSize == 1); - Advance(); - - // Skip initial whitespace allowing HTML comment ends just like - // after a newline and scan first token. - has_line_terminator_before_next_ = true; - SkipWhiteSpace(); - Scan(); -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::Next() { - // BUG 1215673: Find a thread safe way to set a stack limit in - // pre-parse mode. Otherwise, we cannot safely pre-parse from other - // threads. - current_ = next_; - // Check for stack-overflow before returning any tokens. - if (stack_guard_.has_overflowed()) { - stack_overflow_ = true; - next_.token = i::Token::ILLEGAL; - } else { - has_line_terminator_before_next_ = false; - Scan(); - } - return current_.token; -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::StartLiteral(LiteralType type) { - // Only record string and literal identifiers when preparsing. - // Those are the ones that are recorded as symbols. Numbers and - // regexps are not recorded. - if (type == kLiteralString || type == kLiteralIdentifier) { - literal_buffer_.StartLiteral(); - } -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::AddLiteralChar(uc32 c) { - literal_buffer_.AddChar(c); -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::TerminateLiteral() { - i::Vector<const char> chars = literal_buffer_.EndLiteral(); - next_.literal_chars = chars.start(); - next_.literal_length = chars.length(); -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::DropLiteral() { - literal_buffer_.DropLiteral(); -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::AddLiteralCharAdvance() { - AddLiteralChar(c0_); - Advance(); -} - - -static inline bool IsByteOrderMark(uc32 c) { - // The Unicode value U+FFFE is guaranteed never to be assigned as a - // Unicode character; this implies that in a Unicode context the - // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF - // character expressed in little-endian byte order (since it could - // not be a U+FFFE character expressed in big-endian byte - // order). Nevertheless, we check for it to be compatible with - // Spidermonkey. - return c == 0xFEFF || c == 0xFFFE; -} - - -template <typename InputStream, typename LiteralsBuffer> -bool Scanner<InputStream, LiteralsBuffer>::SkipWhiteSpace() { - int start_position = source_pos(); - - while (true) { - // We treat byte-order marks (BOMs) as whitespace for better - // compatibility with Spidermonkey and other JavaScript engines. - while (i::ScannerConstants::kIsWhiteSpace.get(c0_) - || IsByteOrderMark(c0_)) { - // IsWhiteSpace() includes line terminators! - if (i::ScannerConstants::kIsLineTerminator.get(c0_)) { - // Ignore line terminators, but remember them. This is necessary - // for automatic semicolon insertion. - has_line_terminator_before_next_ = true; - } - Advance(); - } - - // If there is an HTML comment end '-->' at the beginning of a - // line (with only whitespace in front of it), we treat the rest - // of the line as a comment. This is in line with the way - // SpiderMonkey handles it. - if (c0_ == '-' && has_line_terminator_before_next_) { - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>') { - // Treat the rest of the line as a comment. - SkipSingleLineComment(); - // Continue skipping white space after the comment. - continue; - } - PushBack('-'); // undo Advance() - } - PushBack('-'); // undo Advance() - } - // Return whether or not we skipped any characters. - return source_pos() != start_position; - } -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::SkipSingleLineComment() { - Advance(); - - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar (see - // ECMA-262, section 7.4, page 12). - while (c0_ >= 0 && !i::ScannerConstants::kIsLineTerminator.get(c0_)) { - Advance(); - } - - return i::Token::WHITESPACE; -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::SkipMultiLineComment() { - ASSERT(c0_ == '*'); - Advance(); - - while (c0_ >= 0) { - char ch = c0_; - Advance(); - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace - even the ones - // containing line terminators. This contradicts ECMA-262, section - // 7.4, page 12, that says that multi-line comments containing - // line terminators should be treated as a line terminator, but it - // matches the behaviour of SpiderMonkey and KJS. - if (ch == '*' && c0_ == '/') { - c0_ = ' '; - return i::Token::WHITESPACE; - } - } - - // Unterminated multi-line comment. - return i::Token::ILLEGAL; -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanHtmlComment() { - // Check for <!-- comments. - ASSERT(c0_ == '!'); - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '-') return SkipSingleLineComment(); - PushBack('-'); // undo Advance() - } - PushBack('!'); // undo Advance() - ASSERT(c0_ == '!'); - return i::Token::LT; -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::Scan() { - next_.literal_length = 0; - i::Token::Value token; - do { - // Remember the position of the next token - next_.location.beg_pos = source_pos(); - - switch (c0_) { - case ' ': - case '\t': - Advance(); - token = i::Token::WHITESPACE; - break; - - case '\n': - Advance(); - has_line_terminator_before_next_ = true; - token = i::Token::WHITESPACE; - break; - - case '"': case '\'': - token = ScanString(); - break; - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') { - token = Select(i::Token::LTE); - } else if (c0_ == '<') { - token = Select('=', i::Token::ASSIGN_SHL, i::Token::SHL); - } else if (c0_ == '!') { - token = ScanHtmlComment(); - } else { - token = i::Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(i::Token::GTE); - } else if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(i::Token::ASSIGN_SAR); - } else if (c0_ == '>') { - token = Select('=', i::Token::ASSIGN_SHR, i::Token::SHR); - } else { - token = i::Token::SAR; - } - } else { - token = i::Token::GT; - } - break; - - case '=': - // = == === - Advance(); - if (c0_ == '=') { - token = Select('=', i::Token::EQ_STRICT, i::Token::EQ); - } else { - token = i::Token::ASSIGN; - } - break; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') { - token = Select('=', i::Token::NE_STRICT, i::Token::NE); - } else { - token = i::Token::NOT; - } - break; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') { - token = Select(i::Token::INC); - } else if (c0_ == '=') { - token = Select(i::Token::ASSIGN_ADD); - } else { - token = i::Token::ADD; - } - break; - - case '-': - // - -- --> -= - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>' && has_line_terminator_before_next_) { - // For compatibility with SpiderMonkey, we skip lines that - // start with an HTML comment end '-->'. - token = SkipSingleLineComment(); - } else { - token = i::Token::DEC; - } - } else if (c0_ == '=') { - token = Select(i::Token::ASSIGN_SUB); - } else { - token = i::Token::SUB; - } - break; - - case '*': - // * *= - token = Select('=', i::Token::ASSIGN_MUL, i::Token::MUL); - break; - - case '%': - // % %= - token = Select('=', i::Token::ASSIGN_MOD, i::Token::MOD); - break; - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') { - token = SkipSingleLineComment(); - } else if (c0_ == '*') { - token = SkipMultiLineComment(); - } else if (c0_ == '=') { - token = Select(i::Token::ASSIGN_DIV); - } else { - token = i::Token::DIV; - } - break; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') { - token = Select(i::Token::AND); - } else if (c0_ == '=') { - token = Select(i::Token::ASSIGN_BIT_AND); - } else { - token = i::Token::BIT_AND; - } - break; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') { - token = Select(i::Token::OR); - } else if (c0_ == '=') { - token = Select(i::Token::ASSIGN_BIT_OR); - } else { - token = i::Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = Select('=', i::Token::ASSIGN_BIT_XOR, i::Token::BIT_XOR); - break; - - case '.': - // . Number - Advance(); - if (i::IsDecimalDigit(c0_)) { - token = ScanNumber(true); - } else { - token = i::Token::PERIOD; - } - break; - - case ':': - token = Select(i::Token::COLON); - break; - - case ';': - token = Select(i::Token::SEMICOLON); - break; - - case ',': - token = Select(i::Token::COMMA); - break; - - case '(': - token = Select(i::Token::LPAREN); - break; - - case ')': - token = Select(i::Token::RPAREN); - break; - - case '[': - token = Select(i::Token::LBRACK); - break; - - case ']': - token = Select(i::Token::RBRACK); - break; - - case '{': - token = Select(i::Token::LBRACE); - break; - - case '}': - token = Select(i::Token::RBRACE); - break; - - case '?': - token = Select(i::Token::CONDITIONAL); - break; - - case '~': - token = Select(i::Token::BIT_NOT); - break; - - default: - if (i::ScannerConstants::kIsIdentifierStart.get(c0_)) { - token = ScanIdentifier(); - } else if (i::IsDecimalDigit(c0_)) { - token = ScanNumber(false); - } else if (SkipWhiteSpace()) { - token = i::Token::WHITESPACE; - } else if (c0_ < 0) { - token = i::Token::EOS; - } else { - token = Select(i::Token::ILLEGAL); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == i::Token::WHITESPACE); - - next_.location.end_pos = source_pos(); - next_.token = token; -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::SeekForward(int pos) { - source_->SeekForward(pos - 1); - Advance(); - // This function is only called to seek to the location - // of the end of a function (at the "}" token). It doesn't matter - // whether there was a line terminator in the part we skip. - has_line_terminator_before_next_ = false; - Scan(); -} - - -template <typename InputStream, typename LiteralsBuffer> -uc32 Scanner<InputStream, LiteralsBuffer>::ScanHexEscape(uc32 c, int length) { - ASSERT(length <= 4); // prevent overflow - - uc32 digits[4]; - uc32 x = 0; - for (int i = 0; i < length; i++) { - digits[i] = c0_; - int d = HexValue(c0_); - if (d < 0) { - // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes - // should be illegal, but other JS VMs just return the - // non-escaped version of the original character. - - // Push back digits read, except the last one (in c0_). - for (int j = i-1; j >= 0; j--) { - PushBack(digits[j]); - } - // Notice: No handling of error - treat it as "\u"->"u". - return c; - } - x = x * 16 + d; - Advance(); - } - - return x; -} - - -// Octal escapes of the forms '\0xx' and '\xxx' are not a part of -// ECMA-262. Other JS VMs support them. -template <typename InputStream, typename LiteralsBuffer> -uc32 Scanner<InputStream, LiteralsBuffer>::ScanOctalEscape( - uc32 c, int length) { - uc32 x = c - '0'; - for (int i = 0; i < length; i++) { - int d = c0_ - '0'; - if (d < 0 || d > 7) break; - int nx = x * 8 + d; - if (nx >= 256) break; - x = nx; - Advance(); - } - return x; -} - - -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::ScanEscape() { - uc32 c = c0_; - Advance(); - - // Skip escaped newlines. - if (i::ScannerConstants::kIsLineTerminator.get(c)) { - // Allow CR+LF newlines in multiline string literals. - if (i::IsCarriageReturn(c) && i::IsLineFeed(c0_)) Advance(); - // Allow LF+CR newlines in multiline string literals. - if (i::IsLineFeed(c) && i::IsCarriageReturn(c0_)) Advance(); - return; - } - - switch (c) { - case '\'': // fall through - case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : c = ScanHexEscape(c, 4); break; - case 'v' : c = '\v'; break; - case 'x' : c = ScanHexEscape(c, 2); break; - case '0' : // fall through - case '1' : // fall through - case '2' : // fall through - case '3' : // fall through - case '4' : // fall through - case '5' : // fall through - case '6' : // fall through - case '7' : c = ScanOctalEscape(c, 2); break; - } - - // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these - // should be illegal, but they are commonly handled - // as non-escaped characters by JS VMs. - AddLiteralChar(c); -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanString() { - uc32 quote = c0_; - Advance(); // consume quote - - LiteralScope literal(this, kLiteralString); - while (c0_ != quote && c0_ >= 0 - && !i::ScannerConstants::kIsLineTerminator.get(c0_)) { - uc32 c = c0_; - Advance(); - if (c == '\\') { - if (c0_ < 0) return i::Token::ILLEGAL; - ScanEscape(); - } else { - AddLiteralChar(c); - } - } - if (c0_ != quote) return i::Token::ILLEGAL; - literal.Complete(); - - Advance(); // consume quote - return i::Token::STRING; -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::Select( - i::Token::Value tok) { - Advance(); - return tok; -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::Select( - uc32 next, - i::Token::Value then, - i::Token::Value else_) { - Advance(); - if (c0_ == next) { - Advance(); - return then; - } else { - return else_; - } -} - - -// Returns true if any decimal digits were scanned, returns false otherwise. -template <typename InputStream, typename LiteralsBuffer> -void Scanner<InputStream, LiteralsBuffer>::ScanDecimalDigits() { - while (i::IsDecimalDigit(c0_)) - AddLiteralCharAdvance(); -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanNumber( - bool seen_period) { - // c0_ is the first digit of the number or the fraction. - ASSERT(i::IsDecimalDigit(c0_)); - - enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; - - LiteralScope literal(this, kLiteralNumber); - if (seen_period) { - // we have already seen a decimal point of the float - AddLiteralChar('.'); - ScanDecimalDigits(); // we know we have at least one digit - - } else { - // if the first character is '0' we must check for octals and hex - if (c0_ == '0') { - AddLiteralCharAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number - if (c0_ == 'x' || c0_ == 'X') { - // hex number - kind = HEX; - AddLiteralCharAdvance(); - if (!i::IsHexDigit(c0_)) { - // we must have at least one hex digit after 'x'/'X' - return i::Token::ILLEGAL; - } - while (i::IsHexDigit(c0_)) { - AddLiteralCharAdvance(); - } - } else if ('0' <= c0_ && c0_ <= '7') { - // (possible) octal number - kind = OCTAL; - while (true) { - if (c0_ == '8' || c0_ == '9') { - kind = DECIMAL; - break; - } - if (c0_ < '0' || '7' < c0_) break; - AddLiteralCharAdvance(); - } - } - } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - ScanDecimalDigits(); // optional - if (c0_ == '.') { - AddLiteralCharAdvance(); - ScanDecimalDigits(); // optional - } - } - } - - // scan exponent, if any - if (c0_ == 'e' || c0_ == 'E') { - ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind == OCTAL) return i::Token::ILLEGAL; - // scan exponent - AddLiteralCharAdvance(); - if (c0_ == '+' || c0_ == '-') - AddLiteralCharAdvance(); - if (!i::IsDecimalDigit(c0_)) { - // we must have at least one decimal digit after 'e'/'E' - return i::Token::ILLEGAL; - } - ScanDecimalDigits(); - } - - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (i::IsDecimalDigit(c0_) - || i::ScannerConstants::kIsIdentifierStart.get(c0_)) - return i::Token::ILLEGAL; - - literal.Complete(); - - return i::Token::NUMBER; -} - - -template <typename InputStream, typename LiteralsBuffer> -uc32 Scanner<InputStream, LiteralsBuffer>::ScanIdentifierUnicodeEscape() { - Advance(); - if (c0_ != 'u') return unibrow::Utf8::kBadChar; - Advance(); - uc32 c = ScanHexEscape('u', 4); - // We do not allow a unicode escape sequence to start another - // unicode escape sequence. - if (c == '\\') return unibrow::Utf8::kBadChar; - return c; -} - - -template <typename InputStream, typename LiteralsBuffer> -i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanIdentifier() { - ASSERT(i::ScannerConstants::kIsIdentifierStart.get(c0_)); - - LiteralScope literal(this, kLiteralIdentifier); - i::KeywordMatcher keyword_match; - - // Scan identifier start character. - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier start characters. - if (!i::ScannerConstants::kIsIdentifierStart.get(c)) { - return i::Token::ILLEGAL; - } - AddLiteralChar(c); - keyword_match.Fail(); - } else { - AddLiteralChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - - // Scan the rest of the identifier characters. - while (i::ScannerConstants::kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier part characters. - if (!i::ScannerConstants::kIsIdentifierPart.get(c)) { - return i::Token::ILLEGAL; - } - AddLiteralChar(c); - keyword_match.Fail(); - } else { - AddLiteralChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - } - literal.Complete(); - - return keyword_match.token(); -} - - -template <typename InputStream, typename LiteralsBuffer> -bool Scanner<InputStream, LiteralsBuffer>::ScanRegExpPattern(bool seen_equal) { - // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags - bool in_character_class = false; - - // Previous token is either '/' or '/=', in the second case, the - // pattern starts at =. - next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); - next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); - - // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, - // the scanner should pass uninterpreted bodies to the RegExp - // constructor. - LiteralScope literal(this, kLiteralRegExp); - if (seen_equal) - AddLiteralChar('='); - - while (c0_ != '/' || in_character_class) { - if (i::ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) { - return false; - } - if (c0_ == '\\') { // escaped character - AddLiteralCharAdvance(); - if (i::ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) { - return false; - } - AddLiteralCharAdvance(); - } else { // unescaped character - if (c0_ == '[') in_character_class = true; - if (c0_ == ']') in_character_class = false; - AddLiteralCharAdvance(); - } - } - Advance(); // consume '/' - - literal.Complete(); - - return true; -} - -template <typename InputStream, typename LiteralsBuffer> -bool Scanner<InputStream, LiteralsBuffer>::ScanRegExpFlags() { - // Scan regular expression flags. - LiteralScope literal(this, kLiteralRegExpFlags); - while (i::ScannerConstants::kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { - // We allow any escaped character, unlike the restriction on - // IdentifierPart when it is used to build an IdentifierName. - AddLiteralChar(c); - continue; - } - } - AddLiteralCharAdvance(); - } - literal.Complete(); - - next_.location.end_pos = source_pos() - 1; - return true; -} - - -} } // namespace v8::preparser - -#endif // V8_PRESCANNER_H_ diff --git a/deps/v8/src/profile-generator-inl.h b/deps/v8/src/profile-generator-inl.h index cdfa9e2d71..8b5c1e21cb 100644 --- a/deps/v8/src/profile-generator-inl.h +++ b/deps/v8/src/profile-generator-inl.h @@ -105,24 +105,6 @@ void CodeMap::DeleteCode(Address addr) { } -template<class Visitor> -void HeapEntriesMap::UpdateEntries(Visitor* visitor) { - for (HashMap::Entry* p = entries_.Start(); - p != NULL; - p = entries_.Next(p)) { - if (!IsAlias(p->value)) { - EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value); - entry_info->entry = visitor->GetEntry( - reinterpret_cast<HeapObject*>(p->key), - entry_info->children_count, - entry_info->retainers_count); - entry_info->children_count = 0; - entry_info->retainers_count = 0; - } - } -} - - CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { switch (tag) { case GC: @@ -139,6 +121,31 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { } } + +inline uint64_t HeapEntry::id() { + union { + Id stored_id; + uint64_t returned_id; + } id_adaptor = {id_}; + return id_adaptor.returned_id; +} + + +template<class Visitor> +void HeapEntriesMap::UpdateEntries(Visitor* visitor) { + for (HashMap::Entry* p = entries_.Start(); + p != NULL; + p = entries_.Next(p)) { + EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value); + entry_info->entry = visitor->GetEntry( + reinterpret_cast<HeapObject*>(p->key), + entry_info->children_count, + entry_info->retainers_count); + entry_info->children_count = 0; + entry_info->retainers_count = 0; + } +} + } } // namespace v8::internal #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index 29f9ab4d35..e0b63f950c 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -829,7 +829,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { void HeapGraphEdge::Init( int child_index, Type type, const char* name, HeapEntry* to) { - ASSERT(type == kContextVariable || type == kProperty || type == kInternal); + ASSERT(type == kContextVariable + || type == kProperty + || type == kInternal + || type == kShortcut); child_index_ = child_index; type_ = type; name_ = name; @@ -837,14 +840,20 @@ void HeapGraphEdge::Init( } -void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { +void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) { + ASSERT(type == kElement || type == kHidden); child_index_ = child_index; - type_ = kElement; + type_ = type; index_ = index; to_ = to; } +void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { + Init(child_index, kElement, index, to); +} + + HeapEntry* HeapGraphEdge::From() { return reinterpret_cast<HeapEntry*>(this - child_index_) - 1; } @@ -860,12 +869,18 @@ void HeapEntry::Init(HeapSnapshot* snapshot, snapshot_ = snapshot; type_ = type; painted_ = kUnpainted; - calculated_data_index_ = kNoCalculatedData; name_ = name; - id_ = id; self_size_ = self_size; + retained_size_ = 0; children_count_ = children_count; retainers_count_ = retainers_count; + dominator_ = NULL; + + union { + uint64_t set_id; + Id stored_id; + } id_adaptor = {id}; + id_ = id_adaptor.stored_id; } @@ -879,9 +894,12 @@ void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, } -void HeapEntry::SetElementReference( - int child_index, int index, HeapEntry* entry, int retainer_index) { - children_arr()[child_index].Init(child_index, index, entry); +void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type, + int child_index, + int index, + HeapEntry* entry, + int retainer_index) { + children_arr()[child_index].Init(child_index, type, index, entry); entry->retainers_arr()[retainer_index] = children_arr() + child_index; } @@ -892,30 +910,16 @@ void HeapEntry::SetUnidirElementReference( } -int HeapEntry::ReachableSize() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); - } - return snapshot_->GetCalculatedData( - calculated_data_index_).ReachableSize(this); -} - - -int HeapEntry::RetainedSize() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); +int HeapEntry::RetainedSize(bool exact) { + if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) { + CalculateExactRetainedSize(); } - return snapshot_->GetCalculatedData( - calculated_data_index_).RetainedSize(this); + return retained_size_ & (~kExactRetainedSizeTag); } List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); - } - return snapshot_->GetCalculatedData( - calculated_data_index_).GetRetainingPaths(this); + return snapshot_->GetRetainingPaths(this); } @@ -929,6 +933,7 @@ void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) { HeapEntry* entry = list.RemoveLast(); Vector<HeapGraphEdge> children = entry->children(); for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; HeapEntry* child = children[i].to(); if (!child->painted_reachable()) { list.Add(child); @@ -952,8 +957,7 @@ void HeapEntry::PaintAllReachable() { void HeapEntry::Print(int max_depth, int indent) { - OS::Print("%6d %6d %6d [%llu] ", - self_size(), ReachableSize(), RetainedSize(), id_); + OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id()); if (type() != kString) { OS::Print("%s %.40s\n", TypeAsString(), name_); } else { @@ -985,6 +989,12 @@ void HeapEntry::Print(int max_depth, int indent) { case HeapGraphEdge::kProperty: OS::Print(" %*c %s: ", indent, ' ', edge.name()); break; + case HeapGraphEdge::kHidden: + OS::Print(" %*c $%d: ", indent, ' ', edge.index()); + break; + case HeapGraphEdge::kShortcut: + OS::Print(" %*c ^%s: ", indent, ' ', edge.name()); + break; default: OS::Print("!!! unknown edge type: %d ", edge.type()); } @@ -995,7 +1005,7 @@ void HeapEntry::Print(int max_depth, int indent) { const char* HeapEntry::TypeAsString() { switch (type()) { - case kInternal: return "/internal/"; + case kHidden: return "/hidden/"; case kObject: return "/object/"; case kClosure: return "/closure/"; case kString: return "/string/"; @@ -1017,44 +1027,6 @@ int HeapEntry::EntriesSize(int entries_count, } -static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { - delete *path_ptr; -} - -void HeapEntryCalculatedData::Dispose() { - if (retaining_paths_ != NULL) retaining_paths_->Iterate(DeleteHeapGraphPath); - delete retaining_paths_; -} - - -int HeapEntryCalculatedData::ReachableSize(HeapEntry* entry) { - if (reachable_size_ == kUnknownSize) CalculateSizes(entry); - return reachable_size_; -} - - -int HeapEntryCalculatedData::RetainedSize(HeapEntry* entry) { - if (retained_size_ == kUnknownSize) CalculateSizes(entry); - return retained_size_; -} - - -class ReachableSizeCalculator { - public: - ReachableSizeCalculator() - : reachable_size_(0) { - } - - int reachable_size() const { return reachable_size_; } - - void Apply(HeapEntry* entry) { - reachable_size_ += entry->self_size(); - } - - private: - int reachable_size_; -}; - class RetainedSizeCalculator { public: RetainedSizeCalculator() @@ -1073,20 +1045,17 @@ class RetainedSizeCalculator { int retained_size_; }; -void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { +void HeapEntry::CalculateExactRetainedSize() { // To calculate retained size, first we paint all reachable nodes in - // one color (and calculate reachable size as a byproduct), then we - // paint (or re-paint) all nodes reachable from other nodes with a - // different color. Then we consider only nodes painted with the - // first color for calculating the retained size. - entry->snapshot()->ClearPaint(); - ReachableSizeCalculator rch_size_calc; - entry->ApplyAndPaintAllReachable(&rch_size_calc); - reachable_size_ = rch_size_calc.reachable_size(); + // one color, then we paint (or re-paint) all nodes reachable from + // other nodes with a different color. Then we sum up self sizes of + // nodes painted with the first color. + snapshot()->ClearPaint(); + PaintAllReachable(); List<HeapEntry*> list(10); - HeapEntry* root = entry->snapshot()->root(); - if (entry != root) { + HeapEntry* root = snapshot()->root(); + if (this != root) { list.Add(root); root->paint_reachable_from_others(); } @@ -1094,8 +1063,9 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { HeapEntry* curr = list.RemoveLast(); Vector<HeapGraphEdge> children = curr->children(); for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; HeapEntry* child = children[i].to(); - if (child != entry && child->not_painted_reachable_from_others()) { + if (child != this && child->not_painted_reachable_from_others()) { list.Add(child); child->paint_reachable_from_others(); } @@ -1103,8 +1073,10 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { } RetainedSizeCalculator ret_size_calc; - entry->snapshot()->IterateEntries(&ret_size_calc); + snapshot()->IterateEntries(&ret_size_calc); retained_size_ = ret_size_calc.reained_size(); + ASSERT((retained_size_ & kExactRetainedSizeTag) == 0); + retained_size_ |= kExactRetainedSizeTag; } @@ -1142,32 +1114,28 @@ class CachedHeapGraphPath { }; -List<HeapGraphPath*>* HeapEntryCalculatedData::GetRetainingPaths( - HeapEntry* entry) { - if (retaining_paths_ == NULL) retaining_paths_ = new List<HeapGraphPath*>(4); - if (retaining_paths_->length() == 0 && entry->retainers().length() != 0) { - CachedHeapGraphPath path; - FindRetainingPaths(entry, &path); - } - return retaining_paths_; +List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() { + List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4); + CachedHeapGraphPath path; + FindRetainingPaths(&path, retaining_paths); + return retaining_paths; } -void HeapEntryCalculatedData::FindRetainingPaths( - HeapEntry* entry, - CachedHeapGraphPath* prev_path) { - Vector<HeapGraphEdge*> retainers = entry->retainers(); - for (int i = 0; i < retainers.length(); ++i) { - HeapGraphEdge* ret_edge = retainers[i]; +void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path, + List<HeapGraphPath*>* retaining_paths) { + Vector<HeapGraphEdge*> rets = retainers(); + for (int i = 0; i < rets.length(); ++i) { + HeapGraphEdge* ret_edge = rets[i]; if (prev_path->ContainsNode(ret_edge->From())) continue; - if (ret_edge->From() != entry->snapshot()->root()) { + if (ret_edge->From() != snapshot()->root()) { CachedHeapGraphPath path(*prev_path); path.Add(ret_edge); - FindRetainingPaths(ret_edge->From(), &path); + ret_edge->From()->FindRetainingPaths(&path, retaining_paths); } else { HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path()); ret_path->Set(0, ret_edge); - retaining_paths_->Add(ret_path); + retaining_paths->Add(ret_path); } } } @@ -1192,6 +1160,7 @@ void HeapGraphPath::Print() { OS::Print("[#%s] ", edge->name()); break; case HeapGraphEdge::kElement: + case HeapGraphEdge::kHidden: OS::Print("[%d] ", edge->index()); break; case HeapGraphEdge::kInternal: @@ -1200,6 +1169,9 @@ void HeapGraphPath::Print() { case HeapGraphEdge::kProperty: OS::Print("[%s] ", edge->name()); break; + case HeapGraphEdge::kShortcut: + OS::Print("[^%s] ", edge->name()); + break; default: OS::Print("!!! unknown edge type: %d ", edge->type()); } @@ -1211,6 +1183,8 @@ void HeapGraphPath::Print() { HeapObject *const HeapSnapshot::kInternalRootObject = reinterpret_cast<HeapObject*>(1); +HeapObject *const HeapSnapshot::kGcRootsObject = + reinterpret_cast<HeapObject*>(2); // It is very important to keep objects that form a heap snapshot @@ -1221,12 +1195,12 @@ template <size_t ptr_size> struct SnapshotSizeConstants; template <> struct SnapshotSizeConstants<4> { static const int kExpectedHeapGraphEdgeSize = 12; - static const int kExpectedHeapEntrySize = 32; + static const int kExpectedHeapEntrySize = 36; }; template <> struct SnapshotSizeConstants<8> { static const int kExpectedHeapGraphEdgeSize = 24; - static const int kExpectedHeapEntrySize = 40; + static const int kExpectedHeapEntrySize = 48; }; } // namespace @@ -1240,8 +1214,10 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, title_(title), uid_(uid), root_entry_(NULL), + gc_roots_entry_(NULL), raw_entries_(NULL), - entries_sorted_(false) { + entries_sorted_(false), + retaining_paths_(HeapEntry::Match) { STATIC_ASSERT( sizeof(HeapGraphEdge) == SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT @@ -1251,13 +1227,20 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, } -static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) { - cdata->Dispose(); +static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { + delete *path_ptr; } HeapSnapshot::~HeapSnapshot() { DeleteArray(raw_entries_); - calculated_data_.Iterate(DisposeCalculatedData); + for (HashMap::Entry* p = retaining_paths_.Start(); + p != NULL; + p = retaining_paths_.Next(p)) { + List<HeapGraphPath*>* list = + reinterpret_cast<List<HeapGraphPath*>*>(p->value); + list->Iterate(DeleteHeapGraphPath); + delete list; + } } @@ -1280,9 +1263,20 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, if (object == kInternalRootObject) { ASSERT(root_entry_ == NULL); ASSERT(retainers_count == 0); - root_entry_ = AddEntry( - HeapEntry::kInternal, "", 0, 0, children_count, retainers_count); - return root_entry_; + return (root_entry_ = AddEntry(HeapEntry::kObject, + "", + HeapObjectsMap::kInternalRootObjectId, + 0, + children_count, + retainers_count)); + } else if (object == kGcRootsObject) { + ASSERT(gc_roots_entry_ == NULL); + return (gc_roots_entry_ = AddEntry(HeapEntry::kObject, + "(GC roots)", + HeapObjectsMap::kGcRootsObjectId, + 0, + children_count, + retainers_count)); } else if (object->IsJSFunction()) { JSFunction* func = JSFunction::cast(object); SharedFunctionInfo* shared = func->shared(); @@ -1345,22 +1339,11 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, children_count, retainers_count); } - // No interest in this object. - return NULL; -} - - -bool HeapSnapshot::WillAddEntry(HeapObject* object) { - return object == kInternalRootObject - || object->IsJSFunction() - || object->IsJSRegExp() - || object->IsJSObject() - || object->IsString() - || object->IsCode() - || object->IsSharedFunctionInfo() - || object->IsScript() - || object->IsFixedArray() - || object->IsHeapNumber(); + return AddEntry(object, + HeapEntry::kHidden, + "system", + children_count, + retainers_count); } @@ -1373,12 +1356,6 @@ void HeapSnapshot::ClearPaint() { } -int HeapSnapshot::AddCalculatedData() { - calculated_data_.Add(HeapEntryCalculatedData()); - return calculated_data_.length() - 1; -} - - HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, HeapEntry::Type type, const char* name, @@ -1387,7 +1364,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, return AddEntry(type, name, collection_->GetObjectId(object->address()), - GetObjectSize(object), + object->Size(), children_count, retainers_count); } @@ -1405,6 +1382,144 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, } +void HeapSnapshot::FillReversePostorderIndexes(Vector<HeapEntry*>* entries) { + ClearPaint(); + int current_entry = 0; + List<HeapEntry*> nodes_to_visit; + nodes_to_visit.Add(root()); + root()->paint_reachable(); + while (!nodes_to_visit.is_empty()) { + HeapEntry* entry = nodes_to_visit.last(); + Vector<HeapGraphEdge> children = entry->children(); + bool has_new_edges = false; + for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* child = children[i].to(); + if (!child->painted_reachable()) { + nodes_to_visit.Add(child); + child->paint_reachable(); + has_new_edges = true; + } + } + if (!has_new_edges) { + entry->set_ordered_index(current_entry); + entries->at(current_entry++) = entry; + nodes_to_visit.RemoveLast(); + } + } + entries->Truncate(current_entry); +} + + +static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) { + int finger1 = i1, finger2 = i2; + while (finger1 != finger2) { + while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index(); + while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index(); + } + return finger1; +} + +// The algorithm is based on the article: +// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" +// Softw. Pract. Exper. 4 (2001), pp. 1–10. +void HeapSnapshot::BuildDominatorTree(const Vector<HeapEntry*>& entries, + Vector<HeapEntry*>* dominators) { + if (entries.length() == 0) return; + const int root_index = entries.length() - 1; + for (int i = 0; i < root_index; ++i) dominators->at(i) = NULL; + dominators->at(root_index) = entries[root_index]; + bool changed = true; + while (changed) { + changed = false; + for (int i = root_index - 1; i >= 0; --i) { + HeapEntry* new_idom = NULL; + Vector<HeapGraphEdge*> rets = entries[i]->retainers(); + int j = 0; + for (; j < rets.length(); ++j) { + if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* ret = rets[j]->From(); + if (dominators->at(ret->ordered_index()) != NULL) { + new_idom = ret; + break; + } + } + for (++j; j < rets.length(); ++j) { + if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* ret = rets[j]->From(); + if (dominators->at(ret->ordered_index()) != NULL) { + new_idom = entries[Intersect(ret->ordered_index(), + new_idom->ordered_index(), + *dominators)]; + } + } + if (new_idom != NULL && dominators->at(i) != new_idom) { + dominators->at(i) = new_idom; + changed = true; + } + } + } +} + + +void HeapSnapshot::SetEntriesDominators() { + // This array is used for maintaining reverse postorder of nodes. + ScopedVector<HeapEntry*> ordered_entries(entries_.length()); + FillReversePostorderIndexes(&ordered_entries); + ScopedVector<HeapEntry*> dominators(ordered_entries.length()); + BuildDominatorTree(ordered_entries, &dominators); + for (int i = 0; i < ordered_entries.length(); ++i) { + ASSERT(dominators[i] != NULL); + ordered_entries[i]->set_dominator(dominators[i]); + } + // For nodes unreachable from root, set dominator to itself. + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + if (entry->dominator() == NULL) entry->set_dominator(entry); + } +} + + +void HeapSnapshot::ApproximateRetainedSizes() { + SetEntriesDominators(); + // As for the dominators tree we only know parent nodes, not + // children, to sum up total sizes we traverse the tree level by + // level upwards, starting from leaves. + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + entry->set_retained_size(entry->self_size()); + entry->set_leaf(); + } + while (true) { + bool onlyLeaves = true; + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry *entry = entries_[i], *dominator = entry->dominator(); + if (!entry->is_processed() && dominator != entry) { + dominator->set_non_leaf(); + onlyLeaves = false; + } + } + if (onlyLeaves) break; + + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry *entry = entries_[i], *dominator = entry->dominator(); + if (entry->is_leaf() && dominator != entry) { + dominator->add_retained_size(entry->retained_size()); + } + } + + // Mark all current leaves as processed, reset non-leaves back to leaves. + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + if (entry->is_leaf()) + entry->set_processed(); + else if (entry->is_non_leaf()) + entry->set_leaf(); + } + } +} + + HeapEntry* HeapSnapshot::GetNextEntryToInit() { if (entries_.length() > 0) { HeapEntry* last_entry = entries_.last(); @@ -1419,38 +1534,18 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() { } -int HeapSnapshot::GetObjectSize(HeapObject* obj) { - return obj->IsJSObject() ? - CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); +HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { + return collection_->CompareSnapshots(this, snapshot); } -int HeapSnapshot::CalculateNetworkSize(JSObject* obj) { - int size = obj->Size(); - // If 'properties' and 'elements' are non-empty (thus, non-shared), - // take their size into account. - if (obj->properties() != Heap::empty_fixed_array()) { - size += obj->properties()->Size(); - } - if (obj->elements() != Heap::empty_fixed_array()) { - size += obj->elements()->Size(); +List<HeapGraphPath*>* HeapSnapshot::GetRetainingPaths(HeapEntry* entry) { + HashMap::Entry* p = + retaining_paths_.Lookup(entry, HeapEntry::Hash(entry), true); + if (p->value == NULL) { + p->value = entry->CalculateRetainingPaths(); } - // For functions, also account non-empty context and literals sizes. - if (obj->IsJSFunction()) { - JSFunction* f = JSFunction::cast(obj); - if (f->unchecked_context()->IsContext()) { - size += f->context()->Size(); - } - if (f->literals()->length() != 0) { - size += f->literals()->Size(); - } - } - return size; -} - - -HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { - return collection_->CompareSnapshots(this, snapshot); + return reinterpret_cast<List<HeapGraphPath*>*>(p->value); } @@ -1475,9 +1570,14 @@ void HeapSnapshot::Print(int max_depth) { } +const uint64_t HeapObjectsMap::kInternalRootObjectId = 0; +const uint64_t HeapObjectsMap::kGcRootsObjectId = 1; +// Increase kFirstAvailableObjectId if new 'special' objects appear. +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2; + HeapObjectsMap::HeapObjectsMap() : initial_fill_mode_(true), - next_id_(1), + next_id_(kFirstAvailableObjectId), entries_map_(AddressesMatch), entries_(new List<EntryInfo>()) { } @@ -1628,17 +1728,7 @@ HeapEntriesMap::HeapEntriesMap() HeapEntriesMap::~HeapEntriesMap() { for (HashMap::Entry* p = entries_.Start(); p != NULL; p = entries_.Next(p)) { - if (!IsAlias(p->value)) delete reinterpret_cast<EntryInfo*>(p->value); - } -} - - -void HeapEntriesMap::Alias(HeapObject* from, HeapObject* to) { - HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), true); - HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false); - if (from_cache_entry->value == NULL) { - ASSERT(to_cache_entry != NULL); - from_cache_entry->value = MakeAlias(to_cache_entry->value); + delete reinterpret_cast<EntryInfo*>(p->value); } } @@ -1646,8 +1736,7 @@ void HeapEntriesMap::Alias(HeapObject* from, HeapObject* to) { HeapEntry* HeapEntriesMap::Map(HeapObject* object) { HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false); if (cache_entry != NULL) { - EntryInfo* entry_info = - reinterpret_cast<EntryInfo*>(Unalias(cache_entry->value)); + EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(cache_entry->value); return entry_info->entry; } else { return NULL; @@ -1671,9 +1760,9 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, ASSERT(from_cache_entry != NULL); ASSERT(to_cache_entry != NULL); EntryInfo* from_entry_info = - reinterpret_cast<EntryInfo*>(Unalias(from_cache_entry->value)); + reinterpret_cast<EntryInfo*>(from_cache_entry->value); EntryInfo* to_entry_info = - reinterpret_cast<EntryInfo*>(Unalias(to_cache_entry->value)); + reinterpret_cast<EntryInfo*>(to_cache_entry->value); if (prev_children_count) *prev_children_count = from_entry_info->children_count; if (prev_retainers_count) @@ -1685,6 +1774,36 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, } +HeapObjectsSet::HeapObjectsSet() + : entries_(HeapEntriesMap::HeapObjectsMatch) { +} + + +void HeapObjectsSet::Clear() { + entries_.Clear(); +} + + +bool HeapObjectsSet::Contains(Object* obj) { + if (!obj->IsHeapObject()) return false; + HeapObject* object = HeapObject::cast(obj); + HashMap::Entry* cache_entry = + entries_.Lookup(object, HeapEntriesMap::Hash(object), false); + return cache_entry != NULL; +} + + +void HeapObjectsSet::Insert(Object* obj) { + if (!obj->IsHeapObject()) return; + HeapObject* object = HeapObject::cast(obj); + HashMap::Entry* cache_entry = + entries_.Lookup(object, HeapEntriesMap::Hash(object), true); + if (cache_entry->value == NULL) { + cache_entry->value = HeapEntriesMap::kHeapEntryPlaceholder; + } +} + + HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot) : snapshot_(snapshot), collection_(snapshot->collection()), @@ -1699,7 +1818,8 @@ class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder); return HeapEntriesMap::kHeapEntryPlaceholder; } - void SetElementReference(HeapObject* parent_obj, + void SetIndexedReference(HeapGraphEdge::Type, + HeapObject* parent_obj, HeapEntry*, int, Object* child_obj, @@ -1714,10 +1834,18 @@ class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { HeapEntry*) { entries_->CountReference(parent_obj, HeapObject::cast(child_obj)); } - void SetRootReference(Object* child_obj, HeapEntry*) { + void SetRootShortcutReference(Object* child_obj, HeapEntry*) { entries_->CountReference( HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj)); } + void SetRootGcRootsReference() { + entries_->CountReference( + HeapSnapshot::kInternalRootObject, HeapSnapshot::kGcRootsObject); + } + void SetStrongRootReference(Object* child_obj, HeapEntry*) { + entries_->CountReference( + HeapSnapshot::kGcRootsObject, HeapObject::cast(child_obj)); + } private: HeapEntriesMap* entries_; }; @@ -1733,16 +1861,19 @@ class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface { UNREACHABLE(); return NULL; } - void SetElementReference(HeapObject* parent_obj, + void SetIndexedReference(HeapGraphEdge::Type type, + HeapObject* parent_obj, HeapEntry* parent_entry, int index, Object* child_obj, HeapEntry* child_entry) { int child_index, retainer_index; - entries_->CountReference(parent_obj, HeapObject::cast(child_obj), - &child_index, &retainer_index); - parent_entry->SetElementReference( - child_index, index, child_entry, retainer_index); + entries_->CountReference(parent_obj, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + parent_entry->SetIndexedReference( + type, child_index, index, child_entry, retainer_index); } void SetNamedReference(HeapGraphEdge::Type type, HeapObject* parent_obj, @@ -1759,13 +1890,43 @@ class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface { child_entry, retainer_index); } - void SetRootReference(Object* child_obj, HeapEntry* child_entry) { + void SetRootGcRootsReference() { int child_index, retainer_index; - entries_->CountReference( - HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj), - &child_index, &retainer_index); - snapshot_->root()->SetElementReference( - child_index, child_index + 1, child_entry, retainer_index); + entries_->CountReference(HeapSnapshot::kInternalRootObject, + HeapSnapshot::kGcRootsObject, + &child_index, + &retainer_index); + snapshot_->root()->SetIndexedReference(HeapGraphEdge::kElement, + child_index, + child_index + 1, + snapshot_->gc_roots(), + retainer_index); + } + void SetRootShortcutReference(Object* child_obj, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference(HeapSnapshot::kInternalRootObject, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + snapshot_->root()->SetNamedReference(HeapGraphEdge::kShortcut, + child_index, + collection_->GetName(child_index + 1), + child_entry, + retainer_index); + } + void SetStrongRootReference(Object* child_obj, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference(HeapSnapshot::kGcRootsObject, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + snapshot_->gc_roots()->SetIndexedReference(HeapGraphEdge::kElement, + child_index, + child_index + 1, + child_entry, + retainer_index); } private: HeapSnapshot* snapshot_; @@ -1788,6 +1949,19 @@ class SnapshotAllocator { HeapSnapshot* snapshot_; }; +class RootsReferencesExtractor : public ObjectVisitor { + public: + explicit RootsReferencesExtractor(HeapSnapshotGenerator* generator) + : generator_(generator) { + } + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) generator_->SetGcRootsReference(*p); + } + private: + HeapSnapshotGenerator* generator_; +}; + + void HeapSnapshotGenerator::GenerateSnapshot() { AssertNoAllocation no_alloc; @@ -1795,12 +1969,14 @@ void HeapSnapshotGenerator::GenerateSnapshot() { SnapshotCounter counter(&entries_); filler_ = &counter; filler_->AddEntry(HeapSnapshot::kInternalRootObject); - HeapIterator iterator1; - for (HeapObject* obj = iterator1.next(); - obj != NULL; - obj = iterator1.next()) { + filler_->AddEntry(HeapSnapshot::kGcRootsObject); + HeapIterator iterator(HeapIterator::kPreciseFiltering); + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { ExtractReferences(obj); } + SetRootGcRootsReference(); + RootsReferencesExtractor extractor(this); + Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG); // Allocate and fill entries in the snapshot, allocate references. snapshot_->AllocateEntries(entries_.entries_count(), @@ -1812,12 +1988,14 @@ void HeapSnapshotGenerator::GenerateSnapshot() { // Pass 2. Fill references. SnapshotFiller filler(snapshot_, &entries_); filler_ = &filler; - HeapIterator iterator2; - for (HeapObject* obj = iterator2.next(); - obj != NULL; - obj = iterator2.next()) { + iterator.reset(); + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { ExtractReferences(obj); } + SetRootGcRootsReference(); + Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG); + + snapshot_->ApproximateRetainedSizes(); } @@ -1825,25 +2003,8 @@ HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) { if (!obj->IsHeapObject()) return NULL; HeapObject* object = HeapObject::cast(obj); HeapEntry* entry = entries_.Map(object); - // A new entry. - if (entry == NULL) { - if (obj->IsJSGlobalPropertyCell()) { - Object* cell_target = JSGlobalPropertyCell::cast(obj)->value(); - entry = GetEntry(cell_target); - // If GPC references an object that we have interest in (see - // HeapSnapshot::AddEntry, WillAddEntry), add the object. We - // don't store HeapEntries for GPCs. Instead, we make our hash - // map to point to object's HeapEntry by GPCs address. - if (entry != NULL) { - entries_.Alias(object, HeapObject::cast(cell_target)); - } - return entry; - } - - if (snapshot_->WillAddEntry(object)) entry = filler_->AddEntry(object); - } - + if (entry == NULL) entry = filler_->AddEntry(object); return entry; } @@ -1852,43 +2013,44 @@ class IndexedReferencesExtractor : public ObjectVisitor { public: IndexedReferencesExtractor(HeapSnapshotGenerator* generator, HeapObject* parent_obj, - HeapEntry* parent_entry) + HeapEntry* parent_entry, + HeapObjectsSet* known_references = NULL) : generator_(generator), parent_obj_(parent_obj), parent_(parent_entry), + known_references_(known_references), next_index_(1) { } - - void VisitPointer(Object** o) { - generator_->SetElementReference(parent_obj_, parent_, next_index_++, *o); - } - void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) VisitPointer(p); + for (Object** p = start; p < end; p++) { + if (!known_references_ || !known_references_->Contains(*p)) { + generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p); + } + } } - private: HeapSnapshotGenerator* generator_; HeapObject* parent_obj_; HeapEntry* parent_; + HeapObjectsSet* known_references_; int next_index_; }; void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { - // We need to reference JS global objects from snapshot's root. - // We use JSGlobalProxy because this is what embedder (e.g. browser) - // uses for the global object. - if (obj->IsJSGlobalProxy()) { - JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); - SetRootReference(proxy->map()->prototype()); - return; - } - HeapEntry* entry = GetEntry(obj); if (entry == NULL) return; // No interest in this object. - if (obj->IsJSObject()) { + known_references_.Clear(); + if (obj->IsJSGlobalProxy()) { + // We need to reference JS global objects from snapshot's root. + // We use JSGlobalProxy because this is what embedder (e.g. browser) + // uses for the global object. + JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); + SetRootShortcutReference(proxy->map()->prototype()); + IndexedReferencesExtractor refs_extractor(this, obj, entry); + obj->Iterate(&refs_extractor); + } else if (obj->IsJSObject()) { JSObject* js_obj = JSObject::cast(obj); ExtractClosureReferences(js_obj, entry); ExtractPropertyReferences(js_obj, entry); @@ -1903,16 +2065,16 @@ void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { obj, entry, Heap::prototype_symbol(), js_fun->prototype()); } } + IndexedReferencesExtractor refs_extractor( + this, obj, entry, &known_references_); + obj->Iterate(&refs_extractor); } else if (obj->IsString()) { if (obj->IsConsString()) { ConsString* cs = ConsString::cast(obj); - SetInternalReference(obj, entry, "1", cs->first()); - SetInternalReference(obj, entry, "2", cs->second()); + SetInternalReference(obj, entry, 1, cs->first()); + SetInternalReference(obj, entry, 2, cs->second()); } - } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) { - IndexedReferencesExtractor refs_extractor(this, obj, entry); - obj->Iterate(&refs_extractor); - } else if (obj->IsFixedArray()) { + } else { IndexedReferencesExtractor refs_extractor(this, obj, entry); obj->Iterate(&refs_extractor); } @@ -1967,8 +2129,17 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, for (int i = 0; i < length; ++i) { Object* k = dictionary->KeyAt(i); if (dictionary->IsKey(k)) { + Object* target = dictionary->ValueAt(i); SetPropertyReference( - js_obj, entry, String::cast(k), dictionary->ValueAt(i)); + js_obj, entry, String::cast(k), target); + // We assume that global objects can only have slow properties. + if (target->IsJSGlobalPropertyCell()) { + SetPropertyShortcutReference(js_obj, + entry, + String::cast(k), + JSGlobalPropertyCell::cast( + target)->value()); + } } } } @@ -2024,6 +2195,7 @@ void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj, collection_->GetName(reference_name), child_obj, child_entry); + known_references_.Insert(child_obj); } } @@ -2034,8 +2206,13 @@ void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj, Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { - filler_->SetElementReference( - parent_obj, parent_entry, index, child_obj, child_entry); + filler_->SetIndexedReference(HeapGraphEdge::kElement, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); + known_references_.Insert(child_obj); } } @@ -2052,6 +2229,7 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, reference_name, child_obj, child_entry); + known_references_.Insert(child_obj); } } @@ -2068,6 +2246,23 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, collection_->GetName(index), child_obj, child_entry); + known_references_.Insert(child_obj); + } +} + + +void HeapSnapshotGenerator::SetHiddenReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetIndexedReference(HeapGraphEdge::kHidden, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); } } @@ -2086,14 +2281,45 @@ void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj, collection_->GetName(reference_name), child_obj, child_entry); + known_references_.Insert(child_obj); } } -void HeapSnapshotGenerator::SetRootReference(Object* child_obj) { +void HeapSnapshotGenerator::SetPropertyShortcutReference( + HeapObject* parent_obj, + HeapEntry* parent_entry, + String* reference_name, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetNamedReference(HeapGraphEdge::kShortcut, + parent_obj, + parent_entry, + collection_->GetName(reference_name), + child_obj, + child_entry); + } +} + + +void HeapSnapshotGenerator::SetRootGcRootsReference() { + filler_->SetRootGcRootsReference(); +} + + +void HeapSnapshotGenerator::SetRootShortcutReference(Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); ASSERT(child_entry != NULL); - filler_->SetRootReference(child_obj, child_entry); + filler_->SetRootShortcutReference(child_obj, child_entry); +} + + +void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetStrongRootReference(child_obj, child_entry); + } } @@ -2101,11 +2327,11 @@ void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) { raw_additions_root_ = NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0)); additions_root()->Init( - snapshot2_, HeapEntry::kInternal, "", 0, 0, additions_count, 0); + snapshot2_, HeapEntry::kHidden, "", 0, 0, additions_count, 0); raw_deletions_root_ = NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0)); deletions_root()->Init( - snapshot1_, HeapEntry::kInternal, "", 0, 0, deletions_count, 0); + snapshot1_, HeapEntry::kHidden, "", 0, 0, deletions_count, 0); } @@ -2324,7 +2550,8 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { writer_->AddCharacter(','); writer_->AddNumber(edge->type()); writer_->AddCharacter(','); - if (edge->type() == HeapGraphEdge::kElement) { + if (edge->type() == HeapGraphEdge::kElement + || edge->type() == HeapGraphEdge::kHidden) { writer_->AddNumber(edge->index()); } else { writer_->AddNumber(GetStringId(edge->name())); @@ -2344,6 +2571,10 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { writer_->AddNumber(entry->id()); writer_->AddCharacter(','); writer_->AddNumber(entry->self_size()); + writer_->AddCharacter(','); + writer_->AddNumber(entry->RetainedSize(false)); + writer_->AddCharacter(','); + writer_->AddNumber(GetNodeId(entry->dominator())); Vector<HeapGraphEdge> children = entry->children(); writer_->AddCharacter(','); writer_->AddNumber(children.length()); @@ -2355,23 +2586,25 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { void HeapSnapshotJSONSerializer::SerializeNodes() { - // The first (zero) item of nodes array is a JSON-ified object - // describing node serialization layout. - // We use a set of macros to improve readability. + // The first (zero) item of nodes array is an object describing node + // serialization layout. We use a set of macros to improve + // readability. #define JSON_A(s) "["s"]" #define JSON_O(s) "{"s"}" -#define JSON_S(s) "\\\""s"\\\"" - writer_->AddString("\"" JSON_O( +#define JSON_S(s) "\""s"\"" + writer_->AddString(JSON_O( JSON_S("fields") ":" JSON_A( JSON_S("type") "," JSON_S("name") "," JSON_S("id") "," JSON_S("self_size") + "," JSON_S("retained_size") + "," JSON_S("dominator") "," JSON_S("children_count") "," JSON_S("children")) "," JSON_S("types") ":" JSON_A( JSON_A( - JSON_S("internal") + JSON_S("hidden") "," JSON_S("array") "," JSON_S("string") "," JSON_S("object") @@ -2383,6 +2616,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("number") "," JSON_S("number") "," JSON_S("number") + "," JSON_S("number") + "," JSON_S("number") "," JSON_O( JSON_S("fields") ":" JSON_A( JSON_S("type") @@ -2393,14 +2628,17 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { JSON_S("context") "," JSON_S("element") "," JSON_S("property") - "," JSON_S("internal")) + "," JSON_S("internal") + "," JSON_S("hidden") + "," JSON_S("shortcut")) "," JSON_S("string_or_number") - "," JSON_S("node"))))) "\""); + "," JSON_S("node")))))); #undef JSON_S #undef JSON_O #undef JSON_A - const int node_fields_count = 5; // type,name,id,self_size,children_count. + const int node_fields_count = 7; + // type,name,id,self_size,retained_size,dominator,children_count. const int edge_fields_count = 3; // type,name|index,to_node. List<HashMap::Entry*> sorted_nodes; SortHashMap(&nodes_, &sorted_nodes); diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index b691a056e8..30d70a2c74 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -439,22 +439,26 @@ class HeapGraphEdge BASE_EMBEDDED { kContextVariable = v8::HeapGraphEdge::kContextVariable, kElement = v8::HeapGraphEdge::kElement, kProperty = v8::HeapGraphEdge::kProperty, - kInternal = v8::HeapGraphEdge::kInternal + kInternal = v8::HeapGraphEdge::kInternal, + kHidden = v8::HeapGraphEdge::kHidden, + kShortcut = v8::HeapGraphEdge::kShortcut }; HeapGraphEdge() { } void Init(int child_index, Type type, const char* name, HeapEntry* to); + void Init(int child_index, Type type, int index, HeapEntry* to); void Init(int child_index, int index, HeapEntry* to); Type type() { return static_cast<Type>(type_); } int index() { - ASSERT(type_ == kElement); + ASSERT(type_ == kElement || type_ == kHidden); return index_; } const char* name() { ASSERT(type_ == kContextVariable || type_ == kProperty - || type_ == kInternal); + || type_ == kInternal + || type_ == kShortcut); return name_; } HeapEntry* to() { return to_; } @@ -462,8 +466,8 @@ class HeapGraphEdge BASE_EMBEDDED { HeapEntry* From(); private: - int child_index_ : 30; - unsigned type_ : 2; + int child_index_ : 29; + unsigned type_ : 3; union { int index_; const char* name_; @@ -500,7 +504,7 @@ class HeapSnapshot; class HeapEntry BASE_EMBEDDED { public: enum Type { - kInternal = v8::HeapGraphNode::kInternal, + kHidden = v8::HeapGraphNode::kHidden, kArray = v8::HeapGraphNode::kArray, kString = v8::HeapGraphNode::kString, kObject = v8::HeapGraphNode::kObject, @@ -522,14 +526,21 @@ class HeapEntry BASE_EMBEDDED { HeapSnapshot* snapshot() { return snapshot_; } Type type() { return static_cast<Type>(type_); } const char* name() { return name_; } - uint64_t id() { return id_; } + uint64_t id(); int self_size() { return self_size_; } + int retained_size() { return retained_size_; } + void add_retained_size(int size) { retained_size_ += size; } + void set_retained_size(int value) { retained_size_ = value; } + int ordered_index() { return ordered_index_; } + void set_ordered_index(int value) { ordered_index_ = value; } Vector<HeapGraphEdge> children() { return Vector<HeapGraphEdge>(children_arr(), children_count_); } Vector<HeapGraphEdge*> retainers() { return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); } List<HeapGraphPath*>* GetRetainingPaths(); + HeapEntry* dominator() { return dominator_; } + void set_dominator(HeapEntry* entry) { dominator_ = entry; } void clear_paint() { painted_ = kUnpainted; } bool painted_reachable() { return painted_ == kPainted; } @@ -547,8 +558,18 @@ class HeapEntry BASE_EMBEDDED { void ApplyAndPaintAllReachable(Visitor* visitor); void PaintAllReachable(); - void SetElementReference( - int child_index, int index, HeapEntry* entry, int retainer_index); + bool is_leaf() { return painted_ == kLeaf; } + void set_leaf() { painted_ = kLeaf; } + bool is_non_leaf() { return painted_ == kNonLeaf; } + void set_non_leaf() { painted_ = kNonLeaf; } + bool is_processed() { return painted_ == kProcessed; } + void set_processed() { painted_ = kProcessed; } + + void SetIndexedReference(HeapGraphEdge::Type type, + int child_index, + int index, + HeapEntry* entry, + int retainer_index); void SetNamedReference(HeapGraphEdge::Type type, int child_index, const char* name, @@ -557,14 +578,19 @@ class HeapEntry BASE_EMBEDDED { void SetUnidirElementReference(int child_index, int index, HeapEntry* entry); int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); } - int ReachableSize(); - int RetainedSize(); + int RetainedSize(bool exact); + List<HeapGraphPath*>* CalculateRetainingPaths(); void Print(int max_depth, int indent); static int EntriesSize(int entries_count, int children_count, int retainers_count); + static uint32_t Hash(HeapEntry* entry) { + return ComputeIntegerHash( + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entry))); + } + static bool Match(void* entry1, void* entry2) { return entry1 == entry2; } private: HeapGraphEdge* children_arr() { @@ -573,53 +599,40 @@ class HeapEntry BASE_EMBEDDED { HeapGraphEdge** retainers_arr() { return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_); } + void CalculateExactRetainedSize(); + void FindRetainingPaths(CachedHeapGraphPath* prev_path, + List<HeapGraphPath*>* retaining_paths); const char* TypeAsString(); unsigned painted_: 2; unsigned type_: 3; - // The calculated data is stored in HeapSnapshot in HeapEntryCalculatedData - // entries. See AddCalculatedData and GetCalculatedData. - int calculated_data_index_: 27; - int self_size_; - int children_count_; + int children_count_: 27; int retainers_count_; + int self_size_; + union { + int ordered_index_; // Used during dominator tree building. + int retained_size_; // At that moment, there is no retained size yet. + }; + HeapEntry* dominator_; HeapSnapshot* snapshot_; + struct Id { + uint32_t id1_; + uint32_t id2_; + } id_; // This is to avoid extra padding of 64-bit value. const char* name_; - uint64_t id_; + // Paints used for exact retained sizes calculation. static const unsigned kUnpainted = 0; static const unsigned kPainted = 1; static const unsigned kPaintedReachableFromOthers = 2; - static const int kNoCalculatedData = -1; - - DISALLOW_COPY_AND_ASSIGN(HeapEntry); -}; - - -class HeapEntryCalculatedData { - public: - HeapEntryCalculatedData() - : retaining_paths_(NULL), - reachable_size_(kUnknownSize), - retained_size_(kUnknownSize) { - } - void Dispose(); - - List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry); - int ReachableSize(HeapEntry* entry); - int RetainedSize(HeapEntry* entry); - - private: - void CalculateSizes(HeapEntry* entry); - void FindRetainingPaths(HeapEntry* entry, CachedHeapGraphPath* prev_path); - - List<HeapGraphPath*>* retaining_paths_; - int reachable_size_; - int retained_size_; + // Paints used for approximate retained sizes calculation. + static const unsigned kLeaf = 0; + static const unsigned kNonLeaf = 1; + static const unsigned kProcessed = 2; - static const int kUnknownSize = -1; + static const int kExactRetainedSizeTag = 1; - // Allow generated copy constructor and assignment operator. + DISALLOW_COPY_AND_ASSIGN(HeapEntry); }; @@ -668,24 +681,22 @@ class HeapSnapshot { const char* title() { return title_; } unsigned uid() { return uid_; } HeapEntry* root() { return root_entry_; } + HeapEntry* gc_roots() { return gc_roots_entry_; } void AllocateEntries( int entries_count, int children_count, int retainers_count); HeapEntry* AddEntry( HeapObject* object, int children_count, int retainers_count); - bool WillAddEntry(HeapObject* object); HeapEntry* AddEntry(HeapEntry::Type type, const char* name, uint64_t id, int size, int children_count, int retainers_count); - int AddCalculatedData(); - HeapEntryCalculatedData& GetCalculatedData(int index) { - return calculated_data_[index]; - } + void ApproximateRetainedSizes(); void ClearPaint(); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); + List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry); List<HeapEntry*>* GetSortedEntriesList(); template<class Visitor> void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); } @@ -693,7 +704,8 @@ class HeapSnapshot { void Print(int max_depth); void PrintEntriesSize(); - static HeapObject *const kInternalRootObject; + static HeapObject* const kInternalRootObject; + static HeapObject* const kGcRootsObject; private: HeapEntry* AddEntry(HeapObject* object, @@ -702,18 +714,21 @@ class HeapSnapshot { int children_count, int retainers_count); HeapEntry* GetNextEntryToInit(); - static int GetObjectSize(HeapObject* obj); - static int CalculateNetworkSize(JSObject* obj); + void BuildDominatorTree(const Vector<HeapEntry*>& entries, + Vector<HeapEntry*>* dominators); + void FillReversePostorderIndexes(Vector<HeapEntry*>* entries); + void SetEntriesDominators(); HeapSnapshotsCollection* collection_; Type type_; const char* title_; unsigned uid_; HeapEntry* root_entry_; + HeapEntry* gc_roots_entry_; char* raw_entries_; List<HeapEntry*> entries_; bool entries_sorted_; - List<HeapEntryCalculatedData> calculated_data_; + HashMap retaining_paths_; #ifdef DEBUG int raw_entries_size_; #endif @@ -733,6 +748,10 @@ class HeapObjectsMap { uint64_t FindObject(Address addr); void MoveObject(Address from, Address to); + static const uint64_t kInternalRootObjectId; + static const uint64_t kGcRootsObjectId; + static const uint64_t kFirstAvailableObjectId; + private: struct EntryInfo { explicit EntryInfo(uint64_t id) : id(id), accessed(true) { } @@ -868,9 +887,6 @@ class HeapEntriesMap { HeapEntriesMap(); ~HeapEntriesMap(); - // Aliasing is used for skipping intermediate proxy objects, like - // JSGlobalPropertyCell. - void Alias(HeapObject* from, HeapObject* to); HeapEntry* Map(HeapObject* object); void Pair(HeapObject* object, HeapEntry* entry); void CountReference(HeapObject* from, HeapObject* to, @@ -894,41 +910,45 @@ class HeapEntriesMap { int retainers_count; }; - uint32_t Hash(HeapObject* object) { + static uint32_t Hash(HeapObject* object) { return ComputeIntegerHash( static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object))); } static bool HeapObjectsMatch(void* key1, void* key2) { return key1 == key2; } - bool IsAlias(void* ptr) { - return reinterpret_cast<intptr_t>(ptr) & kAliasTag; - } - void* MakeAlias(void* ptr) { - return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(ptr) | kAliasTag); - } - void* Unalias(void* ptr) { - return reinterpret_cast<void*>( - reinterpret_cast<intptr_t>(ptr) & (~kAliasTag)); - } - HashMap entries_; int entries_count_; int total_children_count_; int total_retainers_count_; - static const intptr_t kAliasTag = 1; + friend class HeapObjectsSet; DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap); }; +class HeapObjectsSet { + public: + HeapObjectsSet(); + void Clear(); + bool Contains(Object* object); + void Insert(Object* obj); + + private: + HashMap entries_; + + DISALLOW_COPY_AND_ASSIGN(HeapObjectsSet); +}; + + class HeapSnapshotGenerator { public: class SnapshotFillerInterface { public: virtual ~SnapshotFillerInterface() { } virtual HeapEntry* AddEntry(HeapObject* obj) = 0; - virtual void SetElementReference(HeapObject* parent_obj, + virtual void SetIndexedReference(HeapGraphEdge::Type type, + HeapObject* parent_obj, HeapEntry* parent_entry, int index, Object* child_obj, @@ -939,8 +959,11 @@ class HeapSnapshotGenerator { const char* reference_name, Object* child_obj, HeapEntry* child_entry) = 0; - virtual void SetRootReference(Object* child_obj, - HeapEntry* child_entry) = 0; + virtual void SetRootGcRootsReference() = 0; + virtual void SetRootShortcutReference(Object* child_obj, + HeapEntry* child_entry) = 0; + virtual void SetStrongRootReference(Object* child_obj, + HeapEntry* child_entry) = 0; }; explicit HeapSnapshotGenerator(HeapSnapshot* snapshot); @@ -969,19 +992,33 @@ class HeapSnapshotGenerator { HeapEntry* parent, int index, Object* child); + void SetHiddenReference(HeapObject* parent_obj, + HeapEntry* parent, + int index, + Object* child); void SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, Object* child); - void SetRootReference(Object* child); + void SetPropertyShortcutReference(HeapObject* parent_obj, + HeapEntry* parent, + String* reference_name, + Object* child); + void SetRootShortcutReference(Object* child); + void SetRootGcRootsReference(); + void SetGcRootsReference(Object* child); HeapSnapshot* snapshot_; HeapSnapshotsCollection* collection_; // Mapping from HeapObject* pointers to HeapEntry* pointers. HeapEntriesMap entries_; SnapshotFillerInterface* filler_; + // Used during references extraction to mark heap objects that + // are references via non-hidden properties. + HeapObjectsSet known_references_; friend class IndexedReferencesExtractor; + friend class RootsReferencesExtractor; DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); }; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 5534db557c..e20c94fddc 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -6340,15 +6340,20 @@ static MaybeObject* Runtime_NewArgumentsFast(Arguments args) { static MaybeObject* Runtime_NewClosure(Arguments args) { HandleScope scope; - ASSERT(args.length() == 2); + ASSERT(args.length() == 3); CONVERT_ARG_CHECKED(Context, context, 0); CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1); + CONVERT_BOOLEAN_CHECKED(pretenure, args[2]); - PretenureFlag pretenure = (context->global_context() == *context) - ? TENURED // Allocate global closures in old space. - : NOT_TENURED; // Allocate local closures in new space. + // Allocate global closures in old space and allocate local closures + // in new space. Additionally pretenure closures that are assigned + // directly to properties. + pretenure = pretenure || (context->global_context() == *context); + PretenureFlag pretenure_flag = pretenure ? TENURED : NOT_TENURED; Handle<JSFunction> result = - Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure); + Factory::NewFunctionFromSharedFunctionInfo(shared, + context, + pretenure_flag); return *result; } diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 756099b413..e36988da44 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -262,7 +262,7 @@ namespace internal { F(CreateCatchExtensionObject, 2, 1) \ \ /* Statements */ \ - F(NewClosure, 2, 1) \ + F(NewClosure, 3, 1) \ F(NewObject, 1, 1) \ F(NewObjectFromBound, 2, 1) \ F(FinalizeInstanceSize, 1, 1) \ @@ -418,7 +418,8 @@ namespace internal { F(MathSqrt, 1, 1) \ F(IsRegExpEquivalent, 2, 1) \ F(HasCachedArrayIndex, 1, 1) \ - F(GetCachedArrayIndex, 1, 1) + F(GetCachedArrayIndex, 1, 1) \ + F(FastAsciiArrayJoin, 2, 1) // ---------------------------------------------------------------------------- diff --git a/deps/v8/src/scanner-base.cc b/deps/v8/src/scanner-base.cc index 6cde51787f..8242f81c3f 100644 --- a/deps/v8/src/scanner-base.cc +++ b/deps/v8/src/scanner-base.cc @@ -29,11 +29,40 @@ #include "../include/v8stdint.h" #include "scanner-base.h" +#include "char-predicates-inl.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- +// UTF16Buffer + +UTF16Buffer::UTF16Buffer() + : pos_(0), end_(kNoEndPosition) { } + +// ---------------------------------------------------------------------------- +// LiteralCollector + +LiteralCollector::LiteralCollector() + : buffer_(kInitialCapacity), recording_(false) { } + + +LiteralCollector::~LiteralCollector() {} + + +void LiteralCollector::AddCharSlow(uc32 c) { + ASSERT(static_cast<unsigned>(c) > unibrow::Utf8::kMaxOneByteChar); + int length = unibrow::Utf8::Length(c); + Vector<char> block = buffer_.AddBlock(length, '\0'); +#ifdef DEBUG + int written_length = unibrow::Utf8::Encode(block.start(), c); + CHECK_EQ(length, written_length); +#else + unibrow::Utf8::Encode(block.start(), c); +#endif +} + +// ---------------------------------------------------------------------------- // Character predicates unibrow::Predicate<IdentifierStart, 128> ScannerConstants::kIsIdentifierStart; @@ -61,6 +90,707 @@ bool ScannerConstants::IsIdentifier(unibrow::CharacterStream* buffer) { } // ---------------------------------------------------------------------------- +// Scanner + +Scanner::Scanner() : source_(NULL), stack_overflow_(false) {} + + +uc32 Scanner::ScanHexEscape(uc32 c, int length) { + ASSERT(length <= 4); // prevent overflow + + uc32 digits[4]; + uc32 x = 0; + for (int i = 0; i < length; i++) { + digits[i] = c0_; + int d = HexValue(c0_); + if (d < 0) { + // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes + // should be illegal, but other JS VMs just return the + // non-escaped version of the original character. + + // Push back digits read, except the last one (in c0_). + for (int j = i-1; j >= 0; j--) { + PushBack(digits[j]); + } + // Notice: No handling of error - treat it as "\u"->"u". + return c; + } + x = x * 16 + d; + Advance(); + } + + return x; +} + + +// Octal escapes of the forms '\0xx' and '\xxx' are not a part of +// ECMA-262. Other JS VMs support them. +uc32 Scanner::ScanOctalEscape(uc32 c, int length) { + uc32 x = c - '0'; + for (int i = 0; i < length; i++) { + int d = c0_ - '0'; + if (d < 0 || d > 7) break; + int nx = x * 8 + d; + if (nx >= 256) break; + x = nx; + Advance(); + } + return x; +} + + +// ---------------------------------------------------------------------------- +// JavaScriptScanner + +JavaScriptScanner::JavaScriptScanner() + : has_line_terminator_before_next_(false) {} + + +Token::Value JavaScriptScanner::Next() { + current_ = next_; + has_line_terminator_before_next_ = false; + Scan(); + return current_.token; +} + + +static inline bool IsByteOrderMark(uc32 c) { + // The Unicode value U+FFFE is guaranteed never to be assigned as a + // Unicode character; this implies that in a Unicode context the + // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF + // character expressed in little-endian byte order (since it could + // not be a U+FFFE character expressed in big-endian byte + // order). Nevertheless, we check for it to be compatible with + // Spidermonkey. + return c == 0xFEFF || c == 0xFFFE; +} + + +bool JavaScriptScanner::SkipWhiteSpace() { + int start_position = source_pos(); + + while (true) { + // We treat byte-order marks (BOMs) as whitespace for better + // compatibility with Spidermonkey and other JavaScript engines. + while (ScannerConstants::kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { + // IsWhiteSpace() includes line terminators! + if (ScannerConstants::kIsLineTerminator.get(c0_)) { + // Ignore line terminators, but remember them. This is necessary + // for automatic semicolon insertion. + has_line_terminator_before_next_ = true; + } + Advance(); + } + + // If there is an HTML comment end '-->' at the beginning of a + // line (with only whitespace in front of it), we treat the rest + // of the line as a comment. This is in line with the way + // SpiderMonkey handles it. + if (c0_ == '-' && has_line_terminator_before_next_) { + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>') { + // Treat the rest of the line as a comment. + SkipSingleLineComment(); + // Continue skipping white space after the comment. + continue; + } + PushBack('-'); // undo Advance() + } + PushBack('-'); // undo Advance() + } + // Return whether or not we skipped any characters. + return source_pos() != start_position; + } +} + + +Token::Value JavaScriptScanner::SkipSingleLineComment() { + Advance(); + + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar (see + // ECMA-262, section 7.4, page 12). + while (c0_ >= 0 && !ScannerConstants::kIsLineTerminator.get(c0_)) { + Advance(); + } + + return Token::WHITESPACE; +} + + +Token::Value JavaScriptScanner::SkipMultiLineComment() { + ASSERT(c0_ == '*'); + Advance(); + + while (c0_ >= 0) { + char ch = c0_; + Advance(); + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace - even the ones + // containing line terminators. This contradicts ECMA-262, section + // 7.4, page 12, that says that multi-line comments containing + // line terminators should be treated as a line terminator, but it + // matches the behaviour of SpiderMonkey and KJS. + if (ch == '*' && c0_ == '/') { + c0_ = ' '; + return Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return Token::ILLEGAL; +} + + +Token::Value JavaScriptScanner::ScanHtmlComment() { + // Check for <!-- comments. + ASSERT(c0_ == '!'); + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '-') return SkipSingleLineComment(); + PushBack('-'); // undo Advance() + } + PushBack('!'); // undo Advance() + ASSERT(c0_ == '!'); + return Token::LT; +} + + +void JavaScriptScanner::Scan() { + next_.literal_chars = Vector<const char>(); + Token::Value token; + do { + // Remember the position of the next token + next_.location.beg_pos = source_pos(); + + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(Token::LTE); + } else if (c0_ == '<') { + token = Select('=', Token::ASSIGN_SHL, Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == === + Advance(); + if (c0_ == '=') { + token = Select('=', Token::EQ_STRICT, Token::EQ); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', Token::NE_STRICT, Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(Token::INC); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = Token::DEC; + } + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = Select('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + token = SkipSingleLineComment(); + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(Token::AND); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(Token::OR); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = Select(Token::COLON); + break; + + case ';': + token = Select(Token::SEMICOLON); + break; + + case ',': + token = Select(Token::COMMA); + break; + + case '(': + token = Select(Token::LPAREN); + break; + + case ')': + token = Select(Token::RPAREN); + break; + + case '[': + token = Select(Token::LBRACK); + break; + + case ']': + token = Select(Token::RBRACK); + break; + + case '{': + token = Select(Token::LBRACE); + break; + + case '}': + token = Select(Token::RBRACE); + break; + + case '?': + token = Select(Token::CONDITIONAL); + break; + + case '~': + token = Select(Token::BIT_NOT); + break; + + default: + if (ScannerConstants::kIsIdentifierStart.get(c0_)) { + token = ScanIdentifierOrKeyword(); + } else if (IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = Token::WHITESPACE; + } else if (c0_ < 0) { + token = Token::EOS; + } else { + token = Select(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); + + next_.location.end_pos = source_pos(); + next_.token = token; +} + + +void JavaScriptScanner::SeekForward(int pos) { + source_->SeekForward(pos - 1); + Advance(); + // This function is only called to seek to the location + // of the end of a function (at the "}" token). It doesn't matter + // whether there was a line terminator in the part we skip. + has_line_terminator_before_next_ = false; + Scan(); +} + + +void JavaScriptScanner::ScanEscape() { + uc32 c = c0_; + Advance(); + + // Skip escaped newlines. + if (ScannerConstants::kIsLineTerminator.get(c)) { + // Allow CR+LF newlines in multiline string literals. + if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); + // Allow LF+CR newlines in multiline string literals. + if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); + return; + } + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : c = ScanHexEscape(c, 4); break; + case 'v' : c = '\v'; break; + case 'x' : c = ScanHexEscape(c, 2); break; + case '0' : // fall through + case '1' : // fall through + case '2' : // fall through + case '3' : // fall through + case '4' : // fall through + case '5' : // fall through + case '6' : // fall through + case '7' : c = ScanOctalEscape(c, 2); break; + } + + // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these + // should be illegal, but they are commonly handled + // as non-escaped characters by JS VMs. + AddLiteralChar(c); +} + + +Token::Value JavaScriptScanner::ScanString() { + uc32 quote = c0_; + Advance(); // consume quote + + LiteralScope literal(this, kLiteralString); + while (c0_ != quote && c0_ >= 0 + && !ScannerConstants::kIsLineTerminator.get(c0_)) { + uc32 c = c0_; + Advance(); + if (c == '\\') { + if (c0_ < 0) return Token::ILLEGAL; + ScanEscape(); + } else { + AddLiteralChar(c); + } + } + if (c0_ != quote) return Token::ILLEGAL; + literal.Complete(); + + Advance(); // consume quote + return Token::STRING; +} + + +void JavaScriptScanner::ScanDecimalDigits() { + while (IsDecimalDigit(c0_)) + AddLiteralCharAdvance(); +} + + +Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { + ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction + + enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; + + LiteralScope literal(this, kLiteralNumber); + if (seen_period) { + // we have already seen a decimal point of the float + AddLiteralChar('.'); + ScanDecimalDigits(); // we know we have at least one digit + + } else { + // if the first character is '0' we must check for octals and hex + if (c0_ == '0') { + AddLiteralCharAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number + if (c0_ == 'x' || c0_ == 'X') { + // hex number + kind = HEX; + AddLiteralCharAdvance(); + if (!IsHexDigit(c0_)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if ('0' <= c0_ && c0_ <= '7') { + // (possible) octal number + kind = OCTAL; + while (true) { + if (c0_ == '8' || c0_ == '9') { + kind = DECIMAL; + break; + } + if (c0_ < '0' || '7' < c0_) break; + AddLiteralCharAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + ScanDecimalDigits(); // optional + if (c0_ == '.') { + AddLiteralCharAdvance(); + ScanDecimalDigits(); // optional + } + } + } + + // scan exponent, if any + if (c0_ == 'e' || c0_ == 'E') { + ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed + // scan exponent + AddLiteralCharAdvance(); + if (c0_ == '+' || c0_ == '-') + AddLiteralCharAdvance(); + if (!IsDecimalDigit(c0_)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + ScanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(c0_) || ScannerConstants::kIsIdentifierStart.get(c0_)) + return Token::ILLEGAL; + + literal.Complete(); + + return Token::NUMBER; +} + + +uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { + Advance(); + if (c0_ != 'u') return unibrow::Utf8::kBadChar; + Advance(); + uc32 c = ScanHexEscape('u', 4); + // We do not allow a unicode escape sequence to start another + // unicode escape sequence. + if (c == '\\') return unibrow::Utf8::kBadChar; + return c; +} + + +Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { + ASSERT(ScannerConstants::kIsIdentifierStart.get(c0_)); + LiteralScope literal(this, kLiteralIdentifier); + KeywordMatcher keyword_match; + // Scan identifier start character. + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier start characters. + if (!ScannerConstants::kIsIdentifierStart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); + return ScanIdentifierSuffix(&literal); + } + + uc32 first_char = c0_; + Advance(); + AddLiteralChar(first_char); + if (!keyword_match.AddChar(first_char)) { + return ScanIdentifierSuffix(&literal); + } + + // Scan the rest of the identifier characters. + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ != '\\') { + uc32 next_char = c0_; + Advance(); + AddLiteralChar(next_char); + if (keyword_match.AddChar(next_char)) continue; + } + // Fallthrough if no loner able to complete keyword. + return ScanIdentifierSuffix(&literal); + } + literal.Complete(); + + return keyword_match.token(); +} + + +Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { + // Scan the rest of the identifier characters. + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier part characters. + if (!ScannerConstants::kIsIdentifierPart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); + } else { + AddLiteralChar(c0_); + Advance(); + } + } + literal->Complete(); + + return Token::IDENTIFIER; +} + + +bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { + // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags + bool in_character_class = false; + + // Previous token is either '/' or '/=', in the second case, the + // pattern starts at =. + next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); + next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); + + // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, + // the scanner should pass uninterpreted bodies to the RegExp + // constructor. + LiteralScope literal(this, kLiteralRegExp); + if (seen_equal) + AddLiteralChar('='); + + while (c0_ != '/' || in_character_class) { + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; + if (c0_ == '\\') { // escaped character + AddLiteralCharAdvance(); + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; + AddLiteralCharAdvance(); + } else { // unescaped character + if (c0_ == '[') in_character_class = true; + if (c0_ == ']') in_character_class = false; + AddLiteralCharAdvance(); + } + } + Advance(); // consume '/' + + literal.Complete(); + + return true; +} + + +bool JavaScriptScanner::ScanRegExpFlags() { + // Scan regular expression flags. + LiteralScope literal(this, kLiteralRegExpFlags); + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { + // We allow any escaped character, unlike the restriction on + // IdentifierPart when it is used to build an IdentifierName. + AddLiteralChar(c); + continue; + } + } + AddLiteralCharAdvance(); + } + literal.Complete(); + + next_.location.end_pos = source_pos() - 1; + return true; +} + +// ---------------------------------------------------------------------------- // Keyword Matcher KeywordMatcher::FirstState KeywordMatcher::first_states_[] = { @@ -155,9 +885,7 @@ void KeywordMatcher::Step(unibrow::uchar input) { break; case IN: token_ = Token::IDENTIFIER; - if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) { - return; - } + if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) return; break; case N: if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return; diff --git a/deps/v8/src/scanner-base.h b/deps/v8/src/scanner-base.h index 50f30305c4..3714ae2d1b 100644 --- a/deps/v8/src/scanner-base.h +++ b/deps/v8/src/scanner-base.h @@ -37,11 +37,24 @@ #include "unicode-inl.h" #include "char-predicates.h" #include "utils.h" +#include "list-inl.h" namespace v8 { namespace internal { -// Interface through which the scanner reads characters from the input source. +// Returns the value (0 .. 15) of a hexadecimal character c. +// If c is not a legal hexadecimal character, returns a value < 0. +inline int HexValue(uc32 c) { + c -= '0'; + if (static_cast<unsigned>(c) <= 9) return c; + c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36. + if (static_cast<unsigned>(c) <= 5) return c + 10; + return -1; +} + +// ---------------------------------------------------------------------------- +// UTF16Buffer - scanner input source with pushback. + class UTF16Buffer { public: UTF16Buffer(); @@ -54,7 +67,11 @@ class UTF16Buffer { int pos() const { return pos_; } + static const int kNoEndPosition = 1; + protected: + // Initial value of end_ before the input stream is initialized. + int pos_; // Current position in the buffer. int end_; // Position where scanning should stop (EOF). }; @@ -79,6 +96,335 @@ class ScannerConstants : AllStatic { static StaticResource<Utf8Decoder> utf8_decoder_; }; +// ---------------------------------------------------------------------------- +// LiteralCollector - Collector of chars of literals. + +class LiteralCollector { + public: + LiteralCollector(); + ~LiteralCollector(); + + inline void AddChar(uc32 c) { + if (recording_) { + if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { + buffer_.Add(static_cast<char>(c)); + } else { + AddCharSlow(c); + } + } + } + + void StartLiteral() { + buffer_.StartSequence(); + recording_ = true; + } + + Vector<const char> EndLiteral() { + if (recording_) { + recording_ = false; + buffer_.Add(kEndMarker); + Vector<char> sequence = buffer_.EndSequence(); + return Vector<const char>(sequence.start(), sequence.length()); + } + return Vector<const char>(); + } + + void DropLiteral() { + if (recording_) { + recording_ = false; + buffer_.DropSequence(); + } + } + + void Reset() { + buffer_.Reset(); + } + + // The end marker added after a parsed literal. + // Using zero allows the usage of strlen and similar functions on + // identifiers and numbers (but not strings, since they may contain zero + // bytes). + static const char kEndMarker = '\x00'; + private: + static const int kInitialCapacity = 256; + SequenceCollector<char, 4> buffer_; + bool recording_; + void AddCharSlow(uc32 c); +}; + +// ---------------------------------------------------------------------------- +// Scanner base-class. + +// Generic functionality used by both JSON and JavaScript scanners. +class Scanner { + public: + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + class LiteralScope { + public: + explicit LiteralScope(Scanner* self); + ~LiteralScope(); + void Complete(); + + private: + Scanner* scanner_; + bool complete_; + }; + + Scanner(); + + // Returns the current token again. + Token::Value current_token() { return current_.token; } + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return next_.token; } + + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + int beg_pos; + int end_pos; + }; + + // Returns the location information for the current token + // (the token returned by Next()). + Location location() const { return current_.location; } + Location peek_location() const { return next_.location; } + + // Returns the literal string, if any, for the current token (the + // token returned by Next()). The string is 0-terminated and in + // UTF-8 format; they may contain 0-characters. Literal strings are + // collected for identifiers, strings, and numbers. + // These functions only give the correct result if the literal + // was scanned between calls to StartLiteral() and TerminateLiteral(). + const char* literal_string() const { + return current_.literal_chars.start(); + } + + int literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return current_.literal_chars.length() - 1; + } + + Vector<const char> literal() const { + return Vector<const char>(literal_string(), literal_length()); + } + + // Returns the literal string for the next token (the token that + // would be returned if Next() were called). + const char* next_literal_string() const { + return next_.literal_chars.start(); + } + + + // Returns the length of the next token (that would be returned if + // Next() were called). + int next_literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return next_.literal_chars.length() - 1; + } + + Vector<const char> next_literal() const { + return Vector<const char>(next_literal_string(), next_literal_length()); + } + + bool stack_overflow() { return stack_overflow_; } + + static const int kCharacterLookaheadBufferSize = 1; + + protected: + // The current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + Vector<const char> literal_chars; + }; + + // Call this after setting source_ to the input. + void Init() { + // Set c0_ (one character ahead) + ASSERT(kCharacterLookaheadBufferSize == 1); + Advance(); + // Initialize current_ to not refer to a literal. + current_.literal_chars = Vector<const char>(); + // Reset literal buffer. + literal_buffer_.Reset(); + } + + // Literal buffer support + inline void StartLiteral() { + literal_buffer_.StartLiteral(); + } + + inline void AddLiteralChar(uc32 c) { + literal_buffer_.AddChar(c); + } + + // Complete scanning of a literal. + inline void TerminateLiteral() { + next_.literal_chars = literal_buffer_.EndLiteral(); + } + + // Stops scanning of a literal and drop the collected characters, + // e.g., due to an encountered error. + inline void DropLiteral() { + literal_buffer_.DropLiteral(); + } + + inline void AddLiteralCharAdvance() { + AddLiteralChar(c0_); + Advance(); + } + + // Low-level scanning support. + void Advance() { c0_ = source_->Advance(); } + void PushBack(uc32 ch) { + source_->PushBack(ch); + c0_ = ch; + } + + inline Token::Value Select(Token::Value tok) { + Advance(); + return tok; + } + + inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) { + Advance(); + if (c0_ == next) { + Advance(); + return then; + } else { + return else_; + } + } + + uc32 ScanHexEscape(uc32 c, int length); + uc32 ScanOctalEscape(uc32 c, int length); + + // Return the current source position. + int source_pos() { + return source_->pos() - kCharacterLookaheadBufferSize; + } + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + + // Input stream. Must be initialized to an UTF16Buffer. + UTF16Buffer* source_; + + // Buffer to hold literal values (identifiers, strings, numbers) + // using '\x00'-terminated UTF-8 encoding. Handles allocation internally. + LiteralCollector literal_buffer_; + + bool stack_overflow_; + + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; +}; + +// ---------------------------------------------------------------------------- +// JavaScriptScanner - base logic for JavaScript scanning. + +class JavaScriptScanner : public Scanner { + public: + + // Bit vector representing set of types of literals. + enum LiteralType { + kNoLiterals = 0, + kLiteralNumber = 1, + kLiteralIdentifier = 2, + kLiteralString = 4, + kLiteralRegExp = 8, + kLiteralRegExpFlags = 16, + kAllLiterals = 31 + }; + + // A LiteralScope that disables recording of some types of JavaScript + // literals. If the scanner is configured to not record the specific + // type of literal, the scope will not call StartLiteral. + class LiteralScope { + public: + LiteralScope(JavaScriptScanner* self, LiteralType type) + : scanner_(self), complete_(false) { + if (scanner_->RecordsLiteral(type)) { + scanner_->StartLiteral(); + } + } + ~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); + } + void Complete() { + scanner_->TerminateLiteral(); + complete_ = true; + } + + private: + JavaScriptScanner* scanner_; + bool complete_; + }; + + JavaScriptScanner(); + + // Returns the next token. + Token::Value Next(); + + // Returns true if there was a line terminator before the peek'ed token. + bool has_line_terminator_before_next() const { + return has_line_terminator_before_next_; + } + + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Returns true if regexp flags are scanned (always since flags can + // be empty). + bool ScanRegExpFlags(); + + // Tells whether the buffer contains an identifier (no escapes). + // Used for checking if a property name is an identifier. + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + // Whether this scanner records the given literal type or not. + bool RecordsLiteral(LiteralType type) { + return (literal_flags_ & type) != 0; + } + + protected: + bool SkipWhiteSpace(); + Token::Value SkipSingleLineComment(); + Token::Value SkipMultiLineComment(); + + // Scans a single JavaScript token. + void Scan(); + + void ScanDecimalDigits(); + Token::Value ScanNumber(bool seen_period); + Token::Value ScanIdentifierOrKeyword(); + Token::Value ScanIdentifierSuffix(LiteralScope* literal); + + void ScanEscape(); + Token::Value ScanString(); + + // Scans a possible HTML comment -- begins with '<!'. + Token::Value ScanHtmlComment(); + + // Decodes a unicode escape-sequence which is part of an identifier. + // If the escape sequence cannot be decoded the result is kBadChar. + uc32 ScanIdentifierUnicodeEscape(); + + int literal_flags_; + bool has_line_terminator_before_next_; +}; + + +// ---------------------------------------------------------------------------- +// Keyword matching state machine. class KeywordMatcher { // Incrementally recognize keywords. @@ -101,10 +447,11 @@ class KeywordMatcher { Token::Value token() { return token_; } - inline void AddChar(unibrow::uchar input) { + inline bool AddChar(unibrow::uchar input) { if (state_ != UNMATCHABLE) { Step(input); } + return state_ != UNMATCHABLE; } void Fail() { @@ -155,23 +502,23 @@ class KeywordMatcher { const char* keyword, int position, Token::Value token_if_match) { - if (input == static_cast<unibrow::uchar>(keyword[position])) { - state_ = KEYWORD_PREFIX; - this->keyword_ = keyword; - this->counter_ = position + 1; - this->keyword_token_ = token_if_match; - return true; + if (input != static_cast<unibrow::uchar>(keyword[position])) { + return false; } - return false; + state_ = KEYWORD_PREFIX; + this->keyword_ = keyword; + this->counter_ = position + 1; + this->keyword_token_ = token_if_match; + return true; } // If input equals match character, transition to new state and return true. inline bool MatchState(unibrow::uchar input, char match, State new_state) { - if (input == static_cast<unibrow::uchar>(match)) { - state_ = new_state; - return true; + if (input != static_cast<unibrow::uchar>(match)) { + return false; } - return false; + state_ = new_state; + return true; } inline bool MatchKeyword(unibrow::uchar input, diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc index 6b2fcb4c5e..63b2fd807d 100755 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -36,35 +36,8 @@ namespace v8 { namespace internal { // ---------------------------------------------------------------------------- -// UTF8Buffer - -UTF8Buffer::UTF8Buffer() : buffer_(kInitialCapacity), recording_(false) { } - - -UTF8Buffer::~UTF8Buffer() {} - - -void UTF8Buffer::AddCharSlow(uc32 c) { - ASSERT(static_cast<unsigned>(c) > unibrow::Utf8::kMaxOneByteChar); - int length = unibrow::Utf8::Length(c); - Vector<char> block = buffer_.AddBlock(length, '\0'); -#ifdef DEBUG - int written_length = unibrow::Utf8::Encode(block.start(), c); - CHECK_EQ(length, written_length); -#else - unibrow::Utf8::Encode(block.start(), c); -#endif -} - - -// ---------------------------------------------------------------------------- // UTF16Buffer - -UTF16Buffer::UTF16Buffer() - : pos_(0), end_(Scanner::kNoEndPosition) { } - - // CharacterStreamUTF16Buffer CharacterStreamUTF16Buffer::CharacterStreamUTF16Buffer() : pushback_buffer_(0), last_(0), stream_(NULL) { } @@ -78,7 +51,7 @@ void CharacterStreamUTF16Buffer::Initialize(Handle<String> data, if (start_position > 0) { SeekForward(start_position); } - end_ = end_position != Scanner::kNoEndPosition ? end_position : kMaxInt; + end_ = end_position != kNoEndPosition ? end_position : kMaxInt; } @@ -90,7 +63,7 @@ void CharacterStreamUTF16Buffer::PushBack(uc32 ch) { uc32 CharacterStreamUTF16Buffer::Advance() { - ASSERT(end_ != Scanner::kNoEndPosition); + ASSERT(end_ != kNoEndPosition); ASSERT(end_ >= 0); // NOTE: It is of importance to Persian / Farsi resources that we do // *not* strip format control characters in the scanner; see @@ -143,41 +116,74 @@ void Scanner::LiteralScope::Complete() { } // ---------------------------------------------------------------------------- -// Scanner - -Scanner::Scanner() - : has_line_terminator_before_next_(false), - is_parsing_json_(false), - source_(NULL), - stack_overflow_(false) {} +// V8JavaScriptScanner + +void V8JavaScriptScanner::Initialize(Handle<String> source, + int literal_flags) { + source_ = stream_initializer_.Init(source, NULL, 0, source->length()); + // Need to capture identifiers in order to recognize "get" and "set" + // in object literals. + literal_flags_ = literal_flags | kLiteralIdentifier; + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); +} -void Scanner::Initialize(Handle<String> source, - ParserLanguage language) { - Init(source, NULL, 0, source->length(), language); +void V8JavaScriptScanner::Initialize(Handle<String> source, + unibrow::CharacterStream* stream, + int literal_flags) { + source_ = stream_initializer_.Init(source, stream, + 0, UTF16Buffer::kNoEndPosition); + literal_flags_ = literal_flags | kLiteralIdentifier; + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); } -void Scanner::Initialize(Handle<String> source, - unibrow::CharacterStream* stream, - ParserLanguage language) { - Init(source, stream, 0, kNoEndPosition, language); +void V8JavaScriptScanner::Initialize(Handle<String> source, + int start_position, + int end_position, + int literal_flags) { + source_ = stream_initializer_.Init(source, NULL, + start_position, end_position); + literal_flags_ = literal_flags | kLiteralIdentifier; + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); } -void Scanner::Initialize(Handle<String> source, - int start_position, - int end_position, - ParserLanguage language) { - Init(source, NULL, start_position, end_position, language); +Token::Value V8JavaScriptScanner::NextCheckStack() { + // BUG 1215673: Find a thread safe way to set a stack limit in + // pre-parse mode. Otherwise, we cannot safely pre-parse from other + // threads. + StackLimitCheck check; + if (check.HasOverflowed()) { + stack_overflow_ = true; + current_ = next_; + next_.token = Token::ILLEGAL; + return current_.token; + } else { + return Next(); + } } -void Scanner::Init(Handle<String> source, - unibrow::CharacterStream* stream, - int start_position, - int end_position, - ParserLanguage language) { +UTF16Buffer* StreamInitializer::Init(Handle<String> source, + unibrow::CharacterStream* stream, + int start_position, + int end_position) { // Either initialize the scanner from a character stream or from a // string. ASSERT(source.is_null() || stream == NULL); @@ -188,13 +194,13 @@ void Scanner::Init(Handle<String> source, Handle<ExternalTwoByteString>::cast(source), start_position, end_position); - source_ = &two_byte_string_buffer_; + return &two_byte_string_buffer_; } else if (!source.is_null() && StringShape(*source).IsExternalAscii()) { ascii_string_buffer_.Initialize( Handle<ExternalAsciiString>::cast(source), start_position, end_position); - source_ = &ascii_string_buffer_; + return &ascii_string_buffer_; } else { if (!source.is_null()) { safe_string_input_buffer_.Reset(source.location()); @@ -204,28 +210,27 @@ void Scanner::Init(Handle<String> source, stream, start_position, end_position); - source_ = &char_stream_buffer_; + return &char_stream_buffer_; } +} - is_parsing_json_ = (language == JSON); +// ---------------------------------------------------------------------------- +// JsonScanner - // Set c0_ (one character ahead) - ASSERT(kCharacterLookaheadBufferSize == 1); - Advance(); - // Initialize current_ to not refer to a literal. - current_.literal_chars = Vector<const char>(); - // Reset literal buffer. - literal_buffer_.Reset(); +JsonScanner::JsonScanner() {} - // Skip initial whitespace allowing HTML comment ends just like - // after a newline and scan first token. - has_line_terminator_before_next_ = true; - SkipWhiteSpace(); - Scan(); + +void JsonScanner::Initialize(Handle<String> source) { + source_ = stream_initializer_.Init(source, NULL, 0, source->length()); + Init(); + // Skip initial whitespace. + SkipJsonWhiteSpace(); + // Preload first token as look-ahead. + ScanJson(); } -Token::Value Scanner::Next() { +Token::Value JsonScanner::Next() { // BUG 1215673: Find a thread safe way to set a stack limit in // pre-parse mode. Otherwise, we cannot safely pre-parse from other // threads. @@ -236,52 +241,13 @@ Token::Value Scanner::Next() { stack_overflow_ = true; next_.token = Token::ILLEGAL; } else { - has_line_terminator_before_next_ = false; - Scan(); + ScanJson(); } return current_.token; } -void Scanner::StartLiteral() { - literal_buffer_.StartLiteral(); -} - - -void Scanner::AddLiteralChar(uc32 c) { - literal_buffer_.AddChar(c); -} - - -void Scanner::TerminateLiteral() { - next_.literal_chars = literal_buffer_.EndLiteral(); -} - - -void Scanner::DropLiteral() { - literal_buffer_.DropLiteral(); -} - - -void Scanner::AddLiteralCharAdvance() { - AddLiteralChar(c0_); - Advance(); -} - - -static inline bool IsByteOrderMark(uc32 c) { - // The Unicode value U+FFFE is guaranteed never to be assigned as a - // Unicode character; this implies that in a Unicode context the - // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF - // character expressed in little-endian byte order (since it could - // not be a U+FFFE character expressed in big-endian byte - // order). Nevertheless, we check for it to be compatible with - // Spidermonkey. - return c == 0xFEFF || c == 0xFFFE; -} - - -bool Scanner::SkipJsonWhiteSpace() { +bool JsonScanner::SkipJsonWhiteSpace() { int start_position = source_pos(); // JSON WhiteSpace is tab, carrige-return, newline and space. while (c0_ == ' ' || c0_ == '\n' || c0_ == '\r' || c0_ == '\t') { @@ -291,107 +257,9 @@ bool Scanner::SkipJsonWhiteSpace() { } -bool Scanner::SkipJavaScriptWhiteSpace() { - int start_position = source_pos(); - - while (true) { - // We treat byte-order marks (BOMs) as whitespace for better - // compatibility with Spidermonkey and other JavaScript engines. - while (ScannerConstants::kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { - // IsWhiteSpace() includes line terminators! - if (ScannerConstants::kIsLineTerminator.get(c0_)) { - // Ignore line terminators, but remember them. This is necessary - // for automatic semicolon insertion. - has_line_terminator_before_next_ = true; - } - Advance(); - } - - // If there is an HTML comment end '-->' at the beginning of a - // line (with only whitespace in front of it), we treat the rest - // of the line as a comment. This is in line with the way - // SpiderMonkey handles it. - if (c0_ == '-' && has_line_terminator_before_next_) { - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>') { - // Treat the rest of the line as a comment. - SkipSingleLineComment(); - // Continue skipping white space after the comment. - continue; - } - PushBack('-'); // undo Advance() - } - PushBack('-'); // undo Advance() - } - // Return whether or not we skipped any characters. - return source_pos() != start_position; - } -} - - -Token::Value Scanner::SkipSingleLineComment() { - Advance(); - - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar (see - // ECMA-262, section 7.4, page 12). - while (c0_ >= 0 && !ScannerConstants::kIsLineTerminator.get(c0_)) { - Advance(); - } - - return Token::WHITESPACE; -} - - -Token::Value Scanner::SkipMultiLineComment() { - ASSERT(c0_ == '*'); - Advance(); - - while (c0_ >= 0) { - char ch = c0_; - Advance(); - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace - even the ones - // containing line terminators. This contradicts ECMA-262, section - // 7.4, page 12, that says that multi-line comments containing - // line terminators should be treated as a line terminator, but it - // matches the behaviour of SpiderMonkey and KJS. - if (ch == '*' && c0_ == '/') { - c0_ = ' '; - return Token::WHITESPACE; - } - } - - // Unterminated multi-line comment. - return Token::ILLEGAL; -} - - -Token::Value Scanner::ScanHtmlComment() { - // Check for <!-- comments. - ASSERT(c0_ == '!'); - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '-') return SkipSingleLineComment(); - PushBack('-'); // undo Advance() - } - PushBack('!'); // undo Advance() - ASSERT(c0_ == '!'); - return Token::LT; -} - - - -void Scanner::ScanJson() { +void JsonScanner::ScanJson() { next_.literal_chars = Vector<const char>(); Token::Value token; - has_line_terminator_before_next_ = false; do { // Remember the position of the next token next_.location.beg_pos = source_pos(); @@ -468,7 +336,7 @@ void Scanner::ScanJson() { } -Token::Value Scanner::ScanJsonString() { +Token::Value JsonScanner::ScanJsonString() { ASSERT_EQ('"', c0_); Advance(); LiteralScope literal(this); @@ -528,7 +396,7 @@ Token::Value Scanner::ScanJsonString() { } -Token::Value Scanner::ScanJsonNumber() { +Token::Value JsonScanner::ScanJsonNumber() { LiteralScope literal(this); if (c0_ == '-') AddLiteralCharAdvance(); if (c0_ == '0') { @@ -562,8 +430,8 @@ Token::Value Scanner::ScanJsonNumber() { } -Token::Value Scanner::ScanJsonIdentifier(const char* text, - Token::Value token) { +Token::Value JsonScanner::ScanJsonIdentifier(const char* text, + Token::Value token) { LiteralScope literal(this); while (*text != '\0') { if (c0_ != *text) return Token::ILLEGAL; @@ -576,577 +444,5 @@ Token::Value Scanner::ScanJsonIdentifier(const char* text, } -void Scanner::ScanJavaScript() { - next_.literal_chars = Vector<const char>(); - Token::Value token; - do { - // Remember the position of the next token - next_.location.beg_pos = source_pos(); - - switch (c0_) { - case ' ': - case '\t': - Advance(); - token = Token::WHITESPACE; - break; - - case '\n': - Advance(); - has_line_terminator_before_next_ = true; - token = Token::WHITESPACE; - break; - - case '"': case '\'': - token = ScanString(); - break; - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') { - token = Select(Token::LTE); - } else if (c0_ == '<') { - token = Select('=', Token::ASSIGN_SHL, Token::SHL); - } else if (c0_ == '!') { - token = ScanHtmlComment(); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::GTE); - } else if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::ASSIGN_SAR); - } else if (c0_ == '>') { - token = Select('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == === - Advance(); - if (c0_ == '=') { - token = Select('=', Token::EQ_STRICT, Token::EQ); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') { - token = Select('=', Token::NE_STRICT, Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') { - token = Select(Token::INC); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- --> -= - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>' && has_line_terminator_before_next_) { - // For compatibility with SpiderMonkey, we skip lines that - // start with an HTML comment end '-->'. - token = SkipSingleLineComment(); - } else { - token = Token::DEC; - } - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - token = Select('=', Token::ASSIGN_MUL, Token::MUL); - break; - - case '%': - // % %= - token = Select('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') { - token = SkipSingleLineComment(); - } else if (c0_ == '*') { - token = SkipMultiLineComment(); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') { - token = Select(Token::AND); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') { - token = Select(Token::OR); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - Advance(); - if (IsDecimalDigit(c0_)) { - token = ScanNumber(true); - } else { - token = Token::PERIOD; - } - break; - - case ':': - token = Select(Token::COLON); - break; - - case ';': - token = Select(Token::SEMICOLON); - break; - - case ',': - token = Select(Token::COMMA); - break; - - case '(': - token = Select(Token::LPAREN); - break; - - case ')': - token = Select(Token::RPAREN); - break; - - case '[': - token = Select(Token::LBRACK); - break; - - case ']': - token = Select(Token::RBRACK); - break; - - case '{': - token = Select(Token::LBRACE); - break; - - case '}': - token = Select(Token::RBRACE); - break; - - case '?': - token = Select(Token::CONDITIONAL); - break; - - case '~': - token = Select(Token::BIT_NOT); - break; - - default: - if (ScannerConstants::kIsIdentifierStart.get(c0_)) { - token = ScanIdentifier(); - } else if (IsDecimalDigit(c0_)) { - token = ScanNumber(false); - } else if (SkipWhiteSpace()) { - token = Token::WHITESPACE; - } else if (c0_ < 0) { - token = Token::EOS; - } else { - token = Select(Token::ILLEGAL); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == Token::WHITESPACE); - - next_.location.end_pos = source_pos(); - next_.token = token; -} - - -void Scanner::SeekForward(int pos) { - source_->SeekForward(pos - 1); - Advance(); - // This function is only called to seek to the location - // of the end of a function (at the "}" token). It doesn't matter - // whether there was a line terminator in the part we skip. - has_line_terminator_before_next_ = false; - Scan(); -} - - -uc32 Scanner::ScanHexEscape(uc32 c, int length) { - ASSERT(length <= 4); // prevent overflow - - uc32 digits[4]; - uc32 x = 0; - for (int i = 0; i < length; i++) { - digits[i] = c0_; - int d = HexValue(c0_); - if (d < 0) { - // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes - // should be illegal, but other JS VMs just return the - // non-escaped version of the original character. - - // Push back digits read, except the last one (in c0_). - for (int j = i-1; j >= 0; j--) { - PushBack(digits[j]); - } - // Notice: No handling of error - treat it as "\u"->"u". - return c; - } - x = x * 16 + d; - Advance(); - } - - return x; -} - - -// Octal escapes of the forms '\0xx' and '\xxx' are not a part of -// ECMA-262. Other JS VMs support them. -uc32 Scanner::ScanOctalEscape(uc32 c, int length) { - uc32 x = c - '0'; - for (int i = 0; i < length; i++) { - int d = c0_ - '0'; - if (d < 0 || d > 7) break; - int nx = x * 8 + d; - if (nx >= 256) break; - x = nx; - Advance(); - } - return x; -} - - -void Scanner::ScanEscape() { - uc32 c = c0_; - Advance(); - - // Skip escaped newlines. - if (ScannerConstants::kIsLineTerminator.get(c)) { - // Allow CR+LF newlines in multiline string literals. - if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); - // Allow LF+CR newlines in multiline string literals. - if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); - return; - } - - switch (c) { - case '\'': // fall through - case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : c = ScanHexEscape(c, 4); break; - case 'v' : c = '\v'; break; - case 'x' : c = ScanHexEscape(c, 2); break; - case '0' : // fall through - case '1' : // fall through - case '2' : // fall through - case '3' : // fall through - case '4' : // fall through - case '5' : // fall through - case '6' : // fall through - case '7' : c = ScanOctalEscape(c, 2); break; - } - - // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these - // should be illegal, but they are commonly handled - // as non-escaped characters by JS VMs. - AddLiteralChar(c); -} - - -Token::Value Scanner::ScanString() { - uc32 quote = c0_; - Advance(); // consume quote - - LiteralScope literal(this); - while (c0_ != quote && c0_ >= 0 - && !ScannerConstants::kIsLineTerminator.get(c0_)) { - uc32 c = c0_; - Advance(); - if (c == '\\') { - if (c0_ < 0) return Token::ILLEGAL; - ScanEscape(); - } else { - AddLiteralChar(c); - } - } - if (c0_ != quote) return Token::ILLEGAL; - literal.Complete(); - - Advance(); // consume quote - return Token::STRING; -} - - -Token::Value Scanner::Select(Token::Value tok) { - Advance(); - return tok; -} - - -Token::Value Scanner::Select(uc32 next, Token::Value then, Token::Value else_) { - Advance(); - if (c0_ == next) { - Advance(); - return then; - } else { - return else_; - } -} - - -// Returns true if any decimal digits were scanned, returns false otherwise. -void Scanner::ScanDecimalDigits() { - while (IsDecimalDigit(c0_)) - AddLiteralCharAdvance(); -} - - -Token::Value Scanner::ScanNumber(bool seen_period) { - ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction - - enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; - - LiteralScope literal(this); - if (seen_period) { - // we have already seen a decimal point of the float - AddLiteralChar('.'); - ScanDecimalDigits(); // we know we have at least one digit - - } else { - // if the first character is '0' we must check for octals and hex - if (c0_ == '0') { - AddLiteralCharAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number - if (c0_ == 'x' || c0_ == 'X') { - // hex number - kind = HEX; - AddLiteralCharAdvance(); - if (!IsHexDigit(c0_)) { - // we must have at least one hex digit after 'x'/'X' - return Token::ILLEGAL; - } - while (IsHexDigit(c0_)) { - AddLiteralCharAdvance(); - } - } else if ('0' <= c0_ && c0_ <= '7') { - // (possible) octal number - kind = OCTAL; - while (true) { - if (c0_ == '8' || c0_ == '9') { - kind = DECIMAL; - break; - } - if (c0_ < '0' || '7' < c0_) break; - AddLiteralCharAdvance(); - } - } - } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - ScanDecimalDigits(); // optional - if (c0_ == '.') { - AddLiteralCharAdvance(); - ScanDecimalDigits(); // optional - } - } - } - - // scan exponent, if any - if (c0_ == 'e' || c0_ == 'E') { - ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed - // scan exponent - AddLiteralCharAdvance(); - if (c0_ == '+' || c0_ == '-') - AddLiteralCharAdvance(); - if (!IsDecimalDigit(c0_)) { - // we must have at least one decimal digit after 'e'/'E' - return Token::ILLEGAL; - } - ScanDecimalDigits(); - } - - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (IsDecimalDigit(c0_) || ScannerConstants::kIsIdentifierStart.get(c0_)) - return Token::ILLEGAL; - - literal.Complete(); - - return Token::NUMBER; -} - - -uc32 Scanner::ScanIdentifierUnicodeEscape() { - Advance(); - if (c0_ != 'u') return unibrow::Utf8::kBadChar; - Advance(); - uc32 c = ScanHexEscape('u', 4); - // We do not allow a unicode escape sequence to start another - // unicode escape sequence. - if (c == '\\') return unibrow::Utf8::kBadChar; - return c; -} - - -Token::Value Scanner::ScanIdentifier() { - ASSERT(ScannerConstants::kIsIdentifierStart.get(c0_)); - - LiteralScope literal(this); - KeywordMatcher keyword_match; - - // Scan identifier start character. - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier start characters. - if (!ScannerConstants::kIsIdentifierStart.get(c)) return Token::ILLEGAL; - AddLiteralChar(c); - keyword_match.Fail(); - } else { - AddLiteralChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - - // Scan the rest of the identifier characters. - while (ScannerConstants::kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier part characters. - if (!ScannerConstants::kIsIdentifierPart.get(c)) return Token::ILLEGAL; - AddLiteralChar(c); - keyword_match.Fail(); - } else { - AddLiteralChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - } - literal.Complete(); - - return keyword_match.token(); -} - - - -bool Scanner::ScanRegExpPattern(bool seen_equal) { - // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags - bool in_character_class = false; - - // Previous token is either '/' or '/=', in the second case, the - // pattern starts at =. - next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); - next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); - - // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, - // the scanner should pass uninterpreted bodies to the RegExp - // constructor. - LiteralScope literal(this); - if (seen_equal) - AddLiteralChar('='); - - while (c0_ != '/' || in_character_class) { - if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; - if (c0_ == '\\') { // escaped character - AddLiteralCharAdvance(); - if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; - AddLiteralCharAdvance(); - } else { // unescaped character - if (c0_ == '[') in_character_class = true; - if (c0_ == ']') in_character_class = false; - AddLiteralCharAdvance(); - } - } - Advance(); // consume '/' - - literal.Complete(); - - return true; -} - -bool Scanner::ScanRegExpFlags() { - // Scan regular expression flags. - LiteralScope literal(this); - while (ScannerConstants::kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { - // We allow any escaped character, unlike the restriction on - // IdentifierPart when it is used to build an IdentifierName. - AddLiteralChar(c); - continue; - } - } - AddLiteralCharAdvance(); - } - literal.Complete(); - - next_.location.end_pos = source_pos() - 1; - return true; -} } } // namespace v8::internal diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index df5cd72949..acb9b47bd9 100644 --- a/deps/v8/src/scanner.h +++ b/deps/v8/src/scanner.h @@ -35,65 +35,6 @@ namespace v8 { namespace internal { - -class UTF8Buffer { - public: - UTF8Buffer(); - ~UTF8Buffer(); - - inline void AddChar(uc32 c) { - if (recording_) { - if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { - buffer_.Add(static_cast<char>(c)); - } else { - AddCharSlow(c); - } - } - } - - void StartLiteral() { - buffer_.StartSequence(); - recording_ = true; - } - - Vector<const char> EndLiteral() { - if (recording_) { - recording_ = false; - buffer_.Add(kEndMarker); - Vector<char> sequence = buffer_.EndSequence(); - return Vector<const char>(sequence.start(), sequence.length()); - } - return Vector<const char>(); - } - - void DropLiteral() { - if (recording_) { - recording_ = false; - buffer_.DropSequence(); - } - } - - void Reset() { - buffer_.Reset(); - } - - // The end marker added after a parsed literal. - // Using zero allows the usage of strlen and similar functions on - // identifiers and numbers (but not strings, since they may contain zero - // bytes). - // TODO(lrn): Use '\xff' as end marker, since it cannot occur inside - // an utf-8 string. This requires changes in all places that uses - // str-functions on the literals, but allows a single pointer to represent - // the literal, even if it contains embedded zeros. - static const char kEndMarker = '\x00'; - private: - static const int kInitialCapacity = 256; - SequenceCollector<char, 4> buffer_; - bool recording_; - void AddCharSlow(uc32 c); -}; - - // UTF16 buffer to read characters from a character stream. class CharacterStreamUTF16Buffer: public UTF16Buffer { public: @@ -134,175 +75,65 @@ class ExternalStringUTF16Buffer: public UTF16Buffer { }; -enum ParserLanguage { JAVASCRIPT, JSON }; - - -class Scanner { +// Initializes a UTF16Buffer as input stream, using one of a number +// of strategies depending on the available character sources. +class StreamInitializer { public: - typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + UTF16Buffer* Init(Handle<String> source, + unibrow::CharacterStream* stream, + int start_position, + int end_position); + private: + // Different UTF16 buffers used to pull characters from. Based on input one of + // these will be initialized as the actual data source. + CharacterStreamUTF16Buffer char_stream_buffer_; + ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> + two_byte_string_buffer_; + ExternalStringUTF16Buffer<ExternalAsciiString, char> ascii_string_buffer_; - class LiteralScope { - public: - explicit LiteralScope(Scanner* self); - ~LiteralScope(); - void Complete(); + // Used to convert the source string into a character stream when a stream + // is not passed to the scanner. + SafeStringInputBuffer safe_string_input_buffer_; +}; - private: - Scanner* scanner_; - bool complete_; - }; +// ---------------------------------------------------------------------------- +// V8JavaScriptScanner +// JavaScript scanner getting its input from either a V8 String or a unicode +// CharacterStream. - Scanner(); +class V8JavaScriptScanner : public JavaScriptScanner { + public: + V8JavaScriptScanner() {} + + Token::Value NextCheckStack(); // Initialize the Scanner to scan source. - void Initialize(Handle<String> source, - ParserLanguage language); + void Initialize(Handle<String> source, int literal_flags = kAllLiterals); void Initialize(Handle<String> source, unibrow::CharacterStream* stream, - ParserLanguage language); + int literal_flags = kAllLiterals); void Initialize(Handle<String> source, int start_position, int end_position, - ParserLanguage language); - - // Returns the next token. - Token::Value Next(); - - // Returns the current token again. - Token::Value current_token() { return current_.token; } - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return next_.token; } - - // Returns true if there was a line terminator before the peek'ed token. - bool has_line_terminator_before_next() const { - return has_line_terminator_before_next_; - } - - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - int beg_pos; - int end_pos; - }; - - // Returns the location information for the current token - // (the token returned by Next()). - Location location() const { return current_.location; } - Location peek_location() const { return next_.location; } - - // Returns the literal string, if any, for the current token (the - // token returned by Next()). The string is 0-terminated and in - // UTF-8 format; they may contain 0-characters. Literal strings are - // collected for identifiers, strings, and numbers. - // These functions only give the correct result if the literal - // was scanned between calls to StartLiteral() and TerminateLiteral(). - const char* literal_string() const { - return current_.literal_chars.start(); - } - - int literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return current_.literal_chars.length() - 1; - } - - Vector<const char> literal() const { - return Vector<const char>(literal_string(), literal_length()); - } - - // Returns the literal string for the next token (the token that - // would be returned if Next() were called). - const char* next_literal_string() const { - return next_.literal_chars.start(); - } - - - // Returns the length of the next token (that would be returned if - // Next() were called). - int next_literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return next_.literal_chars.length() - 1; - } - - Vector<const char> next_literal() const { - return Vector<const char>(next_literal_string(), next_literal_length()); - } - - // Scans the input as a regular expression pattern, previous - // character(s) must be /(=). Returns true if a pattern is scanned. - bool ScanRegExpPattern(bool seen_equal); - // Returns true if regexp flags are scanned (always since flags can - // be empty). - bool ScanRegExpFlags(); + int literal_flags = kAllLiterals); - // Seek forward to the given position. This operation does not - // work in general, for instance when there are pushed back - // characters, but works for seeking forward until simple delimiter - // tokens, which is what it is used for. - void SeekForward(int pos); - - bool stack_overflow() { return stack_overflow_; } + protected: + StreamInitializer stream_initializer_; +}; - // Tells whether the buffer contains an identifier (no escapes). - // Used for checking if a property name is an identifier. - static bool IsIdentifier(unibrow::CharacterStream* buffer); - static const int kCharacterLookaheadBufferSize = 1; - static const int kNoEndPosition = 1; +class JsonScanner : public Scanner { + public: + JsonScanner(); - private: - // The current and look-ahead token. - struct TokenDesc { - Token::Value token; - Location location; - Vector<const char> literal_chars; - }; - - void Init(Handle<String> source, - unibrow::CharacterStream* stream, - int start_position, int end_position, - ParserLanguage language); - - // Literal buffer support - inline void StartLiteral(); - inline void AddLiteralChar(uc32 ch); - inline void AddLiteralCharAdvance(); - inline void TerminateLiteral(); - // Stops scanning of a literal, e.g., due to an encountered error. - inline void DropLiteral(); - - // Low-level scanning support. - void Advance() { c0_ = source_->Advance(); } - void PushBack(uc32 ch) { - source_->PushBack(ch); - c0_ = ch; - } + // Initialize the Scanner to scan source. + void Initialize(Handle<String> source); - bool SkipWhiteSpace() { - if (is_parsing_json_) { - return SkipJsonWhiteSpace(); - } else { - return SkipJavaScriptWhiteSpace(); - } - } + // Returns the next token. + Token::Value Next(); - bool SkipJavaScriptWhiteSpace(); + protected: + // Skip past JSON whitespace (only space, tab, newline and carrige-return). bool SkipJsonWhiteSpace(); - Token::Value SkipSingleLineComment(); - Token::Value SkipMultiLineComment(); - - inline Token::Value Select(Token::Value tok); - inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_); - - inline void Scan() { - if (is_parsing_json_) { - ScanJson(); - } else { - ScanJavaScript(); - } - } - - // Scans a single JavaScript token. - void ScanJavaScript(); // Scan a single JSON token. The JSON lexical grammar is specified in the // ECMAScript 5 standard, section 15.12.1.1. @@ -331,53 +162,7 @@ class Scanner { // JSONNullLiteral). Token::Value ScanJsonIdentifier(const char* text, Token::Value token); - void ScanDecimalDigits(); - Token::Value ScanNumber(bool seen_period); - Token::Value ScanIdentifier(); - uc32 ScanHexEscape(uc32 c, int length); - uc32 ScanOctalEscape(uc32 c, int length); - void ScanEscape(); - Token::Value ScanString(); - - // Scans a possible HTML comment -- begins with '<!'. - Token::Value ScanHtmlComment(); - - // Return the current source position. - int source_pos() { - return source_->pos() - kCharacterLookaheadBufferSize; - } - - // Decodes a unicode escape-sequence which is part of an identifier. - // If the escape sequence cannot be decoded the result is kBadRune. - uc32 ScanIdentifierUnicodeEscape(); - - TokenDesc current_; // desc for current token (as returned by Next()) - TokenDesc next_; // desc for next token (one token look-ahead) - bool has_line_terminator_before_next_; - bool is_parsing_json_; - - // Different UTF16 buffers used to pull characters from. Based on input one of - // these will be initialized as the actual data source. - CharacterStreamUTF16Buffer char_stream_buffer_; - ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> - two_byte_string_buffer_; - ExternalStringUTF16Buffer<ExternalAsciiString, char> ascii_string_buffer_; - - // Source. Will point to one of the buffers declared above. - UTF16Buffer* source_; - - // Used to convert the source string into a character stream when a stream - // is not passed to the scanner. - SafeStringInputBuffer safe_string_input_buffer_; - - // Buffer to hold literal values (identifiers, strings, numbers) - // using '\x00'-terminated UTF-8 encoding. Handles allocation internally. - UTF8Buffer literal_buffer_; - - bool stack_overflow_; - - // One Unicode character look-ahead; c0_ < 0 at the end of the input. - uc32 c0_; + StreamInitializer stream_initializer_; }; @@ -400,7 +185,7 @@ void ExternalStringUTF16Buffer<StringType, CharType>::Initialize( SeekForward(start_position); } end_ = - end_position != Scanner::kNoEndPosition ? end_position : data->length(); + end_position != kNoEndPosition ? end_position : data->length(); } diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index b7c769c7a2..5cc009f723 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -960,14 +960,11 @@ void StubCache::Clear() { MaybeObject* LoadCallbackProperty(Arguments args) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); - AccessorInfo* callback = AccessorInfo::cast(args[2]); + AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData<Address>(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address); ASSERT(fun != NULL); - CustomArguments custom_args(callback->data(), - JSObject::cast(args[0]), - JSObject::cast(args[1])); - v8::AccessorInfo info(custom_args.end()); + v8::AccessorInfo info(&args[0]); HandleScope scope; v8::Handle<v8::Value> result; { diff --git a/deps/v8/src/third_party/dtoa/COPYING b/deps/v8/src/third_party/dtoa/COPYING deleted file mode 100644 index c991754d4d..0000000000 --- a/deps/v8/src/third_party/dtoa/COPYING +++ /dev/null @@ -1,15 +0,0 @@ -The author of this software is David M. Gay. - -Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - -Permission to use, copy, modify, and distribute this software for any -purpose without fee is hereby granted, provided that this entire -notice is included in all copies of any software which is or includes -a copy or modification of this software and in all copies of the -supporting documentation for such software. - -THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR -IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES -ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE -MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR -PURPOSE. diff --git a/deps/v8/src/third_party/dtoa/dtoa.c b/deps/v8/src/third_party/dtoa/dtoa.c deleted file mode 100644 index 068ed949d0..0000000000 --- a/deps/v8/src/third_party/dtoa/dtoa.c +++ /dev/null @@ -1,3331 +0,0 @@ -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* Please send bug reports to David M. Gay (dmg at acm dot org, - * with " at " changed at "@" and " dot " changed to "."). */ - -/* On a machine with IEEE extended-precision registers, it is - * necessary to specify double-precision (53-bit) rounding precision - * before invoking strtod or dtoa. If the machine uses (the equivalent - * of) Intel 80x87 arithmetic, the call - * _control87(PC_53, MCW_PC); - * does this with many compilers. Whether this or another call is - * appropriate depends on the compiler; for this to work, it may be - * necessary to #include "float.h" or another system-dependent header - * file. - */ - -/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets errno to ERANGE). With IEEE arithmetic, ties are - * broken by the IEEE round-even rule. Otherwise ties are broken by - * biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE, IBM, or VAX double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_8087 for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_MC68k for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define IBM for IBM mainframe-style floating-point arithmetic. - * #define VAX for VAX-style floating-point arithmetic (D_floating). - * #define No_leftright to omit left-right logic in fast floating-point - * computation of dtoa. - * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and strtod and dtoa should round accordingly. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and Honor_FLT_ROUNDS is not #defined. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define NO_LONG_LONG on machines that do not have a "long long" - * integer type (of >= 64 bits). On such machines, you can - * #define Just_16 to store 16 bits per 32-bit Long when doing - * high-precision integer arithmetic. Whether this speeds things - * up or slows things down depends on the machine and the number - * being converted. If long long is available and the name is - * something other than "long long", #define Llong to be the name, - * and if "unsigned Llong" does not work as an unsigned version of - * Llong, #define #ULLong to be the corresponding unsigned type. - * #define KR_headers for old-style C function headers. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. - * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making - * memory allocations from a private pool of memory when possible. - * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, - * unless #defined to be a different length. This default length - * suffices to get rid of MALLOC calls except for unusual cases, - * such as decimal-to-binary conversion of a very long string of - * digits. The longest string dtoa can return is about 751 bytes - * long. For conversions by strtod of strings of 800 digits and - * all dtoa conversions in single-threaded executions with 8-byte - * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte - * pointers, PRIVATE_MEM >= 7112 appears adequate. - * #define INFNAN_CHECK on IEEE systems to cause strtod to check for - * Infinity and NaN (case insensitively). On some systems (e.g., - * some HP systems), it may be necessary to #define NAN_WORD0 - * appropriately -- to the most significant word of a quiet NaN. - * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) - * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, - * strtod also accepts (case insensitively) strings of the form - * NaN(x), where x is a string of hexadecimal digits and spaces; - * if there is only one string of hexadecimal digits, it is taken - * for the 52 fraction bits of the resulting NaN; if there are two - * or more strings of hex digits, the first is for the high 20 bits, - * the second and subsequent for the low 32 bits, with intervening - * white space ignored; but if this results in none of the 52 - * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 - * and NAN_WORD1 are used instead. - * #define MULTIPLE_THREADS if the system offers preemptively scheduled - * multiple threads. In this case, you must provide (or suitably - * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed - * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed - * in pow5mult, ensures lazy evaluation of only one copy of high - * powers of 5; omitting this lock would introduce a small - * probability of wasting memory, but would otherwise be harmless.) - * You must also invoke freedtoa(s) to free the value s returned by - * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. - * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that - * avoids underflows on inputs whose result does not underflow. - * If you #define NO_IEEE_Scale on a machine that uses IEEE-format - * floating-point numbers and flushes underflows to zero rather - * than implementing gradual underflow, then you must also #define - * Sudden_Underflow. - * #define YES_ALIAS to permit aliasing certain double values with - * arrays of ULongs. This leads to slightly better code with - * some compilers and was always used prior to 19990916, but it - * is not strictly legal and can cause trouble with aggressively - * optimizing compilers (e.g., gcc 2.95.1 under -O2). - * #define USE_LOCALE to use the current locale's decimal_point value. - * #define SET_INEXACT if IEEE arithmetic is being used and extra - * computation should be done to set the inexact flag when the - * result is inexact and avoid setting inexact when the result - * is exact. In this case, dtoa.c must be compiled in - * an environment, perhaps provided by #include "dtoa.c" in a - * suitable wrapper, that defines two functions, - * int get_inexact(void); - * void clear_inexact(void); - * such that get_inexact() returns a nonzero value if the - * inexact bit is already set, and clear_inexact() sets the - * inexact bit to 0. When SET_INEXACT is #defined, strtod - * also does extra computations to set the underflow and overflow - * flags when appropriate (i.e., when the result is tiny and - * inexact or when it is a numeric value rounded to +-infinity). - * #define NO_ERRNO if strtod should not assign errno = ERANGE when - * the result overflows to +-Infinity or underflows to 0. - */ - -#ifndef Long -#if __LP64__ -#define Long int -#else -#define Long long -#endif -#endif -#ifndef ULong -typedef unsigned Long ULong; -#endif - -#ifdef DEBUG -#include "stdio.h" -#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} -#endif - -#include "stdlib.h" -#include "string.h" - -#ifdef USE_LOCALE -#include "locale.h" -#endif - -#ifdef MALLOC -#ifdef KR_headers -extern char *MALLOC(); -#else -extern void *MALLOC(size_t); -#endif -#else -#define MALLOC malloc -#endif - -#ifndef Omit_Private_Memory -#ifndef PRIVATE_MEM -#define PRIVATE_MEM 2304 -#endif -#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -static double private_mem[PRIVATE_mem], *pmem_next = private_mem; -#endif - -#undef IEEE_Arith -#undef Avoid_Underflow -#ifdef IEEE_MC68k -#define IEEE_Arith -#endif -#ifdef IEEE_8087 -#define IEEE_Arith -#endif - -#include "errno.h" - -#ifdef Bad_float_h - -#ifdef IEEE_Arith -#define DBL_DIG 15 -#define DBL_MAX_10_EXP 308 -#define DBL_MAX_EXP 1024 -#define FLT_RADIX 2 -#endif /*IEEE_Arith*/ - -#ifdef IBM -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 75 -#define DBL_MAX_EXP 63 -#define FLT_RADIX 16 -#define DBL_MAX 7.2370055773322621e+75 -#endif - -#ifdef VAX -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 38 -#define DBL_MAX_EXP 127 -#define FLT_RADIX 2 -#define DBL_MAX 1.7014118346046923e+38 -#endif - -#ifndef LONG_MAX -#define LONG_MAX 2147483647 -#endif - -#else /* ifndef Bad_float_h */ -#include "float.h" -#endif /* Bad_float_h */ - -#ifndef __MATH_H__ -#include "math.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef CONST -#ifdef KR_headers -#define CONST /* blank */ -#else -#define CONST const -#endif -#endif - -#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 -Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. -#endif - -typedef union { double d; ULong L[2]; } U; - -#ifdef IEEE_8087 -#define word0(x) (x).L[1] -#define word1(x) (x).L[0] -#else -#define word0(x) (x).L[0] -#define word1(x) (x).L[1] -#endif -#define dval(x) (x).d - -/* The following definition of Storeinc is appropriate for MIPS processors. - * An alternative that might be better on some machines is - * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) - */ -#if defined(IEEE_8087) + defined(VAX) -#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ -((unsigned short *)a)[0] = (unsigned short)c, a++) -#else -#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ -((unsigned short *)a)[1] = (unsigned short)c, a++) -#endif - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#ifdef IEEE_Arith -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Bias 1023 -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#ifndef NO_IEEE_Scale -#define Avoid_Underflow -#ifdef Flush_Denorm /* debugging option */ -#undef Sudden_Underflow -#endif -#endif - -#ifndef Flt_Rounds -#ifdef FLT_ROUNDS -#define Flt_Rounds FLT_ROUNDS -#else -#define Flt_Rounds 1 -#endif -#endif /*Flt_Rounds*/ - -#ifdef Honor_FLT_ROUNDS -#define Rounding rounding -#undef Check_FLT_ROUNDS -#define Check_FLT_ROUNDS -#else -#define Rounding Flt_Rounds -#endif - -#else /* ifndef IEEE_Arith */ -#undef Check_FLT_ROUNDS -#undef Honor_FLT_ROUNDS -#undef SET_INEXACT -#undef Sudden_Underflow -#define Sudden_Underflow -#ifdef IBM -#undef Flt_Rounds -#define Flt_Rounds 0 -#define Exp_shift 24 -#define Exp_shift1 24 -#define Exp_msk1 0x1000000 -#define Exp_msk11 0x1000000 -#define Exp_mask 0x7f000000 -#define P 14 -#define Bias 65 -#define Exp_1 0x41000000 -#define Exp_11 0x41000000 -#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ -#define Frac_mask 0xffffff -#define Frac_mask1 0xffffff -#define Bletch 4 -#define Ten_pmax 22 -#define Bndry_mask 0xefffff -#define Bndry_mask1 0xffffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 4 -#define Tiny0 0x100000 -#define Tiny1 0 -#define Quick_max 14 -#define Int_max 15 -#else /* VAX */ -#undef Flt_Rounds -#define Flt_Rounds 1 -#define Exp_shift 23 -#define Exp_shift1 7 -#define Exp_msk1 0x80 -#define Exp_msk11 0x800000 -#define Exp_mask 0x7f80 -#define P 56 -#define Bias 129 -#define Exp_1 0x40800000 -#define Exp_11 0x4080 -#define Ebits 8 -#define Frac_mask 0x7fffff -#define Frac_mask1 0xffff007f -#define Ten_pmax 24 -#define Bletch 2 -#define Bndry_mask 0xffff007f -#define Bndry_mask1 0xffff007f -#define LSB 0x10000 -#define Sign_bit 0x8000 -#define Log2P 1 -#define Tiny0 0x80 -#define Tiny1 0 -#define Quick_max 15 -#define Int_max 15 -#endif /* IBM, VAX */ -#endif /* IEEE_Arith */ - -#ifndef IEEE_Arith -#define ROUND_BIASED -#endif - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -#ifdef KR_headers -extern double rnd_prod(), rnd_quot(); -#else -extern double rnd_prod(double, double), rnd_quot(double, double); -#endif -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef Pack_32 -#define Pack_32 -#endif - -#ifdef KR_headers -#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) -#else -#define FFFFFFFF 0xffffffffUL -#endif - -#ifdef NO_LONG_LONG -#undef ULLong -#ifdef Just_16 -#undef Pack_32 -/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. - * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly - * slower. Hence the default is now to store 32 bits per Long. - */ -#endif -#else /* long long available */ -#ifndef Llong -#define Llong long long -#endif -#ifndef ULLong -#define ULLong unsigned Llong -#endif -#endif /* NO_LONG_LONG */ - -#ifndef MULTIPLE_THREADS -#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ -#define FREE_DTOA_LOCK(n) /*nothing*/ -#endif - -#define Kmax 15 - -#ifdef __cplusplus -extern "C" double strtod(const char *s00, char **se); -extern "C" char *dtoa(double d, int mode, int ndigits, - int *decpt, int *sign, char **rve); -#endif - - struct -Bigint { - struct Bigint *next; - int k, maxwds, sign, wds; - ULong x[1]; - }; - - typedef struct Bigint Bigint; - - static Bigint *freelist[Kmax+1]; - - static Bigint * -Balloc -#ifdef KR_headers - (k) int k; -#else - (int k) -#endif -{ - int x; - Bigint *rv; -#ifndef Omit_Private_Memory - unsigned int len; -#endif - - ACQUIRE_DTOA_LOCK(0); - /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0). */ - /* but this case seems very unlikely. */ - if (k <= Kmax && (rv = freelist[k])) { - freelist[k] = rv->next; - } - else { - x = 1 << k; -#ifdef Omit_Private_Memory - rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); -#else - len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) - /sizeof(double); - if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { - rv = (Bigint*)pmem_next; - pmem_next += len; - } - else - rv = (Bigint*)MALLOC(len*sizeof(double)); -#endif - rv->k = k; - rv->maxwds = x; - } - FREE_DTOA_LOCK(0); - rv->sign = rv->wds = 0; - return rv; - } - - static void -Bfree -#ifdef KR_headers - (v) Bigint *v; -#else - (Bigint *v) -#endif -{ - if (v) { - if (v->k > Kmax) - free((void*)v); - else { - ACQUIRE_DTOA_LOCK(0); - v->next = freelist[v->k]; - freelist[v->k] = v; - FREE_DTOA_LOCK(0); - } - } - } - -#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ -y->wds*sizeof(Long) + 2*sizeof(int)) - - static Bigint * -multadd -#ifdef KR_headers - (b, m, a) Bigint *b; int m, a; -#else - (Bigint *b, int m, int a) /* multiply by m and add a */ -#endif -{ - int i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; -#else - ULong carry, *x, y; -#ifdef Pack_32 - ULong xi, z; -#endif -#endif - Bigint *b1; - - wds = b->wds; - x = b->x; - i = 0; - carry = a; - do { -#ifdef ULLong - y = *x * (ULLong)m + carry; - carry = y >> 32; - *x++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - xi = *x; - y = (xi & 0xffff) * m + carry; - z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); -#else - y = *x * m + carry; - carry = y >> 16; - *x++ = y & 0xffff; -#endif -#endif - } - while(++i < wds); - if (carry) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = carry; - b->wds = wds; - } - return b; - } - - static Bigint * -s2b -#ifdef KR_headers - (s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9; -#else - (CONST char *s, int nd0, int nd, ULong y9) -#endif -{ - Bigint *b; - int i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; -#ifdef Pack_32 - b = Balloc(k); - b->x[0] = y9; - b->wds = 1; -#else - b = Balloc(k+1); - b->x[0] = y9 & 0xffff; - b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; -#endif - - i = 9; - if (9 < nd0) { - s += 9; - do b = multadd(b, 10, *s++ - '0'); - while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); - return b; - } - - static int -hi0bits -#ifdef KR_headers - (x) register ULong x; -#else - (register ULong x) -#endif -{ - register int k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; - } - - static int -lo0bits -#ifdef KR_headers - (y) ULong *y; -#else - (ULong *y) -#endif -{ - register int k; - register ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x) - return 32; - } - *y = x; - return k; - } - - static Bigint * -i2b -#ifdef KR_headers - (i) int i; -#else - (int i) -#endif -{ - Bigint *b; - - b = Balloc(1); - b->x[0] = i; - b->wds = 1; - return b; - } - - static Bigint * -mult -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int k, wa, wb, wc; - ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; - ULong y; -#ifdef ULLong - ULLong carry, z; -#else - ULong carry, z; -#ifdef Pack_32 - ULong z2; -#endif -#endif - - if (a->wds < b->wds) { - c = a; - a = b; - b = c; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - for(x = c->x, xa = x + wc; x < xa; x++) - *x = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef ULLong - for(; xb < xbe; xc0++) { - if ((y = *xb++)) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (ULLong)y + *xc + carry; - carry = z >> 32; - *xc++ = z & FFFFFFFF; - } - while(x < xae); - *xc = carry; - } - } -#else -#ifdef Pack_32 - for(; xb < xbe; xb++, xc0++) { - if (y = *xb & 0xffff) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if (y = *xb >> 16) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#else - for(; xb < xbe; xc0++) { - if (y = *xb++) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * y + *xc + carry; - carry = z >> 16; - *xc++ = z & 0xffff; - } - while(x < xae); - *xc = carry; - } - } -#endif -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; - } - - static Bigint *p5s; - - static Bigint * -pow5mult -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - Bigint *b1, *p5, *p51; - int i; - static int p05[3] = { 5, 25, 125 }; - - if ((i = k & 3)) - b = multadd(b, p05[i-1], 0); - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { - /* first time */ -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p5 = p5s)) { - p5 = p5s = i2b(625); - p5->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p5 = p5s = i2b(625); - p5->next = 0; -#endif - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); - p51->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p51 = p5->next = mult(p5,p5); - p51->next = 0; -#endif - } - p5 = p51; - } - return b; - } - - static Bigint * -lshift -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - int i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - -#ifdef Pack_32 - n = k >> 5; -#else - n = k >> 4; -#endif - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; -#ifdef Pack_32 - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z)) - ++n1; - } -#else - if (k &= 0xf) { - k1 = 16 - k; - z = 0; - do { - *x1++ = *x << k & 0xffff | z; - z = *x++ >> k1; - } - while(x < xe); - if (*x1 = z) - ++n1; - } -#endif - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; - Bfree(b); - return b1; - } - - static int -cmp -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - ULong *xa, *xa0, *xb, *xb0; - int i, j; - - i = a->wds; - j = b->wds; -#ifdef DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; - } - - static Bigint * -diff -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int i, wa, wb; - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef ULLong - ULLong borrow, y; -#else - ULong borrow, y; -#ifdef Pack_32 - ULong z; -#endif -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef ULLong - do { - y = (ULLong)*xa++ - *xb++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } -#else -#ifdef Pack_32 - do { - y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } -#else - do { - y = *xa++ - *xb++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } -#endif -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; - } - - static double -ulp -#ifdef KR_headers - (dx) double dx; -#else - (double dx) -#endif -{ - register Long L; - U x, a; - - dval(x) = dx; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - if (L > 0) { -#endif -#endif -#ifdef IBM - L |= Exp_msk1 >> 4; -#endif - word0(a) = L; - word1(a) = 0; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - word0(a) = 0x80000 >> L; - word1(a) = 0; - } - else { - word0(a) = 0; - L -= Exp_shift; - word1(a) = L >= 31 ? 1 : 1 << 31 - L; - } - } -#endif -#endif - return dval(a); - } - - static double -b2d -#ifdef KR_headers - (a, e) Bigint *a; int *e; -#else - (Bigint *a, int *e) -#endif -{ - ULong *xa, *xa0, w, y, z; - int k; - U d; -#ifdef VAX - ULong d0, d1; -#else -#define d0 word0(d) -#define d1 word1(d) -#endif - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; -#ifdef Pack_32 - if (k < Ebits) { - d0 = Exp_1 | (y >> (Ebits - k)); - w = xa > xa0 ? *--xa : 0; - d1 = (y << ((32-Ebits) + k)) | (w >> (Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - d0 = Exp_1 | (y << k) | (z >> (32 - k)); - y = xa > xa0 ? *--xa : 0; - d1 = (z << k) | (y >> (32 - k)); - } - else { - d0 = Exp_1 | y; - d1 = z; - } -#else - if (k < Ebits + 16) { - z = xa > xa0 ? *--xa : 0; - d0 = Exp_1 | (y << (k - Ebits)) | (z >> (Ebits + 16 - k)); - w = xa > xa0 ? *--xa : 0; - y = xa > xa0 ? *--xa : 0; - d1 = (z << (k + 16 - Ebits)) | (w << (k - Ebits)) | (y >> (16 + Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - w = xa > xa0 ? *--xa : 0; - k -= Ebits + 16; - d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; - y = xa > xa0 ? *--xa : 0; - d1 = w << k + 16 | y << k; -#endif - ret_d: -#ifdef VAX - word0(d) = d0 >> 16 | d0 << 16; - word1(d) = d1 >> 16 | d1 << 16; -#else -#undef d0 -#undef d1 -#endif - return dval(d); - } - - static Bigint * -d2b -#ifdef KR_headers - (dd, e, bits) double dd; int *e, *bits; -#else - (double dd, int *e, int *bits) -#endif -{ - Bigint *b; - int de, k; - ULong *x, y, z; -#ifndef Sudden_Underflow - int i; -#endif -#ifdef VAX - ULong d0, d1; - d0 = word0(d) >> 16 | word0(d) << 16; - d1 = word1(d) >> 16 | word1(d) << 16; -#else - U d; - dval(d) = dd; -#define d0 word0(d) -#define d1 word1(d) -#endif - -#ifdef Pack_32 - b = Balloc(1); -#else - b = Balloc(2); -#endif - x = b->x; - - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int)(d0 >> Exp_shift); -#ifndef IBM - z |= Exp_msk11; -#endif -#else - if ((de = (int)(d0 >> Exp_shift))) - z |= Exp_msk1; -#endif -#ifdef Pack_32 - if ((y = d1)) { - if ((k = lo0bits(&y))) { - x[0] = y | (z << (32 - k)); - z >>= k; - } - else - x[0] = y; -#ifndef Sudden_Underflow - i = -#endif - b->wds = (x[1] = z) ? 2 : 1; - } - else { - /* This assertion fails for "1e-500" and other very - * small numbers. It provides the right result (0) - * though. This assert has also been removed from KJS's - * version of dtoa.c. - * - * #ifdef DEBUG - * if (!z) Bug("zero z in b2d"); - * #endif - */ - k = lo0bits(&z); - x[0] = z; -#ifndef Sudden_Underflow - i = -#endif - b->wds = 1; - k += 32; - } -#else - if (y = d1) { - if (k = lo0bits(&y)) - if (k >= 16) { - x[0] = y | z << 32 - k & 0xffff; - x[1] = z >> k - 16 & 0xffff; - x[2] = z >> k; - i = 2; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16 | z << 16 - k & 0xffff; - x[2] = z >> k & 0xffff; - x[3] = z >> k+16; - i = 3; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16; - x[2] = z & 0xffff; - x[3] = z >> 16; - i = 3; - } - } - else { -#ifdef DEBUG - if (!z) - Bug("Zero passed to d2b"); -#endif - k = lo0bits(&z); - if (k >= 16) { - x[0] = z; - i = 0; - } - else { - x[0] = z & 0xffff; - x[1] = z >> 16; - i = 1; - } - k += 32; - } - while(!x[i]) - --i; - b->wds = i + 1; -#endif -#ifndef Sudden_Underflow - if (de) { -#endif -#ifdef IBM - *e = (de - Bias - (P-1) << 2) + k; - *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); -#else - *e = de - Bias - (P-1) + k; - *bits = P - k; -#endif -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; -#ifdef Pack_32 - *bits = 32*i - hi0bits(x[i-1]); -#else - *bits = (i+2)*16 - hi0bits(x[i]); -#endif - } -#endif - return b; - } -#undef d0 -#undef d1 - - static double -ratio -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - U da, db; - int k, ka, kb; - - dval(da) = b2d(a, &ka); - dval(db) = b2d(b, &kb); -#ifdef Pack_32 - k = ka - kb + 32*(a->wds - b->wds); -#else - k = ka - kb + 16*(a->wds - b->wds); -#endif -#ifdef IBM - if (k > 0) { - word0(da) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(da) *= 1 << k; - } - else { - k = -k; - word0(db) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(db) *= 1 << k; - } -#else - if (k > 0) - word0(da) += k*Exp_msk1; - else { - k = -k; - word0(db) += k*Exp_msk1; - } -#endif - return dval(da) / dval(db); - } - - static CONST double -tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -#ifdef VAX - , 1e23, 1e24 -#endif - }; - - static CONST double -#ifdef IEEE_Arith -bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, -#ifdef Avoid_Underflow - 9007199254740992.*9007199254740992.e-256 - /* = 2^106 * 1e-53 */ -#else - 1e-256 -#endif - }; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -#define Scale_Bit 0x10 -#define n_bigtens 5 -#else -#ifdef IBM -bigtens[] = { 1e16, 1e32, 1e64 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; -#define n_bigtens 3 -#else -bigtens[] = { 1e16, 1e32 }; -static CONST double tinytens[] = { 1e-16, 1e-32 }; -#define n_bigtens 2 -#endif -#endif - -#ifndef IEEE_Arith -#undef INFNAN_CHECK -#endif - -#ifdef INFNAN_CHECK - -#ifndef NAN_WORD0 -#define NAN_WORD0 0x7ff80000 -#endif - -#ifndef NAN_WORD1 -#define NAN_WORD1 0 -#endif - - static int -match -#ifdef KR_headers - (sp, t) char **sp, *t; -#else - (CONST char **sp, char *t) -#endif -{ - int c, d; - CONST char *s = *sp; - - while(d = *t++) { - if ((c = *++s) >= 'A' && c <= 'Z') - c += 'a' - 'A'; - if (c != d) - return 0; - } - *sp = s + 1; - return 1; - } - -#ifndef No_Hex_NaN - static void -hexnan -#ifdef KR_headers - (rvp, sp) double *rvp; CONST char **sp; -#else - (double *rvp, CONST char **sp) -#endif -{ - ULong c, x[2]; - CONST char *s; - int havedig, udx0, xshift; - - x[0] = x[1] = 0; - havedig = xshift = 0; - udx0 = 1; - s = *sp; - while(c = *(CONST unsigned char*)++s) { - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'a' && c <= 'f') - c += 10 - 'a'; - else if (c >= 'A' && c <= 'F') - c += 10 - 'A'; - else if (c <= ' ') { - if (udx0 && havedig) { - udx0 = 0; - xshift = 1; - } - continue; - } - else if (/*(*/ c == ')' && havedig) { - *sp = s + 1; - break; - } - else - return; /* invalid form: don't change *sp */ - havedig = 1; - if (xshift) { - xshift = 0; - x[0] = x[1]; - x[1] = 0; - } - if (udx0) - x[0] = (x[0] << 4) | (x[1] >> 28); - x[1] = (x[1] << 4) | c; - } - if ((x[0] &= 0xfffff) || x[1]) { - word0(*rvp) = Exp_mask | x[0]; - word1(*rvp) = x[1]; - } - } -#endif /*No_Hex_NaN*/ -#endif /* INFNAN_CHECK */ - - double -strtod -#ifdef KR_headers - (s00, se) CONST char *s00; char **se; -#else - (CONST char *s00, char **se) -#endif -{ -#ifdef Avoid_Underflow - int scale; -#endif - int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; - CONST char *s, *s0, *s1; - double aadj; - U aadj1, adj, rv, rv0; - Long L; - ULong y, z; - Bigint *bb = NULL, *bb1, *bd = NULL, *bd0, *bs = NULL, *delta = NULL; -#ifdef SET_INEXACT - int inexact, oldinexact; -#endif -#ifdef Honor_FLT_ROUNDS - int rounding; -#endif -#ifdef USE_LOCALE - CONST char *s2; -#endif - - sign = nz0 = nz = 0; - dval(rv) = 0.; - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - goto ret0; - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - default: - goto break2; - } - break2: - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; -#ifdef USE_LOCALE - s1 = localeconv()->decimal_point; - if (c == *s1) { - c = '.'; - if (*++s1) { - s2 = s; - for(;;) { - if (*++s2 != *s1) { - c = 0; - break; - } - if (!*++s1) { - s = s2; - break; - } - } - } - } -#endif - if (c == '.') { - c = *++s; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = 0; - } - } - } - dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - goto ret0; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = (int)L; - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) { -#ifdef INFNAN_CHECK - /* Check for Nan and Infinity */ - switch(c) { - case 'i': - case 'I': - if (match(&s,"nf")) { - --s; - if (!match(&s,"inity")) - ++s; - word0(rv) = 0x7ff00000; - word1(rv) = 0; - goto ret; - } - break; - case 'n': - case 'N': - if (match(&s, "an")) { - word0(rv) = NAN_WORD0; - word1(rv) = NAN_WORD1; -#ifndef No_Hex_NaN - if (*s == '(') /*)*/ - hexnan(&rv, &s); -#endif - goto ret; - } - } -#endif /* INFNAN_CHECK */ - ret0: - s = s00; - sign = 0; - } - goto ret; - } - e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - dval(rv) = y; - if (k > 9) { -#ifdef SET_INEXACT - if (k > DBL_DIG) - oldinexact = get_inexact(); -#endif - dval(rv) = tens[k - 9] * dval(rv) + z; - } - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT -#ifndef Honor_FLT_ROUNDS - && Flt_Rounds == 1 -#endif -#endif - ) { - if (!e) - goto ret; - if (e > 0) { - if (e <= Ten_pmax) { -#ifdef VAX - goto vax_ovfl_check; -#else -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - /* rv = */ rounded_product(dval(rv), tens[e]); - goto ret; -#endif - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - e -= i; - dval(rv) *= tens[i]; -#ifdef VAX - /* VAX exponent range is so narrow we must - * worry about overflow here... - */ - vax_ovfl_check: - word0(rv) -= P*Exp_msk1; - /* rv = */ rounded_product(dval(rv), tens[e]); - if ((word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) - goto ovfl; - word0(rv) += P*Exp_msk1; -#else - /* rv = */ rounded_product(dval(rv), tens[e]); -#endif - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - /* rv = */ rounded_quotient(dval(rv), tens[-e]); - goto ret; - } -#endif - } - e1 += nd - k; - -#ifdef IEEE_Arith -#ifdef SET_INEXACT - inexact = 1; - if (k <= DBL_DIG) - oldinexact = get_inexact(); -#endif -#ifdef Avoid_Underflow - scale = 0; -#endif -#ifdef Honor_FLT_ROUNDS - if ((rounding = Flt_Rounds) >= 2) { - if (sign) - rounding = rounding == 2 ? 0 : 2; - else - if (rounding != 2) - rounding = 0; - } -#endif -#endif /*IEEE_Arith*/ - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15)) - dval(rv) *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: -#ifndef NO_ERRNO - errno = ERANGE; -#endif - /* Can't trust HUGE_VAL */ -#ifdef IEEE_Arith -#ifdef Honor_FLT_ROUNDS - switch(rounding) { - case 0: /* toward 0 */ - case 3: /* toward -infinity */ - word0(rv) = Big0; - word1(rv) = Big1; - break; - default: - word0(rv) = Exp_mask; - word1(rv) = 0; - } -#else /*Honor_FLT_ROUNDS*/ - word0(rv) = Exp_mask; - word1(rv) = 0; -#endif /*Honor_FLT_ROUNDS*/ -#ifdef SET_INEXACT - /* set overflow bit */ - dval(rv0) = 1e300; - dval(rv0) *= dval(rv0); -#endif -#else /*IEEE_Arith*/ - word0(rv) = Big0; - word1(rv) = Big1; -#endif /*IEEE_Arith*/ - if (bd0) - goto retfree; - goto ret; - } - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= bigtens[j]; - /* The last multiplication could overflow. */ - word0(rv) -= P*Exp_msk1; - dval(rv) *= bigtens[j]; - if ((z = word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - word0(rv) = Big0; - word1(rv) = Big1; - } - else - word0(rv) += P*Exp_msk1; - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15)) - dval(rv) /= tens[i]; - if (e1 >>= 4) { - if (e1 >= 1 << n_bigtens) - goto undfl; -#ifdef Avoid_Underflow - if (e1 & Scale_Bit) - scale = 2*P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= tinytens[j]; - if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; zap j low bits */ - if (j >= 32) { - word1(rv) = 0; - if (j >= 53) - word0(rv) = (P+2)*Exp_msk1; - else - word0(rv) &= 0xffffffff << (j-32); - } - else - word1(rv) &= 0xffffffff << j; - } -#else - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= tinytens[j]; - /* The last multiplication could underflow. */ - dval(rv0) = dval(rv); - dval(rv) *= tinytens[j]; - if (!dval(rv)) { - dval(rv) = 2.*dval(rv0); - dval(rv) *= tinytens[j]; -#endif - if (!dval(rv)) { - undfl: - dval(rv) = 0.; -#ifndef NO_ERRNO - errno = ERANGE; -#endif - if (bd0) - goto retfree; - goto ret; - } -#ifndef Avoid_Underflow - word0(rv) = Tiny0; - word1(rv) = Tiny1; - /* The refinement below will clean - * this approximation up. - */ - } -#endif - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bd0 = s2b(s0, nd0, nd, y); - - for(;;) { - bd = Balloc(bd0->k); - Bcopy(bd, bd0); - bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ - bs = i2b(1); - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Honor_FLT_ROUNDS - if (rounding != 1) - bs2++; -#endif -#ifdef Avoid_Underflow - j = bbe - scale; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#else /*Avoid_Underflow*/ -#ifdef Sudden_Underflow -#ifdef IBM - j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); -#else - j = P + 1 - bbbits; -#endif -#else /*Sudden_Underflow*/ - j = bbe; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - bb2 += j; - bd2 += j; -#ifdef Avoid_Underflow - bd2 += scale; -#endif - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) - bb = lshift(bb, bb2); - if (bd5 > 0) - bd = pow5mult(bd, bd5); - if (bd2 > 0) - bd = lshift(bd, bd2); - if (bs2 > 0) - bs = lshift(bs, bs2); - delta = diff(bb, bd); - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); -#ifdef Honor_FLT_ROUNDS - if (rounding != 1) { - if (i < 0) { - /* Error is less than an ulp */ - if (!delta->x[0] && delta->wds <= 1) { - /* exact */ -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - if (rounding) { - if (dsign) { - dval(adj) = 1.; - goto apply_adj; - } - } - else if (!dsign) { - dval(adj) = -1.; - if (!word1(rv) - && !(word0(rv) & Frac_mask)) { - y = word0(rv) & Exp_mask; -#ifdef Avoid_Underflow - if (!scale || y > 2*P*Exp_msk1) -#else - if (y) -#endif - { - delta = lshift(delta,Log2P); - if (cmp(delta, bs) <= 0) - dval(adj) = -0.5; - } - } - apply_adj: -#ifdef Avoid_Underflow - if (scale && (y = word0(rv) & Exp_mask) - <= 2*P*Exp_msk1) - word0(adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= - P*Exp_msk1) { - word0(rv) += P*Exp_msk1; - dval(rv) += dval(adj)*ulp(dval(rv)); - word0(rv) -= P*Exp_msk1; - } - else -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - dval(rv) += dval(adj)*ulp(dval(rv)); - } - break; - } - dval(adj) = ratio(delta, bs); - if (dval(adj) < 1.) - dval(adj) = 1.; - if (dval(adj) <= 0x7ffffffe) { - /* adj = rounding ? ceil(adj) : floor(adj); */ - y = dval(adj); - if (y != dval(adj)) { - if (!((rounding>>1) ^ dsign)) - y++; - dval(adj) = y; - } - } -#ifdef Avoid_Underflow - if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) - word0(adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - word0(rv) += P*Exp_msk1; - dval(adj) *= ulp(dval(rv)); - if (dsign) - dval(rv) += dval(adj); - else - dval(rv) -= dval(adj); - word0(rv) -= P*Exp_msk1; - goto cont; - } -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - dval(adj) *= ulp(dval(rv)); - if (dsign) - dval(rv) += dval(adj); - else - dval(rv) -= dval(adj); - goto cont; - } -#endif /*Honor_FLT_ROUNDS*/ - - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask -#ifdef IEEE_Arith -#ifdef Avoid_Underflow - || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 -#else - || (word0(rv) & Exp_mask) <= Exp_msk1 -#endif -#endif - ) { -#ifdef SET_INEXACT - if (!delta->x[0] && delta->wds <= 1) - inexact = 0; -#endif - break; - } - if (!delta->x[0] && delta->wds <= 1) { - /* exact result */ -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - delta = lshift(delta,Log2P); - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((word0(rv) & Bndry_mask1) == Bndry_mask1 - && word1(rv) == ( -#ifdef Avoid_Underflow - (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) - ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : -#endif - 0xffffffff)) { - /*boundary case -- increment exponent*/ - word0(rv) = (word0(rv) & Exp_mask) - + Exp_msk1 -#ifdef IBM - | Exp_msk1 >> 4 -#endif - ; - word1(rv) = 0; -#ifdef Avoid_Underflow - dsign = 0; -#endif - break; - } - } - else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow /*{{*/ - L = word0(rv) & Exp_mask; -#ifdef IBM - if (L < Exp_msk1) -#else -#ifdef Avoid_Underflow - if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) -#else - if (L <= Exp_msk1) -#endif /*Avoid_Underflow*/ -#endif /*IBM*/ - goto undfl; - L -= Exp_msk1; -#else /*Sudden_Underflow}{*/ -#ifdef Avoid_Underflow - if (scale) { - L = word0(rv) & Exp_mask; - if (L <= (2*P+1)*Exp_msk1) { - if (L > (P+2)*Exp_msk1) - /* round even ==> */ - /* accept rv */ - break; - /* rv = smallest denormal */ - goto undfl; - } - } -#endif /*Avoid_Underflow*/ - L = (word0(rv) & Exp_mask) - Exp_msk1; -#endif /*Sudden_Underflow}}*/ - word0(rv) = L | Bndry_mask1; - word1(rv) = 0xffffffff; -#ifdef IBM - goto cont; -#else - break; -#endif - } -#ifndef ROUND_BIASED - if (!(word1(rv) & LSB)) - break; -#endif - if (dsign) - dval(rv) += ulp(dval(rv)); -#ifndef ROUND_BIASED - else { - dval(rv) -= ulp(dval(rv)); -#ifndef Sudden_Underflow - if (!dval(rv)) - goto undfl; -#endif - } -#ifdef Avoid_Underflow - dsign = 1 - dsign; -#endif -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = dval(aadj1) = 1.; - else if (word1(rv) || word0(rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (word1(rv) == Tiny1 && !word0(rv)) - goto undfl; -#endif - aadj = 1.; - dval(aadj1) = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - dval(aadj1) = -aadj; - } - } - else { - aadj *= 0.5; - dval(aadj1) = dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(Rounding) { - case 2: /* towards +infinity */ - dval(aadj1) -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - dval(aadj1) += 0.5; - } -#else - if (Flt_Rounds == 0) - dval(aadj1) += 0.5; -#endif /*Check_FLT_ROUNDS*/ - } - y = word0(rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - dval(rv0) = dval(rv); - word0(rv) -= P*Exp_msk1; - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); - if ((word0(rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(rv0) == Big0 && word1(rv0) == Big1) - goto ovfl; - word0(rv) = Big0; - word1(rv) = Big1; - goto cont; - } - else - word0(rv) += P*Exp_msk1; - } - else { -#ifdef Avoid_Underflow - if (scale && y <= 2*P*Exp_msk1) { - if (aadj <= 0x7fffffff) { - if ((z = aadj) <= 0) - z = 1; - aadj = z; - dval(aadj1) = dsign ? aadj : -aadj; - } - word0(aadj1) += (2*P+1)*Exp_msk1 - y; - } - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - dval(rv0) = dval(rv); - word0(rv) += P*Exp_msk1; - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#ifdef IBM - if ((word0(rv) & Exp_mask) < P*Exp_msk1) -#else - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) -#endif - { - if (word0(rv0) == Tiny0 - && word1(rv0) == Tiny1) - goto undfl; - word0(rv) = Tiny0; - word1(rv) = Tiny1; - goto cont; - } - else - word0(rv) -= P*Exp_msk1; - } - else { - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); - } -#else /*Sudden_Underflow*/ - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ - if (y <= (P-1)*Exp_msk1 && aadj > 1.) { - dval(aadj1) = (double)(int)(aadj + 0.5); - if (!dsign) - dval(aadj1) = -dval(aadj1); - } - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - } - z = word0(rv) & Exp_mask; -#ifndef SET_INEXACT -#ifdef Avoid_Underflow - if (!scale) -#endif - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } -#endif - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - } -#ifdef SET_INEXACT - if (inexact) { - if (!oldinexact) { - word0(rv0) = Exp_1 + (70 << Exp_shift); - word1(rv0) = 0; - dval(rv0) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif -#ifdef Avoid_Underflow - if (scale) { - word0(rv0) = Exp_1 - 2*P*Exp_msk1; - word1(rv0) = 0; - dval(rv) *= dval(rv0); -#ifndef NO_ERRNO - /* try to avoid the bug of testing an 8087 register value */ - if (word0(rv) == 0 && word1(rv) == 0) - errno = ERANGE; -#endif - } -#endif /* Avoid_Underflow */ -#ifdef SET_INEXACT - if (inexact && !(word0(rv) & Exp_mask)) { - /* set underflow bit */ - dval(rv0) = 1e-300; - dval(rv0) *= dval(rv0); - } -#endif - retfree: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - ret: - if (se) - *se = (char *)s; - return sign ? -dval(rv) : dval(rv); - } - - static int -quorem -#ifdef KR_headers - (b, S) Bigint *b, *S; -#else - (Bigint *b, Bigint *S) -#endif -{ - int n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; -#else - ULong borrow, carry, y, ys; -#ifdef Pack_32 - ULong si, z, zs; -#endif -#endif - - n = S->wds; -#ifdef DEBUG - /*debug*/ if (b->wds > n) - /*debug*/ Bug("oversize b in quorem"); -#endif - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ -#ifdef DEBUG - /*debug*/ if (q > 9) - /*debug*/ Bug("oversized quotient in quorem"); -#endif - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef ULLong - ys = *sx++ * (ULLong)q + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ * q + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef ULLong - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return q; - } - -#ifndef MULTIPLE_THREADS - static char *dtoa_result; -#endif - - static char * -#ifdef KR_headers -rv_alloc(i) int i; -#else -rv_alloc(int i) -#endif -{ - int j, k, *r; - - j = sizeof(ULong); - for(k = 0; - sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; - j <<= 1) - k++; - r = (int*)Balloc(k); - *r = k; - return -#ifndef MULTIPLE_THREADS - dtoa_result = -#endif - (char *)(r+1); - } - - static char * -#ifdef KR_headers -nrv_alloc(s, rve, n) char *s, **rve; int n; -#else -nrv_alloc(const char *s, char **rve, int n) -#endif -{ - char *rv, *t; - - t = rv = rv_alloc(n); - while ((*t = *s++)) t++; - if (rve) - *rve = t; - return rv; - } - -/* freedtoa(s) must be used to free values s returned by dtoa - * when MULTIPLE_THREADS is #defined. It should be used in all cases, - * but for consistency with earlier versions of dtoa, it is optional - * when MULTIPLE_THREADS is not defined. - */ - - void -#ifdef KR_headers -freedtoa(s) char *s; -#else -freedtoa(char *s) -#endif -{ - Bigint *b = (Bigint *)((int *)s - 1); - b->maxwds = 1 << (b->k = *(int*)b); - Bfree(b); -#ifndef MULTIPLE_THREADS - if (s == dtoa_result) - dtoa_result = 0; -#endif - } - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - - char * -dtoa -#ifdef KR_headers - (dd, mode, ndigits, decpt, sign, rve) - double dd; int mode, ndigits, *decpt, *sign; char **rve; -#else - (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) -#endif -{ - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4,5 ==> similar to 2 and 3, respectively, but (in - round-nearest mode) with the tests of mode 0 to - possibly return a shorter string that rounds to d. - With IEEE arithmetic and compilation with - -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same - as modes 2 and 3 when FLT_ROUNDS != 1. - 6-9 ==> Debugging modes similar to mode - 4: don't try - fast floating-point estimate (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - spec_case, try_quick, bias_round_up; - Long L; -#ifndef Sudden_Underflow - int denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mlo, *mhi, *S; - double ds; - U d2, eps; - char *s, *s0; -#ifdef Honor_FLT_ROUNDS - int rounding; -#endif -#ifdef SET_INEXACT - int inexact, oldinexact; -#endif - U d; - dval(d) = dd; - - /* In mode 2 and 3 we bias rounding up when there are ties. */ - bias_round_up = mode == 2 || mode == 3; - - ilim = ilim1 = 0; /* to avoid Google3 compiler warnings */ - -#ifndef MULTIPLE_THREADS - if (dtoa_result) { - freedtoa(dtoa_result); - dtoa_result = 0; - } -#endif - - if (word0(d) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - word0(d) &= ~Sign_bit; /* clear sign bit */ - } - else - *sign = 0; - -#if defined(IEEE_Arith) + defined(VAX) -#ifdef IEEE_Arith - if ((word0(d) & Exp_mask) == Exp_mask) -#else - if (word0(d) == 0x8000) -#endif - { - /* Infinity or NaN */ - *decpt = 9999; -#ifdef IEEE_Arith - if (!word1(d) && !(word0(d) & 0xfffff)) - return nrv_alloc("Infinity", rve, 8); -#endif - return nrv_alloc("NaN", rve, 3); - } -#endif -#ifdef IBM - dval(d) += 0; /* normalize */ -#endif - if (!dval(d)) { - *decpt = 1; - return nrv_alloc("0", rve, 1); - } - -#ifdef SET_INEXACT - try_quick = oldinexact = get_inexact(); - inexact = 1; -#endif -#ifdef Honor_FLT_ROUNDS - if ((rounding = Flt_Rounds) >= 2) { - if (*sign) - rounding = rounding == 2 ? 0 : 2; - else - if (rounding != 2) - rounding = 0; - } -#endif - - b = d2b(dval(d), &be, &bbits); -#ifdef Sudden_Underflow - i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#else - if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { -#endif - dval(d2) = dval(d); - word0(d2) &= Frac_mask1; - word0(d2) |= Exp_11; -#ifdef IBM - if (j = 11 - hi0bits(word0(d2) & Frac_mask)) - dval(d2) /= 1 << j; -#endif - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifdef IBM - i <<= 2; - i += j; -#endif -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? (word0(d) << (64 - i)) | (word1(d) >> (i - 32)) - : word1(d) << (32 - i); - dval(d2) = x; - word0(d2) -= 31*Exp_msk1; /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (dval(d) < tens[k]) - k--; - k_check = 0; - } - j = bbits - i - 1; - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - if (mode < 0 || mode > 9) - mode = 0; - -#ifndef SET_INEXACT -#ifdef Check_FLT_ROUNDS - try_quick = Rounding == 1; -#else - try_quick = 1; -#endif -#endif /*SET_INEXACT*/ - - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - s = s0 = rv_alloc(i); - -#ifdef Honor_FLT_ROUNDS - if (mode > 1 && rounding != 1) - leftright = 0; -#endif - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - dval(d2) = dval(d); - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - dval(d) /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - dval(d) /= ds; - } - else if ((j1 = -k)) { - dval(d) *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - dval(d) *= bigtens[i]; - } - } - if (k_check && dval(d) < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - dval(d) *= 10.; - ieps++; - } - dval(eps) = ieps*dval(d) + 7.; - word0(eps) -= (P-1)*Exp_msk1; - if (ilim == 0) { - S = mhi = 0; - dval(d) -= 5.; - if (dval(d) > dval(eps)) - goto one_digit; - if (dval(d) < -dval(eps)) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - dval(eps) = 0.5/tens[ilim-1] - dval(eps); - for(i = 0;;) { - L = dval(d); - dval(d) -= L; - *s++ = '0' + (int)L; - if (dval(d) < dval(eps)) - goto ret1; - if (1. - dval(d) < dval(eps)) - goto bump_up; - if (++i >= ilim) - break; - dval(eps) *= 10.; - dval(d) *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - dval(eps) *= tens[ilim-1]; - for(i = 1;; i++, dval(d) *= 10.) { - L = (Long)(dval(d)); - if (!(dval(d) -= L)) - ilim = i; - *s++ = '0' + (int)L; - if (i == ilim) { - if (dval(d) > 0.5 + dval(eps)) - goto bump_up; - else if (dval(d) < 0.5 - dval(eps)) { - while(*--s == '0'); - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = s0; - dval(d) = dval(d2); - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || dval(d) < 5*ds || ((dval(d) == 5*ds) && !bias_round_up)) - goto no_digits; - goto one_digit; - } - - /* Limit looping by the number of digits to produce. - * Firefox had a crash bug because some plugins reduce - * the precision of double arithmetic. With reduced - * precision "dval(d) -= L*ds" might be imprecise and - * d might not become zero and the loop might not - * terminate. - * - * See https://bugzilla.mozilla.org/show_bug.cgi?id=358569 - */ - for(i = 1; i <= k+1; i++, dval(d) *= 10.) { - L = (Long)(dval(d) / ds); - dval(d) -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (dval(d) < 0) { - L--; - dval(d) += ds; - } -#endif - *s++ = '0' + (int)L; - if (!dval(d)) { -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - if (i == ilim) { -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(rounding) { - case 0: goto ret1; - case 2: goto bump_up; - } -#endif - dval(d) += dval(d); - if (dval(d) > ds || (dval(d) == ds && ((L & 1) || bias_round_up))) { - bump_up: - while(*--s == '9') - if (s == s0) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - } - goto ret1; - } - - m2 = b2; - m5 = b5; - mhi = mlo = 0; - if (leftright) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif -#ifdef IBM - 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); -#else - 1 + P - bbits; -#endif - b2 += i; - s2 += i; - mhi = i2b(1); - } - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); - b = b1; - } - if ((j = b5 - m5)) - b = pow5mult(b, j); - } - else - b = pow5mult(b, b5); - } - S = i2b(1); - if (s5 > 0) - S = pow5mult(S, s5); - - /* Check for special case that d is a normalized power of 2. */ - - spec_case = 0; - if ((mode < 2 || leftright) -#ifdef Honor_FLT_ROUNDS - && rounding == 1 -#endif - ) { - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & ~Exp_msk1) -#endif - ) { - /* The special case */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ -#ifdef Pack_32 - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) - i = 32 - i; -#else - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)) - i = 16 - i; -#endif - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - if (b2 > 0) - b = lshift(b, b2); - if (s2 > 0) - S = lshift(S, s2); - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (leftright) - mhi = multadd(mhi, 10, 0); - ilim = ilim1; - } - } - if (ilim <= 0 && (mode == 3 || mode == 5)) { - S = multadd(S, 5, 0); - if (ilim < 0 || cmp(b, S) < 0 || ((cmp(b, S) == 0) && !bias_round_up)) { - /* no digits, fcvt style */ - no_digits: - k = -1 - ndigits; - goto ret; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) - mhi = lshift(mhi, m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - } - - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); -#ifndef ROUND_BIASED - if (j1 == 0 && mode != 1 && !(word1(d) & 1) -#ifdef Honor_FLT_ROUNDS - && rounding >= 1 -#endif - ) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; -#ifdef SET_INEXACT - else if (!b->x[0] && b->wds <= 1) - inexact = 0; -#endif - *s++ = dig; - goto ret; - } -#endif - if (j < 0 || (j == 0 && mode != 1 -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto accept_dig; - } -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(rounding) { - case 0: goto accept_dig; - case 2: goto keep_dig; - } -#endif /*Honor_FLT_ROUNDS*/ - if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); - if ((j1 > 0 || (j1 == 0 && ((dig & 1) || bias_round_up))) - && dig++ == '9') - goto round_9_up; - } - accept_dig: - *s++ = dig; - goto ret; - } - if (j1 > 0) { -#ifdef Honor_FLT_ROUNDS - if (!rounding) - goto accept_dig; -#endif - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = dig + 1; - goto ret; - } -#ifdef Honor_FLT_ROUNDS - keep_dig: -#endif - *s++ = dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); - else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); - } - } - } - else - for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto ret; - } - if (i >= ilim) - break; - b = multadd(b, 10, 0); - } - - /* Round off last digit */ - -#ifdef Honor_FLT_ROUNDS - switch(rounding) { - case 0: goto trimzeros; - case 2: goto roundoff; - } -#endif - b = lshift(b, 1); - j = cmp(b, S); - if (j > 0 || (j == 0 && ((dig & 1) || bias_round_up))) { - roundoff: - while(*--s == '9') - if (s == s0) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { -/* trimzeros: (never used) */ - while(*--s == '0'); - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: -#ifdef SET_INEXACT - if (inexact) { - if (!oldinexact) { - word0(d) = Exp_1 + (70 << Exp_shift); - word1(d) = 0; - dval(d) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif - Bfree(b); - *s = 0; - *decpt = k + 1; - if (rve) - *rve = s; - return s0; - } -#ifdef __cplusplus -} -#endif diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 6270f8efaf..12b7a18e80 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -326,6 +326,8 @@ class Vector { return start_[index]; } + T& at(int i) const { return operator[](i); } + T& first() { return start_[0]; } T& last() { return start_[length_ - 1]; } diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index ae3250fb87..0e0a7cf42e 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 5 -#define BUILD_NUMBER 7 +#define BUILD_NUMBER 8 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 04173e1afb..14e352731f 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -80,8 +80,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ pop(rdx); __ push(rsi); __ push(rdx); + __ Push(Factory::false_value()); __ push(rcx); // Restore return address. - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 6e98a00900..5abf3c838c 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -4244,7 +4244,8 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // The inevitable call will sync frame elements to memory anyway, so // we do it eagerly to allow us to push the arguments directly into // place. @@ -4252,7 +4253,9 @@ void CodeGenerator::InstantiateFunction( // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame_->Push(function_info); Result answer = frame_->CallStub(&stub, 1); @@ -4262,7 +4265,10 @@ void CodeGenerator::InstantiateFunction( // shared function info. frame_->EmitPush(rsi); frame_->EmitPush(function_info); - Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); + frame_->EmitPush(pretenure + ? Factory::true_value() + : Factory::false_value()); + Result result = frame_->CallRuntime(Runtime::kNewClosure, 3); frame_->Push(&result); } } @@ -4279,14 +4285,14 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { SetStackOverflow(); return; } - InstantiateFunction(function_info); + InstantiateFunction(function_info, node->pretenure()); } void CodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* node) { Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - InstantiateFunction(node->shared_function_info()); + InstantiateFunction(node->shared_function_info(), false); } @@ -7217,6 +7223,11 @@ void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + frame_->Push(Factory::undefined_value()); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index c3270add26..1a5e7df31c 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -585,7 +585,8 @@ class CodeGenerator: public AstVisitor { void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - void InstantiateFunction(Handle<SharedFunctionInfo> function_info); + void InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for type checks. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -668,6 +669,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index e4b24ff4cd..ee80169bb9 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -837,17 +837,21 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ Push(info); __ CallStub(&stub); } else { __ push(rsi); __ Push(info); - __ CallRuntime(Runtime::kNewClosure, 2); + __ Push(pretenure ? Factory::true_value() : Factory::false_value()); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(rax); } @@ -2795,6 +2799,11 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + context()->Plug(Heap::kUndefinedValueRootIndex); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 2e60dd53b4..dbf93f5ff2 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -2588,8 +2588,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ push(receiver); __ push(holder_reg); __ Move(holder_reg, Handle<AccessorInfo>(callback)); - __ push(holder_reg); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); + __ push(holder_reg); __ push(name_reg); __ push(scratch2); // restore return address diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index b4f6914ed7..5322314417 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -6324,7 +6324,7 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter, int expected) { v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(getter); + templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle<Value> value = CompileRun(source); @@ -6335,7 +6335,8 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter, static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); - CHECK(v8_str("x")->Equals(name)); + CHECK_EQ(v8_str("data"), info.Data()); + CHECK_EQ(v8_str("x"), name); return v8::Integer::New(42); } @@ -6733,7 +6734,8 @@ THREADED_TEST(InterceptorStoreIC) { v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); templ->SetNamedPropertyHandler(InterceptorLoadICGetter, - InterceptorStoreICSetter); + InterceptorStoreICSetter, + 0, 0, 0, v8_str("data")); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle<Value> value = CompileRun( diff --git a/deps/v8/test/cctest/test-conversions.cc b/deps/v8/test/cctest/test-conversions.cc index c62bbaaabb..eef7184828 100644 --- a/deps/v8/test/cctest/test-conversions.cc +++ b/deps/v8/test/cctest/test-conversions.cc @@ -141,9 +141,6 @@ TEST(LongNumberStr) { } -extern "C" double gay_strtod(const char* s00, const char** se); - - TEST(MaximumSignificantDigits) { char num[] = "4.4501477170144020250819966727949918635852426585926051135169509" @@ -160,12 +157,12 @@ TEST(MaximumSignificantDigits) { "847003580761626016356864581135848683152156368691976240370422601" "6998291015625000000000000000000000000000000000e-308"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS)); // Changes the result of strtod (at least in glibc implementation). num[sizeof(num) - 8] = '1'; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS)); } TEST(MinimumExponent) { @@ -185,19 +182,19 @@ TEST(MinimumExponent) { "470035807616260163568645811358486831521563686919762403704226016" "998291015625000000000000000000000000000000000e-1108"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS)); // Changes the result of strtod (at least in glibc implementation). num[sizeof(num) - 8] = '1'; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS)); } TEST(MaximumExponent) { char num[] = "0.16e309"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(1.59999999999999997765e+308, StringToDouble(num, NO_FLAGS)); } diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index 748e3e8d91..7791185c79 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -6900,26 +6900,71 @@ TEST(DebugEventBreakData) { // Test that setting the terminate execution flag during debug break processing. +static void TestDebugBreakInLoop(const char* loop_head, + const char** loop_bodies, + const char* loop_tail) { + // Receive 100 breaks for each test and then terminate JavaScript execution. + static int count = 0; + + for (int i = 0; loop_bodies[i] != NULL; i++) { + count++; + max_break_point_hit_count = count * 100; + terminate_after_max_break_point_hit = true; + + EmbeddedVector<char, 1024> buffer; + OS::SNPrintF(buffer, + "function f() {%s%s%s}", + loop_head, loop_bodies[i], loop_tail); + + // Function with infinite loop. + CompileRun(buffer.start()); + + // Set the debug break to enter the debugger as soon as possible. + v8::Debug::DebugBreak(); + + // Call function with infinite loop. + CompileRun("f();"); + CHECK_EQ(count * 100, break_point_hit_count); + + CHECK(!v8::V8::IsExecutionTerminating()); + } +} + + TEST(DebugBreakLoop) { v8::HandleScope scope; DebugLocalContext env; - // Receive 100 breaks and terminate. - max_break_point_hit_count = 100; - terminate_after_max_break_point_hit = true; - // Register a debug event listener which sets the break flag and counts. v8::Debug::SetDebugEventListener(DebugEventBreakMax); - // Function with infinite loop. - CompileRun("function f() { while (true) { } }"); + CompileRun("var a = 1;"); + CompileRun("function g() { }"); + CompileRun("function h() { }"); + + const char* loop_bodies[] = { + "", + "g()", + "if (a == 0) { g() }", + "if (a == 1) { g() }", + "if (a == 0) { g() } else { h() }", + "if (a == 0) { continue }", + "if (a == 1) { continue }", + "switch (a) { case 1: g(); }", + "switch (a) { case 1: continue; }", + "switch (a) { case 1: g(); break; default: h() }", + "switch (a) { case 1: continue; break; default: h() }", + NULL + }; + + TestDebugBreakInLoop("while (true) {", loop_bodies, "}"); + TestDebugBreakInLoop("while (a == 1) {", loop_bodies, "}"); - // Set the debug break to enter the debugger as soon as possible. - v8::Debug::DebugBreak(); + TestDebugBreakInLoop("do {", loop_bodies, "} while (true)"); + TestDebugBreakInLoop("do {", loop_bodies, "} while (a == 1)"); - // Call function with infinite loop. - CompileRun("f();"); - CHECK_EQ(100, break_point_hit_count); + TestDebugBreakInLoop("for (;;) {", loop_bodies, "}"); + TestDebugBreakInLoop("for (;a == 1;) {", loop_bodies, "}"); // Get rid of the debug event listener. v8::Debug::SetDebugEventListener(NULL); diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index b165190b0a..4dd7fe823f 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -411,8 +411,12 @@ class NamedEntriesDetector { static const v8::HeapGraphNode* GetGlobalObject( const v8::HeapSnapshot* snapshot) { - CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount()); - return snapshot->GetRoot()->GetChild(0)->GetToNode(); + CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount()); + const v8::HeapGraphNode* global_obj = + snapshot->GetRoot()->GetChild(0)->GetToNode(); + CHECK_EQ("Object", const_cast<i::HeapEntry*>( + reinterpret_cast<const i::HeapEntry*>(global_obj))->name()); + return global_obj; } @@ -479,21 +483,24 @@ TEST(HeapSnapshot) { // Verify, that JS global object of env2 has '..2' properties. const v8::HeapGraphNode* a2_node = - GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2"); + GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2"); CHECK_NE(NULL, a2_node); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2")); - CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); - // Verify that anything related to '[ABC]1' is not reachable. NamedEntriesDetector det; i_snapshot_env2->IterateEntries(&det); CHECK(det.has_A2); CHECK(det.has_B2); CHECK(det.has_C2); + /* + // Currently disabled. Too many retaining paths emerge, need to + // reduce the amount. + // Verify 'a2' object retainers. They are: // - (global object).a2 // - c2.x1, c2.x2, c2[1] @@ -538,6 +545,7 @@ TEST(HeapSnapshot) { CHECK(has_c2_1_ref); CHECK(has_b2_1_x_ref); CHECK(has_b2_2_x_ref); + */ } @@ -550,37 +558,28 @@ TEST(HeapSnapshotObjectSizes) { CompileRun( "function X(a, b) { this.a = a; this.b = b; }\n" "x = new X(new X(), new X());\n" - "x.a.a = x.b;"); + "(function() { x.a.a = x.b; })();"); const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* x = - GetProperty(global, v8::HeapGraphEdge::kProperty, "x"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "x"); CHECK_NE(NULL, x); - const v8::HeapGraphNode* x_prototype = - GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__"); - CHECK_NE(NULL, x_prototype); const v8::HeapGraphNode* x1 = GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, x1); const v8::HeapGraphNode* x2 = GetProperty(x, v8::HeapGraphEdge::kProperty, "b"); CHECK_NE(NULL, x2); - CHECK_EQ( - x->GetSelfSize() * 3, - x->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x->GetSelfSize() * 3, x->GetRetainedSize()); - CHECK_EQ( - x1->GetSelfSize() * 2, - x1->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x1->GetSelfSize(), x1->GetRetainedSize()); - CHECK_EQ( - x2->GetSelfSize(), - x2->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x2->GetSelfSize(), x2->GetRetainedSize()); + + // Test approximate sizes. + CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false)); + CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false)); + CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false)); + // Test exact sizes. + CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true)); + CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true)); + CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true)); } @@ -622,15 +621,15 @@ TEST(HeapSnapshotCodeObjects) { const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* compiled = - GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled"); CHECK_NE(NULL, compiled); CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType()); const v8::HeapGraphNode* lazy = - GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy"); CHECK_NE(NULL, lazy); CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); const v8::HeapGraphNode* anonymous = - GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous"); CHECK_NE(NULL, anonymous); CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType()); v8::String::AsciiValue anonymous_name(anonymous->GetName()); @@ -682,9 +681,9 @@ TEST(HeapSnapshotHeapNumbers) { const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); - CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a")); + CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a")); const v8::HeapGraphNode* b = - GetProperty(global, v8::HeapGraphEdge::kProperty, "b"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "b"); CHECK_NE(NULL, b); CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); } @@ -808,12 +807,12 @@ TEST(HeapSnapshotsDiff) { if (node->GetType() == v8::HeapGraphNode::kObject) { v8::String::AsciiValue node_name(node->GetName()); if (strcmp(*node_name, "A2") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); CHECK(!found_A); found_A = true; s1_A_id = node->GetId(); } else if (strcmp(*node_name, "B") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "b2")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "b2")); CHECK(!found_B); found_B = true; } @@ -832,7 +831,7 @@ TEST(HeapSnapshotsDiff) { if (node->GetType() == v8::HeapGraphNode::kObject) { v8::String::AsciiValue node_name(node->GetName()); if (strcmp(*node_name, "A") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); CHECK(!found_A_del); found_A_del = true; s2_A_id = node->GetId(); @@ -858,37 +857,6 @@ TEST(HeapSnapshotRootPreservedAfterSorting) { } -namespace v8 { -namespace internal { - -class HeapSnapshotTester { - public: - static int CalculateNetworkSize(JSObject* obj) { - return HeapSnapshot::CalculateNetworkSize(obj); - } -}; - -} } // namespace v8::internal - -// http://code.google.com/p/v8/issues/detail?id=822 -// Trying to call CalculateNetworkSize on an object with elements set -// to non-FixedArray may cause an assertion error in debug builds. -TEST(Issue822) { - v8::HandleScope scope; - LocalContext context; - const int kElementCount = 260; - uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, - pixel_data); - v8::Handle<v8::Object> obj = v8::Object::New(); - // Set the elements to be the pixels. - obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); - i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); - // This call must not cause an assertion error in debug builds. - i::HeapSnapshotTester::CalculateNetworkSize(*jsobj); -} - - static const v8::HeapGraphNode* GetChild( const v8::HeapGraphNode* node, v8::HeapGraphNode::Type type, @@ -932,13 +900,13 @@ TEST(AggregatedHeapSnapshot) { v8::HeapProfiler::TakeSnapshot( v8::String::New("agg"), v8::HeapSnapshot::kAggregated); const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(), - v8::HeapGraphNode::kInternal, + v8::HeapGraphNode::kHidden, "STRING_TYPE"); CHECK_NE(NULL, strings); CHECK_NE(0, strings->GetSelfSize()); CHECK_NE(0, strings->GetInstancesCount()); const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(), - v8::HeapGraphNode::kInternal, + v8::HeapGraphNode::kHidden, "MAP_TYPE"); CHECK_NE(NULL, maps); CHECK_NE(0, maps->GetSelfSize()); @@ -998,6 +966,67 @@ TEST(AggregatedHeapSnapshot) { CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. } + +TEST(HeapEntryDominator) { + // The graph looks like this: + // + // -> node1 + // a |^ + // -> node5 ba + // a v| + // node6 -> node2 + // b a |^ + // -> node4 ba + // b v| + // -> node3 + // + // The dominator for all nodes is node6. + + v8::HandleScope scope; + LocalContext env; + + CompileRun( + "function X(a, b) { this.a = a; this.b = b; }\n" + "node6 = new X(new X(new X()), new X(new X(),new X()));\n" + "(function(){\n" + "node6.a.a.b = node6.b.a; // node1 -> node2\n" + "node6.b.a.a = node6.a.a; // node2 -> node1\n" + "node6.b.a.b = node6.b.b; // node2 -> node3\n" + "node6.b.b.a = node6.b.a; // node3 -> node2\n" + "})();"); + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators")); + + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_NE(NULL, global); + const v8::HeapGraphNode* node6 = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6"); + CHECK_NE(NULL, node6); + const v8::HeapGraphNode* node5 = + GetProperty(node6, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node5); + const v8::HeapGraphNode* node4 = + GetProperty(node6, v8::HeapGraphEdge::kProperty, "b"); + CHECK_NE(NULL, node4); + const v8::HeapGraphNode* node3 = + GetProperty(node4, v8::HeapGraphEdge::kProperty, "b"); + CHECK_NE(NULL, node3); + const v8::HeapGraphNode* node2 = + GetProperty(node4, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node2); + const v8::HeapGraphNode* node1 = + GetProperty(node5, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node1); + + CHECK_EQ(node6, node1->GetDominatorNode()); + CHECK_EQ(node6, node2->GetDominatorNode()); + CHECK_EQ(node6, node3->GetDominatorNode()); + CHECK_EQ(node6, node4->GetDominatorNode()); + CHECK_EQ(node6, node5->GetDominatorNode()); +} + + namespace { class TestJSONStream : public v8::OutputStream { @@ -1073,13 +1102,9 @@ TEST(HeapSnapshotJSONSerialization) { CHECK(parsed_snapshot->Has(v8::String::New("nodes"))); CHECK(parsed_snapshot->Has(v8::String::New("strings"))); - // Verify that nodes meta-info is valid JSON. - v8::Local<v8::Value> nodes_meta_parse_result = CompileRun( - "var parsed_meta = JSON.parse(parsed.nodes[0]); true;"); - CHECK(!nodes_meta_parse_result.IsEmpty()); - // Get node and edge "member" offsets. v8::Local<v8::Value> meta_analysis_result = CompileRun( + "var parsed_meta = parsed.nodes[0];\n" "var children_count_offset =" " parsed_meta.fields.indexOf('children_count');\n" "var children_offset =" @@ -1094,19 +1119,21 @@ TEST(HeapSnapshotJSONSerialization) { "var child_to_node_offset =" " children_meta.fields.indexOf('to_node');\n" "var property_type =" - " children_meta.types[child_type_offset].indexOf('property');"); + " children_meta.types[child_type_offset].indexOf('property');\n" + "var shortcut_type =" + " children_meta.types[child_type_offset].indexOf('shortcut');"); CHECK(!meta_analysis_result.IsEmpty()); // A helper function for processing encoded nodes. CompileRun( - "function GetChildPosByProperty(pos, prop_name) {\n" + "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" " var nodes = parsed.nodes;\n" " var strings = parsed.strings;\n" " for (var i = 0,\n" " count = nodes[pos + children_count_offset] * child_fields_count;\n" " i < count; i += child_fields_count) {\n" " var child_pos = pos + children_offset + i;\n" - " if (nodes[child_pos + child_type_offset] === property_type\n" + " if (nodes[child_pos + child_type_offset] === prop_type\n" " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n" " return nodes[child_pos + child_to_node_offset];\n" " }\n" @@ -1117,9 +1144,10 @@ TEST(HeapSnapshotJSONSerialization) { "GetChildPosByProperty(\n" " GetChildPosByProperty(\n" " GetChildPosByProperty(" - " parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n" - " \"x\")," - " \"s\")"); + " parsed.nodes[1 + children_offset + child_to_node_offset]," + " \"b\",shortcut_type),\n" + " \"x\", property_type)," + " \"s\", property_type)"); CHECK(!string_obj_pos_val.IsEmpty()); int string_obj_pos = static_cast<int>(string_obj_pos_val->ToNumber()->Value()); diff --git a/deps/v8/test/cctest/test-parsing.cc b/deps/v8/test/cctest/test-parsing.cc index d661af6c58..243d47884a 100755 --- a/deps/v8/test/cctest/test-parsing.cc +++ b/deps/v8/test/cctest/test-parsing.cc @@ -36,7 +36,6 @@ #include "parser.h" #include "utils.h" #include "execution.h" -#include "scanner.h" #include "preparser.h" #include "cctest.h" @@ -262,9 +261,10 @@ TEST(StandAlonePreParser) { const char* program = programs[i]; unibrow::Utf8InputBuffer<256> stream(program, strlen(program)); i::CompleteParserRecorder log; - i::Scanner scanner; - scanner.Initialize(i::Handle<i::String>::null(), &stream, i::JAVASCRIPT); - v8::preparser::PreParser<i::Scanner, i::CompleteParserRecorder> preparser; + i::V8JavaScriptScanner scanner; + scanner.Initialize(i::Handle<i::String>::null(), &stream); + v8::preparser::PreParser<i::V8JavaScriptScanner, + i::CompleteParserRecorder> preparser; bool result = preparser.PreParseProgram(&scanner, &log, true); CHECK(result); i::ScriptDataImpl data(log.ExtractData()); diff --git a/deps/v8/test/mjsunit/compiler/literals.js b/deps/v8/test/mjsunit/compiler/literals.js index 6775401d44..d846cf5b78 100644 --- a/deps/v8/test/mjsunit/compiler/literals.js +++ b/deps/v8/test/mjsunit/compiler/literals.js @@ -34,6 +34,43 @@ assertEquals("abc", eval("'abc'")); assertEquals(8, eval("6;'abc';8")); +// Characters just outside the ranges of hex-escapes. +// "/" comes just before "0". +assertEquals("x1/", "\x1/"); +assertEquals("u111/", "\u111/"); +assertEquals("\\x1/", RegExp("\\x1/").source); +assertEquals("\\u111/", RegExp("\\u111/").source); + +// ":" comes just after "9". +assertEquals("x1:", "\x1:"); +assertEquals("u111:", "\u111:"); +assertEquals("\\x1:", /\x1:/.source); +assertEquals("\\u111:", /\u111:/.source); + +// "`" comes just before "a". +assertEquals("x1`", "\x1`"); +assertEquals("u111`", "\u111`"); +assertEquals("\\x1`", /\x1`/.source); +assertEquals("\\u111`", /\u111`/.source); + +// "g" comes just before "f". +assertEquals("x1g", "\x1g"); +assertEquals("u111g", "\u111g"); +assertEquals("\\x1g", /\x1g/.source); +assertEquals("\\u111g", /\u111g/.source); + +// "@" comes just before "A". +assertEquals("x1@", "\x1@"); +assertEquals("u111@", "\u111@"); +assertEquals("\\x1@", /\x1@/.source); +assertEquals("\\u111@", /\u111@/.source); + +// "G" comes just after "F". +assertEquals("x1G", "\x1G"); +assertEquals("u111G", "\u111G"); +assertEquals("\\x1G", /\x1G/.source); +assertEquals("\\u111G", /\u111G/.source); + // Test some materialized array literals. assertEquals([1,2,3,4], eval('[1,2,3,4]')); assertEquals([[1,2],3,4], eval('[[1,2],3,4]')); @@ -50,3 +87,4 @@ assertEquals([2,4,6,8], eval(s)); assertEquals(17, eval('[1,2,3,4]; 17')); assertEquals(19, eval('var a=1, b=2; [a,b,3,4]; 19')); assertEquals(23, eval('var a=1, b=2; c=23; [a,b,3,4]; c')); + diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index 30b15a9737..43e1bd4150 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -333,7 +333,6 @@ '../../src/disassembler.h', '../../src/dtoa.cc', '../../src/dtoa.h', - '../../src/dtoa-config.c', '../../src/diy-fp.cc', '../../src/diy-fp.h', '../../src/double.h', @@ -657,11 +656,7 @@ '../../src/platform-win32.cc', ], # 4355, 4800 came from common.vsprops - # 4018, 4244 were a per file config on dtoa-config.c - # TODO: It's probably possible and desirable to stop disabling the - # dtoa-specific warnings by modifying dtoa as was done in Chromium - # r9255. Refer to that revision for details. - 'msvs_disabled_warnings': [4355, 4800, 4018, 4244], + 'msvs_disabled_warnings': [4355, 4800], 'link_settings': { 'libraries': [ '-lwinmm.lib' ], }, diff --git a/deps/v8/tools/visual_studio/README.txt b/deps/v8/tools/visual_studio/README.txt index dd9802b413..c46aa3711a 100644 --- a/deps/v8/tools/visual_studio/README.txt +++ b/deps/v8/tools/visual_studio/README.txt @@ -7,8 +7,7 @@ be performed by Visual Studio. v8_base.vcproj -------------- -Base V8 library containing all the V8 code but no JavaScript library code. This -includes third party code for string/number convertions (dtoa). +Base V8 library containing all the V8 code but no JavaScript library code. v8.vcproj --------- diff --git a/deps/v8/tools/visual_studio/v8_base.vcproj b/deps/v8/tools/visual_studio/v8_base.vcproj index 615889c916..95eb196d31 100644 --- a/deps/v8/tools/visual_studio/v8_base.vcproj +++ b/deps/v8/tools/visual_studio/v8_base.vcproj @@ -122,70 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - <File - RelativePath="..\..\src\bignum.cc" - > - </File> - <File - RelativePath="..\..\src\bignum.h" - > - </File> - <File - RelativePath="..\..\src\bignum-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\bignum-dtoa.h" - > - </File> - <File - RelativePath="..\..\src\dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\dtoa.h" - > - </File> - <File - RelativePath="..\..\src\fast-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\fast-dtoa.h" - > - </File> - <File - RelativePath="..\..\src\fixed-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\fixed-dtoa.h" - > - </File> - </Filter> - <Filter Name="src" > <File @@ -297,6 +233,10 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File RelativePath="..\..\src\cached-powers.h" > </File> @@ -489,6 +429,14 @@ > </File> <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -513,6 +461,14 @@ > </File> <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -994,6 +950,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\ia32\stub-cache-ia32.cc" > </File> diff --git a/deps/v8/tools/visual_studio/v8_base_arm.vcproj b/deps/v8/tools/visual_studio/v8_base_arm.vcproj index 4f9ff4caeb..b87fdf8da4 100644 --- a/deps/v8/tools/visual_studio/v8_base_arm.vcproj +++ b/deps/v8/tools/visual_studio/v8_base_arm.vcproj @@ -122,30 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - </Filter> - <Filter Name="src" > <File @@ -229,6 +205,22 @@ > </File> <File + RelativePath="..\..\src\bignum.cc" + > + </File> + <File + RelativePath="..\..\src\bignum.h" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.h" + > + </File> + <File RelativePath="..\..\src\builtins.cc" > </File> @@ -241,6 +233,14 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File + RelativePath="..\..\src\cached-powers.h" + > + </File> + <File RelativePath="..\..\src\char-predicates-inl.h" > </File> @@ -425,6 +425,26 @@ > </File> <File + RelativePath="..\..\src\diy-fp.cc" + > + </File> + <File + RelativePath="..\..\src\diy-fp.h" + > + </File> + <File + RelativePath="..\..\src\double.h" + > + </File> + <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -441,6 +461,22 @@ > </File> <File + RelativePath="..\..\src\fast-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fast-dtoa.h" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -928,6 +964,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\arm\stub-cache-arm.cc" > </File> diff --git a/deps/v8/tools/visual_studio/v8_base_x64.vcproj b/deps/v8/tools/visual_studio/v8_base_x64.vcproj index c84bce2d82..6d27472ae3 100644 --- a/deps/v8/tools/visual_studio/v8_base_x64.vcproj +++ b/deps/v8/tools/visual_studio/v8_base_x64.vcproj @@ -122,30 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|x64" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|x64" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - </Filter> - <Filter Name="src" > <File @@ -217,6 +193,22 @@ > </File> <File + RelativePath="..\..\src\bignum.cc" + > + </File> + <File + RelativePath="..\..\src\bignum.h" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.h" + > + </File> + <File RelativePath="..\..\src\bootstrapper.cc" > </File> @@ -241,6 +233,14 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File + RelativePath="..\..\src\cached-powers.h" + > + </File> + <File RelativePath="..\..\src\char-predicates-inl.h" > </File> @@ -417,6 +417,26 @@ > </File> <File + RelativePath="..\..\src\diy-fp.cc" + > + </File> + <File + RelativePath="..\..\src\diy-fp.h" + > + </File> + <File + RelativePath="..\..\src\double.h" + > + </File> + <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -433,6 +453,22 @@ > </File> <File + RelativePath="..\..\src\fast-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fast-dtoa.h" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -914,6 +950,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\x64\stub-cache-x64.cc" > </File> |