blob: eabfa85aca37ef0aa7c7df895eae41739d15490e [file] [log] [blame]
/*
* Copyright (C) 2009-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "BatchedTransitionOptimizer.h"
#include "CodeCache.h"
#include "Debugger.h"
#include "VMTrapsInlines.h"
namespace JSC {
const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ProgramExecutable) };
ProgramExecutable::ProgramExecutable(JSGlobalObject* globalObject, const SourceCode& source)
: Base(globalObject->vm().programExecutableStructure.get(), globalObject->vm(), source, false, DerivedContextType::None, false, false, EvalContextType::None, NoIntrinsic)
{
ASSERT(source.provider()->sourceType() == SourceProviderSourceType::Program);
VM& vm = globalObject->vm();
if (vm.typeProfiler() || vm.controlFlowProfiler())
vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(), typeProfilingStartOffset(vm), typeProfilingEndOffset(vm));
}
void ProgramExecutable::destroy(JSCell* cell)
{
static_cast<ProgramExecutable*>(cell)->ProgramExecutable::~ProgramExecutable();
}
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasrestrictedglobalproperty
enum class GlobalPropertyLookUpStatus {
NotFound,
Configurable,
NonConfigurable,
};
static GlobalPropertyLookUpStatus hasRestrictedGlobalProperty(JSGlobalObject* globalObject, PropertyName propertyName)
{
PropertyDescriptor descriptor;
if (!globalObject->getOwnPropertyDescriptor(globalObject, propertyName, descriptor))
return GlobalPropertyLookUpStatus::NotFound;
if (descriptor.configurable())
return GlobalPropertyLookUpStatus::Configurable;
return GlobalPropertyLookUpStatus::NonConfigurable;
}
JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, JSGlobalObject* globalObject, JSScope* scope)
{
DeferTermination deferScope(vm);
auto throwScope = DECLARE_THROW_SCOPE(vm);
RELEASE_ASSERT(scope);
ASSERT(globalObject == scope->globalObject(vm));
RELEASE_ASSERT(globalObject);
ASSERT(&globalObject->vm() == &vm);
ParserError error;
JSParserStrictMode strictMode = isInStrictContext() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict;
OptionSet<CodeGenerationMode> codeGenerationMode = globalObject->defaultCodeGenerationMode();
UnlinkedProgramCodeBlock* unlinkedCodeBlock = vm.codeCache()->getUnlinkedProgramCodeBlock(
vm, this, source(), strictMode, codeGenerationMode, error);
if (globalObject->hasDebugger())
globalObject->debugger()->sourceParsed(globalObject, source().provider(), error.line(), error.message());
if (error.isValid())
return error.toErrorObject(globalObject, source());
JSValue nextPrototype = globalObject->getPrototypeDirect(vm);
while (nextPrototype && nextPrototype.isObject()) {
if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
return createTypeError(globalObject, "Proxy is not allowed in the global prototype chain."_s);
nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
}
JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment();
const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations();
const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations();
// The ES6 spec says that no vars/global properties/let/const can be duplicated in the global scope.
// This carried out section 15.1.8 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-globaldeclarationinstantiation
{
// Check for intersection of "var" and "let"/"const"/"class"
for (auto& entry : lexicalDeclarations) {
if (variableDeclarations.contains(entry.key))
return createSyntaxError(globalObject, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
}
// Check if any new "let"/"const"/"class" will shadow any pre-existing global property names (with configurable = false), or "var"/"let"/"const" variables.
// It's an error to introduce a shadow.
for (auto& entry : lexicalDeclarations) {
// The ES6 spec says that RestrictedGlobalProperty can't be shadowed.
GlobalPropertyLookUpStatus status = hasRestrictedGlobalProperty(globalObject, entry.key.get());
RETURN_IF_EXCEPTION(throwScope, nullptr);
switch (status) {
case GlobalPropertyLookUpStatus::NonConfigurable:
return createSyntaxError(globalObject, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'"));
case GlobalPropertyLookUpStatus::Configurable:
// Lexical bindings can shadow global properties if the given property's attribute is configurable.
// https://tc39.github.io/ecma262/#sec-globaldeclarationinstantiation step 5-c, `hasRestrictedGlobal` becomes false
// However we may emit GlobalProperty look up in bytecodes already and it may cache the value for the global scope.
// To make it invalid,
// 1. In LLInt and Baseline, we bump the global lexical binding epoch and it works.
// 3. In DFG and FTL, we watch the watchpoint and jettison once it is fired.
break;
case GlobalPropertyLookUpStatus::NotFound:
break;
}
bool hasProperty = globalLexicalEnvironment->hasProperty(globalObject, entry.key.get());
RETURN_IF_EXCEPTION(throwScope, nullptr);
if (hasProperty) {
if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isInStrictContext())) {
// We only allow "const" duplicate declarations under this setting.
// For example, we don't "let" variables to be overridden by "const" variables.
if (globalLexicalEnvironment->isConstVariable(entry.key.get()))
continue;
}
return createSyntaxError(globalObject, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
}
}
// Check if any new "var"s will shadow any previous "let"/"const"/"class" names.
// It's an error to introduce a shadow.
if (!globalLexicalEnvironment->isEmpty()) {
for (auto& entry : variableDeclarations) {
bool hasProperty = globalLexicalEnvironment->hasProperty(globalObject, entry.key.get());
RETURN_IF_EXCEPTION(throwScope, nullptr);
if (hasProperty)
return createSyntaxError(globalObject, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'"));
}
}
}
m_unlinkedProgramCodeBlock.set(vm, this, unlinkedCodeBlock);
BatchedTransitionOptimizer optimizer(vm, globalObject);
for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) {
UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i);
ASSERT(!unlinkedFunctionExecutable->name().isEmpty());
globalObject->addFunction(globalObject, unlinkedFunctionExecutable->name());
if (vm.typeProfiler() || vm.controlFlowProfiler()) {
vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
unlinkedFunctionExecutable->typeProfilingStartOffset(),
unlinkedFunctionExecutable->typeProfilingEndOffset());
}
}
for (auto& entry : variableDeclarations) {
ASSERT(entry.value.isVar());
globalObject->addVar(globalObject, Identifier::fromUid(vm, entry.key.get()));
throwScope.assertNoException();
}
{
JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope());
SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable();
ConcurrentJSLocker locker(symbolTable->m_lock);
for (auto& entry : lexicalDeclarations) {
if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isInStrictContext())) {
if (symbolTable->contains(locker, entry.key.get()))
continue;
}
ScopeOffset offset = symbolTable->takeNextScopeOffset(locker);
SymbolTableEntry newEntry(VarOffset(offset), static_cast<unsigned>(entry.value.isConst() ? PropertyAttribute::ReadOnly : PropertyAttribute::None));
newEntry.prepareToWatch();
symbolTable->add(locker, entry.key.get(), newEntry);
ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue());
RELEASE_ASSERT(offsetForAssert == offset);
}
}
if (lexicalDeclarations.size()) {
#if ENABLE(DFG_JIT)
for (auto& entry : lexicalDeclarations) {
// If WatchpointSet exists, just fire it. Since DFG WatchpointSet addition is also done on the main thread, we can sync them.
// So that we do not create WatchpointSet here. DFG will create if necessary on the main thread.
// And it will only create not-invalidated watchpoint set if the global lexical environment binding doesn't exist, which is why this code works.
if (auto* watchpointSet = globalObject->getReferencedPropertyWatchpointSet(entry.key.get()))
watchpointSet->fireAll(vm, "Lexical binding shadows an existing global property");
}
#endif
globalObject->bumpGlobalLexicalBindingEpoch(vm);
}
return nullptr;
}
auto ProgramExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
{
return ensureTemplateObjectMapImpl(m_templateObjectMap);
}
template<typename Visitor>
void ProgramExecutable::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_unlinkedProgramCodeBlock);
visitor.append(thisObject->m_programCodeBlock);
if (TemplateObjectMap* map = thisObject->m_templateObjectMap.get()) {
Locker locker { thisObject->cellLock() };
for (auto& entry : *map)
visitor.append(entry.value);
}
}
DEFINE_VISIT_CHILDREN(ProgramExecutable);
} // namespace JSC