/ runtime / ProgramExecutable.cpp
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