1/*
2 * Copyright (C) 2017 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 "PolyProtoAccessChain.h"
28
29#include "JSCInlines.h"
30#include "JSObject.h"
31
32namespace JSC {
33
34std::unique_ptr<PolyProtoAccessChain> PolyProtoAccessChain::create(JSGlobalObject* globalObject, JSCell* base, const PropertySlot& slot, bool& usesPolyProto)
35{
36 JSObject* target = slot.isUnset() ? nullptr : slot.slotBase();
37 return create(globalObject, base, target, usesPolyProto);
38}
39
40std::unique_ptr<PolyProtoAccessChain> PolyProtoAccessChain::create(JSGlobalObject* globalObject, JSCell* base, JSObject* target, bool& usesPolyProto)
41{
42 JSCell* current = base;
43 VM& vm = *base->vm();
44
45 bool found = false;
46
47 usesPolyProto = false;
48
49 std::unique_ptr<PolyProtoAccessChain> result(new PolyProtoAccessChain());
50
51 for (unsigned iterationNumber = 0; true; ++iterationNumber) {
52 Structure* structure = current->structure(vm);
53
54 if (!structure->propertyAccessesAreCacheable())
55 return nullptr;
56
57 if (structure->isProxy())
58 return nullptr;
59
60 if (structure->isDictionary()) {
61 ASSERT(structure->isObject());
62 if (structure->hasBeenFlattenedBefore())
63 return nullptr;
64
65 structure->flattenDictionaryStructure(vm, asObject(current));
66 }
67
68 // To save memory, we don't include the base in the chain. We let
69 // AccessCase provide the base to us as needed.
70 if (iterationNumber)
71 result->m_chain.append(structure);
72 else
73 RELEASE_ASSERT(current == base);
74
75 if (current == target) {
76 found = true;
77 break;
78 }
79
80 // We only have poly proto if we need to access our prototype via
81 // the poly proto protocol. If the slot base is the only poly proto
82 // thing in the chain, and we have a cache hit on it, then we're not
83 // poly proto.
84 usesPolyProto |= structure->hasPolyProto();
85
86 JSValue prototype = structure->prototypeForLookup(globalObject, current);
87 if (prototype.isNull())
88 break;
89 current = asObject(prototype);
90 }
91
92 if (!found && !!target)
93 return nullptr;
94
95 return result;
96}
97
98bool PolyProtoAccessChain::needImpurePropertyWatchpoint() const
99{
100 for (Structure* structure : m_chain) {
101 if (structure->needImpurePropertyWatchpoint())
102 return true;
103 }
104 return false;
105}
106
107bool PolyProtoAccessChain::operator==(const PolyProtoAccessChain& other) const
108{
109 return m_chain == other.m_chain;
110}
111
112void PolyProtoAccessChain::dump(Structure* baseStructure, PrintStream& out) const
113{
114 out.print("PolyPolyProtoAccessChain: [\n");
115 forEach(baseStructure, [&] (Structure* structure, bool) {
116 out.print("\t");
117 structure->dump(out);
118 out.print("\n");
119 });
120}
121
122}
123