Version 3.12.9
Correctly advance the scanner when scanning unicode regexp flag. (Chromium issue 136084)
Fixed unhandlified code calling Harmony Proxy traps. (issue 2219)
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@12006 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/SConscript b/src/SConscript
index 2482b37..cd35910 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -43,8 +43,8 @@
assembler.cc
ast.cc
atomicops_internals_x86_gcc.cc
- bignum.cc
bignum-dtoa.cc
+ bignum.cc
bootstrapper.cc
builtins.cc
cached-powers.cc
@@ -67,27 +67,29 @@
disassembler.cc
diy-fp.cc
dtoa.cc
- elements.cc
elements-kind.cc
+ elements.cc
execution.cc
+ extensions/externalize-string-extension.cc
+ extensions/gc-extension.cc
factory.cc
+ fast-dtoa.cc
+ fixed-dtoa.cc
flags.cc
frames.cc
full-codegen.cc
func-name-inferrer.cc
gdb-jit.cc
global-handles.cc
- fast-dtoa.cc
- fixed-dtoa.cc
handles.cc
heap-profiler.cc
heap.cc
- hydrogen.cc
hydrogen-instructions.cc
+ hydrogen.cc
ic.cc
incremental-marking.cc
- interface.cc
inspector.cc
+ interface.cc
interpreter-irregexp.cc
isolate.cc
jsregexp.cc
@@ -99,34 +101,36 @@
log.cc
mark-compact.cc
messages.cc
- objects.cc
objects-printer.cc
objects-visiting.cc
+ objects.cc
once.cc
parser.cc
- preparser.cc
preparse-data.cc
+ preparser.cc
profile-generator.cc
property.cc
regexp-macro-assembler-irregexp.cc
regexp-macro-assembler.cc
regexp-stack.cc
rewriter.cc
- runtime.cc
runtime-profiler.cc
+ runtime.cc
safepoint-table.cc
- scanner.cc
scanner-character-streams.cc
+ scanner.cc
scopeinfo.cc
scopes.cc
serialize.cc
snapshot-common.cc
spaces.cc
+ store-buffer.cc
string-search.cc
string-stream.cc
strtod.cc
stub-cache.cc
token.cc
+ transitions.cc
type-info.cc
unicode.cc
utils.cc
@@ -137,10 +141,7 @@
v8utils.cc
variables.cc
version.cc
- store-buffer.cc
zone.cc
- extensions/gc-extension.cc
- extensions/externalize-string-extension.cc
"""),
'arch:arm': Split("""
arm/builtins-arm.cc
diff --git a/src/api.cc b/src/api.cc
index e3596be..4b1a3a1 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -6385,12 +6385,28 @@
void HandleScopeImplementer::IterateThis(ObjectVisitor* v) {
+#ifdef DEBUG
+ bool found_block_before_deferred = false;
+#endif
// Iterate over all handles in the blocks except for the last.
for (int i = blocks()->length() - 2; i >= 0; --i) {
Object** block = blocks()->at(i);
- v->VisitPointers(block, &block[kHandleBlockSize]);
+ if (last_handle_before_deferred_block_ != NULL &&
+ (last_handle_before_deferred_block_ < &block[kHandleBlockSize]) &&
+ (last_handle_before_deferred_block_ >= block)) {
+ v->VisitPointers(block, last_handle_before_deferred_block_);
+ ASSERT(!found_block_before_deferred);
+#ifdef DEBUG
+ found_block_before_deferred = true;
+#endif
+ } else {
+ v->VisitPointers(block, &block[kHandleBlockSize]);
+ }
}
+ ASSERT(last_handle_before_deferred_block_ == NULL ||
+ found_block_before_deferred);
+
// Iterate over live handles in the last block (if any).
if (!blocks()->is_empty()) {
v->VisitPointers(blocks()->last(), handle_scope_data_.next);
@@ -6400,6 +6416,12 @@
Object** start = reinterpret_cast<Object**>(&saved_contexts_.first());
v->VisitPointers(start, start + saved_contexts_.length());
}
+
+ for (DeferredHandles* deferred = deferred_handles_head_;
+ deferred != NULL;
+ deferred = deferred->next_) {
+ deferred->Iterate(v);
+ }
}
@@ -6418,4 +6440,88 @@
return storage + ArchiveSpacePerThread();
}
+
+DeferredHandles* HandleScopeImplementer::Detach(Object** prev_limit) {
+ DeferredHandles* deferred = new DeferredHandles(
+ deferred_handles_head_, isolate()->handle_scope_data()->next, this);
+
+ while (!blocks_.is_empty()) {
+ Object** block_start = blocks_.last();
+ Object** block_limit = &block_start[kHandleBlockSize];
+ // We should not need to check for NoHandleAllocation here. Assert
+ // this.
+ ASSERT(prev_limit == block_limit ||
+ !(block_start <= prev_limit && prev_limit <= block_limit));
+ if (prev_limit == block_limit) break;
+ deferred->blocks_.Add(blocks_.last());
+ blocks_.RemoveLast();
+ }
+
+ // deferred->blocks_ now contains the blocks installed on the
+ // HandleScope stack since BeginDeferredScope was called, but in
+ // reverse order.
+
+ ASSERT(prev_limit == NULL || !blocks_.is_empty());
+
+ ASSERT(!blocks_.is_empty() && prev_limit != NULL);
+ deferred_handles_head_ = deferred;
+ ASSERT(last_handle_before_deferred_block_ != NULL);
+ last_handle_before_deferred_block_ = NULL;
+ return deferred;
+}
+
+
+void HandleScopeImplementer::DestroyDeferredHandles(DeferredHandles* deferred) {
+#ifdef DEBUG
+ DeferredHandles* deferred_iterator = deferred;
+ while (deferred_iterator->previous_ != NULL) {
+ deferred_iterator = deferred_iterator->previous_;
+ }
+ ASSERT(deferred_handles_head_ == deferred_iterator);
+#endif
+ if (deferred_handles_head_ == deferred) {
+ deferred_handles_head_ = deferred_handles_head_->next_;
+ }
+ if (deferred->next_ != NULL) {
+ deferred->next_->previous_ = deferred->previous_;
+ }
+ if (deferred->previous_ != NULL) {
+ deferred->previous_->next_ = deferred->next_;
+ }
+ for (int i = 0; i < deferred->blocks_.length(); i++) {
+#ifdef DEBUG
+ HandleScope::ZapRange(deferred->blocks_[i],
+ &deferred->blocks_[i][kHandleBlockSize]);
+#endif
+ if (spare_ != NULL) DeleteArray(spare_);
+ spare_ = deferred->blocks_[i];
+ }
+}
+
+
+void HandleScopeImplementer::BeginDeferredScope() {
+ ASSERT(last_handle_before_deferred_block_ == NULL);
+ last_handle_before_deferred_block_ = isolate()->handle_scope_data()->next;
+}
+
+
+DeferredHandles::~DeferredHandles() {
+ impl_->DestroyDeferredHandles(this);
+}
+
+
+void DeferredHandles::Iterate(ObjectVisitor* v) {
+ ASSERT(!blocks_.is_empty());
+
+ ASSERT((first_block_limit_ >= blocks_.first()) &&
+ (first_block_limit_ < &(blocks_.first())[kHandleBlockSize]));
+
+ v->VisitPointers(blocks_.first(), first_block_limit_);
+
+ for (int i = 1; i < blocks_.length(); i++) {
+ v->VisitPointers(blocks_[i], &blocks_[i][kHandleBlockSize]);
+ }
+}
+
+
} } // namespace v8::internal
diff --git a/src/api.h b/src/api.h
index 58e6a6e..2341c4c 100644
--- a/src/api.h
+++ b/src/api.h
@@ -392,6 +392,28 @@
};
+class DeferredHandles {
+ public:
+ ~DeferredHandles();
+
+ private:
+ DeferredHandles(DeferredHandles* next, Object** first_block_limit,
+ HandleScopeImplementer* impl)
+ : next_(next), previous_(NULL), first_block_limit_(first_block_limit),
+ impl_(impl) {}
+
+ void Iterate(ObjectVisitor* v);
+
+ List<Object**> blocks_;
+ DeferredHandles* next_;
+ DeferredHandles* previous_;
+ Object** first_block_limit_;
+ HandleScopeImplementer* impl_;
+
+ friend class HandleScopeImplementer;
+};
+
+
// This class is here in order to be able to declare it a friend of
// HandleScope. Moving these methods to be members of HandleScope would be
// neat in some ways, but it would expose internal implementation details in
@@ -409,7 +431,9 @@
entered_contexts_(0),
saved_contexts_(0),
spare_(NULL),
- call_depth_(0) { }
+ call_depth_(0),
+ last_handle_before_deferred_block_(NULL),
+ deferred_handles_head_(NULL) { }
~HandleScopeImplementer() {
DeleteArray(spare_);
@@ -445,6 +469,7 @@
inline bool HasSavedContexts();
inline List<internal::Object**>* blocks() { return &blocks_; }
+ Isolate* isolate() const { return isolate_; }
private:
void ResetAfterArchive() {
@@ -452,6 +477,8 @@
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
spare_ = NULL;
+ deferred_handles_head_ = NULL;
+ last_handle_before_deferred_block_ = NULL;
call_depth_ = 0;
}
@@ -459,6 +486,7 @@
ASSERT(blocks_.length() == 0);
ASSERT(entered_contexts_.length() == 0);
ASSERT(saved_contexts_.length() == 0);
+ ASSERT(deferred_handles_head_ == NULL);
blocks_.Free();
entered_contexts_.Free();
saved_contexts_.Free();
@@ -469,6 +497,10 @@
ASSERT(call_depth_ == 0);
}
+ void BeginDeferredScope();
+ DeferredHandles* Detach(Object** prev_limit);
+ void DestroyDeferredHandles(DeferredHandles* handles);
+
Isolate* isolate_;
List<internal::Object**> blocks_;
// Used as a stack to keep track of entered contexts.
@@ -477,6 +509,8 @@
List<Context*> saved_contexts_;
Object** spare_;
int call_depth_;
+ Object** last_handle_before_deferred_block_;
+ DeferredHandles* deferred_handles_head_;
// This is only used for threading support.
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
@@ -484,6 +518,9 @@
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
+ friend class DeferredHandles;
+ friend class DeferredHandleScope;
+
DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
};
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index e0633d5..36c67d0 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -2577,7 +2577,7 @@
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
- type->LookupInDescriptors(NULL, *name, &lookup);
+ type->LookupTransitionOrDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
diff --git a/src/ast.cc b/src/ast.cc
index 52f452b..69ee2fa 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -503,7 +503,7 @@
}
LookupResult lookup(type->GetIsolate());
while (true) {
- type->LookupInDescriptors(NULL, *name, &lookup);
+ type->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
switch (lookup.type()) {
case CONSTANT_FUNCTION:
@@ -518,10 +518,7 @@
case INTERCEPTOR:
// We don't know the target.
return false;
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- // Perhaps something interesting is up in the prototype chain...
- break;
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
diff --git a/src/ast.h b/src/ast.h
index 2ebf7f9..a2aea52 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1584,7 +1584,12 @@
virtual bool IsMonomorphic() { return is_monomorphic_; }
CheckType check_type() const { return check_type_; }
Handle<JSFunction> target() { return target_; }
+
+ // A cache for the holder, set as a side effect of computing the target of the
+ // call. Note that it contains the null handle when the receiver is the same
+ // as the holder!
Handle<JSObject> holder() { return holder_; }
+
Handle<JSGlobalPropertyCell> cell() { return cell_; }
bool ComputeTarget(Handle<Map> type, Handle<String> name);
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index e910a43..4238a12 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -2191,14 +2191,11 @@
JSObject::SetNormalizedProperty(to, key, callbacks, d);
break;
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- // Ignore non-properties.
- break;
case NORMAL:
// Do not occur since the from object has fast properties.
case HANDLER:
case INTERCEPTOR:
+ case TRANSITION:
case NONEXISTENT:
// No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();
diff --git a/src/compiler.cc b/src/compiler.cc
index d35532f..e282a7f 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -61,7 +61,8 @@
extension_(NULL),
pre_parse_data_(NULL),
osr_ast_id_(AstNode::kNoNumber),
- zone_(zone) {
+ zone_(zone),
+ deferred_handles_(NULL) {
Initialize(BASE);
}
@@ -79,7 +80,8 @@
extension_(NULL),
pre_parse_data_(NULL),
osr_ast_id_(AstNode::kNoNumber),
- zone_(zone) {
+ zone_(zone),
+ deferred_handles_(NULL) {
Initialize(BASE);
}
@@ -97,11 +99,17 @@
extension_(NULL),
pre_parse_data_(NULL),
osr_ast_id_(AstNode::kNoNumber),
- zone_(zone) {
+ zone_(zone),
+ deferred_handles_(NULL) {
Initialize(BASE);
}
+CompilationInfo::~CompilationInfo() {
+ delete deferred_handles_;
+}
+
+
// Disable optimization for the rest of the compilation pipeline.
void CompilationInfo::DisableOptimization() {
bool is_optimizable_closure =
@@ -184,17 +192,9 @@
static bool MakeCrankshaftCode(CompilationInfo* info) {
- // Test if we can optimize this function when asked to. We can only
- // do this after the scopes are computed.
- if (!V8::UseCrankshaft()) {
- info->DisableOptimization();
- }
-
- // In case we are not optimizing simply return the code from
- // the full code generator.
- if (!info->IsOptimizing()) {
- return FullCodeGenerator::MakeCode(info);
- }
+ ASSERT(V8::UseCrankshaft());
+ ASSERT(info->IsOptimizing());
+ ASSERT(!info->IsCompilingForDebugging());
// We should never arrive here if there is not code object on the
// shared function object.
@@ -330,9 +330,19 @@
static bool GenerateCode(CompilationInfo* info) {
- return info->IsCompilingForDebugging() || !V8::UseCrankshaft() ?
- FullCodeGenerator::MakeCode(info) :
- MakeCrankshaftCode(info);
+ bool is_optimizing = V8::UseCrankshaft() &&
+ !info->IsCompilingForDebugging() &&
+ info->IsOptimizing();
+ if (is_optimizing) {
+ return MakeCrankshaftCode(info);
+ } else {
+ if (info->IsOptimizing()) {
+ // Have the CompilationInfo decide if the compilation should be
+ // BASE or NONOPT.
+ info->DisableOptimization();
+ }
+ return FullCodeGenerator::MakeCode(info);
+ }
}
@@ -762,8 +772,7 @@
if (FLAG_lazy && allow_lazy) {
Handle<Code> code = info.isolate()->builtins()->LazyCompile();
info.SetCode(code);
- } else if ((V8::UseCrankshaft() && MakeCrankshaftCode(&info)) ||
- (!V8::UseCrankshaft() && FullCodeGenerator::MakeCode(&info))) {
+ } else if (GenerateCode(&info)) {
ASSERT(!info.code().is_null());
scope_info = ScopeInfo::Create(info.scope(), info.zone());
} else {
diff --git a/src/compiler.h b/src/compiler.h
index 26c0ac4..1359048 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -45,6 +45,8 @@
CompilationInfo(Handle<SharedFunctionInfo> shared_info, Zone* zone);
CompilationInfo(Handle<JSFunction> closure, Zone* zone);
+ ~CompilationInfo();
+
Isolate* isolate() {
ASSERT(Isolate::Current() == isolate_);
return isolate_;
@@ -173,6 +175,11 @@
// current compilation pipeline.
void AbortOptimization();
+ void set_deferred_handles(DeferredHandles* deferred_handles) {
+ ASSERT(deferred_handles_ == NULL);
+ deferred_handles_ = deferred_handles;
+ }
+
private:
Isolate* isolate_;
@@ -259,6 +266,8 @@
// CompilationInfo allocates.
Zone* zone_;
+ DeferredHandles* deferred_handles_;
+
DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
};
@@ -286,6 +295,23 @@
};
+// A wrapper around a CompilationInfo that detaches the Handles from
+// the underlying DeferredHandleScope and stores them in info_ on
+// destruction.
+class CompilationHandleScope BASE_EMBEDDED {
+ public:
+ explicit CompilationHandleScope(CompilationInfo* info)
+ : deferred_(info->isolate()), info_(info) {}
+ ~CompilationHandleScope() {
+ info_->set_deferred_handles(deferred_.Detach());
+ }
+
+ private:
+ DeferredHandleScope deferred_;
+ CompilationInfo* info_;
+};
+
+
// The V8 compiler
//
// General strategy: Source code is translated into an anonymous function w/o
diff --git a/src/debug.cc b/src/debug.cc
index 777e23d..40ddabb 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -892,6 +892,7 @@
}
+// TODO(131642): Remove this when fixed.
void Debug::PutValuesOnStackAndDie(int start,
Address c_entry_fp,
Address last_fp,
@@ -1009,6 +1010,7 @@
it.Advance();
}
+ // TODO(131642): Remove this when fixed.
// Catch the cases that would lead to crashes and capture
// - C entry FP at which to start stack crawl.
// - FP of the frame at which we plan to stop stepping out (last FP).
@@ -1017,18 +1019,23 @@
// - stack trace string.
if (it.done()) {
// We crawled the entire stack, never reaching last_fp_.
+ Handle<String> stack = isolate_->StackTraceString();
+ char buffer[8192];
+ int length = Min(8192, stack->length());
+ String::WriteToFlat(*stack, buffer, 0, length - 1);
PutValuesOnStackAndDie(0xBEEEEEEE,
frame->fp(),
thread_local_.last_fp_,
reinterpret_cast<Address>(0xDEADDEAD),
count,
- NULL,
+ buffer,
0xCEEEEEEE);
} else if (it.frame()->fp() != thread_local_.last_fp_) {
// We crawled over last_fp_, without getting a match.
Handle<String> stack = isolate_->StackTraceString();
char buffer[8192];
- String::WriteToFlat(*stack, buffer, 0, 8191);
+ int length = Min(8192, stack->length());
+ String::WriteToFlat(*stack, buffer, 0, length - 1);
PutValuesOnStackAndDie(0xDEEEEEEE,
frame->fp(),
thread_local_.last_fp_,
diff --git a/src/debug.h b/src/debug.h
index 9b5c250..f3215c9 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -232,6 +232,7 @@
void PreemptionWhileInDebugger();
void Iterate(ObjectVisitor* v);
+ // TODO(131642): Remove this when fixed.
NO_INLINE(void PutValuesOnStackAndDie(int start,
Address c_entry_fp,
Address last_fp,
diff --git a/src/factory.cc b/src/factory.cc
index 46d4d37..3dd7a51 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -894,7 +894,7 @@
Object* value,
PropertyAttributes attributes) {
CallbacksDescriptor desc(key, value, attributes);
- MaybeObject* obj = array->CopyInsert(&desc, REMOVE_TRANSITIONS);
+ MaybeObject* obj = array->CopyInsert(&desc);
return obj;
}
@@ -944,7 +944,7 @@
Handle<String> key =
SymbolFromString(Handle<String>(String::cast(entry->name())));
// Check if a descriptor with this name already exists before writing.
- if (result->LinearSearch(EXPECT_UNSORTED, *key, descriptor_count) ==
+ if (LinearSearch(*result, EXPECT_UNSORTED, *key, descriptor_count) ==
DescriptorArray::kNotFound) {
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
result->Set(descriptor_count, &desc, witness);
diff --git a/src/handles.cc b/src/handles.cc
index def1604..e98e843 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -729,7 +729,7 @@
Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (descs->IsProperty(i) && !descs->GetDetails(i).IsDontEnum()) {
+ if (!descs->GetDetails(i).IsDontEnum()) {
storage->set(index, descs->GetKey(i));
PropertyDetails details = descs->GetDetails(i);
sort_array->set(index, Smi::FromInt(details.index()));
@@ -958,4 +958,47 @@
return len;
}
+
+DeferredHandleScope::DeferredHandleScope(Isolate* isolate)
+ : impl_(isolate->handle_scope_implementer()) {
+ ASSERT(impl_->isolate() == Isolate::Current());
+ impl_->BeginDeferredScope();
+ v8::ImplementationUtilities::HandleScopeData* data =
+ impl_->isolate()->handle_scope_data();
+ Object** new_next = impl_->GetSpareOrNewBlock();
+ Object** new_limit = &new_next[kHandleBlockSize];
+ ASSERT(data->limit == &impl_->blocks()->last()[kHandleBlockSize]);
+ impl_->blocks()->Add(new_next);
+
+#ifdef DEBUG
+ prev_level_ = data->level;
+#endif
+ data->level++;
+ prev_limit_ = data->limit;
+ prev_next_ = data->next;
+ data->next = new_next;
+ data->limit = new_limit;
+}
+
+
+DeferredHandleScope::~DeferredHandleScope() {
+ impl_->isolate()->handle_scope_data()->level--;
+ ASSERT(handles_detached_);
+ ASSERT(impl_->isolate()->handle_scope_data()->level == prev_level_);
+}
+
+
+DeferredHandles* DeferredHandleScope::Detach() {
+ DeferredHandles* deferred = impl_->Detach(prev_limit_);
+ v8::ImplementationUtilities::HandleScopeData* data =
+ impl_->isolate()->handle_scope_data();
+ data->next = prev_next_;
+ data->limit = prev_limit_;
+#ifdef DEBUG
+ handles_detached_ = true;
+#endif
+ return deferred;
+}
+
+
} } // namespace v8::internal
diff --git a/src/handles.h b/src/handles.h
index 960696b..aca8690 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -95,6 +95,9 @@
};
+class HandleScopeImplementer;
+
+
// A stack-allocated class that governs a number of local handles.
// After a handle scope has been created, all local handles will be
// allocated within that handle scope until either the handle scope is
@@ -157,10 +160,37 @@
static void ZapRange(internal::Object** start, internal::Object** end);
friend class v8::HandleScope;
+ friend class v8::internal::HandleScopeImplementer;
friend class v8::ImplementationUtilities;
};
+class DeferredHandles;
+
+
+class DeferredHandleScope {
+ public:
+ explicit DeferredHandleScope(Isolate* isolate);
+ // The DeferredHandles object returned stores the Handles created
+ // since the creation of this DeferredHandleScope. The Handles are
+ // alive as long as the DeferredHandles object is alive.
+ DeferredHandles* Detach();
+ ~DeferredHandleScope();
+
+ private:
+ Object** prev_limit_;
+ Object** prev_next_;
+ HandleScopeImplementer* impl_;
+
+#ifdef DEBUG
+ bool handles_detached_;
+ int prev_level_;
+#endif
+
+ friend class HandleScopeImplementer;
+};
+
+
// ----------------------------------------------------------------------------
// Handle operations.
// They might invoke garbage collection. The result is an handle to
diff --git a/src/heap.h b/src/heap.h
index dd1f710..e9925c2 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -1392,15 +1392,15 @@
STRONG_ROOT_LIST(ROOT_INDEX_DECLARATION)
#undef ROOT_INDEX_DECLARATION
-// Utility type maps
-#define DECLARE_STRUCT_MAP(NAME, Name, name) k##Name##MapRootIndex,
- STRUCT_LIST(DECLARE_STRUCT_MAP)
-#undef DECLARE_STRUCT_MAP
-
#define SYMBOL_INDEX_DECLARATION(name, str) k##name##RootIndex,
SYMBOL_LIST(SYMBOL_INDEX_DECLARATION)
#undef SYMBOL_DECLARATION
+ // Utility type maps
+#define DECLARE_STRUCT_MAP(NAME, Name, name) k##Name##MapRootIndex,
+ STRUCT_LIST(DECLARE_STRUCT_MAP)
+#undef DECLARE_STRUCT_MAP
+
kSymbolTableRootIndex,
kStrongRootListLength = kSymbolTableRootIndex,
kRootListLength
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc
index d44e90a..70ed782 100644
--- a/src/hydrogen-instructions.cc
+++ b/src/hydrogen-instructions.cc
@@ -1638,9 +1638,10 @@
}
LookupResult lookup(isolate);
- JSObject::cast(current)->map()->LookupInDescriptors(NULL, *name, &lookup);
+ Map* map = JSObject::cast(current)->map();
+ map->LookupTransitionOrDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
- if (lookup.type() != MAP_TRANSITION) return false;
+ if (!lookup.IsTransition()) return false;
} else if (!lookup.IsCacheable()) {
return false;
}
@@ -1669,7 +1670,7 @@
++i) {
Handle<Map> map = types->at(i);
LookupResult lookup(map->GetIsolate());
- map->LookupInDescriptors(NULL, *name, &lookup);
+ map->LookupTransitionOrDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
switch (lookup.type()) {
case FIELD: {
@@ -1685,12 +1686,18 @@
case CONSTANT_FUNCTION:
types_.Add(types->at(i), zone);
break;
- case MAP_TRANSITION:
+ case CALLBACKS:
+ break;
+ case TRANSITION:
if (PrototypeChainCanNeverResolve(map, name)) {
negative_lookups.Add(types->at(i), zone);
}
break;
- default:
+ case INTERCEPTOR:
+ case NONEXISTENT:
+ case NORMAL:
+ case HANDLER:
+ UNREACHABLE();
break;
}
} else if (lookup.IsCacheable()) {
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 2d67183..dfde504 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -3053,6 +3053,7 @@
{
HPhase phase("H_Block building");
+ CompilationHandleScope handle_scope(info());
current_block_ = graph()->entry_block();
Scope* scope = info()->scope();
@@ -4917,17 +4918,18 @@
Handle<String> name,
LookupResult* lookup,
bool is_store) {
- type->LookupInDescriptors(NULL, *name, lookup);
+ type->LookupTransitionOrDescriptor(NULL, *name, lookup);
if (lookup->IsField()) return true;
- return is_store && lookup->IsMapTransition() &&
- (type->unused_property_fields() > 0);
+ return is_store &&
+ lookup->IsTransitionToField(*type) &&
+ (type->unused_property_fields() > 0);
}
static int ComputeLoadStoreFieldIndex(Handle<Map> type,
Handle<String> name,
LookupResult* lookup) {
- ASSERT(lookup->IsField() || lookup->type() == MAP_TRANSITION);
+ ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type));
if (lookup->IsField()) {
return lookup->GetLocalFieldIndexFromMap(*type);
} else {
@@ -4988,7 +4990,7 @@
}
HStoreNamedField* instr =
new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
- if (lookup->type() == MAP_TRANSITION) {
+ if (lookup->IsTransitionToField(*type)) {
Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
instr->set_transition(transition);
// TODO(fschneider): Record the new map type of the object in the IR to
@@ -5626,7 +5628,7 @@
Handle<Map> map,
Handle<String> name) {
LookupResult lookup(isolate());
- map->LookupInDescriptors(NULL, *name, &lookup);
+ map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsField()) {
return BuildLoadNamedField(obj,
expr,
@@ -6283,7 +6285,7 @@
}
-void HGraphBuilder::AddCheckConstantFunction(Call* expr,
+void HGraphBuilder::AddCheckConstantFunction(Handle<JSObject> holder,
HValue* receiver,
Handle<Map> receiver_map,
bool smi_and_map_check) {
@@ -6295,10 +6297,9 @@
AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map,
zone()));
}
- if (!expr->holder().is_null()) {
+ if (!holder.is_null()) {
AddInstruction(new(zone()) HCheckPrototypeMaps(
- Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
- expr->holder()));
+ Handle<JSObject>(JSObject::cast(receiver_map->prototype())), holder));
}
}
@@ -6381,7 +6382,7 @@
set_current_block(if_true);
expr->ComputeTarget(map, name);
- AddCheckConstantFunction(expr, receiver, map, false);
+ AddCheckConstantFunction(expr->holder(), receiver, map, false);
if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
Handle<JSFunction> caller = info()->closure();
SmartArrayPointer<char> caller_name =
@@ -6888,7 +6889,7 @@
case kMathCos:
case kMathTan:
if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
- AddCheckConstantFunction(expr, receiver, receiver_map, true);
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
HValue* argument = Pop();
HValue* context = environment()->LookupContext();
Drop(1); // Receiver.
@@ -6901,7 +6902,7 @@
break;
case kMathPow:
if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
- AddCheckConstantFunction(expr, receiver, receiver_map, true);
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
HValue* right = Pop();
HValue* left = Pop();
Pop(); // Pop receiver.
@@ -6943,7 +6944,7 @@
break;
case kMathRandom:
if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
- AddCheckConstantFunction(expr, receiver, receiver_map, true);
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
Drop(1); // Receiver.
HValue* context = environment()->LookupContext();
HGlobalObject* global_object = new(zone()) HGlobalObject(context);
@@ -6956,7 +6957,7 @@
case kMathMax:
case kMathMin:
if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
- AddCheckConstantFunction(expr, receiver, receiver_map, true);
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
HValue* right = Pop();
HValue* left = Pop();
Pop(); // Pop receiver.
@@ -7072,7 +7073,7 @@
VisitForValue(prop->obj());
if (HasStackOverflow() || current_block() == NULL) return true;
HValue* function = Top();
- AddCheckConstantFunction(expr, function, function_map, true);
+ AddCheckConstantFunction(expr->holder(), function, function_map, true);
Drop(1);
VisitForValue(args->at(0));
@@ -7191,7 +7192,7 @@
call = PreProcessCall(
new(zone()) HCallNamed(context, name, argument_count));
} else {
- AddCheckConstantFunction(expr, receiver, receiver_map, true);
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
if (TryInlineCall(expr)) return;
call = PreProcessCall(
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 907279a..85977c1 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -1165,7 +1165,7 @@
HInstruction* BuildThisFunction();
- void AddCheckConstantFunction(Call* expr,
+ void AddCheckConstantFunction(Handle<JSObject> holder,
HValue* receiver,
Handle<Map> receiver_map,
bool smi_and_map_check);
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 48cd780..b307512 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -2410,7 +2410,7 @@
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
- type->LookupInDescriptors(NULL, *name, &lookup);
+ type->LookupTransitionOrDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
@@ -2471,9 +2471,9 @@
Handle<Map> map = list->at(i);
// If the map has ElementsKind transitions, we will generate map checks
// for each kind in __ CompareMap(..., ALLOW_ELEMENTS_TRANSITION_MAPS).
- if (map->elements_transition_map() != NULL) return false;
+ if (map->HasElementsTransition()) return false;
LookupResult lookup(isolate);
- map->LookupInDescriptors(NULL, *name, &lookup);
+ map->LookupDescriptor(NULL, *name, &lookup);
return lookup.IsField() || lookup.IsConstantFunction();
}
diff --git a/src/ic.cc b/src/ic.cc
index e3663c0..615f358 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1304,9 +1304,10 @@
if (!lookup->IsCacheable()) return false;
// If the property is read-only, we leave the IC in its current state.
- if (lookup->IsReadOnly()) return false;
-
- return true;
+ if (lookup->IsTransition()) {
+ return !lookup->GetTransitionDetails().IsReadOnly();
+ }
+ return !lookup->IsReadOnly();
}
@@ -1466,14 +1467,6 @@
Handle<Map>::null(),
strict_mode);
break;
- case MAP_TRANSITION: {
- if (lookup->GetAttributes() != NONE) return;
- Handle<Map> transition(lookup->GetTransitionMap());
- int index = transition->PropertyIndexFor(*name);
- code = isolate()->stub_cache()->ComputeStoreField(
- name, receiver, index, transition, strict_mode);
- break;
- }
case NORMAL:
if (receiver->IsGlobalObject()) {
// The stub generated for the global object picks the value directly
@@ -1517,8 +1510,26 @@
name, receiver, strict_mode);
break;
case CONSTANT_FUNCTION:
- case CONSTANT_TRANSITION:
return;
+ case TRANSITION: {
+ Object* value = lookup->GetTransitionValue();
+ // Callbacks.
+ if (value->IsAccessorPair()) return;
+
+ Handle<Map> transition(Map::cast(value));
+ DescriptorArray* target_descriptors = transition->instance_descriptors();
+ int descriptor = target_descriptors->SearchWithCache(*name);
+ ASSERT(descriptor != DescriptorArray::kNotFound);
+ PropertyDetails details = target_descriptors->GetDetails(descriptor);
+
+ if (details.type() != FIELD || details.attributes() != NONE) return;
+
+ int field_index = target_descriptors->GetFieldIndex(descriptor);
+ code = isolate()->stub_cache()->ComputeStoreField(
+ name, receiver, field_index, transition, strict_mode);
+
+ break;
+ }
case NONEXISTENT:
case HANDLER:
UNREACHABLE();
@@ -1967,20 +1978,34 @@
name, receiver, lookup->GetFieldIndex(),
Handle<Map>::null(), strict_mode);
break;
- case MAP_TRANSITION:
- if (lookup->GetAttributes() == NONE) {
- Handle<Map> transition(lookup->GetTransitionMap());
- int index = transition->PropertyIndexFor(*name);
+ case TRANSITION: {
+ Object* value = lookup->GetTransitionValue();
+ // Callbacks transition.
+ if (value->IsAccessorPair()) {
+ code = (strict_mode == kStrictMode)
+ ? generic_stub_strict()
+ : generic_stub();
+ break;
+ }
+
+ Handle<Map> transition(Map::cast(value));
+ DescriptorArray* target_descriptors = transition->instance_descriptors();
+ int descriptor = target_descriptors->SearchWithCache(*name);
+ ASSERT(descriptor != DescriptorArray::kNotFound);
+ PropertyDetails details = target_descriptors->GetDetails(descriptor);
+
+ if (details.type() == FIELD && details.attributes() == NONE) {
+ int field_index = target_descriptors->GetFieldIndex(descriptor);
code = isolate()->stub_cache()->ComputeKeyedStoreField(
- name, receiver, index, transition, strict_mode);
+ name, receiver, field_index, transition, strict_mode);
break;
}
// fall through.
+ }
case NORMAL:
case CONSTANT_FUNCTION:
case CALLBACKS:
case INTERCEPTOR:
- case CONSTANT_TRANSITION:
// Always rewrite to the generic case so that we do not
// repeatedly try to rewrite.
code = (strict_mode == kStrictMode)
diff --git a/src/liveedit.cc b/src/liveedit.cc
index 0b8654b..d86314c 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -1878,7 +1878,8 @@
void LiveEditFunctionTracker::RecordFunctionInfo(
- Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
+ Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
+ Zone* zone) {
}
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index d0b4ae4..a9d0c16 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -1892,12 +1892,13 @@
enum_cache);
}
- if (descriptors->elements_transition_map() != NULL) {
+ if (descriptors->HasTransitionArray()) {
Object** transitions_slot = descriptors->GetTransitionsSlot();
Object* transitions = *transitions_slot;
mark_compact_collector()->RecordSlot(descriptor_start,
transitions_slot,
transitions);
+ MarkTransitionArray(reinterpret_cast<TransitionArray*>(transitions));
}
// If the descriptor contains a transition (value is a Map), we don't mark the
@@ -1906,7 +1907,7 @@
Object** key_slot = descriptors->GetKeySlot(i);
Object* key = *key_slot;
if (key->IsHeapObject()) {
- base_marker()->MarkObjectAndPush(reinterpret_cast<HeapObject*>(key));
+ base_marker()->MarkObjectAndPush(HeapObject::cast(key));
mark_compact_collector()->RecordSlot(descriptor_start, key_slot, key);
}
@@ -1937,9 +1938,7 @@
MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
}
break;
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- break;
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@@ -1947,6 +1946,42 @@
}
}
+template <class T>
+void Marker<T>::MarkTransitionArray(TransitionArray* transitions) {
+ if (!base_marker()->MarkObjectWithoutPush(transitions)) return;
+ Object** transitions_start = transitions->data_start();
+
+ if (transitions->HasElementsTransition()) {
+ mark_compact_collector()->RecordSlot(transitions_start,
+ transitions->GetElementsSlot(),
+ transitions->elements_transition());
+ }
+
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
+ Object** key_slot = transitions->GetKeySlot(i);
+ Object* key = *key_slot;
+ if (key->IsHeapObject()) {
+ base_marker()->MarkObjectAndPush(HeapObject::cast(key));
+ mark_compact_collector()->RecordSlot(transitions_start, key_slot, key);
+ }
+
+ Object** value_slot = transitions->GetValueSlot(i);
+ if (!(*value_slot)->IsHeapObject()) continue;
+ HeapObject* value = HeapObject::cast(*value_slot);
+
+ if (value->IsAccessorPair()) {
+ mark_compact_collector()->RecordSlot(transitions_start,
+ value_slot,
+ value);
+
+ base_marker()->MarkObjectWithoutPush(value);
+ AccessorPair* accessors = AccessorPair::cast(value);
+ MarkAccessorPairSlot(accessors, AccessorPair::kGetterOffset);
+ MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
+ }
+ }
+}
+
template <class T>
void Marker<T>::MarkAccessorPairSlot(AccessorPair* accessors, int offset) {
diff --git a/src/mark-compact.h b/src/mark-compact.h
index dbc2869..c2a70fc 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -395,6 +395,7 @@
// treating transitions or back pointers weak.
void MarkMapContents(Map* map);
void MarkDescriptorArray(DescriptorArray* descriptors);
+ void MarkTransitionArray(TransitionArray* transitions);
void MarkAccessorPairSlot(AccessorPair* accessors, int offset);
private:
diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc
index a992799..c925640 100644
--- a/src/mips/lithium-codegen-mips.cc
+++ b/src/mips/lithium-codegen-mips.cc
@@ -2321,7 +2321,7 @@
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
- type->LookupInDescriptors(NULL, *name, &lookup);
+ type->LookupDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js
index f71483a..94e616a 100644
--- a/src/mirror-debugger.js
+++ b/src/mirror-debugger.js
@@ -176,10 +176,8 @@
PropertyType.Callbacks = 3;
PropertyType.Handler = 4;
PropertyType.Interceptor = 5;
-PropertyType.MapTransition = 6;
-PropertyType.ExternalArrayTransition = 7;
-PropertyType.ConstantTransition = 8;
-PropertyType.NullDescriptor = 9;
+PropertyType.Transition = 6;
+PropertyType.Nonexistent = 7;
// Different attributes for a property.
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 74c015b..6d54d3a 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -303,7 +303,10 @@
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_ASSERT(instance_descriptors()->IsSortedNoDuplicates());
- SLOW_ASSERT(instance_descriptors()->IsConsistentWithBackPointers(this));
+ if (HasTransitionArray()) {
+ SLOW_ASSERT(transitions()->IsSortedNoDuplicates());
+ SLOW_ASSERT(transitions()->IsConsistentWithBackPointers(this));
+ }
}
@@ -916,47 +919,46 @@
}
+bool TransitionArray::IsSortedNoDuplicates() {
+ String* current_key = NULL;
+ uint32_t current = 0;
+ for (int i = 0; i < number_of_transitions(); i++) {
+ String* key = GetKey(i);
+ if (key == current_key) {
+ PrintTransitions();
+ return false;
+ }
+ current_key = key;
+ uint32_t hash = GetKey(i)->Hash();
+ if (hash < current) {
+ PrintTransitions();
+ return false;
+ }
+ current = hash;
+ }
+ return true;
+}
+
+
static bool CheckOneBackPointer(Map* current_map, Object* target) {
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
}
-bool DescriptorArray::IsConsistentWithBackPointers(Map* current_map) {
- Map* elements_transition = elements_transition_map();
- if (elements_transition != NULL &&
- !CheckOneBackPointer(current_map, elements_transition)) {
+bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
+ if (HasElementsTransition() &&
+ !CheckOneBackPointer(current_map, elements_transition())) {
return false;
}
- for (int i = 0; i < number_of_descriptors(); ++i) {
- switch (GetType(i)) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- if (!CheckOneBackPointer(current_map, GetValue(i))) {
- return false;
- }
- break;
- case CALLBACKS: {
- Object* object = GetValue(i);
- if (object->IsAccessorPair()) {
- AccessorPair* accessors = AccessorPair::cast(object);
- if (!CheckOneBackPointer(current_map, accessors->getter())) {
- return false;
- }
- if (!CheckOneBackPointer(current_map, accessors->setter())) {
- return false;
- }
- }
- break;
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- break;
- case NONEXISTENT:
- UNREACHABLE();
- break;
+ for (int i = 0; i < number_of_transitions(); ++i) {
+ Object* value = GetValue(i);
+ if (value->IsAccessorPair()) {
+ AccessorPair* accessors = AccessorPair::cast(value);
+ if (!CheckOneBackPointer(current_map, accessors->getter())) return false;
+ if (!CheckOneBackPointer(current_map, accessors->setter())) return false;
+ } else {
+ ASSERT(value->IsMap());
+ if (!CheckOneBackPointer(current_map, value)) return false;
}
}
return true;
@@ -1004,12 +1006,12 @@
}
-void Map::ZapInstanceDescriptors() {
- DescriptorArray* descriptors = instance_descriptors();
- if (descriptors == GetHeap()->empty_descriptor_array()) return;
- MemsetPointer(descriptors->data_start(),
+void Map::ZapTransitions() {
+ TransitionArray* transition_array = transitions();
+ if (transition_array == NULL) return;
+ MemsetPointer(transition_array->data_start(),
GetHeap()->the_hole_value(),
- descriptors->length());
+ transition_array->length());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 074f1ed..af5e683 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -47,6 +47,7 @@
#include "v8memory.h"
#include "factory.h"
#include "incremental-marking.h"
+#include "transitions-inl.h"
namespace v8 {
namespace internal {
@@ -524,6 +525,11 @@
}
+bool Object::IsTransitionArray() {
+ return IsFixedArray();
+}
+
+
bool Object::IsDeoptimizationInputData() {
// Must be a fixed array.
if (!IsFixedArray()) return false;
@@ -1885,13 +1891,18 @@
}
+bool DescriptorArray::HasTransitionArray() {
+ return MayContainTransitions() && !get(kTransitionsIndex)->IsSmi();
+}
+
+
int DescriptorArray::bit_field3_storage() {
Object* storage = READ_FIELD(this, kBitField3StorageOffset);
return Smi::cast(storage)->value();
}
void DescriptorArray::set_bit_field3_storage(int value) {
- ASSERT(this->MayContainTransitions());
+ ASSERT(length() > kBitField3StorageIndex);
WRITE_FIELD(this, kBitField3StorageOffset, Smi::FromInt(value));
}
@@ -1905,63 +1916,107 @@
}
-int DescriptorArray::Search(String* name) {
- SLOW_ASSERT(IsSortedNoDuplicates());
+// Perform a binary search in a fixed array. Low and high are entry indices. If
+// there are three entries in this array it should be called with low=0 and
+// high=2.
+template<typename T>
+int BinarySearch(T* array, String* name, int low, int high) {
+ uint32_t hash = name->Hash();
+ int limit = high;
+
+ ASSERT(low <= high);
+
+ while (low != high) {
+ int mid = (low + high) / 2;
+ String* mid_name = array->GetKey(mid);
+ uint32_t mid_hash = mid_name->Hash();
+
+ if (mid_hash >= hash) {
+ high = mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+
+ for (; low <= limit && array->GetKey(low)->Hash() == hash; ++low) {
+ if (array->GetKey(low)->Equals(name)) return low;
+ }
+
+ return T::kNotFound;
+}
+
+
+// Perform a linear search in this fixed array. len is the number of entry
+// indices that are valid.
+template<typename T>
+int LinearSearch(T* array, SearchMode mode, String* name, int len) {
+ uint32_t hash = name->Hash();
+ for (int number = 0; number < len; number++) {
+ String* entry = array->GetKey(number);
+ uint32_t current_hash = entry->Hash();
+ if (mode == EXPECT_SORTED && current_hash > hash) break;
+ if (current_hash == hash && name->Equals(entry)) return number;
+ }
+ return T::kNotFound;
+}
+
+
+template<typename T>
+int Search(T* array, String* name) {
+ SLOW_ASSERT(array->IsSortedNoDuplicates());
// Check for empty descriptor array.
- int nof = number_of_descriptors();
- if (nof == 0) return kNotFound;
+ int nof = array->number_of_entries();
+ if (nof == 0) return T::kNotFound;
// Fast case: do linear search for small arrays.
const int kMaxElementsForLinearSearch = 8;
if (StringShape(name).IsSymbol() && nof < kMaxElementsForLinearSearch) {
- return LinearSearch(EXPECT_SORTED, name, nof);
+ return LinearSearch(array, EXPECT_SORTED, name, nof);
}
// Slow case: perform binary search.
- return BinarySearch(name, 0, nof - 1);
+ return BinarySearch(array, name, 0, nof - 1);
+}
+
+
+int DescriptorArray::Search(String* name) {
+ return internal::Search(this, name);
}
int DescriptorArray::SearchWithCache(String* name) {
- int number = GetIsolate()->descriptor_lookup_cache()->Lookup(this, name);
+ DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
+ int number = cache->Lookup(this, name);
if (number == DescriptorLookupCache::kAbsent) {
- number = Search(name);
- GetIsolate()->descriptor_lookup_cache()->Update(this, name, number);
+ number = internal::Search(this, name);
+ cache->Update(this, name, number);
}
return number;
}
-Map* DescriptorArray::elements_transition_map() {
- if (!this->MayContainTransitions()) {
- return NULL;
- }
- Object* transition_map = get(kTransitionsIndex);
- if (transition_map == Smi::FromInt(0)) {
- return NULL;
- } else {
- return Map::cast(transition_map);
- }
+TransitionArray* DescriptorArray::transitions() {
+ ASSERT(MayContainTransitions());
+ Object* array = get(kTransitionsIndex);
+ return TransitionArray::cast(array);
}
-void DescriptorArray::set_elements_transition_map(
- Map* transition_map, WriteBarrierMode mode) {
- ASSERT(this->length() > kTransitionsIndex);
- Heap* heap = GetHeap();
- WRITE_FIELD(this, kTransitionsOffset, transition_map);
- CONDITIONAL_WRITE_BARRIER(
- heap, this, kTransitionsOffset, transition_map, mode);
- ASSERT(DescriptorArray::cast(this));
-}
-
-
-void DescriptorArray::ClearElementsTransition() {
+void DescriptorArray::ClearTransitions() {
WRITE_FIELD(this, kTransitionsOffset, Smi::FromInt(0));
}
+void DescriptorArray::set_transitions(TransitionArray* transitions_array,
+ WriteBarrierMode mode) {
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kTransitionsOffset, transitions_array);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kTransitionsOffset, transitions_array, mode);
+}
+
+
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
@@ -1976,17 +2031,6 @@
}
-void DescriptorArray::SetKeyUnchecked(Heap* heap,
- int descriptor_number,
- String* key) {
- ASSERT(descriptor_number < number_of_descriptors());
- set_unchecked(heap,
- ToKeyIndex(descriptor_number),
- key,
- UPDATE_WRITE_BARRIER);
-}
-
-
Object** DescriptorArray::GetValueSlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
@@ -2001,24 +2045,6 @@
}
-void DescriptorArray::SetNullValueUnchecked(Heap* heap, int descriptor_number) {
- ASSERT(descriptor_number < number_of_descriptors());
- set_null_unchecked(heap, ToValueIndex(descriptor_number));
-}
-
-
-
-void DescriptorArray::SetValueUnchecked(Heap* heap,
- int descriptor_number,
- Object* value) {
- ASSERT(descriptor_number < number_of_descriptors());
- set_unchecked(heap,
- ToValueIndex(descriptor_number),
- value,
- UPDATE_WRITE_BARRIER);
-}
-
-
PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
Object* details = get(ToDetailsIndex(descriptor_number));
@@ -2026,12 +2052,6 @@
}
-void DescriptorArray::SetDetailsUnchecked(int descriptor_number, Smi* value) {
- ASSERT(descriptor_number < number_of_descriptors());
- set_unchecked(ToDetailsIndex(descriptor_number), value);
-}
-
-
PropertyType DescriptorArray::GetType(int descriptor_number) {
return GetDetails(descriptor_number).type();
}
@@ -2060,38 +2080,6 @@
}
-bool DescriptorArray::IsProperty(int descriptor_number) {
- Entry entry(this, descriptor_number);
- return IsPropertyDescriptor(&entry);
-}
-
-
-bool DescriptorArray::IsTransitionOnly(int descriptor_number) {
- switch (GetType(descriptor_number)) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return true;
- case CALLBACKS: {
- Object* value = GetValue(descriptor_number);
- if (!value->IsAccessorPair()) return false;
- AccessorPair* accessors = AccessorPair::cast(value);
- return accessors->getter()->IsMap() && accessors->setter()->IsMap();
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- return false;
- case NONEXISTENT:
- UNREACHABLE();
- break;
- }
- UNREACHABLE(); // Keep the compiler happy.
- return false;
-}
-
-
void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
desc->Init(GetKey(descriptor_number),
GetValue(descriptor_number),
@@ -2129,16 +2117,14 @@
}
-DescriptorArray::WhitenessWitness::WhitenessWitness(DescriptorArray* array)
+FixedArray::WhitenessWitness::WhitenessWitness(FixedArray* array)
: marking_(array->GetHeap()->incremental_marking()) {
marking_->EnterNoMarkingScope();
- if (array->number_of_descriptors() > 0) {
- ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT);
- }
+ ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT);
}
-DescriptorArray::WhitenessWitness::~WhitenessWitness() {
+FixedArray::WhitenessWitness::~WhitenessWitness() {
marking_->LeaveNoMarkingScope();
}
@@ -3431,12 +3417,8 @@
void Map::clear_instance_descriptors() {
- Object* object = READ_FIELD(this,
- kInstanceDescriptorsOrBitField3Offset);
+ Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (!object->IsSmi()) {
-#ifdef DEBUG
- ZapInstanceDescriptors();
-#endif
WRITE_FIELD(
this,
kInstanceDescriptorsOrBitField3Offset,
@@ -3462,11 +3444,6 @@
}
}
ASSERT(!is_shared());
-#ifdef DEBUG
- if (value != instance_descriptors()) {
- ZapInstanceDescriptors();
- }
-#endif
WRITE_FIELD(this, kInstanceDescriptorsOrBitField3Offset, value);
CONDITIONAL_WRITE_BARRIER(
heap, this, kInstanceDescriptorsOrBitField3Offset, value, mode);
@@ -3489,7 +3466,7 @@
#ifdef DEBUG
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (!object->IsSmi()) {
- ZapInstanceDescriptors();
+ ZapTransitions();
}
#endif
WRITE_FIELD(this,
@@ -3521,13 +3498,93 @@
}
-Map* Map::elements_transition_map() {
- return instance_descriptors()->elements_transition_map();
+bool Map::HasElementsTransition() {
+ return HasTransitionArray() && transitions()->HasElementsTransition();
}
-void Map::set_elements_transition_map(Map* transitioned_map) {
- return instance_descriptors()->set_elements_transition_map(transitioned_map);
+bool Map::HasTransitionArray() {
+ return instance_descriptors()->HasTransitionArray();
+}
+
+
+Map* Map::elements_transition_map() {
+ return transitions()->elements_transition();
+}
+
+
+MaybeObject* Map::AddTransition(String* key, Object* value) {
+ if (HasTransitionArray()) return transitions()->CopyInsert(key, value);
+ return TransitionArray::NewWith(key, value);
+}
+
+
+// If the map does not have a descriptor array, install a new empty
+// descriptor array that has room for a transition array.
+static MaybeObject* AllowTransitions(Map* map) {
+ if (map->instance_descriptors()->MayContainTransitions()) return map;
+ DescriptorArray* descriptors;
+ MaybeObject* maybe_descriptors =
+ DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
+ if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
+ map->set_instance_descriptors(descriptors);
+ return descriptors;
+}
+
+
+// If the descriptor does not have a transition array, install a new
+// transition array that has room for an element transition.
+static MaybeObject* AllowElementsTransition(Map* map) {
+ if (map->HasTransitionArray()) return map;
+
+ AllowTransitions(map);
+
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = TransitionArray::Allocate(0);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ MaybeObject* added_transitions = map->set_transitions(transitions);
+ if (added_transitions->IsFailure()) return added_transitions;
+ return transitions;
+}
+
+
+MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
+ MaybeObject* allow_elements = AllowElementsTransition(this);
+ if (allow_elements->IsFailure()) return allow_elements;
+ transitions()->set_elements_transition(transitioned_map);
+ return this;
+}
+
+
+TransitionArray* Map::transitions() {
+ return instance_descriptors()->transitions();
+}
+
+
+void Map::ClearTransitions() {
+#ifdef DEBUG
+ ZapTransitions();
+#endif
+ DescriptorArray* descriptors = instance_descriptors();
+ if (descriptors->number_of_descriptors() == 0) {
+ ClearDescriptorArray();
+ } else {
+ descriptors->ClearTransitions();
+ }
+}
+
+
+MaybeObject* Map::set_transitions(TransitionArray* transitions_array) {
+ MaybeObject* allow_transitions = AllowTransitions(this);
+ if (allow_transitions->IsFailure()) return allow_transitions;
+#ifdef DEBUG
+ if (HasTransitionArray()) {
+ ASSERT(transitions() != transitions_array);
+ ZapTransitions();
+ }
+#endif
+ instance_descriptors()->set_transitions(transitions_array);
+ return this;
}
@@ -4794,7 +4851,7 @@
Object* JSReceiver::GetPrototype() {
- return HeapObject::cast(this)->map()->prototype();
+ return map()->prototype();
}
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index 2e58c09..679e9dc 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -273,15 +273,11 @@
descs->GetCallbacksObject(i)->ShortPrint(out);
PrintF(out, " (callback)\n");
break;
- case MAP_TRANSITION:
- PrintF(out, "(map transition)\n");
- break;
- case CONSTANT_TRANSITION:
- PrintF(out, "(constant transition)\n");
- break;
case NORMAL: // only in slow mode
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
+ // There are no transitions in the descriptor array.
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@@ -408,6 +404,37 @@
}
+void JSObject::PrintTransitions(FILE* out) {
+ if (!map()->HasTransitionArray()) return;
+ TransitionArray* transitions = map()->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); i++) {
+ PrintF(out, " ");
+ transitions->GetKey(i)->StringPrint(out);
+ PrintF(out, ": ");
+ switch (transitions->GetTargetDetails(i).type()) {
+ case FIELD: {
+ PrintF(out, " (transition to field)\n");
+ break;
+ }
+ case CONSTANT_FUNCTION:
+ PrintF(out, " (transition to constant function)\n");
+ break;
+ case CALLBACKS:
+ PrintF(out, " (transition to callback)\n");
+ break;
+ // Values below are never in the target descriptor array.
+ case NORMAL:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
void JSObject::JSObjectPrint(FILE* out) {
PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
PrintF(out, " - map = %p [", reinterpret_cast<void*>(map()));
@@ -417,11 +444,9 @@
PrintF(out,
"]\n - prototype = %p\n",
reinterpret_cast<void*>(GetPrototype()));
- PrintF(out,
- " - elements transition to = %p\n",
- reinterpret_cast<void*>(map()->elements_transition_map()));
PrintF(out, " {\n");
PrintProperties(out);
+ PrintTransitions(out);
PrintElements(out);
PrintF(out, " }\n");
}
@@ -537,6 +562,10 @@
}
PrintF(out, " - instance descriptors: ");
instance_descriptors()->ShortPrint(out);
+ if (HasTransitionArray()) {
+ PrintF(out, "\n - transitions: ");
+ transitions()->ShortPrint(out);
+ }
PrintF(out, "\n - prototype: ");
prototype()->ShortPrint(out);
PrintF(out, "\n - constructor: ");
@@ -1024,6 +1053,37 @@
}
+void TransitionArray::PrintTransitions(FILE* out) {
+ PrintF(out, "Transition array %d\n", number_of_transitions());
+ for (int i = 0; i < number_of_transitions(); i++) {
+ PrintF(out, " %d: ", i);
+ GetKey(i)->StringPrint(out);
+ PrintF(out, ": ");
+ switch (GetTargetDetails(i).type()) {
+ case FIELD: {
+ PrintF(out, " (transition to field)\n");
+ break;
+ }
+ case CONSTANT_FUNCTION:
+ PrintF(out, " (transition to constant function)\n");
+ break;
+ case CALLBACKS:
+ PrintF(out, " (transition to callback)\n");
+ break;
+ // Values below are never in the target descriptor array.
+ case NORMAL:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+ PrintF(out, "\n");
+}
+
+
#endif // OBJECT_PRINT
diff --git a/src/objects.cc b/src/objects.cc
index 5748317..244e22b 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -416,8 +416,7 @@
}
case HANDLER:
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
}
@@ -641,9 +640,7 @@
return result->holder()->GetPropertyWithInterceptor(
recvr, name, attributes);
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- break;
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@@ -1492,20 +1489,19 @@
MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
String* name,
- Object* value) {
- int index = new_map->PropertyIndexFor(name);
+ Object* value,
+ int field_index) {
if (map()->unused_property_fields() == 0) {
- ASSERT(map()->unused_property_fields() == 0);
int new_unused = new_map->unused_property_fields();
- Object* values;
+ FixedArray* values;
{ MaybeObject* maybe_values =
properties()->CopySize(properties()->length() + new_unused + 1);
- if (!maybe_values->ToObject(&values)) return maybe_values;
+ if (!maybe_values->To(&values)) return maybe_values;
}
- set_properties(FixedArray::cast(values));
+ set_properties(values);
}
set_map(new_map);
- return FastPropertyAtPut(index, value);
+ return FastPropertyAtPut(field_index, value);
}
@@ -1551,10 +1547,10 @@
// Allocate new instance descriptors with (name, index) added
FieldDescriptor new_field(name, index, attributes);
- Object* new_descriptors;
+ DescriptorArray* new_descriptors;
{ MaybeObject* maybe_new_descriptors =
- old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
+ old_descriptors->CopyInsert(&new_field);
+ if (!maybe_new_descriptors->To(&new_descriptors)) {
return maybe_new_descriptors;
}
}
@@ -1562,7 +1558,7 @@
// Only allow map transition if the object isn't the global object and there
// is not a transition for the name, or there's a transition for the name but
// it's unrelated to properties.
- int descriptor_index = old_descriptors->Search(name);
+ int descriptor_index = old_descriptors->SearchWithCache(name);
// Element transitions are stored in the descriptor for property "", which is
// not a identifier and should have forced a switch to slow properties above.
@@ -1580,14 +1576,13 @@
if (!maybe_r->ToObject(&r)) return maybe_r;
}
Map* new_map = Map::cast(r);
+
+ TransitionArray* new_transitions = NULL;
if (allow_map_transition) {
- // Allocate new instance descriptors for the old map with map transition.
- MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
- Object* r;
- { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
- if (!maybe_r->ToObject(&r)) return maybe_r;
+ MaybeObject* maybe_new_transitions = map()->AddTransition(name, new_map);
+ if (!maybe_new_transitions->To(&new_transitions)) {
+ return maybe_new_transitions;
}
- old_descriptors = DescriptorArray::cast(r);
}
if (map()->unused_property_fields() == 0) {
@@ -1610,13 +1605,13 @@
} else {
new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
}
- // We have now allocated all the necessary objects.
- // All the changes can be applied at once, so they are atomic.
+ // Apply all changes at once, so they are atomic.
if (allow_map_transition) {
- map()->set_instance_descriptors(old_descriptors);
+ MaybeObject* transition_added = map()->set_transitions(new_transitions);
+ if (transition_added->IsFailure()) return transition_added;
}
+ new_map->set_instance_descriptors(new_descriptors);
new_map->SetBackPointer(map());
- new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
set_map(new_map);
return FastPropertyAtPut(index, value);
}
@@ -1628,24 +1623,23 @@
PropertyAttributes attributes) {
// Allocate new instance descriptors with (name, function) added
ConstantFunctionDescriptor d(name, function, attributes);
- Object* new_descriptors;
+ DescriptorArray* new_descriptors;
{ MaybeObject* maybe_new_descriptors =
- map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
+ map()->instance_descriptors()->CopyInsert(&d);
+ if (!maybe_new_descriptors->To(&new_descriptors)) {
return maybe_new_descriptors;
}
}
// Allocate a new map for the object.
- Object* new_map;
+ Map* new_map;
{ MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
}
- DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
- Map::cast(new_map)->set_instance_descriptors(descriptors);
+ new_map->set_instance_descriptors(new_descriptors);
Map* old_map = map();
- set_map(Map::cast(new_map));
+ set_map(new_map);
// If the old map is the global object map (from new Object()),
// then transitions are not added to it, so we are done.
@@ -1655,29 +1649,36 @@
return function;
}
- // Do not add CONSTANT_TRANSITIONS to global objects
+ // Do not add constant transitions to global objects
if (IsGlobalObject()) {
return function;
}
- // Add a CONSTANT_TRANSITION descriptor to the old map,
- // so future assignments to this property on other objects
- // of the same type will create a normal field, not a constant function.
- // Don't do this for special properties, with non-trival attributes.
+ // Add a constant transition to the old map, so future assignments to this
+ // property on other objects of the same type will create a normal field, not
+ // a constant function. Don't do this for special properties, with non-trival
+ // attributes.
if (attributes != NONE) {
return function;
}
- ConstTransitionDescriptor mark(name, Map::cast(new_map));
- { MaybeObject* maybe_new_descriptors =
- old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
+
+ TransitionArray* new_transitions;
+ { MaybeObject* maybe_new_transitions =
+ old_map->AddTransition(name, new_map);
+ if (!maybe_new_transitions->To(&new_transitions)) {
// We have accomplished the main goal, so return success.
return function;
}
}
- old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
- Map::cast(new_map)->SetBackPointer(old_map);
+ { MaybeObject* transition_added = old_map->set_transitions(new_transitions);
+ // We have accomplished the main goal, so return success.
+ if (transition_added->IsFailure()) {
+ return function;
+ }
+ }
+
+ new_map->SetBackPointer(old_map);
return function;
}
@@ -1798,7 +1799,6 @@
int new_enumeration_index = 0; // 0 means "Use the next available index."
if (old_index != -1) {
// All calls to ReplaceSlowProperty have had all transitions removed.
- ASSERT(!dictionary->ContainsTransition(old_index));
new_enumeration_index = dictionary->DetailsAt(old_index).index();
}
@@ -1813,33 +1813,40 @@
PropertyAttributes attributes) {
Map* old_map = map();
Object* result;
+
{ MaybeObject* maybe_result =
ConvertDescriptorToField(name, new_value, attributes);
- if (!maybe_result->ToObject(&result)) return maybe_result;
+ if (!maybe_result->To(&result)) return maybe_result;
}
+
// If we get to this point we have succeeded - do not return failure
// after this point. Later stuff is optional.
if (!HasFastProperties()) {
return result;
}
+
// Do not add transitions to the map of "new Object()".
if (map() == GetIsolate()->context()->global_context()->
object_function()->map()) {
return result;
}
- MapTransitionDescriptor transition(name,
- map(),
- attributes);
- Object* new_descriptors;
- { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
- CopyInsert(&transition, KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return result; // Yes, return _result_.
+ TransitionArray* new_transitions;
+ { MaybeObject* maybe_new_transitions = old_map->AddTransition(name, map());
+ if (!maybe_new_transitions->To(&new_transitions)) {
+ // We have accomplished the main goal, so return success.
+ return result;
}
}
- old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
- map()->SetBackPointer(old_map);
+
+ { MaybeObject* transition_added = old_map->set_transitions(new_transitions);
+ // Return success if failure since we accomplished the main goal. Otherwise
+ // also set backpointer.
+ if (!transition_added->IsFailure()) {
+ map()->SetBackPointer(old_map);
+ }
+ }
+
return result;
}
@@ -1861,8 +1868,8 @@
FieldDescriptor new_field(name, index, attributes);
// Make a new DescriptorArray replacing an entry with FieldDescriptor.
Object* descriptors_unchecked;
- { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
- CopyInsert(&new_field, REMOVE_TRANSITIONS);
+ { MaybeObject* maybe_descriptors_unchecked =
+ map()->instance_descriptors()->CopyInsert(&new_field);
if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
return maybe_descriptors_unchecked;
}
@@ -2156,9 +2163,7 @@
return result.proxy()->SetPropertyViaPrototypesWithHandler(
this, name, value, attributes, strict_mode, done);
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- break;
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@@ -2177,33 +2182,44 @@
}
-void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
- DescriptorArray* descriptors = map()->instance_descriptors();
+void Map::LookupDescriptor(JSObject* holder,
+ String* name,
+ LookupResult* result) {
+ DescriptorArray* descriptors = this->instance_descriptors();
int number = descriptors->SearchWithCache(name);
if (number != DescriptorArray::kNotFound) {
- result->DescriptorResult(this, descriptors->GetDetails(number), number);
+ result->DescriptorResult(holder, descriptors->GetDetails(number), number);
} else {
result->NotFound();
}
}
-void Map::LookupInDescriptors(JSObject* holder,
- String* name,
- LookupResult* result) {
- DescriptorArray* descriptors = instance_descriptors();
- DescriptorLookupCache* cache =
- GetHeap()->isolate()->descriptor_lookup_cache();
- int number = cache->Lookup(descriptors, name);
- if (number == DescriptorLookupCache::kAbsent) {
- number = descriptors->Search(name);
- cache->Update(descriptors, name, number);
+void Map::LookupTransition(JSObject* holder,
+ String* name,
+ LookupResult* result) {
+ if (HasTransitionArray()) {
+ TransitionArray* transition_array = transitions();
+ int number = transition_array->Search(name);
+ if (number != TransitionArray::kNotFound) {
+ return result->TransitionResult(holder, number);
+ }
}
- if (number != DescriptorArray::kNotFound) {
- result->DescriptorResult(holder, descriptors->GetDetails(number), number);
- } else {
- result->NotFound();
- }
+ result->NotFound();
+}
+
+
+void Map::LookupTransitionOrDescriptor(JSObject* holder,
+ String* name,
+ LookupResult* result) {
+ // AccessorPairs containing both a Descriptor and a Transition are shared
+ // between the DescriptorArray and the Transition array. This is why looking
+ // up the AccessorPair solely in the DescriptorArray works.
+ // TODO(verwaest) This should be implemented differently so the
+ // DescriptorArray is free of transitions; and so we can freely share it.
+ this->LookupDescriptor(holder, name, result);
+ if (result->IsFound()) return;
+ this->LookupTransition(holder, name, result);
}
@@ -2256,21 +2272,16 @@
ASSERT(index <= to_index);
for (; index < to_index; ++index) {
- Map* next_map = current_map->elements_transition_map();
- if (next_map == NULL) {
- return current_map;
- }
- current_map = next_map;
+ if (!current_map->HasElementsTransition()) return current_map;
+ current_map = current_map->elements_transition_map();
}
- if (!IsFastElementsKind(to_kind)) {
+ if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) {
Map* next_map = current_map->elements_transition_map();
- if (next_map != NULL && next_map->elements_kind() == to_kind) {
- return next_map;
- }
- ASSERT(current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
- } else {
- ASSERT(current_map->elements_kind() == to_kind);
+ if (next_map->elements_kind() == to_kind) return next_map;
}
+ ASSERT(IsFastElementsKind(to_kind)
+ ? current_map->elements_kind() == to_kind
+ : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
return current_map;
}
@@ -2283,7 +2294,7 @@
MaybeObject* Map::CreateNextElementsTransition(ElementsKind next_kind) {
- ASSERT(elements_transition_map() == NULL ||
+ ASSERT(!HasElementsTransition() ||
((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
IsExternalArrayElementsKind(
elements_transition_map()->elements_kind())) &&
@@ -2294,13 +2305,17 @@
ASSERT(next_kind != elements_kind());
Map* next_map;
- MaybeObject* maybe_next_map =
- this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
- if (!maybe_next_map->To(&next_map)) return maybe_next_map;
+ { MaybeObject* maybe_next_map =
+ this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
+ if (!maybe_next_map->To(&next_map)) return maybe_next_map;
+ }
+
+ { MaybeObject* added_elements = this->set_elements_transition_map(next_map);
+ if (added_elements->IsFailure()) return added_elements;
+ }
next_map->set_elements_kind(next_kind);
next_map->SetBackPointer(this);
- this->set_elements_transition_map(next_map);
return next_map;
}
@@ -2346,22 +2361,6 @@
}
-// If the map is using the empty descriptor array, install a new empty
-// descriptor array that will contain an element transition.
-// TODO(verwaest) Goes away once the descriptor array is immutable.
-static MaybeObject* EnsureMayContainTransitions(Map* map) {
- if (map->instance_descriptors()->MayContainTransitions()) return map;
- DescriptorArray* descriptor_array;
- MaybeObject* maybe_descriptor_array =
- DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
- if (!maybe_descriptor_array->To(&descriptor_array)) {
- return maybe_descriptor_array;
- }
- map->set_instance_descriptors(descriptor_array);
- return map;
-}
-
-
MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
Map* start_map = map();
ElementsKind from_kind = start_map->elements_kind();
@@ -2396,7 +2395,6 @@
return new_map;
}
- EnsureMayContainTransitions(start_map);
Map* closest_map = FindClosestElementsTransition(start_map, to_kind);
if (closest_map->elements_kind() == to_kind) {
@@ -2418,41 +2416,40 @@
}
if (HasFastProperties()) {
- LookupInDescriptor(name, result);
- if (result->IsFound()) {
- // A property, a map transition or a null descriptor was found.
- // We return all of these result types because
- // LocalLookupRealNamedProperty is used when setting properties
- // where map transitions and null descriptors are handled.
- ASSERT(result->holder() == this && result->IsFastPropertyType());
- // Disallow caching for uninitialized constants. These can only
- // occur as fields.
- if (result->IsField() &&
- result->IsReadOnly() &&
- FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
- result->DisallowCaching();
- }
- return;
+ map()->LookupTransitionOrDescriptor(this, name, result);
+ // A property or a map transition was found. We return all of these result
+ // types because LocalLookupRealNamedProperty is used when setting
+ // properties where map transitions are handled.
+ ASSERT(!result->IsFound() ||
+ (result->holder() == this && result->IsFastPropertyType()));
+ // Disallow caching for uninitialized constants. These can only
+ // occur as fields.
+ if (result->IsField() &&
+ result->IsReadOnly() &&
+ FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
+ result->DisallowCaching();
}
- } else {
- int entry = property_dictionary()->FindEntry(name);
- if (entry != StringDictionary::kNotFound) {
- Object* value = property_dictionary()->ValueAt(entry);
- if (IsGlobalObject()) {
- PropertyDetails d = property_dictionary()->DetailsAt(entry);
- if (d.IsDeleted()) {
- result->NotFound();
- return;
- }
- value = JSGlobalPropertyCell::cast(value)->value();
- }
- // Make sure to disallow caching for uninitialized constants
- // found in the dictionary-mode objects.
- if (value->IsTheHole()) result->DisallowCaching();
- result->DictionaryResult(this, entry);
- return;
- }
+ return;
}
+
+ int entry = property_dictionary()->FindEntry(name);
+ if (entry != StringDictionary::kNotFound) {
+ Object* value = property_dictionary()->ValueAt(entry);
+ if (IsGlobalObject()) {
+ PropertyDetails d = property_dictionary()->DetailsAt(entry);
+ if (d.IsDeleted()) {
+ result->NotFound();
+ return;
+ }
+ value = JSGlobalPropertyCell::cast(value)->value();
+ }
+ // Make sure to disallow caching for uninitialized constants
+ // found in the dictionary-mode objects.
+ if (value->IsTheHole()) result->DisallowCaching();
+ result->DictionaryResult(this, entry);
+ return;
+ }
+
result->NotFound();
}
@@ -2830,8 +2827,8 @@
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
- String* name,
- Object* value,
+ String* name_raw,
+ Object* value_raw,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
StoreFromKeyed store_mode) {
@@ -2843,113 +2840,140 @@
// Optimization for 2-byte strings often used as keys in a decompression
// dictionary. We make these short keys into symbols to avoid constantly
// reallocating them.
- if (!name->IsSymbol() && name->length() <= 2) {
+ if (!name_raw->IsSymbol() && name_raw->length() <= 2) {
Object* symbol_version;
- { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name);
+ { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name_raw);
if (maybe_symbol_version->ToObject(&symbol_version)) {
- name = String::cast(symbol_version);
+ name_raw = String::cast(symbol_version);
}
}
}
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
- if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(
- result, name, value, true, strict_mode);
+ result, name_raw, value_raw, true, strict_mode);
}
}
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
- if (proto->IsNull()) return value;
+ if (proto->IsNull()) return value_raw;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetPropertyForResult(
- result, name, value, attributes, strict_mode, store_mode);
+ result, name_raw, value_raw, attributes, strict_mode, store_mode);
}
- if (!result->IsProperty() && !IsJSContextExtensionObject()) {
+ // From this point on everything needs to be handlified, because
+ // SetPropertyViaPrototypes might call back into JavaScript.
+ HandleScope scope(GetIsolate());
+ Handle<JSObject> self(this);
+ Handle<String> name(name_raw);
+ Handle<Object> value(value_raw);
+
+ if (!result->IsProperty() && !self->IsJSContextExtensionObject()) {
bool done = false;
- MaybeObject* result_object =
- SetPropertyViaPrototypes(name, value, attributes, strict_mode, &done);
+ MaybeObject* result_object = self->SetPropertyViaPrototypes(
+ *name, *value, attributes, strict_mode, &done);
if (done) return result_object;
}
if (!result->IsFound()) {
// Neither properties nor transitions found.
- return AddProperty(name, value, attributes, strict_mode, store_mode);
+ return self->AddProperty(
+ *name, *value, attributes, strict_mode, store_mode);
}
- if (result->IsReadOnly() && result->IsProperty()) {
+ if (result->IsProperty() && result->IsReadOnly()) {
if (strict_mode == kStrictMode) {
- Handle<JSObject> self(this);
- Handle<String> hname(name);
- Handle<Object> args[] = { hname, self };
+ Handle<Object> args[] = { name, self };
return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
} else {
- return value;
+ return *value;
}
}
+
// This is a real property that is not read-only, or it is a
// transition or null descriptor and there are no setters in the prototypes.
switch (result->type()) {
case NORMAL:
- return SetNormalizedProperty(result, value);
+ return self->SetNormalizedProperty(result, *value);
case FIELD:
- return FastPropertyAtPut(result->GetFieldIndex(), value);
- case MAP_TRANSITION:
- if (attributes == result->GetAttributes()) {
- // Only use map transition if the attributes match.
- return AddFastPropertyUsingMap(result->GetTransitionMap(),
- name,
- value);
- }
- return ConvertDescriptorToField(name, value, attributes);
+ return self->FastPropertyAtPut(result->GetFieldIndex(), *value);
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
- if (value == result->GetConstantFunction()) return value;
+ if (*value == result->GetConstantFunction()) return *value;
// Preserve the attributes of this existing property.
attributes = result->GetAttributes();
- return ConvertDescriptorToField(name, value, attributes);
+ return self->ConvertDescriptorToField(*name, *value, attributes);
case CALLBACKS: {
Object* callback_object = result->GetCallbackObject();
- if (callback_object->IsAccessorPair() &&
- !AccessorPair::cast(callback_object)->ContainsAccessor()) {
- return ConvertDescriptorToField(name, value, attributes);
- }
- return SetPropertyWithCallback(callback_object,
- name,
- value,
- result->holder(),
- strict_mode);
+ return self->SetPropertyWithCallback(callback_object,
+ *name,
+ *value,
+ result->holder(),
+ strict_mode);
}
case INTERCEPTOR:
- return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
- case CONSTANT_TRANSITION: {
+ return self->SetPropertyWithInterceptor(*name,
+ *value,
+ attributes,
+ strict_mode);
+ case TRANSITION: {
+ Object* transition = result->GetTransitionValue();
+
+ if (transition->IsAccessorPair()) {
+ if (!AccessorPair::cast(transition)->ContainsAccessor()) {
+ return self->ConvertDescriptorToField(*name,
+ *value,
+ attributes);
+ }
+ return self->SetPropertyWithCallback(transition,
+ *name,
+ *value,
+ result->holder(),
+ strict_mode);
+ }
+
+ Map* transition_map = Map::cast(transition);
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ int descriptor = descriptors->SearchWithCache(*name);
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ return self->AddFastPropertyUsingMap(transition_map,
+ *name,
+ *value,
+ field_index);
+ }
+ return self->ConvertDescriptorToField(*name, *value, attributes);
+ }
+
+ // Is transition to CONSTANT_FUNCTION.
+ Object* constant_function = descriptors->GetValue(descriptor);
// If the same constant function is being added we can simply
// transition to the target map.
- Map* target_map = result->GetTransitionMap();
- DescriptorArray* target_descriptors = target_map->instance_descriptors();
- int number = target_descriptors->SearchWithCache(name);
- ASSERT(number != DescriptorArray::kNotFound);
- ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
- JSFunction* function =
- JSFunction::cast(target_descriptors->GetValue(number));
- if (value == function) {
- set_map(target_map);
- return value;
+ if (constant_function == *value) {
+ self->set_map(transition_map);
+ return this;
}
- // Otherwise, replace with a MAP_TRANSITION to a new map with a
- // FIELD, even if the value is a constant function.
- return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
+ // Otherwise, replace with a map transition to a new map with a FIELD,
+ // even if the value is a constant function.
+ return self->ConvertDescriptorToFieldAndMapTransition(*name,
+ *value,
+ attributes);
}
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
- return value;
+ return *value;
}
UNREACHABLE(); // keep the compiler happy
- return value;
+ return *value;
}
@@ -3011,22 +3035,14 @@
return AddProperty(name, value, attributes, kNonStrictMode);
}
- PropertyDetails details = PropertyDetails(attributes, NORMAL);
-
// Check of IsReadOnly removed from here in clone.
switch (result.type()) {
- case NORMAL:
+ case NORMAL: {
+ PropertyDetails details = PropertyDetails(attributes, NORMAL);
return SetNormalizedProperty(name, value, details);
+ }
case FIELD:
return FastPropertyAtPut(result.GetFieldIndex(), value);
- case MAP_TRANSITION:
- if (attributes == result.GetAttributes()) {
- // Only use map transition if the attributes match.
- return AddFastPropertyUsingMap(result.GetTransitionMap(),
- name,
- value);
- }
- return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
if (value == result.GetConstantFunction()) return value;
@@ -3037,10 +3053,34 @@
case INTERCEPTOR:
// Override callback in clone
return ConvertDescriptorToField(name, value, attributes);
- case CONSTANT_TRANSITION:
- // Replace with a MAP_TRANSITION to a new map with a FIELD, even
- // if the value is a function.
+ case TRANSITION: {
+ Object* transition = result.GetTransitionValue();
+
+ if (transition->IsAccessorPair()) {
+ return ConvertDescriptorToField(name, value, attributes);
+ }
+
+ Map* transition_map = Map::cast(transition);
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ int descriptor = descriptors->Search(name);
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ return AddFastPropertyUsingMap(transition_map,
+ name,
+ value,
+ field_index);
+ }
+ return ConvertDescriptorToField(name, value, attributes);
+ }
+
+ // Was transition to CONSTANT_FUNCTION. Replace with a map transition to a
+ // new map with a FIELD, even if the value is a function.
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
+ }
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
@@ -3164,7 +3204,8 @@
case INTERCEPTOR:
return result->holder()->GetPropertyAttributeWithInterceptor(
JSObject::cast(receiver), name, continue_search);
- default:
+ case TRANSITION:
+ case NONEXISTENT:
UNREACHABLE();
}
}
@@ -3321,7 +3362,6 @@
break;
}
case CALLBACKS: {
- if (!descs->IsProperty(i)) break;
Object* value = descs->GetCallbacksObject(i);
if (value->IsAccessorPair()) {
MaybeObject* maybe_copy =
@@ -3333,12 +3373,11 @@
if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
break;
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
case INTERCEPTOR:
break;
case HANDLER:
case NORMAL:
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@@ -3678,13 +3717,10 @@
DescriptorArray* descriptors = this->map()->instance_descriptors();
if ((descriptors->number_of_descriptors() > 0) &&
(descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
- if (descriptors->GetType(0) == FIELD) {
- Object* hidden_store =
- this->FastPropertyAt(descriptors->GetFieldIndex(0));
- return StringDictionary::cast(hidden_store);
- } else {
- ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
- }
+ ASSERT(descriptors->GetType(0) == FIELD);
+ Object* hidden_store =
+ this->FastPropertyAt(descriptors->GetFieldIndex(0));
+ return StringDictionary::cast(hidden_store);
}
} else {
PropertyAttributes attributes;
@@ -3727,12 +3763,9 @@
DescriptorArray* descriptors = this->map()->instance_descriptors();
if ((descriptors->number_of_descriptors() > 0) &&
(descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
- if (descriptors->GetType(0) == FIELD) {
- this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
- return this;
- } else {
- ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
- }
+ ASSERT(descriptors->GetType(0) == FIELD);
+ this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
+ return this;
}
}
MaybeObject* store_result =
@@ -4167,7 +4200,7 @@
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
PropertyDetails details = descs->GetDetails(i);
- if (descs->IsProperty(i) && (details.attributes() & filter) == 0) {
+ if ((details.attributes() & filter) == 0) {
result++;
}
}
@@ -4373,7 +4406,7 @@
MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
LookupResult result(GetHeap()->isolate());
LocalLookupRealNamedProperty(name, &result);
- if (result.IsProperty() && result.IsCallbacks()) {
+ if (result.IsPropertyCallbacks()) {
// Note that the result can actually have IsDontDelete() == true when we
// e.g. have to fall back to the slow case while adding a setter after
// successfully reusing a map transition for a getter. Nevertheless, this is
@@ -4582,8 +4615,7 @@
CallbacksDescriptor callbacks_descr2(name, accessors2, attributes);
DescriptorArray* descriptors2;
{ MaybeObject* maybe_descriptors2 =
- map1->instance_descriptors()->CopyInsert(&callbacks_descr2,
- REMOVE_TRANSITIONS);
+ map1->instance_descriptors()->CopyInsert(&callbacks_descr2);
if (!maybe_descriptors2->To(&descriptors2)) return maybe_descriptors2;
}
@@ -4603,18 +4635,20 @@
// step 5: create a copy of the descriptors, incl. the new getter/setter pair
// with the transition
- CallbacksDescriptor callbacks_descr1(name, accessors1, attributes);
- DescriptorArray* descriptors1;
- { MaybeObject* maybe_descriptors1 =
- map1->instance_descriptors()->CopyInsert(&callbacks_descr1,
- KEEP_TRANSITIONS);
- if (!maybe_descriptors1->To(&descriptors1)) return maybe_descriptors1;
+ TransitionArray* new_transitions;
+ { MaybeObject* maybe_new_transitions = map1->AddTransition(name, accessors1);
+ if (!maybe_new_transitions->To(&new_transitions)) {
+ return maybe_new_transitions;
+ }
}
// step 6: everything went well so far, so we make our changes visible
- obj->set_map(map2);
- map1->set_instance_descriptors(descriptors1);
+ { MaybeObject* transition_added = map1->set_transitions(new_transitions);
+ if (transition_added->IsFailure()) return transition_added;
+ }
+
map2->SetBackPointer(map1);
+ obj->set_map(map2);
return obj;
}
@@ -4652,8 +4686,7 @@
CallbacksDescriptor callbacks_descr3(name, accessors3, attributes);
DescriptorArray* descriptors3;
{ MaybeObject* maybe_descriptors3 =
- map2->instance_descriptors()->CopyInsert(&callbacks_descr3,
- REMOVE_TRANSITIONS);
+ map2->instance_descriptors()->CopyInsert(&callbacks_descr3);
if (!maybe_descriptors3->To(&descriptors3)) return maybe_descriptors3;
}
@@ -4664,10 +4697,20 @@
}
map3->set_instance_descriptors(descriptors3);
- // step 4: everything went well so far, so we make our changes visible
+ // step 4: add a new transition to the new map
+ TransitionArray* new_transitions;
+ { MaybeObject* maybe_transitions = map2->AddTransition(name, accessors2);
+ if (!maybe_transitions->To(&new_transitions)) return maybe_transitions;
+ }
+
+ // step 5: everything went well so far, so we make our changes visible
+ { MaybeObject* transition_added = map2->set_transitions(new_transitions);
+ if (transition_added->IsFailure()) return transition_added;
+ }
+
+ map3->SetBackPointer(map2);
obj->set_map(map3);
accessors2->set(component, map3);
- map3->SetBackPointer(map2);
return obj;
}
@@ -4686,7 +4729,8 @@
}
// If the property is not a JavaScript accessor, fall back to the slow case.
- if (result.type() != CALLBACKS) return GetHeap()->null_value();
+ if (!result.IsCallbacks()) return GetHeap()->null_value();
+
Object* callback_value = result.GetCallbackObject();
if (!callback_value->IsAccessorPair()) return GetHeap()->null_value();
AccessorPair* accessors = AccessorPair::cast(callback_value);
@@ -4699,7 +4743,6 @@
return this;
}
- // When we re-add the same accessor again, there is nothing to do.
if (entry == accessor && result.GetAttributes() == attributes) return this;
// Only the other accessor has been set so far, create a new transition.
@@ -4741,9 +4784,7 @@
// Try to flatten before operating on the string.
name->TryFlatten();
- if (!CanSetCallback(name)) {
- return isolate->heap()->undefined_value();
- }
+ if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
uint32_t index = 0;
bool is_element = name->AsArrayIndex(&index);
@@ -4843,7 +4884,7 @@
JSObject::cast(obj)->LocalLookup(name, &result);
if (result.IsProperty()) {
if (result.IsReadOnly()) return heap->undefined_value();
- if (result.IsCallbacks()) {
+ if (result.IsPropertyCallbacks()) {
Object* obj = result.GetCallbackObject();
if (obj->IsAccessorPair()) {
return AccessorPair::cast(obj)->GetComponent(component);
@@ -4903,7 +4944,7 @@
JSFunction* ctor = JSFunction::cast(constructor());
Object* descriptors;
{ MaybeObject* maybe_descriptors =
- ctor->initial_map()->instance_descriptors()->RemoveTransitions(
+ ctor->initial_map()->instance_descriptors()->Copy(
DescriptorArray::MAY_BE_SHARED);
if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
}
@@ -4966,7 +5007,7 @@
}
Object* descriptors;
{ MaybeObject* maybe_descriptors =
- instance_descriptors()->RemoveTransitions(shared_mode);
+ instance_descriptors()->Copy(shared_mode);
if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
}
cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
@@ -5030,18 +5071,16 @@
// field of the contens array while it is running.
class IntrusiveMapTransitionIterator {
public:
- explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array)
- : descriptor_array_(descriptor_array) { }
+ explicit IntrusiveMapTransitionIterator(TransitionArray* transition_array)
+ : transition_array_(transition_array) { }
void Start() {
ASSERT(!IsIterating());
- if (descriptor_array_->MayContainTransitions())
- *DescriptorArrayHeader() = Smi::FromInt(0);
+ *TransitionArrayHeader() = Smi::FromInt(0);
}
bool IsIterating() {
- return descriptor_array_->MayContainTransitions() &&
- (*DescriptorArrayHeader())->IsSmi();
+ return (*TransitionArrayHeader())->IsSmi();
}
Map* Next() {
@@ -5051,66 +5090,51 @@
// next descriptor by adding 2 to the index. The exceptions are the
// CALLBACKS entries: An even index means we look at its getter, and an odd
// index means we look at its setter.
- int raw_index = Smi::cast(*DescriptorArrayHeader())->value();
+ int raw_index = Smi::cast(*TransitionArrayHeader())->value();
int index = raw_index / 2;
- int number_of_descriptors = descriptor_array_->number_of_descriptors();
- while (index < number_of_descriptors) {
- PropertyDetails details(descriptor_array_->GetDetails(index));
- switch (details.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- // We definitely have a map transition.
- *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
- return static_cast<Map*>(descriptor_array_->GetValue(index));
- case CALLBACKS: {
- // We might have a map transition in a getter or in a setter.
- AccessorPair* accessors =
- static_cast<AccessorPair*>(descriptor_array_->GetValue(index));
- Object* accessor;
- if ((raw_index & 1) == 0) {
- accessor = accessors->setter();
- } else {
- ++index;
- accessor = accessors->getter();
- }
- ++raw_index;
- if (accessor->IsMap()) {
- *DescriptorArrayHeader() = Smi::FromInt(raw_index);
- return static_cast<Map*>(accessor);
- }
- break;
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- // We definitely have no map transition.
- raw_index += 2;
- ++index;
- break;
- case NONEXISTENT:
- UNREACHABLE();
- break;
+ int number_of_transitions = transition_array_->number_of_transitions();
+ while (index < number_of_transitions) {
+ Object* value = transition_array_->GetValue(index);
+
+ if (value->IsMap()) {
+ *TransitionArrayHeader() = Smi::FromInt(raw_index + 2);
+ return static_cast<Map*>(value);
+ }
+
+ ASSERT(value->IsAccessorPair());
+
+ // We might have a map transition in a getter or in a setter.
+ AccessorPair* accessors = static_cast<AccessorPair*>(value);
+ Object* accessor;
+ if ((raw_index & 1) == 0) {
+ accessor = accessors->setter();
+ } else {
+ ++index;
+ accessor = accessors->getter();
+ }
+ ++raw_index;
+ if (accessor->IsMap()) {
+ *TransitionArrayHeader() = Smi::FromInt(raw_index);
+ return static_cast<Map*>(accessor);
}
}
- if (index == descriptor_array_->number_of_descriptors()) {
- Map* elements_transition = descriptor_array_->elements_transition_map();
- if (elements_transition != NULL) {
- *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
- return elements_transition;
- }
+
+ if (index == transition_array_->number_of_transitions() &&
+ transition_array_->HasElementsTransition()) {
+ Map* elements_transition = transition_array_->elements_transition();
+ *TransitionArrayHeader() = Smi::FromInt(raw_index + 2);
+ return elements_transition;
}
- *DescriptorArrayHeader() = descriptor_array_->GetHeap()->fixed_array_map();
+ *TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map();
return NULL;
}
private:
- Object** DescriptorArrayHeader() {
- return HeapObject::RawField(descriptor_array_, DescriptorArray::kMapOffset);
+ Object** TransitionArrayHeader() {
+ return HeapObject::RawField(transition_array_, TransitionArray::kMapOffset);
}
- DescriptorArray* descriptor_array_;
+ TransitionArray* transition_array_;
};
@@ -5210,22 +5234,19 @@
// Can either be Smi (no instance descriptors), or a descriptor array with the
// header overwritten as a Smi (thus iterating).
- DescriptorArray* MutatedInstanceDescriptors() {
- Object* object =
- *HeapObject::RawField(this, kInstanceDescriptorsOrBitField3Offset);
- if (object->IsSmi()) {
- return GetHeap()->empty_descriptor_array();
- } else {
- DescriptorArray* descriptor_array =
- static_cast<DescriptorArray*>(object);
- return descriptor_array;
- }
+ TransitionArray* MutatedTransitions() {
+ Object* object = *HeapObject::RawField(instance_descriptors(),
+ DescriptorArray::kTransitionsOffset);
+ TransitionArray* transition_array = static_cast<TransitionArray*>(object);
+ return transition_array;
}
// Start iterating over this map's children, possibly destroying a FixedArray
// map (see explanation above).
void ChildIteratorStart() {
- IntrusiveMapTransitionIterator(instance_descriptors()).Start();
+ if (HasTransitionArray()) {
+ IntrusiveMapTransitionIterator(transitions()).Start();
+ }
IntrusivePrototypeTransitionIterator(
unchecked_prototype_transitions()).Start();
}
@@ -5239,11 +5260,13 @@
Map* next = proto_iterator.Next();
if (next != NULL) return static_cast<TraversableMap*>(next);
}
- IntrusiveMapTransitionIterator
- descriptor_iterator(MutatedInstanceDescriptors());
- if (descriptor_iterator.IsIterating()) {
- Map* next = descriptor_iterator.Next();
- if (next != NULL) return static_cast<TraversableMap*>(next);
+ if (HasTransitionArray()) {
+ IntrusiveMapTransitionIterator
+ transitions_iterator(MutatedTransitions());
+ if (transitions_iterator.IsIterating()) {
+ Map* next = transitions_iterator.Next();
+ if (next != NULL) return static_cast<TraversableMap*>(next);
+ }
}
return NULL;
}
@@ -5872,71 +5895,46 @@
}
-MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
- TransitionFlag transition_flag) {
- // Transitions are only kept when inserting another transition.
- // This precondition is not required by this function's implementation, but
- // is currently required by the semantics of maps, so we check it.
- // Conversely, we filter after replacing, so replacing a transition and
- // removing all other transitions is not supported.
- bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
- ASSERT(remove_transitions == !descriptor->ContainsTransition());
-
+MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
// Ensure the key is a symbol.
{ MaybeObject* maybe_result = descriptor->KeyToSymbol();
if (maybe_result->IsFailure()) return maybe_result;
}
- int new_size = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (remove_transitions && IsTransitionOnly(i)) continue;
- new_size++;
- }
+ int new_size = number_of_descriptors();
// If key is in descriptor, we replace it in-place when filtering.
// Count a null descriptor for key as inserted, not replaced.
- int index = Search(descriptor->GetKey());
+ int index = SearchWithCache(descriptor->GetKey());
const bool replacing = (index != kNotFound);
bool keep_enumeration_index = false;
- if (!replacing) {
- ++new_size;
- } else if (!IsTransitionOnly(index)) {
+ if (replacing) {
// We are replacing an existing descriptor. We keep the enumeration index
// of a visible property.
keep_enumeration_index = true;
- } else if (remove_transitions) {
- // Replaced descriptor has been counted as removed if it is a transition
- // that will be replaced. Adjust count in this case.
+ } else {
++new_size;
}
DescriptorArray* new_descriptors;
- { SharedMode mode = remove_transitions ? MAY_BE_SHARED : CANNOT_BE_SHARED;
- MaybeObject* maybe_result = Allocate(new_size, mode);
+ { MaybeObject* maybe_result = Allocate(new_size, MAY_BE_SHARED);
if (!maybe_result->To(&new_descriptors)) return maybe_result;
}
- DescriptorArray::WhitenessWitness witness(new_descriptors);
+ FixedArray::WhitenessWitness witness(new_descriptors);
// Set the enumeration index in the descriptors and set the enumeration index
// in the result.
int enumeration_index = NextEnumerationIndex();
- if (!descriptor->ContainsTransition()) {
- if (keep_enumeration_index) {
- descriptor->SetEnumerationIndex(GetDetails(index).index());
- } else {
- descriptor->SetEnumerationIndex(enumeration_index);
- ++enumeration_index;
- }
- }
- Map* old_elements_transition = elements_transition_map();
- if ((!remove_transitions) && (old_elements_transition != NULL)) {
- new_descriptors->set_elements_transition_map(old_elements_transition);
+ if (keep_enumeration_index) {
+ descriptor->SetEnumerationIndex(GetDetails(index).index());
+ } else {
+ descriptor->SetEnumerationIndex(enumeration_index);
+ ++enumeration_index;
}
new_descriptors->SetNextEnumerationIndex(enumeration_index);
- // Copy the descriptors, filtering out transitions and null descriptors,
- // and inserting or replacing a descriptor.
+ // Copy the descriptors, inserting or replacing a descriptor.
int to_index = 0;
int insertion_index = -1;
int from_index = 0;
@@ -5946,11 +5944,9 @@
insertion_index = to_index++;
if (replacing) from_index++;
} else {
- if (!(remove_transitions && IsTransitionOnly(from_index))) {
- MaybeObject* copy_result =
- new_descriptors->CopyFrom(to_index++, this, from_index, witness);
- if (copy_result->IsFailure()) return copy_result;
- }
+ MaybeObject* copy_result =
+ new_descriptors->CopyFrom(to_index++, this, from_index, witness);
+ if (copy_result->IsFailure()) return copy_result;
from_index++;
}
}
@@ -5966,31 +5962,25 @@
}
-MaybeObject* DescriptorArray::RemoveTransitions(SharedMode shared_mode) {
+MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) {
// Allocate the new descriptor array.
- int new_number_of_descriptors = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsProperty(i)) new_number_of_descriptors++;
- }
+ int number_of_descriptors = this->number_of_descriptors();
DescriptorArray* new_descriptors;
- { MaybeObject* maybe_result = Allocate(new_number_of_descriptors,
+ { MaybeObject* maybe_result = Allocate(number_of_descriptors,
shared_mode);
if (!maybe_result->To(&new_descriptors)) return maybe_result;
}
// Copy the content.
- DescriptorArray::WhitenessWitness witness(new_descriptors);
- int next_descriptor = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsProperty(i)) {
+ if (number_of_descriptors > 0) {
+ FixedArray::WhitenessWitness witness(new_descriptors);
+ for (int i = 0; i < number_of_descriptors; i++) {
MaybeObject* copy_result =
- new_descriptors->CopyFrom(next_descriptor++, this, i, witness);
+ new_descriptors->CopyFrom(i, this, i, witness);
if (copy_result->IsFailure()) return copy_result;
}
}
- ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
-
return new_descriptors;
}
@@ -6057,43 +6047,6 @@
}
-int DescriptorArray::BinarySearch(String* name, int low, int high) {
- uint32_t hash = name->Hash();
- int limit = high;
-
- ASSERT(low <= high);
-
- while (low != high) {
- int mid = (low + high) / 2;
- String* mid_name = GetKey(mid);
- uint32_t mid_hash = mid_name->Hash();
-
- if (mid_hash >= hash) {
- high = mid;
- } else {
- low = mid + 1;
- }
- }
-
- for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
- if (GetKey(low)->Equals(name)) return low;
- }
-
- return kNotFound;
-}
-
-
-int DescriptorArray::LinearSearch(SearchMode mode, String* name, int len) {
- uint32_t hash = name->Hash();
- for (int number = 0; number < len; number++) {
- String* entry = GetKey(number);
- if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
- if (name->Equals(entry)) return number;
- }
- return kNotFound;
-}
-
-
MaybeObject* AccessorPair::CopyWithoutTransitions() {
Heap* heap = GetHeap();
AccessorPair* copy;
@@ -7336,17 +7289,6 @@
}
-// Clear a possible back pointer in case the transition leads to a dead map.
-// Return true in case a back pointer has been cleared and false otherwise.
-static bool ClearBackPointer(Heap* heap, Object* target) {
- ASSERT(target->IsMap());
- Map* map = Map::cast(target);
- if (Marking::MarkBitFrom(map).Get()) return false;
- map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
- return true;
-}
-
-
// This function should only be called from within the GC, since it uses
// IncrementLiveBytesFromGC. If called from anywhere else, this results in an
// inconsistent live-bytes count.
@@ -7387,118 +7329,101 @@
}
-// If the descriptor describes a transition to a dead map, the back pointer
-// of this map is cleared and we return true. Otherwise we return false.
-static bool ClearNonLiveTransitionsFromDescriptor(Heap* heap,
- DescriptorArray* d,
- int descriptor_index) {
- // If the pair (value, details) is a map transition, check if the target is
- // live. If not, null the descriptor. Also drop the back pointer for that
- // map transition, so that this map is not reached again by following a back
- // pointer from that non-live map.
- PropertyDetails details(d->GetDetails(descriptor_index));
- switch (details.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return ClearBackPointer(heap, d->GetValue(descriptor_index));
- case CALLBACKS: {
- Object* object = d->GetValue(descriptor_index);
- if (object->IsAccessorPair()) {
- bool cleared = true;
- AccessorPair* accessors = AccessorPair::cast(object);
- Object* getter = accessors->getter();
- if (getter->IsMap()) {
- if (ClearBackPointer(heap, getter)) {
- accessors->set_getter(heap->the_hole_value());
- } else {
- cleared = false;
- }
- } else if (!getter->IsTheHole()) {
- cleared = false;
- }
- Object* setter = accessors->setter();
- if (setter->IsMap()) {
- if (ClearBackPointer(heap, setter)) {
- accessors->set_setter(heap->the_hole_value());
- } else {
- cleared = false;
- }
- } else if (!setter->IsTheHole()) {
- cleared = false;
- }
- return cleared;
- }
- return false;
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- return false;
- case NONEXISTENT:
- break;
- }
- UNREACHABLE();
+// Clear a possible back pointer in case the transition leads to a dead map.
+// Return true in case a back pointer has been cleared and false otherwise.
+static bool ClearBackPointer(Heap* heap, Object* target) {
+ ASSERT(target->IsMap());
+ Map* map = Map::cast(target);
+ if (Marking::MarkBitFrom(map).Get()) return false;
+ map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
return true;
}
+static bool ClearAccessorComponent(Heap* heap,
+ AccessorPair* accessors,
+ AccessorComponent component) {
+ Object* component_value = accessors->get(component);
+ if (!component_value->IsMap()) return true;
+ if (ClearBackPointer(heap, component_value)) {
+ accessors->set(component, heap->the_hole_value());
+ return true;
+ }
+ return false;
+}
+
+
+static bool ClearNonLiveTransition(Heap* heap,
+ TransitionArray* t,
+ int transition_index) {
+ // If the value is a map, check if the target is live. If not, clear the
+ // transition. Also drop the back pointer for that map transition, so that
+ // this map is not reached again by following a back pointer from that
+ // non-live map.
+ Object* value = t->GetValue(transition_index);
+ if (value->IsMap()) {
+ return ClearBackPointer(heap, t->GetValue(transition_index));
+ }
+
+ ASSERT(value->IsAccessorPair());
+
+ AccessorPair* accessors = AccessorPair::cast(value);
+ bool getter = ClearAccessorComponent(heap, accessors, ACCESSOR_GETTER);
+ bool setter = ClearAccessorComponent(heap, accessors, ACCESSOR_SETTER);
+ return getter && setter;
+}
+
+
// TODO(mstarzinger): This method should be moved into MarkCompactCollector,
// because it cannot be called from outside the GC and we already have methods
// depending on the transitions layout in the GC anyways.
void Map::ClearNonLiveTransitions(Heap* heap) {
- Object* array = *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset);
- // If there are no descriptors to be cleared, return.
+ // If there are no transitions to be cleared, return.
// TODO(verwaest) Should be an assert, otherwise back pointers are not
// properly cleared.
- if (array->IsSmi()) return;
- DescriptorArray* d = DescriptorArray::cast(array);
+ if (!HasTransitionArray()) return;
- int descriptor_index = 0;
+ TransitionArray* t = transitions();
+
+ int transition_index = 0;
+
// Compact all live descriptors to the left.
- for (int i = 0; i < d->number_of_descriptors(); ++i) {
- if (!ClearNonLiveTransitionsFromDescriptor(heap, d, i)) {
- if (i != descriptor_index) {
- String* key = d->GetKey(i);
- Object* value = d->GetValue(i);
- d->SetKeyUnchecked(heap, descriptor_index, key);
- d->SetDetailsUnchecked(descriptor_index, d->GetDetails(i).AsSmi());
- d->SetValueUnchecked(heap, descriptor_index, value);
+ for (int i = 0; i < t->number_of_transitions(); ++i) {
+ if (!ClearNonLiveTransition(heap, t, i)) {
+ if (i != transition_index) {
+ String* key = t->GetKey(i);
+ Object* value = t->GetValue(i);
+ t->SetKey(transition_index, key);
+ t->SetValue(transition_index, value);
MarkCompactCollector* collector = heap->mark_compact_collector();
- Object** key_slot = d->GetKeySlot(descriptor_index);
+ Object** key_slot = t->GetKeySlot(transition_index);
collector->RecordSlot(key_slot, key_slot, key);
- if (value->IsHeapObject()) {
- Object** value_slot = d->GetValueSlot(descriptor_index);
- collector->RecordSlot(value_slot, value_slot, value);
- }
+ Object** value_slot = t->GetValueSlot(transition_index);
+ collector->RecordSlot(value_slot, value_slot, value);
}
- descriptor_index++;
+ transition_index++;
}
}
- Map* elements_transition = d->elements_transition_map();
- if (elements_transition != NULL &&
- ClearBackPointer(heap, elements_transition)) {
- elements_transition = NULL;
- d->ClearElementsTransition();
+ if (t->HasElementsTransition() &&
+ ClearBackPointer(heap, t->elements_transition())) {
+ t->ClearElementsTransition();
} else {
- // If there are no descriptors to be cleared, return.
+ // If there are no transitions to be cleared, return.
// TODO(verwaest) Should be an assert, otherwise back pointers are not
// properly cleared.
- if (descriptor_index == d->number_of_descriptors()) return;
+ if (transition_index == t->number_of_transitions()) return;
}
- // If the final descriptor array does not contain any live descriptors, remove
- // the descriptor array from the map.
- if (descriptor_index == 0 && elements_transition == NULL) {
- ClearDescriptorArray();
- return;
+ // If the final transition array does not contain any live transitions, remove
+ // the transition array from the map.
+ if (transition_index == 0 && !t->HasElementsTransition()) {
+ return ClearTransitions();
}
- int trim = d->number_of_descriptors() - descriptor_index;
+ int trim = t->number_of_transitions() - transition_index;
if (trim > 0) {
- RightTrimFixedArray(heap, d, trim * DescriptorArray::kDescriptorSize);
+ RightTrimFixedArray(heap, t, trim * TransitionArray::kTransitionSize);
}
}
@@ -10693,10 +10618,10 @@
ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
+ ASSERT(storage->length() >= index + descs->number_of_descriptors());
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
+ storage->set(index + i, descs->GetKey(i));
}
- ASSERT(storage->length() >= index);
} else {
property_dictionary()->CopyKeysTo(storage,
index,
@@ -11324,32 +11249,6 @@
}
-bool StringDictionary::ContainsTransition(int entry) {
- switch (DetailsAt(entry).type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return true;
- case CALLBACKS: {
- Object* value = ValueAt(entry);
- if (!value->IsAccessorPair()) return false;
- AccessorPair* accessors = AccessorPair::cast(value);
- return accessors->getter()->IsMap() || accessors->setter()->IsMap();
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- return false;
- case NONEXISTENT:
- UNREACHABLE();
- break;
- }
- UNREACHABLE(); // Keep the compiler happy.
- return false;
-}
-
-
template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
ASSERT(NumberOfElements() < new_table->Capacity());
@@ -12786,7 +12685,7 @@
}
}
- DescriptorArray::WhitenessWitness witness(descriptors);
+ FixedArray::WhitenessWitness witness(descriptors);
int inobject_props = obj->map()->inobject_properties();
int number_of_allocated_fields =
diff --git a/src/objects.h b/src/objects.h
index 274f1dc..c479a14 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -780,6 +780,7 @@
V(JSModule) \
V(Map) \
V(DescriptorArray) \
+ V(TransitionArray) \
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
V(TypeFeedbackCells) \
@@ -1901,7 +1902,8 @@
// new_map.
MUST_USE_RESULT MaybeObject* AddFastPropertyUsingMap(Map* new_map,
String* name,
- Object* value);
+ Object* value,
+ int field_index);
// Add a constant function property to a fast-case object.
// This leaves a CONSTANT_TRANSITION in the old map, and
@@ -2059,6 +2061,10 @@
PrintElements(stdout);
}
void PrintElements(FILE* out);
+ inline void PrintTransitions() {
+ PrintTransitions(stdout);
+ }
+ void PrintTransitions(FILE* out);
#endif
void PrintElementsTransition(
@@ -2212,7 +2218,6 @@
Object* getter,
Object* setter,
PropertyAttributes attributes);
- void LookupInDescriptor(String* name, LookupResult* result);
// Returns the hidden properties backing store object, currently
// a StringDictionary, stored on this object.
@@ -2247,6 +2252,8 @@
class FixedDoubleArray;
+class IncrementalMarking;
+
// FixedArray describes fixed-sized arrays with element type Object*.
class FixedArray: public FixedArrayBase {
@@ -2342,6 +2349,23 @@
}
};
+ // WhitenessWitness is used to prove that a descriptor array is white
+ // (unmarked), so incremental write barriers can be skipped because the
+ // marking invariant cannot be broken and slots pointing into evacuation
+ // candidates will be discovered when the object is scanned. A witness is
+ // always stack-allocated right after creating an array. By allocating a
+ // witness, incremental marking is globally disabled. The witness is then
+ // passed along wherever needed to statically prove that the array is known to
+ // be white.
+ class WhitenessWitness {
+ public:
+ inline explicit WhitenessWitness(FixedArray* array);
+ inline ~WhitenessWitness();
+
+ private:
+ IncrementalMarking* marking_;
+ };
+
protected:
// Set operation on FixedArray without using write barriers. Can
// only be used for storing old space objects or smis.
@@ -2416,9 +2440,6 @@
};
-class IncrementalMarking;
-
-
// DescriptorArrays are fixed arrays used to hold instance descriptors.
// The format of the these objects is:
// TODO(1399): It should be possible to make room for bit_field3 in the map
@@ -2430,7 +2451,7 @@
// [0]: next enumeration index (Smi)
// [1]: pointer to fixed array with enum cache
// [3]: first key
-// [length() - 1]: last key
+// [length() - kDescriptorSize]: last key
//
class DescriptorArray: public FixedArray {
public:
@@ -2439,17 +2460,20 @@
// yet used.
inline bool IsEmpty();
inline bool MayContainTransitions();
+ inline bool HasTransitionArray();
- DECL_ACCESSORS(elements_transition_map, Map)
- inline void ClearElementsTransition();
+ DECL_ACCESSORS(transitions, TransitionArray)
+ inline void ClearTransitions();
// Returns the number of descriptors in the array.
int number_of_descriptors() {
- ASSERT(length() >= kFirstIndex || IsEmpty());
+ ASSERT(MayContainTransitions() || IsEmpty());
int len = length();
return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kDescriptorSize;
}
+ inline int number_of_entries() { return number_of_descriptors(); }
+
int NextEnumerationIndex() {
if (IsEmpty()) return PropertyDetails::kInitialIndex;
Object* obj = get(kEnumerationIndexIndex);
@@ -2484,7 +2508,6 @@
}
Object** GetTransitionsSlot() {
- ASSERT(elements_transition_map() != NULL);
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kTransitionsOffset);
}
@@ -2504,39 +2527,14 @@
// Accessors for fetching instance descriptor at descriptor number.
inline String* GetKey(int descriptor_number);
inline Object** GetKeySlot(int descriptor_number);
- inline void SetKeyUnchecked(Heap* heap, int descriptor_number, String* value);
inline Object* GetValue(int descriptor_number);
inline Object** GetValueSlot(int descriptor_number);
- inline void SetNullValueUnchecked(Heap* heap, int descriptor_number);
- inline void SetValueUnchecked(Heap* heap,
- int descriptor_number,
- Object* value);
inline PropertyDetails GetDetails(int descriptor_number);
- inline void SetDetailsUnchecked(int descriptor_number, Smi* value);
inline PropertyType GetType(int descriptor_number);
inline int GetFieldIndex(int descriptor_number);
inline JSFunction* GetConstantFunction(int descriptor_number);
inline Object* GetCallbacksObject(int descriptor_number);
inline AccessorDescriptor* GetCallbacks(int descriptor_number);
- inline bool IsProperty(int descriptor_number);
- inline bool IsTransitionOnly(int descriptor_number);
-
- // WhitenessWitness is used to prove that a specific descriptor array is white
- // (unmarked), so incremental write barriers can be skipped because the
- // marking invariant cannot be broken and slots pointing into evacuation
- // candidates will be discovered when the object is scanned. A witness is
- // always stack-allocated right after creating a descriptor array. By
- // allocating a witness, incremental marking is globally disabled. The witness
- // is then passed along wherever needed to statically prove that the
- // descriptor array is known to be white.
- class WhitenessWitness {
- public:
- inline explicit WhitenessWitness(DescriptorArray* array);
- inline ~WhitenessWitness();
-
- private:
- IncrementalMarking* marking_;
- };
// Accessor for complete descriptor.
inline void Get(int descriptor_number, Descriptor* desc);
@@ -2565,8 +2563,7 @@
// or null), its enumeration index is kept as is.
// If adding a real property, map transitions must be removed. If adding
// a transition, they must not be removed. All null descriptors are removed.
- MUST_USE_RESULT MaybeObject* CopyInsert(Descriptor* descriptor,
- TransitionFlag transition_flag);
+ MUST_USE_RESULT MaybeObject* CopyInsert(Descriptor* descriptor);
// Indicates whether the search function should expect a sorted or an unsorted
// descriptor array as input.
@@ -2577,7 +2574,7 @@
// Return a copy of the array with all transitions and null descriptors
// removed. Return a Failure object in case of an allocation failure.
- MUST_USE_RESULT MaybeObject* RemoveTransitions(SharedMode shared_mode);
+ MUST_USE_RESULT MaybeObject* Copy(SharedMode shared_mode);
// Sort the instance descriptors by the hash codes of their keys.
// Does not check for duplicates.
@@ -2597,17 +2594,6 @@
// Tells whether the name is present int the array.
bool Contains(String* name) { return kNotFound != Search(name); }
- // Perform a binary search in the instance descriptors represented
- // by this fixed array. low and high are descriptor indices. If there
- // are three instance descriptors in this array it should be called
- // with low=0 and high=2.
- int BinarySearch(String* name, int low, int high);
-
- // Perform a linear search in the instance descriptors represented
- // by this fixed array. len is the number of descriptor indices that are
- // valid.
- int LinearSearch(SearchMode mode, String* name, int len);
-
// Allocates a DescriptorArray, but returns the singleton
// empty descriptor array object if number_of_descriptors is 0.
MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors,
@@ -2632,8 +2618,8 @@
// Layout description.
static const int kBitField3StorageOffset = FixedArray::kHeaderSize;
- static const int kEnumerationIndexOffset =
- kBitField3StorageOffset + kPointerSize;
+ static const int kEnumerationIndexOffset = kBitField3StorageOffset +
+ kPointerSize;
static const int kTransitionsOffset = kEnumerationIndexOffset + kPointerSize;
static const int kFirstOffset = kTransitionsOffset + kPointerSize;
@@ -2709,7 +2695,7 @@
static inline void NoIncrementalWriteBarrierSwap(
FixedArray* array, int first, int second);
- // Swap descriptor first and second.
+ // Swap first and second descriptor.
inline void NoIncrementalWriteBarrierSwapDescriptors(
int first, int second);
@@ -2717,6 +2703,14 @@
};
+template<typename T>
+inline int LinearSearch(T* array, SearchMode mode, String* name, int len);
+
+
+template<typename T>
+inline int Search(T* array, String* name);
+
+
// HashTable is a subclass of FixedArray that implements a hash table
// that uses open addressing and quadratic probing.
//
@@ -3181,8 +3175,6 @@
// Find entry for key, otherwise return kNotFound. Optimized version of
// HashTable::FindEntry.
int FindEntry(String* key);
-
- bool ContainsTransition(int entry);
};
@@ -4631,6 +4623,7 @@
// DescriptorArray when the map has one).
inline int bit_field3();
inline void set_bit_field3(int value);
+ inline void SetOwnBitField3(int value);
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
@@ -4751,8 +4744,17 @@
static bool IsValidElementsTransition(ElementsKind from_kind,
ElementsKind to_kind);
+ inline bool HasTransitionArray();
+ inline bool HasElementsTransition();
inline Map* elements_transition_map();
- inline void set_elements_transition_map(Map* transitioned_map);
+ MUST_USE_RESULT inline MaybeObject* set_elements_transition_map(
+ Map* transitioned_map);
+ inline TransitionArray* transitions();
+ MUST_USE_RESULT inline MaybeObject* AddTransition(String* key,
+ Object* value);
+ MUST_USE_RESULT inline MaybeObject* set_transitions(
+ TransitionArray* transitions);
+ inline void ClearTransitions();
// Tells whether the map is attached to SharedFunctionInfo
// (for inobject slack tracking).
@@ -4842,9 +4844,17 @@
// Lookup in the map's instance descriptors and fill out the result
// with the given holder if the name is found. The holder may be
// NULL when this function is used from the compiler.
- void LookupInDescriptors(JSObject* holder,
- String* name,
- LookupResult* result);
+ void LookupDescriptor(JSObject* holder,
+ String* name,
+ LookupResult* result);
+
+ void LookupTransition(JSObject* holder,
+ String* name,
+ LookupResult* result);
+
+ void LookupTransitionOrDescriptor(JSObject* holder,
+ String* name,
+ LookupResult* result);
MUST_USE_RESULT MaybeObject* CopyDropDescriptors();
@@ -4928,8 +4938,8 @@
// holding weak references when incremental marking is used, because it also
// iterates over objects that are otherwise unreachable.
#ifdef DEBUG
- void ZapInstanceDescriptors();
void ZapPrototypeTransitions();
+ void ZapTransitions();
#endif
// Dispatched behavior.
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 847cff5..ea93006 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -2233,9 +2233,8 @@
case NORMAL: // only in slow mode
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
- case MAP_TRANSITION: // we do not care about transitions here...
- case CONSTANT_TRANSITION:
break;
+ case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
diff --git a/src/property-details.h b/src/property-details.h
index e898a7d..51b889a 100644
--- a/src/property-details.h
+++ b/src/property-details.h
@@ -55,17 +55,18 @@
// Must fit in the BitField PropertyDetails::TypeField.
// A copy of this is in mirror-debugger.js.
enum PropertyType {
- NORMAL = 0, // only in slow mode
- FIELD = 1, // only in fast mode
- CONSTANT_FUNCTION = 2, // only in fast mode
+ // Only in slow mode.
+ NORMAL = 0,
+ // Only in fast mode.
+ FIELD = 1,
+ CONSTANT_FUNCTION = 2,
CALLBACKS = 3,
- HANDLER = 4, // only in lookup results, not in descriptors
- INTERCEPTOR = 5, // only in lookup results, not in descriptors
- // All properties before MAP_TRANSITION are real.
- MAP_TRANSITION = 6, // only in fast mode
- CONSTANT_TRANSITION = 7, // only in fast mode
+ // Only in lookup results, not in descriptors.
+ HANDLER = 4,
+ INTERCEPTOR = 5,
+ TRANSITION = 6,
// Only used as a marker in LookupResult.
- NONEXISTENT = 8
+ NONEXISTENT = 7
};
@@ -112,10 +113,10 @@
// Bit fields in value_ (type, shift, size). Must be public so the
// constants can be embedded in generated code.
- class TypeField: public BitField<PropertyType, 0, 4> {};
- class AttributesField: public BitField<PropertyAttributes, 4, 3> {};
- class DeletedField: public BitField<uint32_t, 7, 1> {};
- class StorageField: public BitField<uint32_t, 8, 32-8> {};
+ class TypeField: public BitField<PropertyType, 0, 3> {};
+ class AttributesField: public BitField<PropertyAttributes, 3, 3> {};
+ class DeletedField: public BitField<uint32_t, 6, 1> {};
+ class StorageField: public BitField<uint32_t, 7, 32-7> {};
static const int kInitialIndex = 1;
diff --git a/src/property.cc b/src/property.cc
index 3258b8c..132f083 100644
--- a/src/property.cc
+++ b/src/property.cc
@@ -55,12 +55,6 @@
PrintF(out, " -type = normal\n");
PrintF(out, " -entry = %d", GetDictionaryEntry());
break;
- case MAP_TRANSITION:
- PrintF(out, " -type = map transition\n");
- PrintF(out, " -map:\n");
- GetTransitionMap()->Print(out);
- PrintF(out, "\n");
- break;
case CONSTANT_FUNCTION:
PrintF(out, " -type = constant function\n");
PrintF(out, " -function:\n");
@@ -83,12 +77,29 @@
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;
- case CONSTANT_TRANSITION:
- PrintF(out, " -type = constant property transition\n");
- PrintF(out, " -map:\n");
- GetTransitionMap()->Print(out);
- PrintF(out, "\n");
- break;
+ case TRANSITION:
+ switch (GetTransitionDetails().type()) {
+ case FIELD:
+ PrintF(out, " -type = map transition\n");
+ PrintF(out, " -map:\n");
+ GetTransitionMap()->Print(out);
+ PrintF(out, "\n");
+ return;
+ case CONSTANT_FUNCTION:
+ PrintF(out, " -type = constant property transition\n");
+ PrintF(out, " -map:\n");
+ GetTransitionMap()->Print(out);
+ PrintF(out, "\n");
+ return;
+ case CALLBACKS:
+ PrintF(out, " -type = callbacks transition\n");
+ PrintF(out, " -callback object:\n");
+ GetCallbackObject()->Print(out);
+ return;
+ default:
+ UNREACHABLE();
+ return;
+ }
case NONEXISTENT:
UNREACHABLE();
break;
@@ -108,29 +119,4 @@
#endif
-bool Descriptor::ContainsTransition() {
- switch (details_.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return true;
- case CALLBACKS: {
- if (!value_->IsAccessorPair()) return false;
- AccessorPair* accessors = AccessorPair::cast(value_);
- return accessors->getter()->IsMap() || accessors->setter()->IsMap();
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- return false;
- case NONEXISTENT:
- UNREACHABLE();
- break;
- }
- UNREACHABLE(); // Keep the compiler happy.
- return false;
-}
-
-
} } // namespace v8::internal
diff --git a/src/property.h b/src/property.h
index 76ae0fb..7200280 100644
--- a/src/property.h
+++ b/src/property.h
@@ -29,6 +29,7 @@
#define V8_PROPERTY_H_
#include "allocation.h"
+#include "transitions.h"
namespace v8 {
namespace internal {
@@ -68,8 +69,6 @@
details_ = PropertyDetails(details_.attributes(), details_.type(), index);
}
- bool ContainsTransition();
-
private:
String* key_;
Object* value_;
@@ -101,27 +100,6 @@
friend class DescriptorArray;
};
-// A pointer from a map to the new map that is created by adding
-// a named property. These are key to the speed and functioning of V8.
-// The two maps should always have the same prototype, since
-// MapSpace::CreateBackPointers depends on this.
-class MapTransitionDescriptor: public Descriptor {
- public:
- MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes)
- : Descriptor(key, map, attributes, MAP_TRANSITION) { }
-};
-
-// Marks a field name in a map so that adding the field is guaranteed
-// to create a FIELD descriptor in the new map. Used after adding
-// a constant function the first time, creating a CONSTANT_FUNCTION
-// descriptor in the new map. This avoids creating multiple maps with
-// the same CONSTANT_FUNCTION field.
-class ConstTransitionDescriptor: public Descriptor {
- public:
- explicit ConstTransitionDescriptor(String* key, Map* map)
- : Descriptor(key, map, NONE, CONSTANT_TRANSITION) { }
-};
-
class FieldDescriptor: public Descriptor {
public:
@@ -153,36 +131,6 @@
};
-template <class T>
-bool IsPropertyDescriptor(T* desc) {
- switch (desc->type()) {
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- return true;
- case CALLBACKS: {
- Object* callback_object = desc->GetCallbackObject();
- // Non-JavaScript (i.e. native) accessors are always a property, otherwise
- // either the getter or the setter must be an accessor. Put another way:
- // If we only see map transitions and holes in a pair, this is not a
- // property.
- return (!callback_object->IsAccessorPair() ||
- AccessorPair::cast(callback_object)->ContainsAccessor());
- }
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return false;
- case NONEXISTENT:
- UNREACHABLE();
- break;
- }
- UNREACHABLE(); // keep the compiler happy
- return false;
-}
-
-
class LookupResult BASE_EMBEDDED {
public:
explicit LookupResult(Isolate* isolate)
@@ -207,6 +155,13 @@
number_ = number;
}
+ void TransitionResult(JSObject* holder, int number) {
+ lookup_type_ = TRANSITION_TYPE;
+ details_ = PropertyDetails(NONE, TRANSITION);
+ holder_ = holder;
+ number_ = number;
+ }
+
void ConstantResult(JSObject* holder) {
lookup_type_ = CONSTANT_TYPE;
holder_ = holder;
@@ -259,29 +214,41 @@
}
PropertyAttributes GetAttributes() {
+ ASSERT(!IsTransition());
ASSERT(IsFound());
+ ASSERT(details_.type() != NONEXISTENT);
return details_.attributes();
}
PropertyDetails GetPropertyDetails() {
+ ASSERT(!IsTransition());
return details_;
}
bool IsFastPropertyType() {
ASSERT(IsFound());
- return type() != NORMAL;
+ return IsTransition() || type() != NORMAL;
+ }
+
+ // Property callbacks does not include transitions to callbacks.
+ bool IsPropertyCallbacks() {
+ ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
+ return details_.type() == CALLBACKS;
+ }
+
+ // Is callbacks contains both property callbacks and transitions to callbacks.
+ bool IsCallbacks() {
+ return IsPropertyCallbacks() ||
+ (IsTransition() && GetTransitionValue()->IsAccessorPair());
}
bool IsReadOnly() {
ASSERT(IsFound());
+ ASSERT(!IsTransition());
+ ASSERT(details_.type() != NONEXISTENT);
return details_.IsReadOnly();
}
- bool IsCallbacks() {
- ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
- return details_.type() == CALLBACKS;
- }
-
bool IsField() {
ASSERT(!(details_.type() == FIELD && !IsFound()));
return details_.type() == FIELD;
@@ -297,21 +264,17 @@
return details_.type() == CONSTANT_FUNCTION;
}
- bool IsMapTransition() {
- ASSERT(!(details_.type() == MAP_TRANSITION && !IsFound()));
- return details_.type() == MAP_TRANSITION;
- }
-
bool IsDontDelete() { return details_.IsDontDelete(); }
bool IsDontEnum() { return details_.IsDontEnum(); }
bool IsDeleted() { return details_.IsDeleted(); }
bool IsFound() { return lookup_type_ != NOT_FOUND; }
+ bool IsTransition() { return lookup_type_ == TRANSITION_TYPE; }
bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
bool IsInterceptor() { return lookup_type_ == INTERCEPTOR_TYPE; }
// Is the result is a property excluding transitions and the null descriptor?
bool IsProperty() {
- return IsFound() && IsPropertyDescriptor(this);
+ return IsFound() && !IsTransition();
}
bool IsCacheable() { return cacheable_; }
@@ -336,17 +299,35 @@
}
}
+ Object* GetTransitionValue() {
+ ASSERT(IsTransition());
+ TransitionArray* transitions = holder()->map()->transitions();
+ Object* value = transitions->GetValue(number_);
+ return value;
+ }
+
+ PropertyDetails GetTransitionDetails(Map* map) {
+ ASSERT(IsTransition());
+ TransitionArray* transitions = map->transitions();
+ return transitions->GetTargetDetails(number_);
+ }
+
+ PropertyDetails GetTransitionDetails() {
+ return GetTransitionDetails(holder()->map());
+ }
+
+ bool IsTransitionToField(Map* map) {
+ return IsTransition() && GetTransitionDetails(map).type() == FIELD;
+ }
+
Map* GetTransitionMap() {
- ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
- ASSERT(type() == MAP_TRANSITION ||
- type() == CONSTANT_TRANSITION);
+ ASSERT(IsTransition());
return Map::cast(GetValue());
}
Map* GetTransitionMapFromMap(Map* map) {
- ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
- ASSERT(type() == MAP_TRANSITION);
- return Map::cast(map->instance_descriptors()->GetValue(number_));
+ ASSERT(IsTransition());
+ return Map::cast(map->transitions()->GetValue(number_));
}
int GetFieldIndex() {
@@ -356,10 +337,8 @@
}
int GetLocalFieldIndexFromMap(Map* map) {
- ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(IsField());
- return Descriptor::IndexFromValue(
- map->instance_descriptors()->GetValue(number_)) -
+ return Descriptor::IndexFromValue(GetValueFromMap(map)) -
map->inobject_properties();
}
@@ -374,17 +353,19 @@
}
JSFunction* GetConstantFunctionFromMap(Map* map) {
- ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(type() == CONSTANT_FUNCTION);
- return JSFunction::cast(map->instance_descriptors()->GetValue(number_));
+ return JSFunction::cast(GetValueFromMap(map));
}
Object* GetCallbackObject() {
- if (lookup_type_ == CONSTANT_TYPE) {
- // For now we only have the __proto__ as constant type.
- return HEAP->prototype_accessors();
+ switch (lookup_type_) {
+ case CONSTANT_TYPE:
+ return HEAP->prototype_accessors();
+ case TRANSITION_TYPE:
+ return GetTransitionValue();
+ default:
+ return GetValue();
}
- return GetValue();
}
#ifdef OBJECT_PRINT
@@ -393,14 +374,18 @@
Object* GetValue() {
if (lookup_type_ == DESCRIPTOR_TYPE) {
- DescriptorArray* descriptors = holder()->map()->instance_descriptors();
- return descriptors->GetValue(number_);
+ return GetValueFromMap(holder()->map());
}
// In the dictionary case, the data is held in the value field.
ASSERT(lookup_type_ == DICTIONARY_TYPE);
return holder()->GetNormalizedProperty(this);
}
+ Object* GetValueFromMap(Map* map) const {
+ ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ return map->instance_descriptors()->GetValue(number_);
+ }
+
void Iterate(ObjectVisitor* visitor);
private:
@@ -411,6 +396,7 @@
enum {
NOT_FOUND,
DESCRIPTOR_TYPE,
+ TRANSITION_TYPE,
DICTIONARY_TYPE,
HANDLER_TYPE,
INTERCEPTOR_TYPE,
diff --git a/src/runtime.cc b/src/runtime.cc
index 74f3e43..8ed3c71 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1170,7 +1170,7 @@
elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!result.IsDontEnum()));
elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete()));
- bool is_js_accessor = result.IsCallbacks() &&
+ bool is_js_accessor = result.IsPropertyCallbacks() &&
(result.GetCallbackObject()->IsAccessorPair());
if (is_js_accessor) {
@@ -1421,7 +1421,7 @@
// as required for function declarations.
if (lookup.IsProperty() && lookup.IsDontDelete()) {
if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
- lookup.IsCallbacks()) {
+ lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(
isolate, is_function ? "function" : "module", name);
}
@@ -2182,7 +2182,7 @@
// Construct a new field descriptors array containing the new descriptor.
Object* descriptors_unchecked;
{ MaybeObject* maybe_descriptors_unchecked =
- instance_desc->CopyInsert(&new_desc, REMOVE_TRANSITIONS);
+ instance_desc->CopyInsert(&new_desc);
if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
return maybe_descriptors_unchecked;
}
@@ -4566,7 +4566,7 @@
// correctly in the case where a property is a field and is reset with
// new attributes.
if (result.IsProperty() &&
- (attr != result.GetAttributes() || result.IsCallbacks())) {
+ (attr != result.GetAttributes() || result.IsPropertyCallbacks())) {
// New attributes - normalize to avoid writing to instance descriptor
if (js_object->IsJSGlobalProxy()) {
// Since the result is a property, the prototype will exist so
@@ -4892,6 +4892,7 @@
// Check whether debugger and is about to step into the callback that is passed
// to a built-in function such as Array.forEach.
RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugCallbackSupportsStepping) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (!isolate->IsDebuggerActive()) return isolate->heap()->false_value();
CONVERT_ARG_CHECKED(Object, callback, 0);
// We do not step into the callback if it's a builtin or not even a function.
@@ -4899,14 +4900,18 @@
return isolate->heap()->false_value();
}
return isolate->heap()->true_value();
+#else
+ return isolate->heap()->false_value();
+#endif // ENABLE_DEBUGGER_SUPPORT
}
// Set one shot breakpoints for the callback function that is passed to a
// built-in function such as Array.forEach to enable stepping into the callback.
RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debug* debug = isolate->debug();
- if (!debug->IsStepping()) return NULL;
+ if (!debug->IsStepping()) return isolate->heap()->undefined_value();
CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 0);
HandleScope scope(isolate);
// When leaving the callback, step out has been activated, but not performed
@@ -4914,7 +4919,8 @@
// again, we need to clear the step out at this point.
debug->ClearStepOut();
debug->FloodWithOneShot(callback);
- return NULL;
+#endif // ENABLE_DEBUGGER_SUPPORT
+ return isolate->heap()->undefined_value();
}
@@ -10338,8 +10344,7 @@
}
}
case INTERCEPTOR:
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
+ case TRANSITION:
return heap->undefined_value();
case HANDLER:
case NONEXISTENT:
@@ -10419,7 +10424,7 @@
// GC can happen later in this code so put the required fields into
// local variables using handles when required for later use.
Handle<Object> result_callback_obj;
- if (result.IsCallbacks()) {
+ if (result.IsPropertyCallbacks()) {
result_callback_obj = Handle<Object>(result.GetCallbackObject(),
isolate);
}
@@ -10437,7 +10442,7 @@
// If the callback object is a fixed array then it contains JavaScript
// getter and/or setter.
- bool hasJavaScriptAccessors = result.IsCallbacks() &&
+ bool hasJavaScriptAccessors = result.IsPropertyCallbacks() &&
result_callback_obj->IsAccessorPair();
Handle<FixedArray> details =
isolate->factory()->NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
diff --git a/src/scanner.cc b/src/scanner.cc
index f24af2e..bd2db58 100755
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -1077,6 +1077,7 @@
if (!ScanLiteralUnicodeEscape()) {
break;
}
+ Advance();
}
}
literal.Complete();
diff --git a/src/transitions-inl.h b/src/transitions-inl.h
new file mode 100644
index 0000000..9061883
--- /dev/null
+++ b/src/transitions-inl.h
@@ -0,0 +1,182 @@
+// Copyright 2012 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_TRANSITIONS_INL_H_
+#define V8_TRANSITIONS_INL_H_
+
+#include "objects-inl.h"
+#include "transitions.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, value, mode) \
+ if (mode == UPDATE_WRITE_BARRIER) { \
+ heap->incremental_marking()->RecordWrite( \
+ object, HeapObject::RawField(object, offset), value); \
+ if (heap->InNewSpace(value)) { \
+ heap->RecordWrite(object->address(), offset); \
+ } \
+ }
+
+
+TransitionArray* TransitionArray::cast(Object* object) {
+ ASSERT(object->IsTransitionArray());
+ return reinterpret_cast<TransitionArray*>(object);
+}
+
+
+Map* TransitionArray::elements_transition() {
+ Object* transition_map = get(kElementsTransitionIndex);
+ return Map::cast(transition_map);
+}
+
+
+void TransitionArray::ClearElementsTransition() {
+ WRITE_FIELD(this, kElementsTransitionOffset, Smi::FromInt(0));
+}
+
+
+bool TransitionArray::HasElementsTransition() {
+ return get(kElementsTransitionIndex) != Smi::FromInt(0);
+}
+
+
+void TransitionArray::set_elements_transition(Map* transition_map,
+ WriteBarrierMode mode) {
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kElementsTransitionOffset, transition_map);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kElementsTransitionOffset, transition_map, mode);
+}
+
+
+Object** TransitionArray::GetKeySlot(int transition_number) {
+ ASSERT(transition_number < number_of_transitions());
+ return HeapObject::RawField(
+ reinterpret_cast<HeapObject*>(this),
+ OffsetOfElementAt(ToKeyIndex(transition_number)));
+}
+
+
+String* TransitionArray::GetKey(int transition_number) {
+ ASSERT(transition_number < number_of_transitions());
+ return String::cast(get(ToKeyIndex(transition_number)));
+}
+
+
+void TransitionArray::SetKey(int transition_number, String* key) {
+ ASSERT(transition_number < number_of_transitions());
+ set(ToKeyIndex(transition_number), key);
+}
+
+
+Object* TransitionArray::GetValue(int transition_number) {
+ ASSERT(transition_number < number_of_transitions());
+ return get(ToValueIndex(transition_number));
+}
+
+
+Object** TransitionArray::GetValueSlot(int transition_number) {
+ ASSERT(transition_number < number_of_transitions());
+ return HeapObject::RawField(
+ reinterpret_cast<HeapObject*>(this),
+ OffsetOfElementAt(ToValueIndex(transition_number)));
+}
+
+
+void TransitionArray::SetValue(int transition_number, Object* value) {
+ ASSERT(transition_number < number_of_transitions());
+ set(ToValueIndex(transition_number), value);
+}
+
+
+Map* TransitionArray::GetTargetMap(int transition_number) {
+ Object* value = GetValue(transition_number);
+ if (value->IsAccessorPair()) {
+ // TODO(verwaest): Currently details are always taken from the getter if it
+ // is a transition, otherwise from the setter which in that case has to be a
+ // transition. Details should be dependent on which component is requested.
+ AccessorPair* accessors = AccessorPair::cast(value);
+ if (accessors->getter()->IsMap()) {
+ return Map::cast(accessors->getter());
+ }
+ return Map::cast(accessors->setter());
+ }
+ return Map::cast(value);
+}
+
+
+PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
+ Map* map = GetTargetMap(transition_number);
+ DescriptorArray* descriptors = map->instance_descriptors();
+ String* key = GetKey(transition_number);
+ int descriptor = descriptors->SearchWithCache(key);
+ ASSERT(descriptor != DescriptorArray::kNotFound);
+ return descriptors->GetDetails(descriptor);
+}
+
+
+Object** TransitionArray::GetElementsSlot() {
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kElementsTransitionOffset);
+}
+
+
+int TransitionArray::Search(String* name) {
+ return internal::Search(this, name);
+}
+
+
+void TransitionArray::Set(int transition_number,
+ String* key,
+ Object* value,
+ const WhitenessWitness&) {
+ NoIncrementalWriteBarrierSet(this,
+ ToKeyIndex(transition_number),
+ key);
+ NoIncrementalWriteBarrierSet(this,
+ ToValueIndex(transition_number),
+ value);
+}
+
+
+#undef FIELD_ADDR
+#undef WRITE_FIELD
+#undef CONDITIONAL_WRITE_BARRIER
+
+
+} } // namespace v8::internal
+
+#endif // V8_TRANSITIONS_INL_H_
diff --git a/src/transitions.cc b/src/transitions.cc
new file mode 100644
index 0000000..033f224
--- /dev/null
+++ b/src/transitions.cc
@@ -0,0 +1,127 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "objects.h"
+#include "transitions-inl.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+MaybeObject* TransitionArray::Allocate(int number_of_transitions) {
+ Heap* heap = Isolate::Current()->heap();
+ // Use FixedArray to not use DescriptorArray::cast on incomplete object.
+ FixedArray* array;
+ { MaybeObject* maybe_array =
+ heap->AllocateFixedArray(ToKeyIndex(number_of_transitions));
+ if (!maybe_array->To(&array)) return maybe_array;
+ }
+
+ array->set(kElementsTransitionIndex, Smi::FromInt(0));
+ return array;
+}
+
+
+void TransitionArray::CopyFrom(TransitionArray* origin,
+ int origin_transition,
+ int target_transition,
+ const WhitenessWitness& witness) {
+ this->Set(target_transition,
+ origin->GetKey(origin_transition),
+ origin->GetValue(origin_transition),
+ witness);
+}
+
+
+static bool InsertionPointFound(String* key1, String* key2) {
+ return key1->Hash() > key2->Hash();
+}
+
+
+MaybeObject* TransitionArray::NewWith(String* name, Object* value) {
+ TransitionArray* result;
+
+ { MaybeObject* maybe_array;
+ maybe_array = TransitionArray::Allocate(1);
+ if (!maybe_array->To(&result)) return maybe_array;
+ }
+
+ FixedArray::WhitenessWitness witness(result);
+
+ result->Set(0, name, value, witness);
+ return result;
+}
+
+
+MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
+ TransitionArray* result;
+
+ int number_of_transitions = this->number_of_transitions();
+ int new_size = number_of_transitions;
+
+ int insertion_index = this->Search(name);
+ if (insertion_index == kNotFound) ++new_size;
+
+ { MaybeObject* maybe_array;
+ maybe_array = TransitionArray::Allocate(new_size);
+ if (!maybe_array->To(&result)) return maybe_array;
+ }
+
+ if (HasElementsTransition()) {
+ result->set_elements_transition(elements_transition());
+ }
+
+ FixedArray::WhitenessWitness witness(result);
+
+ if (insertion_index != kNotFound) {
+ for (int i = 0; i < number_of_transitions; ++i) {
+ if (i != insertion_index) result->CopyFrom(this, i, i, witness);
+ }
+ result->Set(insertion_index, name, value, witness);
+ return result;
+ }
+
+ insertion_index = 0;
+ for (; insertion_index < number_of_transitions; ++insertion_index) {
+ if (InsertionPointFound(GetKey(insertion_index), name)) break;
+ result->CopyFrom(this, insertion_index, insertion_index, witness);
+ }
+
+ result->Set(insertion_index, name, value, witness);
+
+ for (; insertion_index < number_of_transitions; ++insertion_index) {
+ result->CopyFrom(this, insertion_index, insertion_index + 1, witness);
+ }
+
+ return result;
+}
+
+
+} } // namespace v8::internal
diff --git a/src/transitions.h b/src/transitions.h
new file mode 100644
index 0000000..5abdf99
--- /dev/null
+++ b/src/transitions.h
@@ -0,0 +1,156 @@
+// Copyright 2012 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_TRANSITIONS_H_
+#define V8_TRANSITIONS_H_
+
+#include "elements-kind.h"
+#include "heap.h"
+#include "isolate.h"
+#include "objects.h"
+#include "v8checks.h"
+
+namespace v8 {
+namespace internal {
+
+
+// TransitionArrays are fixed arrays used to hold map transitions for property,
+// constant, and element changes.
+// The format of the these objects is:
+// [0] Elements transition
+// [1] First transition
+// [length() - kTransitionSize] Last transition
+class TransitionArray: public FixedArray {
+ public:
+ inline Map* elements_transition();
+ inline void set_elements_transition(
+ Map* value,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline void ClearElementsTransition();
+ inline bool HasElementsTransition();
+ // Accessors for fetching instance transition at transition number.
+ inline String* GetKey(int transition_number);
+ inline Object** GetKeySlot(int transition_number);
+ inline void SetKey(int transition_number, String* value);
+ inline Object* GetValue(int transition_number);
+ inline Object** GetValueSlot(int transition_number);
+ inline void SetValue(int transition_number, Object* value);
+ inline Map* GetTargetMap(int transition_number);
+ inline PropertyDetails GetTargetDetails(int transition_number);
+ inline Object** GetElementsSlot();
+
+ // Returns the number of transitions in the array.
+ int number_of_transitions() {
+ ASSERT(length() >= kFirstIndex);
+ int len = length();
+ return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kTransitionSize;
+ }
+
+ inline int number_of_entries() { return number_of_transitions(); }
+
+ // Allocate a new transition array with a single entry.
+ static MUST_USE_RESULT MaybeObject* NewWith(String* name, Object* map);
+
+ // Copy the transition array, inserting a new transition.
+ // TODO(verwaest): This should not cause an existing transition to be
+ // overwritten.
+ MUST_USE_RESULT MaybeObject* CopyInsert(String* name, Object* map);
+
+ // Copy a single transition from the origin array.
+ inline void CopyFrom(TransitionArray* origin,
+ int origin_transition,
+ int target_transition,
+ const WhitenessWitness& witness);
+
+ // Search a transition for a given property name.
+ inline int Search(String* name);
+
+ // Allocates a TransitionArray.
+ MUST_USE_RESULT static MaybeObject* Allocate(int number_of_transitions);
+
+ // Casting.
+ static inline TransitionArray* cast(Object* obj);
+
+ // Constant for denoting key was not found.
+ static const int kNotFound = -1;
+
+ static const int kElementsTransitionIndex = 0;
+ static const int kFirstIndex = 1;
+
+ // Layout transition array header.
+ static const int kElementsTransitionOffset = FixedArray::kHeaderSize;
+ static const int kFirstOffset = kElementsTransitionOffset + kPointerSize;
+
+ // Layout of map transition.
+ static const int kTransitionKey = 0;
+ static const int kTransitionValue = 1;
+ static const int kTransitionSize = 2;
+
+#ifdef OBJECT_PRINT
+ // Print all the transitions.
+ inline void PrintTransitions() {
+ PrintTransitions(stdout);
+ }
+ void PrintTransitions(FILE* out);
+#endif
+
+#ifdef DEBUG
+ bool IsSortedNoDuplicates();
+ bool IsConsistentWithBackPointers(Map* current_map);
+ bool IsEqualTo(TransitionArray* other);
+#endif
+
+ // The maximum number of transitions we want in a transition array (should
+ // fit in a page).
+ static const int kMaxNumberOfTransitions = 1024 + 512;
+
+ private:
+ // Conversion from transition number to array indices.
+ static int ToKeyIndex(int transition_number) {
+ return kFirstIndex +
+ (transition_number * kTransitionSize) +
+ kTransitionKey;
+ }
+
+ static int ToValueIndex(int transition_number) {
+ return kFirstIndex +
+ (transition_number * kTransitionSize) +
+ kTransitionValue;
+ }
+
+ inline void Set(int transition_number,
+ String* key,
+ Object* value,
+ const WhitenessWitness&);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_TRANSITIONS_H_
diff --git a/src/v8globals.h b/src/v8globals.h
index 6a1766a..a295f8b 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -126,6 +126,7 @@
class DebugInfo;
class Descriptor;
class DescriptorArray;
+class TransitionArray;
class ExternalReference;
class FixedArray;
class FunctionTemplateInfo;
@@ -311,14 +312,6 @@
StoreBufferEvent event);
-// Whether to remove map transitions and constant transitions from a
-// DescriptorArray.
-enum TransitionFlag {
- REMOVE_TRANSITIONS,
- KEEP_TRANSITIONS
-};
-
-
// Union used for fast testing of specific double values.
union DoubleRepresentation {
double value;
diff --git a/src/version.cc b/src/version.cc
index fa7a163..5aae850 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 12
-#define BUILD_NUMBER 8
+#define BUILD_NUMBER 9
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index a19fcec..6bec93a 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -2284,7 +2284,7 @@
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
- type->LookupInDescriptors(NULL, *name, &lookup);
+ type->LookupDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
@@ -2328,9 +2328,9 @@
Handle<Map> map = list->at(i);
// If the map has ElementsKind transitions, we will generate map checks
// for each kind in __ CompareMap(..., ALLOW_ELEMENTS_TRANSITION_MAPS).
- if (map->elements_transition_map() != NULL) return false;
+ if (map->HasElementsTransition()) return false;
LookupResult lookup(isolate);
- map->LookupInDescriptors(NULL, *name, &lookup);
+ map->LookupDescriptor(NULL, *name, &lookup);
return lookup.IsField() || lookup.IsConstantFunction();
}