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