escargot/src/runtime/ArgumentsObject.cpp
Seonghyun Kim 9e667ccdd2 Allow to save Int32 convertible double value as double in Value for performance
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2022-05-20 09:03:17 +09:00

373 lines
16 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.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
*/
#include "Escargot.h"
#include "ArgumentsObject.h"
#include "Context.h"
#include "EnvironmentRecord.h"
#include "Environment.h"
#include "ScriptFunctionObject.h"
namespace Escargot {
static Value ArgumentsObjectNativeGetter(ExecutionState& state, Object* self, FunctionEnvironmentRecord* targetRecord, InterpretedCodeBlock* codeBlock, AtomicString name)
{
InterpretedCodeBlock::IdentifierInfo info = codeBlock->identifierInfos()[codeBlock->findVarName(name)];
ASSERT(!info.m_needToAllocateOnStack);
if (info.m_indexForIndexedStorage == SIZE_MAX) {
return targetRecord->getBindingValue(state, name).m_value;
}
return targetRecord->getHeapValueByIndex(state, info.m_indexForIndexedStorage);
}
static void ArgumentsObjectNativeSetter(ExecutionState& state, Object* self, const Value& setterInputData, FunctionEnvironmentRecord* targetRecord, InterpretedCodeBlock* codeBlock, AtomicString name)
{
InterpretedCodeBlock::IdentifierInfo info = codeBlock->identifierInfos()[codeBlock->findVarName(name)];
ASSERT(!info.m_needToAllocateOnStack);
if (info.m_indexForIndexedStorage == SIZE_MAX) {
targetRecord->setMutableBinding(state, name, setterInputData);
return;
}
targetRecord->setHeapValueByIndex(state, info.m_indexForIndexedStorage, setterInputData);
}
ArgumentsObject::ArgumentsObject(ExecutionState& state, Object* proto, ScriptFunctionObject* sourceFunctionObject, size_t argc, Value* argv, FunctionEnvironmentRecord* environmentRecordWillArgumentsObjectBeLocatedIn, bool isMapped)
: DerivedObject(state, proto, ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3)
, m_targetRecord(environmentRecordWillArgumentsObjectBeLocatedIn->isFunctionEnvironmentRecordOnStack() ? nullptr : environmentRecordWillArgumentsObjectBeLocatedIn)
, m_sourceFunctionObject(sourceFunctionObject)
, m_argc((argc << 1) | 1)
{
// Let len be the number of elements in argumentsList.
int len = argc;
m_parameterMap.resizeWithUninitializedValues(0, len);
if (isMapped) {
InterpretedCodeBlock* codeBlock = m_sourceFunctionObject->interpretedCodeBlock();
m_structure = state.context()->defaultStructureForMappedArgumentsObject();
// https://www.ecma-international.org/ecma-262/6.0/#sec-createmappedargumentsobject
// Let numberOfParameters be the number of elements in parameterNames
int numberOfParameters = codeBlock->parameterNames().size();
// all parameters are pure identifier nodes, so function length is same as the number of parameters
ASSERT(numberOfParameters == codeBlock->functionLength());
// Let index be 0.
int index = 0;
// Repeat while index < len ,
while (index < len) {
// Let val be argumentsList[index].
Value val = argv[index];
// Perform CreateDataProperty(obj, ToString(index), val).
m_parameterMap[index].first = val;
m_parameterMap[index].second = AtomicString();
// Let index be index + 1
index++;
}
// Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER] = Value(len);
// Let map be ObjectCreate(null). m_parameterMap already allocated
// Let mappedNames be an empty List.
std::vector<AtomicString> mappedNames;
// Let index be numberOfParameters 1.
index = numberOfParameters - 1;
// Repeat while index ≥ 0 ,
while (index >= 0) {
// Let name be parameterNames[index].
AtomicString name = codeBlock->parameterNames()[index];
// If name is not an element of mappedNames, then
if (std::find(mappedNames.begin(), mappedNames.end(), name) == mappedNames.end()) {
// Add name as an element of the list mappedNames.
mappedNames.push_back(name);
// If index < len, then
if (index < len) {
m_parameterMap[index].first = Value();
m_parameterMap[index].second = name;
}
}
// Let index be index 1
index--;
}
// Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:%ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 1] = state.context()->globalObject()->arrayPrototypeValues();
// Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2] = sourceFunctionObject;
} else {
m_structure = state.context()->defaultStructureForUnmappedArgumentsObject();
// https://www.ecma-international.org/ecma-262/6.0/#sec-createunmappedargumentsobject
// Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER] = Value(len);
// Let index be 0.
int index = 0;
// Repeat while index < len,
while (index < len) {
// Let val be argumentsList[index].
Value val = argv[index];
// Perform CreateDataProperty(obj, ToString(index), val).
m_parameterMap[index].first = val;
m_parameterMap[index].second = AtomicString();
// Let index be index + 1
index++;
}
// Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:%ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 1] = state.context()->globalObject()->arrayPrototypeValues();
auto thrower = state.context()->globalObject()->throwerGetterSetterData();
// Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
m_values[ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2] = Value(thrower);
}
}
ObjectHasPropertyResult ArgumentsObject::hasProperty(ExecutionState& state, const ObjectPropertyName& P)
{
size_t index = P.tryToUseAsIndexProperty();
if (LIKELY(!isModifiedArgument(index) && isMatchedArgument(index))) {
return ObjectHasPropertyResult(ObjectGetResult(getIndexedPropertyValueQuickly(state, index), true, true, true));
}
return Object::hasProperty(state, P);
}
ObjectGetResult ArgumentsObject::getOwnProperty(ExecutionState& state, const ObjectPropertyName& P)
{
size_t index = P.tryToUseAsIndexProperty();
if (LIKELY(!isModifiedArgument(index) && isMatchedArgument(index))) {
return ObjectGetResult(getIndexedPropertyValueQuickly(state, index), true, true, true);
}
ObjectGetResult result = Object::getOwnProperty(state, P);
if (isMatchedArgument(index)) {
ASSERT(result.hasValue());
return ObjectGetResult(getIndexedPropertyValueQuickly(state, index), result.isWritable(), result.isEnumerable(), result.isConfigurable());
}
return result;
}
bool ArgumentsObject::defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc)
{
size_t index = P.tryToUseAsIndexProperty();
ObjectPropertyDescriptor newDesc(desc);
if (isMatchedArgument(index)) {
if (desc.isValuePresent()) {
setIndexedPropertyValueQuickly(state, index, desc.value());
}
if (desc.isWritable() && desc.isConfigurable() && desc.isEnumerable() && !desc.isAccessorDescriptor() && !isModifiedArgument(index)) {
return true;
}
if (!isModifiedArgument(index)) {
// first, initialize property on Object's property slot
// we should unset extensible limit
setModifiedArgument(index);
bool extensibleBefore = isExtensible(state);
if (!extensibleBefore) {
rareData()->m_isExtensible = true;
}
ObjectPropertyDescriptor initDesc(getIndexedPropertyValueQuickly(state, index), ObjectPropertyDescriptor::AllPresent);
DerivedObject::defineOwnProperty(state, P, initDesc);
if (!extensibleBefore) {
rareData()->m_isExtensible = false;
}
}
if ((desc.isWritablePresent() && !desc.isWritable()) || desc.isAccessorDescriptor()) {
if (!desc.isAccessorDescriptor()) {
newDesc.setValue(getIndexedPropertyValueQuickly(state, index));
}
// unmap arguments
m_parameterMap[index].first = Value(Value::EmptyValue);
setModifiedArgument(index);
}
}
return DerivedObject::defineOwnProperty(state, P, newDesc);
}
bool ArgumentsObject::deleteOwnProperty(ExecutionState& state, const ObjectPropertyName& P)
{
size_t index = P.tryToUseAsIndexProperty();
bool hasPropertyInObject = isModifiedArgument(index) || !isMatchedArgument(index);
bool deleted = true;
if (hasPropertyInObject) {
deleted = Object::deleteOwnProperty(state, P);
}
if (deleted) {
if (isMatchedArgument(index)) {
m_parameterMap[index].first = Value(Value::EmptyValue);
}
setModifiedArgument(index);
}
return deleted;
}
void ArgumentsObject::enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey)
{
for (size_t i = 0; i < argc(); i++) {
if (isMatchedArgument(i)) {
if (!isModifiedArgument(i)) {
if (!callback(state, this, ObjectPropertyName(state, Value(i)), ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::AllPresent), data)) {
return;
}
}
}
}
Object::enumeration(state, callback, data, shouldSkipSymbolKey);
}
ObjectGetResult ArgumentsObject::getIndexedProperty(ExecutionState& state, const Value& property, const Value& receiver)
{
size_t index = property.tryToUseAsIndexProperty(state);
if (LIKELY(!isModifiedArgument(index) && isMatchedArgument(index))) {
return ObjectGetResult(getIndexedPropertyValueQuickly(state, index), true, true, true);
}
ObjectGetResult result = get(state, ObjectPropertyName(state, property), receiver);
if (isMatchedArgument(index)) {
ASSERT(result.hasValue());
return ObjectGetResult(getIndexedPropertyValueQuickly(state, index), result.isWritable(), result.isEnumerable(), result.isConfigurable());
}
return result;
}
Value ArgumentsObject::getIndexedPropertyValue(ExecutionState& state, const Value& property, const Value& receiver)
{
size_t index = property.tryToUseAsIndexProperty(state);
if (LIKELY(!isModifiedArgument(index) && isMatchedArgument(index))) {
return getIndexedPropertyValueQuickly(state, index);
}
if (isMatchedArgument(index)) {
return getIndexedPropertyValueQuickly(state, index);
}
return get(state, ObjectPropertyName(state, property), receiver).value(state, property);
}
ObjectHasPropertyResult ArgumentsObject::hasIndexedProperty(ExecutionState& state, const Value& propertyName)
{
size_t index = propertyName.tryToUseAsIndexProperty(state);
if (LIKELY(!isModifiedArgument(index) && isMatchedArgument(index))) {
return ObjectHasPropertyResult(ObjectGetResult(getIndexedPropertyValueQuickly(state, index), true, true, true));
}
if (isMatchedArgument(index)) {
ObjectGetResult result = Object::getOwnProperty(state, ObjectPropertyName(state, propertyName));
ASSERT(result.hasValue());
return ObjectHasPropertyResult(ObjectGetResult(getIndexedPropertyValueQuickly(state, index), result.isWritable(), result.isEnumerable(), result.isConfigurable()));
}
return hasProperty(state, ObjectPropertyName(state, propertyName));
}
bool ArgumentsObject::set(ExecutionState& state, const ObjectPropertyName& propertyName, const Value& v, const Value& receiver)
{
if (UNLIKELY(!receiver.isObject() || receiver.asObject() != this)) {
return Object::set(state, propertyName, v, receiver);
}
size_t index = propertyName.tryToUseAsIndexProperty();
if (LIKELY(isMatchedArgument(index))) {
setIndexedPropertyValueQuickly(state, index, v);
return true;
}
return Object::set(state, propertyName, v, receiver);
}
bool ArgumentsObject::setIndexedProperty(ExecutionState& state, const Value& property, const Value& value, const Value& receiver)
{
size_t index = property.tryToUseAsIndexProperty(state);
if (LIKELY(isMatchedArgument(index))) {
setIndexedPropertyValueQuickly(state, index, value);
return true;
}
return Object::set(state, ObjectPropertyName(state, property), value, receiver);
}
Value ArgumentsObject::getIndexedPropertyValueQuickly(ExecutionState& state, size_t index)
{
ASSERT((index != Value::InvalidIndexPropertyValue) && (index < argc()));
ASSERT(!m_parameterMap[index].first.isEmpty());
if (m_parameterMap[index].second.string()->length()) {
// when there was parameter on source function,
// source function must uses heap env record
ASSERT(m_targetRecord != nullptr);
return ArgumentsObjectNativeGetter(state, this, m_targetRecord, m_sourceFunctionObject->interpretedCodeBlock(), m_parameterMap[index].second);
} else {
return m_parameterMap[index].first;
}
}
void ArgumentsObject::setIndexedPropertyValueQuickly(ExecutionState& state, size_t index, const Value& value)
{
ASSERT((index != Value::InvalidIndexPropertyValue) && (index < argc()));
ASSERT(!m_parameterMap[index].first.isEmpty());
if (m_parameterMap[index].second.string()->length()) {
// when there was parameter on source function,
// source function must uses heap env record
ASSERT(m_targetRecord != nullptr);
ArgumentsObjectNativeSetter(state, this, value, m_targetRecord, m_sourceFunctionObject->interpretedCodeBlock(), m_parameterMap[index].second);
} else {
m_parameterMap[index].first = value;
}
}
bool ArgumentsObject::isModifiedArgument(size_t index)
{
if (!modifiedArguments()) {
return false;
}
if (LIKELY(index != Value::InvalidIndexPropertyValue) && index < argc()) {
return m_modifiedArguments->m_modified[index];
}
return false;
}
void ArgumentsObject::setModifiedArgument(size_t index)
{
if (LIKELY(index != Value::InvalidIndexPropertyValue)) {
if (modifiedArguments() == nullptr) {
size_t c = argc();
m_modifiedArguments = new ModifiedArguments(c);
}
if (index < argc()) {
m_modifiedArguments->m_modified[index] = true;
}
}
}
bool ArgumentsObject::isMatchedArgument(size_t index)
{
if (LIKELY(index != Value::InvalidIndexPropertyValue) && index < argc()) {
return !m_parameterMap[index].first.isEmpty();
}
return false;
}
} // namespace Escargot