2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "PutByIdStatus.h"
29 #include "CodeBlock.h"
30 #include "LLIntData.h"
31 #include "LowLevelInterpreter.h"
32 #include "Operations.h"
33 #include "Structure.h"
34 #include "StructureChain.h"
38 PutByIdStatus
PutByIdStatus::computeFromLLInt(CodeBlock
* profiledBlock
, unsigned bytecodeIndex
, Identifier
& ident
)
40 UNUSED_PARAM(profiledBlock
);
41 UNUSED_PARAM(bytecodeIndex
);
44 Instruction
* instruction
= profiledBlock
->instructions().begin() + bytecodeIndex
;
46 Structure
* structure
= instruction
[4].u
.structure
.get();
48 return PutByIdStatus(NoInformation
, 0, 0, 0, invalidOffset
);
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
);
56 return PutByIdStatus(SimpleReplace
, structure
, 0, 0, offset
);
59 ASSERT(structure
->transitionWatchpointSetHasBeenInvalidated());
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
));
66 Structure
* newStructure
= instruction
[6].u
.structure
.get();
67 StructureChain
* chain
= instruction
[7].u
.structureChain
.get();
71 PropertyOffset offset
= newStructure
->get(*profiledBlock
->vm(), ident
);
72 if (!isValidOffset(offset
))
73 return PutByIdStatus(NoInformation
, 0, 0, 0, invalidOffset
);
75 return PutByIdStatus(SimpleTransition
, structure
, newStructure
, chain
, offset
);
77 return PutByIdStatus(NoInformation
, 0, 0, 0, invalidOffset
);
81 PutByIdStatus
PutByIdStatus::computeFor(CodeBlock
* profiledBlock
, unsigned bytecodeIndex
, Identifier
& ident
)
83 UNUSED_PARAM(profiledBlock
);
84 UNUSED_PARAM(bytecodeIndex
);
86 #if ENABLE(JIT) && ENABLE(VALUE_PROFILER)
87 if (!profiledBlock
->numberOfStructureStubInfos())
88 return computeFromLLInt(profiledBlock
, bytecodeIndex
, ident
);
90 if (profiledBlock
->likelyToTakeSlowCase(bytecodeIndex
))
91 return PutByIdStatus(TakesSlowPath
, 0, 0, 0, invalidOffset
);
93 StructureStubInfo
& stubInfo
= profiledBlock
->getStubInfo(bytecodeIndex
);
95 return computeFromLLInt(profiledBlock
, bytecodeIndex
, ident
);
97 if (stubInfo
.resetByGC
)
98 return PutByIdStatus(TakesSlowPath
, 0, 0, 0, invalidOffset
);
100 switch (stubInfo
.accessType
) {
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
);
105 case access_put_by_id_replace
: {
106 PropertyOffset offset
= stubInfo
.u
.putByIdReplace
.baseObjectStructure
->get(
107 *profiledBlock
->vm(), ident
);
108 if (isValidOffset(offset
)) {
109 return PutByIdStatus(
111 stubInfo
.u
.putByIdReplace
.baseObjectStructure
.get(),
115 return PutByIdStatus(TakesSlowPath
, 0, 0, 0, invalidOffset
);
118 case access_put_by_id_transition_normal
:
119 case access_put_by_id_transition_direct
: {
120 ASSERT(stubInfo
.u
.putByIdTransition
.previousStructure
->transitionWatchpointSetHasBeenInvalidated());
121 PropertyOffset offset
= stubInfo
.u
.putByIdTransition
.structure
->get(
122 *profiledBlock
->vm(), ident
);
123 if (isValidOffset(offset
)) {
124 return PutByIdStatus(
126 stubInfo
.u
.putByIdTransition
.previousStructure
.get(),
127 stubInfo
.u
.putByIdTransition
.structure
.get(),
128 stubInfo
.u
.putByIdTransition
.chain
.get(),
131 return PutByIdStatus(TakesSlowPath
, 0, 0, 0, invalidOffset
);
135 return PutByIdStatus(TakesSlowPath
, 0, 0, 0, invalidOffset
);
138 return PutByIdStatus(NoInformation
, 0, 0, 0, invalidOffset
);
139 #endif // ENABLE(JIT)
142 PutByIdStatus
PutByIdStatus::computeFor(VM
& vm
, JSGlobalObject
* globalObject
, Structure
* structure
, Identifier
& ident
, bool isDirect
)
144 if (PropertyName(ident
).asIndex() != PropertyName::NotAnIndex
)
145 return PutByIdStatus(TakesSlowPath
);
147 if (structure
->typeInfo().overridesGetOwnPropertySlot())
148 return PutByIdStatus(TakesSlowPath
);
150 if (!structure
->propertyAccessesAreCacheable())
151 return PutByIdStatus(TakesSlowPath
);
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
);
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
);
164 return PutByIdStatus(SimpleReplace
, structure
, 0, 0, offset
);
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.
170 // Don't cache put transitions on dictionaries.
171 if (structure
->isDictionary())
172 return PutByIdStatus(TakesSlowPath
);
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
);
180 // If the prototype chain has setters or read-only properties, then give up.
181 if (structure
->prototypeChainMayInterceptStoreTo(vm
, ident
))
182 return PutByIdStatus(TakesSlowPath
);
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
);
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
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.
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
);
210 return PutByIdStatus(TakesSlowPath
); // This occurs in bizarre cases only. See above.
211 ASSERT(!transition
->transitionDidInvolveSpecificValue());
212 ASSERT(isValidOffset(offset
));
214 return PutByIdStatus(
215 SimpleTransition
, structure
, transition
,
216 structure
->prototypeChain(vm
, globalObject
), offset
);