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 OptionSet<CodeGenerationMode> codeGenerationMode = globalObject->defaultCodeGenerationMode();
87 UnlinkedProgramCodeBlock* unlinkedCodeBlock = vm.codeCache()->getUnlinkedProgramCodeBlock(
88 vm, this, source(), strictMode, codeGenerationMode, error);
89
90 if (globalObject->hasDebugger())
91 globalObject->debugger()->sourceParsed(callFrame, source().provider(), error.line(), error.message());
92
93 if (error.isValid())
94 return error.toErrorObject(globalObject, source());
95
96 JSValue nextPrototype = globalObject->getPrototypeDirect(vm);
97 while (nextPrototype && nextPrototype.isObject()) {
98 if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType)) {
99 ExecState* exec = globalObject->globalExec();
100 return createTypeError(exec, "Proxy is not allowed in the global prototype chain."_s);
101 }
102 nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
103 }
104
105 JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment();
106 const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations();
107 const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations();
108 // The ES6 spec says that no vars/global properties/let/const can be duplicated in the global scope.
109 // This carried out section 15.1.8 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-globaldeclarationinstantiation
110 {
111 ExecState* exec = globalObject->globalExec();
112 // Check for intersection of "var" and "let"/"const"/"class"
113 for (auto& entry : lexicalDeclarations) {
114 if (variableDeclarations.contains(entry.key))
115 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
116 }
117
118 // Check if any new "let"/"const"/"class" will shadow any pre-existing global property names (with configurable = false), or "var"/"let"/"const" variables.
119 // It's an error to introduce a shadow.
120 for (auto& entry : lexicalDeclarations) {
121 // The ES6 spec says that RestrictedGlobalProperty can't be shadowed.
122 GlobalPropertyLookUpStatus status = hasRestrictedGlobalProperty(exec, globalObject, entry.key.get());
123 RETURN_IF_EXCEPTION(throwScope, nullptr);
124 switch (status) {
125 case GlobalPropertyLookUpStatus::NonConfigurable:
126 return createSyntaxError(exec, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'"));
127 case GlobalPropertyLookUpStatus::Configurable:
128 // Lexical bindings can shadow global properties if the given property's attribute is configurable.
129 // https://tc39.github.io/ecma262/#sec-globaldeclarationinstantiation step 5-c, `hasRestrictedGlobal` becomes false
130 // However we may emit GlobalProperty look up in bytecodes already and it may cache the value for the global scope.
131 // To make it invalid,
132 // 1. In LLInt and Baseline, we bump the global lexical binding epoch and it works.
133 // 3. In DFG and FTL, we watch the watchpoint and jettison once it is fired.
134 break;
135 case GlobalPropertyLookUpStatus::NotFound:
136 break;
137 }
138
139 bool hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get());
140 RETURN_IF_EXCEPTION(throwScope, nullptr);
141 if (hasProperty) {
142 if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) {
143 // We only allow "const" duplicate declarations under this setting.
144 // For example, we don't "let" variables to be overridden by "const" variables.
145 if (globalLexicalEnvironment->isConstVariable(entry.key.get()))
146 continue;
147 }
148 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
149 }
150 }
151
152 // Check if any new "var"s will shadow any previous "let"/"const"/"class" names.
153 // It's an error to introduce a shadow.
154 if (!globalLexicalEnvironment->isEmpty()) {
155 for (auto& entry : variableDeclarations) {
156 bool hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get());
157 RETURN_IF_EXCEPTION(throwScope, nullptr);
158 if (hasProperty)
159 return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
160 }
161 }
162 }
163
164
165 m_unlinkedProgramCodeBlock.set(vm, this, unlinkedCodeBlock);
166
167 BatchedTransitionOptimizer optimizer(vm, globalObject);
168
169 for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) {
170 UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i);
171 ASSERT(!unlinkedFunctionExecutable->name().isEmpty());
172 globalObject->addFunction(callFrame, unlinkedFunctionExecutable->name());
173 if (vm.typeProfiler() || vm.controlFlowProfiler()) {
174 vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
175 unlinkedFunctionExecutable->typeProfilingStartOffset(),
176 unlinkedFunctionExecutable->typeProfilingEndOffset());
177 }
178 }
179
180 for (auto& entry : variableDeclarations) {
181 ASSERT(entry.value.isVar());
182 globalObject->addVar(callFrame, Identifier::fromUid(&vm, entry.key.get()));
183 throwScope.assertNoException();
184 }
185
186 {
187 JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope());
188 SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable();
189 ConcurrentJSLocker locker(symbolTable->m_lock);
190 for (auto& entry : lexicalDeclarations) {
191 if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) {
192 if (symbolTable->contains(locker, entry.key.get()))
193 continue;
194 }
195 ScopeOffset offset = symbolTable->takeNextScopeOffset(locker);
196 SymbolTableEntry newEntry(VarOffset(offset), static_cast<unsigned>(entry.value.isConst() ? PropertyAttribute::ReadOnly : PropertyAttribute::None));
197 newEntry.prepareToWatch();
198 symbolTable->add(locker, entry.key.get(), newEntry);
199
200 ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue());
201 RELEASE_ASSERT(offsetForAssert == offset);
202 }
203 }
204 if (lexicalDeclarations.size()) {
205#if ENABLE(DFG_JIT)
206 for (auto& entry : lexicalDeclarations) {
207 // If WatchpointSet exists, just fire it. Since DFG WatchpointSet addition is also done on the main thread, we can sync them.
208 // So that we do not create WatchpointSet here. DFG will create if necessary on the main thread.
209 // And it will only create not-invalidated watchpoint set if the global lexical environment binding doesn't exist, which is why this code works.
210 if (auto* watchpointSet = globalObject->getReferencedPropertyWatchpointSet(entry.key.get()))
211 watchpointSet->fireAll(vm, "Lexical binding shadows an existing global property");
212 }
213#endif
214 globalObject->bumpGlobalLexicalBindingEpoch(vm);
215 }
216 return nullptr;
217}
218
219auto ProgramExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
220{
221 return ensureTemplateObjectMapImpl(m_templateObjectMap);
222}
223
224void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
225{
226 ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell);
227 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
228 Base::visitChildren(thisObject, visitor);
229 visitor.append(thisObject->m_unlinkedProgramCodeBlock);
230 visitor.append(thisObject->m_programCodeBlock);
231 if (TemplateObjectMap* map = thisObject->m_templateObjectMap.get()) {
232 auto locker = holdLock(thisObject->cellLock());
233 for (auto& entry : *map)
234 visitor.append(entry.value);
235 }
236}
237
238} // namespace JSC
239