]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
1 | /* |
2 | * Copyright (C) 2012 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "config.h" | |
27 | #include "PutByIdStatus.h" | |
28 | ||
29 | #include "CodeBlock.h" | |
93a37866 | 30 | #include "LLIntData.h" |
6fe7ccc8 | 31 | #include "LowLevelInterpreter.h" |
93a37866 | 32 | #include "Operations.h" |
6fe7ccc8 A |
33 | #include "Structure.h" |
34 | #include "StructureChain.h" | |
35 | ||
36 | namespace JSC { | |
37 | ||
38 | PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, Identifier& ident) | |
39 | { | |
40 | UNUSED_PARAM(profiledBlock); | |
41 | UNUSED_PARAM(bytecodeIndex); | |
42 | UNUSED_PARAM(ident); | |
43 | #if ENABLE(LLINT) | |
44 | Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; | |
45 | ||
46 | Structure* structure = instruction[4].u.structure.get(); | |
47 | if (!structure) | |
93a37866 | 48 | return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); |
6fe7ccc8 | 49 | |
93a37866 A |
50 | if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id) |
51 | || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_out_of_line)) { | |
52 | PropertyOffset offset = structure->get(*profiledBlock->vm(), ident); | |
53 | if (!isValidOffset(offset)) | |
54 | return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); | |
6fe7ccc8 A |
55 | |
56 | return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); | |
57 | } | |
58 | ||
93a37866 A |
59 | ASSERT(structure->transitionWatchpointSetHasBeenInvalidated()); |
60 | ||
61 | ASSERT(instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct) | |
62 | || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal) | |
63 | || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct_out_of_line) | |
64 | || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal_out_of_line)); | |
6fe7ccc8 A |
65 | |
66 | Structure* newStructure = instruction[6].u.structure.get(); | |
67 | StructureChain* chain = instruction[7].u.structureChain.get(); | |
68 | ASSERT(newStructure); | |
69 | ASSERT(chain); | |
70 | ||
93a37866 A |
71 | PropertyOffset offset = newStructure->get(*profiledBlock->vm(), ident); |
72 | if (!isValidOffset(offset)) | |
73 | return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); | |
6fe7ccc8 A |
74 | |
75 | return PutByIdStatus(SimpleTransition, structure, newStructure, chain, offset); | |
76 | #else | |
93a37866 | 77 | return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
78 | #endif |
79 | } | |
80 | ||
81 | PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytecodeIndex, Identifier& ident) | |
82 | { | |
83 | UNUSED_PARAM(profiledBlock); | |
84 | UNUSED_PARAM(bytecodeIndex); | |
85 | UNUSED_PARAM(ident); | |
86 | #if ENABLE(JIT) && ENABLE(VALUE_PROFILER) | |
87 | if (!profiledBlock->numberOfStructureStubInfos()) | |
88 | return computeFromLLInt(profiledBlock, bytecodeIndex, ident); | |
89 | ||
90 | if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)) | |
93a37866 | 91 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
92 | |
93 | StructureStubInfo& stubInfo = profiledBlock->getStubInfo(bytecodeIndex); | |
94 | if (!stubInfo.seen) | |
95 | return computeFromLLInt(profiledBlock, bytecodeIndex, ident); | |
96 | ||
93a37866 A |
97 | if (stubInfo.resetByGC) |
98 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); | |
99 | ||
6fe7ccc8 A |
100 | switch (stubInfo.accessType) { |
101 | case access_unset: | |
93a37866 A |
102 | // If the JIT saw it but didn't optimize it, then assume that this takes slow path. |
103 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); | |
6fe7ccc8 A |
104 | |
105 | case access_put_by_id_replace: { | |
93a37866 A |
106 | PropertyOffset offset = stubInfo.u.putByIdReplace.baseObjectStructure->get( |
107 | *profiledBlock->vm(), ident); | |
108 | if (isValidOffset(offset)) { | |
6fe7ccc8 A |
109 | return PutByIdStatus( |
110 | SimpleReplace, | |
111 | stubInfo.u.putByIdReplace.baseObjectStructure.get(), | |
112 | 0, 0, | |
113 | offset); | |
114 | } | |
93a37866 | 115 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
116 | } |
117 | ||
118 | case access_put_by_id_transition_normal: | |
119 | case access_put_by_id_transition_direct: { | |
93a37866 A |
120 | ASSERT(stubInfo.u.putByIdTransition.previousStructure->transitionWatchpointSetHasBeenInvalidated()); |
121 | PropertyOffset offset = stubInfo.u.putByIdTransition.structure->get( | |
122 | *profiledBlock->vm(), ident); | |
123 | if (isValidOffset(offset)) { | |
6fe7ccc8 A |
124 | return PutByIdStatus( |
125 | SimpleTransition, | |
126 | stubInfo.u.putByIdTransition.previousStructure.get(), | |
127 | stubInfo.u.putByIdTransition.structure.get(), | |
128 | stubInfo.u.putByIdTransition.chain.get(), | |
129 | offset); | |
130 | } | |
93a37866 | 131 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
132 | } |
133 | ||
134 | default: | |
93a37866 | 135 | return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
136 | } |
137 | #else // ENABLE(JIT) | |
93a37866 | 138 | return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); |
6fe7ccc8 A |
139 | #endif // ENABLE(JIT) |
140 | } | |
141 | ||
93a37866 A |
142 | PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, Structure* structure, Identifier& ident, bool isDirect) |
143 | { | |
144 | if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex) | |
145 | return PutByIdStatus(TakesSlowPath); | |
146 | ||
147 | if (structure->typeInfo().overridesGetOwnPropertySlot()) | |
148 | return PutByIdStatus(TakesSlowPath); | |
149 | ||
150 | if (!structure->propertyAccessesAreCacheable()) | |
151 | return PutByIdStatus(TakesSlowPath); | |
152 | ||
153 | unsigned attributes; | |
154 | JSCell* specificValue; | |
155 | PropertyOffset offset = structure->get(vm, ident, attributes, specificValue); | |
156 | if (isValidOffset(offset)) { | |
157 | if (attributes & (Accessor | ReadOnly)) | |
158 | return PutByIdStatus(TakesSlowPath); | |
159 | if (specificValue) { | |
160 | // We need the PutById slow path to verify that we're storing the right value into | |
161 | // the specialized slot. | |
162 | return PutByIdStatus(TakesSlowPath); | |
163 | } | |
164 | return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); | |
165 | } | |
166 | ||
167 | // Our hypothesis is that we're doing a transition. Before we prove that this is really | |
168 | // true, we want to do some sanity checks. | |
169 | ||
170 | // Don't cache put transitions on dictionaries. | |
171 | if (structure->isDictionary()) | |
172 | return PutByIdStatus(TakesSlowPath); | |
173 | ||
174 | // If the structure corresponds to something that isn't an object, then give up, since | |
175 | // we don't want to be adding properties to strings. | |
176 | if (structure->typeInfo().type() == StringType) | |
177 | return PutByIdStatus(TakesSlowPath); | |
178 | ||
179 | if (!isDirect) { | |
180 | // If the prototype chain has setters or read-only properties, then give up. | |
181 | if (structure->prototypeChainMayInterceptStoreTo(vm, ident)) | |
182 | return PutByIdStatus(TakesSlowPath); | |
183 | ||
184 | // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) | |
185 | // then give up. The dictionary case would only happen if this structure has not been | |
186 | // used in an optimized put_by_id transition. And really the only reason why we would | |
187 | // bail here is that I don't really feel like having the optimizing JIT go and flatten | |
188 | // dictionaries if we have evidence to suggest that those objects were never used as | |
189 | // prototypes in a cacheable prototype access - i.e. there's a good chance that some of | |
190 | // the other checks below will fail. | |
191 | if (!isPrototypeChainNormalized(globalObject, structure)) | |
192 | return PutByIdStatus(TakesSlowPath); | |
193 | } | |
194 | ||
195 | // We only optimize if there is already a structure that the transition is cached to. | |
196 | // Among other things, this allows us to guard against a transition with a specific | |
197 | // value. | |
198 | // | |
199 | // - If we're storing a value that could be specific: this would only be a problem if | |
200 | // the existing transition did have a specific value already, since if it didn't, | |
201 | // then we would behave "as if" we were not storing a specific value. If it did | |
202 | // have a specific value, then we'll know - the fact that we pass 0 for | |
203 | // specificValue will tell us. | |
204 | // | |
205 | // - If we're not storing a value that could be specific: again, this would only be a | |
206 | // problem if the existing transition did have a specific value, which we check for | |
207 | // by passing 0 for the specificValue. | |
208 | Structure* transition = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, 0, offset); | |
209 | if (!transition) | |
210 | return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above. | |
211 | ASSERT(!transition->transitionDidInvolveSpecificValue()); | |
212 | ASSERT(isValidOffset(offset)); | |
213 | ||
214 | return PutByIdStatus( | |
215 | SimpleTransition, structure, transition, | |
216 | structure->prototypeChain(vm, globalObject), offset); | |
217 | } | |
218 | ||
6fe7ccc8 A |
219 | } // namespace JSC |
220 |