using System.Collections.Generic; using System.Runtime.CompilerServices; using Jint.Native.Object; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Environments; using Jint.Runtime.Interpreter; namespace Jint.Native.Function { public abstract class FunctionInstance : ObjectInstance, ICallable { internal enum FunctionThisMode { Lexical, Strict, Global } protected internal PropertyDescriptor _prototypeDescriptor; protected internal PropertyDescriptor _length; internal PropertyDescriptor _nameDescriptor; protected internal LexicalEnvironment _environment; internal readonly JintFunctionDefinition _functionDefinition; internal readonly FunctionThisMode _thisMode; internal FunctionInstance( Engine engine, JintFunctionDefinition function, LexicalEnvironment scope, FunctionThisMode thisMode) : this(engine, !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null, thisMode) { _functionDefinition = function; _environment = scope; } internal FunctionInstance( Engine engine, JsString name, FunctionThisMode thisMode = FunctionThisMode.Global, ObjectClass objectClass = ObjectClass.Function) : base(engine, objectClass) { if (!(name is null)) { _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable); } _thisMode = thisMode; } protected FunctionInstance( Engine engine, JsString name) : this(engine, name, FunctionThisMode.Global, ObjectClass.Function) { } /// /// Executed when a function object is used as a function /// /// /// /// public abstract JsValue Call(JsValue thisObject, JsValue[] arguments); public bool Strict => _thisMode == FunctionThisMode.Strict; public virtual bool HasInstance(JsValue v) { if (!(v is ObjectInstance o)) { return false; } var p = Get(CommonProperties.Prototype); if (!(p is ObjectInstance prototype)) { ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(p)}' in instanceof check"); } while (true) { o = o.Prototype; if (o is null) { return false; } if (SameValue(p, o)) { return true; } } } public override IEnumerable> GetOwnProperties() { if (_prototypeDescriptor != null) { yield return new KeyValuePair(CommonProperties.Prototype, _prototypeDescriptor); } if (_length != null) { yield return new KeyValuePair(CommonProperties.Length, _length); } if (_nameDescriptor != null) { yield return new KeyValuePair(CommonProperties.Name, GetOwnProperty(CommonProperties.Name)); } foreach (var entry in base.GetOwnProperties()) { yield return entry; } } public override List GetOwnPropertyKeys(Types types) { var keys = new List(); if (_prototypeDescriptor != null) { keys.Add(CommonProperties.Prototype); } if (_length != null) { keys.Add(CommonProperties.Length); } if (_nameDescriptor != null) { keys.Add(CommonProperties.Name); } keys.AddRange(base.GetOwnPropertyKeys(types)); return keys; } public override PropertyDescriptor GetOwnProperty(JsValue property) { if (property == CommonProperties.Prototype) { return _prototypeDescriptor ?? PropertyDescriptor.Undefined; } if (property == CommonProperties.Length) { return _length ?? PropertyDescriptor.Undefined; } if (property == CommonProperties.Name) { return _nameDescriptor ?? PropertyDescriptor.Undefined; } return base.GetOwnProperty(property); } protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc) { if (property == CommonProperties.Prototype) { _prototypeDescriptor = desc; } else if (property == CommonProperties.Length) { _length = desc; } else if (property == CommonProperties.Name) { _nameDescriptor = desc; } else { base.SetOwnProperty(property, desc); } } public override bool HasOwnProperty(JsValue property) { if (property == CommonProperties.Prototype) { return _prototypeDescriptor != null; } if (property == CommonProperties.Length) { return _length != null; } if (property == CommonProperties.Name) { return _nameDescriptor != null; } return base.HasOwnProperty(property); } public override void RemoveOwnProperty(JsValue property) { if (property == CommonProperties.Prototype) { _prototypeDescriptor = null; } if (property == CommonProperties.Length) { _length = null; } if (property == CommonProperties.Name) { _nameDescriptor = null; } base.RemoveOwnProperty(property); } internal void SetFunctionName(JsValue name, string prefix = null, bool force = false) { if (!force && _nameDescriptor != null && !UnwrapJsValue(_nameDescriptor).IsUndefined()) { return; } if (name is JsSymbol symbol) { name = symbol._value.IsUndefined() ? JsString.Empty : new JsString("[" + symbol._value + "]"); } if (!string.IsNullOrWhiteSpace(prefix)) { name = prefix + " " + name; } _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ObjectInstance OrdinaryCreateFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto) { var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto); var obj = new ObjectInstance(_engine) { _prototype = proto }; return obj; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ObjectInstance GetPrototypeFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto) { var proto = constructor.Get(CommonProperties.Prototype, constructor) as ObjectInstance; // If Type(proto) is not Object, then // Let realm be ? GetFunctionRealm(constructor). // Set proto to realm's intrinsic object named intrinsicDefaultProto. return proto ?? intrinsicDefaultProto; } public override string ToString() { // TODO no way to extract SourceText from Esprima at the moment, just returning native code var nameValue = _nameDescriptor != null ? UnwrapJsValue(_nameDescriptor) : JsString.Empty; var name = ""; if (!nameValue.IsUndefined()) { name = TypeConverter.ToString(nameValue); } return "function " + name + "() {{[native code]}}"; } } }