blob: e09ff0ff0cd28edc6d0c5d86c8d13c4e21fc708e [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().
Ben Murdochda12d292016-06-02 14:46:10 +0100128 MUST_USE_RESULT bool AdvanceFollowingProxies() {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000129 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 }
Ben Murdochda12d292016-06-02 14:46:10 +0100136 return AdvanceFollowingProxiesIgnoringAccessChecks();
137 }
138
139 MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000140 if (handle_.is_null() || !handle_->IsJSProxy()) {
141 AdvanceIgnoringProxies();
142 return true;
143 }
Ben Murdochda12d292016-06-02 14:46:10 +0100144
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000145 // Due to possible __proto__ recursion limit the number of Proxies
146 // we visit to an arbitrarily chosen large number.
147 seen_proxies_++;
148 if (seen_proxies_ > kProxyPrototypeLimit) {
149 isolate_->Throw(
150 *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow));
151 return false;
152 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000153 MaybeHandle<Object> proto =
154 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
Ben Murdoch097c5b22016-05-18 11:27:45 +0100155 if (!proto.ToHandle(&handle_)) return false;
156 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull();
157 return true;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000158 }
159
Ben Murdoch097c5b22016-05-18 11:27:45 +0100160 bool IsAtEnd() const { return is_at_end_; }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000161
162 private:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000163 Object* object_;
164 Handle<Object> handle_;
165 Isolate* isolate_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100166 WhereToEnd where_to_end_;
167 bool is_at_end_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000168 int seen_proxies_;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000169
170 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
171};
172
173
174} // namespace internal
175
176} // namespace v8
177
178#endif // V8_PROTOTYPE_H_