blob: 5cf781d7cfeac305b3e87ad92fed217e2e03838f [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
namespace Js
{
Var JavascriptReflect::EntryDefineProperty(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.defineProperty"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.defineProperty"));
}
Var propertyKey, attributes;
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.defineProperty"));
}
Var target = args[1];
propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
PropertyRecord const * propertyRecord;
JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord);
attributes = args.Info.Count > 3 ? args[3] : undefinedValue;
PropertyDescriptor propertyDescriptor;
if (!JavascriptOperators::ToPropertyDescriptor(attributes, &propertyDescriptor, scriptContext))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_PropertyDescriptor_Invalid, scriptContext->GetPropertyName(propertyRecord->GetPropertyId())->GetBuffer());
}
// If the object is HostDispatch try to invoke the operation remotely
BOOL defineResult;
if (JavascriptOperators::GetTypeId(target) == TypeIds_HostDispatch)
{
defineResult = RecyclableObject::FromVar(target)->InvokeBuiltInOperationRemotely(EntryDefineProperty, args, nullptr);
}
else
{
defineResult = JavascriptObject::DefineOwnPropertyHelper(RecyclableObject::FromVar(target), propertyRecord->GetPropertyId(), propertyDescriptor, scriptContext, false);
}
return scriptContext->GetLibrary()->GetTrueOrFalse(defineResult);
}
Var JavascriptReflect::EntryDeleteProperty(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.deleteProperty"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.deleteProperty"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.deleteProperty"));
}
Var target = args[1];
Var propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
return JavascriptOperators::OP_DeleteElementI(target, propertyKey, scriptContext);
}
Var JavascriptReflect::EntryGet(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.get"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.get"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.get"));
}
Var target = args[1];
Var propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
Var receiver = args.Info.Count > 3 ? args[3] : target;
return JavascriptOperators::GetElementIHelper(RecyclableObject::FromVar(target), propertyKey, receiver, scriptContext);
}
Var JavascriptReflect::EntryGetOwnPropertyDescriptor(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.getOwnPropertyDescriptor"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.getOwnPropertyDescriptor"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.getOwnPropertyDescriptor"));
}
Var target = args[1];
Var propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
if (JavascriptOperators::GetTypeId(target) == TypeIds_HostDispatch)
{
Var result;
if (RecyclableObject::FromVar(target)->InvokeBuiltInOperationRemotely(EntryGetOwnPropertyDescriptor, args, &result))
{
return result;
}
}
return JavascriptObject::GetOwnPropertyDescriptorHelper(RecyclableObject::FromVar(target), propertyKey, scriptContext);
}
Var JavascriptReflect::EntryGetPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.getPrototypeOf"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.getPrototypeOf"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.getPrototypeOf"));
}
Var target = args[1];
return JavascriptObject::GetPrototypeOf(RecyclableObject::FromVar(target), scriptContext);
}
Var JavascriptReflect::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.has"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.has"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.has"));
}
Var target = args[1];
Var propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
BOOL result = JavascriptOperators::OP_HasItem(target, propertyKey, scriptContext);
return scriptContext->GetLibrary()->GetTrueOrFalse(result);
}
Var JavascriptReflect::EntryIsExtensible(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.issExtensible"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.isExtensible"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.iesExtensible"));
}
Var target = args[1];
RecyclableObject *object = RecyclableObject::FromVar(target);
BOOL isExtensible = object->IsExtensible();
GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
if (isExtensible && globalObject != object && globalObject && (globalObject->ToThis() == object))
{
isExtensible = globalObject->IsExtensible();
}
return scriptContext->GetLibrary()->GetTrueOrFalse(isExtensible);
}
Var JavascriptReflect::EntryOwnKeys(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.ownKeys"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.ownKeys"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.ownKeys"));
}
Var target = args[1];
return JavascriptOperators::GetOwnPropertyKeys(target, scriptContext);
}
Var JavascriptReflect::EntryPreventExtensions(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.preventExtensions"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.preventExtensions"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.preventExtensions"));
}
Var target = args[1];
RecyclableObject* targetObj = RecyclableObject::FromVar(target);
GlobalObject* globalObject = targetObj->GetLibrary()->GetGlobalObject();
if (globalObject != targetObj && globalObject && (globalObject->ToThis() == targetObj))
{
globalObject->PreventExtensions();
}
return scriptContext->GetLibrary()->GetTrueOrFalse(targetObj->PreventExtensions());
}
Var JavascriptReflect::EntrySet(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.set"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.set"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.set"));
}
Var target = args[1];
Var propertyKey = args.Info.Count > 2 ? args[2] : undefinedValue;
Var value = args.Info.Count > 3 ? args[3] : undefinedValue;
Var receiver = args.Info.Count > 4 ? args[4] : target;
target = JavascriptOperators::ToObject(target, scriptContext);
BOOL result = JavascriptOperators::SetElementIHelper(receiver, RecyclableObject::FromVar(target), propertyKey, value, scriptContext, PropertyOperationFlags::PropertyOperation_None);
return scriptContext->GetLibrary()->GetTrueOrFalse(result);
}
Var JavascriptReflect::EntrySetPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.setPrototypeOf"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.setPrototypeOf"));
}
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.setPrototypeOf"));
}
Var target = args[1];
target = JavascriptOperators::ToObject(target, scriptContext);
if (args.Info.Count < 3 || !JavascriptOperators::IsObjectOrNull(args[2]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NotObjectOrNull, _u("Object.setPrototypeOf"));
}
RecyclableObject* newPrototype = RecyclableObject::FromVar(args[2]);
BOOL changeResult = JavascriptObject::ChangePrototype(RecyclableObject::FromVar(target), newPrototype, /*validate*/false, scriptContext);
return scriptContext->GetLibrary()->GetTrueOrFalse(changeResult);
}
Var JavascriptReflect::EntryApply(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.apply"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.apply"));
}
Var target = args.Info.Count > 1 ? args[1] : undefinedValue;
if (!JavascriptConversion::IsCallable(target))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Reflect.apply"));
}
Var thisArgument = args.Info.Count > 2 ? args[2] : undefinedValue;
Var argArray = args.Info.Count > 3 ? args[3] : undefinedValue;
if (!(JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(argArray))))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedArrayLike, _u("Reflect.apply"));
}
return JavascriptFunction::ApplyHelper(RecyclableObject::FromVar(target), thisArgument, argArray, scriptContext);
}
static const int STACK_ARGS_ALLOCA_THRESHOLD = 8;
Var JavascriptReflect::EntryConstruct(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.construct"));
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.construct"));
}
Var target = args.Info.Count > 1 ? args[1] : undefinedValue;
if (!JavascriptOperators::IsConstructor(target))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedConstructor, _u("target"));
}
Var newTarget = nullptr;
if (scriptContext->GetConfig()->IsES6ClassAndExtendsEnabled())
{
if (args.Info.Count > 3)
{
newTarget = args[3];
if (!JavascriptOperators::IsConstructor(newTarget))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedConstructor, _u("newTarget"));
}
}
else
{
newTarget = target;
}
}
RecyclableObject* thisArg = RecyclableObject::FromVar(undefinedValue);
if (newTarget != nullptr)
{
thisArg = JavascriptOperators::CreateFromConstructor(RecyclableObject::FromVar(newTarget), scriptContext);
}
Var argArray = args.Info.Count > 2 ? args[2] : undefinedValue;
if (!(JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(argArray))))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedArrayLike, _u("Reflect.construct"));
}
return JavascriptFunction::ConstructHelper(RecyclableObject::FromVar(target), thisArg, newTarget, argArray, scriptContext);
}
}