1/*
2 * Copyright (C) 2009-2019 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
28#include "BatchedTransitionOptimizer.h"
29#include "CodeBlock.h"
30#include "CodeCache.h"
31#include "Debugger.h"
32#include "Exception.h"
33#include "JIT.h"
34#include "JSCInlines.h"
35#include "LLIntEntrypoint.h"
36#include "Parser.h"
37#include "ProgramCodeBlock.h"
38#include "TypeProfiler.h"
39#include "VMInlines.h"
40#include <wtf/CommaPrinter.h>
41
42namespace JSC {
43
44const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ProgramExecutable) };
45
46ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source)
47 : Base(exec->vm().programExecutableStructure.get(), exec->vm(), source, false, DerivedContextType::None, false, EvalContextType::None, NoIntrinsic)
48{
49 ASSERT(source.provider()->sourceType() == SourceProviderSourceType::Program);
50 VM& vm = exec->vm();
51 if (vm.typeProfiler() || vm.controlFlowProfiler())
52 vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(), typeProfilingStartOffset(vm), typeProfilingEndOffset(vm));
53}
54
55void ProgramExecutable::destroy(JSCell* cell)
56{
57 static_cast<ProgramExecutable*>(cell)->ProgramExecutable::~ProgramExecutable();
58}
59
60// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasrestrictedglobalproperty
61enum class GlobalPropertyLookUpStatus {
62 NotFound,
63 Configurable,
64 NonConfigurable,
65};
66static GlobalPropertyLookUpStatus hasRestrictedGlobalProperty(ExecState* exec, JSGlobalObject* globalObject, PropertyName propertyName)
67{
68 PropertyDescriptor descriptor;
69 if (!globalObject->getOwnPropertyDescriptor(exec, propertyName, descriptor))
70 return GlobalPropertyLookUpStatus::NotFound;
71 if (descriptor.configurable())
72 return GlobalPropertyLookUpStatus::Configurable;
73 return GlobalPropertyLookUpStatus::NonConfigurable;
74}
75
76JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope)
77{
78 auto throwScope = DECLARE_THROW_SCOPE(vm);
79 RELEASE_ASSERT(scope);
80 JSGlobalObject* globalObject = scope->globalObject(vm);
81 RELEASE_ASSERT(globalObject);
82 ASSERT(&globalObject->vm() == &vm);
83
84 ParserError error;
85 JSParserStrictMode strictMode = isStrictMode() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict;
86 DebuggerMode debuggerMode = globalObject->hasInteractiveDebugger() ? DebuggerOn : DebuggerOff;
87
88 UnlinkedProgramCodeBlock* unlinkedCodeBlock = vm.codeCache()->getUnlinkedProgramCodeBlock(
89 vm, this, source(), strictMode, debuggerMode, error);
90
91 if (globalObject->hasDebugger())
92 globalObject->debugger()->sourceParsed(callFrame, source().provider(), error.line(), error.message());
93
94 if (error.isValid())
95 return error.toErrorObject(globalObject, source());
96
97 JSValue nextPrototype = globalObject->getPrototypeDirect(vm);
98 while (nextPrototype && nextPrototype.isObject()) {
99 if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType)) {
100 ExecState* exec = globalObject->globalExec();
101 return createTypeError(exec, "Proxy is not allowed in the global prototype chain."_s);
102 }
103 nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
104 }
105
106 JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment();
107 const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations();
108 const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations();
109 // The ES6 spec says that no vars/global properties/let/const can be duplicated in the global scope.
110 // This carried out section 15.1.8 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-globaldeclarationinstantiation
111 {
112 ExecState* exec = globalObject->globalExec();
113 // Check for intersection of "var" and "let"/"const"/"class"
114 for (auto& entry : lexicalDeclarations) {
115 if (variableDeclarations.contains(entry.key))
116 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
117 }
118
119 // Check if any new "let"/"const"/"class" will shadow any pre-existing global property names (with configurable = false), or "var"/"let"/"const" variables.
120 // It's an error to introduce a shadow.
121 for (auto& entry : lexicalDeclarations) {
122 // The ES6 spec says that RestrictedGlobalProperty can't be shadowed.
123 GlobalPropertyLookUpStatus status = hasRestrictedGlobalProperty(exec, globalObject, entry.key.get());
124 RETURN_IF_EXCEPTION(throwScope, nullptr);
125 switch (status) {
126 case GlobalPropertyLookUpStatus::NonConfigurable:
127 return createSyntaxError(exec, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'"));
128 case GlobalPropertyLookUpStatus::Configurable:
129 // Lexical bindings can shadow global properties if the given property's attribute is configurable.
130 // https://tc39.github.io/ecma262/#sec-globaldeclarationinstantiation step 5-c, `hasRestrictedGlobal` becomes false
131 // However we may emit GlobalProperty look up in bytecodes already and it may cache the value for the global scope.
132 // To make it invalid,
133 // 1. In LLInt and Baseline, we bump the global lexical binding epoch and it works.
134 // 3. In DFG and FTL, we watch the watchpoint and jettison once it is fired.
135 break;
136 case GlobalPropertyLookUpStatus::NotFound:
137 break;
138 }
139
140 bool hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get());
141 RETURN_IF_EXCEPTION(throwScope, nullptr);
142 if (hasProperty) {
143 if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) {
144 // We only allow "const" duplicate declarations under this setting.
145 // For example, we don't "let" variables to be overridden by "const" variables.
146 if (globalLexicalEnvironment->isConstVariable(entry.key.get()))
147 continue;
148 }
149 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
150 }
151 }
152
153 // Check if any new "var"s will shadow any previous "let"/"const"/"class" names.
154 // It's an error to introduce a shadow.
155 if (!globalLexicalEnvironment->isEmpty()) {
156 for (auto& entry : variableDeclarations) {
157 bool hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get());
158 RETURN_IF_EXCEPTION(throwScope, nullptr);
159 if (hasProperty)
160 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
161 }
162 }
163 }
164
165
166 m_unlinkedProgramCodeBlock.set(vm, this, unlinkedCodeBlock);
167
168 BatchedTransitionOptimizer optimizer(vm, globalObject);
169
170 for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) {
171 UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i);
172 ASSERT(!unlinkedFunctionExecutable->name().isEmpty());
173 globalObject->addFunction(callFrame, unlinkedFunctionExecutable->name());
174 if (vm.typeProfiler() || vm.controlFlowProfiler()) {
175 vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
176 unlinkedFunctionExecutable->typeProfilingStartOffset(),
177 unlinkedFunctionExecutable->typeProfilingEndOffset());
178 }
179 }
180
181 for (auto& entry : variableDeclarations) {
182 ASSERT(entry.value.isVar());
183 globalObject->addVar(callFrame, Identifier::fromUid(&vm, entry.key.get()));
184 throwScope.assertNoException();
185 }
186
187 {
188 JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope());
189 SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable();
190 ConcurrentJSLocker locker(symbolTable->m_lock);
191 for (auto& entry : lexicalDeclarations) {
192 if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) {
193 if (symbolTable->contains(locker, entry.key.get()))
194 continue;
195 }
196 ScopeOffset offset = symbolTable->takeNextScopeOffset(locker);
197 SymbolTableEntry newEntry(VarOffset(offset), static_cast<unsigned>(entry.value.isConst() ? PropertyAttribute::ReadOnly : PropertyAttribute::None));
198 newEntry.prepareToWatch();
199 symbolTable->add(locker, entry.key.get(), newEntry);
200
201 ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue());
202 RELEASE_ASSERT(offsetForAssert == offset);
203 }
204 }
205 if (lexicalDeclarations.size()) {
206#if ENABLE(DFG_JIT)
207 for (auto& entry : lexicalDeclarations) {
208 // If WatchpointSet exists, just fire it. Since DFG WatchpointSet addition is also done on the main thread, we can sync them.
209 // So that we do not create WatchpointSet here. DFG will create if necessary on the main thread.
210 // And it will only create not-invalidated watchpoint set if the global lexical environment binding doesn't exist, which is why this code works.
211 if (auto* watchpointSet = globalObject->getReferencedPropertyWatchpointSet(entry.key.get()))
212 watchpointSet->fireAll(vm, "Lexical binding shadows an existing global property");
213 }
214#endif
215 globalObject->bumpGlobalLexicalBindingEpoch(vm);
216 }
217 return nullptr;
218}
219
220void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
221{
222 ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell);
223 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
224 Base::visitChildren(thisObject, visitor);
225 visitor.append(thisObject->m_unlinkedProgramCodeBlock);
226 visitor.append(thisObject->m_programCodeBlock);
227}
228
229} // namespace JSC
230