/* * Copyright (c) 2022-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.1 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 */ #ifndef __EscargotScriptSimpleFunctionObject__ #define __EscargotScriptSimpleFunctionObject__ #include "runtime/ScriptFunctionObject.h" #include "runtime/Environment.h" #include "runtime/EnvironmentRecord.h" #include "runtime/ErrorObject.h" #include "runtime/VMInstance.h" #include "interpreter/ByteCode.h" #include "interpreter/ByteCodeGenerator.h" #include "interpreter/ByteCodeInterpreter.h" namespace Escargot { // ScriptSimpleFunctionObject currently supports only 4 registerFileSize (4, 8, 16, 24) template class ScriptSimpleFunctionObject : public ScriptFunctionObject { friend class Global; protected: ScriptSimpleFunctionObject() // ctor for reading tag : ScriptFunctionObject() { } virtual bool isScriptSimpleFunctionObject() const override { return true; } virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv) override { CHECK_STACK_OVERFLOW(state); ASSERT(codeBlock()->isInterpretedCodeBlock()); InterpretedCodeBlock* codeBlock = interpretedCodeBlock(); // prepare ByteCodeBlock if needed if (UNLIKELY(codeBlock->byteCodeBlock() == nullptr)) { generateByteCodeBlock(state); } ByteCodeBlock* blk = codeBlock->byteCodeBlock(); Context* ctx = codeBlock->context(); const size_t registerSize = blk->m_requiredOperandRegisterNumber; const size_t programStart = reinterpret_cast(blk->m_code.data()); #if !defined(NDEBUG) const size_t stackStorageSize = codeBlock->totalStackAllocatedVariableSize(); const size_t literalStorageSize = blk->m_numeralLiteralData.size(); ASSERT(codeBlock->isStrict() == isStrict); ASSERT(blk->m_requiredOperandRegisterNumber + stackStorageSize + literalStorageSize <= registerFileSize); #endif // prepare env, ec ASSERT(codeBlock->canAllocateEnvironmentOnStack()); FunctionEnvironmentRecordOnStack record(this); LexicalEnvironment lexEnv(&record, outerEnvironment() #ifndef NDEBUG , false #endif ); // keep ByteCodeBlock pointer in registerFileBuffer char registerFileBuffer[sizeof(Value) * registerFileSize + sizeof(size_t)]; Value* registerFile = reinterpret_cast(registerFileBuffer); memcpy(registerFileBuffer + sizeof(Value) * registerFileSize, &blk, sizeof(size_t)); Value* stackStorage = registerFile + registerSize; ExecutionState newState(ctx, &state, &lexEnv, argc, argv, isStrict); if (isStrict) { stackStorage[0] = thisValue; } else { if (thisValue.isUndefinedOrNull()) { stackStorage[0] = newState.context()->globalObjectProxy(); } else { stackStorage[0] = thisValue.toObject(newState); } } #if defined(ENABLE_TCO) const Value returnValue = Interpreter::interpret(&newState, blk, programStart, registerFile); if (shouldClearStack) { clearStack<512>(); } if (UNLIKELY(newState.inTCO())) { // callee has been called in tail call, so reset the argument buffer memset(Interpreter::tcoBuffer, 0, sizeof(Value) * TCO_ARGUMENT_COUNT_LIMIT); } return returnValue; #else if (shouldClearStack) { const Value returnValue = Interpreter::interpret(&newState, blk, programStart, registerFile); clearStack<512>(); return returnValue; } else { return Interpreter::interpret(&newState, blk, programStart, registerFile); } #endif } virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget) override { // Assert: Type(newTarget) is Object. ASSERT(newTarget->isObject()); ASSERT(newTarget->isConstructor()); // Let kind be F’s [[ConstructorKind]] internal slot. ASSERT(constructorKind() == ConstructorKind::Base); // this is always `Base` because we define ScriptClassConsturctor::construct CHECK_STACK_OVERFLOW(state); // Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%"). Object* proto = Object::getPrototypeFromConstructor(state, newTarget, [](ExecutionState& state, Context* constructorRealm) -> Object* { return constructorRealm->globalObject()->objectPrototype(); }); // Set the [[Prototype]] internal slot of obj to proto. Object* thisArgument = new Object(state, proto); ASSERT(codeBlock()->isInterpretedCodeBlock()); InterpretedCodeBlock* codeBlock = interpretedCodeBlock(); // prepare ByteCodeBlock if needed if (UNLIKELY(codeBlock->byteCodeBlock() == nullptr)) { generateByteCodeBlock(state); } ByteCodeBlock* blk = codeBlock->byteCodeBlock(); Context* ctx = codeBlock->context(); const size_t registerSize = blk->m_requiredOperandRegisterNumber; #if !defined(NDEBUG) const size_t stackStorageSize = codeBlock->totalStackAllocatedVariableSize(); const size_t literalStorageSize = blk->m_numeralLiteralData.size(); ASSERT(codeBlock->isStrict() == isStrict); ASSERT(blk->m_requiredOperandRegisterNumber + stackStorageSize + literalStorageSize <= registerFileSize); #endif // prepare env, ec ASSERT(codeBlock->canAllocateEnvironmentOnStack()); FunctionEnvironmentRecordOnStack record(this); LexicalEnvironment lexEnv(&record, outerEnvironment() #ifndef NDEBUG , false #endif ); // keep ByteCodeBlock pointer in registerFileBuffer char registerFileBuffer[sizeof(Value) * registerFileSize + sizeof(size_t)]; Value* registerFile = reinterpret_cast(registerFileBuffer); memcpy(registerFileBuffer + sizeof(Value) * registerFileSize, &blk, sizeof(size_t)); Value* stackStorage = registerFile + registerSize; ExecutionState newState(ctx, &state, &lexEnv, argc, argv, isStrict); stackStorage[0] = thisArgument; record.setNewTarget(newTarget); const Value returnValue = Interpreter::interpret(&newState, blk, reinterpret_cast(blk->m_code.data()), registerFile); if (shouldClearStack) { clearStack<512>(); } return returnValue.isObject() ? returnValue : thisArgument; } }; COMPILE_ASSERT(sizeof(ScriptSimpleFunctionObject<>) == sizeof(ScriptFunctionObject), ""); } // namespace Escargot #endif