ScriptFunctionInstance.cs 8.1 KB

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