Skip to content

Commit 013fe81

Browse files
committed
...
1 parent 413e189 commit 013fe81

File tree

5 files changed

+579
-11
lines changed

5 files changed

+579
-11
lines changed

src/Asynkron.JsEngine/Ast/ExpressionNodeExtensions.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,11 +508,8 @@ private string DescribeMemberName()
508508

509509
// Return a basic import.meta object with a url property
510510
var metaObject = new JsObject();
511-
if (context.RealmState?.ObjectPrototype is not null)
512-
{
513-
metaObject.SetPrototype(context.RealmState.ObjectPrototype);
514-
}
515-
511+
metaObject.RealmState = context.RealmState;
512+
metaObject.SetPrototype(null);
516513
// Set a default URL if we can determine it from the environment
517514
metaObject.SetProperty("url", string.Empty);
518515
return metaObject;

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.AsyncGeneratorFactory.cs

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Asynkron.JsEngine;
33
using Asynkron.JsEngine.JsTypes;
44
using Asynkron.JsEngine.Runtime;
5+
using Asynkron.JsEngine.StdLib;
56

67
namespace Asynkron.JsEngine.Ast;
78

@@ -289,6 +290,8 @@ public override string ToString()
289290

290291
private void EnsureAsyncGeneratorIntrinsics()
291292
{
293+
var engine = _realmState.Engine ?? throw new InvalidOperationException("Engine reference is missing.");
294+
292295
// %AsyncIteratorPrototype% (inherits from %Object.prototype%)
293296
if (_realmState.AsyncIteratorPrototype is null)
294297
{
@@ -328,13 +331,15 @@ private void EnsureAsyncGeneratorIntrinsics()
328331
HasConfigurable = true
329332
});
330333

331-
// Minimal AsyncGeneratorFunction constructor so constructor.prototype.prototype matches spec shape
332334
if (_realmState.AsyncGeneratorFunctionConstructor is null)
333335
{
334-
var constructor = new HostFunction(_ => null);
335-
constructor.Properties.SetPrototype(_realmState.FunctionPrototype);
336-
constructor.SetProperty("prototype", asyncGenFuncProto);
337-
_realmState.AsyncGeneratorFunctionConstructor = constructor;
336+
_realmState.AsyncGeneratorFunctionConstructor =
337+
CreateAsyncGeneratorFunctionConstructor(engine, _realmState);
338+
}
339+
340+
if (_realmState.AsyncGeneratorFunctionConstructor is { } asyncGenCtor)
341+
{
342+
asyncGenCtor.SetProperty("prototype", asyncGenFuncProto);
338343
}
339344

340345
asyncGenFuncProto.DefineProperty("constructor",
@@ -354,6 +359,124 @@ private void EnsureAsyncGeneratorIntrinsics()
354359
}
355360
}
356361

