blob: 9b9912038ea89cab89978c2ad1ce6480c279798b [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#if DBG
EXTERN_C Js::JavascriptMethod checkCodeGenThunk;
#endif
namespace Js
{
struct PossibleAsmJsReturnValues
{
union
{
int retIntVal;
int64 retInt64Val;
float retFloatVal;
double retDoubleVal;
AsmJsSIMDValue retSimdVal;
};
};
#if _M_X64
extern "C" Var amd64_CallFunction(RecyclableObject *function, JavascriptMethod entryPoint, CallInfo callInfo, uint argc, Var *argv);
#endif
class JavascriptFunction : public DynamicObject
{
private:
static PropertyId const specialPropertyIds[];
// Need a constructor cache on every function (script and native) to avoid extra checks on the fast path, if the function isn't fixed.
Field(ConstructorCache*) constructorCache;
Field(bool) isJsBuiltInCode;
protected:
Field(FunctionInfo *) functionInfo; // Underlying function
DEFINE_VTABLE_CTOR(JavascriptFunction, DynamicObject);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptFunction);
private:
// noinline, we want to use own stack frame.
_NOINLINE JavascriptFunction* FindCaller(BOOL* foundThis, JavascriptFunction* nullValue, ScriptContext* requestContext);
BOOL GetCallerProperty(Var originalInstance, Var* value, ScriptContext* requestContext);
BOOL GetArgumentsProperty(Var originalInstance, Var* value, ScriptContext* requestContext);
bool GetPropertyBuiltIns(Var originalInstance, PropertyId propertyId, Var* value, ScriptContext* requestContext, BOOL* result);
bool GetSetterBuiltIns(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext, DescriptorFlags* descriptorFlags);
void InvalidateConstructorCacheOnPrototypeChange();
bool GetSourceStringName(JavascriptString** name) const;
static const charcount_t DIAG_MAX_FUNCTION_STRING = 256;
protected:
enum class FunctionKind { Normal, Generator, Async };
static Var NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, FunctionKind functionKind = FunctionKind::Normal);
JavascriptFunction(DynamicType * type);
public:
JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo);
JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo, ConstructorCache* cache);
class EntryInfo
{
public:
static FunctionInfo NewInstance;
static FunctionInfo PrototypeEntryPoint;
static FunctionInfo Apply;
static FunctionInfo Bind;
static FunctionInfo Call;
static FunctionInfo ToString;
static FunctionInfo SymbolHasInstance;
static FunctionInfo NewAsyncFunctionInstance;
};
static const int numberLinesPrependedToAnonymousFunction = 1;
static DWORD GetFunctionInfoOffset() { return offsetof(JavascriptFunction, functionInfo); }
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewInstanceRestrictedMode(RecyclableObject* function, CallInfo callInfo, ...);
static Var PrototypeEntryPoint(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryApply(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryBind(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryCall(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySymbolHasInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewAsyncFunctionInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewAsyncFunctionInstanceRestrictedMode(RecyclableObject* function, CallInfo callInfo, ...);
static bool Is(Var aValue);
static JavascriptFunction* FromVar(Var aValue);
Var CallFunction(Arguments args);
Var CallRootFunction(Arguments args, ScriptContext * scriptContext, bool inScript);
#ifdef ASMJS_PLAT
template <typename T>
static T CallAsmJsFunction(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv);
#endif
static PossibleAsmJsReturnValues CallAsmJsFunctionX86Thunk(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv);
template <bool isConstruct>
static Var CalloutHelper(RecyclableObject* function, Var thisArg, Var overridingNewTarget, Var argArray, ScriptContext* scriptContext);
static Var ApplyHelper(RecyclableObject* function, Var thisArg, Var argArray, ScriptContext* scriptContext);
static Var ConstructHelper(RecyclableObject* function, Var thisArg, Var overridingNewTarget, Var argArray, ScriptContext* scriptContext);
static Var CallRootFunctionInScript(JavascriptFunction* func, Arguments args);
static Var CallAsConstructor(Var v, Var overridingNewTarget, Arguments args, ScriptContext* scriptContext, const Js::AuxArray<uint32> *spreadIndices = nullptr);
static Var FinishConstructor(const Var constructorReturnValue, Var newObject, JavascriptFunction *const function);
#if DBG
static void CheckValidDebugThunk(ScriptContext* scriptContext, RecyclableObject *function);
#endif
template <bool doStackProbe>
static Var CallFunction(RecyclableObject* obj, JavascriptMethod entryPoint, Arguments args);
static Var CallRootFunction(RecyclableObject* obj, Arguments args, ScriptContext * scriptContext, bool inScript);
static Var CallRootFunctionInternal(RecyclableObject* obj, Arguments args, ScriptContext * scriptContext, bool inScript);
static Var CallSpreadFunction(RecyclableObject* obj, Arguments args, const Js::AuxArray<uint32> *spreadIndices);
static uint32 GetSpreadSize(const Arguments args, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext);
static void SpreadArgs(const Arguments args, Arguments& destArgs, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext);
static Var EntrySpreadCall(const Js::AuxArray<uint32> *spreadIndices, RecyclableObject* function, CallInfo callInfo, ...);
static void CheckAlignment();
static BOOL IsNativeAddress(ScriptContext * scriptContext, void * codeAddr);
static Var DeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...);
static JavascriptMethod DeferredParse(ScriptFunction** function);
static JavascriptMethod DeferredParseCore(ScriptFunction** function, BOOL &fParsed);
static void ReparseAsmJsModule(ScriptFunction ** function);
static Var DeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...);
static JavascriptMethod DeferredDeserialize(ScriptFunction* function);
static BYTE GetOffsetOfFunctionInfo()
{
CompileAssert(offsetof(JavascriptFunction, functionInfo) <= UCHAR_MAX);
return offsetof(JavascriptFunction, functionInfo);
}
static uint32 GetOffsetOfConstructorCache() { return offsetof(JavascriptFunction, constructorCache); };
static JavascriptString* GetNativeFunctionDisplayString(ScriptContext *scriptContext, JavascriptString *name);
static JavascriptString* GetLibraryCodeDisplayString(ScriptContext* scriptContext, PCWSTR displayName);
template <class StringHelper, class String, class ScriptContext>
static String GetNativeFunctionDisplayStringCommon(ScriptContext* scriptContext, String name);
template <class StringHelper, class String, class ScriptContext>
static String GetLibraryCodeDisplayStringCommon(ScriptContext* scriptContext, PCWSTR displayName);
FunctionInfo * GetFunctionInfo() const { return functionInfo; }
void SetFunctionInfo(FunctionInfo *info) { functionInfo = info; }
FunctionProxy * GetFunctionProxy() const;
ParseableFunctionInfo * GetParseableFunctionInfo() const;
DeferDeserializeFunctionInfo * GetDeferDeserializeFunctionInfo() const;
FunctionBody * GetFunctionBody() const;
virtual JavascriptString* GetDisplayNameImpl() const;
JavascriptString* DisplayNameHelper(const char16* name, charcount_t length) const;
JavascriptString* GetDisplayName() const;
bool GetFunctionName(JavascriptString** name) const;
bool IsLibraryCode() const;
BOOL IsScriptFunction() const;
virtual Var GetSourceString() const { return nullptr; }
virtual Var EnsureSourceString();
virtual BOOL IsExternalFunction() { return FALSE; }
virtual BOOL IsWinRTFunction() { return FALSE; }
BOOL IsStrictMode() const;
BOOL IsLambda() const;
virtual inline BOOL IsConstructor() const;
bool HasRestrictedProperties() const;
void SetIsJsBuiltInCode();
bool IsJsBuiltIn();
ConstructorCache* GetConstructorCache() { Assert(this->constructorCache != nullptr); return this->constructorCache; }
ConstructorCache* EnsureValidConstructorCache();
void ResetConstructorCacheToDefault();
virtual bool HasReadOnlyPropertiesInvisibleToTypeHandler() override { return true; }
virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId) override;
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
virtual BOOL SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any) override;
virtual BOOL GetAccessors(PropertyId propertyId, Var *getter, Var *setter, ScriptContext * requestContext) override;
virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual BOOL IsConfigurable(PropertyId propertyId) override;
virtual BOOL IsEnumerable(PropertyId propertyId) override;
virtual BOOL IsWritable(PropertyId propertyId) override;
virtual BOOL GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext) override;
virtual uint GetSpecialPropertyCount() const override;
virtual PropertyId const * GetSpecialPropertyIds() const override;
virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) override;
virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) override;
virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual Var GetTypeOfString(ScriptContext * requestContext) override;
virtual BOOL HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache = NULL);
static BOOL HasInstance(Var funcPrototype, Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache, JavascriptFunction* function);
// This will be overridden for the BoundFunction
virtual bool IsBoundFunction() const { return false; }
virtual bool IsGeneratorFunction() const { return false; }
void SetEntryPoint(JavascriptMethod method);
#if DBG
void VerifyEntryPoint();
static bool IsBuiltinProperty(Var objectWithProperty, PropertyIds propertyId);
#endif
private:
static int CallRootEventFilter(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo);
#if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
static bool ResumeForOutOfBoundsArrayRefs(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo);
#endif
};
#if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
class ArrayAccessDecoder
{
public:
struct InstructionData
{
public:
bool isLoad : 1;
bool isFloat32 : 1;
bool isFloat64 : 1;
bool isSimd : 1;
bool isInvalidInstr : 1;
BYTE bufferReg = 0;
BYTE dstReg = 0;
uint instrSizeInByte = 0;
uint64 bufferValue = 0;
InstructionData() :isLoad(0), isFloat32(0), isFloat64(0), isSimd(0), isInvalidInstr(0){}
};
struct RexByteValue
{
public:
bool isR : 1;
bool isX : 1;
bool isW : 1;
bool isB : 1;
uint rexValue;
RexByteValue() :isR(0), isX(0), isW(0), isB(0), rexValue(0){}
};
static InstructionData CheckValidInstr(BYTE* &pc, PEXCEPTION_POINTERS exceptionInfo);
};
#endif
//
// ---- implementation shared with diagnostics ----
//
template <class StringHelper, class String, class ScriptContext>
String JavascriptFunction::GetNativeFunctionDisplayStringCommon(ScriptContext* scriptContext, String name)
{
auto library = scriptContext->GetLibrary();
String sourceString;
sourceString = library->CreateStringFromCppLiteral(JS_DISPLAY_STRING_FUNCTION_HEADER); //_u("function ")
sourceString = StringHelper::Concat(sourceString, name);
sourceString = StringHelper::Concat(sourceString, library->CreateStringFromCppLiteral(JS_DISPLAY_STRING_FUNCTION_BODY)); //_u("() { [native code] }")
return sourceString;
}
template <class StringHelper, class String, class ScriptContext>
String JavascriptFunction::GetLibraryCodeDisplayStringCommon(ScriptContext* scriptContext, PCWSTR displayName)
{
String sourceString;
if(wcscmp(displayName, Js::Constants::AnonymousFunction) == 0)
{
sourceString = scriptContext->GetLibrary()->GetFunctionDisplayString();
}
else
{
sourceString = GetNativeFunctionDisplayStringCommon<StringHelper>(scriptContext, StringHelper::NewCopySz(displayName, scriptContext));
}
return sourceString;
}
} // namespace Js