]>
git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSScope.cpp
2 * Copyright (C) 2012-2015 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.
29 #include "JSGlobalObject.h"
30 #include "JSLexicalEnvironment.h"
31 #include "JSNameScope.h"
32 #include "JSWithScope.h"
33 #include "JSCInlines.h"
37 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSScope
);
39 void JSScope::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
41 JSScope
* thisObject
= jsCast
<JSScope
*>(cell
);
42 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
43 Base::visitChildren(thisObject
, visitor
);
44 visitor
.append(&thisObject
->m_next
);
47 // Returns true if we found enough information to terminate optimization.
48 static inline bool abstractAccess(ExecState
* exec
, JSScope
* scope
, const Identifier
& ident
, GetOrPut getOrPut
, size_t depth
, bool& needsVarInjectionChecks
, ResolveOp
& op
)
50 if (JSLexicalEnvironment
* lexicalEnvironment
= jsDynamicCast
<JSLexicalEnvironment
*>(scope
)) {
51 if (ident
== exec
->propertyNames().arguments
) {
52 // We know the property will be at this lexical environment scope, but we don't know how to cache it.
53 op
= ResolveOp(Dynamic
, 0, 0, 0, 0, 0);
57 SymbolTableEntry entry
= lexicalEnvironment
->symbolTable()->get(ident
.impl());
58 if (entry
.isReadOnly() && getOrPut
== Put
) {
59 // We know the property will be at this lexical environment scope, but we don't know how to cache it.
60 op
= ResolveOp(Dynamic
, 0, 0, 0, 0, 0);
64 if (!entry
.isNull()) {
65 op
= ResolveOp(makeType(ClosureVar
, needsVarInjectionChecks
), depth
, 0, lexicalEnvironment
, entry
.watchpointSet(), entry
.scopeOffset().offset());
69 if (lexicalEnvironment
->symbolTable()->usesNonStrictEval())
70 needsVarInjectionChecks
= true;
74 if (JSGlobalObject
* globalObject
= jsDynamicCast
<JSGlobalObject
*>(scope
)) {
75 SymbolTableEntry entry
= globalObject
->symbolTable()->get(ident
.impl());
76 if (!entry
.isNull()) {
77 if (getOrPut
== Put
&& entry
.isReadOnly()) {
78 // We know the property will be at global scope, but we don't know how to cache it.
79 op
= ResolveOp(Dynamic
, 0, 0, 0, 0, 0);
84 makeType(GlobalVar
, needsVarInjectionChecks
), depth
, 0, 0, entry
.watchpointSet(),
85 reinterpret_cast<uintptr_t>(globalObject
->variableAt(entry
.scopeOffset()).slot()));
89 PropertySlot
slot(globalObject
);
90 if (!globalObject
->getOwnPropertySlot(globalObject
, exec
, ident
, slot
)
91 || !slot
.isCacheableValue()
92 || !globalObject
->structure()->propertyAccessesAreCacheable()
93 || (globalObject
->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && getOrPut
== Put
)) {
94 // We know the property will be at global scope, but we don't know how to cache it.
95 ASSERT(!scope
->next());
96 op
= ResolveOp(makeType(GlobalProperty
, needsVarInjectionChecks
), depth
, 0, 0, 0, 0);
100 WatchpointState state
= globalObject
->structure()->ensurePropertyReplacementWatchpointSet(exec
->vm(), slot
.cachedOffset())->state();
101 if (state
== IsWatched
&& getOrPut
== Put
) {
102 // The field exists, but because the replacement watchpoint is still intact. This is
103 // kind of dangerous. We have two options:
104 // 1) Invalidate the watchpoint set. That would work, but it's possible that this code
105 // path never executes - in which case this would be unwise.
106 // 2) Have the invalidation happen at run-time. All we have to do is leave the code
107 // uncached. The only downside is slightly more work when this does execute.
108 // We go with option (2) here because it seems less evil.
109 op
= ResolveOp(makeType(GlobalProperty
, needsVarInjectionChecks
), depth
, 0, 0, 0, 0);
111 op
= ResolveOp(makeType(GlobalProperty
, needsVarInjectionChecks
), depth
, globalObject
->structure(), 0, 0, slot
.cachedOffset());
115 op
= ResolveOp(Dynamic
, 0, 0, 0, 0, 0);
119 JSObject
* JSScope::objectAtScope(JSScope
* scope
)
121 JSObject
* object
= scope
;
122 if (object
->type() == WithScopeType
)
123 return jsCast
<JSWithScope
*>(object
)->object();
131 for (JSScope
* scope
= this; scope
; scope
= scope
->next())
136 // When an exception occurs, the result of isUnscopable becomes false.
137 static inline bool isUnscopable(ExecState
* exec
, JSScope
* scope
, JSObject
* object
, const Identifier
& ident
)
139 if (scope
->type() != WithScopeType
)
142 JSValue unscopables
= object
->get(exec
, exec
->propertyNames().unscopablesSymbol
);
143 if (exec
->hadException())
145 if (!unscopables
.isObject())
147 JSValue blocked
= jsCast
<JSObject
*>(unscopables
)->get(exec
, ident
);
148 if (exec
->hadException())
151 return blocked
.toBoolean(exec
);
154 JSValue
JSScope::resolve(ExecState
* exec
, JSScope
* scope
, const Identifier
& ident
)
156 ScopeChainIterator end
= scope
->end();
157 ScopeChainIterator it
= scope
->begin();
159 JSScope
* scope
= it
.scope();
160 JSObject
* object
= it
.get();
162 if (++it
== end
) // Global scope.
165 if (object
->hasProperty(exec
, ident
)) {
166 if (!isUnscopable(exec
, scope
, object
, ident
))
168 ASSERT_WITH_MESSAGE(!exec
->hadException(), "When an exception occurs, the result of isUnscopable becomes false");
173 ResolveOp
JSScope::abstractResolve(ExecState
* exec
, bool hasTopActivation
, JSScope
* scope
, const Identifier
& ident
, GetOrPut getOrPut
, ResolveType unlinkedType
)
175 ResolveOp
op(Dynamic
, 0, 0, 0, 0, 0);
176 if (unlinkedType
== Dynamic
)
179 size_t depth
= hasTopActivation
? 1 : 0;
180 bool needsVarInjectionChecks
= JSC::needsVarInjectionChecks(unlinkedType
);
181 for (; scope
; scope
= scope
->next()) {
182 if (abstractAccess(exec
, scope
, ident
, getOrPut
, depth
, needsVarInjectionChecks
, op
))
190 const char* resolveModeName(ResolveMode mode
)
192 static const char* const names
[] = {
194 "DoNotThrowIfNotFound"
199 const char* resolveTypeName(ResolveType type
)
201 static const char* const names
[] = {
206 "GlobalPropertyWithVarInjectionChecks",
207 "GlobalVarWithVarInjectionChecks",
208 "ClosureVarWithVarInjectionChecks",