OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 #include "src/builtins/builtins-forin.h" | |
6 | |
7 #include "src/builtins/builtins-utils.h" | |
8 #include "src/builtins/builtins.h" | |
9 #include "src/code-factory.h" | |
10 #include "src/code-stub-assembler.h" | |
11 #include "src/counters.h" | |
12 #include "src/keys.h" | |
13 #include "src/lookup.h" | |
14 #include "src/objects-inl.h" | |
15 #include "src/property-descriptor.h" | |
16 | |
17 namespace v8 { | |
18 namespace internal { | |
19 | |
20 typedef compiler::Node Node; | |
21 | |
22 Node* ForInBuiltinsAssembler::ForInFilter(Node* key, Node* object, | |
23 Node* context) { | |
24 Label return_undefined(this, Label::kDeferred), return_to_name(this), | |
25 end(this); | |
26 | |
27 Variable var_result(this, MachineRepresentation::kTagged); | |
28 | |
29 Node* has_property = | |
30 HasProperty(object, key, context, Runtime::kForInHasProperty); | |
31 | |
32 Branch(WordEqual(has_property, BooleanConstant(true)), &return_to_name, | |
33 &return_undefined); | |
34 | |
35 Bind(&return_to_name); | |
36 { | |
37 var_result.Bind(ToName(context, key)); | |
38 Goto(&end); | |
39 } | |
40 | |
41 Bind(&return_undefined); | |
42 { | |
43 var_result.Bind(UndefinedConstant()); | |
44 Goto(&end); | |
45 } | |
46 | |
47 Bind(&end); | |
48 return var_result.value(); | |
49 } | |
50 | |
51 std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare( | |
52 Node* object, Node* context, Label* call_runtime, | |
53 Label* nothing_to_iterate) { | |
54 Label use_cache(this); | |
55 CSA_ASSERT(this, IsJSReceiver(object)); | |
56 | |
57 CheckEnumCache(object, &use_cache, nothing_to_iterate, call_runtime); | |
58 | |
59 Bind(&use_cache); | |
60 Node* map = LoadMap(object); | |
61 Node* enum_length = EnumLength(map); | |
62 GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate); | |
63 Node* descriptors = LoadMapDescriptors(map); | |
64 Node* cache_offset = | |
65 LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset); | |
66 Node* enum_cache = LoadObjectField( | |
67 cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset); | |
68 | |
69 return std::make_tuple(map, enum_cache, enum_length); | |
70 } | |
71 | |
72 Node* ForInBuiltinsAssembler::EnumLength(Node* map) { | |
73 CSA_ASSERT(this, IsMap(map)); | |
74 Node* bitfield_3 = LoadMapBitField3(map); | |
75 Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3); | |
76 return SmiTag(enum_length); | |
77 } | |
78 | |
79 void ForInBuiltinsAssembler::CheckPrototypeEnumCache(Node* receiver, Node* map, | |
80 Label* use_cache, | |
81 Label* use_runtime) { | |
82 Variable current_js_object(this, MachineRepresentation::kTagged, receiver); | |
83 Variable current_map(this, MachineRepresentation::kTagged, map); | |
84 | |
85 // These variables are updated in the loop below. | |
86 Variable* loop_vars[2] = {¤t_js_object, ¤t_map}; | |
87 Label loop(this, 2, loop_vars), next(this); | |
88 | |
89 Goto(&loop); | |
90 // Check that there are no elements. |current_js_object| contains | |
91 // the current JS object we've reached through the prototype chain. | |
92 Bind(&loop); | |
93 { | |
94 Label if_elements(this), if_no_elements(this); | |
95 Node* elements = LoadElements(current_js_object.value()); | |
96 Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); | |
97 // Check that there are no elements. | |
98 Branch(WordEqual(elements, empty_fixed_array), &if_no_elements, | |
99 &if_elements); | |
100 Bind(&if_elements); | |
101 { | |
102 // Second chance, the object may be using the empty slow element | |
103 // dictionary. | |
104 Node* slow_empty_dictionary = | |
105 LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex); | |
106 Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime, | |
107 &if_no_elements); | |
108 } | |
109 | |
110 Bind(&if_no_elements); | |
111 { | |
112 // Update map prototype. | |
113 current_js_object.Bind(LoadMapPrototype(current_map.value())); | |
114 Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache, | |
115 &next); | |
116 } | |
117 } | |
118 | |
119 Bind(&next); | |
120 { | |
121 // For all objects but the receiver, check that the cache is empty. | |
122 current_map.Bind(LoadMap(current_js_object.value())); | |
123 Node* enum_length = EnumLength(current_map.value()); | |
124 Node* zero_constant = SmiConstant(Smi::kZero); | |
125 Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime); | |
126 } | |
127 } | |
128 | |
129 void ForInBuiltinsAssembler::CheckEnumCache(Node* receiver, Label* use_cache, | |
130 Label* nothing_to_iterate, | |
131 Label* use_runtime) { | |
132 Node* map = LoadMap(receiver); | |
133 | |
134 Label check_empty_prototype(this), | |
135 check_dict_receiver(this, Label::kDeferred); | |
136 | |
137 // Check if the enum length field is properly initialized, indicating that | |
138 // there is an enum cache. | |
139 { | |
140 Node* invalid_enum_cache_sentinel = | |
141 SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel)); | |
142 Node* enum_length = EnumLength(map); | |
143 Branch(WordEqual(enum_length, invalid_enum_cache_sentinel), | |
144 &check_dict_receiver, &check_empty_prototype); | |
145 } | |
146 | |
147 // Check that there are no elements on the fast |receiver| and its prototype | |
148 // chain. | |
149 Bind(&check_empty_prototype); | |
150 CheckPrototypeEnumCache(receiver, map, use_cache, use_runtime); | |
151 | |
152 Label dict_loop(this); | |
153 Bind(&check_dict_receiver); | |
154 { | |
155 // Avoid runtime-call for empty dictionary receivers. | |
156 GotoIfNot(IsDictionaryMap(map), use_runtime); | |
157 Node* properties = LoadProperties(receiver); | |
158 Node* length = LoadFixedArrayElement( | |
159 properties, NameDictionary::kNumberOfElementsIndex); | |
160 GotoIfNot(WordEqual(length, SmiConstant(0)), use_runtime); | |
161 // Check that there are no elements on the |receiver| and its prototype | |
162 // chain. Given that we do not create an EnumCache for dict-mode objects, | |
163 // directly jump to |nothing_to_iterate| if there are no elements and no | |
164 // properties on the |receiver|. | |
165 CheckPrototypeEnumCache(receiver, map, nothing_to_iterate, use_runtime); | |
166 } | |
167 } | |
168 | |
169 TF_BUILTIN(ForInFilter, ForInBuiltinsAssembler) { | |
170 typedef ForInFilterDescriptor Descriptor; | |
171 | |
172 Node* key = Parameter(Descriptor::kKey); | |
173 Node* object = Parameter(Descriptor::kObject); | |
174 Node* context = Parameter(Descriptor::kContext); | |
175 | |
176 Return(ForInFilter(key, object, context)); | |
177 } | |
178 | |
179 TF_BUILTIN(ForInNext, ForInBuiltinsAssembler) { | |
180 typedef ForInNextDescriptor Descriptor; | |
181 | |
182 Label filter(this); | |
183 Node* object = Parameter(Descriptor::kObject); | |
184 Node* cache_array = Parameter(Descriptor::kCacheArray); | |
185 Node* cache_type = Parameter(Descriptor::kCacheType); | |
186 Node* index = Parameter(Descriptor::kIndex); | |
187 Node* context = Parameter(Descriptor::kContext); | |
188 | |
189 Node* key = LoadFixedArrayElement(cache_array, SmiUntag(index)); | |
190 Node* map = LoadMap(object); | |
191 GotoIfNot(WordEqual(map, cache_type), &filter); | |
192 Return(key); | |
193 Bind(&filter); | |
194 Return(ForInFilter(key, object, context)); | |
195 } | |
196 | |
197 TF_BUILTIN(ForInPrepare, ForInBuiltinsAssembler) { | |
198 typedef ForInPrepareDescriptor Descriptor; | |
199 | |
200 Label call_runtime(this), nothing_to_iterate(this); | |
201 Node* object = Parameter(Descriptor::kObject); | |
202 Node* context = Parameter(Descriptor::kContext); | |
203 | |
204 Node* cache_type; | |
205 Node* cache_array; | |
206 Node* cache_length; | |
207 std::tie(cache_type, cache_array, cache_length) = | |
208 EmitForInPrepare(object, context, &call_runtime, ¬hing_to_iterate); | |
209 | |
210 Return(cache_type, cache_array, cache_length); | |
211 | |
212 Bind(&call_runtime); | |
213 TailCallRuntime(Runtime::kForInPrepare, context, object); | |
214 | |
215 Bind(¬hing_to_iterate); | |
216 { | |
217 Node* zero = SmiConstant(0); | |
218 Return(zero, zero, zero); | |
219 } | |
220 } | |
221 } // namespace internal | |
222 } // namespace v8 | |
OLD | NEW |