bool DebuggerScope::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { DebuggerScope* scope = jsCast<DebuggerScope*>(object); ASSERT(scope->isValid()); if (!scope->isValid()) return false; JSObject* thisObject = JSScope::objectAtScope(scope->jsScope()); slot.setThisValue(JSValue(thisObject)); // By default, JSObject::getPropertySlot() will look in the DebuggerScope's prototype // chain and not the wrapped scope, and JSObject::getPropertySlot() cannot be overridden // to behave differently for the DebuggerScope. // // Instead, we'll treat all properties in the wrapped scope and its prototype chain as // the own properties of the DebuggerScope. This is fine because the WebInspector // does not presently need to distinguish between what's owned at each level in the // prototype chain. Hence, we'll invoke getPropertySlot() on the wrapped scope here // instead of getOwnPropertySlot(). bool result = thisObject->getPropertySlot(exec, propertyName, slot); if (result && slot.isValue() && slot.getValue(exec, propertyName) == jsTDZValue()) { // FIXME: // We hit a scope property that has the TDZ empty value. // Currently, we just lie to the inspector and claim that this property is undefined. // This is not ideal and we should fix it. // https://bugs.webkit.org/show_bug.cgi?id=144977 slot.setValue(slot.slotBase(), DontEnum, jsUndefined()); return true; } return result; }
JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope) { auto throwScope = DECLARE_THROW_SCOPE(vm); RELEASE_ASSERT(scope); JSGlobalObject* globalObject = scope->globalObject(); RELEASE_ASSERT(globalObject); ASSERT(&globalObject->vm() == &vm); ParserError error; JSParserStrictMode strictMode = isStrictMode() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; DebuggerMode debuggerMode = globalObject->hasInteractiveDebugger() ? DebuggerOn : DebuggerOff; UnlinkedProgramCodeBlock* unlinkedCodeBlock = vm.codeCache()->getUnlinkedProgramCodeBlock( vm, this, source(), strictMode, debuggerMode, error); if (globalObject->hasDebugger()) globalObject->debugger()->sourceParsed(callFrame, source().provider(), error.line(), error.message()); if (error.isValid()) return error.toErrorObject(globalObject, source()); JSValue nextPrototype = globalObject->getPrototypeDirect(); while (nextPrototype && nextPrototype.isObject()) { if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType)) { ExecState* exec = globalObject->globalExec(); return createTypeError(exec, ASCIILiteral("Proxy is not allowed in the global prototype chain.")); } nextPrototype = asObject(nextPrototype)->getPrototypeDirect(); } 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 { ExecState* exec = globalObject->globalExec(); // Check for intersection of "var" and "let"/"const"/"class" for (auto& entry : lexicalDeclarations) { if (variableDeclarations.contains(entry.key)) return createSyntaxError(exec, 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, or "var"/"let"/"const" variables. // It's an error to introduce a shadow. for (auto& entry : lexicalDeclarations) { bool hasProperty = globalObject->hasProperty(exec, entry.key.get()); RETURN_IF_EXCEPTION(throwScope, throwScope.exception()); if (hasProperty) { // The ES6 spec says that just RestrictedGlobalProperty can't be shadowed // This carried out section 8.1.1.4.14 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasrestrictedglobalproperty PropertyDescriptor descriptor; globalObject->getOwnPropertyDescriptor(exec, entry.key.get(), descriptor); if (descriptor.value() != jsUndefined() && !descriptor.configurable()) return createSyntaxError(exec, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'")); } hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get()); RETURN_IF_EXCEPTION(throwScope, throwScope.exception()); if (hasProperty) { if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) { // 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(exec, 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(exec, entry.key.get()); RETURN_IF_EXCEPTION(throwScope, throwScope.exception()); if (hasProperty) return createSyntaxError(exec, 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(callFrame, 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(callFrame, Identifier::fromUid(&vm, entry.key.get())); ASSERT(!throwScope.exception()); } { 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() && !isStrictMode())) { if (symbolTable->contains(locker, entry.key.get())) continue; } ScopeOffset offset = symbolTable->takeNextScopeOffset(locker); SymbolTableEntry newEntry(VarOffset(offset), entry.value.isConst() ? ReadOnly : 0); newEntry.prepareToWatch(); symbolTable->add(locker, entry.key.get(), newEntry); ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue()); RELEASE_ASSERT(offsetForAssert == offset); } } return nullptr; }
void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecutable* moduleProgramExecutable) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation SymbolTable* symbolTable = moduleProgramExecutable->moduleEnvironmentSymbolTable(); JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, exec->lexicalGlobalObject(), exec->lexicalGlobalObject(), symbolTable, jsTDZValue(), this); // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation // section 15.2.1.16.4 step 9. // Ensure all the indirect exports are correctly resolved to unique bindings. // Even if we avoided duplicate exports in the parser, still ambiguous exports occur due to the star export (`export * from "mod"`). // When we see this type of ambiguity for the indirect exports here, throw a syntax error. for (const auto& pair : exportEntries()) { const ExportEntry& exportEntry = pair.value; if (exportEntry.type == JSModuleRecord::ExportEntry::Type::Indirect) { Resolution resolution = resolveExport(exec, exportEntry.exportName); switch (resolution.type) { case Resolution::Type::NotFound: throwSyntaxError(exec, scope, makeString("Indirectly exported binding name '", String(exportEntry.exportName.impl()), "' is not found.")); return; case Resolution::Type::Ambiguous: throwSyntaxError(exec, scope, makeString("Indirectly exported binding name '", String(exportEntry.exportName.impl()), "' cannot be resolved due to ambiguous multiple bindings.")); return; case Resolution::Type::Error: throwSyntaxError(exec, scope, makeString("Indirectly exported binding name 'default' cannot be resolved by star export entries.")); return; case Resolution::Type::Resolved: break; } } } // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation // section 15.2.1.16.4 step 12. // Instantiate namespace objects and initialize the bindings with them if required. // And ensure that all the imports correctly resolved to unique bindings. for (const auto& pair : importEntries()) { const ImportEntry& importEntry = pair.value; AbstractModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest); if (importEntry.isNamespace(vm)) { JSModuleNamespaceObject* namespaceObject = importedModule->getModuleNamespace(exec); RETURN_IF_EXCEPTION(scope, void()); bool putResult = false; symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, importEntry.localName, namespaceObject, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); } else { Resolution resolution = importedModule->resolveExport(exec, importEntry.importName); switch (resolution.type) { case Resolution::Type::NotFound: throwSyntaxError(exec, scope, makeString("Importing binding name '", String(importEntry.importName.impl()), "' is not found.")); return; case Resolution::Type::Ambiguous: throwSyntaxError(exec, scope, makeString("Importing binding name '", String(importEntry.importName.impl()), "' cannot be resolved due to ambiguous multiple bindings.")); return; case Resolution::Type::Error: throwSyntaxError(exec, scope, makeString("Importing binding name 'default' cannot be resolved by star export entries.")); return; case Resolution::Type::Resolved: break; } } } // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation // section 15.2.1.16.4 step 14. // Module environment contains the heap allocated "var", "function", "let", "const", and "class". // When creating the environment, we initialized all the slots with empty, it's ok for lexical values. // But for "var" and "function", we should initialize it with undefined. They are contained in the declared variables. for (const auto& variable : declaredVariables()) { SymbolTableEntry entry = symbolTable->get(variable.key.get()); VarOffset offset = entry.varOffset(); if (!offset.isStack()) { bool putResult = false; symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, Identifier::fromUid(exec, variable.key.get()), jsUndefined(), /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); } } // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation // section 15.2.1.16.4 step 16-a-iv. // Initialize heap allocated function declarations. // They can be called before the body of the module is executed under circular dependencies. UnlinkedModuleProgramCodeBlock* unlinkedCodeBlock = moduleProgramExecutable->unlinkedModuleProgramCodeBlock(); for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) { UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i); SymbolTableEntry entry = symbolTable->get(unlinkedFunctionExecutable->name().impl()); VarOffset offset = entry.varOffset(); if (!offset.isStack()) { ASSERT(!unlinkedFunctionExecutable->name().isEmpty()); if (vm.typeProfiler() || vm.controlFlowProfiler()) { vm.functionHasExecutedCache()->insertUnexecutedRange(moduleProgramExecutable->sourceID(), unlinkedFunctionExecutable->typeProfilingStartOffset(), unlinkedFunctionExecutable->typeProfilingEndOffset()); } JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable->source()), moduleEnvironment); bool putResult = false; symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, unlinkedFunctionExecutable->name(), function, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult); } } m_moduleEnvironment.set(vm, this, moduleEnvironment); }
JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope) { RELEASE_ASSERT(scope); JSGlobalObject* globalObject = scope->globalObject(); RELEASE_ASSERT(globalObject); ASSERT(&globalObject->vm() == &vm); JSObject* exception = 0; UnlinkedProgramCodeBlock* unlinkedCodeBlock = globalObject->createProgramCodeBlock(callFrame, this, &exception); if (exception) return exception; 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 { ExecState* exec = globalObject->globalExec(); // Check for intersection of "var" and "let"/"const"/"class" for (auto& entry : lexicalDeclarations) { if (variableDeclarations.contains(entry.key)) return createSyntaxError(exec, 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, or "var"/"let"/"const" variables. // It's an error to introduce a shadow. for (auto& entry : lexicalDeclarations) { if (globalObject->hasProperty(exec, entry.key.get())) return createSyntaxError(exec, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'")); if (globalLexicalEnvironment->hasProperty(exec, entry.key.get())) return createSyntaxError(exec, 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) { if (globalLexicalEnvironment->hasProperty(exec, entry.key.get())) return createSyntaxError(exec, 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(callFrame, 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(callFrame, Identifier::fromUid(&vm, entry.key.get())); } { JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope()); SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable(); ConcurrentJITLocker locker(symbolTable->m_lock); for (auto& entry : lexicalDeclarations) { ScopeOffset offset = symbolTable->takeNextScopeOffset(locker); SymbolTableEntry newEntry(VarOffset(offset), entry.value.isConst() ? ReadOnly : 0); newEntry.prepareToWatch(); symbolTable->add(locker, entry.key.get(), newEntry); ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue()); RELEASE_ASSERT(offsetForAssert == offset); } } return 0; }