//------------------------------------------------------------------------------------------------------- // 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 { #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. ConstructorCache* constructorCache; protected: 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. __declspec(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 bool Is(Var aValue); static JavascriptFunction* FromVar(Var aValue); Var CallFunction(Arguments args); Var CallRootFunction(Arguments args, ScriptContext * scriptContext, bool inScript); Var CallRootFunctionInternal(Arguments args, ScriptContext * scriptContext, bool inScript); template static T CallAsmJsFunction(RecyclableObject * function, void* entryPoint, uint argc, Var * argv); template 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 *spreadIndices = nullptr); static Var FinishConstructor(const Var constructorReturnValue, Var newObject, JavascriptFunction *const function); #if DBG static void CheckValidDebugThunk(ScriptContext* scriptContext, RecyclableObject *function); #endif template static Var CallFunction(RecyclableObject* obj, JavascriptMethod entryPoint, Arguments args); static Var CallSpreadFunction(RecyclableObject* obj, JavascriptMethod entryPoint, Arguments args, const Js::AuxArray *spreadIndices); static uint32 GetSpreadSize(const Arguments args, const Js::AuxArray *spreadIndices, ScriptContext *scriptContext); static void SpreadArgs(const Arguments args, Arguments& destArgs, const Js::AuxArray *spreadIndices, ScriptContext *scriptContext); static Var EntrySpreadCall(const Js::AuxArray *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 static String GetNativeFunctionDisplayStringCommon(ScriptContext* scriptContext, String name); template 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 wchar_t* 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; ConstructorCache* GetConstructorCache() { Assert(this->constructorCache != nullptr); return this->constructorCache; } ConstructorCache* EnsureValidConstructorCache(); void ResetConstructorCacheToDefault(); virtual bool HasReadOnlyPropertiesInvisibleToTypeHandler() override { return true; } virtual BOOL HasProperty(PropertyId propertyId) override; virtual BOOL GetProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; virtual BOOL GetProperty(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; virtual BOOL GetPropertyReference(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, Var *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 GetDiagValueString(StringBuilder* stringBuilder, ScriptContext* requestContext) override; virtual BOOL GetDiagTypeString(StringBuilder* 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; } BOOL IsThrowTypeErrorFunction(); void SetEntryPoint(JavascriptMethod method); #if DBG void VerifyEntryPoint(); static bool IsBuiltinProperty(Var objectWithProperty, PropertyIds propertyId); #endif private: static int ResumeForOutOfBoundsArrayRefs(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo); }; 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, FunctionBody* funcBody); }; // // ---- implementation shared with diagnostics ---- // template String JavascriptFunction::GetNativeFunctionDisplayStringCommon(ScriptContext* scriptContext, String name) { auto library = scriptContext->GetLibrary(); String sourceString; sourceString = library->CreateStringFromCppLiteral(JS_DISPLAY_STRING_FUNCTION_HEADER); //L"function " sourceString = StringHelper::Concat(sourceString, name); sourceString = StringHelper::Concat(sourceString, library->CreateStringFromCppLiteral(JS_DISPLAY_STRING_FUNCTION_BODY)); //L"() { [native code] }" return sourceString; } template String JavascriptFunction::GetLibraryCodeDisplayStringCommon(ScriptContext* scriptContext, PCWSTR displayName) { String sourceString; if(wcscmp(displayName, Js::Constants::AnonymousFunction) == 0) { sourceString = scriptContext->GetLibrary()->GetFunctionDisplayString(); } else { sourceString = GetNativeFunctionDisplayStringCommon(scriptContext, StringHelper::NewCopySz(displayName, scriptContext)); } return sourceString; } } // namespace Js