escargot/src/parser/Script.cpp
seonghyun kim 53906a6d81 Optimize runtime performance
* Optimize ObjectStructurePropertyDescriptor
* Don't initialize inline storage of VectorWithInlineStorage
* Add Object::setPrototypeForIntrinsicObjectCreation for fast initialize
* Add ArrayObject::ArrayObject(ExecutionState& state, const uint64_t& size) for fast initialize
* Store stack-limit instead of stack-base
* Reduce size of ExecutionState
* Add fast version of Object::ownPropertyKeys for optimize Object.keys
* Add ValueVectorWithInlineStorage
* Remove some compiler warnings

Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
2019-12-05 15:04:20 +09:00

628 lines
30 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "Script.h"
#include "interpreter/ByteCode.h"
#include "interpreter/ByteCodeGenerator.h"
#include "interpreter/ByteCodeInterpreter.h"
#include "parser/ast/Node.h"
#include "runtime/Context.h"
#include "runtime/Environment.h"
#include "runtime/EnvironmentRecord.h"
#include "runtime/ErrorObject.h"
#include "runtime/SandBox.h"
#include "runtime/ScriptFunctionObject.h"
#include "runtime/ModuleNamespaceObject.h"
#include "util/Util.h"
#include "parser/ast/AST.h"
namespace Escargot {
bool Script::isExecuted()
{
return m_topCodeBlock->m_byteCodeBlock == nullptr;
}
Context* Script::context()
{
return m_topCodeBlock->context();
}
static Optional<Script*> findLoadedModule(Context* context, Optional<Script*> referrer, String* src)
{
const auto& lm = context->loadedModules();
for (size_t j = 0; j < lm.size(); j++) {
if (lm[j].m_referrer == referrer && lm[j].m_src->equals(src)) {
return Optional<Script*>(context->loadedModules()[j].m_loadedModule);
}
}
return Optional<Script*>();
}
static void registerToLoadedModuleIfNeeds(Context* context, Optional<Script*> referrer, String* src, Script* module)
{
if (!findLoadedModule(context, referrer, src)) {
LoadedModule m;
m.m_loadedModule = module;
m.m_referrer = referrer;
m.m_src = src;
auto& lm = context->loadedModules();
lm.push_back(m);
}
}
void Script::loadExternalModule(ExecutionState& state)
{
for (size_t i = 0; i < m_moduleData->m_importEntries.size(); i++) {
bool loaded = findLoadedModule(context(), this, m_moduleData->m_importEntries[i].m_moduleRequest).hasValue();
if (!loaded) {
loadModuleFromScript(state, m_moduleData->m_importEntries[i].m_moduleRequest);
}
}
for (size_t i = 0; i < m_moduleData->m_indirectExportEntries.size(); i++) {
bool loaded = findLoadedModule(context(), this, m_moduleData->m_indirectExportEntries[i].m_moduleRequest.value()).hasValue();
if (!loaded) {
loadModuleFromScript(state, m_moduleData->m_indirectExportEntries[i].m_moduleRequest.value());
}
}
for (size_t i = 0; i < m_moduleData->m_starExportEntries.size(); i++) {
bool loaded = findLoadedModule(context(), this, m_moduleData->m_starExportEntries[i].m_moduleRequest.value()).hasValue();
if (!loaded) {
loadModuleFromScript(state, m_moduleData->m_starExportEntries[i].m_moduleRequest.value());
}
}
}
void Script::loadModuleFromScript(ExecutionState& state, String* src)
{
Platform::LoadModuleResult result = context()->vmInstance()->platform()->onLoadModule(context(), this, src);
if (!result.script) {
ErrorObject::throwBuiltinError(state, (ErrorObject::Code)result.errorCode, result.errorMessage->toNonGCUTF8StringData().data());
}
registerToLoadedModuleIfNeeds(context(), this, src, result.script.value());
result.script->loadExternalModule(state);
}
size_t Script::moduleRequestsLength()
{
if (!isModule()) {
return 0;
}
return m_moduleData->m_importEntries.size() + m_moduleData->m_indirectExportEntries.size() + m_moduleData->m_starExportEntries.size();
}
String* Script::moduleRequest(size_t i)
{
if (!isModule()) {
return String::emptyString;
}
if (i < m_moduleData->m_importEntries.size()) {
return m_moduleData->m_importEntries[i].m_moduleRequest;
}
i = i - m_moduleData->m_importEntries.size();
if (i < m_moduleData->m_indirectExportEntries.size()) {
return m_moduleData->m_indirectExportEntries[i].m_moduleRequest.value();
}
i = i - m_moduleData->m_indirectExportEntries.size();
return m_moduleData->m_starExportEntries[i].m_moduleRequest.value();
}
AtomicStringVector Script::exportedNames(ExecutionState& state, std::vector<Script*>& exportStarSet)
{
// Let module be this Source Text Module Record.
Script* module = this;
// If exportStarSet contains module, then
for (size_t i = 0; i < exportStarSet.size(); i++) {
if (exportStarSet[i] == module) {
// Assert: Weve reached the starting point of an import * circularity.
// Return a new empty List.
return AtomicStringVector();
}
}
// Append module to exportStarSet.
exportStarSet.push_back(module);
// Let exportedNames be a new empty List.
AtomicStringVector exportedNames;
// For each ExportEntry Record e in module.[[LocalExportEntries]], do
auto& localExportEntries = m_moduleData->m_localExportEntries;
for (size_t i = 0; i < localExportEntries.size(); i++) {
auto& e = localExportEntries[i];
// Assert: module provides the direct binding for this export.
// Append e.[[ExportName]] to exportedNames.
exportedNames.push_back(e.m_exportName.value());
}
// For each ExportEntry Record e in module.[[IndirectExportEntries]], do
auto& indirectExportEntries = m_moduleData->m_indirectExportEntries;
for (size_t i = 0; i < indirectExportEntries.size(); i++) {
auto& e = indirectExportEntries[i];
// Assert: module imports a specific binding for this export.
// Append e.[[ExportName]] to exportedNames.
exportedNames.push_back(e.m_exportName.value());
}
// For each ExportEntry Record e in module.[[StarExportEntries]], do
auto& starExportEntries = m_moduleData->m_starExportEntries;
for (size_t i = 0; i < starExportEntries.size(); i++) {
auto& e = starExportEntries[i];
// Let requestedModule be HostResolveImportedModule(module, e.[[ModuleRequest]]).
// ReturnIfAbrupt(requestedModule).
Script* requestedModule = findLoadedModule(context(), this, e.m_moduleRequest.value()).value();
// Let starNames be requestedModule.GetExportedNames(exportStarSet).
auto starNames = requestedModule->exportedNames(state, exportStarSet);
// For each element n of starNames, do
for (size_t i = 0; i < starNames.size(); i++) {
// If SameValue(n, "default") is false, then
if (starNames[i] != state.context()->staticStrings().stringDefault) {
// If n is not an element of exportedNames, then
bool found = false;
for (size_t j = 0; j < exportedNames.size(); j++) {
if (starNames[i] == exportedNames[j]) {
found = true;
break;
}
}
if (!found) {
// Append n to exportedNames.
exportedNames.push_back(starNames[i]);
}
}
}
}
// Return exportedNames.
return exportedNames;
}
Script::ResolveExportResult Script::resolveExport(ExecutionState& state, AtomicString exportName, std::vector<std::tuple<Script*, AtomicString>>& resolveSet, std::vector<Script*>& exportStarSet)
{
ASSERT(isModule());
// Let module be this Source Text Module Record.
Script* module = this;
// For each Record {[[module]], [[exportName]]} r in resolveSet, do:
for (size_t i = 0; i < resolveSet.size(); i++) {
// If module and r.[[module]] are the same Module Record and SameValue(exportName, r.[[exportName]]) is true, then
if (std::get<0>(resolveSet[i]) == module && std::get<1>(resolveSet[i]) == exportName) {
// Assert: this is a circular import request.
// Return null.
return Script::ResolveExportResult(Script::ResolveExportResult::Null);
}
}
// Append the Record {[[module]]: module, [[exportName]]: exportName} to resolveSet.
resolveSet.push_back(std::make_tuple(this, exportName));
// For each ExportEntry Record e in module.[[LocalExportEntries]], do
auto& localExportEntries = m_moduleData->m_localExportEntries;
for (size_t i = 0; i < localExportEntries.size(); i++) {
// If SameValue(exportName, e.[[ExportName]]) is true, then
if (localExportEntries[i].m_exportName == exportName) {
// Assert: module provides the direct binding for this export.
// Return Record{[[module]]: module, [[bindingName]]: e.[[LocalName]]}.
return Script::ResolveExportResult(Script::ResolveExportResult::Record, Optional<std::tuple<Script*, AtomicString>>(std::make_tuple(module, localExportEntries[i].m_localName.value())));
}
}
// For each ExportEntry Record e in module.[[IndirectExportEntries]], do
auto& indirectExportEntries = m_moduleData->m_indirectExportEntries;
for (size_t i = 0; i < indirectExportEntries.size(); i++) {
auto& e = indirectExportEntries[i];
// If SameValue(exportName, e.[[ExportName]]) is true, then
if (e.m_exportName == exportName) {
// Assert: module imports a specific binding for this export.
// Let importedModule be HostResolveImportedModule(module, e.[[ModuleRequest]]).
// ReturnIfAbrupt(importedModule).
Script* importedModule = findLoadedModule(context(), this, e.m_moduleRequest.value()).value();
// Let indirectResolution be importedModule.ResolveExport(e.[[ImportName]], resolveSet, exportStarSet).
auto indirectResolution = importedModule->resolveExport(state, e.m_importName.value(), resolveSet, exportStarSet);
// ReturnIfAbrupt(indirectResolution).
// If indirectResolution is not null, return indirectResolution.
if (indirectResolution.m_type != Script::ResolveExportResult::Null) {
return indirectResolution;
}
}
}
// If SameValue(exportName, "default") is true, then
if (exportName == context()->staticStrings().stringDefault) {
// Assert: A default export was not explicitly defined by this module.
// Throw a SyntaxError exception.
// NOTE A default export cannot be provided by an export *.
ErrorObject::throwBuiltinError(state, ErrorObject::Code::SyntaxError, "The module '%s' does not provide an export named 'default'", src());
}
// If exportStarSet contains module, then return null.
for (size_t i = 0; i < exportStarSet.size(); i++) {
if (exportStarSet[i] == module) {
return Script::ResolveExportResult(Script::ResolveExportResult::Null);
}
}
// Append module to exportStarSet.
exportStarSet.push_back(module);
// Let starResolution be null.
Script::ResolveExportResult starResolution(Script::ResolveExportResult::Null);
// For each ExportEntry Record e in module.[[StarExportEntries]], do
auto& starExportEntries = m_moduleData->m_starExportEntries;
for (size_t i = 0; i < starExportEntries.size(); i++) {
auto& e = starExportEntries[i];
// Let importedModule be HostResolveImportedModule(module, e.[[ModuleRequest]]).
// ReturnIfAbrupt(importedModule).
Script* importedModule = findLoadedModule(context(), this, e.m_moduleRequest.value()).value();
// Let resolution be importedModule.ResolveExport(exportName, resolveSet, exportStarSet).
// ReturnIfAbrupt(resolution).
auto resolution = importedModule->resolveExport(state, exportName, resolveSet, exportStarSet);
// If resolution is "ambiguous", return "ambiguous".
if (resolution.m_type == Script::ResolveExportResult::Ambiguous) {
return resolution;
}
// If resolution is not null, then
if (resolution.m_type != Script::ResolveExportResult::Null) {
// If starResolution is null, let starResolution be resolution.
if (starResolution.m_type == Script::ResolveExportResult::Null) {
starResolution = resolution;
} else {
// Else
// Assert: there is more than one * import that includes the requested name.
// If resolution.[[module]] and starResolution.[[module]] are not the same Module Record or SameValue(resolution.[[exportName]], starResolution.[[exportName]]) is false, return "ambiguous".
if (std::get<0>(resolution.m_record.value()) != std::get<0>(starResolution.m_record.value())
|| std::get<1>(resolution.m_record.value()) != std::get<1>(starResolution.m_record.value())) {
return Script::ResolveExportResult(Script::ResolveExportResult::Ambiguous);
}
}
}
}
return starResolution;
}
Value Script::executeModule(ExecutionState& state, Optional<Script*> referrer)
{
// http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
// ModuleDeclarationInstantiation( ) Concrete Method
LexicalEnvironment* globalLexicalEnv = new LexicalEnvironment(
new GlobalEnvironmentRecord(state, nullptr, context()->globalObject(), &context()->globalDeclarativeRecord(), &context()->globalDeclarativeStorage()), nullptr);
ExecutionState newState(context(), state.stackLimit());
ModuleEnvironmentRecord* moduleRecord = new ModuleEnvironmentRecord(this);
m_moduleData->m_moduleRecord = moduleRecord;
context()->vmInstance()->platform()->didLoadModule(context(), referrer, this);
registerToLoadedModuleIfNeeds(context(), referrer, src(), this);
loadExternalModule(state);
const InterpretedCodeBlock::IdentifierInfoVector& vec = m_topCodeBlock->identifierInfos();
size_t len = vec.size();
for (size_t i = 0; i < len; i++) {
moduleRecord->createBinding(newState, vec[i].m_name, false, vec[i].m_isMutable, true);
}
InterpretedCodeBlock::BlockInfo* bi = m_topCodeBlock->blockInfo(0);
if (bi) {
len = bi->m_identifiers.size();
for (size_t i = 0; i < len; i++) {
moduleRecord->createBinding(newState, bi->m_identifiers[i].m_name, false, bi->m_identifiers[i].m_isMutable, false);
}
}
// Test import, export first
// For each ImportEntry Record in in module.[[ImportEntries]], do
for (size_t i = 0; i < m_moduleData->m_importEntries.size(); i++) {
auto& in = m_moduleData->m_importEntries[i];
// Let importedModule be HostResolveImportedModule(module, in.[[ModuleRequest]]).
// ReturnIfAbrupt(importedModule).
Script* importedModule = findLoadedModule(context(), this, in.m_moduleRequest).value();
// If in.[[ImportName]] is "*", then
if (in.m_importName == context()->staticStrings().asciiTable[(unsigned char)'*']) {
} else {
// Let resolution be importedModule.ResolveExport(in.[[ImportName]], « », «‍ »).
auto resolution = importedModule->resolveExport(newState, in.m_importName);
// ReturnIfAbrupt(resolution).
// If resolution is null or resolution is "ambiguous", throw a SyntaxError exception.
// Call envRec.CreateImportBinding(in.[[LocalName]], resolution.[[module]], resolution.[[bindingName]]).
if (resolution.m_type == Script::ResolveExportResult::Null) {
StringBuilder builder;
builder.appendString("The requested module '");
builder.appendString(in.m_moduleRequest);
builder.appendString("' does not provide an export named '");
builder.appendString(in.m_localName.string());
builder.appendString("'");
ErrorObject::throwBuiltinError(newState, ErrorObject::Code::SyntaxError, builder.finalize(&newState)->toNonGCUTF8StringData().data());
} else if (resolution.m_type == Script::ResolveExportResult::Ambiguous) {
StringBuilder builder;
builder.appendString("The requested module '");
builder.appendString(in.m_moduleRequest);
builder.appendString("' does not provide an export named '");
builder.appendString(in.m_localName.string());
builder.appendString("' correctly");
ErrorObject::throwBuiltinError(newState, ErrorObject::Code::SyntaxError, builder.finalize(&newState)->toNonGCUTF8StringData().data());
}
}
}
// execute external modules
for (size_t i = 0; i < m_moduleData->m_importEntries.size(); i++) {
Script* script = findLoadedModule(context(), this, m_moduleData->m_importEntries[i].m_moduleRequest).value();
if (!script->isExecuted()) {
script->executeModule(state, this);
}
}
for (size_t i = 0; i < m_moduleData->m_indirectExportEntries.size(); i++) {
Script* script = findLoadedModule(context(), this, m_moduleData->m_indirectExportEntries[i].m_moduleRequest.value()).value();
if (!script->isExecuted()) {
script->executeModule(state, this);
}
}
for (size_t i = 0; i < m_moduleData->m_starExportEntries.size(); i++) {
Script* script = findLoadedModule(context(), this, m_moduleData->m_starExportEntries[i].m_moduleRequest.value()).value();
if (!script->isExecuted()) {
script->executeModule(state, this);
}
}
// Import variables
// For each ImportEntry Record in in module.[[ImportEntries]], do
for (size_t i = 0; i < m_moduleData->m_importEntries.size(); i++) {
auto& in = m_moduleData->m_importEntries[i];
// Let importedModule be HostResolveImportedModule(module, in.[[ModuleRequest]]).
// ReturnIfAbrupt(importedModule).
Script* importedModule = findLoadedModule(context(), this, in.m_moduleRequest).value();
// If in.[[ImportName]] is "*", then
if (in.m_importName == context()->staticStrings().asciiTable[(unsigned char)'*']) {
// Let namespace be GetModuleNamespace(importedModule).
// ReturnIfAbrupt(module).
ModuleNamespaceObject* namespaceObject = importedModule->moduleData()->m_moduleRecord->namespaceObject(newState);
// Let status be envRec.CreateImmutableBinding(in.[[LocalName]], true).
// Assert: status is not an abrupt completion.
// Call envRec.InitializeBinding(in.[[LocalName]], namespace).
moduleRecord->initializeBinding(newState, in.m_localName, Value(namespaceObject));
} else {
// Let resolution be importedModule.ResolveExport(in.[[ImportName]], « », «‍ »).
auto resolution = importedModule->resolveExport(newState, in.m_importName);
// ReturnIfAbrupt(resolution).
// If resolution is null or resolution is "ambiguous", throw a SyntaxError exception.
// Call envRec.CreateImportBinding(in.[[LocalName]], resolution.[[module]], resolution.[[bindingName]]).
ASSERT(resolution.m_type == Script::ResolveExportResult::Record);
auto bindingResult = std::get<0>(resolution.m_record.value())->moduleData()->m_moduleRecord->getBindingValue(newState, std::get<1>(resolution.m_record.value()));
ASSERT(bindingResult.m_hasBindingValue);
moduleRecord->initializeBinding(newState, in.m_localName, bindingResult.m_value);
}
}
newState.setLexicalEnvironment(new LexicalEnvironment(moduleRecord, globalLexicalEnv), true);
size_t literalStorageSize = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.size();
Value* registerFile = (Value*)ALLOCA((m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize + 1 + literalStorageSize + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth()) * sizeof(Value), Value, state);
registerFile[0] = Value();
Value* stackStorage = registerFile + m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize;
stackStorage[0] = Value();
Value* literalStorage = stackStorage + 1 + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth();
Value* src = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.data();
for (size_t i = 0; i < literalStorageSize; i++) {
literalStorage[i] = src[i];
}
Value resultValue = ByteCodeInterpreter::interpret(&newState, m_topCodeBlock->byteCodeBlock(), 0, registerFile);
clearStack<512>();
// we give up program bytecodeblock after first excution for reducing memory usage
m_topCodeBlock->m_byteCodeBlock = nullptr;
return resultValue;
}
Value Script::execute(ExecutionState& state, bool isExecuteOnEvalFunction, bool inStrictMode)
{
if (UNLIKELY(isExecuted())) {
if (!m_canExecuteAgain) {
ESCARGOT_LOG_ERROR("You cannot re-execute is type of Script object");
RELEASE_ASSERT_NOT_REACHED();
}
m_topCodeBlock = state.context()->scriptParser().initializeScript(m_sourceCode, m_src, m_moduleData).script->m_topCodeBlock;
}
if (isModule()) {
return executeModule(state, nullptr);
}
ExecutionState newState(context(), state.stackLimit());
ExecutionState* codeExecutionState = &newState;
EnvironmentRecord* globalRecord = new GlobalEnvironmentRecord(state, m_topCodeBlock, context()->globalObject(), &context()->globalDeclarativeRecord(), &context()->globalDeclarativeStorage());
LexicalEnvironment* globalLexicalEnvironment = new LexicalEnvironment(globalRecord, nullptr);
newState.setLexicalEnvironment(globalLexicalEnvironment, m_topCodeBlock->isStrict());
if (inStrictMode && isExecuteOnEvalFunction) {
// NOTE: ES5 10.4.2.1 eval in strict mode
EnvironmentRecord* newVariableRecord = new DeclarativeEnvironmentRecordNotIndexed(state, true);
ExecutionState* newVariableState = new ExecutionState(context());
newVariableState->setLexicalEnvironment(new LexicalEnvironment(newVariableRecord, globalLexicalEnvironment), m_topCodeBlock->isStrict());
newVariableState->setParent(&newState);
codeExecutionState = newVariableState;
}
const InterpretedCodeBlock::IdentifierInfoVector& vec = m_topCodeBlock->identifierInfos();
size_t len = vec.size();
for (size_t i = 0; i < len; i++) {
// https://www.ecma-international.org/ecma-262/5.1/#sec-10.5
// Step 2. If code is eval code, then let configurableBindings be true.
if (vec[i].m_isVarDeclaration) {
codeExecutionState->lexicalEnvironment()->record()->createBinding(*codeExecutionState, vec[i].m_name, isExecuteOnEvalFunction, vec[i].m_isMutable, true);
}
}
if (!isExecuteOnEvalFunction) {
InterpretedCodeBlock* child = m_topCodeBlock->firstChild();
while (child) {
if (child->isFunctionDeclaration()) {
if (!state.context()->globalObject()->defineOwnProperty(state, child->functionName(),
ObjectPropertyDescriptor(Value(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectStructurePropertyDescriptor::EnumerablePresent)))) {
ErrorObject::throwBuiltinError(state, ErrorObject::Code::SyntaxError, "Identifier '%s' has already been declared", child->functionName());
}
}
child = child->nextSibling();
}
const auto& globalLexicalVector = m_topCodeBlock->blockInfo(0)->m_identifiers;
size_t len = globalLexicalVector.size();
for (size_t i = 0; i < len; i++) {
codeExecutionState->lexicalEnvironment()->record()->createBinding(*codeExecutionState, globalLexicalVector[i].m_name, false, globalLexicalVector[i].m_isMutable, false);
}
}
Value thisValue(context()->globalObject());
size_t literalStorageSize = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.size();
Value* registerFile = (Value*)ALLOCA((m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize + 1 + literalStorageSize + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth()) * sizeof(Value), Value, state);
registerFile[0] = Value();
Value* stackStorage = registerFile + m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize;
stackStorage[0] = thisValue;
Value* literalStorage = stackStorage + 1 + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth();
Value* src = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.data();
for (size_t i = 0; i < literalStorageSize; i++) {
literalStorage[i] = src[i];
}
Value resultValue = ByteCodeInterpreter::interpret(codeExecutionState, m_topCodeBlock->byteCodeBlock(), 0, registerFile);
clearStack<512>();
// we give up program bytecodeblock after first excution for reducing memory usage
m_topCodeBlock->m_byteCodeBlock = nullptr;
return resultValue;
}
// NOTE: eval by direct call
Value Script::executeLocal(ExecutionState& state, Value thisValue, InterpretedCodeBlock* parentCodeBlock, bool isStrictModeOutside, bool isEvalCodeOnFunction)
{
EnvironmentRecord* record;
bool inStrict = false;
if (UNLIKELY(isStrictModeOutside)) {
// NOTE: ES5 10.4.2.1 eval in strict mode
inStrict = true;
record = new DeclarativeEnvironmentRecordNotIndexed(state, true);
} else {
record = state.lexicalEnvironment()->record();
}
const InterpretedCodeBlock::IdentifierInfoVector& vec = m_topCodeBlock->identifierInfos();
size_t vecLen = vec.size();
// test there was let on block scope
LexicalEnvironment* e = state.lexicalEnvironment();
while (e) {
if (e->record()->isDeclarativeEnvironmentRecord() && e->record()->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
break;
}
if (e->record()->isGlobalEnvironmentRecord()) {
break;
}
for (size_t i = 0; i < vecLen; i++) {
if (vec[i].m_isVarDeclaration) {
auto slot = e->record()->hasBinding(state, vec[i].m_name);
if (slot.m_isLexicallyDeclared && slot.m_index != SIZE_MAX) {
ErrorObject::throwBuiltinError(state, ErrorObject::SyntaxError, vec[i].m_name.string(), false, String::emptyString, errorMessage_DuplicatedIdentifier);
}
}
}
e = e->outerEnvironment();
}
EnvironmentRecord* recordToAddVariable = record;
e = state.lexicalEnvironment();
while (!recordToAddVariable->isVarDeclarationTarget()) {
e = e->outerEnvironment();
recordToAddVariable = e->record();
}
for (size_t i = 0; i < vecLen; i++) {
if (vec[i].m_isVarDeclaration) {
recordToAddVariable->createBinding(state, vec[i].m_name, inStrict ? false : true, true);
}
}
LexicalEnvironment* newEnvironment = new LexicalEnvironment(record, state.lexicalEnvironment());
ExecutionState newState(&state, newEnvironment, m_topCodeBlock->isStrict());
size_t stackStorageSize = m_topCodeBlock->totalStackAllocatedVariableSize();
size_t identifierOnStackCount = m_topCodeBlock->identifierOnStackCount();
size_t literalStorageSize = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.size();
Value* registerFile = ALLOCA((m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize + stackStorageSize + literalStorageSize + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth()) * sizeof(Value), Value, state);
registerFile[0] = Value();
Value* stackStorage = registerFile + m_topCodeBlock->byteCodeBlock()->m_requiredRegisterFileSizeInValueSize;
for (size_t i = 0; i < identifierOnStackCount; i++) {
stackStorage[i] = Value();
}
Value* literalStorage = stackStorage + stackStorageSize + m_topCodeBlock->lexicalBlockStackAllocatedIdentifierMaximumDepth();
Value* src = m_topCodeBlock->byteCodeBlock()->m_numeralLiteralData.data();
for (size_t i = 0; i < literalStorageSize; i++) {
literalStorage[i] = src[i];
}
stackStorage[0] = thisValue;
if (isEvalCodeOnFunction && m_topCodeBlock->usesArgumentsObject()) {
AtomicString arguments = state.context()->staticStrings().arguments;
FunctionEnvironmentRecord* fnRecord = nullptr;
{
LexicalEnvironment* env = state.lexicalEnvironment();
while (env) {
if (env->record()->isDeclarativeEnvironmentRecord() && env->record()->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
fnRecord = env->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord();
break;
}
env = env->outerEnvironment();
}
}
FunctionObject* callee = state.resolveCallee();
if (fnRecord->hasBinding(newState, arguments).m_index == SIZE_MAX && callee->isScriptFunctionObject()) {
// FIXME check if formal parameters does not contain a rest parameter, any binding patterns, or any initializers.
bool isMapped = !callee->codeBlock()->hasParameterOtherThanIdentifier() && !inStrict;
callee->asScriptFunctionObject()->generateArgumentsObject(newState, state.argc(), state.argv(), fnRecord, nullptr, isMapped);
}
}
newState.ensureRareData()->m_codeBlock = m_topCodeBlock;
Value resultValue = ByteCodeInterpreter::interpret(&newState, m_topCodeBlock->byteCodeBlock(), 0, registerFile);
clearStack<512>();
return resultValue;
}
}