ScriptFunction.cs 8.3 KB

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