ScriptFunctionInstance.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. using Esprima.Ast;
  2. using Jint.Native.Object;
  3. using Jint.Runtime;
  4. using Jint.Runtime.Descriptors;
  5. using Jint.Runtime.Descriptors.Specialized;
  6. using Jint.Runtime.Environments;
  7. using Jint.Runtime.Interpreter;
  8. namespace Jint.Native.Function
  9. {
  10. public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
  11. {
  12. internal bool _isClassConstructor;
  13. internal JsValue? _classFieldInitializerName;
  14. internal List<PrivateElement>? _privateMethods;
  15. internal List<ClassFieldDefinition>? _fields;
  16. /// <summary>
  17. /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
  18. /// </summary>
  19. public ScriptFunctionInstance(
  20. Engine engine,
  21. IFunction functionDeclaration,
  22. EnvironmentRecord env,
  23. bool strict,
  24. ObjectInstance? proto = null)
  25. : this(
  26. engine,
  27. new JintFunctionDefinition(functionDeclaration),
  28. env,
  29. strict ? FunctionThisMode.Strict : FunctionThisMode.Global,
  30. proto)
  31. {
  32. }
  33. internal ScriptFunctionInstance(
  34. Engine engine,
  35. JintFunctionDefinition function,
  36. EnvironmentRecord env,
  37. FunctionThisMode thisMode,
  38. ObjectInstance? proto = null)
  39. : base(engine, engine.Realm, function, env, thisMode)
  40. {
  41. _prototype = proto ?? _engine.Realm.Intrinsics.Function.PrototypeObject;
  42. _length = new LazyPropertyDescriptor(null, _ => JsNumber.Create(function.Initialize().Length), PropertyFlag.Configurable);
  43. if (!function.Strict
  44. && function.Function is not ArrowFunctionExpression
  45. && !function.Function.Generator)
  46. {
  47. SetProperty(KnownKeys.Arguments, new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(engine, PropertyFlag.Configurable));
  48. SetProperty(KnownKeys.Caller, new PropertyDescriptor(Undefined, PropertyFlag.Configurable));
  49. }
  50. }
  51. /// <summary>
  52. /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist
  53. /// </summary>
  54. protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
  55. {
  56. var strict = _functionDefinition.Strict || _thisMode == FunctionThisMode.Strict;
  57. using (new StrictModeScope(strict, true))
  58. {
  59. try
  60. {
  61. var calleeContext = PrepareForOrdinaryCall(Undefined);
  62. if (_isClassConstructor)
  63. {
  64. ExceptionHelper.ThrowTypeError(calleeContext.Realm, $"Class constructor {_functionDefinition.Name} cannot be invoked without 'new'");
  65. }
  66. OrdinaryCallBindThis(calleeContext, thisObject);
  67. // actual call
  68. var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
  69. var result = _functionDefinition.EvaluateBody(context, this, arguments);
  70. if (result.Type == CompletionType.Throw)
  71. {
  72. ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
  73. }
  74. // The DebugHandler needs the current execution context before the return for stepping through the return point
  75. if (context.DebugMode)
  76. {
  77. // We don't have a statement, but we still need a Location for debuggers. DebugHandler will infer one from
  78. // the function body:
  79. _engine.DebugHandler.OnReturnPoint(
  80. _functionDefinition.Function.Body,
  81. result.Type == CompletionType.Normal ? Undefined : result.Value
  82. );
  83. }
  84. if (result.Type == CompletionType.Return)
  85. {
  86. return result.Value;
  87. }
  88. }
  89. finally
  90. {
  91. _engine.LeaveExecutionContext();
  92. }
  93. return Undefined;
  94. }
  95. }
  96. internal override bool IsConstructor
  97. {
  98. get
  99. {
  100. if (!_homeObject.IsUndefined() && !_isClassConstructor)
  101. {
  102. return false;
  103. }
  104. var function = _functionDefinition?.Function;
  105. return function is not null
  106. && function is not ArrowFunctionExpression
  107. && !function.Generator
  108. && !function.Async;
  109. }
  110. }
  111. /// <summary>
  112. /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
  113. /// </summary>
  114. ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
  115. {
  116. var callerContext = _engine.ExecutionContext;
  117. var kind = _constructorKind;
  118. var thisArgument = Undefined;
  119. if (kind == ConstructorKind.Base)
  120. {
  121. thisArgument = OrdinaryCreateFromConstructor(
  122. newTarget,
  123. static intrinsics => intrinsics.Object.PrototypeObject,
  124. static (Engine engine, Realm _, object? _) => new JsObject(engine));
  125. }
  126. var calleeContext = PrepareForOrdinaryCall(newTarget);
  127. var constructorEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment;
  128. var strict = _thisMode == FunctionThisMode.Strict;
  129. using (new StrictModeScope(strict, force: true))
  130. {
  131. try
  132. {
  133. if (kind == ConstructorKind.Base)
  134. {
  135. OrdinaryCallBindThis(calleeContext, thisArgument);
  136. ((ObjectInstance) thisArgument).InitializeInstanceElements(this);
  137. }
  138. var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
  139. var result = _functionDefinition.EvaluateBody(context, this, arguments);
  140. // The DebugHandler needs the current execution context before the return for stepping through the return point
  141. // We exclude the empty constructor generated for classes without an explicit constructor.
  142. bool isStep = context.DebugMode &&
  143. result.Type != CompletionType.Throw &&
  144. _functionDefinition.Function != ClassDefinition._emptyConstructor.Value;
  145. if (isStep)
  146. {
  147. // We don't have a statement, but we still need a Location for debuggers. DebugHandler will infer one from
  148. // the function body:
  149. _engine.DebugHandler.OnReturnPoint(
  150. _functionDefinition.Function.Body,
  151. result.Type == CompletionType.Normal ? thisArgument : result.Value
  152. );
  153. }
  154. if (result.Type == CompletionType.Return)
  155. {
  156. if (result.Value is ObjectInstance oi)
  157. {
  158. return oi;
  159. }
  160. if (kind == ConstructorKind.Base)
  161. {
  162. return (ObjectInstance) thisArgument;
  163. }
  164. if (!result.Value.IsUndefined())
  165. {
  166. ExceptionHelper.ThrowTypeError(callerContext.Realm);
  167. }
  168. }
  169. else if (result.Type == CompletionType.Throw)
  170. {
  171. ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
  172. }
  173. }
  174. finally
  175. {
  176. _engine.LeaveExecutionContext();
  177. }
  178. }
  179. return (ObjectInstance) constructorEnv.GetThisBinding();
  180. }
  181. internal void MakeClassConstructor()
  182. {
  183. _isClassConstructor = true;
  184. }
  185. }
  186. }