blob: c5e954554ca88cf6f76f2a06e03be2710b52a383 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_PROTOTYPE_H_
6#define V8_PROTOTYPE_H_
7
8#include "src/isolate.h"
9#include "src/objects.h"
10
11namespace v8 {
12namespace internal {
13
14/**
15 * A class to uniformly access the prototype of any Object and walk its
16 * prototype chain.
17 *
18 * The PrototypeIterator can either start at the prototype (default), or
19 * include the receiver itself. If a PrototypeIterator is constructed for a
20 * Map, it will always start at the prototype.
21 *
22 * The PrototypeIterator can either run to the null_value(), the first
23 * non-hidden prototype, or a given object.
24 */
Ben Murdoch097c5b22016-05-18 11:27:45 +010025
Ben Murdochb8a8cc12014-11-26 15:28:44 +000026class PrototypeIterator {
27 public:
28 enum WhereToStart { START_AT_RECEIVER, START_AT_PROTOTYPE };
29
30 enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN };
31
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000032 const int kProxyPrototypeLimit = 100 * 1000;
33
Ben Murdoch097c5b22016-05-18 11:27:45 +010034 PrototypeIterator(Isolate* isolate, Handle<JSReceiver> receiver,
35 WhereToStart where_to_start = START_AT_PROTOTYPE,
36 WhereToEnd where_to_end = END_AT_NULL)
37 : object_(NULL),
Ben Murdochb8a8cc12014-11-26 15:28:44 +000038 handle_(receiver),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000039 isolate_(isolate),
Ben Murdoch097c5b22016-05-18 11:27:45 +010040 where_to_end_(where_to_end),
41 is_at_end_(false),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000042 seen_proxies_(0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000043 CHECK(!handle_.is_null());
Ben Murdoch097c5b22016-05-18 11:27:45 +010044 if (where_to_start == START_AT_PROTOTYPE) Advance();
Ben Murdochb8a8cc12014-11-26 15:28:44 +000045 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000046
Ben Murdoch097c5b22016-05-18 11:27:45 +010047 PrototypeIterator(Isolate* isolate, JSReceiver* receiver,
48 WhereToStart where_to_start = START_AT_PROTOTYPE,
49 WhereToEnd where_to_end = END_AT_NULL)
50 : object_(receiver),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000051 isolate_(isolate),
Ben Murdoch097c5b22016-05-18 11:27:45 +010052 where_to_end_(where_to_end),
53 is_at_end_(false),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000054 seen_proxies_(0) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010055 if (where_to_start == START_AT_PROTOTYPE) Advance();
Ben Murdochb8a8cc12014-11-26 15:28:44 +000056 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000057
Ben Murdochb8a8cc12014-11-26 15:28:44 +000058 explicit PrototypeIterator(Map* receiver_map)
Ben Murdoch097c5b22016-05-18 11:27:45 +010059 : object_(receiver_map->prototype()),
60 isolate_(receiver_map->GetIsolate()),
61 where_to_end_(END_AT_NULL),
62 is_at_end_(object_->IsNull()) {}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000063
Ben Murdochb8a8cc12014-11-26 15:28:44 +000064 explicit PrototypeIterator(Handle<Map> receiver_map)
Ben Murdoch097c5b22016-05-18 11:27:45 +010065 : object_(NULL),
Ben Murdochb8a8cc12014-11-26 15:28:44 +000066 handle_(handle(receiver_map->prototype(), receiver_map->GetIsolate())),
Ben Murdoch097c5b22016-05-18 11:27:45 +010067 isolate_(receiver_map->GetIsolate()),
68 where_to_end_(END_AT_NULL),
69 is_at_end_(handle_->IsNull()) {}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000070
Ben Murdochb8a8cc12014-11-26 15:28:44 +000071 ~PrototypeIterator() {}
72
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000073 bool HasAccess() const {
74 // We can only perform access check in the handlified version of the
75 // PrototypeIterator.
76 DCHECK(!handle_.is_null());
77 if (handle_->IsAccessCheckNeeded()) {
78 return isolate_->MayAccess(handle(isolate_->context()),
79 Handle<JSObject>::cast(handle_));
80 }
81 return true;
82 }
83
84 template <typename T = Object>
85 T* GetCurrent() const {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000086 DCHECK(handle_.is_null());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000087 return T::cast(object_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000088 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000089
90 template <typename T = Object>
91 static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000092 DCHECK(!iterator.handle_.is_null());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000093 DCHECK(iterator.object_ == NULL);
94 return Handle<T>::cast(iterator.handle_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000095 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000096
Ben Murdochb8a8cc12014-11-26 15:28:44 +000097 void Advance() {
98 if (handle_.is_null() && object_->IsJSProxy()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010099 is_at_end_ = true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000100 object_ = isolate_->heap()->null_value();
101 return;
102 } else if (!handle_.is_null() && handle_->IsJSProxy()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100103 is_at_end_ = true;
104 handle_ = isolate_->factory()->null_value();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000105 return;
106 }
107 AdvanceIgnoringProxies();
108 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000110 void AdvanceIgnoringProxies() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100111 Object* object = handle_.is_null() ? object_ : *handle_;
112 Map* map = HeapObject::cast(object)->map();
113
114 Object* prototype = map->prototype();
115 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN
116 ? !map->has_hidden_prototype()
117 : prototype->IsNull();
118
119 if (handle_.is_null()) {
120 object_ = prototype;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000121 } else {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100122 handle_ = handle(prototype, isolate_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000123 }
124 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000125
126 // Returns false iff a call to JSProxy::GetPrototype throws.
127 // TODO(neis): This should probably replace Advance().
128 bool AdvanceFollowingProxies() {
129 DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
130 if (!HasAccess()) {
131 // Abort the lookup if we do not have access to the current object.
132 handle_ = isolate_->factory()->null_value();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100133 is_at_end_ = true;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000134 return true;
135 }
136 if (handle_.is_null() || !handle_->IsJSProxy()) {
137 AdvanceIgnoringProxies();
138 return true;
139 }
140 // Due to possible __proto__ recursion limit the number of Proxies
141 // we visit to an arbitrarily chosen large number.
142 seen_proxies_++;
143 if (seen_proxies_ > kProxyPrototypeLimit) {
144 isolate_->Throw(
145 *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow));
146 return false;
147 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000148 MaybeHandle<Object> proto =
149 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
Ben Murdoch097c5b22016-05-18 11:27:45 +0100150 if (!proto.ToHandle(&handle_)) return false;
151 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull();
152 return true;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000153 }
154
Ben Murdoch097c5b22016-05-18 11:27:45 +0100155 bool IsAtEnd() const { return is_at_end_; }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000156
157 private:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000158 Object* object_;
159 Handle<Object> handle_;
160 Isolate* isolate_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100161 WhereToEnd where_to_end_;
162 bool is_at_end_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000163 int seen_proxies_;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000164
165 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
166};
167
168
169} // namespace internal
170
171} // namespace v8
172
173#endif // V8_PROTOTYPE_H_