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