Exception is a JSCell, not a JSObject.
[WebKit-https.git] / Source / JavaScriptCore / runtime / ProgramExecutable.cpp
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
42 namespace JSC {
43
44 const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &ScriptExecutable::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ProgramExecutable) };
45
46 ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source)
47     : ScriptExecutable(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
55 void 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
61 enum class GlobalPropertyLookUpStatus {
62     NotFound,
63     Configurable,
64     NonConfigurable,
65 };
66 static 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
76 JSObject* 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
220 void 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