ScriptFunction.cs 7.8 KB

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