362+
private static HostFunction CreateAsyncGeneratorFunctionConstructor(JsEngine engine, RealmState realm)
363+
{
364+
HostFunction constructor = null!;
365+
366+
constructor = new HostFunction((_, args) =>
367+
AsyncGeneratorFunctionConstructorBody(args, constructor, engine, realm))
368+
{
369+
RealmState = realm
370+
};
371+
372+
constructor.SetInvokeWithContext((args, _, _, newTarget) =>
373+
AsyncGeneratorFunctionConstructorBody(args, newTarget as IJsCallable ?? constructor, engine, realm));
374+
375+
StandardLibrary.DefineConstantProperty(constructor, "length", 1d, configurable: true);
376+
StandardLibrary.DefineConstantProperty(constructor, "name", "AsyncGeneratorFunction", configurable: true);
377+
378+
if (realm.FunctionPrototype is { } functionPrototype)
379+
{
380+
constructor.Properties.SetPrototype(functionPrototype);
381+
}
382+
383+
return constructor;
384+
}
385+
386+
private static object? AsyncGeneratorFunctionConstructorBody(
387+
IReadOnlyList<object?> args,
388+
IJsCallable newTarget,
389+
JsEngine engine,
390+
RealmState realm)
391+
{
392+
var evalContext = realm.CreateContext();
393+
var argCount = args.Count;
394+
var bodyValue = argCount > 0 ? args[argCount - 1] : string.Empty;
395+
var parameterCount = Math.Max(argCount - 1, 0);
396+
397+
var parameters = new string[parameterCount];
398+
for (var i = 0; i < parameterCount; i++)
399+
{
400+
var paramText = ToFunctionArgumentString(args[i], evalContext, realm);
401+
parameters[i] = paramText;
402+
}
403+
404+
var bodySource = ToFunctionArgumentString(bodyValue, evalContext, realm);
405+
var paramList = string.Join(",", parameters);
406+
var functionSource = $"(async function* anonymous({paramList}\n) {{\n{bodySource}\n}})";
407+
408+
var scriptGoalOptions = new JsEngineOptions
409+
{
410+
AllowImportMeta = false
411+
};
412+
413+
ParsedProgram program;
414+
try
415+
{
416+
program = engine.ParseForExecution(functionSource, options: scriptGoalOptions);
417+
}
418+
catch (Parser.ParseException parseException)
419+
{
420+
var message = parseException.Message ?? "SyntaxError";
421+
throw new ThrowSignal(StandardLibrary.CreateSyntaxError(message, evalContext, realm));
422+
}
423+
424+
var created = engine.ExecuteProgram(
425+
program,
426+
engine.GlobalEnvironment,
427+
CancellationToken.None);
428+
429+
if (created is IJsObjectLike objectLike)
430+
{
431+
var proto = StandardLibrary.ResolveConstructPrototype(
432+
newTarget,
433+
realm.AsyncGeneratorFunctionConstructor,
434+
realm);
435+
if (proto is not null)
436+
{
437+
objectLike.SetPrototype(proto);
438+
}
439+
}
440+
441+
return created;
442+
}
443+
444+
private static string ToFunctionArgumentString(object? value, EvaluationContext evalContext, RealmState realm)
445+
{
446+
var primitive = Runtime.JsOps.ToPrimitive(value, "string", evalContext);
447+
if (evalContext.IsThrow)
448+
{
449+
throw new ThrowSignal(evalContext.FlowValue);
450+
}
451+
452+
switch (primitive)
453+
{
454+
case null:
455+
return "null";
456+
case Symbol sym when ReferenceEquals(sym, Symbol.Undefined):
457+
return "undefined";
458+
case Symbol:
459+
case TypedAstSymbol:
460+
throw StandardLibrary.ThrowTypeError("Cannot convert a Symbol value to a string", evalContext, realm);
461+
case bool flag:
462+
return flag ? "true" : "false";
463+
case string s:
464+
return s;
465+
case JsBigInt bigInt:
466+
return bigInt.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
467+
case double d when double.IsNaN(d):
468+
return "NaN";
469+
case double d when double.IsPositiveInfinity(d):
470+
return "Infinity";
471+
case double d when double.IsNegativeInfinity(d):
472+
return "-Infinity";
473+
case double d:
474+
return d.ToString(System.Globalization.CultureInfo.InvariantCulture);
475+
}
476+
477+
return Convert.ToString(primitive, System.Globalization.CultureInfo.InvariantCulture) ?? string.Empty;
478+
}
479+
357480
private void InitializeProperties()
358481
{
359482
EnsureAsyncGeneratorIntrinsics();

src/Asynkron.JsEngine/EvalHostFunction.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ public EvalHostFunction(JsEngine engine)
7979
environment.RealmState);
8080
}
8181

82+
if (JsEngine.ProgramContainsImportMeta(program.Typed))
83+
{
84+
throw StandardLibrary.ThrowSyntaxError(
85+
"'import.meta' is only valid in module code.",
86+
CallingContext,
87+
environment.RealmState);
88+
}
89+
8290
var insideClassFieldInitializer = InClassFieldInitializer ||
8391
CallingContext?.InClassFieldInitializer == true ||
8492
(CallingJsEnvironment?.HasBinding(FieldInitializerEvalFlag) ?? false);

0 commit comments

Comments
 (0)