Revise function call processes (#347)

* Revise [[call]], [[construct]] as described in spec
* Remove m_homeObject in FunctionObject.
* Implement ScriptClass{Constructor, Method}FunctionObject
    this subclass is used for saving [[homeObject]] and implement [[call]], [[consturct]]
* Add more(NewTargetBinder, ReturnValueBinder) into FunctionObjectProcessCallGenerator
* Remove feCounter in ByteCodeGenerationProcess. in ES6, function expression order & evaluation order can differ
* Add CallSuper ByteCode for interpret `super()` in class constructor
* Remove isOngoingSuperCall in ExecutionState
* Remove BuiltinFunctionObject. that was unnecessary

Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
This commit is contained in:
Patrick Kim 2019-08-06 09:58:06 +09:00 committed by Boram Bae
commit 43f442c560
71 changed files with 1371 additions and 948 deletions

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 2019-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 "ScriptFunctionObject.h"
#include "runtime/ArrayObject.h"
#include "runtime/GeneratorObject.h"
#include "runtime/Context.h"
#include "runtime/ProxyObject.h"
#include "interpreter/ByteCode.h"
#include "interpreter/ByteCodeGenerator.h"
#include "interpreter/ByteCodeInterpreter.h"
#include "runtime/Environment.h"
#include "runtime/EnvironmentRecord.h"
#include "runtime/ErrorObject.h"
#include "runtime/VMInstance.h"
#include "parser/ScriptParser.h"
#include "parser/ast/AST.h"
#include "util/Util.h"
#include "FunctionObjectInlines.h"
namespace Escargot {
// function for derived classes. derived class MUST initlize member variable of FunctionObject.
ScriptFunctionObject::ScriptFunctionObject(ExecutionState& state, size_t defaultSpace)
: FunctionObject(state, defaultSpace)
#ifndef NDEBUG
, m_outerEnvironment((LexicalEnvironment*)SIZE_MAX)
#endif
{
}
ScriptFunctionObject::ScriptFunctionObject(ExecutionState& state, CodeBlock* codeBlock, LexicalEnvironment* outerEnv, bool isConstructor, bool isGenerator)
: FunctionObject(state,
((isConstructor || isGenerator) ? (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3) : (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2)) + (codeBlock->isStrict() ? 2 : 0))
{
m_codeBlock = codeBlock;
m_outerEnvironment = outerEnv;
initStructureAndValues(state, isConstructor, isGenerator);
if (isGenerator) {
Object::setPrototype(state, state.context()->globalObject()->generator());
} else {
Object::setPrototype(state, state.context()->globalObject()->functionPrototype());
}
}
NEVER_INLINE void ScriptFunctionObject::generateByteCodeBlock(ExecutionState& state)
{
Vector<CodeBlock*, GCUtil::gc_malloc_ignore_off_page_allocator<CodeBlock*>>& v = state.context()->compiledCodeBlocks();
auto& currentCodeSizeTotal = state.context()->vmInstance()->compiledByteCodeSize();
if (currentCodeSizeTotal > FUNCTION_OBJECT_BYTECODE_SIZE_MAX) {
currentCodeSizeTotal = 0;
std::vector<CodeBlock*, gc_allocator<CodeBlock*>> codeBlocksInCurrentStack;
ExecutionState* es = &state;
while (es != nullptr) {
auto env = es->lexicalEnvironment();
if (env != nullptr && env->record()->isDeclarativeEnvironmentRecord() && env->record()->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
if (env->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject()->codeBlock()->isInterpretedCodeBlock()) {
InterpretedCodeBlock* cblk = env->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject()->codeBlock()->asInterpretedCodeBlock();
if (cblk->script() && cblk->byteCodeBlock() && std::find(codeBlocksInCurrentStack.begin(), codeBlocksInCurrentStack.end(), cblk) == codeBlocksInCurrentStack.end()) {
codeBlocksInCurrentStack.push_back(cblk);
}
}
}
es = es->parent();
}
for (size_t i = 0; i < v.size(); i++) {
if (std::find(codeBlocksInCurrentStack.begin(), codeBlocksInCurrentStack.end(), v[i]) == codeBlocksInCurrentStack.end()) {
v[i]->m_byteCodeBlock = nullptr;
}
}
v.clear();
v.resizeWithUninitializedValues(codeBlocksInCurrentStack.size());
for (size_t i = 0; i < codeBlocksInCurrentStack.size(); i++) {
v[i] = codeBlocksInCurrentStack[i];
currentCodeSizeTotal += v[i]->m_byteCodeBlock->memoryAllocatedSize();
}
}
ASSERT(!m_codeBlock->hasCallNativeFunctionCode());
volatile int sp;
size_t currentStackBase = (size_t)&sp;
#ifdef STACK_GROWS_DOWN
size_t stackRemainApprox = STACK_LIMIT_FROM_BASE - (state.stackBase() - currentStackBase);
#else
size_t stackRemainApprox = STACK_LIMIT_FROM_BASE - (currentStackBase - state.stackBase());
#endif
state.context()->scriptParser().generateFunctionByteCode(state, m_codeBlock->asInterpretedCodeBlock(), stackRemainApprox);
v.pushBack(m_codeBlock);
currentCodeSizeTotal += m_codeBlock->m_byteCodeBlock->memoryAllocatedSize();
}
Value ScriptFunctionObject::call(ExecutionState& state, const Value& thisValue, const size_t argc, NULLABLE Value* argv)
{
ASSERT(!codeBlock()->hasCallNativeFunctionCode());
return FunctionObjectProcessCallGenerator::processCall<ScriptFunctionObject, false, FunctionObjectThisValueBinder, FunctionObjectNewTargetBinder, FunctionObjectReturnValueBinder>(state, this, thisValue, argc, argv, nullptr);
}
class ScriptFunctionObjectObjectThisValueBinderWithConstruct {
public:
Value operator()(ExecutionState& calleeState, FunctionObject* self, const Value& thisArgument, bool isStrict)
{
ASSERT(thisArgument.isObject());
return thisArgument;
}
};
class ScriptFunctionObjectReturnValueBinderWithConstruct {
public:
Value operator()(ExecutionState& state, ScriptFunctionObject* self, const Value& interpreterReturnValue, const Value& thisArgument, FunctionEnvironmentRecord* record)
{
// Let result be OrdinaryCallEvaluateBody(F, argumentsList).
const Value& result = interpreterReturnValue;
// If result.[[type]] is return, then
// If Type(result.[[value]]) is Object, return NormalCompletion(result.[[value]]).
if (result.isObject()) {
return result;
}
// If kind is "base", return NormalCompletion(thisArgument).
// -> kind is always `base`
return thisArgument;
}
};
class ScriptFunctionObjectNewTargetBinderWithConstruct {
public:
void operator()(ExecutionState& calleeState, FunctionObject* self, Object* newTarget, FunctionEnvironmentRecord* record)
{
// TODO don't do this if possible
record->setNewTarget(newTarget);
}
};
Object* ScriptFunctionObject::construct(ExecutionState& state, const size_t argc, NULLABLE Value* argv, Object* newTarget)
{
ASSERT(!codeBlock()->hasCallNativeFunctionCode());
// Assert: Type(newTarget) is Object.
ASSERT(newTarget->isObject());
ASSERT(newTarget->isConstructor());
// Let kind be Fs [[ConstructorKind]] internal slot.
ASSERT(constructorKind() == ConstructorKind::Base); // this is always `Base` because we define ScriptClassConsturctor::construct
CodeBlock* cb = codeBlock();
FunctionObject* constructor = this;
// Let thisArgument be OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
// OrdinaryCreateFromConstructor -> Let proto be GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
// OrdinaryCreateFromConstructor -> GetPrototypeFromConstructor -> Let proto be Get(constructor, "prototype").
Value proto = newTarget->get(state, ObjectPropertyName(state.context()->staticStrings().prototype)).value(state, newTarget);
// OrdinaryCreateFromConstructor -> GetPrototypeFromConstructor -> If Type(proto) is not Object, then
// OrdinaryCreateFromConstructor -> GetPrototypeFromConstructor -> Let realm be GetFunctionRealm(constructor).
// OrdinaryCreateFromConstructor -> GetPrototypeFromConstructor -> ReturnIfAbrupt(realm).
// OrdinaryCreateFromConstructor -> GetPrototypeFromConstructor -> Let proto be realms intrinsic object named intrinsicDefaultProto.
if (!proto.isObject()) {
proto = codeBlock()->context()->globalObject()->objectPrototype();
}
Object* thisArgument = new Object(state);
// Set the [[Prototype]] internal slot of obj to proto.
thisArgument->setPrototype(state, proto);
// ReturnIfAbrupt(thisArgument).
return FunctionObjectProcessCallGenerator::processCall<ScriptFunctionObject, true, ScriptFunctionObjectObjectThisValueBinderWithConstruct, ScriptFunctionObjectNewTargetBinderWithConstruct, ScriptFunctionObjectReturnValueBinderWithConstruct>(state, this, Value(thisArgument), argc, argv, newTarget).asObject();
}
void ScriptFunctionObject::generateArgumentsObject(ExecutionState& state, FunctionEnvironmentRecord* fnRecord, Value* stackStorage, bool isMapped)
{
AtomicString arguments = state.context()->staticStrings().arguments;
if (fnRecord->isFunctionEnvironmentRecordNotIndexed()) {
auto result = fnRecord->hasBinding(state, arguments);
if (UNLIKELY(result.m_index == SIZE_MAX)) {
fnRecord->createBinding(state, arguments, false, true);
result = fnRecord->hasBinding(state, arguments);
}
fnRecord->initializeBinding(state, arguments, fnRecord->createArgumentsObject(state, isMapped));
} else {
const InterpretedCodeBlock::IdentifierInfoVector& v = fnRecord->functionObject()->codeBlock()->asInterpretedCodeBlock()->identifierInfos();
for (size_t i = 0; i < v.size(); i++) {
if (v[i].m_name == arguments) {
if (v[i].m_needToAllocateOnStack) {
stackStorage[v[i].m_indexForIndexedStorage] = fnRecord->createArgumentsObject(state, isMapped);
} else {
ASSERT(fnRecord->isFunctionEnvironmentRecordOnHeap());
((FunctionEnvironmentRecordOnHeap*)fnRecord)->m_heapStorage[v[i].m_indexForIndexedStorage] = fnRecord->createArgumentsObject(state, isMapped);
}
break;
}
}
}
}
void ScriptFunctionObject::generateRestParameter(ExecutionState& state, FunctionEnvironmentRecord* record, Value* parameterStorageInStack, const size_t argc, Value* argv)
{
ArrayObject* newArray;
size_t parameterLen = (size_t)m_codeBlock->parameterCount();
if (argc > parameterLen) {
size_t arrLen = argc - parameterLen;
newArray = new ArrayObject(state, (double)arrLen);
for (size_t i = 0; i < arrLen; i++) {
newArray->setIndexedProperty(state, Value(i), argv[parameterLen + i]);
}
} else {
newArray = new ArrayObject(state);
}
InterpretedCodeBlock::FunctionParametersInfo lastInfo = const_cast<InterpretedCodeBlock::FunctionParametersInfoVector&>(m_codeBlock->asInterpretedCodeBlock()->parametersInfomation()).back();
if (!m_codeBlock->canUseIndexedVariableStorage()) {
record->initializeBinding(state, lastInfo.m_name, Value(newArray));
} else if (lastInfo.m_isHeapAllocated) {
ASSERT(record->isFunctionEnvironmentRecordOnHeap());
((FunctionEnvironmentRecordOnHeap*)record)->m_heapStorage[lastInfo.m_index] = newArray;
} else {
parameterStorageInStack[lastInfo.m_index] = newArray;
}
}
}