mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
373 lines
16 KiB
C++
373 lines
16 KiB
C++
/*
|
||
* 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
|