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 PropertyDescriptor _length;
private JsValue _name;
private 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)
{
_name = name;
_thisMode = thisMode;
}
///
/// 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, this);
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;
}
}
}
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
///
public override JsValue Get(JsValue property, JsValue receiver)
{
var v = base.Get(property, receiver);
if (property == CommonProperties.Caller
&& v.As()?._thisMode == FunctionThisMode.Strict)
{
ExceptionHelper.ThrowTypeError(_engine);
}
return v;
}
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 (!(_name is 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 (!(_name is 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 !(_name is null)
? _nameDescriptor ??= new PropertyDescriptor(_name, PropertyFlag.Configurable)
: 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)
{
_name = desc._value;
_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 !(_name is 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)
{
_name = null;
_nameDescriptor = null;
}
base.RemoveOwnProperty(property);
}
internal void SetFunctionName(JsValue name, bool throwIfExists = false)
{
if (_name is null)
{
JsString value;
if (name is JsSymbol symbol)
{
value = new JsString(symbol._value.IsUndefined()
? ""
: "[" + symbol._value + "]");
}
else
{
value = name as JsString ?? new JsString(name.ToString());
}
_name = value;
}
else if (throwIfExists)
{
ExceptionHelper.ThrowError(_engine, "cannot set name");
}
}
[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;
}
}
}