| //------------------------------------------------------------------------------------------------------- |
| // 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 |