From 06d0171d8b4d1a17e9eb62a0e699cc7a6fbf404d Mon Sep 17 00:00:00 2001 From: bendtherules Date: Mon, 7 Sep 2020 18:58:41 +0530 Subject: [PATCH] Install old engine262 in src/engine262 New version was not returning completion records easily --- src/engine262/.eslintignore | 4 + src/engine262/.eslintrc.js | 72 + src/engine262/.github/FUNDING.yml | 12 + src/engine262/.github/workflows/publish.yml | 55 + src/engine262/.gitignore | 6 + src/engine262/.gitmodules | 6 + src/engine262/.npmignore | 9 + src/engine262/.npmrc | 1 + src/engine262/CODE_OF_CONDUCT.md | 46 + src/engine262/LICENSE | 19 + src/engine262/README.md | 149 + src/engine262/bin/engine262.js | 202 + src/engine262/bin/snekparse.js | 76 + src/engine262/bin/test262_realm.js | 117 + src/engine262/inspector/context.js | 187 + src/engine262/inspector/index.js | 6 + src/engine262/inspector/js_protocol.json | 3288 +++++++++++++++++ src/engine262/inspector/methods.js | 102 + src/engine262/inspector/server.js | 87 + src/engine262/package.json | 57 + src/engine262/rollup.config.js | 54 + .../scripts/tag_version_with_git_hash.js | 20 + src/engine262/scripts/transform.js | 252 ++ src/engine262/src/abstract-ops/all.mjs | 32 + .../src/abstract-ops/arguments-operations.mjs | 248 ++ .../src/abstract-ops/array-objects.mjs | 250 ++ .../src/abstract-ops/arraybuffer-objects.mjs | 213 ++ .../async-function-operations.mjs | 47 + .../abstract-ops/async-generator-objects.mjs | 193 + .../abstract-ops/data-types-and-values.mjs | 49 + .../src/abstract-ops/dataview-objects.mjs | 91 + .../src/abstract-ops/date-objects.mjs | 233 ++ .../src/abstract-ops/execution-contexts.mjs | 68 + .../src/abstract-ops/function-operations.mjs | 410 ++ .../src/abstract-ops/generator-operations.mjs | 200 + .../src/abstract-ops/global-object.mjs | 243 ++ .../immutable-prototype-objects.mjs | 17 + .../abstract-ops/integer-indexed-objects.mjs | 314 ++ .../src/abstract-ops/iterator-operations.mjs | 224 ++ .../module-namespace-exotic-objects.mjs | 199 + .../src/abstract-ops/module-records.mjs | 263 ++ .../abstract-ops/notational-conventions.mjs | 55 + .../src/abstract-ops/object-operations.mjs | 434 +++ src/engine262/src/abstract-ops/objects.mjs | 405 ++ .../src/abstract-ops/promise-operations.mjs | 392 ++ .../src/abstract-ops/proxy-objects.mjs | 579 +++ .../src/abstract-ops/reference-operations.mjs | 142 + .../src/abstract-ops/regexp-objects.mjs | 93 + .../src/abstract-ops/source-code.mjs | 82 + src/engine262/src/abstract-ops/spec-types.mjs | 210 ++ .../src/abstract-ops/string-objects.mjs | 145 + .../src/abstract-ops/symbol-objects.mjs | 12 + .../src/abstract-ops/testing-comparison.mjs | 412 +++ .../src/abstract-ops/type-conversion.mjs | 469 +++ .../src/abstract-ops/typedarray-objects.mjs | 240 ++ .../src/abstract-ops/weak-operations.mjs | 45 + src/engine262/src/api.mjs | 388 ++ src/engine262/src/ast.mjs | 1085 ++++++ src/engine262/src/completion.mjs | 176 + src/engine262/src/engine.mjs | 403 ++ src/engine262/src/environment.mjs | 1006 +++++ src/engine262/src/evaluator.mjs | 516 +++ src/engine262/src/grammar/Scientific.mjs | 212 ++ .../src/grammar/StrNumericLiteral-gen.mjs | 116 + .../src/grammar/StrNumericLiteral.ne | 163 + src/engine262/src/grammar/util.mjs | 36 + src/engine262/src/helpers.mjs | 349 ++ src/engine262/src/inspect.mjs | 192 + .../src/intrinsics/AggregateError.mjs | 52 + .../intrinsics/AggregateErrorPrototype.mjs | 26 + src/engine262/src/intrinsics/Array.mjs | 215 ++ src/engine262/src/intrinsics/ArrayBuffer.mjs | 44 + .../src/intrinsics/ArrayBufferPrototype.mjs | 114 + .../src/intrinsics/ArrayIteratorPrototype.mjs | 71 + .../src/intrinsics/ArrayPrototype.mjs | 592 +++ .../src/intrinsics/ArrayPrototypeShared.mjs | 561 +++ .../AsyncFromSyncIteratorPrototype.mjs | 81 + .../src/intrinsics/AsyncFunction.mjs | 24 + .../src/intrinsics/AsyncFunctionPrototype.mjs | 7 + .../src/intrinsics/AsyncGenerator.mjs | 18 + .../src/intrinsics/AsyncGeneratorFunction.mjs | 30 + .../intrinsics/AsyncGeneratorPrototype.mjs | 37 + .../src/intrinsics/AsyncIteratorPrototype.mjs | 14 + src/engine262/src/intrinsics/BigInt.mjs | 52 + .../src/intrinsics/BigIntPrototype.mjs | 74 + src/engine262/src/intrinsics/Boolean.mjs | 26 + .../src/intrinsics/BooleanPrototype.mjs | 48 + src/engine262/src/intrinsics/Bootstrap.mjs | 116 + src/engine262/src/intrinsics/DataView.mjs | 65 + .../src/intrinsics/DataViewPrototype.mjs | 275 ++ src/engine262/src/intrinsics/Date.mjs | 203 + .../src/intrinsics/DatePrototype.mjs | 697 ++++ src/engine262/src/intrinsics/Error.mjs | 44 + .../src/intrinsics/ErrorPrototype.mjs | 49 + .../src/intrinsics/FinalizationRegistry.mjs | 42 + .../FinalizationRegistryPrototype.mjs | 99 + .../src/intrinsics/ForInIteratorPrototype.mjs | 104 + src/engine262/src/intrinsics/Function.mjs | 14 + .../src/intrinsics/FunctionPrototype.mjs | 181 + src/engine262/src/intrinsics/Generator.mjs | 21 + .../src/intrinsics/GeneratorFunction.mjs | 26 + .../src/intrinsics/GeneratorPrototype.mjs | 41 + .../src/intrinsics/IteratorPrototype.mjs | 15 + src/engine262/src/intrinsics/JSON.mjs | 540 +++ src/engine262/src/intrinsics/Map.mjs | 78 + .../src/intrinsics/MapIteratorPrototype.mjs | 56 + src/engine262/src/intrinsics/MapPrototype.mjs | 166 + src/engine262/src/intrinsics/Math.mjs | 135 + src/engine262/src/intrinsics/NativeError.mjs | 66 + src/engine262/src/intrinsics/Number.mjs | 120 + .../src/intrinsics/NumberPrototype.mjs | 230 ++ src/engine262/src/intrinsics/Object.mjs | 303 ++ .../src/intrinsics/ObjectPrototype.mjs | 111 + src/engine262/src/intrinsics/Promise.mjs | 513 +++ .../src/intrinsics/PromisePrototype.mjs | 105 + src/engine262/src/intrinsics/Proxy.mjs | 82 + src/engine262/src/intrinsics/Reflect.mjs | 153 + src/engine262/src/intrinsics/RegExp.mjs | 71 + .../src/intrinsics/RegExpPrototype.mjs | 912 +++++ .../RegExpStringIteratorPrototype.mjs | 81 + src/engine262/src/intrinsics/Set.mjs | 54 + .../src/intrinsics/SetIteratorPrototype.mjs | 48 + src/engine262/src/intrinsics/SetPrototype.mjs | 138 + src/engine262/src/intrinsics/String.mjs | 114 + .../intrinsics/StringIteratorPrototype.mjs | 55 + .../src/intrinsics/StringPrototype.mjs | 750 ++++ src/engine262/src/intrinsics/Symbol.mjs | 81 + .../src/intrinsics/SymbolPrototype.mjs | 61 + .../src/intrinsics/ThrowTypeError.mjs | 32 + src/engine262/src/intrinsics/TypedArray.mjs | 145 + .../src/intrinsics/TypedArrayConstructors.mjs | 286 ++ .../src/intrinsics/TypedArrayPrototype.mjs | 841 +++++ .../src/intrinsics/TypedArrayPrototypes.mjs | 16 + src/engine262/src/intrinsics/WeakMap.mjs | 38 + .../src/intrinsics/WeakMapPrototype.mjs | 123 + src/engine262/src/intrinsics/WeakRef.mjs | 31 + .../src/intrinsics/WeakRefPrototype.mjs | 31 + src/engine262/src/intrinsics/WeakSet.mjs | 60 + .../src/intrinsics/WeakSetPrototype.mjs | 97 + src/engine262/src/intrinsics/eval.mjs | 26 + src/engine262/src/intrinsics/isFinite.mjs | 23 + src/engine262/src/intrinsics/isNaN.mjs | 23 + src/engine262/src/intrinsics/parseFloat.mjs | 24 + src/engine262/src/intrinsics/parseInt.mjs | 103 + src/engine262/src/messages.mjs | 124 + src/engine262/src/modules.mjs | 400 ++ src/engine262/src/parse.mjs | 1156 ++++++ src/engine262/src/realm.mjs | 391 ++ .../runtime-semantics/AdditiveExpression.mjs | 83 + .../ArgumentListEvaluation.mjs | 119 + .../src/runtime-semantics/ArrayLiteral.mjs | 80 + .../src/runtime-semantics/ArrowFunction.mjs | 23 + .../AssignmentExpression.mjs | 120 + .../runtime-semantics/AsyncArrowFunction.mjs | 10 + .../AsyncFunctionExpression.mjs | 37 + .../AsyncGeneratorExpression.mjs | 47 + .../src/runtime-semantics/AwaitExpression.mjs | 11 + .../BindingInitialization.mjs | 146 + .../runtime-semantics/BitwiseOperators.mjs | 59 + .../src/runtime-semantics/BlockStatement.mjs | 76 + .../src/runtime-semantics/BreakStatement.mjs | 15 + .../runtime-semantics/BreakableStatement.mjs | 61 + .../src/runtime-semantics/CallExpression.mjs | 118 + .../src/runtime-semantics/ClassDefinition.mjs | 181 + .../runtime-semantics/CoalesceExpression.mjs | 25 + .../ConditionalExpression.mjs | 29 + .../runtime-semantics/ContinueStatement.mjs | 15 + .../CreateDynamicFunction.mjs | 189 + .../runtime-semantics/DebuggerStatement.mjs | 19 + .../src/runtime-semantics/DefineMethod.mjs | 32 + .../DestructuringAssignmentEvaluation.mjs | 390 ++ .../src/runtime-semantics/EmptyStatement.mjs | 7 + .../runtime-semantics/EqualityExpression.mjs | 52 + .../src/runtime-semantics/EvaluateBody.mjs | 486 +++ .../EvaluatePropertyAccess.mjs | 43 + .../ExponentiationExpression.mjs | 28 + .../runtime-semantics/ExportDeclaration.mjs | 63 + .../runtime-semantics/ExpressionWithComma.mjs | 16 + .../src/runtime-semantics/ForStatement.mjs | 454 +++ .../runtime-semantics/FunctionDeclaration.mjs | 11 + .../runtime-semantics/FunctionExpression.mjs | 41 + .../FunctionStatementList.mjs | 12 + .../runtime-semantics/GeneratorExpression.mjs | 47 + .../src/runtime-semantics/GetSubstitution.mjs | 105 + .../GlobalDeclarationInstantiation.mjs | 138 + .../HoistableDeclaration.mjs | 30 + .../src/runtime-semantics/Identifier.mjs | 12 + .../src/runtime-semantics/IfStatement.mjs | 43 + .../src/runtime-semantics/ImportCall.mjs | 22 + .../InstantiateFunctionObject.mjs | 108 + .../IteratorBindingInitialization.mjs | 354 ++ .../KeyedBindingInitialization.mjs | 94 + .../runtime-semantics/LabelledStatement.mjs | 44 + .../runtime-semantics/LexicalDeclaration.mjs | 92 + .../src/runtime-semantics/Literal.mjs | 33 + .../LogicalANDExpression.mjs | 20 + .../runtime-semantics/LogicalORExpression.mjs | 20 + src/engine262/src/runtime-semantics/MV.mjs | 110 + .../runtime-semantics/MemberExpression.mjs | 62 + .../src/runtime-semantics/MetaProperty.mjs | 67 + .../MultiplicativeExpression.mjs | 43 + .../src/runtime-semantics/NamedEvaluation.mjs | 202 + .../src/runtime-semantics/NewExpression.mjs | 35 + .../src/runtime-semantics/NumberToBigInt.mjs | 12 + .../src/runtime-semantics/ObjectLiteral.mjs | 25 + .../runtime-semantics/OptionalExpression.mjs | 86 + .../PropertyBindingInitialization.mjs | 56 + .../PropertyDefinitionEvaluation.mjs | 312 ++ .../src/runtime-semantics/PropertyName.mjs | 57 + .../src/runtime-semantics/RegExp.mjs | 865 +++++ .../RegularExpressionLiteral.mjs | 11 + .../runtime-semantics/RelationalOperators.mjs | 94 + .../RestBindingInitialization.mjs | 32 + .../src/runtime-semantics/ReturnStatement.mjs | 27 + .../src/runtime-semantics/ShiftExpression.mjs | 47 + .../src/runtime-semantics/StringIndexOf.mjs | 48 + .../src/runtime-semantics/StringPad.mjs | 31 + .../src/runtime-semantics/SuperCall.mjs | 39 + .../src/runtime-semantics/SuperProperty.mjs | 50 + .../src/runtime-semantics/SwitchStatement.mjs | 146 + .../src/runtime-semantics/TaggedTemplate.mjs | 71 + .../src/runtime-semantics/TemplateLiteral.mjs | 34 + .../src/runtime-semantics/ThisExpression.mjs | 8 + .../src/runtime-semantics/ThrowStatement.mjs | 17 + .../src/runtime-semantics/TrimString.mjs | 19 + .../src/runtime-semantics/TryStatement.mjs | 118 + .../src/runtime-semantics/UnaryExpression.mjs | 172 + .../runtime-semantics/UpdateExpression.mjs | 64 + .../runtime-semantics/VariableStatement.mjs | 82 + .../src/runtime-semantics/WithStatement.mjs | 28 + .../src/runtime-semantics/YieldExpression.mjs | 141 + src/engine262/src/runtime-semantics/all.mjs | 83 + .../src/static-semantics/BoundNames.mjs | 496 +++ .../static-semantics/ConstructorMethod.mjs | 11 + .../static-semantics/ContainsExpression.mjs | 245 ++ .../static-semantics/ContainsUseStrict.mjs | 28 + .../src/static-semantics/DeclarationPart.mjs | 25 + .../ExpectedArgumentCount.mjs | 67 + .../src/static-semantics/ExportEntries.mjs | 181 + .../ExportEntriesForModule.mjs | 48 + .../src/static-semantics/HasInitializer.mjs | 52 + .../src/static-semantics/HasName.mjs | 98 + .../src/static-semantics/ImportEntries.mjs | 81 + .../ImportEntriesForModule.mjs | 104 + .../static-semantics/ImportedLocalNames.mjs | 8 + .../IsAnonymousFunctionDefinition.mjs | 16 + .../IsConstantDeclaration.mjs | 3 + .../src/static-semantics/IsDestructuring.mjs | 60 + .../static-semantics/IsFunctionDefinition.mjs | 81 + .../src/static-semantics/IsIdentifierRef.mjs | 14 + .../src/static-semantics/IsInTailPosition.mjs | 5 + .../IsSimpleParameterList.mjs | 85 + .../src/static-semantics/IsStatic.mjs | 8 + .../src/static-semantics/IsStrict.mjs | 5 + .../LexicallyDeclaredNames.mjs | 121 + .../LexicallyScopedDeclarations.mjs | 199 + src/engine262/src/static-semantics/MV.mjs | 27 + .../src/static-semantics/ModuleRequests.mjs | 108 + .../NonConstructorMethodDefinitions.mjs | 11 + src/engine262/src/static-semantics/TRV.mjs | 40 + src/engine262/src/static-semantics/TV.mjs | 41 + .../src/static-semantics/TemplateStrings.mjs | 99 + .../TopLevelLexicallyDeclaredNames.mjs | 39 + .../TopLevelLexicallyScopedDeclarations.mjs | 36 + .../TopLevelVarDeclaredNames.mjs | 67 + .../TopLevelVarScopedDeclarations.mjs | 64 + .../src/static-semantics/VarDeclaredNames.mjs | 371 ++ .../VarScopedDeclarations.mjs | 375 ++ src/engine262/src/static-semantics/all.mjs | 36 + src/engine262/src/value.mjs | 851 +++++ src/engine262/test/base.js | 109 + src/engine262/test/coverage_root.sh | 7 + .../test/eslint-plugin-engine262/index.js | 8 + .../eslint-plugin-engine262/no-use-in-def.js | 68 + .../eslint-plugin-engine262/valid-throw.js | 63 + src/engine262/test/json/json.js | 62 + src/engine262/test/stepped.js | 73 + src/engine262/test/supplemental.js | 177 + src/engine262/test/test262/features | 25 + src/engine262/test/test262/longlist | 13 + src/engine262/test/test262/skiplist | 90 + src/engine262/test/test262/test262.js | 280 ++ src/engine262/yarn.lock | 2222 +++++++++++ 283 files changed, 44506 insertions(+) create mode 100644 src/engine262/.eslintignore create mode 100644 src/engine262/.eslintrc.js create mode 100644 src/engine262/.github/FUNDING.yml create mode 100644 src/engine262/.github/workflows/publish.yml create mode 100644 src/engine262/.gitignore create mode 100644 src/engine262/.gitmodules create mode 100644 src/engine262/.npmignore create mode 100644 src/engine262/.npmrc create mode 100644 src/engine262/CODE_OF_CONDUCT.md create mode 100644 src/engine262/LICENSE create mode 100644 src/engine262/README.md create mode 100755 src/engine262/bin/engine262.js create mode 100644 src/engine262/bin/snekparse.js create mode 100644 src/engine262/bin/test262_realm.js create mode 100644 src/engine262/inspector/context.js create mode 100644 src/engine262/inspector/index.js create mode 100644 src/engine262/inspector/js_protocol.json create mode 100644 src/engine262/inspector/methods.js create mode 100644 src/engine262/inspector/server.js create mode 100644 src/engine262/package.json create mode 100644 src/engine262/rollup.config.js create mode 100644 src/engine262/scripts/tag_version_with_git_hash.js create mode 100644 src/engine262/scripts/transform.js create mode 100644 src/engine262/src/abstract-ops/all.mjs create mode 100644 src/engine262/src/abstract-ops/arguments-operations.mjs create mode 100644 src/engine262/src/abstract-ops/array-objects.mjs create mode 100644 src/engine262/src/abstract-ops/arraybuffer-objects.mjs create mode 100644 src/engine262/src/abstract-ops/async-function-operations.mjs create mode 100644 src/engine262/src/abstract-ops/async-generator-objects.mjs create mode 100644 src/engine262/src/abstract-ops/data-types-and-values.mjs create mode 100644 src/engine262/src/abstract-ops/dataview-objects.mjs create mode 100644 src/engine262/src/abstract-ops/date-objects.mjs create mode 100644 src/engine262/src/abstract-ops/execution-contexts.mjs create mode 100644 src/engine262/src/abstract-ops/function-operations.mjs create mode 100644 src/engine262/src/abstract-ops/generator-operations.mjs create mode 100644 src/engine262/src/abstract-ops/global-object.mjs create mode 100644 src/engine262/src/abstract-ops/immutable-prototype-objects.mjs create mode 100644 src/engine262/src/abstract-ops/integer-indexed-objects.mjs create mode 100644 src/engine262/src/abstract-ops/iterator-operations.mjs create mode 100644 src/engine262/src/abstract-ops/module-namespace-exotic-objects.mjs create mode 100644 src/engine262/src/abstract-ops/module-records.mjs create mode 100644 src/engine262/src/abstract-ops/notational-conventions.mjs create mode 100644 src/engine262/src/abstract-ops/object-operations.mjs create mode 100644 src/engine262/src/abstract-ops/objects.mjs create mode 100644 src/engine262/src/abstract-ops/promise-operations.mjs create mode 100644 src/engine262/src/abstract-ops/proxy-objects.mjs create mode 100644 src/engine262/src/abstract-ops/reference-operations.mjs create mode 100644 src/engine262/src/abstract-ops/regexp-objects.mjs create mode 100644 src/engine262/src/abstract-ops/source-code.mjs create mode 100644 src/engine262/src/abstract-ops/spec-types.mjs create mode 100644 src/engine262/src/abstract-ops/string-objects.mjs create mode 100644 src/engine262/src/abstract-ops/symbol-objects.mjs create mode 100644 src/engine262/src/abstract-ops/testing-comparison.mjs create mode 100644 src/engine262/src/abstract-ops/type-conversion.mjs create mode 100644 src/engine262/src/abstract-ops/typedarray-objects.mjs create mode 100644 src/engine262/src/abstract-ops/weak-operations.mjs create mode 100644 src/engine262/src/api.mjs create mode 100644 src/engine262/src/ast.mjs create mode 100644 src/engine262/src/completion.mjs create mode 100644 src/engine262/src/engine.mjs create mode 100644 src/engine262/src/environment.mjs create mode 100644 src/engine262/src/evaluator.mjs create mode 100644 src/engine262/src/grammar/Scientific.mjs create mode 100644 src/engine262/src/grammar/StrNumericLiteral-gen.mjs create mode 100644 src/engine262/src/grammar/StrNumericLiteral.ne create mode 100644 src/engine262/src/grammar/util.mjs create mode 100644 src/engine262/src/helpers.mjs create mode 100644 src/engine262/src/inspect.mjs create mode 100644 src/engine262/src/intrinsics/AggregateError.mjs create mode 100644 src/engine262/src/intrinsics/AggregateErrorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Array.mjs create mode 100644 src/engine262/src/intrinsics/ArrayBuffer.mjs create mode 100644 src/engine262/src/intrinsics/ArrayBufferPrototype.mjs create mode 100644 src/engine262/src/intrinsics/ArrayIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/ArrayPrototype.mjs create mode 100644 src/engine262/src/intrinsics/ArrayPrototypeShared.mjs create mode 100644 src/engine262/src/intrinsics/AsyncFromSyncIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/AsyncFunction.mjs create mode 100644 src/engine262/src/intrinsics/AsyncFunctionPrototype.mjs create mode 100644 src/engine262/src/intrinsics/AsyncGenerator.mjs create mode 100644 src/engine262/src/intrinsics/AsyncGeneratorFunction.mjs create mode 100644 src/engine262/src/intrinsics/AsyncGeneratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/AsyncIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/BigInt.mjs create mode 100644 src/engine262/src/intrinsics/BigIntPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Boolean.mjs create mode 100644 src/engine262/src/intrinsics/BooleanPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Bootstrap.mjs create mode 100644 src/engine262/src/intrinsics/DataView.mjs create mode 100644 src/engine262/src/intrinsics/DataViewPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Date.mjs create mode 100644 src/engine262/src/intrinsics/DatePrototype.mjs create mode 100644 src/engine262/src/intrinsics/Error.mjs create mode 100644 src/engine262/src/intrinsics/ErrorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/FinalizationRegistry.mjs create mode 100644 src/engine262/src/intrinsics/FinalizationRegistryPrototype.mjs create mode 100644 src/engine262/src/intrinsics/ForInIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Function.mjs create mode 100644 src/engine262/src/intrinsics/FunctionPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Generator.mjs create mode 100644 src/engine262/src/intrinsics/GeneratorFunction.mjs create mode 100644 src/engine262/src/intrinsics/GeneratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/IteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/JSON.mjs create mode 100644 src/engine262/src/intrinsics/Map.mjs create mode 100644 src/engine262/src/intrinsics/MapIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/MapPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Math.mjs create mode 100644 src/engine262/src/intrinsics/NativeError.mjs create mode 100644 src/engine262/src/intrinsics/Number.mjs create mode 100644 src/engine262/src/intrinsics/NumberPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Object.mjs create mode 100644 src/engine262/src/intrinsics/ObjectPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Promise.mjs create mode 100644 src/engine262/src/intrinsics/PromisePrototype.mjs create mode 100644 src/engine262/src/intrinsics/Proxy.mjs create mode 100644 src/engine262/src/intrinsics/Reflect.mjs create mode 100644 src/engine262/src/intrinsics/RegExp.mjs create mode 100644 src/engine262/src/intrinsics/RegExpPrototype.mjs create mode 100644 src/engine262/src/intrinsics/RegExpStringIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Set.mjs create mode 100644 src/engine262/src/intrinsics/SetIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/SetPrototype.mjs create mode 100644 src/engine262/src/intrinsics/String.mjs create mode 100644 src/engine262/src/intrinsics/StringIteratorPrototype.mjs create mode 100644 src/engine262/src/intrinsics/StringPrototype.mjs create mode 100644 src/engine262/src/intrinsics/Symbol.mjs create mode 100644 src/engine262/src/intrinsics/SymbolPrototype.mjs create mode 100644 src/engine262/src/intrinsics/ThrowTypeError.mjs create mode 100644 src/engine262/src/intrinsics/TypedArray.mjs create mode 100644 src/engine262/src/intrinsics/TypedArrayConstructors.mjs create mode 100644 src/engine262/src/intrinsics/TypedArrayPrototype.mjs create mode 100644 src/engine262/src/intrinsics/TypedArrayPrototypes.mjs create mode 100644 src/engine262/src/intrinsics/WeakMap.mjs create mode 100644 src/engine262/src/intrinsics/WeakMapPrototype.mjs create mode 100644 src/engine262/src/intrinsics/WeakRef.mjs create mode 100644 src/engine262/src/intrinsics/WeakRefPrototype.mjs create mode 100644 src/engine262/src/intrinsics/WeakSet.mjs create mode 100644 src/engine262/src/intrinsics/WeakSetPrototype.mjs create mode 100644 src/engine262/src/intrinsics/eval.mjs create mode 100644 src/engine262/src/intrinsics/isFinite.mjs create mode 100644 src/engine262/src/intrinsics/isNaN.mjs create mode 100644 src/engine262/src/intrinsics/parseFloat.mjs create mode 100644 src/engine262/src/intrinsics/parseInt.mjs create mode 100644 src/engine262/src/messages.mjs create mode 100644 src/engine262/src/modules.mjs create mode 100644 src/engine262/src/parse.mjs create mode 100644 src/engine262/src/realm.mjs create mode 100644 src/engine262/src/runtime-semantics/AdditiveExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ArgumentListEvaluation.mjs create mode 100644 src/engine262/src/runtime-semantics/ArrayLiteral.mjs create mode 100644 src/engine262/src/runtime-semantics/ArrowFunction.mjs create mode 100644 src/engine262/src/runtime-semantics/AssignmentExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/AsyncArrowFunction.mjs create mode 100644 src/engine262/src/runtime-semantics/AsyncFunctionExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/AsyncGeneratorExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/AwaitExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/BindingInitialization.mjs create mode 100644 src/engine262/src/runtime-semantics/BitwiseOperators.mjs create mode 100644 src/engine262/src/runtime-semantics/BlockStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/BreakStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/BreakableStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/CallExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ClassDefinition.mjs create mode 100644 src/engine262/src/runtime-semantics/CoalesceExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ConditionalExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ContinueStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/CreateDynamicFunction.mjs create mode 100644 src/engine262/src/runtime-semantics/DebuggerStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/DefineMethod.mjs create mode 100644 src/engine262/src/runtime-semantics/DestructuringAssignmentEvaluation.mjs create mode 100644 src/engine262/src/runtime-semantics/EmptyStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/EqualityExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/EvaluateBody.mjs create mode 100644 src/engine262/src/runtime-semantics/EvaluatePropertyAccess.mjs create mode 100644 src/engine262/src/runtime-semantics/ExponentiationExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ExportDeclaration.mjs create mode 100644 src/engine262/src/runtime-semantics/ExpressionWithComma.mjs create mode 100644 src/engine262/src/runtime-semantics/ForStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/FunctionDeclaration.mjs create mode 100644 src/engine262/src/runtime-semantics/FunctionExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/FunctionStatementList.mjs create mode 100644 src/engine262/src/runtime-semantics/GeneratorExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/GetSubstitution.mjs create mode 100644 src/engine262/src/runtime-semantics/GlobalDeclarationInstantiation.mjs create mode 100644 src/engine262/src/runtime-semantics/HoistableDeclaration.mjs create mode 100644 src/engine262/src/runtime-semantics/Identifier.mjs create mode 100644 src/engine262/src/runtime-semantics/IfStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/ImportCall.mjs create mode 100644 src/engine262/src/runtime-semantics/InstantiateFunctionObject.mjs create mode 100644 src/engine262/src/runtime-semantics/IteratorBindingInitialization.mjs create mode 100644 src/engine262/src/runtime-semantics/KeyedBindingInitialization.mjs create mode 100644 src/engine262/src/runtime-semantics/LabelledStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/LexicalDeclaration.mjs create mode 100644 src/engine262/src/runtime-semantics/Literal.mjs create mode 100644 src/engine262/src/runtime-semantics/LogicalANDExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/LogicalORExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/MV.mjs create mode 100644 src/engine262/src/runtime-semantics/MemberExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/MetaProperty.mjs create mode 100644 src/engine262/src/runtime-semantics/MultiplicativeExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/NamedEvaluation.mjs create mode 100644 src/engine262/src/runtime-semantics/NewExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/NumberToBigInt.mjs create mode 100644 src/engine262/src/runtime-semantics/ObjectLiteral.mjs create mode 100644 src/engine262/src/runtime-semantics/OptionalExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/PropertyBindingInitialization.mjs create mode 100644 src/engine262/src/runtime-semantics/PropertyDefinitionEvaluation.mjs create mode 100644 src/engine262/src/runtime-semantics/PropertyName.mjs create mode 100644 src/engine262/src/runtime-semantics/RegExp.mjs create mode 100644 src/engine262/src/runtime-semantics/RegularExpressionLiteral.mjs create mode 100644 src/engine262/src/runtime-semantics/RelationalOperators.mjs create mode 100644 src/engine262/src/runtime-semantics/RestBindingInitialization.mjs create mode 100644 src/engine262/src/runtime-semantics/ReturnStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/ShiftExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/StringIndexOf.mjs create mode 100644 src/engine262/src/runtime-semantics/StringPad.mjs create mode 100644 src/engine262/src/runtime-semantics/SuperCall.mjs create mode 100644 src/engine262/src/runtime-semantics/SuperProperty.mjs create mode 100644 src/engine262/src/runtime-semantics/SwitchStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/TaggedTemplate.mjs create mode 100644 src/engine262/src/runtime-semantics/TemplateLiteral.mjs create mode 100644 src/engine262/src/runtime-semantics/ThisExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/ThrowStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/TrimString.mjs create mode 100644 src/engine262/src/runtime-semantics/TryStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/UnaryExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/UpdateExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/VariableStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/WithStatement.mjs create mode 100644 src/engine262/src/runtime-semantics/YieldExpression.mjs create mode 100644 src/engine262/src/runtime-semantics/all.mjs create mode 100644 src/engine262/src/static-semantics/BoundNames.mjs create mode 100644 src/engine262/src/static-semantics/ConstructorMethod.mjs create mode 100644 src/engine262/src/static-semantics/ContainsExpression.mjs create mode 100644 src/engine262/src/static-semantics/ContainsUseStrict.mjs create mode 100644 src/engine262/src/static-semantics/DeclarationPart.mjs create mode 100644 src/engine262/src/static-semantics/ExpectedArgumentCount.mjs create mode 100644 src/engine262/src/static-semantics/ExportEntries.mjs create mode 100644 src/engine262/src/static-semantics/ExportEntriesForModule.mjs create mode 100644 src/engine262/src/static-semantics/HasInitializer.mjs create mode 100644 src/engine262/src/static-semantics/HasName.mjs create mode 100644 src/engine262/src/static-semantics/ImportEntries.mjs create mode 100644 src/engine262/src/static-semantics/ImportEntriesForModule.mjs create mode 100644 src/engine262/src/static-semantics/ImportedLocalNames.mjs create mode 100644 src/engine262/src/static-semantics/IsAnonymousFunctionDefinition.mjs create mode 100644 src/engine262/src/static-semantics/IsConstantDeclaration.mjs create mode 100644 src/engine262/src/static-semantics/IsDestructuring.mjs create mode 100644 src/engine262/src/static-semantics/IsFunctionDefinition.mjs create mode 100644 src/engine262/src/static-semantics/IsIdentifierRef.mjs create mode 100644 src/engine262/src/static-semantics/IsInTailPosition.mjs create mode 100644 src/engine262/src/static-semantics/IsSimpleParameterList.mjs create mode 100644 src/engine262/src/static-semantics/IsStatic.mjs create mode 100644 src/engine262/src/static-semantics/IsStrict.mjs create mode 100644 src/engine262/src/static-semantics/LexicallyDeclaredNames.mjs create mode 100644 src/engine262/src/static-semantics/LexicallyScopedDeclarations.mjs create mode 100644 src/engine262/src/static-semantics/MV.mjs create mode 100644 src/engine262/src/static-semantics/ModuleRequests.mjs create mode 100644 src/engine262/src/static-semantics/NonConstructorMethodDefinitions.mjs create mode 100644 src/engine262/src/static-semantics/TRV.mjs create mode 100644 src/engine262/src/static-semantics/TV.mjs create mode 100644 src/engine262/src/static-semantics/TemplateStrings.mjs create mode 100644 src/engine262/src/static-semantics/TopLevelLexicallyDeclaredNames.mjs create mode 100644 src/engine262/src/static-semantics/TopLevelLexicallyScopedDeclarations.mjs create mode 100644 src/engine262/src/static-semantics/TopLevelVarDeclaredNames.mjs create mode 100644 src/engine262/src/static-semantics/TopLevelVarScopedDeclarations.mjs create mode 100644 src/engine262/src/static-semantics/VarDeclaredNames.mjs create mode 100644 src/engine262/src/static-semantics/VarScopedDeclarations.mjs create mode 100644 src/engine262/src/static-semantics/all.mjs create mode 100644 src/engine262/src/value.mjs create mode 100644 src/engine262/test/base.js create mode 100644 src/engine262/test/coverage_root.sh create mode 100644 src/engine262/test/eslint-plugin-engine262/index.js create mode 100644 src/engine262/test/eslint-plugin-engine262/no-use-in-def.js create mode 100644 src/engine262/test/eslint-plugin-engine262/valid-throw.js create mode 100644 src/engine262/test/json/json.js create mode 100644 src/engine262/test/stepped.js create mode 100644 src/engine262/test/supplemental.js create mode 100644 src/engine262/test/test262/features create mode 100644 src/engine262/test/test262/longlist create mode 100644 src/engine262/test/test262/skiplist create mode 100644 src/engine262/test/test262/test262.js create mode 100644 src/engine262/yarn.lock diff --git a/src/engine262/.eslintignore b/src/engine262/.eslintignore new file mode 100644 index 0000000..0078bff --- /dev/null +++ b/src/engine262/.eslintignore @@ -0,0 +1,4 @@ +!.eslintrc.js +*-gen.mjs +test/test262/test262 +test/json/JSONTestSuite diff --git a/src/engine262/.eslintrc.js b/src/engine262/.eslintrc.js new file mode 100644 index 0000000..7801902 --- /dev/null +++ b/src/engine262/.eslintrc.js @@ -0,0 +1,72 @@ +'use strict'; + +const Module = require('module'); + +// eslint-disable-next-line no-underscore-dangle +const ModuleFindPath = Module._findPath; +const hacks = [ + 'eslint-plugin-engine262', +]; +// eslint-disable-next-line no-underscore-dangle +Module._findPath = (request, paths, isMain) => { + const r = ModuleFindPath(request, paths, isMain); + if (!r && hacks.includes(request)) { + return require.resolve(`./test/${request}`); + } + return r; +}; + +module.exports = { + extends: 'airbnb-base', + plugins: ['engine262'], + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 2019, + }, + overrides: [ + { + files: ['*.js'], + parserOptions: { sourceType: 'script' }, + }, + ], + globals: { + BigInt: false, + Atomics: false, + SharedArrayBuffer: false, + WeakRef: false, + globalThis: false, + }, + rules: { + 'arrow-parens': ['error', 'always'], + 'brace-style': ['error', '1tbs', { allowSingleLine: false }], + 'curly': ['error', 'all'], + 'engine262/no-use-in-def': 'error', + 'engine262/valid-throw': 'error', + 'import/order': ['error', { 'newlines-between': 'never' }], + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'no-multiple-empty-lines': ['error', { maxBOF: 0, max: 2 }], + 'no-unused-vars': ['error', { vars: 'all', args: 'after-used', argsIgnorePattern: '^_' }], + 'no-empty': ['error', { allowEmptyCatch: true }], + 'quote-props': ['error', 'consistent'], + 'strict': ['error', 'global'], + + 'camelcase': 'off', + 'class-methods-use-this': 'off', + 'global-require': 'off', + 'import/extensions': 'off', + 'import/no-cycle': 'off', + 'import/no-mutable-exports': 'off', + 'import/prefer-default-export': 'off', + 'lines-between-class-members': 'off', + 'max-classes-per-file': 'off', + 'max-len': 'off', + 'no-constant-condition': 'off', + 'no-continue': 'off', + 'no-else-return': 'off', + 'no-lonely-if': 'off', + 'no-param-reassign': 'off', + 'no-restricted-syntax': 'off', + 'no-use-before-define': 'off', + 'prefer-destructuring': 'off', + }, +}; diff --git a/src/engine262/.github/FUNDING.yml b/src/engine262/.github/FUNDING.yml new file mode 100644 index 0000000..f247960 --- /dev/null +++ b/src/engine262/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [devsnek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with a single custom sponsorship URL diff --git a/src/engine262/.github/workflows/publish.yml b/src/engine262/.github/workflows/publish.yml new file mode 100644 index 0000000..7ec9245 --- /dev/null +++ b/src/engine262/.github/workflows/publish.yml @@ -0,0 +1,55 @@ +name: Publish Package + +on: + push: + branches: + - master + +jobs: + publish-gpr: + runs-on: ubuntu-latest + steps: + # Set everything up + - uses: actions/checkout@v2 + with: + persist-credentials: false + - uses: actions/setup-node@master + with: + node-version: 12 + registry-url: https://npm.pkg.github.com/ + scope: '@engine262' + always-auth: true # required if using yarn + - run: git submodule update --init --recursive + + # Run tests and whatnot + - run: npm install + - run: npm run build + - run: npm run lint + - run: npm run coverage + env: + CONTINUOUS_INTEGRATION: 1 + + # Upload coverage data + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{github.token}} + + # Publish built package + - run: npm publish --access=public + env: + NODE_AUTH_TOKEN: ${{github.token}} + + # Push build to to gh-pages + - run: | + git config --global user.email "gha@example.com" + git config --global user.name "GHA" + git remote add github "https://$GITHUB_ACTOR:$BETTER_GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git" + git fetch github + git checkout gh-pages + cp dist/* . + git add engine262.* + git commit -m "autobuild" || exit 0 # exit silently if nothing changed + git push -u github gh-pages + env: + BETTER_GITHUB_TOKEN: ${{secrets.BETTER_GITHUB_TOKEN}} diff --git a/src/engine262/.gitignore b/src/engine262/.gitignore new file mode 100644 index 0000000..24bcd18 --- /dev/null +++ b/src/engine262/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +test.mjs +.eslintcache +coverage +.nyc_output diff --git a/src/engine262/.gitmodules b/src/engine262/.gitmodules new file mode 100644 index 0000000..11609fb --- /dev/null +++ b/src/engine262/.gitmodules @@ -0,0 +1,6 @@ +[submodule "test/JSONTestSuite"] + path = test/json/JSONTestSuite + url = https://github.com/nst/JSONTestSuite +[submodule "test/test262/test262"] + path = test/test262/test262 + url = https://github.com/tc39/test262 diff --git a/src/engine262/.npmignore b/src/engine262/.npmignore new file mode 100644 index 0000000..2173bf3 --- /dev/null +++ b/src/engine262/.npmignore @@ -0,0 +1,9 @@ +test +src +scripts +coverage +rollup.config.js +.eslintcache +.eslintrc.js +.eslintignore +.travis.yml diff --git a/src/engine262/.npmrc b/src/engine262/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/src/engine262/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/src/engine262/CODE_OF_CONDUCT.md b/src/engine262/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e22b282 --- /dev/null +++ b/src/engine262/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devsnek@users.noreply.github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/src/engine262/LICENSE b/src/engine262/LICENSE new file mode 100644 index 0000000..6cbeb88 --- /dev/null +++ b/src/engine262/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 engine262 Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/src/engine262/README.md b/src/engine262/README.md new file mode 100644 index 0000000..6f23599 --- /dev/null +++ b/src/engine262/README.md @@ -0,0 +1,149 @@ +# engine262 + +An implementation of ECMA-262 in JavaScript + +Goals +- 100% Spec Compliance +- Introspection +- Ease of modification + +Non-Goals +- Speed at the expense of any of the goals + +This project is bound by a [Code of Conduct][COC]. + +Join us on [#engine262 on freenode][irc] ([web][irc-webchat]). + +## Why this exists + +While helping develop new features for JavaScript, I've found that one of the +most useful methods of finding what works and what doesn't is being able to +actually run code using the new feature. [Babel][] is fantastic for this, but +sometimes features just can't be nicely represented with it. Similarly, +implementing a feature in one of the engines is a large undertaking, involving +long compile times and annoying bugs with the optimizing compilers. + +engine262 is a tool to allow JavaScript developers to have a sandbox where new +features can be quickly prototyped and explored. As an example, adding +[do expressions][] to this engine is as simple as the following diff: + +```diff +--- a/src/evaluator.mjs ++++ b/src/evaluator.mjs +@@ -416,6 +416,9 @@ function* Inner_Evaluate_Expression(Expression) { + case isExpressionWithComma(Expression): + return yield* Evaluate_ExpressionWithComma(Expression); + ++ case Expression.type === 'DoExpression': ++ return yield* Evaluate_BlockStatement(Expression.body); ++ + default: + throw new OutOfRange('Evaluate_Expression', Expression); + } +--- a/src/parse.mjs ++++ b/src/parse.mjs +@@ -11,6 +11,17 @@ const Parser = acorn.Parser.extend((P) => class Parse262 extends P { + node.source = () => this.input.slice(node.start, node.end); + return ret; + } ++ ++ parseExprAtom(refDestructuringErrors) { ++ if (this.value === 'do') { ++ // DoExpression : `do` Block ++ this.next(); ++ const node = this.startNode(); ++ node.body = this.parseBlock(); ++ return this.finishNode(node, 'DoExpression'); ++ } ++ return super.parseExprAtom(refDestructuringErrors); ++ } + }); +``` + +This simplicity applies to many other proposals, such as [optional chaining][], +[pattern matching][], [the pipeline operator][], and more. This engine has also +been used to find bugs in ECMA-262 and test262, the test suite for +conforming JavaScript implementations. + +## Requirements + +To run engine262 itself, a engine with support for recent ECMAScript features +is needed. Additionally, the CLI (`bin/engine262.js`) and test262 runner +(`test/test262.js`) require a recent version of Node.js. + +## Using engine262 + +Use it online: https://engine262.js.org + +You can install the latest engine262 build from [GitHub Packages][]. + +If you install it globally, you can use the CLI like so: + +`$ engine262` + +Or, you can install it locally and use the API: + +```js +'use strict'; + +const { Agent, Realm } = require('engine262'); + +const agent = new Agent({ + // onDebugger() {}, + // ensureCanCompileStrings() {}, + // hasSourceTextAvailable() {}, + // onNodeEvaluation() {}, + // features: [], +}); +agent.enter(); + +const realm = new Realm({ + // promiseRejectionTracker() {}, + // resolveImportedModule() {}, + // getImportMetaProperties() {}, + // finalizeImportMeta() {}, +}); + +realm.evaluateScript(` +'use strict'; + +async function* numbers() { + let i = 0; + while (true) { + const n = await Promise.resolve(i++); + yield n; + } +} + +(async () => { + for await (const item of numbers()) { + print(item); + } +})(); +`); + +// a stream of numbers fills your console. it fills you with determination. +``` + +## Related Projects + +Many people and organizations have attempted to write a JavaScript interpreter +in JavaScript much like engine262, with different goals. Some of them are +included here for reference, though engine262 is not based on any of them. + +- https://github.com/facebook/prepack +- https://github.com/mozilla/narcissus +- https://github.com/NeilFraser/JS-Interpreter +- https://github.com/metaes/metaes +- https://github.com/Siubaak/sval + +[Babel]: https://babeljs.io/ +[COC]: https://github.com/engine262/engine262/blob/master/CODE_OF_CONDUCT.md +[do expressions]: https://github.com/tc39/proposal-do-expressions +[irc]: ircs://chat.freenode.net:6697/engine262 +[irc-webchat]: https://webchat.freenode.net/?channels=engine262 +[nodejs/node#25221]: https://github.com/nodejs/node/issues/25221 +[optional chaining]: https://github.com/tc39/proposal-optional-chaining +[pattern matching]: https://github.com/tc39/proposal-pattern-matching +[the pipeline operator]: https://github.com/tc39/proposal-pipeline-operator +[GitHub Packages]: https://github.com/engine262/engine262/packages diff --git a/src/engine262/bin/engine262.js b/src/engine262/bin/engine262.js new file mode 100755 index 0000000..987974e --- /dev/null +++ b/src/engine262/bin/engine262.js @@ -0,0 +1,202 @@ +#!/usr/bin/env node + +'use strict'; + +/* eslint-disable import/order */ + +try { + require('@snek/source-map-support/register'); +} catch { + // empty +} + +const repl = require('repl'); +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const packageJson = require('../package.json'); +const snekparse = require('./snekparse'); +const { + inspect, + Agent, + Completion, + AbruptCompletion, + Value, + Object: APIObject, + Abstract, + Throw, + FEATURES, +} = require('..'); +const { createRealm } = require('./test262_realm'); + +const execArgv = []; +let entry; +const programArgv = []; + +{ + let target = execArgv; + process.argv.slice(2).forEach((a) => { + if (a.startsWith('--')) { + target.push(a); + } else if (!entry) { + entry = a; + target = programArgv; + } else { + target.push(a); + } + }); +} + +const help = ` +engine262 v${require('../package.json').version} + +Usage: + + engine262 [options] + engine262 [options] [input file] + engine262 [input file] + +Options: + + -h, --help Show help (this screen) + -m, --module Evaluate contents of input-file as a module. + Must be followed by input-file + --features=... A comma separated list of features. If no features + are provided, the available features are listed. + +`; + +const argv = snekparse(execArgv); + +if (argv.h || argv.help) { + process.stdout.write(help); + process.exit(0); +} else if (argv.features === true) { + FEATURES.forEach(({ name, url }) => { + process.stdout.write(`${name} - ${url}\n`); + }); + process.exit(0); +} + +let features; +if (argv.features === 'all') { + features = FEATURES.map((f) => f.name); +} else if (argv.features) { + features = argv.features.split(','); +} else { + features = []; +} + +const agent = new Agent({ features }); +agent.enter(); + +const { realm, resolverCache } = createRealm({ printCompatMode: true }); +{ + const console = new APIObject(realm); + Abstract.CreateDataProperty(realm.global, new Value(realm, 'console'), console); + + const format = (args) => args.map((a, i) => { + if (i === 0 && Abstract.Type(a) === 'String') { + return a.stringValue(); + } + return inspect(a, realm); + }).join(' '); + + const log = new Value(realm, (args) => { + process.stdout.write(`${format(args)}\n`); + return Value.undefined; + }); + + Abstract.CreateDataProperty(console, new Value(realm, 'log'), log); + + const error = new Value(realm, (args) => { + process.stderr.write(`${format(args)}\n`); + return Value.undefined; + }); + + Abstract.CreateDataProperty(console, new Value(realm, 'error'), error); + + const debug = new Value(realm, (args) => { + process.stderr.write(`${util.format(...args)}\n`); + return Value.undefined; + }); + + Abstract.CreateDataProperty(console, new Value(realm, 'debug'), debug); +} + +if (argv.inspector) { + const inspector = require('../inspector'); + inspector.attachRealm(realm); +} + +function oneShotEval(source, filename) { + let result; + if (argv.m || argv.module || filename.endsWith('.mjs')) { + result = realm.createSourceTextModule(filename, source); + if (!(result instanceof AbruptCompletion)) { + const module = result; + resolverCache.set(filename, result); + result = module.Link(); + if (!(result instanceof AbruptCompletion)) { + result = module.Evaluate(); + } + if (!(result instanceof AbruptCompletion)) { + if (result.PromiseState === 'rejected') { + result = Throw(realm, result.PromiseResult); + } + } + } + } else { + result = realm.evaluateScript(source, { specifier: filename }); + } + if (result instanceof AbruptCompletion) { + let inspected; + if (Abstract.Type(result.Value) === 'Object') { + const errorToString = realm.realm.Intrinsics['%Error.prototype%'].properties.get(new Value(realm, 'toString')).Value; + inspected = Abstract.Call(errorToString, result.Value).stringValue(); + } else { + inspected = inspect(result, realm); + } + process.stderr.write(`${inspected}\n`); + process.exit(1); + } else { + process.exit(0); + } +} + +if (entry) { + const source = fs.readFileSync(entry, 'utf8'); + oneShotEval(source, path.resolve(entry)); +} else if (!process.stdin.isTTY) { + process.stdin.setEncoding('utf8'); + let source = ''; + process.stdin.on('data', (data) => { + source += data; + }); + process.stdin.once('end', () => { + oneShotEval(source, process.cwd()); + }); +} else { + process.stdout.write(`${packageJson.name} v${packageJson.version} +Please report bugs to ${packageJson.bugs.url} +`); + repl.start({ + prompt: '> ', + eval: (cmd, context, filename, callback) => { + try { + const result = realm.evaluateScript(cmd, { specifier: process.cwd() }); + callback(null, result); + } catch (e) { + callback(e, null); + } + }, + preview: false, + completer: () => [], + writer: (o) => { + if (o instanceof Value || o instanceof Completion) { + return inspect(o, realm); + } + return util.inspect(o); + }, + }); +} diff --git a/src/engine262/bin/snekparse.js b/src/engine262/bin/snekparse.js new file mode 100644 index 0000000..a5d22c7 --- /dev/null +++ b/src/engine262/bin/snekparse.js @@ -0,0 +1,76 @@ +'use strict'; + +function snekparse(args) { + if (typeof args === 'string') { + args = args.split(' '); + } + const argv = []; + for (let i = 0; i < args.length; i += 1) { + const arg = args[i]; + if (/^--.+=/.test(arg)) { + const match = arg.match(/^--([^=]+)=([\s\S]*)$/); + argv[match[1]] = match[2]; + } else if (/^--no-.+/.test(arg)) { + argv[arg.match(/^--no-(.+)/)[1]] = false; + } else if (/^--.+/.test(arg)) { + const key = arg.match(/^--(.+)/)[1]; + const next = args[i + 1]; + if (typeof next !== 'undefined' && !/^-/.test(next)) { + argv[key] = next; + } else if (/^(true|false)$/.test(next)) { + argv[key] = next === 'true'; + } else { + argv[key] = true; + } + } else if (/^-[^-]+/.test(arg)) { + const letters = arg.slice(1, -1).split(''); + let broken = false; + for (const j of letters) { + const next = arg.slice(j + 2); + + if (next === '-') { + argv[letters[j]] = next; + continue; + } + + if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { + argv[letters[j]] = next.split('=')[1]; + broken = true; + break; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + argv[letters[j]] = next; + broken = true; + break; + } + + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + argv[letters[j]] = arg.slice(j + 2); + broken = true; + break; + } else { + argv[letters[j]] = true; + } + } + + const key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])) { + argv[key] = args[i + 1]; + } else if (args[i + 1] && /true|false/.test(args[i + 1])) { + argv[key] = args[i + 1] === 'true'; + } else { + argv[key] = true; + } + } + } else { + argv.push(arg); + } + } + + return argv; +} + +module.exports = snekparse; diff --git a/src/engine262/bin/test262_realm.js b/src/engine262/bin/test262_realm.js new file mode 100644 index 0000000..15f38b5 --- /dev/null +++ b/src/engine262/bin/test262_realm.js @@ -0,0 +1,117 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const { + Realm, Throw, + Abstract, Value, + Object: APIObject, + ToString, + AbruptCompletion, + inspect, +} = require('..'); + +const createRealm = ({ printCompatMode = false } = {}) => { + const resolverCache = new Map(); + const trackedPromises = new Set(); + + const realm = new Realm({ + promiseRejectionTracker(promise, operation) { + switch (operation) { + case 'reject': + trackedPromises.add(promise); + break; + case 'handle': + trackedPromises.delete(promise); + break; + default: + throw new RangeError('promiseRejectionTracker', operation); + } + }, + resolveImportedModule(referencingScriptOrModule, specifier) { + try { + const base = path.dirname(referencingScriptOrModule.specifier); + const resolved = path.resolve(base, specifier); + if (resolverCache.has(resolved)) { + return resolverCache.get(resolved); + } + const source = fs.readFileSync(resolved, 'utf8'); + const m = realm.createSourceTextModule(resolved, source); + resolverCache.set(resolved, m); + return m; + } catch (e) { + return Throw(realm, e.name, e.message); + } + }, + }); + + const $262 = new APIObject(realm); + + let printHandle; + const setPrintHandle = (f) => { + printHandle = f; + }; + Abstract.CreateDataProperty(realm.global, new Value(realm, 'print'), new Value(realm, (args) => { + if (printHandle !== undefined) { + printHandle(...args); + } else { + if (printCompatMode) { + for (let i = 0; i < args.length; i += 1) { + const arg = args[i]; + const s = ToString(realm, arg); + if (s instanceof AbruptCompletion) { + return s; + } + process.stdout.write(s); + if (i !== args.length - 1) { + process.stdout.write(' '); + } + } + process.stdout.write('\n'); + return Value.undefined; + } else { + const formatted = args.map((a, i) => { + if (i === 0 && Abstract.Type(a) === 'String') { + return a.stringValue(); + } + return inspect(a, realm); + }).join(' '); + console.log(formatted); // eslint-disable-line no-console + } + } + return Value.undefined; + })); + + [ + ['global', realm.global], + ['createRealm', () => { + const info = createRealm(); + return info.$262; + }], + ['evalScript', ([sourceText]) => realm.evaluateScript(sourceText.stringValue())], + ['detachArrayBuffer', ([arrayBuffer]) => Abstract.DetachArrayBuffer(arrayBuffer)], + ['gc', () => Value.undefined], + ['spec', ([v]) => { + if (v.nativeFunction && v.nativeFunction.section) { + return new Value(realm, v.nativeFunction.section); + } + return Value.undefined; + }], + ].forEach(([name, value]) => { + const v = value instanceof Value ? value : new Value(realm, value); + Abstract.CreateDataProperty($262, new Value(realm, name), v); + }); + + Abstract.CreateDataProperty(realm.global, new Value(realm, '$262'), $262); + Abstract.CreateDataProperty(realm.global, new Value(realm, '$'), $262); + + return { + realm, + $262, + resolverCache, + trackedPromises, + setPrintHandle, + }; +}; + +module.exports = { createRealm }; diff --git a/src/engine262/inspector/context.js b/src/engine262/inspector/context.js new file mode 100644 index 0000000..7a01841 --- /dev/null +++ b/src/engine262/inspector/context.js @@ -0,0 +1,187 @@ +'use strict'; + +const engine262 = require('..'); + +const contexts = []; + +class InspectorContext { + constructor(realm) { + this.realm = realm; + this.idToObject = new Map(); + this.objectToId = new Map(); + this.objectCounter = 0; + this.previewStack = []; + } + + internObject(object, group = 'default') { + if (this.objectToId.has(object)) { + return this.objectToId.get(object); + } + const id = `${group}:${this.objectCounter}`; + this.objectCounter += 1; + this.idToObject.set(id, object); + this.objectToId.set(object, id); + return id; + } + + releaseObjectGroup(group) { + for (const [id, object] of this.idToObject.entries()) { + if (id.startsWith(group)) { + this.idToObject.delete(id); + this.objectToId.delete(object); + } + } + } + + getObject(objectId) { + return this.idToObject.get(objectId); + } + + toRemoteObject(object, options) { + const result = {}; + switch (engine262.Abstract.Type(object)) { + case 'Object': + result.objectId = this.internObject(object, options.objectGroup); + if ('Call' in object) { + result.type = 'function'; + } else { + result.type = 'object'; + if ('PromiseState' in object) { + result.subtype = 'promise'; + } else if ('MapData' in object) { + result.subtype = 'map'; + } else if ('SetData' in object) { + result.subtype = 'set'; + } else if ('ErrorData' in object) { + result.subtype = 'error'; + } else if ('TypedArrayName' in object) { + result.subtype = 'typedarray'; + } else if ('DataView' in object) { + result.subtype = 'dataview'; + } else if ('ProxyTarget' in object) { + result.subtype = 'proxy'; + } else if ('DateValue' in object) { + result.subtype = 'date'; + } else if ('GeneratorState' in object) { + result.subtype = 'generator'; + } else if (engine262.Abstract.IsArray(object) === engine262.Value.true) { + result.subtype = 'array'; + } + } + break; + case 'Null': + result.type = 'object'; + result.subtype = 'null'; + result.value = null; + break; + case 'Undefined': + result.type = 'undefined'; + break; + case 'String': + result.type = 'string'; + result.value = object.stringValue(); + break; + case 'Number': { + result.type = 'number'; + const v = object.numberValue(); + if (!Number.isFinite(v)) { + result.unserializableValue = v.toString(); + } else { + result.value = v; + } + break; + } + case 'Boolean': + result.type = 'boolean'; + result.value = object.booleanValue(); + break; + case 'BigInt': + result.type = 'bigint'; + result.unserializableValue = `${object.bigintValue().toString()}n`; + break; + case 'Symbol': + result.type = 'symbol'; + result.description = object.Description === engine262.Value.undefined + ? undefined + : object.Description.stringValue(); + break; + default: + throw new RangeError(); + } + if (options.generatePreview + && result.type === 'object' + && result.subtype !== 'null' + && !this.previewStack.includes(object)) { + this.previewStack.push(object); + result.preview = { + type: result.type, + subtype: result.subtype, + overflow: false, + properties: this.getProperties(object, options), + }; + this.previewStack.pop(); + } + return result; + } + + getProperties(object, options) { + const wrap = (v) => this.toRemoteObject(v, options); + + const keys = object.OwnPropertyKeys(); + if (keys instanceof engine262.AbruptCompletion) { + return keys; + } + + const properties = []; + for (const key of keys) { + const desc = object.GetOwnProperty(key); + if (desc instanceof engine262.AbruptCompletion) { + return desc; + } + const descriptor = { + name: key.stringValue + ? key.stringValue() + : undefined, + value: desc.Value ? wrap(desc.Value) : undefined, + writable: desc.Writable === engine262.Value.true, + get: desc.Get ? wrap(desc.Get) : undefined, + set: desc.Set ? wrap(desc.Set) : undefined, + configurable: desc.Configurable === engine262.Value.true, + enumerable: desc.Enumerable === engine262.Value.true, + wasThrown: false, + isOwn: true, + symbol: key.stringValue ? undefined : wrap(key), + }; + properties.push(descriptor); + } + + return properties; + } + + createEvaluationResult(completion, options) { + if (completion instanceof engine262.AbruptCompletion) { + return { + exceptionDetails: { + text: 'uh oh', + lineNumber: 0, + columnNumber: 0, + exception: this.toRemoteObject(completion.Value, options), + }, + }; + } else { + return { + result: this.toRemoteObject(completion.Value, options), + }; + } + } +} + +function attachRealm(realm) { + contexts.push(new InspectorContext(realm)); +} + +function getContext(id) { + return contexts[id] || contexts[0]; +} + +module.exports = { attachRealm, getContext }; diff --git a/src/engine262/inspector/index.js b/src/engine262/inspector/index.js new file mode 100644 index 0000000..58301cc --- /dev/null +++ b/src/engine262/inspector/index.js @@ -0,0 +1,6 @@ +'use strict'; + +require('./server'); +const { attachRealm } = require('./context'); + +module.exports = { attachRealm }; diff --git a/src/engine262/inspector/js_protocol.json b/src/engine262/inspector/js_protocol.json new file mode 100644 index 0000000..a200a5b --- /dev/null +++ b/src/engine262/inspector/js_protocol.json @@ -0,0 +1,3288 @@ +{ + "version": { + "major": "1", + "minor": "3" + }, + "domains": [ + { + "domain": "Console", + "description": "This domain is deprecated - use Runtime or Log instead.", + "deprecated": true, + "dependencies": [ + "Runtime" + ], + "types": [ + { + "id": "ConsoleMessage", + "description": "Console message.", + "type": "object", + "properties": [ + { + "name": "source", + "description": "Message source.", + "type": "string", + "enum": [ + "xml", + "javascript", + "network", + "console-api", + "storage", + "appcache", + "rendering", + "security", + "other", + "deprecation", + "worker" + ] + }, + { + "name": "level", + "description": "Message severity.", + "type": "string", + "enum": [ + "log", + "warning", + "error", + "debug", + "info" + ] + }, + { + "name": "text", + "description": "Message text.", + "type": "string" + }, + { + "name": "url", + "description": "URL of the message origin.", + "optional": true, + "type": "string" + }, + { + "name": "line", + "description": "Line number in the resource that generated this message (1-based).", + "optional": true, + "type": "integer" + }, + { + "name": "column", + "description": "Column number in the resource that generated this message (1-based).", + "optional": true, + "type": "integer" + } + ] + } + ], + "commands": [ + { + "name": "clearMessages", + "description": "Does nothing." + }, + { + "name": "disable", + "description": "Disables console domain, prevents further console messages from being reported to the client." + }, + { + "name": "enable", + "description": "Enables console domain, sends the messages collected so far to the client by means of the\n`messageAdded` notification." + } + ], + "events": [ + { + "name": "messageAdded", + "description": "Issued when new console message is added.", + "parameters": [ + { + "name": "message", + "description": "Console message that has been added.", + "$ref": "ConsoleMessage" + } + ] + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing\nbreakpoints, stepping through execution, exploring stack traces, etc.", + "dependencies": [ + "Runtime" + ], + "types": [ + { + "id": "BreakpointId", + "description": "Breakpoint identifier.", + "type": "string" + }, + { + "id": "CallFrameId", + "description": "Call frame identifier.", + "type": "string" + }, + { + "id": "Location", + "description": "Location in the source code.", + "type": "object", + "properties": [ + { + "name": "scriptId", + "description": "Script identifier as reported in the `Debugger.scriptParsed`.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "lineNumber", + "description": "Line number in the script (0-based).", + "type": "integer" + }, + { + "name": "columnNumber", + "description": "Column number in the script (0-based).", + "optional": true, + "type": "integer" + } + ] + }, + { + "id": "ScriptPosition", + "description": "Location in the source code.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "lineNumber", + "type": "integer" + }, + { + "name": "columnNumber", + "type": "integer" + } + ] + }, + { + "id": "CallFrame", + "description": "JavaScript call frame. Array of call frames form the call stack.", + "type": "object", + "properties": [ + { + "name": "callFrameId", + "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused.", + "$ref": "CallFrameId" + }, + { + "name": "functionName", + "description": "Name of the JavaScript function called on this call frame.", + "type": "string" + }, + { + "name": "functionLocation", + "description": "Location in the source code.", + "optional": true, + "$ref": "Location" + }, + { + "name": "location", + "description": "Location in the source code.", + "$ref": "Location" + }, + { + "name": "url", + "description": "JavaScript script name or url.", + "type": "string" + }, + { + "name": "scopeChain", + "description": "Scope chain for this call frame.", + "type": "array", + "items": { + "$ref": "Scope" + } + }, + { + "name": "this", + "description": "`this` object for this call frame.", + "$ref": "Runtime.RemoteObject" + }, + { + "name": "returnValue", + "description": "The value being returned, if the function is at return point.", + "optional": true, + "$ref": "Runtime.RemoteObject" + } + ] + }, + { + "id": "Scope", + "description": "Scope description.", + "type": "object", + "properties": [ + { + "name": "type", + "description": "Scope type.", + "type": "string", + "enum": [ + "global", + "local", + "with", + "closure", + "catch", + "block", + "script", + "eval", + "module" + ] + }, + { + "name": "object", + "description": "Object representing the scope. For `global` and `with` scopes it represents the actual\nobject; for the rest of the scopes, it is artificial transient object enumerating scope\nvariables as its properties.", + "$ref": "Runtime.RemoteObject" + }, + { + "name": "name", + "optional": true, + "type": "string" + }, + { + "name": "startLocation", + "description": "Location in the source code where scope starts", + "optional": true, + "$ref": "Location" + }, + { + "name": "endLocation", + "description": "Location in the source code where scope ends", + "optional": true, + "$ref": "Location" + } + ] + }, + { + "id": "SearchMatch", + "description": "Search match for resource.", + "type": "object", + "properties": [ + { + "name": "lineNumber", + "description": "Line number in resource content.", + "type": "number" + }, + { + "name": "lineContent", + "description": "Line with match content.", + "type": "string" + } + ] + }, + { + "id": "BreakLocation", + "type": "object", + "properties": [ + { + "name": "scriptId", + "description": "Script identifier as reported in the `Debugger.scriptParsed`.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "lineNumber", + "description": "Line number in the script (0-based).", + "type": "integer" + }, + { + "name": "columnNumber", + "description": "Column number in the script (0-based).", + "optional": true, + "type": "integer" + }, + { + "name": "type", + "optional": true, + "type": "string", + "enum": [ + "debuggerStatement", + "call", + "return" + ] + } + ] + } + ], + "commands": [ + { + "name": "continueToLocation", + "description": "Continues execution until specific location is reached.", + "parameters": [ + { + "name": "location", + "description": "Location to continue to.", + "$ref": "Location" + }, + { + "name": "targetCallFrames", + "optional": true, + "type": "string", + "enum": [ + "any", + "current" + ] + } + ] + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been\nenabled until the result for this command is received.", + "parameters": [ + { + "name": "maxScriptsCacheSize", + "description": "The maximum size in bytes of collected scripts (not referenced by other heap objects)\nthe debugger can hold. Puts no limit if paramter is omitted.", + "experimental": true, + "optional": true, + "type": "number" + } + ], + "returns": [ + { + "name": "debuggerId", + "description": "Unique identifier of the debugger.", + "experimental": true, + "$ref": "Runtime.UniqueDebuggerId" + } + ] + }, + { + "name": "evaluateOnCallFrame", + "description": "Evaluates expression on a given call frame.", + "parameters": [ + { + "name": "callFrameId", + "description": "Call frame identifier to evaluate on.", + "$ref": "CallFrameId" + }, + { + "name": "expression", + "description": "Expression to evaluate.", + "type": "string" + }, + { + "name": "objectGroup", + "description": "String object group name to put result into (allows rapid releasing resulting object handles\nusing `releaseObjectGroup`).", + "optional": true, + "type": "string" + }, + { + "name": "includeCommandLineAPI", + "description": "Specifies whether command line API should be available to the evaluated expression, defaults\nto false.", + "optional": true, + "type": "boolean" + }, + { + "name": "silent", + "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.", + "optional": true, + "type": "boolean" + }, + { + "name": "returnByValue", + "description": "Whether the result is expected to be a JSON object that should be sent by value.", + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the result.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "throwOnSideEffect", + "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation.", + "optional": true, + "type": "boolean" + }, + { + "name": "timeout", + "description": "Terminate execution after timing out (number of milliseconds).", + "experimental": true, + "optional": true, + "$ref": "Runtime.TimeDelta" + } + ], + "returns": [ + { + "name": "result", + "description": "Object wrapper for the evaluation result.", + "$ref": "Runtime.RemoteObject" + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "Runtime.ExceptionDetails" + } + ] + }, + { + "name": "getPossibleBreakpoints", + "description": "Returns possible locations for breakpoint. scriptId in start and end range locations should be\nthe same.", + "parameters": [ + { + "name": "start", + "description": "Start of range to search possible breakpoint locations in.", + "$ref": "Location" + }, + { + "name": "end", + "description": "End of range to search possible breakpoint locations in (excluding). When not specified, end\nof scripts is used as end of range.", + "optional": true, + "$ref": "Location" + }, + { + "name": "restrictToFunction", + "description": "Only consider locations which are in the same (non-nested) function as start.", + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "locations", + "description": "List of the possible breakpoint locations.", + "type": "array", + "items": { + "$ref": "BreakLocation" + } + } + ] + }, + { + "name": "getScriptSource", + "description": "Returns source for the script with given id.", + "parameters": [ + { + "name": "scriptId", + "description": "Id of the script to get source for.", + "$ref": "Runtime.ScriptId" + } + ], + "returns": [ + { + "name": "scriptSource", + "description": "Script source (empty in case of Wasm bytecode).", + "type": "string" + }, + { + "name": "bytecode", + "description": "Wasm bytecode.", + "optional": true, + "type": "string" + } + ] + }, + { + "name": "getWasmBytecode", + "description": "This command is deprecated. Use getScriptSource instead.", + "deprecated": true, + "parameters": [ + { + "name": "scriptId", + "description": "Id of the Wasm script to get source for.", + "$ref": "Runtime.ScriptId" + } + ], + "returns": [ + { + "name": "bytecode", + "description": "Script source.", + "type": "string" + } + ] + }, + { + "name": "getStackTrace", + "description": "Returns stack trace with given `stackTraceId`.", + "experimental": true, + "parameters": [ + { + "name": "stackTraceId", + "$ref": "Runtime.StackTraceId" + } + ], + "returns": [ + { + "name": "stackTrace", + "$ref": "Runtime.StackTrace" + } + ] + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "pauseOnAsyncCall", + "experimental": true, + "deprecated": true, + "parameters": [ + { + "name": "parentStackTraceId", + "description": "Debugger will pause when async call with given stack trace is started.", + "$ref": "Runtime.StackTraceId" + } + ] + }, + { + "name": "removeBreakpoint", + "description": "Removes JavaScript breakpoint.", + "parameters": [ + { + "name": "breakpointId", + "$ref": "BreakpointId" + } + ] + }, + { + "name": "restartFrame", + "description": "Restarts particular call frame from the beginning.", + "parameters": [ + { + "name": "callFrameId", + "description": "Call frame identifier to evaluate on.", + "$ref": "CallFrameId" + } + ], + "returns": [ + { + "name": "callFrames", + "description": "New stack trace.", + "type": "array", + "items": { + "$ref": "CallFrame" + } + }, + { + "name": "asyncStackTrace", + "description": "Async stack trace, if any.", + "optional": true, + "$ref": "Runtime.StackTrace" + }, + { + "name": "asyncStackTraceId", + "description": "Async stack trace, if any.", + "experimental": true, + "optional": true, + "$ref": "Runtime.StackTraceId" + } + ] + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "description": "Searches for given string in script content.", + "parameters": [ + { + "name": "scriptId", + "description": "Id of the script to search in.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "query", + "description": "String to search for.", + "type": "string" + }, + { + "name": "caseSensitive", + "description": "If true, search is case sensitive.", + "optional": true, + "type": "boolean" + }, + { + "name": "isRegex", + "description": "If true, treats string parameter as regex.", + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "result", + "description": "List of search matches.", + "type": "array", + "items": { + "$ref": "SearchMatch" + } + } + ] + }, + { + "name": "setAsyncCallStackDepth", + "description": "Enables or disables async call stacks tracking.", + "parameters": [ + { + "name": "maxDepth", + "description": "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default).", + "type": "integer" + } + ] + }, + { + "name": "setBlackboxPatterns", + "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in\nscripts with url matching one of the patterns. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.", + "experimental": true, + "parameters": [ + { + "name": "patterns", + "description": "Array of regexps that will be used to check script url for blackbox state.", + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + { + "name": "setBlackboxedRanges", + "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted\nscripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful.\nPositions array contains positions where blackbox state is changed. First interval isn't\nblackboxed. Array should be sorted.", + "experimental": true, + "parameters": [ + { + "name": "scriptId", + "description": "Id of the script.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "positions", + "type": "array", + "items": { + "$ref": "ScriptPosition" + } + } + ] + }, + { + "name": "setBreakpoint", + "description": "Sets JavaScript breakpoint at a given location.", + "parameters": [ + { + "name": "location", + "description": "Location to set breakpoint in.", + "$ref": "Location" + }, + { + "name": "condition", + "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "breakpointId", + "description": "Id of the created breakpoint for further reference.", + "$ref": "BreakpointId" + }, + { + "name": "actualLocation", + "description": "Location this breakpoint resolved into.", + "$ref": "Location" + } + ] + }, + { + "name": "setInstrumentationBreakpoint", + "description": "Sets instrumentation breakpoint.", + "parameters": [ + { + "name": "instrumentation", + "description": "Instrumentation name.", + "type": "string", + "enum": [ + "beforeScriptExecution", + "beforeScriptWithSourceMapExecution" + ] + } + ], + "returns": [ + { + "name": "breakpointId", + "description": "Id of the created breakpoint for further reference.", + "$ref": "BreakpointId" + } + ] + }, + { + "name": "setBreakpointByUrl", + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this\ncommand is issued, all existing parsed scripts will have breakpoints resolved and returned in\n`locations` property. Further matching script parsing will result in subsequent\n`breakpointResolved` events issued. This logical breakpoint will survive page reloads.", + "parameters": [ + { + "name": "lineNumber", + "description": "Line number to set breakpoint at.", + "type": "integer" + }, + { + "name": "url", + "description": "URL of the resources to set breakpoint on.", + "optional": true, + "type": "string" + }, + { + "name": "urlRegex", + "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or\n`urlRegex` must be specified.", + "optional": true, + "type": "string" + }, + { + "name": "scriptHash", + "description": "Script hash of the resources to set breakpoint on.", + "optional": true, + "type": "string" + }, + { + "name": "columnNumber", + "description": "Offset in the line to set breakpoint at.", + "optional": true, + "type": "integer" + }, + { + "name": "condition", + "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "breakpointId", + "description": "Id of the created breakpoint for further reference.", + "$ref": "BreakpointId" + }, + { + "name": "locations", + "description": "List of the locations this breakpoint resolved into upon addition.", + "type": "array", + "items": { + "$ref": "Location" + } + } + ] + }, + { + "name": "setBreakpointOnFunctionCall", + "description": "Sets JavaScript breakpoint before each call to the given function.\nIf another function was created from the same source as a given one,\ncalling it will also trigger the breakpoint.", + "experimental": true, + "parameters": [ + { + "name": "objectId", + "description": "Function object id.", + "$ref": "Runtime.RemoteObjectId" + }, + { + "name": "condition", + "description": "Expression to use as a breakpoint condition. When specified, debugger will\nstop on the breakpoint if this expression evaluates to true.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "breakpointId", + "description": "Id of the created breakpoint for further reference.", + "$ref": "BreakpointId" + } + ] + }, + { + "name": "setBreakpointsActive", + "description": "Activates / deactivates all breakpoints on the page.", + "parameters": [ + { + "name": "active", + "description": "New value for breakpoints active state.", + "type": "boolean" + } + ] + }, + { + "name": "setPauseOnExceptions", + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or\nno exceptions. Initial pause on exceptions state is `none`.", + "parameters": [ + { + "name": "state", + "description": "Pause on exceptions mode.", + "type": "string", + "enum": [ + "none", + "uncaught", + "all" + ] + } + ] + }, + { + "name": "setReturnValue", + "description": "Changes return value in top frame. Available only at return break position.", + "experimental": true, + "parameters": [ + { + "name": "newValue", + "description": "New return value.", + "$ref": "Runtime.CallArgument" + } + ] + }, + { + "name": "setScriptSource", + "description": "Edits JavaScript source live.", + "parameters": [ + { + "name": "scriptId", + "description": "Id of the script to edit.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "scriptSource", + "description": "New content of the script.", + "type": "string" + }, + { + "name": "dryRun", + "description": "If true the change will not actually be applied. Dry run may be used to get result\ndescription without actually modifying the code.", + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "callFrames", + "description": "New stack trace in case editing has happened while VM was stopped.", + "optional": true, + "type": "array", + "items": { + "$ref": "CallFrame" + } + }, + { + "name": "stackChanged", + "description": "Whether current call stack was modified after applying the changes.", + "optional": true, + "type": "boolean" + }, + { + "name": "asyncStackTrace", + "description": "Async stack trace, if any.", + "optional": true, + "$ref": "Runtime.StackTrace" + }, + { + "name": "asyncStackTraceId", + "description": "Async stack trace, if any.", + "experimental": true, + "optional": true, + "$ref": "Runtime.StackTraceId" + }, + { + "name": "exceptionDetails", + "description": "Exception details if any.", + "optional": true, + "$ref": "Runtime.ExceptionDetails" + } + ] + }, + { + "name": "setSkipAllPauses", + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc).", + "parameters": [ + { + "name": "skip", + "description": "New value for skip pauses state.", + "type": "boolean" + } + ] + }, + { + "name": "setVariableValue", + "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be\nmutated manually.", + "parameters": [ + { + "name": "scopeNumber", + "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch'\nscope types are allowed. Other scopes could be manipulated manually.", + "type": "integer" + }, + { + "name": "variableName", + "description": "Variable name.", + "type": "string" + }, + { + "name": "newValue", + "description": "New variable value.", + "$ref": "Runtime.CallArgument" + }, + { + "name": "callFrameId", + "description": "Id of callframe that holds variable.", + "$ref": "CallFrameId" + } + ] + }, + { + "name": "stepInto", + "description": "Steps into the function call.", + "parameters": [ + { + "name": "breakOnAsyncCall", + "description": "Debugger will pause on the execution of the first async task which was scheduled\nbefore next pause.", + "experimental": true, + "optional": true, + "type": "boolean" + } + ] + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + } + ], + "events": [ + { + "name": "breakpointResolved", + "description": "Fired when breakpoint is resolved to an actual script and location.", + "parameters": [ + { + "name": "breakpointId", + "description": "Breakpoint unique identifier.", + "$ref": "BreakpointId" + }, + { + "name": "location", + "description": "Actual breakpoint location.", + "$ref": "Location" + } + ] + }, + { + "name": "paused", + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.", + "parameters": [ + { + "name": "callFrames", + "description": "Call stack the virtual machine stopped on.", + "type": "array", + "items": { + "$ref": "CallFrame" + } + }, + { + "name": "reason", + "description": "Pause reason.", + "type": "string", + "enum": [ + "ambiguous", + "assert", + "debugCommand", + "DOM", + "EventListener", + "exception", + "instrumentation", + "OOM", + "other", + "promiseRejection", + "XHR" + ] + }, + { + "name": "data", + "description": "Object containing break-specific auxiliary properties.", + "optional": true, + "type": "object" + }, + { + "name": "hitBreakpoints", + "description": "Hit breakpoints IDs", + "optional": true, + "type": "array", + "items": { + "type": "string" + } + }, + { + "name": "asyncStackTrace", + "description": "Async stack trace, if any.", + "optional": true, + "$ref": "Runtime.StackTrace" + }, + { + "name": "asyncStackTraceId", + "description": "Async stack trace, if any.", + "experimental": true, + "optional": true, + "$ref": "Runtime.StackTraceId" + }, + { + "name": "asyncCallStackTraceId", + "description": "Never present, will be removed.", + "experimental": true, + "deprecated": true, + "optional": true, + "$ref": "Runtime.StackTraceId" + } + ] + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + }, + { + "name": "scriptFailedToParse", + "description": "Fired when virtual machine fails to parse the script.", + "parameters": [ + { + "name": "scriptId", + "description": "Identifier of the script parsed.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "url", + "description": "URL or name of the script parsed (if any).", + "type": "string" + }, + { + "name": "startLine", + "description": "Line offset of the script within the resource with given URL (for script tags).", + "type": "integer" + }, + { + "name": "startColumn", + "description": "Column offset of the script within the resource with given URL.", + "type": "integer" + }, + { + "name": "endLine", + "description": "Last line of the script.", + "type": "integer" + }, + { + "name": "endColumn", + "description": "Length of the last line of the script.", + "type": "integer" + }, + { + "name": "executionContextId", + "description": "Specifies script creation context.", + "$ref": "Runtime.ExecutionContextId" + }, + { + "name": "hash", + "description": "Content hash of the script.", + "type": "string" + }, + { + "name": "executionContextAuxData", + "description": "Embedder-specific auxiliary data.", + "optional": true, + "type": "object" + }, + { + "name": "sourceMapURL", + "description": "URL of source map associated with script (if any).", + "optional": true, + "type": "string" + }, + { + "name": "hasSourceURL", + "description": "True, if this script has sourceURL.", + "optional": true, + "type": "boolean" + }, + { + "name": "isModule", + "description": "True, if this script is ES6 module.", + "optional": true, + "type": "boolean" + }, + { + "name": "length", + "description": "This script length.", + "optional": true, + "type": "integer" + }, + { + "name": "stackTrace", + "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", + "experimental": true, + "optional": true, + "$ref": "Runtime.StackTrace" + } + ] + }, + { + "name": "scriptParsed", + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected\nscripts upon enabling debugger.", + "parameters": [ + { + "name": "scriptId", + "description": "Identifier of the script parsed.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "url", + "description": "URL or name of the script parsed (if any).", + "type": "string" + }, + { + "name": "startLine", + "description": "Line offset of the script within the resource with given URL (for script tags).", + "type": "integer" + }, + { + "name": "startColumn", + "description": "Column offset of the script within the resource with given URL.", + "type": "integer" + }, + { + "name": "endLine", + "description": "Last line of the script.", + "type": "integer" + }, + { + "name": "endColumn", + "description": "Length of the last line of the script.", + "type": "integer" + }, + { + "name": "executionContextId", + "description": "Specifies script creation context.", + "$ref": "Runtime.ExecutionContextId" + }, + { + "name": "hash", + "description": "Content hash of the script.", + "type": "string" + }, + { + "name": "executionContextAuxData", + "description": "Embedder-specific auxiliary data.", + "optional": true, + "type": "object" + }, + { + "name": "isLiveEdit", + "description": "True, if this script is generated as a result of the live edit operation.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "sourceMapURL", + "description": "URL of source map associated with script (if any).", + "optional": true, + "type": "string" + }, + { + "name": "hasSourceURL", + "description": "True, if this script has sourceURL.", + "optional": true, + "type": "boolean" + }, + { + "name": "isModule", + "description": "True, if this script is ES6 module.", + "optional": true, + "type": "boolean" + }, + { + "name": "length", + "description": "This script length.", + "optional": true, + "type": "integer" + }, + { + "name": "stackTrace", + "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", + "experimental": true, + "optional": true, + "$ref": "Runtime.StackTrace" + } + ] + } + ] + }, + { + "domain": "HeapProfiler", + "experimental": true, + "dependencies": [ + "Runtime" + ], + "types": [ + { + "id": "HeapSnapshotObjectId", + "description": "Heap snapshot object id.", + "type": "string" + }, + { + "id": "SamplingHeapProfileNode", + "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", + "type": "object", + "properties": [ + { + "name": "callFrame", + "description": "Function location.", + "$ref": "Runtime.CallFrame" + }, + { + "name": "selfSize", + "description": "Allocations size in bytes for the node excluding children.", + "type": "number" + }, + { + "name": "id", + "description": "Node id. Ids are unique across all profiles collected between startSampling and stopSampling.", + "type": "integer" + }, + { + "name": "children", + "description": "Child nodes.", + "type": "array", + "items": { + "$ref": "SamplingHeapProfileNode" + } + } + ] + }, + { + "id": "SamplingHeapProfileSample", + "description": "A single sample from a sampling profile.", + "type": "object", + "properties": [ + { + "name": "size", + "description": "Allocation size in bytes attributed to the sample.", + "type": "number" + }, + { + "name": "nodeId", + "description": "Id of the corresponding profile tree node.", + "type": "integer" + }, + { + "name": "ordinal", + "description": "Time-ordered sample ordinal number. It is unique across all profiles retrieved\nbetween startSampling and stopSampling.", + "type": "number" + } + ] + }, + { + "id": "SamplingHeapProfile", + "description": "Sampling profile.", + "type": "object", + "properties": [ + { + "name": "head", + "$ref": "SamplingHeapProfileNode" + }, + { + "name": "samples", + "type": "array", + "items": { + "$ref": "SamplingHeapProfileSample" + } + } + ] + } + ], + "commands": [ + { + "name": "addInspectedHeapObject", + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details\n$x functions).", + "parameters": [ + { + "name": "heapObjectId", + "description": "Heap snapshot object id to be accessible by means of $x command line API.", + "$ref": "HeapSnapshotObjectId" + } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "disable" + }, + { + "name": "enable" + }, + { + "name": "getHeapObjectId", + "parameters": [ + { + "name": "objectId", + "description": "Identifier of the object to get heap object id for.", + "$ref": "Runtime.RemoteObjectId" + } + ], + "returns": [ + { + "name": "heapSnapshotObjectId", + "description": "Id of the heap snapshot object corresponding to the passed remote object id.", + "$ref": "HeapSnapshotObjectId" + } + ] + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { + "name": "objectId", + "$ref": "HeapSnapshotObjectId" + }, + { + "name": "objectGroup", + "description": "Symbolic group name that can be used to release multiple objects.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "result", + "description": "Evaluation result.", + "$ref": "Runtime.RemoteObject" + } + ] + }, + { + "name": "getSamplingProfile", + "returns": [ + { + "name": "profile", + "description": "Return the sampling profile being collected.", + "$ref": "SamplingHeapProfile" + } + ] + }, + { + "name": "startSampling", + "parameters": [ + { + "name": "samplingInterval", + "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The\ndefault value is 32768 bytes.", + "optional": true, + "type": "number" + } + ] + }, + { + "name": "startTrackingHeapObjects", + "parameters": [ + { + "name": "trackAllocations", + "optional": true, + "type": "boolean" + } + ] + }, + { + "name": "stopSampling", + "returns": [ + { + "name": "profile", + "description": "Recorded sampling heap profile.", + "$ref": "SamplingHeapProfile" + } + ] + }, + { + "name": "stopTrackingHeapObjects", + "parameters": [ + { + "name": "reportProgress", + "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken\nwhen the tracking is stopped.", + "optional": true, + "type": "boolean" + } + ] + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { + "name": "reportProgress", + "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken.", + "optional": true, + "type": "boolean" + } + ] + } + ], + "events": [ + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { + "name": "chunk", + "type": "string" + } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { + "name": "statsUpdate", + "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment\nindex, the second integer is a total count of objects for the fragment, the third integer is\na total size of the objects for the fragment.", + "type": "array", + "items": { + "type": "integer" + } + } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regularly sends a current value for last\nseen object id and corresponding timestamp. If the were changes in the heap since last event\nthen one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { + "name": "lastSeenObjectId", + "type": "integer" + }, + { + "name": "timestamp", + "type": "number" + } + ] + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { + "name": "done", + "type": "integer" + }, + { + "name": "total", + "type": "integer" + }, + { + "name": "finished", + "optional": true, + "type": "boolean" + } + ] + }, + { + "name": "resetProfiles" + } + ] + }, + { + "domain": "Profiler", + "dependencies": [ + "Runtime", + "Debugger" + ], + "types": [ + { + "id": "ProfileNode", + "description": "Profile node. Holds callsite information, execution statistics and child nodes.", + "type": "object", + "properties": [ + { + "name": "id", + "description": "Unique id of the node.", + "type": "integer" + }, + { + "name": "callFrame", + "description": "Function location.", + "$ref": "Runtime.CallFrame" + }, + { + "name": "hitCount", + "description": "Number of samples where this node was on top of the call stack.", + "optional": true, + "type": "integer" + }, + { + "name": "children", + "description": "Child node ids.", + "optional": true, + "type": "array", + "items": { + "type": "integer" + } + }, + { + "name": "deoptReason", + "description": "The reason of being not optimized. The function may be deoptimized or marked as don't\noptimize.", + "optional": true, + "type": "string" + }, + { + "name": "positionTicks", + "description": "An array of source position ticks.", + "optional": true, + "type": "array", + "items": { + "$ref": "PositionTickInfo" + } + } + ] + }, + { + "id": "Profile", + "description": "Profile.", + "type": "object", + "properties": [ + { + "name": "nodes", + "description": "The list of profile nodes. First item is the root node.", + "type": "array", + "items": { + "$ref": "ProfileNode" + } + }, + { + "name": "startTime", + "description": "Profiling start timestamp in microseconds.", + "type": "number" + }, + { + "name": "endTime", + "description": "Profiling end timestamp in microseconds.", + "type": "number" + }, + { + "name": "samples", + "description": "Ids of samples top nodes.", + "optional": true, + "type": "array", + "items": { + "type": "integer" + } + }, + { + "name": "timeDeltas", + "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the\nprofile startTime.", + "optional": true, + "type": "array", + "items": { + "type": "integer" + } + } + ] + }, + { + "id": "PositionTickInfo", + "description": "Specifies a number of samples attributed to a certain source position.", + "type": "object", + "properties": [ + { + "name": "line", + "description": "Source line number (1-based).", + "type": "integer" + }, + { + "name": "ticks", + "description": "Number of samples attributed to the source line.", + "type": "integer" + } + ] + }, + { + "id": "CoverageRange", + "description": "Coverage data for a source range.", + "type": "object", + "properties": [ + { + "name": "startOffset", + "description": "JavaScript script source offset for the range start.", + "type": "integer" + }, + { + "name": "endOffset", + "description": "JavaScript script source offset for the range end.", + "type": "integer" + }, + { + "name": "count", + "description": "Collected execution count of the source range.", + "type": "integer" + } + ] + }, + { + "id": "FunctionCoverage", + "description": "Coverage data for a JavaScript function.", + "type": "object", + "properties": [ + { + "name": "functionName", + "description": "JavaScript function name.", + "type": "string" + }, + { + "name": "ranges", + "description": "Source ranges inside the function with coverage data.", + "type": "array", + "items": { + "$ref": "CoverageRange" + } + }, + { + "name": "isBlockCoverage", + "description": "Whether coverage data for this function has block granularity.", + "type": "boolean" + } + ] + }, + { + "id": "ScriptCoverage", + "description": "Coverage data for a JavaScript script.", + "type": "object", + "properties": [ + { + "name": "scriptId", + "description": "JavaScript script id.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "url", + "description": "JavaScript script name or url.", + "type": "string" + }, + { + "name": "functions", + "description": "Functions contained in the script that has coverage data.", + "type": "array", + "items": { + "$ref": "FunctionCoverage" + } + } + ] + }, + { + "id": "TypeObject", + "description": "Describes a type collected during runtime.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "name", + "description": "Name of a type collected with type profiling.", + "type": "string" + } + ] + }, + { + "id": "TypeProfileEntry", + "description": "Source offset and types for a parameter or return value.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "offset", + "description": "Source offset of the parameter or end of function for return values.", + "type": "integer" + }, + { + "name": "types", + "description": "The types for this parameter or return value.", + "type": "array", + "items": { + "$ref": "TypeObject" + } + } + ] + }, + { + "id": "ScriptTypeProfile", + "description": "Type profile data collected during runtime for a JavaScript script.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "scriptId", + "description": "JavaScript script id.", + "$ref": "Runtime.ScriptId" + }, + { + "name": "url", + "description": "JavaScript script name or url.", + "type": "string" + }, + { + "name": "entries", + "description": "Type profile entries for parameters and return values of the functions in the script.", + "type": "array", + "items": { + "$ref": "TypeProfileEntry" + } + } + ] + }, + { + "id": "CounterInfo", + "description": "Collected counter information.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "name", + "description": "Counter name.", + "type": "string" + }, + { + "name": "value", + "description": "Counter value.", + "type": "integer" + } + ] + } + ], + "commands": [ + { + "name": "disable" + }, + { + "name": "enable" + }, + { + "name": "getBestEffortCoverage", + "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to\ngarbage collection.", + "returns": [ + { + "name": "result", + "description": "Coverage data for the current isolate.", + "type": "array", + "items": { + "$ref": "ScriptCoverage" + } + } + ] + }, + { + "name": "setSamplingInterval", + "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started.", + "parameters": [ + { + "name": "interval", + "description": "New sampling interval in microseconds.", + "type": "integer" + } + ] + }, + { + "name": "start" + }, + { + "name": "startPreciseCoverage", + "description": "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code\ncoverage may be incomplete. Enabling prevents running optimized code and resets execution\ncounters.", + "parameters": [ + { + "name": "callCount", + "description": "Collect accurate call counts beyond simple 'covered' or 'not covered'.", + "optional": true, + "type": "boolean" + }, + { + "name": "detailed", + "description": "Collect block-based coverage.", + "optional": true, + "type": "boolean" + } + ] + }, + { + "name": "startTypeProfile", + "description": "Enable type profile.", + "experimental": true + }, + { + "name": "stop", + "returns": [ + { + "name": "profile", + "description": "Recorded profile.", + "$ref": "Profile" + } + ] + }, + { + "name": "stopPreciseCoverage", + "description": "Disable precise code coverage. Disabling releases unnecessary execution count records and allows\nexecuting optimized code." + }, + { + "name": "stopTypeProfile", + "description": "Disable type profile. Disabling releases type profile data collected so far.", + "experimental": true + }, + { + "name": "takePreciseCoverage", + "description": "Collect coverage data for the current isolate, and resets execution counters. Precise code\ncoverage needs to have started.", + "returns": [ + { + "name": "result", + "description": "Coverage data for the current isolate.", + "type": "array", + "items": { + "$ref": "ScriptCoverage" + } + } + ] + }, + { + "name": "takeTypeProfile", + "description": "Collect type profile.", + "experimental": true, + "returns": [ + { + "name": "result", + "description": "Type profile for all scripts since startTypeProfile() was turned on.", + "type": "array", + "items": { + "$ref": "ScriptTypeProfile" + } + } + ] + }, + { + "name": "enableRuntimeCallStats", + "description": "Enable run time call stats collection.", + "experimental": true + }, + { + "name": "disableRuntimeCallStats", + "description": "Disable run time call stats collection.", + "experimental": true + }, + { + "name": "getRuntimeCallStats", + "description": "Retrieve run time call stats.", + "experimental": true, + "returns": [ + { + "name": "result", + "description": "Collected counter information.", + "type": "array", + "items": { + "$ref": "CounterInfo" + } + } + ] + } + ], + "events": [ + { + "name": "consoleProfileFinished", + "parameters": [ + { + "name": "id", + "type": "string" + }, + { + "name": "location", + "description": "Location of console.profileEnd().", + "$ref": "Debugger.Location" + }, + { + "name": "profile", + "$ref": "Profile" + }, + { + "name": "title", + "description": "Profile title passed as an argument to console.profile().", + "optional": true, + "type": "string" + } + ] + }, + { + "name": "consoleProfileStarted", + "description": "Sent when new profile recording is started using console.profile() call.", + "parameters": [ + { + "name": "id", + "type": "string" + }, + { + "name": "location", + "description": "Location of console.profile().", + "$ref": "Debugger.Location" + }, + { + "name": "title", + "description": "Profile title passed as an argument to console.profile().", + "optional": true, + "type": "string" + } + ] + } + ] + }, + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects.\nEvaluation results are returned as mirror object that expose object type, string representation\nand unique identifier that can be used for further object reference. Original objects are\nmaintained in memory unless they are either explicitly released or are released along with the\nother objects in their object group.", + "types": [ + { + "id": "ScriptId", + "description": "Unique script identifier.", + "type": "string" + }, + { + "id": "RemoteObjectId", + "description": "Unique object identifier.", + "type": "string" + }, + { + "id": "UnserializableValue", + "description": "Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,\n`-Infinity`, and bigint literals.", + "type": "string" + }, + { + "id": "RemoteObject", + "description": "Mirror object referencing original JavaScript object.", + "type": "object", + "properties": [ + { + "name": "type", + "description": "Object type.", + "type": "string", + "enum": [ + "object", + "function", + "undefined", + "string", + "number", + "boolean", + "symbol", + "bigint" + ] + }, + { + "name": "subtype", + "description": "Object subtype hint. Specified for `object` type values only.", + "optional": true, + "type": "string", + "enum": [ + "array", + "null", + "node", + "regexp", + "date", + "map", + "set", + "weakmap", + "weakset", + "iterator", + "generator", + "error", + "proxy", + "promise", + "typedarray", + "arraybuffer", + "dataview" + ] + }, + { + "name": "className", + "description": "Object class (constructor) name. Specified for `object` type values only.", + "optional": true, + "type": "string" + }, + { + "name": "value", + "description": "Remote object value in case of primitive values or JSON values (if it was requested).", + "optional": true, + "type": "any" + }, + { + "name": "unserializableValue", + "description": "Primitive value which can not be JSON-stringified does not have `value`, but gets this\nproperty.", + "optional": true, + "$ref": "UnserializableValue" + }, + { + "name": "description", + "description": "String representation of the object.", + "optional": true, + "type": "string" + }, + { + "name": "objectId", + "description": "Unique object identifier (for non-primitive values).", + "optional": true, + "$ref": "RemoteObjectId" + }, + { + "name": "preview", + "description": "Preview containing abbreviated property values. Specified for `object` type values only.", + "experimental": true, + "optional": true, + "$ref": "ObjectPreview" + }, + { + "name": "customPreview", + "experimental": true, + "optional": true, + "$ref": "CustomPreview" + } + ] + }, + { + "id": "CustomPreview", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "header", + "description": "The JSON-stringified result of formatter.header(object, config) call.\nIt contains json ML array that represents RemoteObject.", + "type": "string" + }, + { + "name": "bodyGetterId", + "description": "If formatter returns true as a result of formatter.hasBody call then bodyGetterId will\ncontain RemoteObjectId for the function that returns result of formatter.body(object, config) call.\nThe result value is json ML array.", + "optional": true, + "$ref": "RemoteObjectId" + } + ] + }, + { + "id": "ObjectPreview", + "description": "Object containing abbreviated remote object value.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "type", + "description": "Object type.", + "type": "string", + "enum": [ + "object", + "function", + "undefined", + "string", + "number", + "boolean", + "symbol", + "bigint" + ] + }, + { + "name": "subtype", + "description": "Object subtype hint. Specified for `object` type values only.", + "optional": true, + "type": "string", + "enum": [ + "array", + "null", + "node", + "regexp", + "date", + "map", + "set", + "weakmap", + "weakset", + "iterator", + "generator", + "error" + ] + }, + { + "name": "description", + "description": "String representation of the object.", + "optional": true, + "type": "string" + }, + { + "name": "overflow", + "description": "True iff some of the properties or entries of the original object did not fit.", + "type": "boolean" + }, + { + "name": "properties", + "description": "List of the properties.", + "type": "array", + "items": { + "$ref": "PropertyPreview" + } + }, + { + "name": "entries", + "description": "List of the entries. Specified for `map` and `set` subtype values only.", + "optional": true, + "type": "array", + "items": { + "$ref": "EntryPreview" + } + } + ] + }, + { + "id": "PropertyPreview", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "name", + "description": "Property name.", + "type": "string" + }, + { + "name": "type", + "description": "Object type. Accessor means that the property itself is an accessor property.", + "type": "string", + "enum": [ + "object", + "function", + "undefined", + "string", + "number", + "boolean", + "symbol", + "accessor", + "bigint" + ] + }, + { + "name": "value", + "description": "User-friendly property value string.", + "optional": true, + "type": "string" + }, + { + "name": "valuePreview", + "description": "Nested value preview.", + "optional": true, + "$ref": "ObjectPreview" + }, + { + "name": "subtype", + "description": "Object subtype hint. Specified for `object` type values only.", + "optional": true, + "type": "string", + "enum": [ + "array", + "null", + "node", + "regexp", + "date", + "map", + "set", + "weakmap", + "weakset", + "iterator", + "generator", + "error" + ] + } + ] + }, + { + "id": "EntryPreview", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "key", + "description": "Preview of the key. Specified for map-like collection entries.", + "optional": true, + "$ref": "ObjectPreview" + }, + { + "name": "value", + "description": "Preview of the value.", + "$ref": "ObjectPreview" + } + ] + }, + { + "id": "PropertyDescriptor", + "description": "Object property descriptor.", + "type": "object", + "properties": [ + { + "name": "name", + "description": "Property name or symbol description.", + "type": "string" + }, + { + "name": "value", + "description": "The value associated with the property.", + "optional": true, + "$ref": "RemoteObject" + }, + { + "name": "writable", + "description": "True if the value associated with the property may be changed (data descriptors only).", + "optional": true, + "type": "boolean" + }, + { + "name": "get", + "description": "A function which serves as a getter for the property, or `undefined` if there is no getter\n(accessor descriptors only).", + "optional": true, + "$ref": "RemoteObject" + }, + { + "name": "set", + "description": "A function which serves as a setter for the property, or `undefined` if there is no setter\n(accessor descriptors only).", + "optional": true, + "$ref": "RemoteObject" + }, + { + "name": "configurable", + "description": "True if the type of this property descriptor may be changed and if the property may be\ndeleted from the corresponding object.", + "type": "boolean" + }, + { + "name": "enumerable", + "description": "True if this property shows up during enumeration of the properties on the corresponding\nobject.", + "type": "boolean" + }, + { + "name": "wasThrown", + "description": "True if the result was thrown during the evaluation.", + "optional": true, + "type": "boolean" + }, + { + "name": "isOwn", + "description": "True if the property is owned for the object.", + "optional": true, + "type": "boolean" + }, + { + "name": "symbol", + "description": "Property symbol object, if the property is of the `symbol` type.", + "optional": true, + "$ref": "RemoteObject" + } + ] + }, + { + "id": "InternalPropertyDescriptor", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "type": "object", + "properties": [ + { + "name": "name", + "description": "Conventional property name.", + "type": "string" + }, + { + "name": "value", + "description": "The value associated with the property.", + "optional": true, + "$ref": "RemoteObject" + } + ] + }, + { + "id": "PrivatePropertyDescriptor", + "description": "Object private field descriptor.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "name", + "description": "Private property name.", + "type": "string" + }, + { + "name": "value", + "description": "The value associated with the private property.", + "$ref": "RemoteObject" + } + ] + }, + { + "id": "CallArgument", + "description": "Represents function call argument. Either remote object id `objectId`, primitive `value`,\nunserializable primitive value or neither of (for undefined) them should be specified.", + "type": "object", + "properties": [ + { + "name": "value", + "description": "Primitive value or serializable javascript object.", + "optional": true, + "type": "any" + }, + { + "name": "unserializableValue", + "description": "Primitive value which can not be JSON-stringified.", + "optional": true, + "$ref": "UnserializableValue" + }, + { + "name": "objectId", + "description": "Remote object handle.", + "optional": true, + "$ref": "RemoteObjectId" + } + ] + }, + { + "id": "ExecutionContextId", + "description": "Id of an execution context.", + "type": "integer" + }, + { + "id": "ExecutionContextDescription", + "description": "Description of an isolated world.", + "type": "object", + "properties": [ + { + "name": "id", + "description": "Unique id of the execution context. It can be used to specify in which execution context\nscript evaluation should be performed.", + "$ref": "ExecutionContextId" + }, + { + "name": "origin", + "description": "Execution context origin.", + "type": "string" + }, + { + "name": "name", + "description": "Human readable name describing given context.", + "type": "string" + }, + { + "name": "auxData", + "description": "Embedder-specific auxiliary data.", + "optional": true, + "type": "object" + } + ] + }, + { + "id": "ExceptionDetails", + "description": "Detailed information about exception (or error) that was thrown during script compilation or\nexecution.", + "type": "object", + "properties": [ + { + "name": "exceptionId", + "description": "Exception id.", + "type": "integer" + }, + { + "name": "text", + "description": "Exception text, which should be used together with exception object when available.", + "type": "string" + }, + { + "name": "lineNumber", + "description": "Line number of the exception location (0-based).", + "type": "integer" + }, + { + "name": "columnNumber", + "description": "Column number of the exception location (0-based).", + "type": "integer" + }, + { + "name": "scriptId", + "description": "Script ID of the exception location.", + "optional": true, + "$ref": "ScriptId" + }, + { + "name": "url", + "description": "URL of the exception location, to be used when the script was not reported.", + "optional": true, + "type": "string" + }, + { + "name": "stackTrace", + "description": "JavaScript stack trace if available.", + "optional": true, + "$ref": "StackTrace" + }, + { + "name": "exception", + "description": "Exception object if available.", + "optional": true, + "$ref": "RemoteObject" + }, + { + "name": "executionContextId", + "description": "Identifier of the context where exception happened.", + "optional": true, + "$ref": "ExecutionContextId" + } + ] + }, + { + "id": "Timestamp", + "description": "Number of milliseconds since epoch.", + "type": "number" + }, + { + "id": "TimeDelta", + "description": "Number of milliseconds.", + "type": "number" + }, + { + "id": "CallFrame", + "description": "Stack entry for runtime errors and assertions.", + "type": "object", + "properties": [ + { + "name": "functionName", + "description": "JavaScript function name.", + "type": "string" + }, + { + "name": "scriptId", + "description": "JavaScript script id.", + "$ref": "ScriptId" + }, + { + "name": "url", + "description": "JavaScript script name or url.", + "type": "string" + }, + { + "name": "lineNumber", + "description": "JavaScript script line number (0-based).", + "type": "integer" + }, + { + "name": "columnNumber", + "description": "JavaScript script column number (0-based).", + "type": "integer" + } + ] + }, + { + "id": "StackTrace", + "description": "Call frames for assertions or error messages.", + "type": "object", + "properties": [ + { + "name": "description", + "description": "String label of this stack trace. For async traces this may be a name of the function that\ninitiated the async call.", + "optional": true, + "type": "string" + }, + { + "name": "callFrames", + "description": "JavaScript function name.", + "type": "array", + "items": { + "$ref": "CallFrame" + } + }, + { + "name": "parent", + "description": "Asynchronous JavaScript stack trace that preceded this stack, if available.", + "optional": true, + "$ref": "StackTrace" + }, + { + "name": "parentId", + "description": "Asynchronous JavaScript stack trace that preceded this stack, if available.", + "experimental": true, + "optional": true, + "$ref": "StackTraceId" + } + ] + }, + { + "id": "UniqueDebuggerId", + "description": "Unique identifier of current debugger.", + "experimental": true, + "type": "string" + }, + { + "id": "StackTraceId", + "description": "If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This\nallows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages.", + "experimental": true, + "type": "object", + "properties": [ + { + "name": "id", + "type": "string" + }, + { + "name": "debuggerId", + "optional": true, + "$ref": "UniqueDebuggerId" + } + ] + } + ], + "commands": [ + { + "name": "awaitPromise", + "description": "Add handler to promise with given promise object id.", + "parameters": [ + { + "name": "promiseObjectId", + "description": "Identifier of the promise.", + "$ref": "RemoteObjectId" + }, + { + "name": "returnByValue", + "description": "Whether the result is expected to be a JSON object that should be sent by value.", + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the result.", + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "result", + "description": "Promise result. Will contain rejected value if promise was rejected.", + "$ref": "RemoteObject" + }, + { + "name": "exceptionDetails", + "description": "Exception details if stack strace is available.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "callFunctionOn", + "description": "Calls function with given declaration on the given object. Object group of the result is\ninherited from the target object.", + "parameters": [ + { + "name": "functionDeclaration", + "description": "Declaration of the function to call.", + "type": "string" + }, + { + "name": "objectId", + "description": "Identifier of the object to call function on. Either objectId or executionContextId should\nbe specified.", + "optional": true, + "$ref": "RemoteObjectId" + }, + { + "name": "arguments", + "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target\nobject.", + "optional": true, + "type": "array", + "items": { + "$ref": "CallArgument" + } + }, + { + "name": "silent", + "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.", + "optional": true, + "type": "boolean" + }, + { + "name": "returnByValue", + "description": "Whether the result is expected to be a JSON object which should be sent by value.", + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the result.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "userGesture", + "description": "Whether execution should be treated as initiated by user in the UI.", + "optional": true, + "type": "boolean" + }, + { + "name": "awaitPromise", + "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.", + "optional": true, + "type": "boolean" + }, + { + "name": "executionContextId", + "description": "Specifies execution context which global object will be used to call function on. Either\nexecutionContextId or objectId should be specified.", + "optional": true, + "$ref": "ExecutionContextId" + }, + { + "name": "objectGroup", + "description": "Symbolic group name that can be used to release multiple objects. If objectGroup is not\nspecified and objectId is, objectGroup will be inherited from object.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "result", + "description": "Call result.", + "$ref": "RemoteObject" + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "compileScript", + "description": "Compiles expression.", + "parameters": [ + { + "name": "expression", + "description": "Expression to compile.", + "type": "string" + }, + { + "name": "sourceURL", + "description": "Source url to be set for the script.", + "type": "string" + }, + { + "name": "persistScript", + "description": "Specifies whether the compiled script should be persisted.", + "type": "boolean" + }, + { + "name": "executionContextId", + "description": "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.", + "optional": true, + "$ref": "ExecutionContextId" + } + ], + "returns": [ + { + "name": "scriptId", + "description": "Id of the script.", + "optional": true, + "$ref": "ScriptId" + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "disable", + "description": "Disables reporting of execution contexts creation." + }, + { + "name": "discardConsoleEntries", + "description": "Discards collected exceptions and console API calls." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of `executionContextCreated` event.\nWhen the reporting gets enabled the event will be sent immediately for each existing execution\ncontext." + }, + { + "name": "evaluate", + "description": "Evaluates expression on global object.", + "parameters": [ + { + "name": "expression", + "description": "Expression to evaluate.", + "type": "string" + }, + { + "name": "objectGroup", + "description": "Symbolic group name that can be used to release multiple objects.", + "optional": true, + "type": "string" + }, + { + "name": "includeCommandLineAPI", + "description": "Determines whether Command Line API should be available during the evaluation.", + "optional": true, + "type": "boolean" + }, + { + "name": "silent", + "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.", + "optional": true, + "type": "boolean" + }, + { + "name": "contextId", + "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.", + "optional": true, + "$ref": "ExecutionContextId" + }, + { + "name": "returnByValue", + "description": "Whether the result is expected to be a JSON object that should be sent by value.", + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the result.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "userGesture", + "description": "Whether execution should be treated as initiated by user in the UI.", + "optional": true, + "type": "boolean" + }, + { + "name": "awaitPromise", + "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.", + "optional": true, + "type": "boolean" + }, + { + "name": "throwOnSideEffect", + "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation.\nThis implies `disableBreaks` below.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "timeout", + "description": "Terminate execution after timing out (number of milliseconds).", + "experimental": true, + "optional": true, + "$ref": "TimeDelta" + }, + { + "name": "disableBreaks", + "description": "Disable breakpoints during execution.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "replMode", + "description": "Reserved flag for future REPL mode support. Setting this flag has currently no effect.", + "experimental": true, + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "result", + "description": "Evaluation result.", + "$ref": "RemoteObject" + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "getIsolateId", + "description": "Returns the isolate id.", + "experimental": true, + "returns": [ + { + "name": "id", + "description": "The isolate id.", + "type": "string" + } + ] + }, + { + "name": "getHeapUsage", + "description": "Returns the JavaScript heap usage.\nIt is the total usage of the corresponding isolate not scoped to a particular Runtime.", + "experimental": true, + "returns": [ + { + "name": "usedSize", + "description": "Used heap size in bytes.", + "type": "number" + }, + { + "name": "totalSize", + "description": "Allocated heap size in bytes.", + "type": "number" + } + ] + }, + { + "name": "getProperties", + "description": "Returns properties of a given object. Object group of the result is inherited from the target\nobject.", + "parameters": [ + { + "name": "objectId", + "description": "Identifier of the object to return properties for.", + "$ref": "RemoteObjectId" + }, + { + "name": "ownProperties", + "description": "If true, returns properties belonging only to the element itself, not to its prototype\nchain.", + "optional": true, + "type": "boolean" + }, + { + "name": "accessorPropertiesOnly", + "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not\nreturned either.", + "experimental": true, + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the results.", + "experimental": true, + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "result", + "description": "Object properties.", + "type": "array", + "items": { + "$ref": "PropertyDescriptor" + } + }, + { + "name": "internalProperties", + "description": "Internal object properties (only of the element itself).", + "optional": true, + "type": "array", + "items": { + "$ref": "InternalPropertyDescriptor" + } + }, + { + "name": "privateProperties", + "description": "Object private properties.", + "experimental": true, + "optional": true, + "type": "array", + "items": { + "$ref": "PrivatePropertyDescriptor" + } + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "globalLexicalScopeNames", + "description": "Returns all let, const and class variables from global scope.", + "parameters": [ + { + "name": "executionContextId", + "description": "Specifies in which execution context to lookup global scope variables.", + "optional": true, + "$ref": "ExecutionContextId" + } + ], + "returns": [ + { + "name": "names", + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + { + "name": "queryObjects", + "parameters": [ + { + "name": "prototypeObjectId", + "description": "Identifier of the prototype to return objects for.", + "$ref": "RemoteObjectId" + }, + { + "name": "objectGroup", + "description": "Symbolic group name that can be used to release the results.", + "optional": true, + "type": "string" + } + ], + "returns": [ + { + "name": "objects", + "description": "Array with objects.", + "$ref": "RemoteObject" + } + ] + }, + { + "name": "releaseObject", + "description": "Releases remote object with given id.", + "parameters": [ + { + "name": "objectId", + "description": "Identifier of the object to release.", + "$ref": "RemoteObjectId" + } + ] + }, + { + "name": "releaseObjectGroup", + "description": "Releases all remote objects that belong to a given group.", + "parameters": [ + { + "name": "objectGroup", + "description": "Symbolic object group name.", + "type": "string" + } + ] + }, + { + "name": "runIfWaitingForDebugger", + "description": "Tells inspected instance to run if it was waiting for debugger to attach." + }, + { + "name": "runScript", + "description": "Runs script with given id in a given context.", + "parameters": [ + { + "name": "scriptId", + "description": "Id of the script to run.", + "$ref": "ScriptId" + }, + { + "name": "executionContextId", + "description": "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.", + "optional": true, + "$ref": "ExecutionContextId" + }, + { + "name": "objectGroup", + "description": "Symbolic group name that can be used to release multiple objects.", + "optional": true, + "type": "string" + }, + { + "name": "silent", + "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state.", + "optional": true, + "type": "boolean" + }, + { + "name": "includeCommandLineAPI", + "description": "Determines whether Command Line API should be available during the evaluation.", + "optional": true, + "type": "boolean" + }, + { + "name": "returnByValue", + "description": "Whether the result is expected to be a JSON object which should be sent by value.", + "optional": true, + "type": "boolean" + }, + { + "name": "generatePreview", + "description": "Whether preview should be generated for the result.", + "optional": true, + "type": "boolean" + }, + { + "name": "awaitPromise", + "description": "Whether execution should `await` for resulting value and return once awaited promise is\nresolved.", + "optional": true, + "type": "boolean" + } + ], + "returns": [ + { + "name": "result", + "description": "Run result.", + "$ref": "RemoteObject" + }, + { + "name": "exceptionDetails", + "description": "Exception details.", + "optional": true, + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "setAsyncCallStackDepth", + "description": "Enables or disables async call stacks tracking.", + "redirect": "Debugger", + "parameters": [ + { + "name": "maxDepth", + "description": "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default).", + "type": "integer" + } + ] + }, + { + "name": "setCustomObjectFormatterEnabled", + "experimental": true, + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ] + }, + { + "name": "setMaxCallStackSizeToCapture", + "experimental": true, + "parameters": [ + { + "name": "size", + "type": "integer" + } + ] + }, + { + "name": "terminateExecution", + "description": "Terminate current or next JavaScript execution.\nWill cancel the termination when the outer-most script execution ends.", + "experimental": true + }, + { + "name": "addBinding", + "description": "If executionContextId is empty, adds binding with the given name on the\nglobal objects of all inspected contexts, including those created later,\nbindings survive reloads.\nIf executionContextId is specified, adds binding only on global object of\ngiven execution context.\nBinding function takes exactly one argument, this argument should be string,\nin case of any other input, function throws an exception.\nEach binding function call produces Runtime.bindingCalled notification.", + "experimental": true, + "parameters": [ + { + "name": "name", + "type": "string" + }, + { + "name": "executionContextId", + "optional": true, + "$ref": "ExecutionContextId" + } + ] + }, + { + "name": "removeBinding", + "description": "This method does not remove binding function from global object but\nunsubscribes current runtime agent from Runtime.bindingCalled notifications.", + "experimental": true, + "parameters": [ + { + "name": "name", + "type": "string" + } + ] + } + ], + "events": [ + { + "name": "bindingCalled", + "description": "Notification is issued every time when binding is called.", + "experimental": true, + "parameters": [ + { + "name": "name", + "type": "string" + }, + { + "name": "payload", + "type": "string" + }, + { + "name": "executionContextId", + "description": "Identifier of the context where the call was made.", + "$ref": "ExecutionContextId" + } + ] + }, + { + "name": "consoleAPICalled", + "description": "Issued when console API was called.", + "parameters": [ + { + "name": "type", + "description": "Type of the call.", + "type": "string", + "enum": [ + "log", + "debug", + "info", + "error", + "warning", + "dir", + "dirxml", + "table", + "trace", + "clear", + "startGroup", + "startGroupCollapsed", + "endGroup", + "assert", + "profile", + "profileEnd", + "count", + "timeEnd" + ] + }, + { + "name": "args", + "description": "Call arguments.", + "type": "array", + "items": { + "$ref": "RemoteObject" + } + }, + { + "name": "executionContextId", + "description": "Identifier of the context where the call was made.", + "$ref": "ExecutionContextId" + }, + { + "name": "timestamp", + "description": "Call timestamp.", + "$ref": "Timestamp" + }, + { + "name": "stackTrace", + "description": "Stack trace captured when the call was made. The async stack chain is automatically reported for\nthe following call types: `assert`, `error`, `trace`, `warning`. For other types the async call\nchain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field.", + "optional": true, + "$ref": "StackTrace" + }, + { + "name": "context", + "description": "Console context descriptor for calls on non-default console context (not console.*):\n'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call\non named context.", + "experimental": true, + "optional": true, + "type": "string" + } + ] + }, + { + "name": "exceptionRevoked", + "description": "Issued when unhandled exception was revoked.", + "parameters": [ + { + "name": "reason", + "description": "Reason describing why exception was revoked.", + "type": "string" + }, + { + "name": "exceptionId", + "description": "The id of revoked exception, as reported in `exceptionThrown`.", + "type": "integer" + } + ] + }, + { + "name": "exceptionThrown", + "description": "Issued when exception was thrown and unhandled.", + "parameters": [ + { + "name": "timestamp", + "description": "Timestamp of the exception.", + "$ref": "Timestamp" + }, + { + "name": "exceptionDetails", + "$ref": "ExceptionDetails" + } + ] + }, + { + "name": "executionContextCreated", + "description": "Issued when new execution context is created.", + "parameters": [ + { + "name": "context", + "description": "A newly created execution context.", + "$ref": "ExecutionContextDescription" + } + ] + }, + { + "name": "executionContextDestroyed", + "description": "Issued when execution context is destroyed.", + "parameters": [ + { + "name": "executionContextId", + "description": "Id of the destroyed context", + "$ref": "ExecutionContextId" + } + ] + }, + { + "name": "executionContextsCleared", + "description": "Issued when all executionContexts were cleared in browser" + }, + { + "name": "inspectRequested", + "description": "Issued when object should be inspected (for example, as a result of inspect() command line API\ncall).", + "parameters": [ + { + "name": "object", + "$ref": "RemoteObject" + }, + { + "name": "hints", + "type": "object" + } + ] + } + ] + }, + { + "domain": "Schema", + "description": "This domain is deprecated.", + "deprecated": true, + "types": [ + { + "id": "Domain", + "description": "Description of the protocol domain.", + "type": "object", + "properties": [ + { + "name": "name", + "description": "Domain name.", + "type": "string" + }, + { + "name": "version", + "description": "Domain version.", + "type": "string" + } + ] + } + ], + "commands": [ + { + "name": "getDomains", + "description": "Returns supported domains.", + "returns": [ + { + "name": "domains", + "description": "List of supported domains.", + "type": "array", + "items": { + "$ref": "Domain" + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/engine262/inspector/methods.js b/src/engine262/inspector/methods.js new file mode 100644 index 0000000..df6d3d1 --- /dev/null +++ b/src/engine262/inspector/methods.js @@ -0,0 +1,102 @@ +'use strict'; + +const engine262 = require('..'); +const { getContext } = require('./context'); + +module.exports = { + Debugger: { + enable() { + return { debuggerId: 'debugger.0' }; + }, + setAsyncCallStackDepth() {}, + setBlackboxPatterns() {}, + setPauseOnExceptions() {}, + }, + Profiler: { + enable() {}, + }, + Runtime: { + enable() {}, + compileScript() { + return { scriptId: 'script.0' }; + }, + callFunctionOn(options) { + const context = getContext(options.executionContextId); + const { Value: F } = context.realm.evaluateScript(`(${options.functionDeclaration})`); + const thisValue = options.objectId + ? context.getObject(options.objectId) + : engine262.Value.undefined; + const args = options.arguments.map((a) => { + if ('value' in a) { + return new engine262.Value(context.realm, a.value); + } + if ('objectId' in a) { + return context.getObject(a.objectId); + } + if ('unserializableValue' in a) { + throw new RangeError(); + } + return engine262.Value.undefined; + }); + const r = engine262.Abstract.Call(F, thisValue, args); + return context.createEvaluationResult(r, options); + }, + evaluate(options) { + if (options.throwOnSideEffect || options.awaitPromise) { + return { + exceptionDetails: { + text: 'unsupported', + lineNumber: 0, + columnNumber: 0, + }, + }; + } + + const context = getContext(options.contextId); + const r = context.realm.evaluateScript(options.expression); + return context.createEvaluationResult(r, options); + }, + getHeapUsage() { + return { usedSize: 0, totalSize: 0 }; + }, + getIsolateId() { + return { id: 'isolate.0' }; + }, + getProperties(options) { + const context = getContext(); + const object = context.getObject(options.objectId); + + const properties = context.getProperties(object, options); + if (properties instanceof engine262.AbruptCompletion) { + return context.createEvaluationResult(properties, options); + } + + return { result: properties }; + }, + globalLexicalScopeNames({ executionContextId }) { + const context = getContext(executionContextId); + const envRec = context.realm.realm.GlobalEnv.EnvironmentRecord; + const names = Map.prototype.keys.call(envRec.DeclarativeRecord.bindings); + return { + names: [...names], + }; + }, + releaseObjectGroup({ objectGroup }) { + getContext().releaseObjectGroup(objectGroup); + }, + runIfWaitingForDebugger(params, ctx) { + ctx.sendEvent('Runtime.executionContextCreated', { + context: { + id: '0', + origin: 'file://', + name: 'context.0', + auxData: {}, + }, + }); + }, + }, + HeapProfiler: { + enable() {}, + collectGarbage() {}, + }, +}; diff --git a/src/engine262/inspector/server.js b/src/engine262/inspector/server.js new file mode 100644 index 0000000..d41c040 --- /dev/null +++ b/src/engine262/inspector/server.js @@ -0,0 +1,87 @@ +'use strict'; + +const http = require('http'); +const WebSocket = require('ws'); // eslint-disable-line import/no-extraneous-dependencies +const packageJson = require('../package.json'); +const protocol = require('./js_protocol.json'); +const methods = require('./methods'); + +const server = http.createServer((req, res) => { + if (req.method !== 'GET') { + res.writeHead(405); + res.end(); + return; + } + + const json = (obj) => { + const s = JSON.stringify(obj); + res.writeHead(200, { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(s), + }); + res.end(s); + }; + + switch (req.url) { + case '/json': + case '/json/list': + json([{ + description: `${packageJson.name} instance`, + devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=localhost:9229/', + devtoolsFrontendUrlCompat: 'chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:9229/', + faviconUrl: 'https://avatars0.githubusercontent.com/u/51185628', + id: 'inspector.0', + title: 'engine262', + type: 'node', + url: `file://${process.cwd()}`, + webSocketDebuggerUrl: 'ws://localhost:9229/', + }]); + break; + case '/json/version': + json({ + 'Browser': `${packageJson.name}/v${packageJson.version}`, + 'Protocol-Version': `${protocol.version.major}.${protocol.version.minor}`, + }); + break; + case '/json/protocol': + json(protocol); + break; + default: + res.writeHead(404); + res.end(); + break; + } +}); + +const wss = new WebSocket.Server({ server }); + +wss.on('connection', (ws) => { + const send = (obj) => { + const s = JSON.stringify(obj); + // console.log('<-', s); + ws.send(s); + }; + + ws._socket.unref(); // eslint-disable-line no-underscore-dangle + + const context = { + sendEvent(event, params) { + send({ method: event, params }); + }, + }; + + ws.on('message', (data) => { + // console.log('->', data); + const { id, method, params } = JSON.parse(data); + const [k, v] = method.split('.'); + Promise.resolve(methods[k][v](params, context)) + .then((result = {}) => { + send({ id, result }); + }); + }); +}); + +server.listen(9229, '127.0.0.1', () => { + console.log('Debugger listening at localhost:9229'); // eslint-disable-line no-console +}); +server.unref(); diff --git a/src/engine262/package.json b/src/engine262/package.json new file mode 100644 index 0000000..8125d08 --- /dev/null +++ b/src/engine262/package.json @@ -0,0 +1,57 @@ +{ + "name": "engine262", + "version": "0.0.1", + "description": "Implementation of ECMA-262 in JavaScript", + "author": "engine262 Contributors", + "license": "MIT", + "homepage": "https://github.com/engine262/engine262#readme", + "bugs": { + "url": "https://github.com/engine262/engine262/issues" + }, + "main": "dist/engine262", + "scripts": { + "test:test262": "node test/test262/test262.js", + "test:supplemental": "node test/supplemental.js", + "test:json": "node test/json/json.js", + "build:grammar": "nearleyc src/grammar/StrNumericLiteral.ne -o src/grammar/StrNumericLiteral-gen.mjs", + "build:engine": "rollup -c", + "lint": "eslint rollup.config.js test/ src/ bin/ inspector/ scripts/ --cache --ext=js,mjs", + "build": "npm run build:engine", + "test": "npm run test:test262 && npm run test:supplemental", + "coverage": "nyc --reporter=lcov bash test/coverage_root.sh", + "prepublishOnly": "node scripts/tag_version_with_git_hash.js", + "postpublish": "git reset --hard HEAD" + }, + "bin": { + "engine262": "bin/engine262.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/engine262/engine262.git" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com/@engine262" + }, + "dependencies": {}, + "devDependencies": { + "@babel/core": "^7.8.7", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@snek/source-map-support": "^1.0.4", + "acorn": "^7.1.1", + "babel-eslint": "^10.1.0", + "eslint": "^6.8.0", + "eslint-config-airbnb-base": "^14.1.0", + "eslint-plugin-import": "^2.20.2", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "nearley": "2.16.0", + "nyc": "^15.0.0", + "rollup": "^1.32.1", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "test262-stream": "^1.3.0", + "unicode-13.0.0": "^0.8.0", + "ws": "^7.2.3" + } +} diff --git a/src/engine262/rollup.config.js b/src/engine262/rollup.config.js new file mode 100644 index 0000000..3aac9da --- /dev/null +++ b/src/engine262/rollup.config.js @@ -0,0 +1,54 @@ +'use strict'; + +const fs = require('fs'); +const { execSync } = require('child_process'); +const babel = require('rollup-plugin-babel'); +const commonjs = require('rollup-plugin-commonjs'); +const nodeResolve = require('rollup-plugin-node-resolve'); +const { name, version } = require('./package.json'); + +const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); + +const banner = `/* + * engine262 ${version} ${hash} + * + * ${fs.readFileSync('./LICENSE', 'utf8').trim().split('\n').join('\n * ')} + */ +`; + +module.exports = () => ({ + input: './src/api.mjs', + plugins: [ + commonjs(), + nodeResolve(), + babel({ + exclude: 'node_modules/**', + plugins: [ + '@babel/plugin-syntax-bigint', + './scripts/transform.js', + ], + }), + ], + output: [ + { + file: 'dist/engine262.js', + format: 'umd', + sourcemap: true, + name, + banner, + }, + { + file: 'dist/engine262.mjs', + format: 'es', + sourcemap: true, + banner, + }, + ], + onwarn(warning, warn) { + if (warning.code === 'CIRCULAR_DEPENDENCY') { + // Squelch. + return; + } + warn(warning); + }, +}); diff --git a/src/engine262/scripts/tag_version_with_git_hash.js b/src/engine262/scripts/tag_version_with_git_hash.js new file mode 100644 index 0000000..f974fa3 --- /dev/null +++ b/src/engine262/scripts/tag_version_with_git_hash.js @@ -0,0 +1,20 @@ +'use strict'; + +const { execSync } = require('child_process'); +const fs = require('fs'); + +const pjsonPath = require.resolve('../package.json'); + +const pjson = JSON.parse(fs.readFileSync(pjsonPath, 'utf8')); + +process.stdout.write('Checking package.json for git revision...\n'); + +if (!pjson.version.includes('-')) { + process.stdout.write('Inserting git revision into package.json...\n'); + + const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); + pjson.version = `${pjson.version}-${hash}`; + fs.writeFileSync(pjsonPath, `${JSON.stringify(pjson, null, 2)}\n`); +} + +process.stdout.write('Done!\n'); diff --git a/src/engine262/scripts/transform.js b/src/engine262/scripts/transform.js new file mode 100644 index 0000000..c3e8974 --- /dev/null +++ b/src/engine262/scripts/transform.js @@ -0,0 +1,252 @@ +'use strict'; + +const { relative, resolve } = require('path'); + +const COMPLETION_PATH = resolve('./src/completion.mjs'); +const ABSTRACT_OPS_PATH = resolve('./src/abstract-ops/all.mjs'); +const VALUE_PATH = resolve('./src/value.mjs'); + +function fileToImport(file, refPath) { + return relative(file.opts.filename, refPath) + .replace(/\\/g, '/') // Support building on Windows + .replace('../', './'); +} + +function findParentStatementPath(path) { + while (path && !path.isStatement()) { + path = path.parentPath; + } + return path; +} + +function getEnclosingConditionalExpression(path) { + while (path && !path.isStatement()) { + if (path.isConditionalExpression()) { + return path; + } + path = path.parentPath; + } + return null; +} + +module.exports = ({ types: t, template }) => { + function createImportCompletion(file) { + const r = fileToImport(file, COMPLETION_PATH); + return template.ast(` + import { Completion } from "${r}"; + `); + } + + function createImportAbruptCompletion(file) { + const r = fileToImport(file, COMPLETION_PATH); + return template.ast(` + import { AbruptCompletion } from "${r}"; + `); + } + + function createImportAssert(file) { + const r = fileToImport(file, ABSTRACT_OPS_PATH); + return template.ast(` + import { Assert } from "${r}"; + `); + } + + function createImportCall(file) { + const r = fileToImport(file, ABSTRACT_OPS_PATH); + return template.ast(` + import { Call } from "${r}"; + `); + } + + function createImportValue(file) { + const r = fileToImport(file, VALUE_PATH); + return template.ast(` + import { Value } from "${r}"; + `); + } + + const MACROS = { + Q: { + template: template(` + let ID = ARGUMENT; + /* istanbul ignore if */ + if (ID instanceof AbruptCompletion) { + return ID; + } + /* istanbul ignore if */ + if (ID instanceof Completion) { + ID = ID.Value; + } + `, { preserveComments: true }), + imports: ['AbruptCompletion', 'Completion'], + }, + X: { + template: template(` + let ID = ARGUMENT; + Assert(!(ID instanceof AbruptCompletion), SOURCE + ' returned an abrupt completion'); + /* istanbul ignore if */ + if (ID instanceof Completion) { + ID = ID.Value; + } + `, { preserveComments: true }), + imports: ['Assert', 'Completion', 'AbruptCompletion'], + }, + IfAbruptRejectPromise: { + template: template(` + /* istanbul ignore if */ + if (ID instanceof AbruptCompletion) { + const hygenicTemp2 = Call(CAPABILITY.Reject, Value.undefined, [ID.Value]); + if (hygenicTemp2 instanceof AbruptCompletion) { + return hygenicTemp2; + } + return CAPABILITY.Promise; + } + /* istanbul ignore if */ + if (ID instanceof Completion) { + ID = ID.Value; + } + `, { preserveComments: true }), + imports: ['Call', 'Value', 'AbruptCompletion', 'Completion'], + }, + }; + MACROS.ReturnIfAbrupt = MACROS.Q; + const MACRO_NAMES = Object.keys(MACROS); + + return { + visitor: { + Program: { + enter(path, state) { + state.needed = {}; + }, + exit(path, state) { + if (state.needed.Completion && !state.file.opts.filename.endsWith('completion.mjs')) { + path.node.body.unshift(createImportCompletion(state.file)); + } + if (state.needed.AbruptCompletion && !state.file.opts.filename.endsWith('completion.mjs')) { + path.node.body.unshift(createImportAbruptCompletion(state.file)); + } + if (state.needed.Assert) { + path.node.body.unshift(createImportAssert(state.file)); + } + if (state.needed.Call) { + path.node.body.unshift(createImportCall(state.file)); + } + if (state.needed.Value) { + path.node.body.unshift(createImportValue(state.file)); + } + }, + }, + CallExpression(path, state) { + if (!t.isIdentifier(path.node.callee)) { + return; + } + + const macroName = path.node.callee.name; + if (MACRO_NAMES.includes(macroName)) { + const enclosingConditional = getEnclosingConditionalExpression(path); + if (enclosingConditional !== null) { + if (enclosingConditional.parentPath.isVariableDeclarator()) { + const declaration = enclosingConditional.parentPath.parentPath; + const id = enclosingConditional.parentPath.get('id'); + declaration.replaceWithMultiple(template.ast(` + let ${id}; + if (${enclosingConditional.get('test')}) { + ${id} = ${enclosingConditional.get('consequent')} + } else { + ${id} = ${enclosingConditional.get('alternate')} + } + `)); + return; + } else { + throw path.buildCodeFrameError('Macros may not be used within conditional expressions'); + } + } + + const macro = MACROS[macroName]; + const [argument] = path.node.arguments; + + if (macro === MACROS.Q && (t.isReturnStatement(path.parentPath) || path.parentPath.isArrowFunctionExpression())) { + path.replaceWith(path.node.arguments[0]); + return; + } + + if (path.parentPath.isArrowFunctionExpression()) { + throw path.buildCodeFrameError('Macros may not be the sole expression of an arrow function'); + } + + const statementPath = findParentStatementPath(path); + + macro.imports.forEach((i) => { + state.needed[i] = path.scope.getBinding(i) === undefined; + }); + + if (macro === MACROS.Q && t.isIdentifier(argument)) { + const binding = path.scope.getBinding(argument.name); + binding.path.parent.kind = 'let'; + statementPath.insertBefore(template(` + /* istanbul ignore if */ + if (ID instanceof AbruptCompletion) { + return ID; + } + /* istanbul ignore if */ + if (ID instanceof Completion) { + ID = ID.Value; + } + `, { preserveComments: true })({ ID: argument })); + path.replaceWith(argument); + } else { + if (macro === MACROS.IfAbruptRejectPromise) { + const [, capability] = path.node.arguments; + if (!t.isIdentifier(argument)) { + throw path.get('arguments.0').buildCodeFrameError('First argument to IfAbruptRejectPromise should be an identifier'); + } + if (!t.isIdentifier(capability)) { + throw path.get('arguments.1').buildCodeFrameError('Second argument to IfAbruptRejectPromise should be an identifier'); + } + const binding = path.scope.getBinding(argument.name); + binding.path.parent.kind = 'let'; + statementPath.insertBefore(macro.template({ ID: argument, CAPABILITY: capability })); + path.remove(); + } else { + const id = statementPath.scope.generateUidIdentifier(); + const replacement = { + ARGUMENT: argument, + ID: id, + }; + if (macro === MACROS.X) { + replacement.SOURCE = t.stringLiteral(path.get('arguments.0').getSource()); + } + statementPath.insertBefore(macro.template(replacement)); + path.replaceWith(id); + } + } + } else if (macroName === 'Assert') { + path.node.arguments.push(t.stringLiteral(path.get('arguments.0').getSource())); + } + }, + SwitchCase(path) { + const n = path.node.consequent[0]; + if (t.isThrowStatement(n) && t.isNewExpression(n.argument) && n.argument.callee.name === 'OutOfRange') { + path.node.leadingComments = path.node.leadingComments || []; + path.node.leadingComments.push({ type: 'CommentBlock', value: 'istanbul ignore next' }); + } + }, + FunctionDeclaration(path) { + if (path.node.leadingComments) { + outer: // eslint-disable-line no-labels + for (const c of path.node.leadingComments) { + const lines = c.value.split('\n'); + for (const line of lines) { + if (/#sec/.test(line)) { + const section = line.split(' ').find((l) => l.includes('#sec')); + const url = section.includes('https') ? section : `https://tc39.es/ecma262/${section}`; + path.insertAfter(template.ast(`${path.node.id.name}.section = '${url}';`)); + break outer; // eslint-disable-line no-labels + } + } + } + } + }, + }, + }; +}; diff --git a/src/engine262/src/abstract-ops/all.mjs b/src/engine262/src/abstract-ops/all.mjs new file mode 100644 index 0000000..f0d3577 --- /dev/null +++ b/src/engine262/src/abstract-ops/all.mjs @@ -0,0 +1,32 @@ +export * from './arguments-operations.mjs'; +export * from './array-objects.mjs'; +export * from './arraybuffer-objects.mjs'; +export * from './async-function-operations.mjs'; +export * from './async-generator-objects.mjs'; +export * from './data-types-and-values.mjs'; +export * from './dataview-objects.mjs'; +export * from './date-objects.mjs'; +export * from './execution-contexts.mjs'; +export * from './function-operations.mjs'; +export * from './generator-operations.mjs'; +export * from './global-object.mjs'; +export * from './immutable-prototype-objects.mjs'; +export * from './integer-indexed-objects.mjs'; +export * from './iterator-operations.mjs'; +export * from './module-namespace-exotic-objects.mjs'; +export * from './module-records.mjs'; +export * from './notational-conventions.mjs'; +export * from './object-operations.mjs'; +export * from './objects.mjs'; +export * from './promise-operations.mjs'; +export * from './proxy-objects.mjs'; +export * from './reference-operations.mjs'; +export * from './regexp-objects.mjs'; +export * from './source-code.mjs'; +export * from './spec-types.mjs'; +export * from './string-objects.mjs'; +export * from './symbol-objects.mjs'; +export * from './testing-comparison.mjs'; +export * from './type-conversion.mjs'; +export * from './typedarray-objects.mjs'; +export * from './weak-operations.mjs'; diff --git a/src/engine262/src/abstract-ops/arguments-operations.mjs b/src/engine262/src/abstract-ops/arguments-operations.mjs new file mode 100644 index 0000000..b60b613 --- /dev/null +++ b/src/engine262/src/abstract-ops/arguments-operations.mjs @@ -0,0 +1,248 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Descriptor, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { BoundNames_FormalParameters } from '../static-semantics/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { ValueSet } from '../helpers.mjs'; +import { + Assert, + CreateBuiltinFunction, + CreateDataProperty, + DefinePropertyOrThrow, + SetFunctionLength, + ToString, + SameValue, + MakeBasicObject, + OrdinaryObjectCreate, + OrdinaryGetOwnProperty, + OrdinaryDefineOwnProperty, + OrdinaryGet, + OrdinarySet, + OrdinaryDelete, + Get, + Set, + HasOwnProperty, + IsAccessorDescriptor, + IsDataDescriptor, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 9.4.4 #sec-arguments-exotic-objects + + +function ArgumentsGetOwnProperty(P) { + const args = this; + const desc = OrdinaryGetOwnProperty(args, P); + if (desc === Value.undefined) { + return desc; + } + const map = args.ParameterMap; + const isMapped = X(HasOwnProperty(map, P)); + if (isMapped === Value.true) { + desc.Value = Get(map, P); + } + return desc; +} + +function ArgumentsDefineOwnProperty(P, Desc) { + const args = this; + const map = args.ParameterMap; + const isMapped = X(HasOwnProperty(map, P)); + let newArgDesc = Desc; + if (isMapped === Value.true && IsDataDescriptor(Desc) === true) { + if (Desc.Value === undefined && Desc.Writable !== undefined && Desc.Writable === Value.false) { + newArgDesc = Descriptor({ ...Desc }); + newArgDesc.Value = X(Get(map, P)); + } + } + const allowed = Q(OrdinaryDefineOwnProperty(args, P, newArgDesc)); + if (allowed === Value.false) { + return Value.false; + } + if (isMapped === Value.true) { + if (IsAccessorDescriptor(Desc) === true) { + map.Delete(P); + } else { + if (Desc.Value !== undefined) { + const setStatus = Set(map, P, Desc.Value, Value.false); + Assert(setStatus === Value.true); + } + if (Desc.Writable !== undefined && Desc.Writable === Value.false) { + map.Delete(P); + } + } + } + return Value.true; +} + +function ArgumentsGet(P, Receiver) { + const args = this; + const map = args.ParameterMap; + const isMapped = X(HasOwnProperty(map, P)); + if (isMapped === Value.false) { + return Q(OrdinaryGet(args, P, Receiver)); + } else { + return Get(map, P); + } +} + +function ArgumentsSet(P, V, Receiver) { + const args = this; + let isMapped; + let map; + if (SameValue(args, Receiver) === Value.false) { + isMapped = false; + } else { + map = args.ParameterMap; + isMapped = X(HasOwnProperty(map, P)) === Value.true; + } + if (isMapped) { + const setStatus = Set(map, P, V, Value.false); + Assert(setStatus === Value.true); + } + return Q(OrdinarySet(args, P, V, Receiver)); +} + +function ArgumentsDelete(P) { + const args = this; + const map = args.ParameterMap; + const isMapped = X(HasOwnProperty(map, P)); + const result = Q(OrdinaryDelete(args, P)); + if (result === Value.true && isMapped === Value.true) { + map.Delete(P); + } + return result; +} + +// 9.4.4.6 #sec-createunmappedargumentsobject +export function CreateUnmappedArgumentsObject(argumentsList) { + const len = argumentsList.length; + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'), ['ParameterMap']); + obj.ParameterMap = Value.undefined; + DefinePropertyOrThrow(obj, new Value('length'), Descriptor({ + Value: new Value(len), + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + })); + let index = 0; + while (index < len) { + const val = argumentsList[index]; + const idxStr = X(ToString(new Value(index))); + X(CreateDataProperty(obj, idxStr, val)); + index += 1; + } + X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({ + Value: surroundingAgent.intrinsic('%Array.prototype.values%'), + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({ + Get: surroundingAgent.intrinsic('%ThrowTypeError%'), + Set: surroundingAgent.intrinsic('%ThrowTypeError%'), + Enumerable: Value.false, + Configurable: Value.false, + }))); + return obj; +} + +function ArgGetterSteps() { + const f = this; + const name = f.Name; + const env = f.Env; + return env.GetBindingValue(name, Value.false); +} + +// 9.4.4.7.1 #sec-makearggetter +function MakeArgGetter(name, env) { + const steps = ArgGetterSteps; + const getter = X(CreateBuiltinFunction(steps, ['Name', 'Env'])); + getter.Name = name; + getter.Env = env; + return getter; +} + +function ArgSetterSteps([value]) { + Assert(value !== undefined); + const f = this; + const name = f.Name; + const env = f.Env; + return env.SetMutableBinding(name, value, Value.false); +} + +// 9.4.4.7.2 #sec-makeargsetter +function MakeArgSetter(name, env) { + const steps = ArgSetterSteps; + const setter = X(CreateBuiltinFunction(steps, ['Name', 'Env'])); + SetFunctionLength(setter, new Value(1)); + setter.Name = name; + setter.Env = env; + return setter; +} + +// 9.4.4.7 #sec-createmappedargumentsobject +export function CreateMappedArgumentsObject(func, formals, argumentsList, env) { + // Assert: formals does not contain a rest parameter, any binding + // patterns, or any initializers. It may contain duplicate identifiers. + const len = argumentsList.length; + const obj = X(MakeBasicObject(['Prototype', 'Extensible', 'ParameterMap'])); + obj.GetOwnProperty = ArgumentsGetOwnProperty; + obj.DefineOwnProperty = ArgumentsDefineOwnProperty; + obj.Get = ArgumentsGet; + obj.Set = ArgumentsSet; + obj.Delete = ArgumentsDelete; + obj.Prototype = surroundingAgent.intrinsic('%Object.prototype%'); + const map = OrdinaryObjectCreate(Value.null); + obj.ParameterMap = map; + const parameterNames = BoundNames_FormalParameters(formals).map(Value); + const numberOfParameters = parameterNames.length; + let index = 0; + while (index < len) { + const val = argumentsList[index]; + const idxStr = X(ToString(new Value(index))); + X(CreateDataProperty(obj, idxStr, val)); + index += 1; + } + X(DefinePropertyOrThrow(obj, new Value('length'), Descriptor({ + Value: new Value(len), + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + const mappedNames = new ValueSet(); + index = numberOfParameters - 1; + while (index >= 0) { + const name = parameterNames[index]; + if (!mappedNames.has(name)) { + mappedNames.add(name); + if (index < len) { + const g = MakeArgGetter(name, env); + const p = MakeArgSetter(name, env); + X(map.DefineOwnProperty(X(ToString(new Value(index))), Descriptor({ + Set: p, + Get: g, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + } + index -= 1; + } + X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({ + Value: surroundingAgent.intrinsic('%Array.prototype.values%'), + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({ + Value: func, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + return obj; +} diff --git a/src/engine262/src/abstract-ops/array-objects.mjs b/src/engine262/src/abstract-ops/array-objects.mjs new file mode 100644 index 0000000..1eb5e3f --- /dev/null +++ b/src/engine262/src/abstract-ops/array-objects.mjs @@ -0,0 +1,250 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Descriptor, + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { + AbstractRelationalComparison, + Assert, + Call, + Construct, + Get, + GetFunctionRealm, + IsDataDescriptor, + IsArray, + IsConstructor, + OrdinaryDefineOwnProperty, + OrdinaryGetOwnProperty, + OrdinaryObjectCreate, + MakeBasicObject, + SameValue, + ToBoolean, + ToNumber, + ToString, + ToUint32, + IsPropertyKey, + IsNonNegativeInteger, + isArrayIndex, +} from './all.mjs'; + +// #sec-array-exotic-objects-defineownproperty-p-desc +function ArrayDefineOwnProperty(P, Desc) { + const A = this; + + Assert(IsPropertyKey(P)); + if (Type(P) === 'String' && P.stringValue() === 'length') { + return Q(ArraySetLength(A, Desc)); + } else if (isArrayIndex(P)) { + const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length')); + Assert(X(IsDataDescriptor(oldLenDesc))); + Assert(oldLenDesc.Configurable === Value.false); + const oldLen = oldLenDesc.Value; + const index = X(ToUint32(P)); + if (index.numberValue() >= oldLen.numberValue() && oldLenDesc.Writable === Value.false) { + return Value.false; + } + const succeeded = X(OrdinaryDefineOwnProperty(A, P, Desc)); + if (succeeded === Value.false) { + return Value.false; + } + if (index.numberValue() >= oldLen.numberValue()) { + oldLenDesc.Value = new Value(index.numberValue() + 1); + const succeeded = OrdinaryDefineOwnProperty(A, new Value('length'), oldLenDesc); // eslint-disable-line no-shadow + Assert(succeeded === Value.true); + } + return Value.true; + } + return OrdinaryDefineOwnProperty(A, P, Desc); +} + +export function isArrayExoticObject(O) { + return O.DefineOwnProperty === ArrayDefineOwnProperty; +} + +// 9.4.2.2 #sec-arraycreate +export function ArrayCreate(length, proto) { + Assert(X(IsNonNegativeInteger(length)) === Value.true); + if (Object.is(length.numberValue(), -0)) { + length = new Value(0); + } + if (length.numberValue() > (2 ** 32) - 1) { + return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', length); + } + if (proto === undefined) { + proto = surroundingAgent.intrinsic('%Array.prototype%'); + } + const A = X(MakeBasicObject(['Prototype', 'Extensible'])); + A.Prototype = proto; + A.DefineOwnProperty = ArrayDefineOwnProperty; + + X(OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({ + Value: length, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + + return A; +} + +// 9.4.2.3 #sec-arrayspeciescreate +export function ArraySpeciesCreate(originalArray, length) { + Assert(Type(length) === 'Number' && Number.isInteger(length.numberValue()) && length.numberValue() >= 0); + if (Object.is(length.numberValue(), -0)) { + length = new Value(+0); + } + const isArray = Q(IsArray(originalArray)); + if (isArray === Value.false) { + return Q(ArrayCreate(length)); + } + let C = Q(Get(originalArray, new Value('constructor'))); + if (IsConstructor(C) === Value.true) { + const thisRealm = surroundingAgent.currentRealmRecord; + const realmC = Q(GetFunctionRealm(C)); + if (thisRealm !== realmC) { + if (SameValue(C, realmC.Intrinsics['%Array%']) === Value.true) { + C = Value.undefined; + } + } + } + if (Type(C) === 'Object') { + C = Q(Get(C, wellKnownSymbols.species)); + if (C === Value.null) { + C = Value.undefined; + } + } + if (C === Value.undefined) { + return Q(ArrayCreate(length)); + } + if (IsConstructor(C) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', C); + } + return Q(Construct(C, [length])); +} + +// 9.4.2.4 #sec-arraysetlength +export function ArraySetLength(A, Desc) { + if (Desc.Value === undefined) { + return OrdinaryDefineOwnProperty(A, new Value('length'), Desc); + } + const newLenDesc = Descriptor({ ...Desc }); + const newLen = Q(ToUint32(Desc.Value)).numberValue(); + const numberLen = Q(ToNumber(Desc.Value)).numberValue(); + if (newLen !== numberLen) { + return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', Desc.Value); + } + newLenDesc.Value = new Value(newLen); + const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length')); + Assert(X(IsDataDescriptor(oldLenDesc))); + Assert(oldLenDesc.Configurable === Value.false); + const oldLen = oldLenDesc.Value.numberValue(); + if (newLen >= oldLen) { + return OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc); + } + if (oldLenDesc.Writable === Value.false) { + return Value.false; + } + let newWritable; + if (newLenDesc.Writable === undefined || newLenDesc.Writable === Value.true) { + newWritable = true; + } else { + newWritable = false; + newLenDesc.Writable = Value.true; + } + const succeeded = X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc)); + if (succeeded === Value.false) { + return Value.false; + } + const keys = []; + A.properties.forEach((value, key) => { + if (isArrayIndex(key) && Number(key.stringValue()) >= newLen) { + keys.push(key); + } + }); + keys.sort((a, b) => Number(b.stringValue()) - Number(a.stringValue())); + for (const P of keys) { + const deleteSucceeded = X(A.Delete(P)); + if (deleteSucceeded === Value.false) { + newLenDesc.Value = new Value(X(ToUint32(P)).numberValue() + 1); + if (newWritable === false) { + newLenDesc.Writable = Value.false; + } + X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc)); + return Value.false; + } + } + if (newWritable === false) { + const s = OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({ Writable: Value.false })); + Assert(s === Value.true); + } + return Value.true; +} + +// 22.1.3.1.1 #sec-isconcatspreadable +export function IsConcatSpreadable(O) { + if (Type(O) !== 'Object') { + return Value.false; + } + const spreadable = Q(Get(O, wellKnownSymbols.isConcatSpreadable)); + if (spreadable !== Value.undefined) { + return ToBoolean(spreadable); + } + return Q(IsArray(O)); +} + +// 22.1.3.27.1 #sec-sortcompare +export function SortCompare(x, y, comparefn) { + if (x === Value.undefined && y === Value.undefined) { + return new Value(+0); + } + if (x === Value.undefined) { + return new Value(1); + } + if (y === Value.undefined) { + return new Value(-1); + } + if (comparefn !== Value.undefined) { + const callRes = Q(Call(comparefn, Value.undefined, [x, y])); + const v = Q(ToNumber(callRes)); + if (v.isNaN()) { + return new Value(+0); + } + return v; + } + const xString = Q(ToString(x)); + const yString = Q(ToString(y)); + const xSmaller = AbstractRelationalComparison(xString, yString); + if (xSmaller === Value.true) { + return new Value(-1); + } + const ySmaller = AbstractRelationalComparison(yString, xString); + if (ySmaller === Value.true) { + return new Value(1); + } + return new Value(+0); +} + +// 22.1.5.1 #sec-createarrayiterator +export function CreateArrayIterator(array, kind) { + // 1. Assert: Type(array) is Object. + Assert(Type(array) === 'Object'); + // 2. Assert: kind is key+value, key, or value. + Assert(kind === 'key+value' || kind === 'key' || kind === 'value'); + // 3. Let iterator be ObjectCreate(%ArrayIteratorPrototype%, « [[IteratedArrayLike]], [[ArrayLikeNextIndex]], [[ArrayLikeIterationKind]] »). + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ArrayIterator.prototype%'), [ + 'IteratedArrayLike', + 'ArrayLikeNextIndex', + 'ArrayLikeIterationKind', + ]); + // 4. Set iterator.[[IteratedArrayLike]] to array. + iterator.IteratedArrayLike = array; + // 5. Set iterator.[[ArrayLikeNextIndex]] to 0. + iterator.ArrayLikeNextIndex = 0; + // 6. Set iterator.[[ArrayLikeIterationKind]] to kind. + iterator.ArrayLikeIterationKind = kind; + // 7. Return iterator. + return iterator; +} diff --git a/src/engine262/src/abstract-ops/arraybuffer-objects.mjs b/src/engine262/src/abstract-ops/arraybuffer-objects.mjs new file mode 100644 index 0000000..56866bf --- /dev/null +++ b/src/engine262/src/abstract-ops/arraybuffer-objects.mjs @@ -0,0 +1,213 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q, X, NormalCompletion } from '../completion.mjs'; +import { + Assert, OrdinaryCreateFromConstructor, + IsNonNegativeInteger, CreateByteDataBlock, + SameValue, IsConstructor, CopyDataBlockBytes, + typedArrayInfoByType, +} from './all.mjs'; + +// #sec-allocatearraybuffer +export function AllocateArrayBuffer(constructor, byteLength) { + // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »). + const obj = Q(OrdinaryCreateFromConstructor(constructor, '%ArrayBuffer.prototype%', [ + 'ArrayBufferData', 'ArrayBufferByteLength', 'ArrayBufferDetachKey', + ])); + // 2. Assert: ! IsNonNegativeInteger(byteLength) is true. + Assert(X(IsNonNegativeInteger(byteLength)) === Value.true); + // 3. Let block be ? CreateByteDataBlock(byteLength). + const block = Q(CreateByteDataBlock(byteLength)); + // 4. Set obj.[[ArrayBufferData]] to block. + obj.ArrayBufferData = block; + // 5. Set obj.[[ArrayBufferByteLength]] to byteLength. + obj.ArrayBufferByteLength = byteLength; + // 6. Return obj. + return obj; +} + +// #sec-isdetachedbuffer +export function IsDetachedBuffer(arrayBuffer) { + // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + Assert(Type(arrayBuffer) === 'Object' && 'ArrayBufferData' in arrayBuffer); + // 2. If arrayBuffer.[[ArrayBufferData]] is null, return true. + if (arrayBuffer.ArrayBufferData === Value.null) { + return Value.true; + } + // 3. Return false. + return Value.false; +} + +// #sec-detacharraybuffer +export function DetachArrayBuffer(arrayBuffer, key) { + // 1. Assert: Type(arrayBuffer) is Object and it has [[ArrayBufferData]], [[ArrayBufferByteLength]], and [[ArrayBufferDetachKey]] internal slots. + Assert(Type(arrayBuffer) === 'Object' + && 'ArrayBufferData' in arrayBuffer + && 'ArrayBufferByteLength' in arrayBuffer + && 'ArrayBufferDetachKey' in arrayBuffer); + // 2. Assert: IsSharedArrayBuffer(arrayBuffer) is false. + Assert(IsSharedArrayBuffer(arrayBuffer) === Value.false); + // 3. If key is not present, set key to undefined. + if (key === undefined) { + key = Value.undefined; + } + // 4. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception. + if (SameValue(arrayBuffer.ArrayBufferDetachKey, key) === Value.false) { + return surroundingAgent.Throw('TypeError', 'BufferDetachKeyMismatch', key, arrayBuffer); + } + // 5. Set arrayBuffer.[[ArrayBufferData]] to null. + arrayBuffer.ArrayBufferData = Value.null; + // 6. Set arrayBuffer.[[ArrayBufferByteLength]] to 0. + arrayBuffer.ArrayBufferByteLength = new Value(0); + // 7. Return NormalCompletion(null). + return NormalCompletion(Value.null); +} + +// #sec-issharedarraybuffer +export function IsSharedArrayBuffer(_obj) { + return Value.false; +} + +export function CloneArrayBuffer(srcBuffer, srcByteOffset, srcLength, cloneConstructor) { + // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + Assert(Type(srcBuffer) === 'Object' && 'ArrayBufferData' in srcBuffer); + // 2. Assert: IsConstructor(cloneConstructor) is true. + Assert(IsConstructor(cloneConstructor) === Value.true); + // 3. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength). + const targetBuffer = Q(AllocateArrayBuffer(cloneConstructor, srcLength)); + // 4. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. Let srcBlock be srcBuffer.[[ArrayBufferData]]. + const srcBlock = srcBuffer.ArrayBufferData; + // 6. Let targetBlock be targetBuffer.[[ArrayBufferData]]. + const targetBlock = targetBuffer.ArrayBufferData; + // 7. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength). + CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset.numberValue(), srcLength.numberValue()); + // 8. Return targetBuffer. + return targetBuffer; +} + +// #sec-isbigintelementtype +export function IsBigIntElementType(type) { + // 1. If type is BigUint64 or BigInt64, return true. + if (type === 'BigUint64' || type === 'BigInt64') { + return Value.true; + } + // 2. Return false + return Value.false; +} + +const throwawayBuffer = new ArrayBuffer(8); +const throwawayDataView = new DataView(throwawayBuffer); +const throwawayArray = new Uint8Array(throwawayBuffer); + +// #sec-rawbytestonumeric +export function RawBytesToNumeric(type, rawBytes, isLittleEndian) { + // 1. Let elementSize be the Element Size value specified in Table 61 for Element Type type. + const elementSize = typedArrayInfoByType[type].ElementSize; + Assert(elementSize === rawBytes.length); + const dataViewType = type === 'Uint8C' ? 'Uint8' : type; + Object.assign(throwawayArray, rawBytes); + return new Value(throwawayDataView[`get${dataViewType}`](0, isLittleEndian === Value.true)); +} + +// #sec-getvaluefrombuffer +export function GetValueFromBuffer(arrayBuffer, byteIndex, type, isTypedArray, order, isLittleEndian) { + // 1. Assert: IsDetachedBuffer(arrayBuffer) is false. + Assert(IsDetachedBuffer(arrayBuffer) === Value.false); + // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. + // 3. Assert: ! IsNonNegativeInteger(byteIndex) is true. + Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true); + // 4. Let block be arrayBuffer.[[ArrayBufferData]]. + const block = arrayBuffer.ArrayBufferData; + // 5. Let elementSize be the Element Size value specified in Table 61 for Element Type type. + const elementSize = typedArrayInfoByType[type].ElementSize; + // 6. If IsSharedArrayBuffer(arrayBuffer) is true, then + if (IsSharedArrayBuffer(arrayBuffer) === Value.true) { + Assert(false); + } + // 7. Else, let rawValue be a List of elementSize containing, in order, the elementSize sequence of bytes starting with block[byteIndex]. + const rawValue = [...block.subarray(byteIndex.numberValue(), byteIndex.numberValue() + elementSize)]; + // 8. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. + if (isLittleEndian === undefined) { + isLittleEndian = surroundingAgent.LittleEndian; + } + // 9. Return RawBytesToNumeric(type, rawValue, isLittleEndian). + return RawBytesToNumeric(type, rawValue, isLittleEndian); +} + +const float32NaNLE = Object.freeze([0, 0, 192, 127]); +const float32NaNBE = Object.freeze([127, 192, 0, 0]); +const float64NaNLE = Object.freeze([0, 0, 0, 0, 0, 0, 248, 127]); +const float64NaNBE = Object.freeze([127, 248, 0, 0, 0, 0, 0, 0]); + +// #sec-numerictorawbytes +export function NumericToRawBytes(type, value, isLittleEndian) { + Assert(Type(isLittleEndian) === 'Boolean'); + isLittleEndian = isLittleEndian === Value.true; + let rawBytes; + // One day, we will write our own IEEE 754 and two's complement encoder… + if (type === 'Float32') { + if (Number.isNaN(value.numberValue())) { + rawBytes = isLittleEndian ? [...float32NaNLE] : [...float32NaNBE]; + } else { + throwawayDataView.setFloat32(0, value.numberValue(), isLittleEndian); + rawBytes = [...throwawayArray.subarray(0, 4)]; + } + } else if (type === 'Float64') { + if (Number.isNaN(value.numberValue())) { + rawBytes = isLittleEndian ? [...float64NaNLE] : [...float64NaNBE]; + } else { + throwawayDataView.setFloat64(0, value.numberValue(), isLittleEndian); + rawBytes = [...throwawayArray.subarray(0, 8)]; + } + } else { + // a. Let n be the Element Size value specified in Table 61 for Element Type type. + const n = typedArrayInfoByType[type].ElementSize; + // b. Let convOp be the abstract operation named in the Conversion Operation column in Table 61 for Element Type type. + const convOp = typedArrayInfoByType[type].ConversionOperation; + // c. Let intValue be convOp(value) treated as a mathematical value, whether the result is a BigInt or Number. + const intValue = X(convOp(value)); + const dataViewType = type === 'Uint8C' ? 'Uint8' : type; + throwawayDataView[`set${dataViewType}`](0, intValue.bigintValue ? intValue.bigintValue() : intValue.numberValue(), isLittleEndian); + rawBytes = [...throwawayArray.subarray(0, n)]; + } + return rawBytes; +} + +// #sec-setvalueinbuffer +export function SetValueInBuffer(arrayBuffer, byteIndex, type, value, isTypedArray, order, isLittleEndian) { + // 1. Assert: IsDetachedBuffer(arrayBuffer) is false. + Assert(IsDetachedBuffer(arrayBuffer) === Value.false); + // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. + // 3. Assert: ! IsNonNegativeInteger(byteIndex) is true. + Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true); + // 4. Assert: Type(value) is BigInt if ! IsBigIntElementType(type) is true; otherwise, Type(value) is Number. + if (X(IsBigIntElementType(type)) === Value.true) { + Assert(Type(value) === 'BigInt'); + } else { + Assert(Type(value) === 'Number'); + } + // 5. Let block be arrayBuffer.[[ArrayBufferData]]. + const block = arrayBuffer.ArrayBufferData; + // 6. Let elementSize be the Element Size value specified in Table 61 for Element Type type. + // const elementSize = typedArrayInfo[type].ElementSize; + // 7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. + if (isLittleEndian === undefined) { + isLittleEndian = surroundingAgent.LittleEndian; + } + // 8. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian). + const rawBytes = NumericToRawBytes(type, value, isLittleEndian); + // 9. If IsSharedArrayBuffer(arrayBuffer) is true, then + if (IsSharedArrayBuffer(arrayBuffer) === Value.true) { + Assert(false); + } + // 10. Else, store the individual bytes of rawBytes into block, in order, starting at block[byteIndex]. + rawBytes.forEach((byte, i) => { + block[byteIndex.numberValue() + i] = byte; + }); + // 11. Return NormalCompletion(undefined). + return NormalCompletion(Value.undefined); +} diff --git a/src/engine262/src/abstract-ops/async-function-operations.mjs b/src/engine262/src/abstract-ops/async-function-operations.mjs new file mode 100644 index 0000000..3393a89 --- /dev/null +++ b/src/engine262/src/abstract-ops/async-function-operations.mjs @@ -0,0 +1,47 @@ +import { isExpressionBody } from '../ast.mjs'; +import { EnsureCompletion, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + Evaluate_ExpressionBody, + Evaluate_FunctionBody, +} from '../runtime-semantics/all.mjs'; +import { Value } from '../value.mjs'; +import { resume } from '../helpers.mjs'; +import { Assert, Call } from './all.mjs'; + +// This file covers abstract operations defined in +// 25.7 #sec-async-function-objects + +// https://tc39.es/proposal-top-level-await/#sec-asyncblockstart +export function AsyncBlockStart(promiseCapability, asyncBody, asyncContext) { + asyncContext.promiseCapability = promiseCapability; + + const runningContext = surroundingAgent.runningExecutionContext; + asyncContext.codeEvaluationState = (function* resumer() { + const evaluator = isExpressionBody(asyncBody) ? Evaluate_ExpressionBody : Evaluate_FunctionBody; + const result = EnsureCompletion(yield* evaluator(asyncBody)); + // Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + surroundingAgent.executionContextStack.pop(asyncContext); + if (result.Type === 'normal') { + X(Call(promiseCapability.Resolve, Value.undefined, [Value.undefined])); + } else if (result.Type === 'return') { + X(Call(promiseCapability.Resolve, Value.undefined, [result.Value])); + } else { + Assert(result.Type === 'throw'); + X(Call(promiseCapability.Reject, Value.undefined, [result.Value])); + } + return Value.undefined; + }()); + surroundingAgent.executionContextStack.push(asyncContext); + const result = EnsureCompletion(resume(asyncContext, undefined)); + Assert(surroundingAgent.runningExecutionContext === runningContext); + Assert(result.Type === 'normal' && result.Value === Value.undefined); + return Value.undefined; +} + +// 25.7.5.1 #sec-async-functions-abstract-operations-async-function-start +export function AsyncFunctionStart(promiseCapability, asyncFunctionBody) { + const runningContext = surroundingAgent.runningExecutionContext; + const asyncContext = runningContext.copy(); + X(AsyncBlockStart(promiseCapability, asyncFunctionBody, asyncContext)); +} diff --git a/src/engine262/src/abstract-ops/async-generator-objects.mjs b/src/engine262/src/abstract-ops/async-generator-objects.mjs new file mode 100644 index 0000000..f45f4dc --- /dev/null +++ b/src/engine262/src/abstract-ops/async-generator-objects.mjs @@ -0,0 +1,193 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate_FunctionBody } from '../runtime-semantics/all.mjs'; +import { + Q, X, + Await, + Completion, + EnsureCompletion, + NormalCompletion, + AbruptCompletion, +} from '../completion.mjs'; +import { Value, Type } from '../value.mjs'; +import { resume, handleInResume } from '../helpers.mjs'; +import { + Assert, + Call, + CreateBuiltinFunction, + CreateIterResultObject, + GetGeneratorKind, + NewPromiseCapability, + PerformPromiseThen, + PromiseResolve, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 25.5 #sec-asyncgenerator-objects + +// 25.5.3.1 #sec-asyncgeneratorrequest-records +class AsyncGeneratorRequestRecord { + constructor(completion, promiseCapability) { + this.Completion = completion; + this.Capability = promiseCapability; + } +} + +// 25.5.3.2 #sec-asyncgeneratorstart +export function AsyncGeneratorStart(generator, generatorBody) { + // Assert: generator is an AsyncGenerator instance. + Assert(generator.AsyncGeneratorState === Value.undefined); + const genContext = surroundingAgent.runningExecutionContext; + genContext.Generator = generator; + genContext.codeEvaluationState = (function* resumer() { + const result = EnsureCompletion(yield* Evaluate_FunctionBody(generatorBody)); + // Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + surroundingAgent.executionContextStack.pop(genContext); + generator.AsyncGeneratorState = 'completed'; + let resultValue; + if (result instanceof NormalCompletion) { + resultValue = Value.undefined; + } else { + resultValue = result.Value; + if (result.Type !== 'return') { + return X(AsyncGeneratorReject(generator, resultValue)); + } + } + return X(AsyncGeneratorResolve(generator, resultValue, Value.true)); + }()); + generator.AsyncGeneratorContext = genContext; + generator.AsyncGeneratorState = 'suspendedStart'; + generator.AsyncGeneratorQueue = []; + return Value.undefined; +} + +// 25.5.3.3 #sec-asyncgeneratorresolve +function AsyncGeneratorResolve(generator, value, done) { + // Assert: generator is an AsyncGenerator instance. + const queue = generator.AsyncGeneratorQueue; + Assert(queue.length > 0); + const next = queue.shift(); + const promiseCapability = next.Capability; + const iteratorResult = X(CreateIterResultObject(value, done)); + X(Call(promiseCapability.Resolve, Value.undefined, [iteratorResult])); + X(AsyncGeneratorResumeNext(generator)); + return Value.undefined; +} + +// 25.5.3.4 #sec-asyncgeneratorreject +function AsyncGeneratorReject(generator, exception) { + // Assert: generator is an AsyncGenerator instance. + const queue = generator.AsyncGeneratorQueue; + Assert(queue.length > 0); + const next = queue.shift(); + const promiseCapability = next.Capability; + X(Call(promiseCapability.Reject, Value.undefined, [exception])); + X(AsyncGeneratorResumeNext(generator)); + return Value.undefined; +} + +// 25.5.3.5.1 #async-generator-resume-next-return-processor-fulfilled +function AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions([value = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + F.Generator.AsyncGeneratorState = 'completed'; + return X(AsyncGeneratorResolve(F.Generator, value, Value.true)); +} + +// 25.5.3.5.2 #async-generator-resume-next-return-processor-rejected +function AsyncGeneratorResumeNextReturnProcessorRejectedFunctions([reason = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + F.Generator.AsyncGeneratorState = 'completed'; + return X(AsyncGeneratorReject(F.Generator, reason)); +} + +// 25.5.3.5 #sec-asyncgeneratorresumenext +function AsyncGeneratorResumeNext(generator) { + // Assert: generator is an AsyncGenerator instance. + let state = generator.AsyncGeneratorState; + Assert(state !== 'executing'); + if (state === 'awaiting-return') { + return Value.undefined; + } + const queue = generator.AsyncGeneratorQueue; + if (queue.length === 0) { + return Value.undefined; + } + const next = queue[0]; + Assert(next instanceof AsyncGeneratorRequestRecord); + const completion = next.Completion; + if (completion instanceof AbruptCompletion) { + if (state === 'suspendedStart') { + generator.AsyncGeneratorState = 'completed'; + state = 'completed'; + } + if (state === 'completed') { + if (completion.Type === 'return') { + generator.AsyncGeneratorState = 'awaiting-return'; + const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), completion.Value)); + const stepsFulfilled = AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions; + const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['Generator'])); + onFulfilled.Generator = generator; + const stepsRejected = AsyncGeneratorResumeNextReturnProcessorRejectedFunctions; + const onRejected = X(CreateBuiltinFunction(stepsRejected, ['Generator'])); + onRejected.Generator = generator; + X(PerformPromiseThen(promise, onFulfilled, onRejected)); + return Value.undefined; + } else { + Assert(completion.Type === 'throw'); + X(AsyncGeneratorReject(generator, completion.Value)); + return Value.undefined; + } + } + } else if (state === 'completed') { + return X(AsyncGeneratorResolve(generator, Value.undefined, Value.true)); + } + Assert(state === 'suspendedStart' || state === 'suspendedYield'); + const genContext = generator.AsyncGeneratorContext; + const callerContext = surroundingAgent.runningExecutionContext; + // Suspend callerContext + generator.AsyncGeneratorState = 'executing'; + surroundingAgent.executionContextStack.push(genContext); + const result = resume(genContext, completion); + Assert(!(result instanceof AbruptCompletion)); + Assert(surroundingAgent.runningExecutionContext === callerContext); + return Value.undefined; +} + +// 25.5.3.6 #sec-asyncgeneratorenqueue +export function AsyncGeneratorEnqueue(generator, completion) { + Assert(completion instanceof Completion); + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + if (Type(generator) !== 'Object' || !('AsyncGeneratorState' in generator)) { + const badGeneratorError = surroundingAgent.Throw('TypeError', 'NotATypeObject', 'AsyncGenerator', generator).Value; + X(Call(promiseCapability.Reject, Value.undefined, [badGeneratorError])); + return promiseCapability.Promise; + } + const queue = generator.AsyncGeneratorQueue; + const request = new AsyncGeneratorRequestRecord(completion, promiseCapability); + queue.push(request); + const state = generator.AsyncGeneratorState; + if (state !== 'executing') { + X(AsyncGeneratorResumeNext(generator)); + } + return promiseCapability.Promise; +} + +// 25.5.3.7 #sec-asyncgeneratoryield +export function* AsyncGeneratorYield(value) { + const genContext = surroundingAgent.runningExecutionContext; + Assert(genContext.Generator !== Value.undefined); + const generator = genContext.Generator; + Assert(GetGeneratorKind() === 'async'); + value = Q(yield* Await(value)); + generator.AsyncGeneratorState = 'suspendedYield'; + surroundingAgent.executionContextStack.pop(genContext); + const resumptionValue = EnsureCompletion(yield handleInResume(AsyncGeneratorResolve, generator, value, Value.false)); + if (resumptionValue.Type !== 'return') { + return Completion(resumptionValue); + } + const awaited = EnsureCompletion(yield* Await(resumptionValue.Value)); + if (awaited.Type === 'Throw') { + return Completion(awaited); + } + Assert(awaited.Type === 'normal'); + return new Completion('return', awaited.Value, undefined); +} diff --git a/src/engine262/src/abstract-ops/data-types-and-values.mjs b/src/engine262/src/abstract-ops/data-types-and-values.mjs new file mode 100644 index 0000000..395db68 --- /dev/null +++ b/src/engine262/src/abstract-ops/data-types-and-values.mjs @@ -0,0 +1,49 @@ +import { Type, Value } from '../value.mjs'; +import { X } from '../completion.mjs'; +import { CanonicalNumericIndexString } from './all.mjs'; + +// This file covers predicates defined in +// 6 #sec-ecmascript-data-types-and-values + +// 6.1.4 #leading-surrogate +export function isLeadingSurrogate(cp) { + return cp >= 0xD800 && cp <= 0xDBFF; +} + +// 6.1.4 #trailing-surrogate +export function isTrailingSurrogate(cp) { + return cp >= 0xDC00 && cp <= 0xDFFF; +} + +// 6.1.7 #integer-index +export function isIntegerIndex(V) { + if (Type(V) !== 'String') { + return false; + } + const numeric = X(CanonicalNumericIndexString(V)); + if (numeric === Value.undefined) { + return false; + } + if (Object.is(numeric.numberValue(), +0)) { + return true; + } + return numeric.numberValue() > 0 && Number.isSafeInteger(numeric.numberValue()); +} + +// 6.1.7 #array-index +export function isArrayIndex(V) { + if (Type(V) !== 'String') { + return false; + } + const numeric = X(CanonicalNumericIndexString(V)); + if (numeric === Value.undefined) { + return false; + } + if (!Number.isInteger(numeric.numberValue())) { + return false; + } + if (Object.is(numeric.numberValue(), +0)) { + return true; + } + return numeric.numberValue() > 0 && numeric.numberValue() < (2 ** 32) - 1; +} diff --git a/src/engine262/src/abstract-ops/dataview-objects.mjs b/src/engine262/src/abstract-ops/dataview-objects.mjs new file mode 100644 index 0000000..59d3e33 --- /dev/null +++ b/src/engine262/src/abstract-ops/dataview-objects.mjs @@ -0,0 +1,91 @@ +import { Q, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { + Assert, + GetValueFromBuffer, + IsDetachedBuffer, + IsBigIntElementType, + SetValueInBuffer, + ToBoolean, + ToIndex, + ToNumber, + ToBigInt, + RequireInternalSlot, + typedArrayInfoByType, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 24.3 #sec-dataview-objects + +// 24.3.1.1 #sec-getviewvalue +export function GetViewValue(view, requestIndex, isLittleEndian, type) { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + Q(RequireInternalSlot(view, 'DataView')); + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in view); + // 3. Let getIndex be ? ToIndex(requestIndex). + const getIndex = Q(ToIndex(requestIndex)).numberValue(); + // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian). + isLittleEndian = X(ToBoolean(isLittleEndian)); + // 5. Let buffer be view.[[ViewedArrayBuffer]]. + const buffer = view.ViewedArrayBuffer; + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 7. Let viewOffset be view.[[ByteOffset]]. + const viewOffset = view.ByteOffset.numberValue(); + // 8. Let viewSize be view.[[ByteLength]]. + const viewSize = view.ByteLength.numberValue(); + // 9. Let elementSize be the Element Size value specified in Table 61 for Element Type type. + const elementSize = typedArrayInfoByType[type].ElementSize; + // 10. If getIndex + elementSize > viewSize, throw a RangeError exception. + if (getIndex + elementSize > viewSize) { + return surroundingAgent.Throw('RangeError', 'DataViewOOB'); + } + // 11. Let bufferIndex be getIndex + viewOffset. + const bufferIndex = new Value(getIndex + viewOffset); + // 12. Return GetValueFromBuffer(buffer, bufferIndex, type, false, Unordered, isLittleEndian). + return GetValueFromBuffer(buffer, bufferIndex, type, Value.false, 'Unordered', isLittleEndian); +} + +// 24.3.1.2 #sec-setviewvalue +export function SetViewValue(view, requestIndex, isLittleEndian, type, value) { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + Q(RequireInternalSlot(view, 'DataView')); + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in view); + // 3. Let getIndex be ? ToIndex(requestIndex). + const getIndex = Q(ToIndex(requestIndex)).numberValue(); + // 4. If ! IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value). + // 5. Otherwise, let numberValue be ? ToNumber(value). + let numberValue; + if (X(IsBigIntElementType(type)) === Value.true) { + numberValue = Q(ToBigInt(value)); + } else { + numberValue = Q(ToNumber(value)); + } + // 6. Set isLittleEndian to ! ToBoolean(isLittleEndian). + isLittleEndian = X(ToBoolean(isLittleEndian)); + // 7. Let buffer be view.[[ViewedArrayBuffer]]. + const buffer = view.ViewedArrayBuffer; + // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 9. Let viewOffset be view.[[ByteOffset]]. + const viewOffset = view.ByteOffset.numberValue(); + // 10. Let viewSize be view.[[ByteLength]]. + const viewSize = view.ByteLength.numberValue(); + // 11. Let elementSize be the Element Size value specified in Table 61 for Element Type type. + const elementSize = typedArrayInfoByType[type].ElementSize; + // 12. If getIndex + elementSize > viewSize, throw a RangeError exception. + if (getIndex + elementSize > viewSize) { + return surroundingAgent.Throw('RangeError', 'DataViewOOB'); + } + // 13. Let bufferIndex be getIndex + viewOffset. + const bufferIndex = new Value(getIndex + viewOffset); + // 14. Return SetValueInBuffer(buffer, bufferIndex, type, numberValue, false, Unordered, isLittleEndian). + return SetValueInBuffer(buffer, bufferIndex, type, numberValue, Value.false, 'Unordered', isLittleEndian); +} diff --git a/src/engine262/src/abstract-ops/date-objects.mjs b/src/engine262/src/abstract-ops/date-objects.mjs new file mode 100644 index 0000000..b0d4427 --- /dev/null +++ b/src/engine262/src/abstract-ops/date-objects.mjs @@ -0,0 +1,233 @@ +// This file covers abstract operations defined in +// 20.3 #sec-date-objects + +import { + Value, +} from '../value.mjs'; +import { X } from '../completion.mjs'; +import { + ToInteger, +} from './all.mjs'; + +const mod = (n, m) => { + const r = n % m; + return Math.floor(r >= 0 ? r : r + m); +}; + +export const HoursPerDay = 24; +export const MinutesPerHour = 60; +export const SecondsPerMinute = 60; +export const msPerSecond = 1000; +export const msPerMinute = msPerSecond * SecondsPerMinute; +export const msPerHour = msPerMinute * MinutesPerHour; +export const msPerDay = msPerHour * HoursPerDay; + +// 20.3.1.2 #sec-day-number-and-time-within-day +export function Day(t) { + return new Value(Math.floor(t.numberValue() / msPerDay)); +} + +export function TimeWithinDay(t) { + return new Value(mod(t.numberValue(), msPerDay)); +} + +// 20.3.1.3 #sec-year-number +export function DaysInYear(y) { + y = y.numberValue(); + if (mod(y, 4) !== 0) { + return new Value(365); + } + if (mod(y, 4) === 0 && mod(y, 100) !== 0) { + return new Value(366); + } + if (mod(y, 100) === 0 && mod(y, 400) !== 0) { + return new Value(365); + } + if (mod(y, 400) === 0) { + return new Value(366); + } +} + +export function DayFromYear(y) { + y = y.numberValue(); + return new Value(365 * (y - 1970) + Math.floor((y - 1969) / 4) - Math.floor((y - 1901) / 100) + Math.floor((y - 1601) / 400)); +} + +export function TimeFromYear(y) { + return new Value(msPerDay * DayFromYear(y).numberValue()); +} + +export const msPerAverageYear = 12 * 30.436875 * msPerDay; + +export function YearFromTime(t) { + t = t.numberValue(); + let year = Math.floor((t + msPerAverageYear / 2) / msPerAverageYear) + 1970; + if (TimeFromYear(new Value(year)).numberValue() > t) { + year -= 1; + } + return new Value(year); +} + +export function InLeapYear(t) { + if (DaysInYear(YearFromTime(t)).numberValue() === 365) { + return new Value(0); + } + if (DaysInYear(YearFromTime(t)).numberValue() === 366) { + return new Value(1); + } +} + +// 20.3.1.4 #sec-month-number +export function MonthFromTime(t) { + const dayWithinYear = DayWithinYear(t).numberValue(); + const inLeapYear = InLeapYear(t).numberValue(); + if (dayWithinYear >= 0 && dayWithinYear < 31) { + return new Value(0); + } + if (dayWithinYear >= 31 && dayWithinYear < 59 + inLeapYear) { + return new Value(1); + } + if (dayWithinYear >= 59 + inLeapYear && dayWithinYear < 90 + inLeapYear) { + return new Value(2); + } + if (dayWithinYear >= 90 + inLeapYear && dayWithinYear < 120 + inLeapYear) { + return new Value(3); + } + if (dayWithinYear >= 120 + inLeapYear && dayWithinYear < 151 + inLeapYear) { + return new Value(4); + } + if (dayWithinYear >= 151 + inLeapYear && dayWithinYear < 181 + inLeapYear) { + return new Value(5); + } + if (dayWithinYear >= 181 + inLeapYear && dayWithinYear < 212 + inLeapYear) { + return new Value(6); + } + if (dayWithinYear >= 212 + inLeapYear && dayWithinYear < 243 + inLeapYear) { + return new Value(7); + } + if (dayWithinYear >= 243 + inLeapYear && dayWithinYear < 273 + inLeapYear) { + return new Value(8); + } + if (dayWithinYear >= 273 + inLeapYear && dayWithinYear < 304 + inLeapYear) { + return new Value(9); + } + if (dayWithinYear >= 304 + inLeapYear && dayWithinYear < 334 + inLeapYear) { + return new Value(10); + } + if (dayWithinYear >= 334 + inLeapYear && dayWithinYear < 365 + inLeapYear) { + return new Value(11); + } +} + +export function DayWithinYear(t) { + return new Value(Day(t).numberValue() - DayFromYear(YearFromTime(t)).numberValue()); +} + +// 20.3.1.5 #sec-date-number +export function DateFromTime(t) { + const dayWithinYear = DayWithinYear(t).numberValue(); + const monthFromTime = MonthFromTime(t).numberValue(); + const inLeapYear = InLeapYear(t).numberValue(); + switch (monthFromTime) { + case 0: return new Value(dayWithinYear + 1); + case 1: return new Value(dayWithinYear - 30); + case 2: return new Value(dayWithinYear - 58 - inLeapYear); + case 3: return new Value(dayWithinYear - 89 - inLeapYear); + case 4: return new Value(dayWithinYear - 119 - inLeapYear); + case 5: return new Value(dayWithinYear - 150 - inLeapYear); + case 6: return new Value(dayWithinYear - 180 - inLeapYear); + case 7: return new Value(dayWithinYear - 211 - inLeapYear); + case 8: return new Value(dayWithinYear - 242 - inLeapYear); + case 9: return new Value(dayWithinYear - 272 - inLeapYear); + case 10: return new Value(dayWithinYear - 303 - inLeapYear); + case 11: return new Value(dayWithinYear - 333 - inLeapYear); + default: // Unreachable + } +} + +// 20.3.1.6 #sec-week-day +export function WeekDay(t) { + return new Value(mod(Day(t).numberValue() + 4, 7)); +} + +// 20.3.1.7 #sec-local-time-zone-adjustment +export function LocalTZA(/* t, isUTC */) { + // TODO: implement this function properly. + return 0; +} + +// 20.3.1.8 #sec-localtime +export function LocalTime(t) { + return new Value(t.numberValue() + LocalTZA(t, true)); +} + +// 20.3.1.9 #sec-utc-t +export function UTC(t) { + return new Value(t.numberValue() - LocalTZA(t, false)); +} + +// 20.3.1.10 #sec-hours-minutes-second-and-milliseconds +export function HourFromTime(t) { + return new Value(mod(Math.floor(t.numberValue() / msPerHour), HoursPerDay)); +} + +export function MinFromTime(t) { + return new Value(mod(Math.floor(t.numberValue() / msPerMinute), MinutesPerHour)); +} + +export function SecFromTime(t) { + return new Value(mod(Math.floor(t.numberValue() / msPerSecond), SecondsPerMinute)); +} + +export function msFromTime(t) { + return new Value(mod(t.numberValue(), msPerSecond)); +} + +// 20.3.1.11 #sec-maketime +export function MakeTime(hour, min, sec, ms) { + if (!Number.isFinite(hour.numberValue()) || !Number.isFinite(min.numberValue()) || !Number.isFinite(sec.numberValue()) || !Number.isFinite(ms.numberValue())) { + return new Value(NaN); + } + const h = X(ToInteger(hour)).numberValue(); + const m = X(ToInteger(min)).numberValue(); + const s = X(ToInteger(sec)).numberValue(); + const milli = X(ToInteger(ms)).numberValue(); + const t = h * msPerHour + m * msPerMinute + s * msPerSecond + milli; + return new Value(t); +} + +const daysWithinYearToEndOfMonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + +// 20.3.1.12 #sec-makeday +export function MakeDay(year, month, date) { + if (!Number.isFinite(year.numberValue()) || !Number.isFinite(month.numberValue()) || !Number.isFinite(date.numberValue())) { + return new Value(NaN); + } + const y = X(ToInteger(year)).numberValue(); + const m = X(ToInteger(month)).numberValue(); + const dt = X(ToInteger(date)).numberValue(); + const ym = y + Math.floor(m / 12); + const mn = mod(m, 12); + const ymday = DayFromYear(new Value(ym + (mn > 1 ? 1 : 0))).numberValue() - 365 * (mn > 1 ? 1 : 0) + daysWithinYearToEndOfMonth[mn]; + const t = new Value(ymday * msPerDay); + return new Value(Day(t).numberValue() + dt - 1); +} + +// 20.3.1.13 #sec-makedate +export function MakeDate(day, time) { + if (!Number.isFinite(day.numberValue()) || !Number.isFinite(time.numberValue())) { + return new Value(NaN); + } + return new Value(day.numberValue() * msPerDay + time.numberValue()); +} + +// 20.3.1.14 #sec-timeclip +export function TimeClip(time) { + if (!Number.isFinite(time.numberValue())) { + return new Value(NaN); + } + if (Math.abs(time.numberValue()) > 8.64e15) { + return new Value(NaN); + } + return X(ToInteger(time)); +} diff --git a/src/engine262/src/abstract-ops/execution-contexts.mjs b/src/engine262/src/abstract-ops/execution-contexts.mjs new file mode 100644 index 0000000..3c518ac --- /dev/null +++ b/src/engine262/src/abstract-ops/execution-contexts.mjs @@ -0,0 +1,68 @@ +import { Q } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + GetIdentifierReference, + LexicalEnvironment, +} from '../environment.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { Assert } from './all.mjs'; + +// This file covers abstract operations defined in +// 8.3 #sec-execution-contexts + +// 8.3.1 #sec-getactivescriptormodule +export function GetActiveScriptOrModule() { + for (let i = surroundingAgent.executionContextStack.length - 1; i >= 0; i -= 1) { + const e = surroundingAgent.executionContextStack[i]; + if (e.ScriptOrModule !== Value.null) { + return e.ScriptOrModule; + } + } + return Value.null; +} + +// 8.3.2 #sec-resolvebinding +export function ResolveBinding(name, env, strict) { + if (!env || Type(env) === 'Undefined') { + env = surroundingAgent.runningExecutionContext.LexicalEnvironment; + } + Assert(env instanceof LexicalEnvironment); + return GetIdentifierReference(env, name, strict ? Value.true : Value.false); +} + +// 8.3.3 #sec-getthisenvironment +export function GetThisEnvironment() { + let lex = surroundingAgent.runningExecutionContext.LexicalEnvironment; + while (true) { // eslint-disable-line no-constant-condition + const envRec = lex.EnvironmentRecord; + const exists = envRec.HasThisBinding(); + if (exists === Value.true) { + return envRec; + } + const outer = lex.outerEnvironmentReference; + Assert(Type(outer) !== 'Null'); + lex = outer; + } +} + +// 8.3.4 #sec-resolvethisbinding +export function ResolveThisBinding() { + const envRec = GetThisEnvironment(); + return Q(envRec.GetThisBinding()); +} + +// 8.3.5 #sec-getnewtarget +export function GetNewTarget() { + const envRec = GetThisEnvironment(); + Assert('NewTarget' in envRec); + return envRec.NewTarget; +} + +// 8.3.6 #sec-getglobalobject +export function GetGlobalObject() { + const currentRealm = surroundingAgent.currentRealmRecord; + return currentRealm.GlobalObject; +} diff --git a/src/engine262/src/abstract-ops/function-operations.mjs b/src/engine262/src/abstract-ops/function-operations.mjs new file mode 100644 index 0000000..6bc500d --- /dev/null +++ b/src/engine262/src/abstract-ops/function-operations.mjs @@ -0,0 +1,410 @@ +import { + surroundingAgent, + ExecutionContext, +} from '../engine.mjs'; +import { Realm } from '../realm.mjs'; +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { + EnsureCompletion, NormalCompletion, Q, + ReturnIfAbrupt, + X, +} from '../completion.mjs'; +import { ExpectedArgumentCount } from '../static-semantics/all.mjs'; +import { + EvaluateBody_AsyncConciseBody_ExpressionBody, + EvaluateBody_AsyncFunctionBody, + EvaluateBody_ConciseBody_ExpressionBody, + EvaluateBody_FunctionBody, + EvaluateBody_GeneratorBody, + EvaluateBody_AsyncGeneratorBody, + getFunctionBodyType, +} from '../runtime-semantics/all.mjs'; +import { + FunctionEnvironmentRecord, + GlobalEnvironmentRecord, + NewFunctionEnvironment, +} from '../environment.mjs'; +import { unwind, OutOfRange } from '../helpers.mjs'; +import { + Assert, + DefinePropertyOrThrow, + GetActiveScriptOrModule, + HasOwnProperty, + IsConstructor, + IsExtensible, + IsInteger, + MakeBasicObject, + OrdinaryObjectCreate, + OrdinaryCreateFromConstructor, + ToObject, + isStrictModeCode, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 9.2 #sec-ecmascript-function-objects +// 9.3 #sec-built-in-function-objects +// and +// 14.9 #sec-tail-position-calls + +export function isECMAScriptFunctionObject(O) { + return 'ECMAScriptCode' in O; +} + +export function isFunctionObject(O) { + return 'Call' in O; +} + +// 9.2.1.1 #sec-prepareforordinarycall +function PrepareForOrdinaryCall(F, newTarget) { + Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object'); + // const callerContext = surroundingAgent.runningExecutionContext; + const calleeContext = new ExecutionContext(); + calleeContext.Function = F; + const calleeRealm = F.Realm; + calleeContext.Realm = calleeRealm; + calleeContext.ScriptOrModule = F.ScriptOrModule; + const localEnv = NewFunctionEnvironment(F, newTarget); + calleeContext.LexicalEnvironment = localEnv; + calleeContext.VariableEnvironment = localEnv; + // Suspend(callerContext); + surroundingAgent.executionContextStack.push(calleeContext); + return calleeContext; +} + +// 9.2.1.2 #sec-ordinarycallbindthis +function OrdinaryCallBindThis(F, calleeContext, thisArgument) { + const thisMode = F.ThisMode; + if (thisMode === 'lexical') { + return new NormalCompletion(Value.undefined); + } + const calleeRealm = F.Realm; + const localEnv = calleeContext.LexicalEnvironment; + let thisValue; + if (thisMode === 'strict') { + thisValue = thisArgument; + } else { + if (thisArgument === Value.undefined || thisArgument === Value.null) { + const globalEnv = calleeRealm.GlobalEnv; + const globalEnvRec = globalEnv.EnvironmentRecord; + Assert(globalEnvRec instanceof GlobalEnvironmentRecord); + thisValue = globalEnvRec.GlobalThisValue; + } else { + thisValue = X(ToObject(thisArgument)); + // NOTE: ToObject produces wrapper objects using calleeRealm. + } + } + const envRec = localEnv.EnvironmentRecord; + Assert(envRec instanceof FunctionEnvironmentRecord); + Assert(envRec.ThisBindingStatus !== 'initialized'); + return envRec.BindThisValue(thisValue); +} + +// 9.2.1.3 #sec-ordinarycallevaluatebody +export function* OrdinaryCallEvaluateBody(F, argumentsList) { + switch (getFunctionBodyType(F.ECMAScriptCode)) { + // FunctionBody : FunctionStatementList + // ConciseBody : `{` FunctionBody `}` + case 'FunctionBody': + case 'ConciseBody_FunctionBody': + return yield* EvaluateBody_FunctionBody(F.ECMAScriptCode.body.body, F, argumentsList); + + // ConciseBody : ExpressionBody + case 'ConciseBody_ExpressionBody': + return yield* EvaluateBody_ConciseBody_ExpressionBody(F.ECMAScriptCode.body, F, argumentsList); + + case 'GeneratorBody': + return yield* EvaluateBody_GeneratorBody(F.ECMAScriptCode.body.body, F, argumentsList); + + case 'AsyncFunctionBody': + case 'AsyncConciseBody_AsyncFunctionBody': + return yield* EvaluateBody_AsyncFunctionBody(F.ECMAScriptCode.body.body, F, argumentsList); + + case 'AsyncConciseBody_ExpressionBody': + return yield* EvaluateBody_AsyncConciseBody_ExpressionBody(F.ECMAScriptCode.body, F, argumentsList); + + case 'AsyncGeneratorBody': + return yield* EvaluateBody_AsyncGeneratorBody(F.ECMAScriptCode.body.body, F, argumentsList); + + default: + throw new OutOfRange('OrdinaryCallEvaluateBody', F.ECMAScriptCode); + } +} + +// 9.2.1 #sec-ecmascript-function-objects-call-thisargument-argumentslist +function FunctionCallSlot(thisArgument, argumentsList) { + const F = this; + + Assert(isECMAScriptFunctionObject(F)); + if (F.IsClassConstructor === Value.true) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', F); + } + // const callerContext = surroundingAgent.runningExecutionContext; + const calleeContext = PrepareForOrdinaryCall(F, Value.undefined); + Assert(surroundingAgent.runningExecutionContext === calleeContext); + OrdinaryCallBindThis(F, calleeContext, thisArgument); + const result = EnsureCompletion(unwind(OrdinaryCallEvaluateBody(F, argumentsList))); + // Remove calleeContext from the execution context stack and + // restore callerContext as the running execution context. + surroundingAgent.executionContextStack.pop(calleeContext); + if (result.Type === 'return') { + return new NormalCompletion(result.Value); + } + ReturnIfAbrupt(result); + return new NormalCompletion(Value.undefined); +} + +// 9.2.2 #sec-ecmascript-function-objects-construct-argumentslist-newtarget +function FunctionConstructSlot(argumentsList, newTarget) { + const F = this; + + Assert(isECMAScriptFunctionObject(F)); + Assert(Type(newTarget) === 'Object'); + // const callerContext = surroundingAgent.runningExecutionContext; + const kind = F.ConstructorKind; + let thisArgument; + if (kind === 'base') { + thisArgument = Q(OrdinaryCreateFromConstructor(newTarget, '%Object.prototype%')); + } + const calleeContext = PrepareForOrdinaryCall(F, newTarget); + Assert(surroundingAgent.runningExecutionContext === calleeContext); + surroundingAgent.runningExecutionContext.callSite.constructCall = true; + if (kind === 'base') { + OrdinaryCallBindThis(F, calleeContext, thisArgument); + } + const constructorEnv = calleeContext.LexicalEnvironment; + const envRec = constructorEnv.EnvironmentRecord; + const result = EnsureCompletion(unwind(OrdinaryCallEvaluateBody(F, argumentsList))); + // Remove calleeContext from the execution context stack and + // restore callerContext as the running execution context. + surroundingAgent.executionContextStack.pop(calleeContext); + if (result.Type === 'return') { + if (Type(result.Value) === 'Object') { + return new NormalCompletion(result.Value); + } + if (kind === 'base') { + return new NormalCompletion(thisArgument); + } + if (Type(result.Value) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'DerivedConstructorReturnedNonObject'); + } + } else { + ReturnIfAbrupt(result); + } + return Q(envRec.GetThisBinding()); +} + +// 9.2.3 #sec-functionallocate +export function OrdinaryFunctionCreate(functionPrototype, ParameterList, Body, thisMode, Scope) { + Assert(Type(functionPrototype) === 'Object'); + const internalSlotsList = [ + 'Environment', + 'FormalParameters', + 'ECMAScriptCode', + 'ConstructorKind', + 'Realm', + 'ScriptOrModule', + 'ThisMode', + 'Strict', + 'HomeObject', + 'SourceText', + 'IsClassConstructor', + ]; + const F = X(OrdinaryObjectCreate(functionPrototype, internalSlotsList)); + F.Call = FunctionCallSlot; + F.Environment = Scope; + F.FormalParameters = ParameterList; + F.ECMAScriptCode = Body; + const Strict = isStrictModeCode(Body); + F.Strict = Strict; + if (thisMode === 'lexical-this') { + F.ThisMode = 'lexical'; + } else if (Strict) { + F.ThisMode = 'strict'; + } else { + F.ThisMode = 'global'; + } + F.IsClassConstructor = Value.false; + F.Environment = Scope; + F.ScriptOrModule = GetActiveScriptOrModule(); + F.Realm = surroundingAgent.currentRealmRecord; + F.HomeObject = Value.undefined; + const len = ExpectedArgumentCount(ParameterList); + X(SetFunctionLength(F, new Value(len))); + return F; +} + +// 9.2.10 #sec-makeconstructor +export function MakeConstructor(F, writablePrototype, prototype) { + Assert(isECMAScriptFunctionObject(F)); + Assert(IsConstructor(F) === Value.false); + Assert(X(IsExtensible(F)) === Value.true && X(HasOwnProperty(F, new Value('prototype'))) === Value.false); + F.Construct = FunctionConstructSlot; + F.ConstructorKind = 'base'; + if (writablePrototype === undefined) { + writablePrototype = true; + } + if (prototype === undefined) { + prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + X(DefinePropertyOrThrow(prototype, new Value('constructor'), Descriptor({ + Value: F, + Writable: writablePrototype ? Value.true : Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: writablePrototype ? Value.true : Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + return new NormalCompletion(Value.undefined); +} + +// 9.2.11 #sec-makeclassconstructor +export function MakeClassConstructor(F) { + Assert(isECMAScriptFunctionObject(F)); + Assert(F.IsClassConstructor === Value.false); + F.IsClassConstructor = Value.true; + return new NormalCompletion(Value.undefined); +} + +// 9.2.12 #sec-makemethod +export function MakeMethod(F, homeObject) { + Assert(isECMAScriptFunctionObject(F)); + Assert(Type(homeObject) === 'Object'); + F.HomeObject = homeObject; + return new NormalCompletion(Value.undefined); +} + +// 9.2.13 #sec-setfunctionname +export function SetFunctionName(F, name, prefix) { + Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('name')) === Value.false); + Assert(Type(name) === 'Symbol' || Type(name) === 'String'); + Assert(!prefix || Type(prefix) === 'String'); + if (Type(name) === 'Symbol') { + const description = name.Description; + if (Type(description) === 'Undefined') { + name = new Value(''); + } else { + name = new Value(`[${description.stringValue()}]`); + } + } + if (prefix !== undefined) { + name = new Value(`${prefix.stringValue()} ${name.stringValue()}`); + } + return X(DefinePropertyOrThrow(F, new Value('name'), Descriptor({ + Value: name, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); +} + +// 9.2.14 #sec-setfunctionlength +export function SetFunctionLength(F, length) { + Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('length')) === Value.false); + Assert(Type(length) === 'Number'); + Assert(length.numberValue() >= 0 && X(IsInteger(length)) === Value.true); + return X(DefinePropertyOrThrow(F, new Value('length'), Descriptor({ + Value: length, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); +} + + +function nativeCall(F, argumentsList, thisArgument, newTarget) { + return F.nativeFunction(argumentsList, { + thisValue: thisArgument || Value.undefined, + NewTarget: newTarget || Value.undefined, + }); +} + +function BuiltinFunctionCall(thisArgument, argumentsList) { + const F = this; + + // const callerContext = surroundingAgent.runningExecutionContext; + // If callerContext is not already suspended, suspend callerContext. + const calleeContext = new ExecutionContext(); + calleeContext.Function = F; + const calleeRealm = F.Realm; + calleeContext.Realm = calleeRealm; + calleeContext.ScriptOrModule = F.ScriptOrModule; + // 8. Perform any necessary implementation-defined initialization of calleeContext. + surroundingAgent.executionContextStack.push(calleeContext); + const result = nativeCall(F, argumentsList, thisArgument, Value.undefined); + // Remove calleeContext from the execution context stack and + // restore callerContext as the running execution context. + surroundingAgent.executionContextStack.pop(calleeContext); + return result; +} + +function BuiltinFunctionConstruct(argumentsList, newTarget) { + const F = this; + + // const callerContext = surroundingAgent.runningExecutionContext; + // If callerContext is not already suspended, suspend callerContext. + const calleeContext = new ExecutionContext(); + calleeContext.Function = F; + const calleeRealm = F.Realm; + calleeContext.Realm = calleeRealm; + calleeContext.ScriptOrModule = F.ScriptOrModule; + // 8. Perform any necessary implementation-defined initialization of calleeContext. + surroundingAgent.executionContextStack.push(calleeContext); + surroundingAgent.runningExecutionContext.callSite.constructCall = true; + const result = nativeCall(F, argumentsList, undefined, newTarget); + // Remove calleeContext from the execution context stack and + // restore callerContext as the running execution context. + surroundingAgent.executionContextStack.pop(calleeContext); + return result; +} + +// 9.3.3 #sec-createbuiltinfunction +export function CreateBuiltinFunction(steps, internalSlotsList, realm, prototype, isConstructor = Value.false) { + // 1. Assert: steps is either a set of algorithm steps or other definition of a function's behaviour provided in this specification. + Assert(typeof steps === 'function'); + // 2. If realm is not present, set realm to the current Realm Record. + if (realm === undefined) { + realm = surroundingAgent.currentRealmRecord; + } + // 3. Assert: realm is a Realm Record. + Assert(realm instanceof Realm); + // 4. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. + if (prototype === undefined) { + prototype = realm.Intrinsics['%Function.prototype%']; + } + // 5. Let func be a new built-in function object that when called performs the action described by steps. The new function object has internal slots whose names are the elements of internalSlotsList. + const func = X(MakeBasicObject(internalSlotsList)); + func.Call = BuiltinFunctionCall; + if (isConstructor === Value.true) { + func.Construct = BuiltinFunctionConstruct; + } + func.nativeFunction = steps; + // 6. Set func.[[Realm]] to realm. + func.Realm = realm; + // 7. Set func.[[Prototype]] to prototype. + func.Prototype = prototype; + // 8. Set func.[[Extensible]] to true. + func.Extensible = Value.true; + // 9. Set func.[[Extensible]] to true. + func.ScriptOrModule = Value.null; + // 10. Return func. + return func; +} + +// 14.9.3 #sec-preparefortailcall +export function PrepareForTailCall() { + // 1. Let leafContext be the running execution context. + const leafContext = surroundingAgent.runningExecutionContext; + // 2. Suspend leafContext. + // 3. Pop leafContext from the execution context stack. The execution context now on the top of the stack becomes the running execution context. + surroundingAgent.executionContextStack.pop(leafContext); + // 4. Assert: leafContext has no further use. It will never be activated as the running execution context. + leafContext.poppedForTailCall = true; +} diff --git a/src/engine262/src/abstract-ops/generator-operations.mjs b/src/engine262/src/abstract-ops/generator-operations.mjs new file mode 100644 index 0000000..38d73a0 --- /dev/null +++ b/src/engine262/src/abstract-ops/generator-operations.mjs @@ -0,0 +1,200 @@ +import { + Completion, + NormalCompletion, + Q, X, + EnsureCompletion, +} from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate_FunctionBody } from '../runtime-semantics/all.mjs'; +import { Value } from '../value.mjs'; +import { resume } from '../helpers.mjs'; +import { + Assert, + CreateIterResultObject, + RequireInternalSlot, +} from './all.mjs'; + +// This file covers abstract operations defined in #sec-generator-objects + +// #sec-generatorstart +export function GeneratorStart(generator, generatorBody) { + // 1. Assert: The value of generator.[[GeneratorState]] is undefined. + Assert(generator.GeneratorState === Value.undefined); + // 2. Let genContext be the running execution context. + const genContext = surroundingAgent.runningExecutionContext; + // 3. Set the Generator component of genContext to generator. + genContext.Generator = generator; + // 4. Set the code evaluation state of genContext such that when evaluation is resumed + // for that execution context the following steps will be performed: + genContext.codeEvaluationState = (function* resumer() { + // a. Let result be the result of evaluating generatorBody. + const result = EnsureCompletion(yield* Evaluate_FunctionBody(generatorBody)); + // b. Assert: If we return here, the generator either threw an exception or + // performed either an implicit or explicit return. + // c. Remove genContext from the execution context stack and restore the execution context + // that is at the top of the execution context stack as the running execution context. + surroundingAgent.executionContextStack.pop(genContext); + // d. Set generator.[[GeneratorState]] to completed. + generator.GeneratorState = 'completed'; + // e. Once a generator enters the completed state it never leaves it and its + // associated execution context is never resumed. Any execution state associated + // with generator can be discarded at this point. + genContext.codeEvaluationState = null; + // f. If result.[[Type]] is normal, let resultValue be undefined. + let resultValue; + if (result.Type === 'normal') { + resultValue = Value.undefined; + } else if (result.Type === 'return') { + // g. Else if result.[[Type]] is return, let resultValue be result.[[Value]]. + resultValue = result.Value; + } else { + // i. Assert: result.[[Type]] is throw. + Assert(result.Type === 'throw'); + // ii. Return Completion(result). + return Completion(result); + } + // i. Return CreateIterResultObject(resultValue, true). + return X(CreateIterResultObject(resultValue, Value.true)); + }()); + // 5. Set generator.[[GeneratorContext]] to genContext. + generator.GeneratorContext = genContext; + // 6. Set generator.[[GeneratorState]] to suspendedStart. + generator.GeneratorState = 'suspendedStart'; + // 7. Return NormalCompletion(undefined). + return new NormalCompletion(Value.undefined); +} + +// #sec-generatorvalidate +export function GeneratorValidate(generator) { + // 1. Perform ? RequireInternalSlot(generator, [[GeneratorState]]). + Q(RequireInternalSlot(generator, 'GeneratorState')); + // 2. Assert: generator also has a [[GeneratorContext]] internal slot. + Assert('GeneratorContext' in generator); + // 3. Let state be generator.[[GeneratorState]]. + const state = generator.GeneratorState; + // 4. If state is executing, throw a TypeError exception. + if (state === 'executing') { + return surroundingAgent.Throw('TypeError', 'GeneratorRunning'); + } + // 5. Return state. + return state; +} + +// #sec-generatorresume +export function GeneratorResume(generator, value) { + // 1. Let state be ? GeneratorValidate(generator). + const state = Q(GeneratorValidate(generator)); + // 2. If state is completed, return CreateIterResultObject(undefined, true). + if (state === 'completed') { + return X(CreateIterResultObject(Value.undefined, Value.true)); + } + // 3. Assert: state is either suspendedStart or suspendedYield. + Assert(state === 'suspendedStart' || state === 'suspendedYield'); + // 4. Let genContext be generator.[[GeneratorContext]]. + const genContext = generator.GeneratorContext; + // 5. Let methodContext be the running execution context. + // 6. Suspend methodContext. + const methodContext = surroundingAgent.runningExecutionContext; + // 7. Set generator.[[GeneratorState]] to executing. + generator.GeneratorState = 'executing'; + // 8. Push genContext onto the execution context stack. + surroundingAgent.executionContextStack.push(genContext); + // 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as + // the result of the operation that suspended it. Let result be the value returned by + // the resumed computation. + const result = resume(genContext, new NormalCompletion(value)); + // 10. Assert: When we return here, genContext has already been removed from the execution + // context stack and methodContext is the currently running execution context. + Assert(surroundingAgent.runningExecutionContext === methodContext); + // 11. Return Completion(result). + return Completion(result); +} + +// #sec-generatorresumeabrupt +export function GeneratorResumeAbrupt(generator, abruptCompletion) { + // 1. Let state be ? GeneratorValidate(generator). + let state = Q(GeneratorValidate(generator)); + // 2. If state is suspendedStart, then + if (state === 'suspendedStart') { + // a. Set generator.[[GeneratorState]] to completed. + generator.GeneratorState = 'completed'; + // b. Once a generator enters the completed state it never leaves it and its + // associated execution context is never resumed. Any execution state associate + // with generator can be discarded at this point. + generator.GeneratorContext = null; + // c. Set state to completed. + state = 'completed'; + } + // 3. If state is completed, then + if (state === 'completed') { + // a. If abruptCompletion.[[Type]] is return, then + if (abruptCompletion.Type === 'return') { + // i. Return CreateIterResultObject(abruptCompletion.[[Value]], true). + return X(CreateIterResultObject(abruptCompletion.Value, Value.true)); + } + // b. Return Completion(abruptCompletion). + return Completion(abruptCompletion); + } + // 4. Assert: state is suspendedYield. + Assert(state === 'suspendedYield'); + // 5. Let genContext be generator.[[GeneratorContext]]. + const genContext = generator.GeneratorContext; + // 6. Let methodContext be the running execution context. + // 7. Suspend methodContext. + const methodContext = surroundingAgent.runningExecutionContext; + // 8. Set generator.[[GeneratorState]] to executing. + generator.GeneratorState = 'executing'; + // 9. Push genContext onto the execution context stack. + surroundingAgent.executionContextStack.push(genContext); + // 10. Resume the suspended evaluation of genContext using abruptCompletion as the + // result of the operation that suspended it. Let result be the completion record + // returned by the resumed computation. + const result = resume(genContext, abruptCompletion); + // 11. Assert: When we return here, genContext has already been removed from the + // execution context stack and methodContext is the currently running execution context. + Assert(surroundingAgent.runningExecutionContext === methodContext); + // 12. Return Completion(result). + return Completion(result); +} + +// #sec-getgeneratorkind +export function GetGeneratorKind() { + // 1. Let genContext be the running execution context. + const genContext = surroundingAgent.runningExecutionContext; + // 2. If genContext does not have a Generator component, return non-generator. + if (!genContext.Generator) { + return 'non-generator'; + } + // 3. Let generator be the Generator component of genContext. + const generator = genContext.Generator; + // 4. If generator has an [[AsyncGeneratorState]] internal slot, return async. + if ('AsyncGeneratorState' in generator) { + return 'async'; + } + // 5. Else, return sync. + return 'sync'; +} + +// #sec-generatoryield +export function* GeneratorYield(iterNextObj) { + // 1. Assert: iterNextObj is an Object that implements the IteratorResult interface. + // 2. Let genContext be the running execution context. + const genContext = surroundingAgent.runningExecutionContext; + // 3. Assert: genContext is the execution context of a generator. + Assert(genContext.Generator !== undefined); + // 4. Let generator be the value of the Generator component of genContext. + const generator = genContext.Generator; + // 5. Assert: GetGeneratorKind is sync. + Assert(GetGeneratorKind() === 'sync'); + // 6. Set generator.GeneratorState to suspendedYield. + generator.GeneratorState = 'suspendedYield'; + // 7. Remove genContext from the execution context stack. + surroundingAgent.executionContextStack.pop(genContext); + // 8. Set the code evaluation state of genContext such that when evaluation is resumed with + // a Completion resumptionValue the following steps will be performed: + // a. Return resumptionValue + const resumptionValue = yield new NormalCompletion(iterNextObj); + // 9. Return NormalCompletion(iterNextObj). + return resumptionValue; + // 10. NOTE: this returns to the evaluation of the operation that had most previously resumed evaluation of genContext. +} diff --git a/src/engine262/src/abstract-ops/global-object.mjs b/src/engine262/src/abstract-ops/global-object.mjs new file mode 100644 index 0000000..e71c92f --- /dev/null +++ b/src/engine262/src/abstract-ops/global-object.mjs @@ -0,0 +1,243 @@ +import { ExecutionContext, HostEnsureCanCompileStrings, surroundingAgent } from '../engine.mjs'; +import { InstantiateFunctionObject } from '../runtime-semantics/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { + AbruptCompletion, Completion, + NormalCompletion, + Q, + X, +} from '../completion.mjs'; +import { ParseScript } from '../parse.mjs'; +import { + BoundNames_BindingIdentifier, + BoundNames_Declaration, + BoundNames_ForBinding, + BoundNames_FunctionDeclaration, + BoundNames_VariableDeclaration, + IsConstantDeclaration, + IsStrict, + LexicallyScopedDeclarations_ScriptBody, + VarDeclaredNames_ScriptBody, + VarScopedDeclarations_ScriptBody, +} from '../static-semantics/all.mjs'; +import { + isAsyncFunctionDeclaration, + isAsyncGeneratorDeclaration, + isBindingIdentifier, + isForBinding, + isFunctionDeclaration, + isGeneratorDeclaration, + isVariableDeclaration, +} from '../ast.mjs'; +import { Evaluate_Script } from '../evaluator.mjs'; +import { + NewDeclarativeEnvironment, + // FunctionEnvironmentRecord, + GlobalEnvironmentRecord, + ObjectEnvironmentRecord, +} from '../environment.mjs'; +import { + Assert, + // GetThisEnvironment, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 18 #sec-global-object + +// 18.2.1.1 #sec-performeval +export function PerformEval(x, callerRealm, strictCaller, direct) { + if (direct === false) { + Assert(strictCaller === false); + } + if (Type(x) !== 'String') { + return x; + } + const evalRealm = surroundingAgent.currentRealmRecord; + Q(HostEnsureCanCompileStrings(callerRealm, evalRealm)); + /* + const thisEnvRec = X(GetThisEnvironment()); + let inFunction; + let inMethod; + let inDerivedConstructor; + if (thisEnvRec instanceof FunctionEnvironmentRecord) { + const F = thisEnvRec.FunctionObject; + inFunction = true; + inMethod = thisEnvRec.HasSuperBinding() === Value.true; + if (F.ConstructorKind === 'derived') { + inDerivedConstructor = true; + } else { + inDerivedConstructor = false; + } + } else { + inFunction = false; + inMethod = false; + inDerivedConstructor = false; + } + */ + const r = ParseScript(x.stringValue(), evalRealm, undefined, strictCaller); + if (Array.isArray(r)) { + return surroundingAgent.Throw(r[0]); + } + const script = r.ECMAScriptCode; + // If script Contains ScriptBody is false, return undefined. + const body = script.body; + let strictEval; + if (strictCaller === true) { + strictEval = true; + } else { + strictEval = IsStrict(script); + } + const runningContext = surroundingAgent.runningExecutionContext; + let lexEnv; + let varEnv; + if (direct === true) { + lexEnv = NewDeclarativeEnvironment(runningContext.LexicalEnvironment); + varEnv = runningContext.VariableEnvironment; + } else { + lexEnv = NewDeclarativeEnvironment(evalRealm.GlobalEnv); + varEnv = evalRealm.GlobalEnv; + } + if (strictEval === true) { + varEnv = lexEnv; + } + // If runningContext is not already suspended, suspend runningContext. + const evalContext = new ExecutionContext(); + evalContext.Function = Value.null; + evalContext.Realm = evalRealm; + evalContext.ScriptOrModule = runningContext.ScriptOrModule; + evalContext.VariableEnvironment = varEnv; + evalContext.LexicalEnvironment = lexEnv; + surroundingAgent.executionContextStack.push(evalContext); + let result = EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval); + if (result.Type === 'normal') { + result = Evaluate_Script(body); + } + if (result.Type === 'normal' && result.Value === undefined) { + result = new NormalCompletion(Value.undefined); + } + surroundingAgent.executionContextStack.pop(evalContext); + // Resume the context that is now on the top of the execution context stack as the running execution context. + return Completion(result); +} + +// 18.2.1.3 #sec-evaldeclarationinstantiation +function EvalDeclarationInstantiation(body, varEnv, lexEnv, strict) { + const varNames = VarDeclaredNames_ScriptBody(body).map(Value); + const varDeclarations = VarScopedDeclarations_ScriptBody(body); + const lexEnvRec = lexEnv.EnvironmentRecord; + const varEnvRec = varEnv.EnvironmentRecord; + if (strict === false) { + if (varEnvRec instanceof GlobalEnvironmentRecord) { + for (const name of varNames) { + if (varEnvRec.HasLexicalDeclaration(name) === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + } + // NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration. + } + } + let thisLex = lexEnv; + // Assert: The following loop will terminate. + while (thisLex !== varEnv) { + const thisEnvRec = thisLex.EnvironmentRecord; + if (!(thisEnvRec instanceof ObjectEnvironmentRecord)) { + for (const name of varNames) { + if (thisEnvRec.HasBinding(name) === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + // NOTE: Annex B.3.5 defines alternate semantics for the above step. + } + // NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration + } + } + thisLex = thisLex.outerEnvironmentReference; + } + } + const functionsToInitialize = []; + const declaredFunctionNames = []; + for (const d of [...varDeclarations].reverse()) { + if (!isVariableDeclaration(d) && !isForBinding(d) && !isBindingIdentifier(d)) { + Assert(isFunctionDeclaration(d) || isGeneratorDeclaration(d) + || isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d)); + const fn = new Value(BoundNames_FunctionDeclaration(d)[0]); + if (!declaredFunctionNames.includes(fn)) { + if (varEnvRec instanceof GlobalEnvironmentRecord) { + const fnDefinable = Q(varEnvRec.CanDeclareGlobalFunction(fn)); + if (fnDefinable === Value.false) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', fn); + } + } + declaredFunctionNames.push(fn); + functionsToInitialize.unshift(d); + } + } + } + // NOTE: Annex B.3.3.3 adds additional steps at this point. + const declaredVarNames = []; + for (const d of varDeclarations) { + let boundNames; + if (isVariableDeclaration(d)) { + boundNames = BoundNames_VariableDeclaration(d); + } else if (isForBinding(d)) { + boundNames = BoundNames_ForBinding(d); + } else if (isBindingIdentifier(d)) { + boundNames = BoundNames_BindingIdentifier(d); + } + if (boundNames !== undefined) { + for (const vn of boundNames.map(Value)) { + if (!declaredFunctionNames.includes(vn)) { + if (varEnvRec instanceof GlobalEnvironmentRecord) { + const vnDefinable = Q(varEnvRec.CanDeclareGlobalVar(vn)); + if (vnDefinable === Value.false) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', vn); + } + } + if (!declaredVarNames.includes(vn)) { + declaredVarNames.push(vn); + } + } + } + } + } + // NOTE: No abnormal terminations occur after this algorithm step unless + // varEnvRec is a global Environment Record and the global object is a Proxy exotic object. + const lexDeclarations = LexicallyScopedDeclarations_ScriptBody(body); + for (const d of lexDeclarations) { + for (const dn of BoundNames_Declaration(d).map(Value)) { + if (IsConstantDeclaration(d)) { + Q(lexEnvRec.CreateImmutableBinding(dn, Value.true)); + } else { + Q(lexEnvRec.CreateMutableBinding(dn, Value.false)); + } + } + } + for (const f of functionsToInitialize) { + const fn = new Value(BoundNames_FunctionDeclaration(f)[0]); + const fo = InstantiateFunctionObject(f, lexEnv); + if (varEnvRec instanceof GlobalEnvironmentRecord) { + Q(varEnvRec.CreateGlobalFunctionBinding(fn, fo, Value.true)); + } else { + const bindingExists = varEnvRec.HasBinding(fn); + if (bindingExists === Value.false) { + const status = X(varEnvRec.CreateMutableBinding(fn, Value.true)); + Assert(!(status instanceof AbruptCompletion)); + X(varEnvRec.InitializeBinding(fn, fo)); + } else { + X(varEnvRec.SetMutableBinding(fn, fo, Value.false)); + } + } + } + for (const vn of declaredVarNames) { + if (!declaredFunctionNames.includes(vn)) { + if (varEnvRec instanceof GlobalEnvironmentRecord) { + Q(varEnvRec.CreateGlobalVarBinding(vn, Value.true)); + } else { + const bindingExists = varEnvRec.HasBinding(vn); + if (bindingExists === Value.false) { + const status = X(varEnvRec.CreateMutableBinding(vn, Value.true)); + Assert(!(status instanceof AbruptCompletion)); + X(varEnvRec.InitializeBinding(vn, Value.undefined)); + } + } + } + } + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/abstract-ops/immutable-prototype-objects.mjs b/src/engine262/src/abstract-ops/immutable-prototype-objects.mjs new file mode 100644 index 0000000..f453265 --- /dev/null +++ b/src/engine262/src/abstract-ops/immutable-prototype-objects.mjs @@ -0,0 +1,17 @@ +import { Type, Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { Assert, SameValue } from './all.mjs'; + +// #sec-set-immutable-prototype +export function SetImmutablePrototype(O, V) { + // 1. Assert: Either Type(V) is Object or Type(V) is Null. + Assert(Type(V) === 'Object' || Type(V) === 'Null'); + // 2. Let current be ? O.[[GetPrototypeOf]](). + const current = Q(O.GetPrototypeOf()); + // 3. If SameValue(V, current) is true, return true. + if (SameValue(V, current) === Value.true) { + return Value.true; + } + // 4. Return false. + return Value.false; +} diff --git a/src/engine262/src/abstract-ops/integer-indexed-objects.mjs b/src/engine262/src/abstract-ops/integer-indexed-objects.mjs new file mode 100644 index 0000000..b8c1743 --- /dev/null +++ b/src/engine262/src/abstract-ops/integer-indexed-objects.mjs @@ -0,0 +1,314 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value, Type, Descriptor } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { + Assert, + CanonicalNumericIndexString, + IsAccessorDescriptor, + IsDetachedBuffer, + IsPropertyKey, + IsValidIntegerIndex, + MakeBasicObject, + OrdinaryGetOwnProperty, + OrdinaryHasProperty, + OrdinaryDefineOwnProperty, + OrdinaryGet, + OrdinarySet, + GetValueFromBuffer, + SetValueInBuffer, + ToString, + ToNumber, + ToBigInt, + isIntegerIndex, + typedArrayInfoByName, +} from './all.mjs'; + +export function isIntegerIndexedExoticObject(O) { + return O.GetOwnProperty === IntegerIndexedGetOwnProperty; +} + +// 9.4.5.1 #sec-integer-indexed-exotic-objects-getownproperty-p +export function IntegerIndexedGetOwnProperty(P) { + const O = this; + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 3. If Type(P) is String, then + if (Type(P) === 'String') { + // a. Let numericIndex be ! CanonicalNumericIndexString(P). + const numericIndex = X(CanonicalNumericIndexString(P)); + // b. If numericIndex is not undefined, then + if (numericIndex !== Value.undefined) { + // i. Let value be ? IntegerIndexedElementGet(O, numericIndex). + const value = Q(IntegerIndexedElementGet(O, numericIndex)); + // ii. If value is undefined, return undefined. + if (value === Value.undefined) { + return Value.undefined; + } + // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }. + return Descriptor({ + Value: value, + Writable: Value.true, + Enumerable: Value.true, + Configurable: Value.false, + }); + } + } + // 4. Return OrdinaryGetOwnProperty(O, P). + return OrdinaryGetOwnProperty(O, P); +} + +// 9.4.5.2 #sec-integer-indexed-exotic-objects-hasproperty-p +export function IntegerIndexedHasProperty(P) { + const O = this; + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 3. If Type(P) is String, then + if (Type(P) === 'String') { + // a. Let numericIndex be ! CanonicalNumericIndexString(P). + const numericIndex = X(CanonicalNumericIndexString(P)); + // b. If numericIndex is not undefined, then + if (numericIndex !== Value.undefined) { + // i. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // iii. If ! IsValidIntegerIndex(O, numericIndex) is false, return false. + if (IsValidIntegerIndex(O, numericIndex) === Value.false) { + return Value.false; + } + // iv. Return true. + return Value.true; + } + } + // 4. Return ? OrdinaryHasProperty(O, P) + return Q(OrdinaryHasProperty(O, P)); +} + +// #sec-integer-indexed-exotic-objects-defineownproperty-p-desc +export function IntegerIndexedDefineOwnProperty(P, Desc) { + const O = this; + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 3. If Type(P) is String, then + if (Type(P) === 'String') { + // a. Let numericIndex be ! CanonicalNumericIndexString(P). + const numericIndex = X(CanonicalNumericIndexString(P)); + // b. If numericIndex is not undefined, then + if (numericIndex !== Value.undefined) { + // i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false. + if (IsValidIntegerIndex(O, numericIndex) === Value.false) { + return Value.false; + } + // ii. If IsAccessorDescriptor(Desc) is true, return false. + if (IsAccessorDescriptor(Desc)) { + return Value.false; + } + // iii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false. + if (Desc.Configurable === Value.true) { + return Value.false; + } + // iv. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false. + if (Desc.Enumerable === Value.false) { + return Value.false; + } + // v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false. + if (Desc.Writable === Value.false) { + return Value.false; + } + // vi. If Desc has a [[Value]] field, then + if (Desc.Value !== undefined) { + // 1. Let value be Desc.[[Value]]. + const value = Desc.Value; + // 2. Return ? IntegerIndexedElementSet(O, numericIndex, value). + return Q(IntegerIndexedElementSet(O, numericIndex, value)); + } + // vii. Return true. + return Value.true; + } + } + // 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc). + return Q(OrdinaryDefineOwnProperty(O, P, Desc)); +} + +// 9.4.5.4 #sec-integer-indexed-exotic-objects-get-p-receiver +export function IntegerIndexedGet(P, Receiver) { + const O = this; + // 1. Assert: IsPropertykey(P) is true. + Assert(IsPropertyKey(P)); + // 2. If Type(P) is String, then + if (Type(P) === 'String') { + // a. Let numericIndex be ! CanonicalNumericIndexString(P). + const numericIndex = X(CanonicalNumericIndexString(P)); + // b. If numericIndex is not undefined, then + if (numericIndex !== Value.undefined) { + // i. Return ? IntegerIndexedElementGet(O, numericIndex). + return Q(IntegerIndexedElementGet(O, numericIndex)); + } + } + // 3. Return ? OrdinaryGet(O, P, Receiver). + return Q(OrdinaryGet(O, P, Receiver)); +} + +// 9.4.5.5 #sec-integer-indexed-exotic-objects-set-p-v-receiver +export function IntegerIndexedSet(P, V, Receiver) { + const O = this; + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. If Type(P) is String, then + if (Type(P) === 'String') { + // a. Let numericIndex be ! CanonicalNumericIndexString(P). + const numericIndex = X(CanonicalNumericIndexString(P)); + // b. If numericIndex is not undefined, then + if (numericIndex !== Value.undefined) { + // i. Return ? IntegerIndexedElementSet(O, numericIndex, V). + return Q(IntegerIndexedElementSet(O, numericIndex, V)); + } + } + // 3. Return ? OrdinarySet(O, P, V, Receiver). + return Q(OrdinarySet(O, P, V, Receiver)); +} + +// 9.4.5.6 #sec-integer-indexed-exotic-objects-ownpropertykeys +export function IntegerIndexedOwnPropertyKeys() { + const O = this; + // 1. Let keys be a new empty List. + const keys = []; + // 2. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 3. Let len be O.[[ArrayLength]]. + const len = O.ArrayLength.numberValue(); + // 4. For each integer i starting with 0 such that i < len, in ascending order, do + for (let i = 0; i < len; i += 1) { + // a. Add ! ToString(i) as the last element of keys. + keys.push(X(ToString(new Value(i)))); + } + // 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in ascending chronological order of property creation, do + for (const P of O.properties.keys()) { + if (Type(P) === 'String') { + if (!isIntegerIndex(P)) { + // a. Add P as the last element of keys. + keys.push(P); + } + } + } + // 6. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do + for (const P of O.properties.keys()) { + if (Type(P) === 'Symbol') { + // a. Add P as the last element of keys. + keys.push(P); + } + } + // 7. Return keys. + return keys; +} + +// #sec-integerindexedelementget +export function IntegerIndexedElementGet(O, index) { + // 1. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 2. Assert: Type(index) is Number. + Assert(Type(index) === 'Number'); + // 3. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. If ! IsValidIntegerIndex(O, index) is false, return undefined. + if (IsValidIntegerIndex(O, index) === Value.false) { + return Value.undefined; + } + // 6. Let offset be O.[[ByteOffset]]. + const offset = O.ByteOffset; + // 7. Let arrayTypeName be the String value of O.[[TypedArrayName]]. + const arrayTypeName = O.TypedArrayName.stringValue(); + // 8. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName. + const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize; + // 9. Let indexedPosition be (index × elementSize) + offset. + const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue()); + // 10. Let elementType be the Element Type value in Table 61 for arrayTypeName. + const elementType = typedArrayInfoByName[arrayTypeName].ElementType; + // 11. Return GetValueFromBuffer(buffer, indexedPosition, elementType, true, Unordered). + return GetValueFromBuffer(buffer, indexedPosition, elementType, Value.true, 'Unordered'); +} + +// #sec-integerindexedelementset +export function IntegerIndexedElementSet(O, index, value) { + // 1. Assert: O is an Integer-Indexed exotic object. + Assert(isIntegerIndexedExoticObject(O)); + // 2. Assert: Type(index) is Number. + Assert(Type(index) === 'Number'); + // 3. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value). + // 4. Otherwise, let numValue be ? ToNumber(value). + let numValue; + if (O.ContentType === 'BigInt') { + numValue = Q(ToBigInt(value)); + } else { + numValue = Q(ToNumber(value)); + } + // 5. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 7. If ! IsValidIntegerIndex(O, index) is false, return false. + if (IsValidIntegerIndex(O, index) === Value.false) { + return Value.false; + } + // 8. Let offset be O.[[ByteOffset]]. + const offset = O.ByteOffset; + // 9. Let arrayTypeName be the String value of O.[[TypedArrayName]]. + const arrayTypeName = O.TypedArrayName.stringValue(); + // 10. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName. + const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize; + // 11. Let indexedPosition be (index × elementSize) + offset. + const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue()); + // 12. Let elementType be the Element Type value in Table 61 for arrayTypeName. + const elementType = typedArrayInfoByName[arrayTypeName].ElementType; + // 13. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue, true, Unordered). + X(SetValueInBuffer(buffer, indexedPosition, elementType, numValue, Value.true, 'Unordered')); + // 14. Return true. + return Value.true; +} + +// #sec-integerindexedobjectcreate +export function IntegerIndexedObjectCreate(prototype) { + // 1. Let internalSlotsList be « [[Prototype]], [[Extensible]], [[ViewedArrayBuffer]], [[TypedArrayName]], [[ContentType]], [[ByteLength]], [[ByteOffset]], [[ArrayLength]] ». + const internalSlotsList = [ + 'Prototype', + 'Extensible', + 'ViewedArrayBuffer', + 'TypedArrayName', + 'ContentType', + 'ByteLength', + 'ByteOffset', + 'ArrayLength', + ]; + // 2. Let A be ! MakeBasicObject(internalSlotsList). + const A = X(MakeBasicObject(internalSlotsList)); + // 3. Set A.[[GetOwnProperty]] as specified in 9.4.5.1. + A.GetOwnProperty = IntegerIndexedGetOwnProperty; + // 4. Set A.[[HasProperty]] as specified in 9.4.5.2. + A.HasProperty = IntegerIndexedHasProperty; + // 5. Set A.[[DefineOwnProperty]] as specified in 9.4.5.3. + A.DefineOwnProperty = IntegerIndexedDefineOwnProperty; + // 6. Set A.[[Get]] as specified in 9.4.5.4. + A.Get = IntegerIndexedGet; + // 7. Set A.[[Set]] as specified in 9.4.5.5. + A.Set = IntegerIndexedSet; + // 8. Set A.[[OwnPropertyKeys]] as specified in 9.4.5.6. + A.OwnPropertyKeys = IntegerIndexedOwnPropertyKeys; + // 9. Set A.[[Prototype]] to prototype. + A.Prototype = prototype; + // 10. Return A. + return A; +} diff --git a/src/engine262/src/abstract-ops/iterator-operations.mjs b/src/engine262/src/abstract-ops/iterator-operations.mjs new file mode 100644 index 0000000..f4a17cb --- /dev/null +++ b/src/engine262/src/abstract-ops/iterator-operations.mjs @@ -0,0 +1,224 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + Completion, + EnsureCompletion, + IfAbruptRejectPromise, + Q, X, + Await, +} from '../completion.mjs'; +import { + Assert, + Call, + CreateBuiltinFunction, + CreateDataProperty, + Get, + GetMethod, + GetV, + PromiseResolve, + OrdinaryObjectCreate, + PerformPromiseThen, + ToBoolean, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 7.4 #sec-operations-on-iterator-objects +// and +// 25.1 #sec-iteration + +// 7.4.1 #sec-getiterator +export function GetIterator(obj, hint, method) { + if (!hint) { + hint = 'sync'; + } + Assert(hint === 'sync' || hint === 'async'); + if (!method) { + if (hint === 'async') { + method = Q(GetMethod(obj, wellKnownSymbols.asyncIterator)); + if (method === Value.undefined) { + const syncMethod = Q(GetMethod(obj, wellKnownSymbols.iterator)); + const syncIteratorRecord = Q(GetIterator(obj, 'sync', syncMethod)); + return Q(CreateAsyncFromSyncIterator(syncIteratorRecord)); + } + } else { + method = Q(GetMethod(obj, wellKnownSymbols.iterator)); + } + } + const iterator = Q(Call(method, obj)); + if (Type(iterator) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', iterator); + } + const nextMethod = Q(GetV(iterator, new Value('next'))); + const iteratorRecord = { + Iterator: iterator, + NextMethod: nextMethod, + Done: Value.false, + }; + return EnsureCompletion(iteratorRecord); +} + +// 7.4.2 #sec-iteratornext +export function IteratorNext(iteratorRecord, value) { + let result; + if (!value) { + result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator)); + } else { + result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator, [value])); + } + if (Type(result) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', result); + } + return EnsureCompletion(result); +} + +// 7.4.3 #sec-iteratorcomplete +export function IteratorComplete(iterResult) { + Assert(Type(iterResult) === 'Object'); + return EnsureCompletion(ToBoolean(Q(Get(iterResult, new Value('done'))))); +} + +// 7.4.4 #sec-iteratorvalue +export function IteratorValue(iterResult) { + Assert(Type(iterResult) === 'Object'); + return EnsureCompletion(Q(Get(iterResult, new Value('value')))); +} + +// 7.4.5 #sec-iteratorstep +export function IteratorStep(iteratorRecord) { + const result = Q(IteratorNext(iteratorRecord)); + const done = Q(IteratorComplete(result)); + if (done === Value.true) { + return EnsureCompletion(Value.false); + } + return EnsureCompletion(result); +} + +// 7.4.6 #sec-iteratorclose +export function IteratorClose(iteratorRecord, completion) { + // TODO: completion should be a Completion Record so this should not be necessary + completion = EnsureCompletion(completion); + Assert(Type(iteratorRecord.Iterator) === 'Object'); + Assert(completion instanceof Completion); + const iterator = iteratorRecord.Iterator; + const ret = Q(GetMethod(iterator, new Value('return'))); + if (ret === Value.undefined) { + return Completion(completion); + } + const innerResult = EnsureCompletion(Call(ret, iterator)); + if (completion.Type === 'throw') { + return Completion(completion); + } + if (innerResult.Type === 'throw') { + return Completion(innerResult); + } + if (Type(innerResult.Value) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value); + } + return Completion(completion); +} + +// 7.4.7 #sec-asynciteratorclose +export function* AsyncIteratorClose(iteratorRecord, completion) { + Assert(Type(iteratorRecord.Iterator) === 'Object'); + Assert(completion instanceof Completion); + const iterator = iteratorRecord.Iterator; + const ret = Q(GetMethod(iterator, new Value('return'))); + if (ret === Value.undefined) { + return Completion(completion); + } + let innerResult = EnsureCompletion(Call(ret, iterator)); + if (innerResult.Type === 'normal') { + innerResult = EnsureCompletion(yield* Await(innerResult.Value)); + } + if (completion.Type === 'throw') { + return Completion(completion); + } + if (innerResult.Type === 'throw') { + return Completion(innerResult); + } + if (Type(innerResult.Value) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value); + } + return Completion(completion); +} + +// 7.4.8 #sec-createiterresultobject +export function CreateIterResultObject(value, done) { + Assert(Type(done) === 'Boolean'); + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + X(CreateDataProperty(obj, new Value('value'), value)); + X(CreateDataProperty(obj, new Value('done'), done)); + return obj; +} + +// 7.4.9 #sec-createlistiteratorRecord +export function CreateListIteratorRecord(list) { + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%IteratorPrototype%'), [ + 'IteratedList', + 'ListNextIndex', + ]); + iterator.IteratedList = list; + iterator.ListNextIndex = 0; + const steps = ListIteratorNextSteps; + const next = X(CreateBuiltinFunction(steps, [])); + return { + Iterator: iterator, + NextMethod: next, + Done: Value.false, + }; +} + +// 7.4.9.1 #sec-listiterator-next +function ListIteratorNextSteps(args, { thisValue }) { + const O = thisValue; + Assert(Type(O) === 'Object'); + Assert('IteratedList' in O); + const list = O.IteratedList; + const index = O.ListNextIndex; + const len = list.length; + if (index >= len) { + return CreateIterResultObject(Value.undefined, Value.true); + } + O.ListNextIndex += 1; + return CreateIterResultObject(list[index], Value.false); +} + +// 25.1.4.1 #sec-createasyncfromsynciterator +export function CreateAsyncFromSyncIterator(syncIteratorRecord) { + const asyncIterator = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncFromSyncIteratorPrototype%'), [ + 'SyncIteratorRecord', + ])); + asyncIterator.SyncIteratorRecord = syncIteratorRecord; + const nextMethod = X(Get(asyncIterator, new Value('next'))); + return { + Iterator: asyncIterator, + NextMethod: nextMethod, + Done: Value.false, + }; +} + +// 25.1.4.2.4 #sec-async-from-sync-iterator-value-unwrap-functions +function AsyncFromSyncIteratorValueUnwrapFunctions([value = Value.undefined]) { + const F = this; + + return X(CreateIterResultObject(value, F.Done)); +} + +// 25.1.4.4 #sec-asyncfromsynciteratorcontinuation +export function AsyncFromSyncIteratorContinuation(result, promiseCapability) { + const done = IteratorComplete(result); + IfAbruptRejectPromise(done, promiseCapability); + const value = IteratorValue(result); + IfAbruptRejectPromise(value, promiseCapability); + const valueWrapper = PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value); + IfAbruptRejectPromise(valueWrapper, promiseCapability); + const steps = AsyncFromSyncIteratorValueUnwrapFunctions; + const onFulfilled = X(CreateBuiltinFunction(steps, ['Done'])); + onFulfilled.Done = done; + X(PerformPromiseThen(valueWrapper, onFulfilled, Value.undefined, promiseCapability)); + return promiseCapability.Promise; +} diff --git a/src/engine262/src/abstract-ops/module-namespace-exotic-objects.mjs b/src/engine262/src/abstract-ops/module-namespace-exotic-objects.mjs new file mode 100644 index 0000000..19ae7e1 --- /dev/null +++ b/src/engine262/src/abstract-ops/module-namespace-exotic-objects.mjs @@ -0,0 +1,199 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Q, X } from '../completion.mjs'; +import { AbstractModuleRecord, ResolvedBindingRecord } from '../modules.mjs'; +import { + Type, + Value, + Descriptor, + wellKnownSymbols, +} from '../value.mjs'; +import { ValueSet } from '../helpers.mjs'; +import { + Assert, + SortCompare, + SameValue, + MakeBasicObject, + IsPropertyKey, + IsAccessorDescriptor, + SetImmutablePrototype, + OrdinaryGetOwnProperty, + OrdinaryDefineOwnProperty, + OrdinaryHasProperty, + OrdinaryGet, + OrdinaryDelete, + OrdinaryOwnPropertyKeys, +} from './all.mjs'; + + +function ModuleNamespaceSetPrototypeOf(V) { + const O = this; + + return Q(SetImmutablePrototype(O, V)); +} + +function ModuleNamespaceIsExtensible() { + return Value.false; +} + +function ModuleNamespacePreventExtensions() { + return Value.true; +} + +function ModuleNamespaceGetOwnProperty(P) { + const O = this; + + if (Type(P) === 'Symbol') { + return OrdinaryGetOwnProperty(O, P); + } + const exports = O.Exports; + if (!exports.has(P)) { + return Value.undefined; + } + const value = Q(O.Get(P, O)); + return Descriptor({ + Value: value, + Writable: Value.true, + Enumerable: Value.true, + Configurable: Value.false, + }); +} + +function ModuleNamespaceDefineOwnProperty(P, Desc) { + const O = this; + + if (Type(P) === 'Symbol') { + return OrdinaryDefineOwnProperty(O, P, Desc); + } + + const current = Q(O.GetOwnProperty(P)); + if (current === Value.undefined) { + return Value.false; + } + if (IsAccessorDescriptor(Desc)) { + return Value.false; + } + if (Desc.Writable !== undefined && Desc.Writable === Value.false) { + return Value.false; + } + if (Desc.Enumerable !== undefined && Desc.Enumerable === Value.false) { + return Value.false; + } + if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) { + return Value.false; + } + if (Desc.Value !== undefined) { + return SameValue(Desc.Value, current.Value); + } + return Value.true; +} + +function ModuleNamespaceHasProperty(P) { + const O = this; + + if (Type(P) === 'Symbol') { + return OrdinaryHasProperty(O, P); + } + const exports = O.Exports; + if (exports.has(P)) { + return Value.true; + } + return Value.false; +} + +function ModuleNamespaceGet(P, Receiver) { + const O = this; + + Assert(IsPropertyKey(P)); + if (Type(P) === 'Symbol') { + return OrdinaryGet(O, P, Receiver); + } + const exports = O.Exports; + if (!exports.has(P)) { + return Value.undefined; + } + const m = O.Module; + const binding = m.ResolveExport(P); + Assert(binding instanceof ResolvedBindingRecord); + const targetModule = binding.Module; + Assert(targetModule !== Value.undefined); + const targetEnv = targetModule.Environment; + if (targetEnv === Value.undefined) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', P); + } + const targetEnvRec = targetEnv.EnvironmentRecord; + return Q(targetEnvRec.GetBindingValue(binding.BindingName, Value.true)); +} + +function ModuleNamespaceSet() { + return Value.false; +} + +function ModuleNamespaceDelete(P) { + const O = this; + + Assert(IsPropertyKey(P)); + if (Type(P) === 'Symbol') { + return Q(OrdinaryDelete(O, P)); + } + const exports = O.Exports; + if (exports.has(P)) { + return Value.false; + } + return Value.true; +} + +function ModuleNamespaceOwnPropertyKeys() { + const O = this; + + const exports = [...O.Exports]; + const symbolKeys = X(OrdinaryOwnPropertyKeys(O)); + exports.push(...symbolKeys); + return exports; +} + +// 9.4.6.11 #sec-modulenamespacecreate +export function ModuleNamespaceCreate(module, exports) { + // 1. Assert: module is a Module Record. + Assert(module instanceof AbstractModuleRecord); + // 2. Assert: module.[[Namespace]] is undefined. + Assert(module.Namespace === Value.undefined); + // 3. Assert: exports is a List of String values. + Assert(Array.isArray(exports)); + // 4. Let internalSlotsList be the internal slots listed in Table 31. + const internalSlotsList = ['Module', 'Exports', 'Prototype']; + // 5. Let M be ! MakeBasicObject(internalSlotsList). + const M = X(MakeBasicObject(internalSlotsList)); + // 6. Set M's essential internal methods to the definitions specified in #sec-module-namespace-exotic-objects + M.SetPrototypeOf = ModuleNamespaceSetPrototypeOf; + M.IsExtensible = ModuleNamespaceIsExtensible; + M.PreventExtensions = ModuleNamespacePreventExtensions; + M.GetOwnProperty = ModuleNamespaceGetOwnProperty; + M.DefineOwnProperty = ModuleNamespaceDefineOwnProperty; + M.HasProperty = ModuleNamespaceHasProperty; + M.Get = ModuleNamespaceGet; + M.Set = ModuleNamespaceSet; + M.Delete = ModuleNamespaceDelete; + M.OwnPropertyKeys = ModuleNamespaceOwnPropertyKeys; + // 7. Set M.[[Prototype]] to null. + M.Prototype = Value.null; + // 8. Set M.[[Module]] to module. + M.Module = module; + // 9. Let sortedExports be a new List containing the same values as the list exports where the values are ordered as if an Array of the same values had been sorted using Array.prototype.sort using undefined as comparefn. + const sortedExports = [...exports].sort((x, y) => { + const result = X(SortCompare(x, y, Value.undefined)); + return result.numberValue(); + }); + // 10. Set M.[[Exports]] to sortedExports. + M.Exports = new ValueSet(sortedExports); + // 11. Create own properties of M corresponding to the definitions in 26.3. + M.properties.set(wellKnownSymbols.toStringTag, Descriptor({ + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + Value: new Value('Module'), + })); + // 12. Set module.[[Namespace]] to M. + module.Namespace = M; + // 13. Return M; + return M; +} diff --git a/src/engine262/src/abstract-ops/module-records.mjs b/src/engine262/src/abstract-ops/module-records.mjs new file mode 100644 index 0000000..310d76d --- /dev/null +++ b/src/engine262/src/abstract-ops/module-records.mjs @@ -0,0 +1,263 @@ +import { surroundingAgent, HostResolveImportedModule } from '../engine.mjs'; +import { + AbstractModuleRecord, + CyclicModuleRecord, + ResolvedBindingRecord, +} from '../modules.mjs'; +import { Value } from '../value.mjs'; +import { + Q, X, NormalCompletion, ThrowCompletion, +} from '../completion.mjs'; +import { + Assert, + ModuleNamespaceCreate, + NewPromiseCapability, + PerformPromiseThen, + CreateBuiltinFunction, + Call, +} from './all.mjs'; + +// 15.2.1.16.1.1 #sec-InnerModuleLinking +export function InnerModuleLinking(module, stack, index) { + if (!(module instanceof CyclicModuleRecord)) { + Q(module.Link()); + return index; + } + if (module.Status === 'linking' || module.Status === 'linked' || module.Status === 'evaluated') { + return index; + } + Assert(module.Status === 'unlinked'); + module.Status = 'linking'; + module.DFSIndex = index; + module.DFSAncestorIndex = index; + index += 1; + stack.push(module); + for (const required of module.RequestedModules) { + const requiredModule = Q(HostResolveImportedModule(module, required)); + index = Q(InnerModuleLinking(requiredModule, stack, index)); + if (requiredModule instanceof CyclicModuleRecord) { + Assert(requiredModule.Status === 'linking' || requiredModule.Status === 'linked' || requiredModule.Status === 'evaluated'); + Assert((requiredModule.Status === 'linking') === stack.includes(requiredModule)); + if (requiredModule.Status === 'linking') { + module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex); + } + } + } + Q(module.InitializeEnvironment()); + Assert(stack.indexOf(module) === stack.lastIndexOf(module)); + Assert(module.DFSAncestorIndex <= module.DFSIndex); + if (module.DFSAncestorIndex === module.DFSIndex) { + let done = false; + while (done === false) { + const requiredModule = stack.pop(); + Assert(requiredModule instanceof CyclicModuleRecord); + requiredModule.Status = 'linked'; + if (requiredModule === module) { + done = true; + } + } + } + return index; +} + +// 15.2.1.16.2.1 #sec-innermoduleevaluation +export function InnerModuleEvaluation(module, stack, index) { + if (!(module instanceof CyclicModuleRecord)) { + Q(module.Evaluate()); + return index; + } + if (module.Status === 'evaluated') { + if (module.EvaluationError === Value.undefined) { + return index; + } else { + return module.EvaluationError; + } + } + if (module.Status === 'evaluating') { + return index; + } + Assert(module.Status === 'linked'); + module.Status = 'evaluating'; + module.DFSIndex = index; + module.DFSAncestorIndex = index; + module.PendingAsyncDependencies = 0; + module.AsyncParentModules = []; + index += 1; + stack.push(module); + for (const required of module.RequestedModules) { + let requiredModule = X(HostResolveImportedModule(module, required)); + index = Q(InnerModuleEvaluation(requiredModule, stack, index)); + if (requiredModule instanceof CyclicModuleRecord) { + Assert(requiredModule.Status === 'evaluating' || requiredModule.Status === 'evaluated'); + if (stack.includes(requiredModule)) { + Assert(requiredModule.Status === 'evaluating'); + } + if (requiredModule.Status === 'evaluating') { + module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex); + } else { + requiredModule = GetAsyncCycleRoot(requiredModule); + Assert(requiredModule.Status === 'evaluated'); + if (requiredModule.EvaluationError !== Value.undefined) { + return module.EvaluationError; + } + } + if (requiredModule.AsyncEvaluating === Value.true) { + module.PendingAsyncDependencies += 1; + requiredModule.AsyncParentModules.push(module); + } + } + } + if (module.PendingAsyncDependencies > 0) { + module.AsyncEvaluating = Value.true; + } else if (module.Async === Value.true) { + X(ExecuteAsyncModule(module)); + } else { + Q(module.ExecuteModule()); + } + Assert(stack.indexOf(module) === stack.lastIndexOf(module)); + Assert(module.DFSAncestorIndex <= module.DFSIndex); + if (module.DFSAncestorIndex === module.DFSIndex) { + let done = false; + while (done === false) { + const requiredModule = stack.pop(); + Assert(requiredModule instanceof CyclicModuleRecord); + requiredModule.Status = 'evaluated'; + if (requiredModule === module) { + done = true; + } + } + } + return index; +} + +// https://tc39.es/proposal-top-level-await/#sec-execute-async-module +function ExecuteAsyncModule(module) { + Assert(module.Status === 'evaluating' || module.Status === 'evaluated'); + Assert(module.Async === Value.true); + module.AsyncEvaluating = Value.true; + const capability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const stepsFulfilled = CallAsyncModuleFulfilled; + const onFulfilled = CreateBuiltinFunction(stepsFulfilled, ['Module']); + onFulfilled.Module = module; + const stepsRejected = CallAsyncModuleRejected; + const onRejected = CreateBuiltinFunction(stepsRejected, ['Module']); + onRejected.Module = module; + X(PerformPromiseThen(capability.Promise, onFulfilled, onRejected)); + X(module.ExecuteModule(capability)); + return Value.undefined; +} + +// https://tc39.es/proposal-top-level-await/#sec-execute-async-module +function CallAsyncModuleFulfilled() { + const f = surroundingAgent.activeFunctionObject; + const module = f.Module; + X(AsyncModuleExecutionFulfilled(module)); + return Value.undefined; +} + +// https://tc39.es/proposal-top-level-await/#sec-execute-async-module +function CallAsyncModuleRejected([error = Value.undefined]) { + const f = surroundingAgent.activeFunctionObject; + const module = f.Module; + X(AsyncModuleExecutionRejected(module, error)); + return Value.undefined; +} + +// https://tc39.es/proposal-top-level-await/#sec-getcycleroot +export function GetAsyncCycleRoot(module) { + Assert(module.Status === 'evaluated'); + if (module.AsyncParentModules.length === 0) { + return module; + } + while (module.DFSIndex > module.DFSAncestorIndex) { + Assert(module.AsyncParentModules.length > 0); + const nextCycleModule = module.AsyncParentModules[0]; + Assert(nextCycleModule.DFSAncestorIndex === module.DFSAncestorIndex); + module = nextCycleModule; + } + Assert(module.DFSIndex === module.DFSAncestorIndex); + return module; +} + +// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled +function AsyncModuleExecutionFulfilled(module) { + Assert(module.Status === 'evaluated'); + if (module.AsyncEvaluating === Value.false) { + Assert(module.EvaluationError !== Value.undefined); + return Value.undefined; + } + Assert(module.EvaluationError === Value.undefined); + module.AsyncEvaluating = Value.false; + for (const m of module.AsyncParentModules) { + if (module.DFSIndex !== module.DFSAncestorIndex) { + Assert(m.DFSAncestorIndex === module.DFSAncestorIndex); + } + m.PendingAsyncDependencies -= 1; + if (m.PendingAsyncDependencies === 0 && m.EvaluationError === Value.undefined) { + Assert(m.AsyncEvaluating === Value.true); + const cycleRoot = X(GetAsyncCycleRoot(m)); + if (cycleRoot.EvaluationError !== Value.undefined) { + return Value.undefined; + } + if (m.Async === Value.true) { + X(ExecuteAsyncModule(m)); + } else { + const result = m.ExecuteModule(); + if (result instanceof NormalCompletion) { + X(AsyncModuleExecutionFulfilled(m)); + } else { + X(AsyncModuleExecutionRejected(m, result.Value)); + } + } + } + } + if (module.TopLevelCapability !== Value.undefined) { + Assert(module.DFSIndex === module.DFSAncestorIndex); + X(Call(module.TopLevelCapability.Resolve, Value.undefined, [Value.undefined])); + } + return Value.undefined; +} + +// https://tc39.es/proposal-top-level-await/#sec-AsyncModuleExecutionRejected +function AsyncModuleExecutionRejected(module, error) { + Assert(module.Status === 'evaluated'); + if (module.AsyncEvaluating === Value.false) { + Assert(module.EvaluationError !== Value.undefined); + return Value.undefined; + } + Assert(module.EvaluationError === Value.undefined); + module.EvaluationError = new ThrowCompletion(error); + module.AsyncEvaluating = Value.false; + for (const m of module.AsyncParentModules) { + if (module.DFSIndex !== module.DFSAncestorIndex) { + Assert(m.DFSAncestorIndex === module.DFSAncestorIndex); + } + X(AsyncModuleExecutionRejected(m, error)); + } + if (module.TopLevelCapability !== Value.undefined) { + Assert(module.DFSIndex === module.DFSAncestorIndex); + X(Call(module.TopLevelCapability.Reject, Value.undefined, [error])); + } + return Value.undefined; +} + +// 15.2.1.21 #sec-getmodulenamespace +export function GetModuleNamespace(module) { + Assert(module instanceof AbstractModuleRecord); + if (module instanceof CyclicModuleRecord) { + Assert(module.Status !== 'unlinked'); + } + let namespace = module.Namespace; + if (namespace === Value.undefined) { + const exportedNames = Q(module.GetExportedNames()); + const unambiguousNames = []; + for (const name of exportedNames) { + const resolution = Q(module.ResolveExport(name)); + if (resolution instanceof ResolvedBindingRecord) { + unambiguousNames.push(name); + } + } + namespace = ModuleNamespaceCreate(module, unambiguousNames); + } + return namespace; +} diff --git a/src/engine262/src/abstract-ops/notational-conventions.mjs b/src/engine262/src/abstract-ops/notational-conventions.mjs new file mode 100644 index 0000000..fffb0d9 --- /dev/null +++ b/src/engine262/src/abstract-ops/notational-conventions.mjs @@ -0,0 +1,55 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value, Type } from '../value.mjs'; + +export function Assert(invariant, source) { + if (!invariant) { + throw new TypeError(`Assert failed${source ? `: ${source}` : ''}`.trim()); + } +} + +// 9.1.15 #sec-requireinternalslot +export function RequireInternalSlot(O, internalSlot) { + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', O); + } + if (!(internalSlot in O)) { + return surroundingAgent.Throw('TypeError', 'InternalSlotMissing', O, internalSlot); + } +} + +export function sourceTextMatchedBy(node) { + return new Value(node.sourceText()); +} + +// An ECMAScript Script syntactic unit may be processed using either unrestricted or strict mode syntax and semantics. +// Code is interpreted as strict mode code in the following situations: +// +// - Global code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive. +// +// - Module code is always strict mode code. +// +// - All parts of a ClassDeclaration or a ClassExpression are strict mode code. +// +// - Eval code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive or +// if the call to eval is a direct eval that is contained in strict mode code. +// +// - Function code is strict mode code if the associated FunctionDeclaration, FunctionExpression, GeneratorDeclaration, +// GeneratorExpression, AsyncFunctionDeclaration, AsyncFunctionExpression, AsyncGeneratorDeclaration, +// AsyncGeneratorExpression, MethodDefinition, ArrowFunction, or AsyncArrowFunction is contained in strict mode code +// or if the code that produces the value of the function's [[ECMAScriptCode]] internal slot begins with a Directive +// Prologue that contains a Use Strict Directive. +// +// - Function code that is supplied as the arguments to the built-in Function, Generator, AsyncFunction, and +// AsyncGenerator constructors is strict mode code if the last argument is a String that when processed is a +// FunctionBody that begins with a Directive Prologue that contains a Use Strict Directive. +export function isStrictModeCode(node) { + if (node.strict === true) { + return true; + } + + if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { + return isStrictModeCode(node.body); + } + + return false; +} diff --git a/src/engine262/src/abstract-ops/object-operations.mjs b/src/engine262/src/abstract-ops/object-operations.mjs new file mode 100644 index 0000000..6cf1c5d --- /dev/null +++ b/src/engine262/src/abstract-ops/object-operations.mjs @@ -0,0 +1,434 @@ +import { + Descriptor, + Type, + Value, + ObjectValue, + wellKnownSymbols, +} from '../value.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { + InstanceofOperator, +} from '../runtime-semantics/all.mjs'; +import { + NormalCompletion, + Q, X, +} from '../completion.mjs'; +import { + ArrayCreate, + Assert, + IsAccessorDescriptor, + IsCallable, + IsConstructor, + IsDataDescriptor, + IsExtensible, + IsPropertyKey, + SameValue, + ToLength, + ToObject, + ToString, + isProxyExoticObject, +} from './all.mjs'; + + +// This file covers abstract operations defined in +// 7.3 #sec-operations-on-objects + +// #sec-makebasicobject +export function MakeBasicObject(internalSlotsList) { + // 1. Assert: internalSlotsList is a List of internal slot names. + Assert(Array.isArray(internalSlotsList)); + // 2. Let obj be a newly created object with an internal slot for each name in internalSlotsList. + // 3. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1. + const obj = new ObjectValue(internalSlotsList); + internalSlotsList.forEach((s) => { + obj[s] = Value.undefined; + }); + // 4. Assert: If the caller will not be overriding both obj's [[GetPrototypeOf]] and [[SetPrototypeOf]] essential internal methods, then internalSlotsList contains [[Prototype]]. + // 5. Assert: If the caller will not be overriding all of obj's [[SetPrototypeOf]], [[IsExtensible]], and [[PreventExtensions]] essential internal methods, then internalSlotsList contains [[Extensible]]. + // 6. If internalSlotsList contains [[Extensible]], then set obj.[[Extensible]] to true. + if (internalSlotsList.includes('Extensible')) { + obj.Extensible = Value.true; + } + // 7. Return obj. + return obj; +} + +// 7.3.1 #sec-get-o-p +export function Get(O, P) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + // TODO: This should just return Q(O.Get(P, O)) + return new NormalCompletion(Q(O.Get(P, O))); +} + +// 7.3.2 #sec-getv +export function GetV(V, P) { + Assert(IsPropertyKey(P)); + const O = Q(ToObject(V)); + return Q(O.Get(P, V)); +} + +// 7.3.3 #sec-set-o-p-v-throw +export function Set(O, P, V, Throw) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + Assert(Type(Throw) === 'Boolean'); + const success = Q(O.Set(P, V, O)); + if (success === Value.false && Throw === Value.true) { + return surroundingAgent.Throw('TypeError', 'CannotSetProperty', P, O); + } + return success; +} + +// 7.3.4 #sec-createdataproperty +export function CreateDataProperty(O, P, V) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + + const newDesc = Descriptor({ + Value: V, + Writable: Value.true, + Enumerable: Value.true, + Configurable: Value.true, + }); + return Q(O.DefineOwnProperty(P, newDesc)); +} + +// 7.3.5 #sec-createmethodproperty +export function CreateMethodProperty(O, P, V) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + + const newDesc = Descriptor({ + Value: V, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }); + return Q(O.DefineOwnProperty(P, newDesc)); +} + +// 7.3.6 #sec-createdatapropertyorthrow +export function CreateDataPropertyOrThrow(O, P, V) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + const success = Q(CreateDataProperty(O, P, V)); + if (success === Value.false) { + return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P); + } + return success; +} + +// 7.3.7 #sec-definepropertyorthrow +export function DefinePropertyOrThrow(O, P, desc) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + const success = Q(O.DefineOwnProperty(P, desc)); + if (success === Value.false) { + return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P); + } + return success; +} + +// 7.3.8 #sec-deletepropertyorthrow +export function DeletePropertyOrThrow(O, P) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + const success = Q(O.Delete(P)); + if (success === Value.false) { + return surroundingAgent.Throw('TypeError', 'CannotDeleteProperty', P); + } + return success; +} + +// 7.3.9 #sec-getmethod +export function GetMethod(V, P) { + Assert(IsPropertyKey(P)); + const func = Q(GetV(V, P)); + if (func === Value.null || func === Value.undefined) { + return Value.undefined; + } + if (IsCallable(func) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); + } + return func; +} + +// 7.3.10 #sec-hasproperty +export function HasProperty(O, P) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + return Q(O.HasProperty(P)); +} + +// 7.3.11 #sec-hasownproperty +export function HasOwnProperty(O, P) { + Assert(Type(O) === 'Object'); + Assert(IsPropertyKey(P)); + const desc = Q(O.GetOwnProperty(P)); + if (desc === Value.undefined) { + return Value.false; + } + return Value.true; +} + +// 7.3.12 #sec-call +export function Call(F, V, argumentsList) { + if (!argumentsList) { + argumentsList = []; + } + Assert(argumentsList.every((a) => a instanceof Value)); + + if (IsCallable(F) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', F); + } + + return Q(F.Call(V, argumentsList)); +} + +// 7.3.13 #sec-construct +export function Construct(F, argumentsList, newTarget) { + if (!newTarget) { + newTarget = F; + } + if (!argumentsList) { + argumentsList = []; + } + Assert(IsConstructor(F) === Value.true); + Assert(IsConstructor(newTarget) === Value.true); + return Q(F.Construct(argumentsList, newTarget)); +} + +// 7.3.14 #sec-setintegritylevel +export function SetIntegrityLevel(O, level) { + Assert(Type(O) === 'Object'); + Assert(level === 'sealed' || level === 'frozen'); + const status = Q(O.PreventExtensions()); + if (status === Value.false) { + return Value.false; + } + const keys = Q(O.OwnPropertyKeys()); + if (level === 'sealed') { + for (const k of keys) { + Q(DefinePropertyOrThrow(O, k, Descriptor({ Configurable: Value.false }))); + } + } else if (level === 'frozen') { + for (const k of keys) { + const currentDesc = Q(O.GetOwnProperty(k)); + if (currentDesc !== Value.undefined) { + let desc; + if (IsAccessorDescriptor(currentDesc) === true) { + desc = Descriptor({ Configurable: Value.false }); + } else { + desc = Descriptor({ Configurable: Value.false, Writable: Value.false }); + } + Q(DefinePropertyOrThrow(O, k, desc)); + } + } + } + return Value.true; +} + +// 7.3.15 #sec-testintegritylevel +export function TestIntegrityLevel(O, level) { + Assert(Type(O) === 'Object'); + Assert(level === 'sealed' || level === 'frozen'); + const extensible = Q(IsExtensible(O)); + if (extensible === Value.true) { + return Value.false; + } + const keys = Q(O.OwnPropertyKeys()); + for (const k of keys) { + const currentDesc = Q(O.GetOwnProperty(k)); + if (currentDesc !== Value.undefined) { + if (currentDesc.Configurable === Value.true) { + return Value.false; + } + if (level === 'frozen' && IsDataDescriptor(currentDesc)) { + if (currentDesc.Writable === Value.true) { + return Value.false; + } + } + } + } + return Value.true; +} + +// 7.3.16 #sec-createarrayfromlist +export function CreateArrayFromList(elements) { + Assert(elements.every((e) => e instanceof Value)); + const array = X(ArrayCreate(new Value(0))); + let n = 0; + for (const e of elements) { + const nStr = X(ToString(new Value(n))); + const status = X(CreateDataProperty(array, nStr, e)); + Assert(status === Value.true); + n += 1; + } + return array; +} + +// 7.3.17 #sec-lengthofarraylike +export function LengthOfArrayLike(obj) { + Assert(Type(obj) === 'Object'); + return Q(ToLength(Q(Get(obj, new Value('length'))))); +} + +// 7.3.17 #sec-createlistfromarraylike +export function CreateListFromArrayLike(obj, elementTypes) { + if (!elementTypes) { + elementTypes = ['Undefined', 'Null', 'Boolean', 'String', 'Symbol', 'Number', 'Object']; + } + if (Type(obj) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', obj); + } + const len = Q(LengthOfArrayLike(obj)).numberValue(); + const list = []; + let index = 0; + while (index < len) { + const indexName = X(ToString(new Value(index))); + const next = Q(Get(obj, indexName)); + if (!elementTypes.includes(Type(next))) { + return surroundingAgent.Throw('TypeError', 'NotPropertyName', next); + } + list.push(next); + index += 1; + } + return list; +} + +// 7.3.18 #sec-invoke +export function Invoke(V, P, argumentsList) { + Assert(IsPropertyKey(P)); + if (!argumentsList) { + argumentsList = []; + } + const func = Q(GetV(V, P)); + return Q(Call(func, V, argumentsList)); +} + +// 7.3.19 #sec-ordinaryhasinstance +export function OrdinaryHasInstance(C, O) { + if (IsCallable(C) === Value.false) { + return Value.false; + } + if ('BoundTargetFunction' in C) { + const BC = C.BoundTargetFunction; + return Q(InstanceofOperator(O, BC)); + } + if (Type(O) !== 'Object') { + return Value.false; + } + const P = Q(Get(C, new Value('prototype'))); + if (Type(P) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', P); + } + while (true) { + O = Q(O.GetPrototypeOf()); + if (O === Value.null) { + return Value.false; + } + if (SameValue(P, O) === Value.true) { + return Value.true; + } + } +} + +// 7.3.20 #sec-speciesconstructor +export function SpeciesConstructor(O, defaultConstructor) { + Assert(Type(O) === 'Object'); + const C = Q(Get(O, new Value('constructor'))); + if (C === Value.undefined) { + return defaultConstructor; + } + if (Type(C) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', C); + } + const S = Q(Get(C, wellKnownSymbols.species)); + if (S === Value.undefined || S === Value.null) { + return defaultConstructor; + } + if (IsConstructor(S) === Value.true) { + return S; + } + return surroundingAgent.Throw('TypeError', 'SpeciesNotConstructor'); +} + +// 7.3.21 #sec-enumerableownpropertynames +export function EnumerableOwnPropertyNames(O, kind) { + Assert(Type(O) === 'Object'); + const ownKeys = Q(O.OwnPropertyKeys()); + const properties = []; + for (const key of ownKeys) { + if (Type(key) === 'String') { + const desc = Q(O.GetOwnProperty(key)); + if (desc !== Value.undefined && desc.Enumerable === Value.true) { + if (kind === 'key') { + properties.push(key); + } else { + const value = Q(Get(O, key)); + if (kind === 'value') { + properties.push(value); + } else { + Assert(kind === 'key+value'); + const entry = X(CreateArrayFromList([key, value])); + properties.push(entry); + } + } + } + } + } + return properties; +} + +// 7.3.22 #sec-getfunctionrealm +export function GetFunctionRealm(obj) { + Assert(X(IsCallable(obj)) === Value.true); + if ('Realm' in obj) { + return obj.Realm; + } + + if ('BoundTargetFunction' in obj) { + const target = obj.BoundTargetFunction; + return Q(GetFunctionRealm(target)); + } + + if (isProxyExoticObject(obj)) { + if (obj.ProxyHandler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'GetFunctionRealm'); + } + const proxyTarget = obj.ProxyTarget; + return Q(GetFunctionRealm(proxyTarget)); + } + + return surroundingAgent.currentRealmRecord; +} + +// 7.3.23 #sec-copydataproperties +export function CopyDataProperties(target, source, excludedItems) { + Assert(Type(target) === 'Object'); + Assert(excludedItems.every((i) => IsPropertyKey(i))); + if (source === Value.undefined || source === Value.null) { + return target; + } + const from = X(ToObject(source)); + const keys = Q(from.OwnPropertyKeys()); + for (const nextKey of keys) { + let excluded = false; + for (const e of excludedItems) { + if (SameValue(e, nextKey) === Value.true) { + excluded = true; + } + } + if (excluded === false) { + const desc = Q(from.GetOwnProperty(nextKey)); + if (desc !== Value.undefined && desc.Enumerable === Value.true) { + const propValue = Q(Get(from, nextKey)); + X(CreateDataProperty(target, nextKey, propValue)); + } + } + } + return target; +} diff --git a/src/engine262/src/abstract-ops/objects.mjs b/src/engine262/src/abstract-ops/objects.mjs new file mode 100644 index 0000000..036087b --- /dev/null +++ b/src/engine262/src/abstract-ops/objects.mjs @@ -0,0 +1,405 @@ +import { + Descriptor, + ObjectValue, + Type, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { + Assert, + Call, + CreateDataProperty, + Get, + GetFunctionRealm, + IsAccessorDescriptor, + IsCallable, + IsDataDescriptor, + IsExtensible, + IsGenericDescriptor, + IsPropertyKey, + SameValue, + MakeBasicObject, + isArrayIndex, +} from './all.mjs'; + +// 9.1.1.1 OrdinaryGetPrototypeOf +export function OrdinaryGetPrototypeOf(O) { + return O.Prototype; +} + +// 9.1.2.1 OrdinarySetPrototypeOf +export function OrdinarySetPrototypeOf(O, V) { + Assert(Type(V) === 'Object' || Type(V) === 'Null'); + + const current = O.Prototype; + if (SameValue(V, current) === Value.true) { + return Value.true; + } + const extensible = O.Extensible; + if (extensible === Value.false) { + return Value.false; + } + let p = V; + let done = false; + while (done === false) { + if (p === Value.null) { + done = true; + } else if (SameValue(p, O) === Value.true) { + return Value.false; + } else if (p.GetPrototypeOf !== ObjectValue.prototype.GetPrototypeOf) { + done = true; + } else { + p = p.Prototype; + } + } + O.Prototype = V; + return Value.true; +} + +// 9.1.3.1 OrdinaryIsExtensible +export function OrdinaryIsExtensible(O) { + return O.Extensible; +} + +// 9.1.4.1 OrdinaryPreventExtensions +export function OrdinaryPreventExtensions(O) { + O.Extensible = Value.false; + return Value.true; +} + +// 9.1.5.1 OrdinaryGetOwnProperty +export function OrdinaryGetOwnProperty(O, P) { + Assert(IsPropertyKey(P)); + + if (!O.properties.has(P)) { + return Value.undefined; + } + + const D = Descriptor({}); + + const x = O.properties.get(P); + + if (IsDataDescriptor(x)) { + D.Value = x.Value; + D.Writable = x.Writable; + } else if (IsAccessorDescriptor(x)) { + D.Get = x.Get; + D.Set = x.Set; + } + D.Enumerable = x.Enumerable; + D.Configurable = x.Configurable; + + return D; +} + +// 9.1.6.1 OrdinaryDefineOwnProperty +export function OrdinaryDefineOwnProperty(O, P, Desc) { + const current = Q(O.GetOwnProperty(P)); + const extensible = Q(IsExtensible(O)); + return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current); +} + +// 9.1.6.2 #sec-iscompatiblepropertydescriptor +export function IsCompatiblePropertyDescriptor(Extensible, Desc, Current) { + return ValidateAndApplyPropertyDescriptor( + Value.undefined, Value.undefined, Extensible, Desc, Current, + ); +} + +// 9.1.6.3 ValidateAndApplyPropertyDescriptor +export function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) { + Assert(O === Value.undefined || IsPropertyKey(P)); + + if (current === Value.undefined) { + if (extensible === Value.false) { + return Value.false; + } + + Assert(extensible === Value.true); + + if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) { + if (Type(O) !== 'Undefined') { + O.properties.set(P, Descriptor({ + Value: Desc.Value === undefined ? Value.undefined : Desc.Value, + Writable: Desc.Writable === undefined ? Value.false : Desc.Writable, + Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable, + Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable, + })); + } + } else { + Assert(IsAccessorDescriptor(Desc)); + if (Type(O) !== 'Undefined') { + O.properties.set(P, Descriptor({ + Get: Desc.Get === undefined ? Value.undefined : Desc.Get, + Set: Desc.Set === undefined ? Value.undefined : Desc.Set, + Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable, + Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable, + })); + } + } + + return Value.true; + } + + if (Desc.everyFieldIsAbsent()) { + return Value.true; + } + + if (current.Configurable === Value.false) { + if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) { + return Value.false; + } + + if (Desc.Enumerable !== undefined && Desc.Enumerable !== current.Enumerable) { + return Value.false; + } + } + + if (IsGenericDescriptor(Desc)) { + // No further validation is required. + } else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) { + if (current.Configurable === Value.false) { + return Value.false; + } + if (IsDataDescriptor(current)) { + if (Type(O) !== 'Undefined') { + const entry = O.properties.get(P); + entry.Value = undefined; + entry.Writable = undefined; + entry.Get = Value.undefined; + entry.Set = Value.undefined; + } + } else { + if (Type(O) !== 'Undefined') { + const entry = O.properties.get(P); + entry.Get = undefined; + entry.Set = undefined; + entry.Value = Value.undefined; + entry.Writable = Value.false; + } + } + } else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) { + if (current.Configurable === Value.false && current.Writable === Value.false) { + if (Desc.Writable !== undefined && Desc.Writable === Value.true) { + return Value.false; + } + if (Desc.Value !== undefined && SameValue(Desc.Value, current.Value) === Value.false) { + return Value.false; + } + return Value.true; + } + } else { + Assert(IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc)); + if (current.Configurable === Value.false) { + if (Desc.Set !== undefined && SameValue(Desc.Set, current.Set) === Value.false) { + return Value.false; + } + if (Desc.Get !== undefined && SameValue(Desc.Get, current.Get) === Value.false) { + return Value.false; + } + return Value.true; + } + } + + if (Type(O) !== 'Undefined') { + const target = O.properties.get(P); + if (Desc.Value !== undefined) { + target.Value = Desc.Value; + } + if (Desc.Writable !== undefined) { + target.Writable = Desc.Writable; + } + if (Desc.Get !== undefined) { + target.Get = Desc.Get; + } + if (Desc.Set !== undefined) { + target.Set = Desc.Set; + } + if (Desc.Enumerable !== undefined) { + target.Enumerable = Desc.Enumerable; + } + if (Desc.Configurable !== undefined) { + target.Configurable = Desc.Configurable; + } + } + + return Value.true; +} + +// 9.1.7.1 OrdinaryHasProperty +export function OrdinaryHasProperty(O, P) { + Assert(IsPropertyKey(P)); + + const hasOwn = Q(O.GetOwnProperty(P)); + if (Type(hasOwn) !== 'Undefined') { + return Value.true; + } + const parent = Q(O.GetPrototypeOf()); + if (Type(parent) !== 'Null') { + return Q(parent.HasProperty(P)); + } + return Value.false; +} + +// 9.1.8.1 +export function OrdinaryGet(O, P, Receiver) { + Assert(IsPropertyKey(P)); + + const desc = Q(O.GetOwnProperty(P)); + if (Type(desc) === 'Undefined') { + const parent = Q(O.GetPrototypeOf()); + if (Type(parent) === 'Null') { + return Value.undefined; + } + return Q(parent.Get(P, Receiver)); + } + if (IsDataDescriptor(desc)) { + return desc.Value; + } + Assert(IsAccessorDescriptor(desc)); + const getter = desc.Get; + if (Type(getter) === 'Undefined') { + return Value.undefined; + } + return Q(Call(getter, Receiver)); +} + +// 9.1.9.1 OrdinarySet +export function OrdinarySet(O, P, V, Receiver) { + Assert(IsPropertyKey(P)); + const ownDesc = Q(O.GetOwnProperty(P)); + return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc); +} + +// 9.1.9.2 OrdinarySetWithOwnDescriptor +export function OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc) { + Assert(IsPropertyKey(P)); + + if (Type(ownDesc) === 'Undefined') { + const parent = Q(O.GetPrototypeOf()); + if (Type(parent) !== 'Null') { + return Q(parent.Set(P, V, Receiver)); + } + ownDesc = Descriptor({ + Value: Value.undefined, + Writable: Value.true, + Enumerable: Value.true, + Configurable: Value.true, + }); + } + + if (IsDataDescriptor(ownDesc)) { + if (ownDesc.Writable !== undefined && ownDesc.Writable === Value.false) { + return Value.false; + } + if (Type(Receiver) !== 'Object') { + return Value.false; + } + + const existingDescriptor = Q(Receiver.GetOwnProperty(P)); + if (Type(existingDescriptor) !== 'Undefined') { + if (IsAccessorDescriptor(existingDescriptor)) { + return Value.false; + } + if (existingDescriptor.Writable === Value.false) { + return Value.false; + } + const valueDesc = Descriptor({ Value: V }); + return Q(Receiver.DefineOwnProperty(P, valueDesc)); + } + return CreateDataProperty(Receiver, P, V); + } + + Assert(IsAccessorDescriptor(ownDesc)); + const setter = ownDesc.Set; + if (setter === undefined || Type(setter) === 'Undefined') { + return Value.false; + } + Q(Call(setter, Receiver, [V])); + return Value.true; +} + +// 9.1.10.1 OrdinaryDelete +export function OrdinaryDelete(O, P) { + Assert(IsPropertyKey(P)); + const desc = Q(O.GetOwnProperty(P)); + if (Type(desc) === 'Undefined') { + return Value.true; + } + if (desc.Configurable === Value.true) { + O.properties.delete(P); + return Value.true; + } + return Value.false; +} + +// 9.1.11.1 +export function OrdinaryOwnPropertyKeys(O) { + const keys = []; + + // For each own property key P of O that is an array index, in ascending numeric index order, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + if (isArrayIndex(P)) { + keys.push(P); + } + } + keys.sort((a, b) => Number.parseInt(a.stringValue(), 10) - Number.parseInt(b.stringValue(), 10)); + + // For each own property key P of O such that Type(P) is String and + // P is not an array index, in ascending chronological order of property creation, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + if (Type(P) === 'String' && isArrayIndex(P) === false) { + keys.push(P); + } + } + + // For each own property key P of O such that Type(P) is Symbol, + // in ascending chronological order of property creation, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + if (Type(P) === 'Symbol') { + keys.push(P); + } + } + + return keys; +} + +// #sec-ordinaryobjectcreate +export function OrdinaryObjectCreate(proto, additionalInternalSlotsList) { + // 1. Let internalSlotsList be « [[Prototype]], [[Extensible]] ». + const internalSlotsList = ['Prototype', 'Extensible']; + // 2. If additionalInternalSlotsList is present, append each of its elements to internalSlotsList. + if (additionalInternalSlotsList !== undefined) { + internalSlotsList.push(...additionalInternalSlotsList); + } + // 3. Let O be ! MakeBasicObject(internalSlotsList). + const O = X(MakeBasicObject(internalSlotsList)); + // 4. Set O.[[Prototype]] to proto. + O.Prototype = proto; + // 5. Return O. + return O; +} + +// 9.1.13 OrdinaryCreateFromConstructor +export function OrdinaryCreateFromConstructor(constructor, intrinsicDefaultProto, internalSlotsList) { + // Assert: intrinsicDefaultProto is a String value that is this specification's name of an intrinsic object. + const proto = Q(GetPrototypeFromConstructor(constructor, intrinsicDefaultProto)); + return OrdinaryObjectCreate(proto, internalSlotsList); +} + +// 9.1.14 GetPrototypeFromConstructor +export function GetPrototypeFromConstructor(constructor, intrinsicDefaultProto) { + // Assert: intrinsicDefaultProto is a String value that + // is this specification's name of an intrinsic object. + Assert(IsCallable(constructor) === Value.true); + let proto = Q(Get(constructor, new Value('prototype'))); + if (Type(proto) !== 'Object') { + const realm = Q(GetFunctionRealm(constructor)); + proto = realm.Intrinsics[intrinsicDefaultProto]; + } + return proto; +} + diff --git a/src/engine262/src/abstract-ops/promise-operations.mjs b/src/engine262/src/abstract-ops/promise-operations.mjs new file mode 100644 index 0000000..73487de --- /dev/null +++ b/src/engine262/src/abstract-ops/promise-operations.mjs @@ -0,0 +1,392 @@ +import { + HostEnqueuePromiseJob, + HostPromiseRejectionTracker, + surroundingAgent, +} from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { + Completion, + AbruptCompletion, + NormalCompletion, + Q, + X, + ThrowCompletion, + EnsureCompletion, +} from '../completion.mjs'; +import { + Assert, + Call, + Construct, + CreateBuiltinFunction, + Get, + IsCallable, + IsConstructor, + SameValue, + SetFunctionLength, + SetFunctionName, + GetFunctionRealm, + isFunctionObject, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 25.6 #sec-promise-objects + +// 25.6.1.1 #sec-promisecapability-records +export class PromiseCapabilityRecord { + constructor() { + this.Promise = Value.undefined; + this.Resolve = Value.undefined; + this.Reject = Value.undefined; + } +} + +// 25.6.1.2 #sec-promisereaction-records +export class PromiseReactionRecord { + constructor(O) { + Assert(O.Capability instanceof PromiseCapabilityRecord + || O.Capability === Value.undefined); + Assert(O.Type === 'Fulfill' || O.Type === 'Reject'); + Assert(O.Handler === Value.undefined + || isFunctionObject(O.Handler)); + this.Capability = O.Capability; + this.Type = O.Type; + this.Handler = O.Handler; + } +} + +// 25.6.1.3 #sec-createresolvingfunctions +export function CreateResolvingFunctions(promise) { + const alreadyResolved = { Value: false }; + const stepsResolve = PromiseResolveFunctions; + const resolve = X(CreateBuiltinFunction(stepsResolve, ['Promise', 'AlreadyResolved'])); + SetFunctionLength(resolve, new Value(1)); + SetFunctionName(resolve, new Value('')); + resolve.Promise = promise; + resolve.AlreadyResolved = alreadyResolved; + const stepsReject = PromiseRejectFunctions; + const reject = X(CreateBuiltinFunction(stepsReject, ['Promise', 'AlreadyResolved'])); + SetFunctionLength(reject, new Value(1)); + SetFunctionName(reject, new Value('')); + reject.Promise = promise; + reject.AlreadyResolved = alreadyResolved; + return { + Resolve: resolve, + Reject: reject, + }; +} + +// 25.6.1.3.1 #sec-promise-reject-functions +function PromiseRejectFunctions([reason = Value.undefined]) { + const F = this; + + Assert('Promise' in F && Type(F.Promise) === 'Object'); + const promise = F.Promise; + const alreadyResolved = F.AlreadyResolved; + if (alreadyResolved.Value === true) { + return Value.undefined; + } + alreadyResolved.Value = true; + return RejectPromise(promise, reason); +} + +// #sec-newpromiseresolvethenablejob +function NewPromiseResolveThenableJob(promiseToResolve, thenable, then) { + // 1. Let job be a new Job abstract closure with no parameters that captures + // promiseToResolve, thenable, and then and performs the following steps when called: + const job = () => { + // a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve). + const resolvingFunctions = CreateResolvingFunctions(promiseToResolve); + // b. Let thenCallResult be Call(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »). + const thenCallResult = Call(then, thenable, [resolvingFunctions.Resolve, resolvingFunctions.Reject]); + // c. If thenCallResult is an abrupt completion, then + if (thenCallResult instanceof AbruptCompletion) { + // i .Let status be Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »). + const status = Call(resolvingFunctions.Reject, Value.undefined, [thenCallResult.Value]); + // ii. Return Completion(status). + return Completion(status); + } + // d. Return Completion(thenCallResult). + return Completion(thenCallResult); + }; + // 2. Let getThenRealmResult be GetFunctionRealm(then). + const getThenRealmResult = GetFunctionRealm(then); + // 3. If getThenRealmResult is a normal completion, then let thenRealm be getThenRealmResult.[[Value]]. + // 4. Otherwise, let thenRealm be null. + let thenRealm; + if (getThenRealmResult instanceof NormalCompletion) { + thenRealm = getThenRealmResult.Value; + } else { + thenRealm = Value.null; + } + // 5. Return { [[Job]]: job, [[Realm]]: thenRealm }. + return { Job: job, Realm: thenRealm }; +} + +// 25.6.1.3.2 #sec-promise-resolve-functions +function PromiseResolveFunctions([resolution = Value.undefined]) { + const F = this; + + Assert('Promise' in F && Type(F.Promise) === 'Object'); + const promise = F.Promise; + const alreadyResolved = F.AlreadyResolved; + if (alreadyResolved.Value === true) { + return Value.undefined; + } + alreadyResolved.Value = true; + if (SameValue(resolution, promise) === Value.true) { + const selfResolutionError = surroundingAgent.Throw('TypeError', 'CannotResolvePromiseWithItself').Value; + return RejectPromise(promise, selfResolutionError); + } + if (Type(resolution) !== 'Object') { + return FulfillPromise(promise, resolution); + } + + const then = Get(resolution, new Value('then')); + if (then instanceof AbruptCompletion) { + return RejectPromise(promise, then.Value); + } + const thenAction = then.Value; + if (IsCallable(thenAction) === Value.false) { + return FulfillPromise(promise, resolution); + } + const job = NewPromiseResolveThenableJob(promise, resolution, thenAction); + HostEnqueuePromiseJob(job.Job, job.Realm); + return Value.undefined; +} + +// 25.6.1.4 #sec-fulfillpromise +function FulfillPromise(promise, value) { + Assert(promise.PromiseState === 'pending'); + const reactions = promise.PromiseFulfillReactions; + promise.PromiseResult = value; + promise.PromiseFulfillReactions = undefined; + promise.PromiseRejectReactions = undefined; + promise.PromiseState = 'fulfilled'; + return TriggerPromiseReactions(reactions, value); +} + +// 25.6.1.5 #sec-newpromisecapability +export function NewPromiseCapability(C) { + if (IsConstructor(C) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', C); + } + const promiseCapability = new PromiseCapabilityRecord(); + const steps = GetCapabilitiesExecutorFunctions; + const executor = X(CreateBuiltinFunction(steps, ['Capability'])); + SetFunctionLength(executor, new Value(2)); + SetFunctionName(executor, new Value('')); + executor.Capability = promiseCapability; + const promise = Q(Construct(C, [executor])); + if (IsCallable(promiseCapability.Resolve) === Value.false) { + return surroundingAgent.Throw('TypeError', 'PromiseResolveFunction', promiseCapability.Resolve); + } + if (IsCallable(promiseCapability.Reject) === Value.false) { + return surroundingAgent.Throw('TypeError', 'PromiseRejectFunction', promiseCapability.Reject); + } + promiseCapability.Promise = promise; + return promiseCapability; +} + +// 25.6.1.5.1 #sec-getcapabilitiesexecutor-functions +function GetCapabilitiesExecutorFunctions([resolve = Value.undefined, reject = Value.undefined]) { + const F = this; + + const promiseCapability = F.Capability; + if (Type(promiseCapability.Resolve) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'resolve'); + } + if (Type(promiseCapability.Reject) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'reject'); + } + promiseCapability.Resolve = resolve; + promiseCapability.Reject = reject; + return Value.undefined; +} + +// 25.6.1.6 #sec-ispromise +export function IsPromise(x) { + if (Type(x) !== 'Object') { + return Value.false; + } + if (!('PromiseState' in x)) { + return Value.false; + } + return Value.true; +} + +// 25.6.1.7 #sec-rejectpromise +function RejectPromise(promise, reason) { + Assert(promise.PromiseState === 'pending'); + const reactions = promise.PromiseRejectReactions; + promise.PromiseResult = reason; + promise.PromiseFulfillReactions = undefined; + promise.PromiseRejectReactions = undefined; + promise.PromiseState = 'rejected'; + if (promise.PromiseIsHandled === Value.false) { + HostPromiseRejectionTracker(promise, 'reject'); + } + return TriggerPromiseReactions(reactions, reason); +} + +// #sec-triggerpromisereactions +function TriggerPromiseReactions(reactions, argument) { + // 1. For each reaction in reactions, in original insertion order, do + reactions.forEach((reaction) => { + // a. Let job be NewPromiseReactionJob(reaction, argument). + const job = NewPromiseReactionJob(reaction, argument); + // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). + HostEnqueuePromiseJob(job.Job, job.Realm); + }); + // 2. Return undefined. + return Value.undefined; +} + +// 25.6.4.5.1 #sec-promise-resolve +export function PromiseResolve(C, x) { + Assert(Type(C) === 'Object'); + if (IsPromise(x) === Value.true) { + const xConstructor = Q(Get(x, new Value('constructor'))); + if (SameValue(xConstructor, C) === Value.true) { + return x; + } + } + const promiseCapability = Q(NewPromiseCapability(C)); + Q(Call(promiseCapability.Resolve, Value.undefined, [x])); + return promiseCapability.Promise; +} + +// #sec-newpromisereactionjob +function NewPromiseReactionJob(reaction, argument) { + // 1. Let job be a new Job abstract closure with no parameters that captures + // reaction and argument and performs the following steps when called: + const job = () => { + // a. Assert: reaction is a PromiseReaction Record. + Assert(reaction instanceof PromiseReactionRecord); + // b. Let promiseCapability be reaction.[[Capability]]. + const promiseCapability = reaction.Capability; + // c. Let type be reaction.[[Type]]. + const type = reaction.Type; + // d. Let handler be reaction.[[Handler]]. + const handler = reaction.Handler; + let handlerResult; + // e. If handler is undefined, then + if (handler === Value.undefined) { + // i. If type is Fulfill, let handlerResult be NormalCompletion(argument). + if (type === 'Fulfill') { + handlerResult = new NormalCompletion(argument); + } else { + // 1. Assert: type is Reject. + Assert(type === 'Reject'); + // 2. Let handlerResult be ThrowCompletion(argument). + handlerResult = new ThrowCompletion(argument); + } + } else { + // f. let handlerResult be Call(handler, undefined, « argument »). + handlerResult = Call(handler, Value.undefined, [argument]); + } + // g. If promiseCapability is undefined, then + if (promiseCapability === Value.undefined) { + // i. Assert: handlerResult is not an abrupt completion. + Assert(!(handlerResult instanceof AbruptCompletion)); + // ii. Return NormalCompletion(empty). + return new NormalCompletion(undefined); + } + let status; + // h. If handlerResult is an abrupt completion, then + if (handlerResult instanceof AbruptCompletion) { + // i. Let status be Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »). + status = Call(promiseCapability.Reject, Value.undefined, [handlerResult.Value]); + } else { + // ii. Let status be Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »). + status = Call(promiseCapability.Resolve, Value.undefined, [EnsureCompletion(handlerResult).Value]); + } + // j. Return Completion(status). + return Completion(status); + }; + // 2. Let handlerRealm be null. + let handlerRealm = Value.null; + // 3. If reaction.[[Handler]] is not undefined, then + if (reaction.Handler !== Value.undefined) { + // a. Let getHandlerRealmResult be GetFunctionRealm(handler). + const getHandlerRealmResult = GetFunctionRealm(reaction.Handler); + // b. If getHandlerRealmResult is a normal completion, then set handlerRealm to getHandlerRealmResult.[[Value]]. + if (getHandlerRealmResult instanceof NormalCompletion) { + handlerRealm = getHandlerRealmResult.Value; + } + } + // 4. Return { [[Job]]: job, [[Realm]]: handlerRealm }. + return { Job: job, Realm: handlerRealm }; +} + +// 25.6.5.4.1 #sec-performpromisethen +export function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability) { + // 1. Assert: IsPromise(promise) is true. + Assert(IsPromise(promise) === Value.true); + // 2. If resultCapability is present, then + if (resultCapability) { + // a. Assert: resultCapability is a PromiseCapability Record. + Assert(resultCapability instanceof PromiseCapabilityRecord); + } else { + // a. Set resultCapability to undefined. + resultCapability = Value.undefined; + } + // 4. If IsCallable(onFulfilled) is false, then + if (IsCallable(onFulfilled) === Value.false) { + // a. Set onFulfilled to undefined. + onFulfilled = Value.undefined; + } + // 5. If IsCallable(onRejected) is false, then + if (IsCallable(onRejected) === Value.false) { + // a. Set onRejected to undefined. + onRejected = Value.undefined; + } + // 6. Let fulfillReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Fulfill, [[Handler]]: onFulfilled }. + const fulfillReaction = new PromiseReactionRecord({ + Capability: resultCapability, + Type: 'Fulfill', + Handler: onFulfilled, + }); + // 7. Let rejectReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Reject, [[Handler]]: onRejected }. + const rejectReaction = new PromiseReactionRecord({ + Capability: resultCapability, + Type: 'Reject', + Handler: onRejected, + }); + // 8. If promise.[[PromiseState]] is pending, then + if (promise.PromiseState === 'pending') { + // a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]]. + promise.PromiseFulfillReactions.push(fulfillReaction); + // b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]]. + promise.PromiseRejectReactions.push(rejectReaction); + } else if (promise.PromiseState === 'fulfilled') { + // a. Let value be promise.[[PromiseResult]]. + const value = promise.PromiseResult; + // b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value). + const fulfillJob = NewPromiseReactionJob(fulfillReaction, value); + // c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]). + HostEnqueuePromiseJob(fulfillJob.Job, fulfillJob.Realm); + } else { + // a. Assert: The value of promise.[[PromiseState]] is rejected. + Assert(promise.PromiseState === 'rejected'); + // b. Let reason be promise.[[PromiseResult]]. + const reason = promise.PromiseResult; + // c. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "handle"). + if (promise.PromiseIsHandled === Value.false) { + HostPromiseRejectionTracker(promise, 'handle'); + } + // d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason). + const rejectJob = NewPromiseReactionJob(rejectReaction, reason); + // e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]). + HostEnqueuePromiseJob(rejectJob.Job, rejectJob.Realm); + } + // 11. Set promise.[[PromiseIsHandled]] to true. + promise.PromiseIsHandled = Value.true; + // 12. If resultCapability is undefined, then + if (resultCapability === Value.undefined) { + // a. Return undefined. + return Value.undefined; + } else { + // return resultCapability.[[Promise]]. + return resultCapability.Promise; + } +} diff --git a/src/engine262/src/abstract-ops/proxy-objects.mjs b/src/engine262/src/abstract-ops/proxy-objects.mjs new file mode 100644 index 0000000..85dec5f --- /dev/null +++ b/src/engine262/src/abstract-ops/proxy-objects.mjs @@ -0,0 +1,579 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { ValueSet } from '../helpers.mjs'; +import { + Assert, + MakeBasicObject, + IsConstructor, + IsCallable, + Call, + Construct, + GetMethod, + CreateArrayFromList, + CreateListFromArrayLike, + IsExtensible, + IsPropertyKey, + SameValue, + ToBoolean, + ToPropertyDescriptor, + FromPropertyDescriptor, + CompletePropertyDescriptor, + IsCompatiblePropertyDescriptor, + IsDataDescriptor, + IsAccessorDescriptor, +} from './all.mjs'; + +// #sec-proxy-object-internal-methods-and-internal-slots-getprototypeof +function ProxyGetPrototypeOf() { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getPrototypeOf'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('getPrototypeOf'))); + if (trap === Value.undefined) { + return Q(target.GetPrototypeOf()); + } + const handlerProto = Q(Call(trap, handler, [target])); + if (Type(handlerProto) !== 'Object' && Type(handlerProto) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfInvalid'); + } + const extensibleTarget = Q(IsExtensible(target)); + if (extensibleTarget === Value.true) { + return handlerProto; + } + const targetProto = Q(target.GetPrototypeOf()); + if (SameValue(handlerProto, targetProto) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfNonExtensible'); + } + return handlerProto; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v +function ProxySetPrototypeOf(V) { + const O = this; + + Assert(Type(V) === 'Object' || Type(V) === 'Null'); + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'setPrototypeOf'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('setPrototypeOf'))); + if (trap === Value.undefined) { + return Q(target.SetPrototypeOf(V)); + } + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, V]))); + if (booleanTrapResult === Value.false) { + return Value.false; + } + const extensibleTarget = Q(IsExtensible(target)); + if (extensibleTarget === Value.true) { + return Value.true; + } + const targetProto = Q(target.GetPrototypeOf()); + if (SameValue(V, targetProto) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxySetPrototypeOfNonExtensible'); + } + return Value.true; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-isextensible +function ProxyIsExtensible() { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'isExtensible'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('isExtensible'))); + if (trap === Value.undefined) { + return Q(IsExtensible(target)); + } + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target]))); + const targetResult = Q(IsExtensible(target)); + if (SameValue(booleanTrapResult, targetResult) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyIsExtensibleInconsistent', targetResult); + } + return booleanTrapResult; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-preventextensions +function ProxyPreventExtensions() { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'preventExtensions'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('preventExtensions'))); + if (trap === Value.undefined) { + return Q(target.PreventExtensions()); + } + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target]))); + if (booleanTrapResult === Value.true) { + const extensibleTarget = Q(IsExtensible(target)); + if (extensibleTarget === Value.true) { + return surroundingAgent.Throw('TypeError', 'ProxyPreventExtensionsExtensible'); + } + } + return booleanTrapResult; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p +function ProxyGetOwnProperty(P) { + const O = this; + + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Let handler be O.[[ProxyHandler]]. + const handler = O.ProxyHandler; + // 3. If handler is null, throw a TypeError exception. + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getOwnPropertyDescriptor'); + } + // 4. Assert: Type(Handler) is Object. + Assert(Type(handler) === 'Object'); + // 5. Let target be O.[[ProxyTarget]]. + const target = O.ProxyTarget; + // 6. Let trap be ? Getmethod(handler, "getOwnPropertyDescriptor"). + const trap = Q(GetMethod(handler, new Value('getOwnPropertyDescriptor'))); + // 7. If trap is undefined, then + if (trap === Value.undefined) { + // a. Return ? target.[[GetOwnProperty]](P). + return Q(target.GetOwnProperty(P)); + } + // 8. Let trapResultObj be ? Call(trap, handler, « target, P »). + const trapResultObj = Q(Call(trap, handler, [target, P])); + // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. + if (Type(trapResultObj) !== 'Object' && Type(trapResultObj) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorInvalid', P); + } + // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + const targetDesc = Q(target.GetOwnProperty(P)); + // 11. If trapResultObj is undefined, then + if (trapResultObj === Value.undefined) { + // a. If targetDesc is undefined, return undefined. + if (targetDesc === Value.undefined) { + return Value.undefined; + } + // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (targetDesc.Configurable === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorUndefined', P); + } + // c. Let extensibleTarget be ? IsExtensible(target). + const extensibleTarget = Q(IsExtensible(target)); + // d. If extensibleTarget is false, throw a TypeError exception. + if (extensibleTarget === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonExtensible', P); + } + // e. Return undefined. + return Value.undefined; + } + // 12. Let extensibleTarget be ? IsExtensible(target). + const extensibleTarget = Q(IsExtensible(target)); + // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). + const resultDesc = Q(ToPropertyDescriptor(trapResultObj)); + // 14. Call CompletePropertyDescriptor(resultDesc). + CompletePropertyDescriptor(resultDesc); + // 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). + const valid = IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc); + // 16. If valid is false, throw a TypeError exception. + if (valid === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorIncompatible', P); + } + // 17. If resultDesc.[[Configurable]] is false, then + if (resultDesc.Configurable === Value.false) { + // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then + if (targetDesc === Value.undefined || targetDesc.Configurable === Value.true) { + // i. Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurable', P); + } + // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + if ('Writable' in resultDesc && resultDesc.Writable === Value.false) { + // i. If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (targetDesc.Writable === Value.true) { + return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurableWritable', P); + } + } + } + // 18. Return resultDesc. + return resultDesc; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc +function ProxyDefineOwnProperty(P, Desc) { + const O = this; + + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Let handler be O.[[ProxyHandler]]. + const handler = O.ProxyHandler; + // 3. If handler is null, throw a TypeError exception. + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'defineProperty'); + } + // 4. Assert: Type(handler) is Object. + Assert(Type(handler) === 'Object'); + // 5. Let target be O.[[ProxyTarget]]. + const target = O.ProxyTarget; + // 6. Let trap be ? GetMethod(handler, "defineProperty"). + const trap = Q(GetMethod(handler, new Value('defineProperty'))); + // 7. If trap is undefined, then + if (trap === Value.undefined) { + // a. Return ? target.[[DefineOwnProperty]](P, Desc). + return Q(target.DefineOwnProperty(P, Desc)); + } + // 8. Let descObj be FromPropertyDescriptor(Desc). + const descObj = FromPropertyDescriptor(Desc); + // 9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)). + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, descObj]))); + // 10. If booleanTrapResult is false, return false. + if (booleanTrapResult === Value.false) { + return Value.false; + } + // 11. Let targetDesc be ? target.[[GetOwnProperty]](P). + const targetDesc = Q(target.GetOwnProperty(P)); + // 12. Let extensibleTarget be ? IsExtensible(target). + const extensibleTarget = Q(IsExtensible(target)); + let settingConfigFalse; + // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then + if (Desc.Configurable !== undefined && Desc.Configurable === Value.false) { + // a. Let settingConfigFalse be true. + settingConfigFalse = true; + } else { + // Else, let settingConfigFalse be false. + settingConfigFalse = false; + } + // 15. If targetDesc is undefined, then + if (targetDesc === Value.undefined) { + // a. If extensibleTarget is false, throw a TypeError exception. + if (extensibleTarget === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonExtensible', P); + } + // b. If settingConfigFalse is true, throw a TypeError exception. + if (settingConfigFalse === true) { + return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P); + } + } else { + // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception. + if (IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyIncompatible', P); + } + // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. + if (settingConfigFalse === true && targetDesc.Configurable === Value.true) { + return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P); + } + // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then + if (IsDataDescriptor(targetDesc) + && targetDesc.Configurable === Value.false + && targetDesc.Writable === Value.true) { + // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + if ('Writable' in Desc && Desc.Writable === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurableWritable', P); + } + } + } + return Value.true; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p +function ProxyHasProperty(P) { + const O = this; + + Assert(IsPropertyKey(P)); + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'has'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('has'))); + if (trap === Value.undefined) { + return Q(target.HasProperty(P)); + } + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P]))); + if (booleanTrapResult === Value.false) { + const targetDesc = Q(target.GetOwnProperty(P)); + if (targetDesc !== Value.undefined) { + if (targetDesc.Configurable === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyHasNonConfigurable', P); + } + const extensibleTarget = Q(IsExtensible(target)); + if (extensibleTarget === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyHasNonExtensible', P); + } + } + } + return booleanTrapResult; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver +function ProxyGet(P, Receiver) { + const O = this; + + Assert(IsPropertyKey(P)); + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'get'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('get'))); + if (trap === Value.undefined) { + return Q(target.Get(P, Receiver)); + } + const trapResult = Q(Call(trap, handler, [target, P, Receiver])); + const targetDesc = Q(target.GetOwnProperty(P)); + if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) { + if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) { + if (SameValue(trapResult, targetDesc.Value) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableData', P); + } + } + if (IsAccessorDescriptor(targetDesc) === true && targetDesc.Get === Value.undefined) { + if (trapResult !== Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableAccessor', P); + } + } + } + return trapResult; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver +function ProxySet(P, V, Receiver) { + const O = this; + + Assert(IsPropertyKey(P)); + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'set'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('set'))); + if (trap === Value.undefined) { + return Q(target.Set(P, V, Receiver)); + } + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, V, Receiver]))); + if (booleanTrapResult === Value.false) { + return Value.false; + } + const targetDesc = Q(target.GetOwnProperty(P)); + if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) { + if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) { + if (SameValue(V, targetDesc.Value) === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxySetFrozenData', P); + } + } + if (IsAccessorDescriptor(targetDesc) === true) { + if (targetDesc.Set === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ProxySetFrozenAccessor', P); + } + } + } + return Value.true; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-delete-p +function ProxyDelete(P) { + const O = this; + + // 1. Assert: IsPropertyKey(P) is true. + Assert(IsPropertyKey(P)); + // 2. Let handler be O.[[ProxyHandler]]. + const handler = O.ProxyHandler; + // 3. If handler is null, throw a TypeError exception. + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'deleteProperty'); + } + // 4. Assert: Type(handler) is Object. + Assert(Type(handler) === 'Object'); + // 5. Let target be O.[[ProxyTarget]]. + const target = O.ProxyTarget; + // 6. Let trap be ? GetMethod(handler, "deleteProperty"). + const trap = Q(GetMethod(handler, new Value('deleteProperty'))); + // 7. If trap is undefined, then + if (trap === Value.undefined) { + // a. Return ? target.[[Delete]](P). + return Q(target.Delete(P)); + } + // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). + const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P]))); + // 9. If booleanTrapResult is false, return false. + if (booleanTrapResult === Value.false) { + return Value.false; + } + // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + const targetDesc = Q(target.GetOwnProperty(P)); + // 11. If targetDesc is undefined, return true. + if (targetDesc === Value.undefined) { + return Value.true; + } + // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (targetDesc.Configurable === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonConfigurable', P); + } + // 13. Let extensibleTarget be ? IsExtensible(target). + const extensibleTarget = Q(IsExtensible(target)); + // 14. If extensibleTarget is false, throw a TypeError exception. + if (extensibleTarget === Value.false) { + return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonExtensible', P); + } + // 15. Return true. + return Value.true; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys +function ProxyOwnPropertyKeys() { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'ownKeys'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('ownKeys'))); + if (trap === Value.undefined) { + return Q(target.OwnPropertyKeys()); + } + const trapResultArray = Q(Call(trap, handler, [target])); + const trapResult = Q(CreateListFromArrayLike(trapResultArray, ['String', 'Symbol'])); + if (new ValueSet(trapResult).size !== trapResult.length) { + return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysDuplicateEntries'); + } + const extensibleTarget = Q(IsExtensible(target)); + const targetKeys = Q(target.OwnPropertyKeys()); + // Assert: targetKeys is a List containing only String and Symbol values. + // Assert: targetKeys contains no duplicate entries. + const targetConfigurableKeys = []; + const targetNonconfigurableKeys = []; + for (const key of targetKeys) { + const desc = Q(target.GetOwnProperty(key)); + if (desc !== Value.undefined && desc.Configurable === Value.false) { + targetNonconfigurableKeys.push(key); + } else { + targetConfigurableKeys.push(key); + } + } + if (extensibleTarget === Value.true && targetNonconfigurableKeys.length === 0) { + return trapResult; + } + const uncheckedResultKeys = new ValueSet(trapResult); + for (const key of targetNonconfigurableKeys) { + if (!uncheckedResultKeys.has(key)) { + return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'non-configurable key'); + } + uncheckedResultKeys.delete(key); + } + if (extensibleTarget === Value.true) { + return trapResult; + } + for (const key of targetConfigurableKeys) { + if (!uncheckedResultKeys.has(key)) { + return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'configurable key'); + } + uncheckedResultKeys.delete(key); + } + if (uncheckedResultKeys.size > 0) { + return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysNonExtensible'); + } + return trapResult; +} + +// #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist +function ProxyCall(thisArgument, argumentsList) { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'apply'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + const trap = Q(GetMethod(handler, new Value('apply'))); + if (trap === Value.undefined) { + return Q(Call(target, thisArgument, argumentsList)); + } + const argArray = X(CreateArrayFromList(argumentsList)); + return Q(Call(trap, handler, [target, thisArgument, argArray])); +} + +// #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget +function ProxyConstruct(argumentsList, newTarget) { + const O = this; + + const handler = O.ProxyHandler; + if (handler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'construct'); + } + Assert(Type(handler) === 'Object'); + const target = O.ProxyTarget; + Assert(IsConstructor(target) === Value.true); + const trap = Q(GetMethod(handler, new Value('construct'))); + if (trap === Value.undefined) { + return Q(Construct(target, argumentsList, newTarget)); + } + const argArray = X(CreateArrayFromList(argumentsList)); + const newObj = Q(Call(trap, handler, [target, argArray, newTarget])); + if (Type(newObj) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', newObj); + } + return newObj; +} + +export function isProxyExoticObject(O) { + return 'ProxyHandler' in O; +} + +// #sec-proxycreate +export function ProxyCreate(target, handler) { + // 1. If Type(target) is not Object, throw a TypeError exception. + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'target'); + } + // 2. If Type(handler) is not Object, throw a TypeError exception. + if (Type(handler) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'handler'); + } + // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »). + const P = X(MakeBasicObject(['ProxyHandler', 'ProxyTarget'])); + // 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 9.5. + P.GetPrototypeOf = ProxyGetPrototypeOf; + P.SetPrototypeOf = ProxySetPrototypeOf; + P.IsExtensible = ProxyIsExtensible; + P.PreventExtensions = ProxyPreventExtensions; + P.GetOwnProperty = ProxyGetOwnProperty; + P.DefineOwnProperty = ProxyDefineOwnProperty; + P.HasProperty = ProxyHasProperty; + P.Get = ProxyGet; + P.Set = ProxySet; + P.Delete = ProxyDelete; + P.OwnPropertyKeys = ProxyOwnPropertyKeys; + // 5. If IsCallable(target) is true, then + if (IsCallable(target) === Value.true) { + // a. Set P.[[Call]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist. + P.Call = ProxyCall; + // b. If IsConstructor(target) is true, then + if (IsConstructor(target) === Value.true) { + // i. Set P.[[Construct]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget. + P.Construct = ProxyConstruct; + } + } + // 6. Set P.[[ProxyTarget]] to target. + P.ProxyTarget = target; + // 7. Set P.[[ProxyHandler]] to handler. + P.ProxyHandler = handler; + // 8. Return P. + return P; +} diff --git a/src/engine262/src/abstract-ops/reference-operations.mjs b/src/engine262/src/abstract-ops/reference-operations.mjs new file mode 100644 index 0000000..af8e3bb --- /dev/null +++ b/src/engine262/src/abstract-ops/reference-operations.mjs @@ -0,0 +1,142 @@ +import { surroundingAgent } from '../engine.mjs'; +import { PrimitiveValue, Type, Value } from '../value.mjs'; +import { + NormalCompletion, + Q, + ReturnIfAbrupt, + X, +} from '../completion.mjs'; +import { + Assert, + GetGlobalObject, + ToObject, + Set, +} from './all.mjs'; + + +// 6.2.4.1 #sec-getbase +export function GetBase(V) { + Assert(Type(V) === 'Reference'); + return V.BaseValue; +} + +// 6.2.4.2 #sec-getreferencedname +export function GetReferencedName(V) { + Assert(Type(V) === 'Reference'); + return V.ReferencedName; +} + +// 6.2.4.3 #sec-isstrictreference +export function IsStrictReference(V) { + Assert(Type(V) === 'Reference'); + return V.StrictReference; +} + +// 6.2.4.4 #sec-hasprimitivebase +export function HasPrimitiveBase(V) { + Assert(Type(V) === 'Reference'); + if (V.BaseValue instanceof PrimitiveValue) { + return Value.true; + } + return Value.false; +} + +// 6.2.4.5 #sec-ispropertyreference +export function IsPropertyReference(V) { + Assert(Type(V) === 'Reference'); + if (Type(V.BaseValue) === 'Object' || HasPrimitiveBase(V) === Value.true) { + return Value.true; + } + return Value.false; +} + +// 6.2.4.6 #sec-isunresolvablereference +export function IsUnresolvableReference(V) { + Assert(Type(V) === 'Reference'); + if (V.BaseValue === Value.undefined) { + return Value.true; + } + return Value.false; +} + +// 6.2.4.7 #sec-issuperreference +export function IsSuperReference(V) { + // 1. Assert: Type(V) is Reference. + Assert(Type(V) === 'Reference'); + // 2. If V has a thisValue component, return true; otherwise return false. + return 'thisValue' in V ? Value.true : Value.false; +} + +// 6.2.4.8 #sec-getvalue +export function GetValue(V) { + ReturnIfAbrupt(V); + if (Type(V) !== 'Reference') { + return V; + } + let base = GetBase(V); + if (IsUnresolvableReference(V) === Value.true) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V)); + } + if (IsPropertyReference(V) === Value.true) { + if (HasPrimitiveBase(V) === Value.true) { + Assert(base !== Value.undefined && base !== Value.null); + base = X(ToObject(base)); + } + return Q(base.Get(GetReferencedName(V), GetThisValue(V))); + } else { + return Q(base.GetBindingValue(GetReferencedName(V), IsStrictReference(V))); + } +} + +// 6.2.4.9 #sec-putvalue +export function PutValue(V, W) { + ReturnIfAbrupt(V); + ReturnIfAbrupt(W); + if (Type(V) !== 'Reference') { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', V); + } + let base = GetBase(V); + if (IsUnresolvableReference(V) === Value.true) { + if (IsStrictReference(V) === Value.true) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V)); + } + const globalObj = GetGlobalObject(); + return Q(Set(globalObj, GetReferencedName(V), W, Value.false)); + } else if (IsPropertyReference(V) === Value.true) { + if (HasPrimitiveBase(V) === Value.true) { + Assert(Type(base) !== 'Undefined' && Type(base) !== 'Null'); + base = X(ToObject(base)); + } + const succeeded = Q(base.Set(GetReferencedName(V), W, GetThisValue(V))); + if (succeeded === Value.false && IsStrictReference(V) === Value.true) { + return surroundingAgent.Throw('TypeError', 'CannotSetProperty', GetReferencedName(V), base); + } + return new NormalCompletion(Value.undefined); + } else { + return Q(base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V))); + } +} + +// 6.2.4.10 #sec-getthisvalue +export function GetThisValue(V) { + // 1. Assert: IsPropertyReference(V) is true. + Assert(IsPropertyReference(V) === Value.true); + // 2. If IsSuperReference(V) is true, then + if (IsSuperReference(V) === Value.true) { + // a. Return the value of the thisValue component of the reference V. + return V.thisValue; + } + // 3. Return GetBase(V). + return GetBase(V); +} + +// 6.2.4.11 #sec-initializereferencedbinding +export function InitializeReferencedBinding(V, W) { + ReturnIfAbrupt(V); + ReturnIfAbrupt(W); + Assert(Type(V) === 'Reference'); + Assert(IsUnresolvableReference(V) === Value.false); + const base = GetBase(V); + Assert(Type(base) === 'EnvironmentRecord'); + return base.InitializeBinding(GetReferencedName(V), W); +} diff --git a/src/engine262/src/abstract-ops/regexp-objects.mjs b/src/engine262/src/abstract-ops/regexp-objects.mjs new file mode 100644 index 0000000..e761036 --- /dev/null +++ b/src/engine262/src/abstract-ops/regexp-objects.mjs @@ -0,0 +1,93 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + ParseRegExp, +} from '../parse.mjs'; +import { + Descriptor, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { + getMatcher, +} from '../runtime-semantics/all.mjs'; +import { + Assert, + DefinePropertyOrThrow, + OrdinaryCreateFromConstructor, + Set, + ToString, +} from './all.mjs'; + + +// https://tc39.es/proposal-regexp-match-indices/#sec-match-records +export class MatchRecord { + constructor(StartIndex, EndIndex) { + Assert(Number.isInteger(StartIndex) && StartIndex >= 0); + Assert(Number.isInteger(EndIndex) && EndIndex >= StartIndex); + this.StartIndex = StartIndex; + this.EndIndex = EndIndex; + } +} + +// 21.2.3.2.1 #sec-regexpalloc +export function RegExpAlloc(newTarget) { + const obj = Q(OrdinaryCreateFromConstructor(newTarget, '%RegExp.prototype%', ['RegExpMatcher', 'OriginalSource', 'OriginalFlags'])); + X(DefinePropertyOrThrow(obj, new Value('lastIndex'), Descriptor({ + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + return obj; +} + +// 21.2.3.2.2 #sec-regexpinitialize +export function RegExpInitialize(obj, pattern, flags) { + let P; + if (pattern === Value.undefined) { + P = new Value(''); + } else { + P = Q(ToString(pattern)); + } + + let F; + if (flags === Value.undefined) { + F = new Value(''); + } else { + F = Q(ToString(flags)); + } + + const f = F.stringValue(); + if (/^[gimsuy]*$/.test(f) === false || (new globalThis.Set(f).size !== f.length)) { + return surroundingAgent.Throw('SyntaxError', 'InvalidRegExpFlags', f); + } + + let parsed; + try { + parsed = ParseRegExp(P.stringValue(), F.stringValue()); + } catch (e) { + return surroundingAgent.Throw('SyntaxError', 'Raw', e.message); + } + + obj.OriginalSource = P; + obj.OriginalFlags = F; + obj.RegExpMatcher = getMatcher(parsed, F.stringValue()); + obj.parsedRegExp = parsed; + + Q(Set(obj, new Value('lastIndex'), new Value(0), Value.true)); + return obj; +} + +// 21.2.3.2.3 #sec-regexpcreate +export function RegExpCreate(P, F) { + const obj = Q(RegExpAlloc(surroundingAgent.intrinsic('%RegExp%'))); + return Q(RegExpInitialize(obj, P, F)); +} + +// 21.2.3.2.4 #sec-escaperegexppattern +export function EscapeRegExpPattern(P, F) { + // TODO: implement this without host + const re = new RegExp(P.stringValue(), F.stringValue()); + return new Value(re.source); +} diff --git a/src/engine262/src/abstract-ops/source-code.mjs b/src/engine262/src/abstract-ops/source-code.mjs new file mode 100644 index 0000000..1388994 --- /dev/null +++ b/src/engine262/src/abstract-ops/source-code.mjs @@ -0,0 +1,82 @@ +import { X } from '../completion.mjs'; +import { Value } from '../value.mjs'; +import { + Assert, + isLeadingSurrogate, + isTrailingSurrogate, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 10 #sec-ecmascript-language-source-code + +// 10.1.1 #sec-utf16encoding +export function UTF16Encoding(cp) { + Assert(cp >= 0 && cp <= 0x10FFFF); + if (cp <= 0xFFFF) { + return [cp]; + } + const cu1 = Math.floor((cp - 0x10000) / 0x400) + 0xD800; + const cu2 = ((cp - 0x10000) % 0x400) + 0xDC00; + return [cu1, cu2]; +} + +// 10.1.2 #sec-utf16encode +export function UTF16Encode(text) { + return new Value(text.map(UTF16Encoding).join('')); +} + +// 10.1.3 #sec-utf16decodesurrogatepair +export function UTF16DecodeSurrogatePair(lead, trail) { + Assert(isLeadingSurrogate(lead) && isTrailingSurrogate(trail)); + const cp = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000; + return cp; +} + +// 10.1.4 #sec-codepointat +export function CodePointAt(string, position) { + const size = string.stringValue().length; + Assert(position >= 0 && position < size); + const first = string.stringValue().charCodeAt(position); + let cp = first; + if (!isLeadingSurrogate(first) && !isTrailingSurrogate(first)) { + return { + CodePoint: new Value(cp), + CodeUnitCount: new Value(1), + IsUnpairedSurrogate: Value.false, + }; + } + if (isTrailingSurrogate(first) || position + 1 === size) { + return { + CodePoint: new Value(cp), + CodeUnitCount: new Value(1), + IsUnpairedSurrogate: Value.true, + }; + } + const second = string.stringValue().charCodeAt(position + 1); + if (!isTrailingSurrogate(second)) { + return { + CodePoint: new Value(cp), + CodeUnitCount: new Value(1), + IsUnpairedSurrogate: Value.true, + }; + } + cp = X(UTF16DecodeSurrogatePair(first, second)); + return { + CodePoint: new Value(cp), + CodeUnitCount: new Value(2), + IsUnpairedSurrogate: Value.false, + }; +} + +// 10.1.5 #sec-utf16decodestring +export function UTF16DecodeString(string) { + const codePoints = []; + const size = string.stringValue().length; + let position = 0; + while (position < size) { + const cp = X(CodePointAt(string, position)); + codePoints.push(cp.CodePoint); + position += cp.CodeUnitCount; + } + return codePoints; +} diff --git a/src/engine262/src/abstract-ops/spec-types.mjs b/src/engine262/src/abstract-ops/spec-types.mjs new file mode 100644 index 0000000..4cc15b7 --- /dev/null +++ b/src/engine262/src/abstract-ops/spec-types.mjs @@ -0,0 +1,210 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + DataBlock, + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { NormalCompletion, Q, X } from '../completion.mjs'; +import { + Assert, + CreateDataProperty, + Get, + HasProperty, + IsCallable, + OrdinaryObjectCreate, + ToBoolean, +} from './all.mjs'; + + +// 6.2.5.1 IsAccessorDescriptor +export function IsAccessorDescriptor(Desc) { + if (Type(Desc) === 'Undefined') { + return false; + } + + if (Desc.Get === undefined && Desc.Set === undefined) { + return false; + } + + return true; +} + +// 6.2.5.2 IsDataDescriptor +export function IsDataDescriptor(Desc) { + if (Type(Desc) === 'Undefined') { + return false; + } + + if (Desc.Value === undefined && Desc.Writable === undefined) { + return false; + } + + return true; +} + +// 6.2.5.3 IsGenericDescriptor +export function IsGenericDescriptor(Desc) { + if (Type(Desc) === 'Undefined') { + return false; + } + + if (!IsAccessorDescriptor(Desc) && !IsDataDescriptor(Desc)) { + return true; + } + + return false; +} + +// 6.2.5.4 #sec-frompropertydescriptor +export function FromPropertyDescriptor(Desc) { + if (Type(Desc) === 'Undefined') { + return Value.undefined; + } + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + if (Desc.Value !== undefined) { + X(CreateDataProperty(obj, new Value('value'), Desc.Value)); + } + if (Desc.Writable !== undefined) { + X(CreateDataProperty(obj, new Value('writable'), Desc.Writable)); + } + if (Desc.Get !== undefined) { + X(CreateDataProperty(obj, new Value('get'), Desc.Get)); + } + if (Desc.Set !== undefined) { + X(CreateDataProperty(obj, new Value('set'), Desc.Set)); + } + if (Desc.Enumerable !== undefined) { + X(CreateDataProperty(obj, new Value('enumerable'), Desc.Enumerable)); + } + if (Desc.Configurable !== undefined) { + X(CreateDataProperty(obj, new Value('configurable'), Desc.Configurable)); + } + // Assert: All of the above CreateDataProperty operations return true. + return obj; +} + +// 6.2.5.5 #sec-topropertydescriptor +export function ToPropertyDescriptor(Obj) { + if (Type(Obj) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', Obj); + } + + const desc = Descriptor({}); + const hasEnumerable = Q(HasProperty(Obj, new Value('enumerable'))); + if (hasEnumerable === Value.true) { + const enumerable = ToBoolean(Q(Get(Obj, new Value('enumerable')))); + desc.Enumerable = enumerable; + } + const hasConfigurable = Q(HasProperty(Obj, new Value('configurable'))); + if (hasConfigurable === Value.true) { + const conf = ToBoolean(Q(Get(Obj, new Value('configurable')))); + desc.Configurable = conf; + } + const hasValue = Q(HasProperty(Obj, new Value('value'))); + if (hasValue === Value.true) { + const value = Q(Get(Obj, new Value('value'))); + desc.Value = value; + } + const hasWritable = Q(HasProperty(Obj, new Value('writable'))); + if (hasWritable === Value.true) { + const writable = ToBoolean(Q(Get(Obj, new Value('writable')))); + desc.Writable = writable; + } + const hasGet = Q(HasProperty(Obj, new Value('get'))); + if (hasGet === Value.true) { + const getter = Q(Get(Obj, new Value('get'))); + if (IsCallable(getter) === Value.false && Type(getter) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'NotAFunction', getter); + } + desc.Get = getter; + } + const hasSet = Q(HasProperty(Obj, new Value('set'))); + if (hasSet === Value.true) { + const setter = Q(Get(Obj, new Value('set'))); + if (IsCallable(setter) === Value.false && Type(setter) !== 'Undefined') { + return surroundingAgent.Throw('TypeError', 'NotAFunction', setter); + } + desc.Set = setter; + } + if (desc.Get !== undefined || desc.Set !== undefined) { + if (desc.Value !== undefined || desc.Writable !== undefined) { + return surroundingAgent.Throw('TypeError', 'InvalidPropertyDescriptor'); + } + } + return desc; +} + +// 6.2.5.6 #sec-completepropertydescriptor +export function CompletePropertyDescriptor(Desc) { + Assert(Type(Desc) === 'Descriptor'); + const like = Descriptor({ + Value: Value.undefined, + Writable: false, + Get: Value.undefined, + Set: Value.undefined, + Enumerable: false, + Configurable: false, + }); + if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) { + if (Desc.Value === undefined) { + Desc.Value = like.Value; + } + if (Desc.Writable === undefined) { + Desc.Writable = like.Writable; + } + } else { + if (Desc.Get === undefined) { + Desc.Get = like.Get; + } + if (Desc.Set === undefined) { + Desc.Set = like.Set; + } + } + if (Desc.Enumerable === undefined) { + Desc.Enumerable = like.Enumerable; + } + if (Desc.Configurable === undefined) { + Desc.Configurable = like.Configurable; + } + return Desc; +} + +// 6.2.7.1 #sec-createbytedatablock +export function CreateByteDataBlock(size) { + size = size.numberValue(); + Assert(size >= 0); + let db; + try { + db = new DataBlock(size); + } catch (err) { + return surroundingAgent.Throw('RangeError', 'CannotAllocateDataBlock'); + } + return db; +} + +// 6.2.7.3 #sec-copydatablockbytes +export function CopyDataBlockBytes(toBlock, toIndex, fromBlock, fromIndex, count) { + Assert(fromBlock !== toBlock); + Assert(Type(fromBlock) === 'Data Block' || Type(fromBlock) === 'Shared Data Block'); + Assert(Type(toBlock) === 'Data Block' || Type(toBlock) === 'Shared Data Block'); + Assert(Number.isSafeInteger(fromIndex) && fromIndex >= 0); + Assert(Number.isSafeInteger(toIndex) && toIndex >= 0); + Assert(Number.isSafeInteger(count) && count >= 0); + const fromSize = fromBlock.byteLength; + Assert(fromIndex + count <= fromSize); + const toSize = toBlock.byteLength; + Assert(toIndex + count <= toSize); + while (count > 0) { + if (Type(fromBlock) === 'Shared Data Block') { + Assert(false); + } else { + Assert(Type(toBlock) !== 'Shared Data Block'); + toBlock[toIndex] = fromBlock[fromIndex]; + } + toIndex += 1; + fromIndex += 1; + count -= 1; + } + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/abstract-ops/string-objects.mjs b/src/engine262/src/abstract-ops/string-objects.mjs new file mode 100644 index 0000000..79ab987 --- /dev/null +++ b/src/engine262/src/abstract-ops/string-objects.mjs @@ -0,0 +1,145 @@ +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { X } from '../completion.mjs'; +import { + Assert, + CanonicalNumericIndexString, + DefinePropertyOrThrow, + IsInteger, + IsPropertyKey, + MakeBasicObject, + OrdinaryGetOwnProperty, + OrdinaryDefineOwnProperty, + IsCompatiblePropertyDescriptor, + ToInteger, + isArrayIndex, +} from './all.mjs'; + +function StringExoticGetOwnProperty(P) { + const S = this; + Assert(IsPropertyKey(P)); + const desc = OrdinaryGetOwnProperty(S, P); + if (Type(desc) !== 'Undefined') { + return desc; + } + return X(StringGetOwnProperty(S, P)); +} + +function StringExoticDefineOwnProperty(P, Desc) { + const S = this; + Assert(IsPropertyKey(P)); + const stringDesc = X(StringGetOwnProperty(S, P)); + if (Type(stringDesc) !== 'Undefined') { + const extensible = S.Extensible; + return X(IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc)); + } + return X(OrdinaryDefineOwnProperty(S, P, Desc)); +} + +function StringExoticOwnPropertyKeys() { + const O = this; + const keys = []; + const str = O.StringData; + Assert(Type(str) === 'String'); + const len = str.stringValue().length; + + for (let i = 0; i < len; i += 1) { + keys.push(new Value(`${i}`)); + } + + // For each own property key P of O such that P is an array index and + // ToInteger(P) ≥ len, in ascending numeric index order, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + // This is written with two nested ifs to work around https://github.com/devsnek/engine262/issues/24 + if (isArrayIndex(P)) { + if (X(ToInteger(P)).numberValue() >= len) { + keys.push(P); + } + } + } + + // For each own property key P of O such that Type(P) is String and + // P is not an array index, in ascending chronological order of property creation, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + if (Type(P) === 'String' && isArrayIndex(P) === false) { + keys.push(P); + } + } + + // For each own property key P of O such that Type(P) is Symbol, + // in ascending chronological order of property creation, do + // Add P as the last element of keys. + for (const P of O.properties.keys()) { + if (Type(P) === 'Symbol') { + keys.push(P); + } + } + + return keys; +} + +// 9.4.3.4 #sec-stringcreate +export function StringCreate(value, prototype) { + // 1. Assert: Type(value) is String. + Assert(Type(value) === 'String'); + // 2. Let S be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[StringData]] »). + const S = X(MakeBasicObject(['Prototype', 'Extensible', 'StringData'])); + // 3. Set S.[[Prototype]] to prototype. + S.Prototype = prototype; + // 4. Set S.[[StringData]] to value. + S.StringData = value; + // 5. Set S.[[GetOwnProperty]] as specified in 9.4.3.1. + S.GetOwnProperty = StringExoticGetOwnProperty; + // 6. Set S.[[DefineOwnProperty]] as specified in 9.4.3.2. + S.DefineOwnProperty = StringExoticDefineOwnProperty; + // 7. Set S.[[OwnPropertyKeys]] as specified in 9.4.3.3. + S.OwnPropertyKeys = StringExoticOwnPropertyKeys; + // 8. Let length be the number of code unit elements in value. + const length = new Value(value.stringValue().length); + // 9. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). + X(DefinePropertyOrThrow(S, new Value('length'), Descriptor({ + Value: length, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + // 10. Return S. + return S; +} + +// 9.4.3.5 #sec-stringgetownproperty +export function StringGetOwnProperty(S, P) { + Assert(Type(S) === 'Object' && 'StringData' in S); + Assert(IsPropertyKey(P)); + if (Type(P) !== 'String') { + return Value.undefined; + } + const index = X(CanonicalNumericIndexString(P)); + if (Type(index) === 'Undefined') { + return Value.undefined; + } + if (IsInteger(index) === Value.false) { + return Value.undefined; + } + if (Object.is(index.numberValue(), -0)) { + return Value.undefined; + } + const str = S.StringData; + Assert(Type(str) === 'String'); + const len = str.stringValue().length; + if (index.numberValue() < 0 || len <= index.numberValue()) { + return Value.undefined; + } + const resultStr = str.stringValue()[index.numberValue()]; + return Descriptor({ + Value: new Value(resultStr), + Writable: Value.false, + Enumerable: Value.true, + Configurable: Value.false, + }); +} diff --git a/src/engine262/src/abstract-ops/symbol-objects.mjs b/src/engine262/src/abstract-ops/symbol-objects.mjs new file mode 100644 index 0000000..455734f --- /dev/null +++ b/src/engine262/src/abstract-ops/symbol-objects.mjs @@ -0,0 +1,12 @@ +import { Type, Value } from '../value.mjs'; +import { Assert } from './all.mjs'; + +// 19.4.3.3.1 #sec-symboldescriptivestring +export function SymbolDescriptiveString(sym) { + Assert(Type(sym) === 'Symbol'); + let desc = sym.Description; + if (Type(desc) === 'Undefined') { + desc = new Value(''); + } + return new Value(`Symbol(${desc.stringValue()})`); +} diff --git a/src/engine262/src/abstract-ops/testing-comparison.mjs b/src/engine262/src/abstract-ops/testing-comparison.mjs new file mode 100644 index 0000000..8d6661a --- /dev/null +++ b/src/engine262/src/abstract-ops/testing-comparison.mjs @@ -0,0 +1,412 @@ +import { + BigIntValue, + Type, + TypeNumeric, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Q, X } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + Assert, + Get, + ToBoolean, + ToNumber, + ToNumeric, + ToPrimitive, + StringToBigInt, + isProxyExoticObject, + isArrayExoticObject, + isIntegerIndexedExoticObject, +} from './all.mjs'; + +// This file covers abstract operations defined in +// 7.2 #sec-testing-and-comparison-operations + +// 7.2.1 #sec-requireobjectcoercible +export function RequireObjectCoercible(argument) { + const type = Type(argument); + switch (type) { + case 'Undefined': + return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined'); + case 'Null': + return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null'); + case 'Boolean': + case 'Number': + case 'String': + case 'Symbol': + case 'BigInt': + case 'Object': + return argument; + default: + throw new OutOfRange('RequireObjectCoercible', { type, argument }); + } +} + +// 7.2.2 #sec-isarray +export function IsArray(argument) { + if (Type(argument) !== 'Object') { + return Value.false; + } + if (isArrayExoticObject(argument)) { + return Value.true; + } + if (isProxyExoticObject(argument)) { + if (argument.ProxyHandler === Value.null) { + return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'IsArray'); + } + const target = argument.ProxyTarget; + return IsArray(target); + } + return Value.false; +} + +// 7.2.3 #sec-iscallable +export function IsCallable(argument) { + if (Type(argument) !== 'Object') { + return Value.false; + } + if ('Call' in argument) { + return Value.true; + } + return Value.false; +} + +// 7.2.4 #sec-isconstructor +export function IsConstructor(argument) { + if (Type(argument) !== 'Object') { + return Value.false; + } + if ('Construct' in argument) { + return Value.true; + } + return Value.false; +} + +// 7.2.5 #sec-isextensible-o +export function IsExtensible(O) { + Assert(Type(O) === 'Object'); + return O.IsExtensible(); +} + +// 7.2.6 #sec-isinteger +export function IsInteger(argument) { + if (Type(argument) !== 'Number') { + return Value.false; + } + if (argument.isNaN() || argument.isInfinity()) { + return Value.false; + } + if (Math.floor(Math.abs(argument.numberValue())) !== Math.abs(argument.numberValue())) { + return Value.false; + } + return Value.true; +} + +// 7.2.7 #sec-ispropertykey +export function IsPropertyKey(argument) { + if (Type(argument) === 'String') { + return true; + } + if (Type(argument) === 'Symbol') { + return true; + } + return false; +} + +// 7.2.8 #sec-isregexp +export function IsRegExp(argument) { + if (Type(argument) !== 'Object') { + return Value.false; + } + const matcher = Q(Get(argument, wellKnownSymbols.match)); + if (matcher !== Value.undefined) { + return ToBoolean(matcher); + } + if ('RegExpMatcher' in argument) { + return Value.true; + } + return Value.false; +} + +// 7.2.9 #sec-isstringprefix +export function IsStringPrefix(p, q) { + Assert(Type(p) === 'String'); + Assert(Type(q) === 'String'); + return q.stringValue().startsWith(p.stringValue()); +} + +// 7.2.10 #sec-samevalue +export function SameValue(x, y) { + // 1. If Type(x) is different from Type(y), return false. + if (Type(x) !== Type(y)) { + return Value.false; + } + // 2. If Type(x) is Number or BigInt, then + if (Type(x) === 'Number' || Type(x) === 'BigInt') { + // a. Return ! Type(x)::sameValue(x, y). + return TypeNumeric(x).sameValue(x, y); + } + // 3. Return ! SameValueNonNumeric(x, y). + return X(SameValueNonNumber(x, y)); +} + +// 7.2.11 #sec-samevaluezero +export function SameValueZero(x, y) { + // 1. If Type(x) is different from Type(y), return false. + if (Type(x) !== Type(y)) { + return Value.false; + } + // 2. If Type(x) is Number or BigInt, then + if (Type(x) === 'Number' || Type(x) === 'BigInt') { + // a. Return ! Type(x)::sameValueZero(x, y). + return TypeNumeric(x).sameValueZero(x, y); + } + // 3. Return ! SameValueNonNumeric(x, y). + return X(SameValueNonNumber(x, y)); +} + +// 7.2.12 #sec-samevaluenonnumber +export function SameValueNonNumber(x, y) { + Assert(Type(x) !== 'Number'); + Assert(Type(x) === Type(y)); + + if (Type(x) === 'Undefined') { + return Value.true; + } + + if (Type(x) === 'Null') { + return Value.true; + } + + if (Type(x) === 'String') { + if (x.stringValue() === y.stringValue()) { + return Value.true; + } + return Value.false; + } + + if (Type(x) === 'Boolean') { + if (x === y) { + return Value.true; + } + return Value.false; + } + + if (Type(x) === 'Symbol') { + return x === y ? Value.true : Value.false; + } + + return x === y ? Value.true : Value.false; +} + +// 7.2.13 #sec-abstract-relational-comparison +export function AbstractRelationalComparison(x, y, LeftFirst = true) { + let px; + let py; + // 1. If the LeftFirst flag is true, then + if (LeftFirst === true) { + // a. Let px be ? ToPrimitive(x, hint Number). + px = Q(ToPrimitive(x, 'Number')); + // b. Let py be ? ToPrimitive(y, hint Number). + py = Q(ToPrimitive(y, 'Number')); + } else { + // a. NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation. + // b. Let py be ? ToPrimitive(y, hint Number). + py = Q(ToPrimitive(y, 'Number')); + // c. Let px be ? ToPrimitive(x, hint Number). + px = Q(ToPrimitive(x, 'Number')); + } + // 3. If Type(px) is String and Type(py) is String, then + if (Type(px) === 'String' && Type(py) === 'String') { + // a. If IsStringPrefix(py, px) is true, return false. + if (IsStringPrefix(py, px)) { + return Value.false; + } + // b. If IsStringPrefix(px, py) is true, return true. + if (IsStringPrefix(px, py)) { + return Value.true; + } + // c. Let k be the smallest nonnegative integer such that the code unit at index k within px + // is different from the code unit at index k within py. (There must be such a k, for + // neither String is a prefix of the other.) + let k = 0; + while (true) { + if (px.stringValue()[k] !== py.stringValue()[k]) { + break; + } + k += 1; + } + // d. Let m be the integer that is the numeric value of the code unit at index k within px. + const m = px.stringValue().charCodeAt(k); + // e. Let n be the integer that is the numeric value of the code unit at index k within py. + const n = py.stringValue().charCodeAt(k); + // f. If m < n, return true. Otherwise, return false. + if (m < n) { + return Value.true; + } else { + return Value.false; + } + } else { + // a. If Type(px) is BigInt and Type(py) is String, then + if (Type(px) === 'BigInt' && Type(py) === 'String') { + // i. Let ny be ! StringToBigInt(py). + const ny = X(StringToBigInt(py)); + // ii. If ny is NaN, return undefined. + if (Number.isNaN(ny)) { + return Value.undefined; + } + // iii. Return BigInt::lessThan(px, ny). + return BigIntValue.lessThan(px, ny); + } + // b. If Type(px) is String and Type(py) is BigInt, then + if (Type(px) === 'String' && Type(py) === 'BigInt') { + // i. Let ny be ! StringToBigInt(py). + const nx = X(StringToBigInt(px)); + // ii. If ny is NaN, return undefined. + if (Number.isNaN(nx)) { + return Value.undefined; + } + // iii. Return BigInt::lessThan(px, ny). + return BigIntValue.lessThan(nx, py); + } + // c. Let nx be ? ToNumeric(px). NOTE: Because px and py are primitive values evaluation order is not important. + const nx = Q(ToNumeric(px)); + // d. Let ny be ? ToNumeric(py). + const ny = Q(ToNumeric(py)); + // e. If Type(nx) is the same as Type(ny), return Type(nx)::lessThan(nx, ny). + if (Type(nx) === Type(ny)) { + return TypeNumeric(nx).lessThan(nx, ny); + } + // f. Assert: Type(nx) is BigInt and Type(ny) is Number, or Type(nx) is Number and Type(ny) is BigInt. + Assert((Type(nx) === 'BigInt' && Type(ny) === 'Number') || (Type(nx) === 'Number' && Type(ny) === 'BigInt')); + // g. If nx or ny is NaN, return undefined. + if ((nx.isNaN && nx.isNaN()) || (ny.isNaN && ny.isNaN())) { + return Value.undefined; + } + // h. If nx is -∞ or ny is +∞, return true. + if ((nx.numberValue && nx.numberValue() === -Infinity) || (ny.numberValue && ny.numberValue() === +Infinity)) { + return Value.true; + } + // i. If nx is +∞ or ny is -∞, return false. + if ((nx.numberValue && nx.numberValue() === +Infinity) || (ny.numberValue && ny.numberValue() === -Infinity)) { + return Value.false; + } + // j. If the mathematical value of nx is less than the mathematical value of ny, return true; otherwise return false. + const a = nx.numberValue ? nx.numberValue() : nx.bigintValue(); + const b = ny.numberValue ? ny.numberValue() : ny.bigintValue(); + return a < b ? Value.true : Value.false; + } +} + +// 7.2.14 #sec-abstract-equality-comparison +export function AbstractEqualityComparison(x, y) { + // 1. If Type(x) is the same as Type(y), then + if (Type(x) === Type(y)) { + // a. Return the result of performing Strict Equality Comparison x === y. + return StrictEqualityComparison(x, y); + } + // 2. If x is null and y is undefined, return true. + if (x === Value.null && y === Value.undefined) { + return Value.true; + } + // 3. If x is undefined and y is null, return true. + if (x === Value.undefined && y === Value.null) { + return Value.true; + } + // 4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y). + if (Type(x) === 'Number' && Type(y) === 'String') { + return AbstractEqualityComparison(x, X(ToNumber(y))); + } + // 5. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y. + if (Type(x) === 'String' && Type(y) === 'Number') { + return AbstractEqualityComparison(X(ToNumber(x)), y); + } + // 6. If Type(x) is BigInt and Type(y) is String, then + if (Type(x) === 'BigInt' && Type(y) === 'String') { + // a. Let n be ! StringToBigInt(y). + const n = X(StringToBigInt(y)); + // b. If n is NaN, return false. + if (Number.isNaN(n)) { + return Value.false; + } + // c. Return the result of the comparison x == n. + return AbstractEqualityComparison(x, n); + } + // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. + if (Type(x) === 'String' && Type(y) === 'BigInt') { + return AbstractEqualityComparison(y, x); + } + // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. + if (Type(x) === 'Boolean') { + return AbstractEqualityComparison(X(ToNumber(x)), y); + } + // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). + if (Type(y) === 'Boolean') { + return AbstractEqualityComparison(x, X(ToNumber(y))); + } + // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y). + if (['String', 'Number', 'BigInt', 'Symbol'].includes(Type(x)) && Type(y) === 'Object') { + return AbstractEqualityComparison(x, Q(ToPrimitive(y))); + } + // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result of the comparison ToPrimitive(x) == y. + if (Type(x) === 'Object' && ['String', 'Number', 'BigInt', 'Symbol'].includes(Type(y))) { + return AbstractEqualityComparison(Q(ToPrimitive(x)), y); + } + // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then + if ((Type(x) === 'BigInt' && Type(y) === 'Number') || (Type(x) === 'Number' && Type(y) === 'BigInt')) { + // a. If x or y are any of NaN, +∞, or -∞, return false. + if ((x.isNaN && (x.isNaN() || !x.isFinite())) || (y.isNaN && (y.isNaN() || !y.isFinite()))) { + return Value.false; + } + // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. + const a = (x.numberValue ? x.numberValue() : x.bigintValue()); + const b = (y.numberValue ? y.numberValue() : y.bigintValue()); + return a == b ? Value.true : Value.false; // eslint-disable-line eqeqeq + } + // 13. Return false. + return Value.false; +} + +// 7.2.15 #sec-strict-equality-comparison +export function StrictEqualityComparison(x, y) { + // 1. If Type(x) is different from Type(y), return false. + if (Type(x) !== Type(y)) { + return Value.false; + } + // 2. If Type(x) is Number or BigInt, then + if (Type(x) === 'Number' || Type(x) === 'BigInt') { + // a. Return ! Type(x)::equal(x, y). + return X(TypeNumeric(x).equal(x, y)); + } + // 3. Return ! SameValueNonNumeric(x, y). + return SameValueNonNumber(x, y); +} + +// #sec-isvalidintegerindex +export function IsValidIntegerIndex(O, index) { + Assert(isIntegerIndexedExoticObject(O)); + Assert(Type(index) === 'Number'); + if (IsInteger(index) === Value.false) { + return Value.false; + } + index = index.numberValue(); + if (Object.is(index, -0)) { + return Value.false; + } + if (index < 0 || index >= O.ArrayLength.numberValue()) { + return Value.false; + } + return Value.true; +} + +// #sec-isnonnegativeinteger +export function IsNonNegativeInteger(argument) { + // 1. If ! IsInteger(argument) is true and argument ≥ 0, return true. + if (X(IsInteger(argument)) === Value.true && argument.numberValue() >= 0) { + return Value.true; + } + // 2. Otherwise, return false. + return Value.false; +} diff --git a/src/engine262/src/abstract-ops/type-conversion.mjs b/src/engine262/src/abstract-ops/type-conversion.mjs new file mode 100644 index 0000000..f38dcf2 --- /dev/null +++ b/src/engine262/src/abstract-ops/type-conversion.mjs @@ -0,0 +1,469 @@ +import { + Type, + Value, + NumberValue, + BigIntValue, + wellKnownSymbols, +} from '../value.mjs'; +import { MV_StringNumericLiteral } from '../runtime-semantics/all.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { Q, X } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + Assert, + Call, + Get, + GetMethod, + IsCallable, + OrdinaryObjectCreate, + SameValue, + StringCreate, +} from './all.mjs'; + +// 7.1.1 #sec-toprimitive +export function ToPrimitive(input, PreferredType) { + Assert(input instanceof Value); + if (Type(input) === 'Object') { + let hint; + if (PreferredType === undefined) { + hint = new Value('default'); + } else if (PreferredType === 'String') { + hint = new Value('string'); + } else { + Assert(PreferredType === 'Number'); + hint = new Value('number'); + } + const exoticToPrim = Q(GetMethod(input, wellKnownSymbols.toPrimitive)); + if (exoticToPrim !== Value.undefined) { + const result = Q(Call(exoticToPrim, input, [hint])); + if (Type(result) !== 'Object') { + return result; + } + return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive'); + } + if (hint.stringValue() === 'default') { + hint = new Value('number'); + } + return Q(OrdinaryToPrimitive(input, hint)); + } + return input; +} + +// 7.1.1.1 #sec-ordinarytoprimitive +export function OrdinaryToPrimitive(O, hint) { + Assert(Type(O) === 'Object'); + Assert(Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'number')); + let methodNames; + if (hint.stringValue() === 'string') { + methodNames = [new Value('toString'), new Value('valueOf')]; + } else { + methodNames = [new Value('valueOf'), new Value('toString')]; + } + for (const name of methodNames) { + const method = Q(Get(O, name)); + if (IsCallable(method) === Value.true) { + const result = Q(Call(method, O)); + if (Type(result) !== 'Object') { + return result; + } + } + } + return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive'); +} + +// 7.1.2 #sec-toboolean +export function ToBoolean(argument) { + const type = Type(argument); + switch (type) { + case 'Undefined': + return Value.false; + case 'Null': + return Value.false; + case 'Boolean': + return argument; + case 'Number': + if (argument.numberValue() === 0 || argument.isNaN()) { + return Value.false; + } + return Value.true; + case 'String': + if (argument.stringValue().length === 0) { + return Value.false; + } + return Value.true; + case 'Symbol': + return Value.true; + case 'BigInt': + if (argument.bigintValue() === 0n) { + return Value.false; + } + return Value.true; + case 'Object': + return Value.true; + default: + throw new OutOfRange('ToBoolean', { type, argument }); + } +} + +// #sec-tonumeric +export function ToNumeric(value) { + // 1. Let primValue be ? ToPrimitive(value, hint Number). + const primValue = Q(ToPrimitive(value, 'Number')); + // 2. If Type(primValue) is BigInt, return primValue. + if (Type(primValue) === 'BigInt') { + return primValue; + } + // 3. Return ? ToNumber(primValue). + return Q(ToNumber(primValue)); +} + +// 7.1.3 #sec-tonumber +export function ToNumber(argument) { + const type = Type(argument); + switch (type) { + case 'Undefined': + return new Value(NaN); + case 'Null': + return new Value(0); + case 'Boolean': + if (argument === Value.true) { + return new Value(1); + } + return new Value(0); + case 'Number': + return argument; + case 'String': + return MV_StringNumericLiteral(argument.stringValue()); + case 'BigInt': + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + case 'Symbol': + return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'number'); + case 'Object': { + const primValue = Q(ToPrimitive(argument, 'Number')); + return Q(ToNumber(primValue)); + } + default: + throw new OutOfRange('ToNumber', { type, argument }); + } +} + +const mod = (n, m) => { + const r = n % m; + return Math.floor(r >= 0 ? r : r + m); +}; + +// 7.1.4 #sec-tointeger +export function ToInteger(argument) { + // 1. Let number be ? ToNumber(argument). + const number = Q(ToNumber(argument)).numberValue(); + // 2. If number is NaN, +0, or -0, return +0. + if (Number.isNaN(number) || number === 0) { + return new Value(0); + } + // 3. If number is +∞, or -∞, return number. + if (!Number.isFinite(number)) { + return new Value(number); + } + // 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)). + const integer = Math.sign(number) * Math.floor(Math.abs(number)); + // 5. If integer is -0, return +0. + if (Object.is(integer, -0)) { + return new Value(+0); + } + // 6. Return integer. + return new Value(integer); +} + +// 7.1.5 #sec-toint32 +export function ToInt32(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int32bit = mod(int, 2 ** 32); + if (int32bit >= (2 ** 31)) { + return new Value(int32bit - (2 ** 32)); + } + return new Value(int32bit); +} + +// 7.1.6 #sec-touint32 +export function ToUint32(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int32bit = mod(int, 2 ** 32); + return new Value(int32bit); +} + +// 7.1.7 #sec-toint16 +export function ToInt16(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int16bit = mod(int, 2 ** 16); + if (int16bit >= (2 ** 15)) { + return new Value(int16bit - (2 ** 16)); + } + return new Value(int16bit); +} + +// 7.1.8 #sec-touint16 +export function ToUint16(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int16bit = mod(int, 2 ** 16); + return new Value(int16bit); +} + +// 7.1.9 #sec-toint8 +export function ToInt8(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int8bit = mod(int, 2 ** 8); + if (int8bit >= (2 ** 7)) { + return new Value(int8bit - (2 ** 8)); + } + return new Value(int8bit); +} + +// 7.1.10 #sec-touint8 +export function ToUint8(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) { + return new Value(0); + } + const int = Math.sign(number) * Math.floor(Math.abs(number)); + const int8bit = mod(int, 2 ** 8); + return new Value(int8bit); +} + +// 7.1.11 #sec-touint8clamp +export function ToUint8Clamp(argument) { + const number = Q(ToNumber(argument)).numberValue(); + if (Number.isNaN(number)) { + return new Value(0); + } + if (number <= 0) { + return new Value(0); + } + if (number >= 255) { + return new Value(255); + } + const f = Math.floor(number); + if (f + 0.5 < number) { + return new Value(f + 1); + } + if (number < f + 0.5) { + return new Value(f); + } + if (f % 2 === 1) { + return new Value(f + 1); + } + return new Value(f); +} + +// #sec-tobigint +export function ToBigInt(argument) { + // 1. Let prim be ? ToPrimitive(argument, hint Number). + const prim = Q(ToPrimitive(argument, 'Number')); + // 2. Return the value that prim corresponds to in Table 12 (#table-tobigint). + switch (Type(prim)) { + case 'Undefined': + // Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim); + case 'Null': + // Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim); + case 'Boolean': + // Return 1n if prim is true and 0n if prim is false. + if (prim === Value.true) { + return new Value(1n); + } + return new Value(0n); + case 'BigInt': + // Return prim. + return prim; + case 'Number': + // Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim); + case 'String': { + // 1. Let n be ! StringToBigInt(prim). + const n = X(StringToBigInt(prim)); + // 2. If n is NaN, throw a SyntaxError exception. + if (Number.isNaN(n)) { + return surroundingAgent.Throw('SyntaxError', 'CannotConvertToBigInt', prim); + } + // 3. Return n. + return n; + } + case 'Symbol': + return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'bigint'); + default: + throw new OutOfRange('ToBigInt', argument); + } +} + +// #sec-stringtobigint +export function StringToBigInt(argument) { + // Apply the algorithm in 7.1.4.1 (#sec-tonumber-applied-to-the-string-type) with the following changes: + // 1. Replace the StrUnsignedDecimalLiteral production with DecimalDigits to not allow Infinity, decimal points, or exponents. + // 2. If the MV is NaN, return NaN, otherwise return the BigInt which exactly corresponds to the MV, rather than rounding to a Number. + // TODO: Adapt nearley grammar for this. + try { + return new Value(BigInt(argument.stringValue())); + } catch { + return NaN; + } +} + +// #sec-tobigint64 +export function ToBigInt64(argument) { + // 1. Let n be ? ToBigInt(argument). + const n = Q(ToBigInt(argument)); + // 2. Let int64bit be n modulo 2^64. + const int64bit = n.bigintValue() % (2n ** 64n); + // 3. If int64bit ≥ 2^63, return int64bit - 2^64; otherwise return int64bit. + if (int64bit >= 2n ** 63n) { + return new Value(int64bit - (2n ** 64n)); + } + return new Value(int64bit); +} + +// #sec-tobiguint64 +export function ToBigUint64(argument) { + // 1. Let n be ? ToBigInt(argument). + const n = Q(ToBigInt(argument)); + // 2. Let int64bit be n modulo 2^64. + const int64bit = n.bigintValue() % (2n ** 64n); + // 3. Return int64bit. + return new Value(int64bit); +} + +// 7.1.12 #sec-tostring +export function ToString(argument) { + const type = Type(argument); + switch (type) { + case 'Undefined': + return new Value('undefined'); + case 'Null': + return new Value('null'); + case 'Boolean': + return new Value(argument === Value.true ? 'true' : 'false'); + case 'Number': + // Return ! Number::toString(argument). + return X(NumberValue.toString(argument)); + case 'String': + return argument; + case 'Symbol': + return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'string'); + case 'BigInt': + // Return ! BigInt::toString(argument). + return X(BigIntValue.toString(argument)); + case 'Object': { + const primValue = Q(ToPrimitive(argument, 'String')); + return Q(ToString(primValue)); + } + default: + throw new OutOfRange('ToString', { type, argument }); + } +} + +// 7.1.13 #sec-toobject +export function ToObject(argument) { + const type = Type(argument); + switch (type) { + case 'Undefined': + return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined'); + case 'Null': + return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null'); + case 'Boolean': { + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Boolean.prototype%')); + obj.BooleanData = argument; + return obj; + } + case 'Number': { + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Number.prototype%')); + obj.NumberData = argument; + return obj; + } + case 'String': + return StringCreate(argument, surroundingAgent.intrinsic('%String.prototype%')); + case 'Symbol': { + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Symbol.prototype%')); + obj.SymbolData = argument; + return obj; + } + case 'BigInt': { + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%BigInt.prototype%')); + obj.BigIntData = argument; + return obj; + } + case 'Object': + return argument; + default: + throw new OutOfRange('ToObject', { type, argument }); + } +} + +// 7.1.14 #sec-topropertykey +export function ToPropertyKey(argument) { + const key = Q(ToPrimitive(argument, 'String')); + if (Type(key) === 'Symbol') { + return key; + } + return X(ToString(key)); +} + +// 7.1.15 #sec-tolength +export function ToLength(argument) { + const len = Q(ToInteger(argument)); + if (len.numberValue() <= 0) { + return new Value(0); + } + return new Value(Math.min(len.numberValue(), (2 ** 53) - 1)); +} + +// 7.1.16 #sec-canonicalnumericindexstring +export function CanonicalNumericIndexString(argument) { + Assert(Type(argument) === 'String'); + if (argument.stringValue() === '-0') { + return new Value(-0); + } + const n = X(ToNumber(argument)); + if (SameValue(X(ToString(n)), argument) === Value.false) { + return Value.undefined; + } + return n; +} + +// 7.1.17 #sec-toindex +export function ToIndex(value) { + let index; + if (Type(value) === 'Undefined') { + index = new Value(0); + } else { + const integerIndex = Q(ToInteger(value)); + if (integerIndex.numberValue() < 0) { + return surroundingAgent.Throw('RangeError', 'NegativeIndex', 'Index'); + } + index = X(ToLength(integerIndex)); + if (X(SameValue(integerIndex, index)) === Value.false) { + return surroundingAgent.Throw('RangeError', 'OutOfRange', 'Index'); + } + } + return index; +} diff --git a/src/engine262/src/abstract-ops/typedarray-objects.mjs b/src/engine262/src/abstract-ops/typedarray-objects.mjs new file mode 100644 index 0000000..b2798cb --- /dev/null +++ b/src/engine262/src/abstract-ops/typedarray-objects.mjs @@ -0,0 +1,240 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { + Assert, + ToInt8, + ToUint8, + ToUint8Clamp, + ToInt16, + ToUint16, + ToInt32, + ToUint32, + ToBigInt64, + ToBigUint64, + RequireInternalSlot, + Construct, + GetIterator, + IteratorStep, + IteratorValue, + SpeciesConstructor, + IsDetachedBuffer, + IsNonNegativeInteger, + IntegerIndexedObjectCreate, + GetPrototypeFromConstructor, + AllocateArrayBuffer, +} from './all.mjs'; + +export const typedArrayInfoByName = { + Int8Array: { + IntrinsicName: '%Int8Array%', + ElementType: 'Int8', + ElementSize: 1, + ConversionOperation: ToInt8, + }, + Uint8Array: { + IntrinsicName: '%Uint8Array%', + ElementType: 'Uint8', + ElementSize: 1, + ConversionOperation: ToUint8, + }, + Uint8ClampedArray: { + IntrinsicName: '%Uint8ClampedArray%', + ElementType: 'Uint8C', + ElementSize: 1, + ConversionOperation: ToUint8Clamp, + }, + Int16Array: { + IntrinsicName: '%Int16Array%', + ElementType: 'Int16', + ElementSize: 2, + ConversionOperation: ToInt16, + }, + Uint16Array: { + IntrinsicName: '%Uint16Array%', + ElementType: 'Uint16', + ElementSize: 2, + ConversionOperation: ToUint16, + }, + Int32Array: { + IntrinsicName: '%Int32Array%', + ElementType: 'Int32', + ElementSize: 4, + ConversionOperation: ToInt32, + }, + Uint32Array: { + IntrinsicName: '%Uint32Array%', + ElementType: 'Uint32', + ElementSize: 4, + ConversionOperation: ToUint32, + }, + BigInt64Array: { + IntrinsicName: '%BigInt64Array%', + ElementType: 'BigInt64', + ElementSize: 8, + ConversionOperation: ToBigInt64, + }, + BigUint64Array: { + IntrinsicName: '%BigUint64Array%', + ElementType: 'BigUint64', + ElementSize: 8, + ConversionOperation: ToBigUint64, + }, + Float32Array: { + IntrinsicName: '%Float32Array%', + ElementType: 'Float32', + ElementSize: 4, + ConversionOperation: undefined, + }, + Float64Array: { + IntrinsicName: '%Float64Array%', + ElementType: 'Float64', + ElementSize: 8, + ConversionOperation: undefined, + }, +}; + +export const typedArrayInfoByType = {}; +Object.values(typedArrayInfoByName).forEach((v) => { + typedArrayInfoByType[v.ElementType] = v; +}); + +// #sec-validatetypedarray +export function ValidateTypedArray(O) { + // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 3. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. Return buffer. + return buffer; +} + +// #typedarray-create +export function TypedArrayCreate(constructor, argumentList) { + // 1. Let newTypedArray be ? Construct(constructor, argumentList). + const newTypedArray = Q(Construct(constructor, argumentList)); + // 2. Perform ? ValidateTypedArray(newTypedArray). + Q(ValidateTypedArray(newTypedArray)); + // 3. If argumentList is a List of a single Number, then + if (argumentList.length === 1 && Type(argumentList[0]) === 'Number') { + // a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception. + if (newTypedArray.ArrayLength.numberValue() < argumentList[0].numberValue()) { + return surroundingAgent.Throw('TypeError', 'TypedArrayTooSmall'); + } + } + // 4. Return newTypedArray. + return newTypedArray; +} + +// #sec-allocatedtypedarray +export function AllocateTypedArray(constructorName, newTarget, defaultProto, length) { + // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). + const proto = Q(GetPrototypeFromConstructor(newTarget, defaultProto)); + // 2. Let obj be ! IntegerIndexedObjectCreate(proto). + const obj = X(IntegerIndexedObjectCreate(proto)); + // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined. + Assert(obj.ViewedArrayBuffer === Value.undefined); + // 4. Set obj.[[TypedArrayName]] to constructorName. + obj.TypedArrayName = constructorName; + // 5. If constructorName is "BigInt64Array" or "BigUint64Array", set obj.[[ContentType]] to BigInt. + // 6. Otherwise, set obj.[[ContentType]] to Number. + if (constructorName.stringValue() === 'BigInt64Array' || constructorName.stringValue() === 'BigUint64Array') { + obj.ContentType = 'BigInt'; + } else { + obj.ContentType = 'Number'; + } + // 7. If length is not present, then + if (length === undefined) { + // 1. Set obj.[[ByteLength]] to 0. + obj.ByteLength = new Value(0); + // 1. Set obj.[[ByteOffset]] to 0. + obj.ByteOffset = new Value(0); + // 1. Set obj.[[ArrayLength]] to 0. + obj.ArrayLength = new Value(0); + } else { + // a. Perform ? AllocateTypedArrayBuffer(obj, length). + Q(AllocateTypedArrayBuffer(obj, length)); + } + // 9. Return obj. + return obj; +} + +// #sec-allocatetypedarraybuffer +export function AllocateTypedArrayBuffer(O, length) { + // 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + Assert(Type(O) === 'Object' && 'ViewedArrayBuffer' in O); + // 2. Assert: O.[[ViewedArrayBuffer]] is undefined. + Assert(O.ViewedArrayBuffer === Value.undefined); + // 3. Assert: ! IsNonNegativeInteger(length) is true. + Assert(X(IsNonNegativeInteger(length)) === Value.true); + // 4. Let constructorName be the String value of O.[[TypedArrayName]]. + const constructorName = O.TypedArrayName.stringValue(); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + const elementSize = typedArrayInfoByName[constructorName].ElementSize; + // 6. Let byteLength be elementSize × length. + const byteLength = new Value(elementSize * length.numberValue()); + // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). + const data = Q(AllocateArrayBuffer(surroundingAgent.intrinsic('%ArrayBuffer%'), byteLength)); + // 8. Set O.[[ViewedArrayBuffer]] to data. + O.ViewedArrayBuffer = data; + // 9. Set O.[[ByteLength]] to byteLength. + O.ByteLength = byteLength; + // 10. Set O.[[ByteOffset]] to 0. + O.ByteOffset = new Value(0); + // 11. Set O.[[ArrayLength]] to length. + O.ArrayLength = length; + // 12. Return O. + return O; +} + +// #sec-typedarray-species-create +export function TypedArraySpeciesCreate(exemplar, argumentList) { + // 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots. + Assert(Type(exemplar) === 'Object' + && 'TypedArrayName' in exemplar + && 'ContentType' in exemplar); + // 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for exemplar.[[TypedArrayName]]. + const defaultConstructor = surroundingAgent.intrinsic(typedArrayInfoByName[exemplar.TypedArrayName.stringValue()].IntrinsicName); + // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). + const constructor = Q(SpeciesConstructor(exemplar, defaultConstructor)); + // 4. Let result be ? TypedArrayCreate(constructor, argumentList). + const result = Q(TypedArrayCreate(constructor, argumentList)); + // 5. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots. + Assert('TypedArrayName' in result && 'ContentType' in result); + // 6. If result.[[ContentType]] is not equal to exemplar.[[ContentType]], throw a TypeError exception. + if (result.ContentType !== exemplar.ContentType) { + return surroundingAgent.Throw('TypeError', 'BufferContentTypeMismatch'); + } + // 7. Return result. + return result; +} + +// #sec-iterabletolist +export function IterableToList(items, method) { + // 1. Let iteratorRecord be ? GetIterator(items, sync, method). + const iteratorRecord = Q(GetIterator(items, 'sync', method)); + // 2. Let values be a new empty List. + const values = []; + // 3. Let next be true. + let next = Value.true; + // 4. Repeat, while next is not false + while (next !== Value.false) { + // a. Set next to ? IteratorStep(iteratorRecord). + next = Q(IteratorStep(iteratorRecord)); + // b. If next is not false, then + if (next !== Value.false) { + // i. Let nextValue be ? IteratorValue(next). + const nextValue = Q(IteratorValue(next)); + // ii. Append nextValue to the end of the List values. + values.push(nextValue); + } + } + // 5. Return values. + return values; +} diff --git a/src/engine262/src/abstract-ops/weak-operations.mjs b/src/engine262/src/abstract-ops/weak-operations.mjs new file mode 100644 index 0000000..cfc0b4b --- /dev/null +++ b/src/engine262/src/abstract-ops/weak-operations.mjs @@ -0,0 +1,45 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { NormalCompletion, Q } from '../completion.mjs'; +import { Assert, Call } from './all.mjs'; + +// https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects +export function ClearKeptObjects() { + // 1. Let agent be the surrounding agent. + const agent = surroundingAgent; + // 2. Set agent.[[KeptAlive]] to a new empty List. + agent.KeptAlive = new Set(); +} + +// https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects +export function AddToKeptObjects(object) { + // 1. Let agent be the surrounding agent. + const agent = surroundingAgent; + // 2. Append object to agent.[[KeptAlive]]. + agent.KeptAlive.add(object); +} + +// https://tc39.es/proposal-weakrefs/#sec-cleanup-finalization-registry +export function CleanupFinalizationRegistry(finalizationRegistry, callback) { + // 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots. + Assert('Cells' in finalizationRegistry); + // 2. If callback is not present or undefined, set callback to finalizationRegistry.[[CleanupCallback]]. + if (callback === undefined || callback === Value.undefined) { + callback = finalizationRegistry.CleanupCallback; + } + // 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, do + for (let i = 0; i < finalizationRegistry.Cells.length; i += 1) { + // a. Choose any such _cell_. + const cell = finalizationRegistry.Cells[i]; + if (cell.WeakRefTarget !== undefined) { + continue; + } + // b. Remove cell from finalizationRegistry.[[Cells]]. + finalizationRegistry.Cells.splice(i, 1); + i -= 1; + // c. Perform ? Call(callback, undefined, « cell.[[HeldValue]] »). + Q(Call(callback, Value.undefined, [cell.HeldValue])); + } + // 4. Return NormalCompletion(undefined). + return NormalCompletion(Value.undefined); +} diff --git a/src/engine262/src/api.mjs b/src/engine262/src/api.mjs new file mode 100644 index 0000000..968aec7 --- /dev/null +++ b/src/engine262/src/api.mjs @@ -0,0 +1,388 @@ +import { + CreateRealm, + SetDefaultGlobalBindings, + SetRealmGlobalObject, +} from './realm.mjs'; +import { + ExecutionContext, + surroundingAgent, + setSurroundingAgent, + evaluateScript, + Agent, + HostCleanupFinalizationRegistry, + FEATURES, +} from './engine.mjs'; +import { + Descriptor, + Type, + Value, +} from './value.mjs'; +import { ParseModule } from './parse.mjs'; +import { + AbruptCompletion, + Completion, + NormalCompletion, + Q, X, + ThrowCompletion, +} from './completion.mjs'; +import * as AbstractOps from './abstract-ops/all.mjs'; + +export const Abstract = { ...AbstractOps, Type }; +const { + OrdinaryObjectCreate, + CreateBuiltinFunction, + GetModuleNamespace, + ToPrimitive, +} = Abstract; +export { + AbruptCompletion, + NormalCompletion, + Completion, + Descriptor, + FEATURES, +}; + +export { inspect } from './inspect.mjs'; + +function mark() { + // https://tc39.es/proposal-weakrefs/#sec-weakref-execution + // At any time, if a set of objects S is not live, an ECMAScript implementation may perform the following steps automically: + // 1. For each obj os S, do + // a. For each WeakRef ref such that ref.[[WeakRefTarget]] is obj, + // i. Set ref.[[WeakRefTarget]] to empty. + // b. For each FinalizationRegistry fg such that fg.[[Cells]] contains cell, such that cell.[[WeakRefTarget]] is obj, + // i. Set cell.[[WeakRefTarget]] to empty. + // ii. Optionally, perform ! HostCleanupFinalizationRegistry(fg). + // c. For each WeakMap map such that map.WeakMapData contains a record r such that r.Key is obj, + // i. Remove r from map.WeakMapData. + // d. For each WeakSet set such that set.WeakSetData contains obj, + // i. Remove obj from WeakSetData. + + const marked = new Set(); + const weakrefs = new Set(); + const fgs = new Set(); + const weakmaps = new Set(); + const weaksets = new Set(); + const ephemeronQueue = []; + + const markCb = (O) => { + if (typeof O !== 'object' || O === null) { + return; + } + + if (marked.has(O)) { + return; + } + marked.add(O); + + if ('WeakRefTarget' in O && !('HeldValue' in O)) { + weakrefs.add(O); + markCb(O.properties); + markCb(O.Prototype); + } else if ('Cells' in O) { + fgs.add(O); + markCb(O.properties); + markCb(O.Prototype); + O.Cells.forEach((cell) => { + markCb(cell.HeldValue); + }); + } else if ('WeakMapData' in O) { + weakmaps.add(O); + markCb(O.properties); + markCb(O.Prototype); + O.WeakMapData.forEach((r) => { + ephemeronQueue.push(r); + }); + } else if ('WeakSetData' in O) { + weaksets.add(O); + markCb(O.properties); + markCb(O.Prototype); + } else if (O.mark) { + O.mark(markCb); + } + }; + + if (surroundingAgent.feature('WeakRefs')) { + AbstractOps.ClearKeptObjects(); + } + + markCb(surroundingAgent); + + while (ephemeronQueue.length > 0) { + const item = ephemeronQueue.shift(); + if (marked.has(item.Key)) { + markCb(item.Value); + } + } + + if (surroundingAgent.feature('WeakRefs')) { + weakrefs.forEach((ref) => { + if (!marked.has(ref.WeakRefTarget)) { + ref.WeakRefTarget = undefined; + } + }); + + fgs.forEach((fg) => { + let dirty = false; + fg.Cells.forEach((cell) => { + if (!marked.has(cell.WeakRefTarget)) { + cell.WeakRefTarget = undefined; + dirty = true; + } + }); + if (dirty) { + X(HostCleanupFinalizationRegistry(fg)); + } + }); + } + + weakmaps.forEach((map) => { + map.WeakMapData.forEach((r) => { + if (!marked.has(r.Key)) { + r.Key = undefined; + r.Value = undefined; + } + }); + }); + + weaksets.forEach((set) => { + set.WeakSetData.forEach((obj, i) => { + if (!marked.has(obj)) { + set.WeakSetData[i] = undefined; + } + }); + }); +} + +// https://tc39.es/ecma262/#sec-jobs +function runJobQueue() { + if (surroundingAgent.executionContextStack.length !== 0) { + return; + } + + // At some future point in time, when there is no running execution context + // and the execution context stack is empty, the implementation must: + + while (true) { // eslint-disable-line no-constant-condition + if (surroundingAgent.jobQueue.length === 0) { + break; + } + const { + job: abstractClosure, + callerRealm, + callerScriptOrModule, + } = surroundingAgent.jobQueue.shift(); + + // 1. Push an execution context onto the execution context stack. + const newContext = new ExecutionContext(); + surroundingAgent.executionContextStack.push(newContext); + // 2. Perform any implementation-defined preparation steps. + newContext.Function = Value.null; + newContext.Realm = callerRealm; + newContext.ScriptOrModule = callerScriptOrModule; + // 3. Call the abstract closure. + X(abstractClosure()); + // 4. Perform any implementation-defined cleanup steps. + mark(); + // 5. Pop the previously-pushed execution context from the execution context stack. + surroundingAgent.executionContextStack.pop(newContext); + } +} + +class APIAgent { + constructor(options = {}) { + this.agent = new Agent(options); + this.active = false; + this.outerAgent = undefined; + } + + scope(cb) { + this.enter(); + try { + return cb(); + } finally { + this.exit(); + } + } + + enter() { + if (this.active) { + throw new Error('Agent is already entered'); + } + this.active = true; + this.outerAgent = surroundingAgent; + setSurroundingAgent(this.agent); + } + + exit() { + if (!this.active) { + throw new Error('Agent is not entered'); + } + setSurroundingAgent(this.outerAgent); + this.outerAgent = undefined; + this.active = false; + } +} + +class APIRealm { + constructor(options = {}) { + const realm = CreateRealm(); + + realm.HostDefined = options; + + const newContext = new ExecutionContext(); + newContext.Function = Value.null; + newContext.Realm = realm; + newContext.ScriptOrModule = Value.null; + surroundingAgent.executionContextStack.push(newContext); + const global = Value.undefined; + const thisValue = Value.undefined; + SetRealmGlobalObject(realm, global, thisValue); + const globalObj = SetDefaultGlobalBindings(realm); + + // Create any implementation-defined global object properties on globalObj. + + surroundingAgent.executionContextStack.pop(newContext); + + this.global = globalObj; + this.realm = realm; + this.context = newContext; + this.agent = surroundingAgent; + + this.active = false; + } + + evaluateScript(sourceText, { specifier } = {}) { + if (typeof sourceText !== 'string') { + throw new TypeError('sourceText must be a string'); + } + + const res = this.scope(() => { + const realm = surroundingAgent.currentRealmRecord; + return evaluateScript(sourceText, realm, { + specifier, + public: { + specifier, + }, + }); + }); + + if (!(res instanceof AbruptCompletion)) { + runJobQueue(); + } + + return res; + } + + createSourceTextModule(specifier, sourceText) { + if (typeof sourceText !== 'string') { + throw new TypeError('sourceText must be a string'); + } + if (typeof specifier !== 'string') { + throw new TypeError('specifier must be a string'); + } + const module = this.scope(() => ParseModule(sourceText, this.realm, { + specifier, + public: { + specifier, + Link: () => this.scope(() => module.Link()), + GetNamespace: () => this.scope(() => GetModuleNamespace(module)), + Evaluate: () => { + const res = this.scope(() => module.Evaluate()); + if (!(res instanceof AbruptCompletion)) { + runJobQueue(); + } + return res; + }, + }, + })); + if (Array.isArray(module)) { + return new ThrowCompletion(module[0]); + } + module.HostDefined.public.module = module; + return module.HostDefined.public; + } + + scope(cb) { + if (this.active) { + return cb(); + } + this.active = true; + surroundingAgent.executionContextStack.push(this.context); + const r = cb(); + surroundingAgent.executionContextStack.pop(this.context); + this.active = false; + return r; + } +} + +function APIObject(realm, intrinsic = '%Object.prototype%') { + return OrdinaryObjectCreate(realm.realm.Intrinsics[intrinsic]); +} + +class APIValue extends Value { + constructor(realm, value) { + if (typeof value === 'function') { + return CreateBuiltinFunction(value, [], realm.realm); + } + if (value === undefined) { + return Value.undefined; + } + if (value === null) { + return Value.null; + } + if (value === true) { + return Value.true; + } + if (value === false) { + return Value.false; + } + return new Value(value); + } + + static [Symbol.hasInstance](v) { + return v instanceof Value; + } +} + +export { + APIAgent as Agent, + APIRealm as Realm, + APIValue as Value, + APIObject as Object, +}; + +export function Throw(realm, V, ...args) { + return realm.scope(() => { + if (typeof V === 'string') { + // eslint-disable-next-line engine262/valid-throw + return surroundingAgent.Throw(V, 'Raw', args[0]); + } + return new ThrowCompletion(V); + }); +} + +export function ToString(realm, value) { + return realm.scope(() => { + while (true) { + const type = Type(value); + switch (type) { + case 'String': + return value.stringValue(); + case 'Number': + return value.numberValue().toString(); + case 'Boolean': + return value === Value.true ? 'true' : 'false'; + case 'Undefined': + return 'undefined'; + case 'Null': + return 'null'; + case 'Symbol': + return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'string'); + default: + value = Q(ToPrimitive(value, 'String')); + break; + } + } + }); +} diff --git a/src/engine262/src/ast.mjs b/src/engine262/src/ast.mjs new file mode 100644 index 0000000..7156e24 --- /dev/null +++ b/src/engine262/src/ast.mjs @@ -0,0 +1,1085 @@ +import { Assert } from './abstract-ops/notational-conventions.mjs'; + +// #prod-NullLiteral +export function isNullLiteral(node) { + return node.type === 'Literal' && node.value === null; +} + +// #prod-BooleanLiteral +export function isBooleanLiteral(node) { + return node.type === 'Literal' && typeof node.value === 'boolean'; +} + +// #prod-NumericLiteral +export function isNumericLiteral(node) { + return node.type === 'Literal' + && (typeof node.value === 'number' || typeof node.value === 'bigint'); +} + +// #prod-StringLiteral +export function isStringLiteral(node) { + return node.type === 'Literal' && typeof node.value === 'string'; +} + +export function isSpreadElement(node) { + return node.type === 'SpreadElement'; +} + +// #prod-RegularExpressionLiteral +export function isRegularExpressionLiteral(node) { + return node.type === 'Literal' && typeof node.regex === 'object'; +} + +// #prod-Identifier +// Not exact, as we allow reserved words when appropriate. (This is more like +// IdentifierReference.) +export function isIdentifier(node) { + return node.type === 'Identifier'; +} + +// #prod-IdentifierReference +export const isIdentifierReference = isIdentifier; + +// #prod-IdentifierName +export const isIdentifierName = isIdentifier; + +// #prod-BindingIdentifier +export const isBindingIdentifier = isIdentifier; + +// #prod-LabelIdentifier +export const isLabelIdentifier = isIdentifier; + +// Used in #prod-PrimaryExpression +export function isThis(node) { + return node.type === 'ThisExpression'; +} + +// #prod-Literal +export function isLiteral(node) { + // Just checking node.type is not enough as RegularExpressionLiteral also + // uses 'Literal' as node.type. + return isNullLiteral(node) + || isBooleanLiteral(node) + || isNumericLiteral(node) + || isStringLiteral(node); +} + +// #prod-ArrayLiteral +export function isArrayLiteral(node) { + return node.type === 'ArrayExpression'; +} + +// #prod-ObjectLiteral +export function isObjectLiteral(node) { + return node.type === 'ObjectExpression'; +} + +// #prod-GeneratorMethod +export function isGeneratorMethod(node) { + return ( + (node.type === 'Property' && !node.shorthand && node.method && node.kind === 'init') + || (node.type === 'MethodDefinition' && node.kind === 'method') + ) && isGeneratorExpression(node.value); +} + +// #prod-AsyncMethod +export function isAsyncMethod(node) { + return ( + (node.type === 'Property' && !node.shorthand && node.method && node.kind === 'init') + || (node.type === 'MethodDefinition' && node.kind === 'method') + ) && isAsyncFunctionExpression(node.value); +} + +// #prod-AsyncGeneratorMethod +export function isAsyncGeneratorMethod(node) { + return ( + (node.type === 'Property' && !node.shorthand && node.method && node.kind === 'init') + || (node.type === 'MethodDefinition' && node.kind === 'method') + ) && isAsyncGeneratorExpression(node.value); +} + +// Used in #prod-MethodDefinition +export function isMethodDefinitionRegularFunction(node) { + return ( + (node.type === 'Property' && !node.shorthand && node.method && node.kind === 'init') + || (node.type === 'MethodDefinition' && node.kind === 'method') + ) && isFunctionExpression(node.value); +} + +// Used in #prod-MethodDefinition +export function isMethodDefinitionGetter(node) { + return ( + (node.type === 'Property' && !node.shorthand && !node.method) + || node.type === 'MethodDefinition' + ) && node.kind === 'get'; +} + +// Used in #prod-MethodDefinition +export function isMethodDefinitionSetter(node) { + return ( + (node.type === 'Property' && !node.shorthand && !node.method) + || node.type === 'MethodDefinition' + ) && node.kind === 'set'; +} + +// #prod-MethodDefinition +export function isMethodDefinition(node) { + return isMethodDefinitionRegularFunction(node) + || isGeneratorMethod(node) + || isAsyncMethod(node) + || isAsyncGeneratorMethod(node) + || isMethodDefinitionGetter(node) + || isMethodDefinitionSetter(node); +} + +// Used in #prod-PropertyDefinition +export function isPropertyDefinitionIdentifierReference(node) { + return node.type === 'Property' && node.shorthand && !node.method && !node.computed && node.kind === 'init'; +} + +// Used in #prod-PropertyDefinition +export function isPropertyDefinitionKeyValue(node) { + return node.type === 'Property' && !node.shorthand && !node.method && node.kind === 'init'; +} + +// Used in #prod-PropertyDefinition +export function isPropertyDefinitionSpread(node) { + return node.type === 'SpreadElement'; +} + +// #prod-FunctionExpression +export function isFunctionExpression(node) { + return node.type === 'FunctionExpression' + && !node.generator + && !node.async; +} + +export function isFunctionExpressionWithBindingIdentifier(node) { + return isFunctionExpression(node) && node.id !== null; +} + +export function isAsyncFunctionExpressionWithBindingIdentifier(node) { + return isAsyncFunctionExpression(node) && node.id !== null; +} + +// #prod-ClassExpression +export function isClassExpression(node) { + return node.type === 'ClassExpression'; +} + +// #prod-GeneratorExpression +export function isGeneratorExpression(node) { + return node.type === 'FunctionExpression' + && node.generator + && !node.async; +} + +// #prod-AsyncFunctionExpression +export function isAsyncFunctionExpression(node) { + return node.type === 'FunctionExpression' + && !node.generator + && node.async; +} + +// #prod-AsyncGeneratorExpression +export function isAsyncGeneratorExpression(node) { + return node.type === 'FunctionExpression' + && node.generator + && node.async; +} + +// #prod-TemplateLiteral +export function isTemplateLiteral(node) { + return node.type === 'TemplateLiteral'; +} + +// #prod-NoSubstitutionTemplate +export function isNoSubstitutionTemplate(node) { + return isTemplateLiteral(node) && node.expressions.length === 0; +} + +// #prod-SubstitutionTemplate +export function isSubstitutionTemplate(node) { + return isTemplateLiteral(node) && node.expressions.length !== 0; +} + +export function unrollTemplateLiteral(TemplateLiteral) { + const all = [TemplateLiteral.quasis[0]]; + for (let i = 1; i < TemplateLiteral.quasis.length; i += 1) { + all.push(TemplateLiteral.expressions[i - 1]); + all.push(TemplateLiteral.quasis[i]); + } + return all; +} + +export function isTaggedTemplate(node) { + return node.type === 'TaggedTemplateExpression'; +} + +// Used in #prod-MemberExpression and #prod-CallExpression +export function isActualMemberExpression(node) { + return node.type === 'MemberExpression' && node.object.type !== 'Super'; +} + +// Used in #prod-MemberExpression and #prod-CallExpression +export function isActualMemberExpressionWithBrackets(node) { + return isActualMemberExpression(node) && node.computed; +} + +// Used in #prod-MemberExpression and #prod-CallExpression +export function isActualMemberExpressionWithDot(node) { + return isActualMemberExpression(node) && !node.computed; +} + +// #prod-SuperProperty +export function isSuperProperty(node) { + return node.type === 'MemberExpression' && node.object.type === 'Super'; +} + +// #prod-MetaProperty +export function isMetaProperty(node) { + return node.type === 'MetaProperty'; +} + +// #prod-NewTarget +export function isNewTarget(node) { + return isMetaProperty(node) + && node.meta.name === 'new' + && node.property.name === 'target'; +} + +// #prod-ImportMeta +export function isImportMeta(node) { + return isMetaProperty(node) + && node.meta.name === 'import' + && node.property.name === 'meta'; +} + +// Used in #prod-MemberExpression and #prod-NewExpression +export function isActualNewExpression(node) { + return node.type === 'NewExpression'; +} + +// Used in #prod-CallExpression and #prod-CallMemberExpression +export function isActualCallExpression(node) { + return node.type === 'CallExpression' && node.callee.type !== 'Super'; +} + +// #prod-SuperCall +export function isSuperCall(node) { + return node.type === 'CallExpression' && node.callee.type === 'Super'; +} + +// #prod-ImportCall +export function isImportCall(node) { + return node.type === 'ImportExpression'; +} + +// Used in #prod-UpdateExpression +export function isActualUpdateExpression(node) { + return node.type === 'UpdateExpression'; +} + +// Used in #prod-UnaryExpression +export function isActualUnaryExpression(node) { + return node.type === 'UnaryExpression'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithDelete(node) { + return node.type === 'UnaryExpression' && node.operator === 'delete'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithVoid(node) { + return node.type === 'UnaryExpression' && node.operator === 'void'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithTypeof(node) { + return node.type === 'UnaryExpression' && node.operator === 'typeof'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithPlus(node) { + return node.type === 'UnaryExpression' && node.operator === '+'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithMinus(node) { + return node.type === 'UnaryExpression' && node.operator === '-'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithTilde(node) { + return node.type === 'UnaryExpression' && node.operator === '~'; +} + +// Used in #prod-UnaryExpression +export function isUnaryExpressionWithBang(node) { + return node.type === 'UnaryExpression' && node.operator === '!'; +} + +// #prod-AwaitExpression +export function isAwaitExpression(node) { + return node.type === 'AwaitExpression'; +} + +// Used in #prod-ExponentiationExpression +export function isActualExponentiationExpression(node) { + return node.type === 'BinaryExpression' && node.operator === '**'; +} + +// Used in #prod-MultiplicativeExpression +export function isActualMultiplicativeExpression(node) { + return node.type === 'BinaryExpression' + && ( + node.operator === '*' + || node.operator === '/' + || node.operator === '%' + ); +} + +// Used in #prod-AdditiveExpression +export function isActualAdditiveExpression(node) { + return node.type === 'BinaryExpression' + && (node.operator === '+' || node.operator === '-'); +} + +// Used in #prod-AdditiveExpression +export function isAdditiveExpressionWithPlus(node) { + return isActualAdditiveExpression(node) && node.operator === '+'; +} + +// Used in #prod-AdditiveExpression +export function isAdditiveExpressionWithMinus(node) { + return isActualAdditiveExpression(node) && node.operator === '-'; +} + +// Used in #prod-ShiftExpression +export function isActualShiftExpression(node) { + return node.type === 'BinaryExpression' + && ( + node.operator === '<<' + || node.operator === '>>' + || node.operator === '>>>' + ); +} + +// Used in #prod-RelationalExpression +export function isActualRelationalExpression(node) { + return node.type === 'BinaryExpression' + && ( + node.operator === '<' + || node.operator === '>' + || node.operator === '<=' + || node.operator === '>=' + || node.operator === 'instanceof' + || node.operator === 'in' + ); +} + +// Used in #prod-EqualityExpression +export function isActualEqualityExpression(node) { + return node.type === 'BinaryExpression' + && ( + node.operator === '==' + || node.operator === '!=' + || node.operator === '===' + || node.operator === '!==' + ); +} + +// Used in #prod-BitwiseANDExpression +export function isActualBitwiseANDExpression(node) { + return node.type === 'BinaryExpression' && node.operator === '&'; +} + +// Used in #prod-BitwiseXORExpression +export function isActualBitwiseXORExpression(node) { + return node.type === 'BinaryExpression' && node.operator === '^'; +} + +// Used in #prod-BitwiseORExpression +export function isActualBitwiseORExpression(node) { + return node.type === 'BinaryExpression' && node.operator === '|'; +} + +// Used in #prod-LogicalANDExpression +export function isActualLogicalANDExpression(node) { + return node.type === 'LogicalExpression' && node.operator === '&&'; +} + +// Used in #prod-LogicalORExpression +export function isActualLogicalORExpression(node) { + return node.type === 'LogicalExpression' && node.operator === '||'; +} + +// Used in #prod-ConditionalExpression +export function isActualConditionalExpression(node) { + return node.type === 'ConditionalExpression'; +} + +// #prod-YieldExpression +export function isYieldExpression(node) { + return node.type === 'YieldExpression'; +} + +// Used in #prod-YieldExpression. +export function isYieldExpressionWithStar(node) { + return isYieldExpression(node) && node.delegate; +} + +// #prod-ArrowFunction +export function isArrowFunction(node) { + return node.type === 'ArrowFunctionExpression' + && !node.async + && !node.generator; +} + +// #prod-AsyncArrowFunction +export function isAsyncArrowFunction(node) { + return node.type === 'ArrowFunctionExpression' + && node.async + && !node.generator; +} + +// Used in #prod-AssignmentExpression +export function isActualAssignmentExpression(node) { + return node.type === 'AssignmentExpression'; +} + +// Used in #prod-AssignmentExpression +export function isAssignmentExpressionWithEquals(node) { + return isActualAssignmentExpression(node) && node.operator === '='; +} + +// Used in #prod-AssignmentExpression +export function isAssignmentExpressionWithAssignmentOperator(node) { + return isActualAssignmentExpression(node) && node.operator !== '='; +} + +// Used in #prod-Expression +export function isExpressionWithComma(node) { + return node.type === 'SequenceExpression'; +} + +export function isParenthesizedExpression(node) { + return node.type === 'ParenthesizedExpression'; +} + +// #prod-CoalesceExpression +export function isActualCoalesceExpression(node) { + return node.type === 'BinaryExpression' && node.operator === '??'; +} + +// #prod-OptionalExpression +export function isOptionalExpression(node) { + return node.type === 'OptionalExpression'; +} + +// #prod-OptionalChain +export function isOptionalChain(node) { + return node.type === 'OptionalChain'; +} + +export function isOptionalChainWithOptionalChain(node) { + return isOptionalChain(node) && node.base !== null; +} + +export function isOptionalChainWithExpression(node) { + return isOptionalChain(node) && node.property && isExpression(node.property) && !isIdentifierReference(node.property); +} + +export function isOptionalChainWithIdentifierName(node) { + return isOptionalChain(node) && node.property && isIdentifier(node.property); +} + +export function isOptionalChainWithArguments(node) { + return isOptionalChain(node) && Array.isArray(node.arguments); +} + +// #prod-Expression +export function isExpression(node) { + return ( + // PrimaryExpression + isThis(node) + || isIdentifierReference(node) + || isLiteral(node) + || isArrayLiteral(node) + || isObjectLiteral(node) + || isFunctionExpression(node) + || isClassExpression(node) + || isGeneratorExpression(node) + || isAsyncFunctionExpression(node) + || isAsyncGeneratorExpression(node) + || isRegularExpressionLiteral(node) + || isTemplateLiteral(node) + || isParenthesizedExpression(node) + + // LeftHandSideExpression (including MemberExpression, NewExpression, and + // CallExpression) + || isActualMemberExpression(node) + || isOptionalExpression(node) + || isSuperProperty(node) + || isSuperCall(node) + || isImportCall(node) + || isMetaProperty(node) + || isActualNewExpression(node) + || isActualCallExpression(node) + || isTaggedTemplate(node) + + // UpdateExpression + || isActualUpdateExpression(node) + + // UnaryExpression + || isActualUnaryExpression(node) + || isAwaitExpression(node) + + // ExponentiationExpression + || isActualExponentiationExpression(node) + + // MultiplicativeExpression + || isActualMultiplicativeExpression(node) + + // AdditiveExpression + || isActualAdditiveExpression(node) + + // ShiftExpression + || isActualShiftExpression(node) + + // RelationalExpression + || isActualRelationalExpression(node) + + // EqualityExpression + || isActualEqualityExpression(node) + + // BitwiseANDExpression + || isActualBitwiseANDExpression(node) + + // BitwiseXORExpression + || isActualBitwiseXORExpression(node) + + // BitwiseORExpression + || isActualBitwiseORExpression(node) + + // LogicalANDExpression + || isActualLogicalANDExpression(node) + + // LogicalORExpression + || isActualLogicalORExpression(node) + + // CoalesceExpression + || isActualCoalesceExpression(node) + + // ConditionalExpression + || isActualConditionalExpression(node) + + // AssignmentExpression + || isYieldExpression(node) + || isArrowFunction(node) + || isAsyncArrowFunction(node) + || isActualAssignmentExpression(node) + + // Expression + || isExpressionWithComma(node) + ); +} + +// #prod-ExpressionBody +export const isExpressionBody = isExpression; + +// Used in #prod-SingleNameBinding +export function isBindingIdentifierAndInitializer(node) { + return node.type === 'AssignmentPattern' && isBindingIdentifier(node.left); +} + +// #prod-SingleNameBinding +// +// Use isBindingPropertyWithSingleNameBinding() instead if SingleNameBinding is +// used as a child of BindingProperty. +export function isSingleNameBinding(node) { + return isBindingIdentifier(node) || isBindingIdentifierAndInitializer(node); +} + +// Used in #prod-BindingElement +export function isBindingPatternAndInitializer(node) { + return node.type === 'AssignmentPattern' && isBindingPattern(node.left); +} + +// #prod-BindingElement +export function isBindingElement(node) { + return isSingleNameBinding(node) + || isBindingPattern(node) + || isBindingPatternAndInitializer(node); +} + +// #prod-FormalParameter +export const isFormalParameter = isBindingElement; + +// #prod-BindingRestElement +export function isBindingRestElement(node) { + return node.type === 'RestElement'; +} + +// #prod-FunctionRestParameter +export const isFunctionRestParameter = isBindingRestElement; + +// #prod-BindingProperty +export function isBindingProperty(node) { + // ESTree puts the SingleNameBinding in node.value. + return node.type === 'Property' && isBindingElement(node.value); +} + +// Used in #prod-BindingProperty. +export function isBindingPropertyWithSingleNameBinding(node) { + return isBindingProperty(node) && node.shorthand; +} + +// Used in #prod-BindingProperty. +export function isBindingPropertyWithColon(node) { + return isBindingProperty(node) && !node.shorthand; +} + +// #prod-BindingRestProperty +export function isBindingRestProperty(node) { + return node.type === 'RestElement'; +} + +// #prod-BlockStatement +export function isBlockStatement(node) { + return node.type === 'BlockStatement'; +} + +// #prod-BindingPattern +export function isBindingPattern(node) { + return isObjectBindingPattern(node) || isArrayBindingPattern(node); +} + +// #prod-ObjectBindingPattern +export function isObjectBindingPattern(node) { + return node.type === 'ObjectPattern'; +} + +export function isEmptyObjectBindingPattern(node) { + return isObjectBindingPattern(node) && node.properties.length === 0; +} + +export function isObjectBindingPatternWithBindingPropertyList(node) { + return isObjectBindingPattern(node) + && node.properties.length > 0 + && !isBindingRestProperty(node.properties[node.properties.length - 1]); +} + +export function isObjectBindingPatternWithSingleBindingRestProperty(node) { + return isObjectBindingPattern(node) + && node.properties.length === 1 + && isBindingRestProperty(node.properties[0]); +} + +export function isObjectBindingPatternWithBindingPropertyListAndBindingRestProperty(node) { + return isObjectBindingPattern(node) + && node.properties.length >= 2 + && isBindingRestProperty(node.properties[node.properties.length - 1]); +} + +// #prod-ArrayBindingPattern +export function isArrayBindingPattern(node) { + return node.type === 'ArrayPattern'; +} + +// #prod-AssignmentPattern +export const isAssignmentPattern = isBindingPattern; + +// #prod-ObjectAssignmentPattern +export const isObjectAssignmentPattern = isObjectBindingPattern; + +// #prod-ArrayAssignmentPattern +export const isArrayAssignmentPattern = isArrayBindingPattern; + +// #prod-AssignmentRestProperty +export const isAssignmentRestProperty = isBindingRestElement; + +// #prod-Block +export const isBlock = isBlockStatement; + +// #prod-VariableStatement +export function isVariableStatement(node) { + return node.type === 'VariableDeclaration' && node.kind === 'var'; +} + +// #prod-VariableDeclaration +export function isVariableDeclaration(node) { + return node.type === 'VariableDeclarator'; +} + +// #prod-EmptyStatement +export function isEmptyStatement(node) { + return node.type === 'EmptyStatement'; +} + +// #prod-ExpressionStatement +export function isExpressionStatement(node) { + return node.type === 'ExpressionStatement'; +} + +// #prod-IfStatement +export function isIfStatement(node) { + return node.type === 'IfStatement'; +} + +// #prod-BreakableStatement +export function isBreakableStatement(node) { + return isIterationStatement(node) || isSwitchStatement(node); +} + +// #prod-IterationStatement +export function isIterationStatement(node) { + // for-await-of is ForOfStatement with await = true + return node.type === 'DoWhileStatement' + || node.type === 'WhileStatement' + || node.type === 'ForStatement' + || node.type === 'ForInStatement' + || node.type === 'ForOfStatement'; +} + +// Used in #prod-IterationStatement +export function isDoWhileStatement(node) { + return node.type === 'DoWhileStatement'; +} + +// Used in #prod-IterationStatement +export function isWhileStatement(node) { + return node.type === 'WhileStatement'; +} + +// Used in #prod-IterationStatement +export function isForStatement(node) { + return node.type === 'ForStatement'; +} + +// Used in #prod-IterationStatement +export function isForStatementWithExpression(node) { + return isForStatement(node) && (node.init === null || isExpression(node.init)); +} + +// Used in #prod-IterationStatement +export function isForStatementWithVariableStatement(node) { + return isForStatement(node) && isVariableStatement(node.init); +} + +// Used in #prod-IterationStatement +export function isForStatementWithLexicalDeclaration(node) { + return isForStatement(node) && isLexicalDeclaration(node.init); +} + +// Used in #prod-IterationStatement +export function isForInStatement(node) { + return node.type === 'ForInStatement'; +} + +// Used in #prod-IterationStatement +// This covers cases like for ({ a } in b), in which case the { a } is in fact +// parsed as an ObjectLiteral per spec. +export function isForInStatementWithExpression(node) { + return isForInStatement(node) && node.left.type !== 'VariableDeclaration'; +} + +// Used in #prod-IterationStatement +export function isForInStatementWithVarForBinding(node) { + return isForInStatement(node) && isVariableStatement(node.left) + && !node.left.declarations[0].init; +} + +// Used in #prod-IterationStatement +export function isForInStatementWithForDeclaration(node) { + return isForInStatement(node) && isForDeclaration(node.left); +} + +// Used in #prod-IterationStatement +export function isForOfStatement(node) { + return node.type === 'ForOfStatement'; +} + +// Used in #prod-IterationStatement +// This covers cases like for ({ a } of b), in which case the { a } is in fact +// parsed as an ObjectLiteral per spec. +export function isForOfStatementWithExpression(node) { + return isForOfStatement(node) && node.left.type !== 'VariableDeclaration'; +} + +// Used in #prod-IterationStatement +export function isForOfStatementWithVarForBinding(node) { + return isForOfStatement(node) && isVariableStatement(node.left) + && !node.left.declarations[0].init; +} + +// Used in #prod-IterationStatement +export function isForOfStatementWithForDeclaration(node) { + return isForOfStatement(node) && isForDeclaration(node.left); +} + +// #prod-ForBinding +export function isForBinding(node) { + return isBindingIdentifier(node) || isBindingPattern(node); +} + +// #prod-SwitchStatement +export function isSwitchStatement(node) { + return node.type === 'SwitchStatement'; +} + +export function isSwitchCase(node) { + return node.type === 'SwitchCase'; +} + +// #prod-ContinueStatement +export function isContinueStatement(node) { + return node.type === 'ContinueStatement'; +} + +// #prod-BreakStatement +export function isBreakStatement(node) { + return node.type === 'BreakStatement'; +} + +// #prod-ReturnStatement +export function isReturnStatement(node) { + return node.type === 'ReturnStatement'; +} + +// #prod-WithStatement +export function isWithStatement(node) { + return node.type === 'WithStatement'; +} + +// #prod-LabelledStatement +export function isLabelledStatement(node) { + return node.type === 'LabeledStatement'; // sic +} + +// #prod-ThrowStatement +export function isThrowStatement(node) { + return node.type === 'ThrowStatement'; +} + +// #prod-TryStatement +export function isTryStatement(node) { + return node.type === 'TryStatement'; +} + +// Used in #prod-TryStatement +export function isTryStatementWithCatch(node) { + return isTryStatement(node) && node.handler !== null; +} + +// Used in #prod-TryStatement +export function isTryStatementWithFinally(node) { + return isTryStatement(node) && node.finalizer !== null; +} + +// #prod-DebuggerStatement +export function isDebuggerStatement(node) { + return node.type === 'DebuggerStatement'; +} + +// #prod-Statement +export function isStatement(node) { + return isBlockStatement(node) + || isVariableStatement(node) + || isEmptyStatement(node) + || isExpressionStatement(node) + || isIfStatement(node) + || isBreakableStatement(node) + || isContinueStatement(node) + || isBreakStatement(node) + || isReturnStatement(node) + || isWithStatement(node) + || isLabelledStatement(node) + || isThrowStatement(node) + || isTryStatement(node) + || isDebuggerStatement(node); +} + +// #prod-Declaration +export function isDeclaration(node) { + return isHoistableDeclaration(node) + || isClassDeclaration(node) + || isLexicalDeclaration(node); +} + +// #prod-HoistableDeclaration +// The other kinds of HoistableDeclarations are grouped under +// FunctionDeclaration in ESTree. +export function isHoistableDeclaration(node) { + return node.type === 'FunctionDeclaration'; +} + +// #prod-FunctionDeclaration +export function isFunctionDeclaration(node) { + return node.type === 'FunctionDeclaration' + && !node.generator + && !node.async; +} + +// #prod-GeneratorDeclaration +export function isGeneratorDeclaration(node) { + return node.type === 'FunctionDeclaration' + && node.generator + && !node.async; +} + +// #prod-AsyncFunctionDeclaration +export function isAsyncFunctionDeclaration(node) { + return node.type === 'FunctionDeclaration' + && !node.generator + && node.async; +} + +// #prod-AsyncGeneratorDeclaration +export function isAsyncGeneratorDeclaration(node) { + return node.type === 'FunctionDeclaration' + && node.generator + && node.async; +} + +// #prod-ClassDeclaration +export function isClassDeclaration(node) { + return node.type === 'ClassDeclaration'; +} + +// #prod-LexicalDeclaration +export function isLexicalDeclaration(node) { + return node.type === 'VariableDeclaration' && (node.kind === 'let' || node.kind === 'const'); +} + +// #prod-StatementListItem +export function isStatementListItem(node) { + return isStatement(node) || isDeclaration(node); +} + +// #prod-ForDeclaration +export const isForDeclaration = isLexicalDeclaration; + +// #prod-LexicalBinding +export function isLexicalBinding(node) { + return node.type === 'VariableDeclarator'; +} + +// Used in #prod-ImportDeclaration +// +// Note: +// import {} from 'abc'; +// is treated the same as +// import 'abc'; +// and this method returns false with such constructs. +export function isImportDeclarationWithClause(node) { + return isImportDeclaration(node) && node.specifiers.length !== 0; +} + +// Used in #prod-ImportDeclaration +// +// Note: +// import {} from 'abc'; +// is treated the same as +// import 'abc'; +// and this method returns true with such constructs. +export function isImportDeclarationWithSpecifierOnly(node) { + return isImportDeclaration(node) && node.specifiers.length === 0; +} + +// #prod-ImportDeclaration +export function isImportDeclaration(node) { + return node.type === 'ImportDeclaration'; +} + +// #prod-ImportedDefaultBinding +export function isImportedDefaultBinding(node) { + return node.type === 'ImportDefaultSpecifier'; +} + +// #prod-NameSpaceImport +export function isNameSpaceImport(node) { + return node.type === 'ImportNamespaceSpecifier'; +} + +// #prod-ImportSpecifier +export function isImportSpecifier(node) { + return node.type === 'ImportSpecifier'; +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithStar(node) { + return node.type === 'ExportAllDeclaration'; +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithExportAndFrom(node) { + return node.type === 'ExportNamedDeclaration' + && node.declaration === null + && node.source !== null; +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithExport(node) { + return node.type === 'ExportNamedDeclaration' + && node.declaration === null + && node.source === null; +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithVariable(node) { + return node.type === 'ExportNamedDeclaration' + && node.declaration !== null + && isVariableStatement(node.declaration); +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithDeclaration(node) { + return node.type === 'ExportNamedDeclaration' + && node.declaration !== null + && isDeclaration(node.declaration); +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithDefaultAndHoistable(node) { + return node.type === 'ExportDefaultDeclaration' && isHoistableDeclaration(node.declaration); +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithDefaultAndClass(node) { + return node.type === 'ExportDefaultDeclaration' && isClassDeclaration(node.declaration); +} + +// Used in #prod-ExportDeclaration +export function isExportDeclarationWithDefaultAndExpression(node) { + return node.type === 'ExportDefaultDeclaration' && isExpression(node.declaration); +} + +// #prod-ExportDeclaration +export function isExportDeclaration(node) { + return isExportDeclarationWithStar(node) + || isExportDeclarationWithExportAndFrom(node) + || isExportDeclarationWithExport(node) + || isExportDeclarationWithVariable(node) + || isExportDeclarationWithDeclaration(node) + || isExportDeclarationWithDefaultAndHoistable(node) + || isExportDeclarationWithDefaultAndClass(node) + || isExportDeclarationWithDefaultAndExpression(node); +} + +// 14.1.1 #sec-directive-prologues-and-the-use-strict-directive +export function directivePrologueContainsUseStrictDirective(nodes) { + for (const node of nodes) { + if (isExpressionStatement(node) && isStringLiteral(node.expression)) { + Assert(typeof node.directive === 'string'); + if (node.directive === 'use strict') { + return true; + } + } else { + Assert(typeof node.directive !== 'string'); + return false; + } + } + return false; +} diff --git a/src/engine262/src/completion.mjs b/src/engine262/src/completion.mjs new file mode 100644 index 0000000..b320d24 --- /dev/null +++ b/src/engine262/src/completion.mjs @@ -0,0 +1,176 @@ +import { surroundingAgent } from './engine.mjs'; +import { + Assert, + CreateBuiltinFunction, + PerformPromiseThen, + PromiseResolve, + SetFunctionLength, +} from './abstract-ops/all.mjs'; +import { Reference, Value } from './value.mjs'; +import { resume } from './helpers.mjs'; + +// 6.2.3 #sec-completion-record-specification-type +export function Completion(type, value, target) { + if (new.target === Completion) { + if (typeof type !== 'string') { + throw new TypeError('Completion type is not a string'); + } + this.Type = type; + this.Value = value; + this.Target = target; + } + return type; +} + +// NON-SPEC +Completion.prototype.mark = function mark(m) { + m(this.Value); +}; + +// #sec-normalcompletion +export function NormalCompletion(value) { + return new Completion('normal', value); +} + +Object.defineProperty(NormalCompletion, Symbol.hasInstance, { + value: function hasInstance(v) { + return v instanceof Completion && v.Type === 'normal'; + }, + writable: true, + enumerable: false, + configurable: true, +}); + +export class AbruptCompletion { + static [Symbol.hasInstance](v) { + return v instanceof Completion && v.Type !== 'normal'; + } +} + +export class BreakCompletion { + constructor(target) { + return new Completion('break', undefined, target); + } + + static [Symbol.hasInstance](v) { + return v instanceof Completion && v.Type === 'break'; + } +} + +export class ContinueCompletion { + constructor(target) { + return new Completion('continue', undefined, target); + } + + static [Symbol.hasInstance](v) { + return v instanceof Completion && v.Type === 'continue'; + } +} + +// 6.2.3.2 #sec-normalcompletion +export class ReturnCompletion { + constructor(value) { + return new Completion('return', value); + } + + static [Symbol.hasInstance](v) { + return v instanceof Completion && v.Type === 'return'; + } +} + +// 6.2.3.3 #sec-throwcompletion +export function ThrowCompletion(value) { + return new Completion('throw', value); +} + +Object.defineProperty(ThrowCompletion, Symbol.hasInstance, { + value: function hasInstance(v) { + return v instanceof Completion && v.Type === 'throw'; + }, + writable: true, + enumerable: false, + configurable: true, +}); + +// 6.2.3.4 #sec-updateempty +export function UpdateEmpty(completionRecord, value) { + Assert(completionRecord instanceof Completion); + if (completionRecord.Type === 'return' || completionRecord.Type === 'throw') { + Assert(completionRecord.Value !== undefined); + } + if (completionRecord.Value !== undefined) { + return completionRecord; + } + return new Completion(completionRecord.Type, value, completionRecord.Target); +} + +// 5.2.3.3 #sec-returnifabrupt +export function ReturnIfAbrupt() { + throw new TypeError('ReturnIfAbrupt requires build'); +} + +// #sec-returnifabrupt-shorthands ? OperationName() +export const Q = ReturnIfAbrupt; + +// #sec-returnifabrupt-shorthands ! OperationName() +export function X(val) { + Assert(!(val instanceof AbruptCompletion)); + if (val instanceof Completion) { + return val.Value; + } + return val; +} + +// 25.6.1.1.1 #sec-ifabruptrejectpromise +export function IfAbruptRejectPromise() { + throw new TypeError('IfAbruptRejectPromise requires build'); +} + +export function EnsureCompletion(val) { + if (val instanceof Completion) { + return val; + } + if (val instanceof Reference) { + return val; + } + return new NormalCompletion(val); +} + +export function AwaitFulfilledFunctions([value]) { + const F = surroundingAgent.activeFunctionObject; + const asyncContext = F.AsyncContext; + const prevContext = surroundingAgent.runningExecutionContext; + // Suspend prevContext + surroundingAgent.executionContextStack.push(asyncContext); + resume(asyncContext, new NormalCompletion(value)); + Assert(surroundingAgent.runningExecutionContext === prevContext); + return Value.undefined; +} + +function AwaitRejectedFunctions([reason]) { + const F = surroundingAgent.activeFunctionObject; + const asyncContext = F.AsyncContext; + const prevContext = surroundingAgent.runningExecutionContext; + // Suspend prevContext + surroundingAgent.executionContextStack.push(asyncContext); + resume(asyncContext, new ThrowCompletion(reason)); + Assert(surroundingAgent.runningExecutionContext === prevContext); + return Value.undefined; +} + +export function* Await(value) { + const asyncContext = surroundingAgent.runningExecutionContext; + const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value)); + const stepsFulfilled = AwaitFulfilledFunctions; + const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['AsyncContext'])); + X(SetFunctionLength(onFulfilled, new Value(1))); + onFulfilled.AsyncContext = asyncContext; + const stepsRejected = AwaitRejectedFunctions; + const onRejected = X(CreateBuiltinFunction(stepsRejected, ['AsyncContext'])); + X(SetFunctionLength(onRejected, new Value(1))); + onRejected.AsyncContext = asyncContext; + X(PerformPromiseThen(promise, onFulfilled, onRejected)); + surroundingAgent.executionContextStack.pop(asyncContext); + const completion = yield Value.undefined; + return completion; +} diff --git a/src/engine262/src/engine.mjs b/src/engine262/src/engine.mjs new file mode 100644 index 0000000..b727208 --- /dev/null +++ b/src/engine262/src/engine.mjs @@ -0,0 +1,403 @@ +import { Value } from './value.mjs'; +import { + AbruptCompletion, + EnsureCompletion, + NormalCompletion, + ThrowCompletion, + Q, X, +} from './completion.mjs'; +import { + CreateRealm, + SetDefaultGlobalBindings, + SetRealmGlobalObject, +} from './realm.mjs'; +import { + Call, Construct, Assert, GetModuleNamespace, + PerformPromiseThen, CreateBuiltinFunction, + GetActiveScriptOrModule, + CleanupFinalizationRegistry, +} from './abstract-ops/all.mjs'; +import { ParseScript } from './parse.mjs'; +import { GlobalDeclarationInstantiation } from './runtime-semantics/all.mjs'; +import { Evaluate_Script } from './evaluator.mjs'; +import { CyclicModuleRecord } from './modules.mjs'; +import { CallSite } from './helpers.mjs'; +import * as messages from './messages.mjs'; + +export const FEATURES = Object.freeze([ + { + name: 'TopLevelAwait', + url: 'https://github.com/tc39/proposal-top-level-await', + }, + { + name: 'WeakRefs', + url: 'https://github.com/tc39/proposal-weakrefs', + }, + { + name: 'LogicalAssignment', + url: 'https://github.com/tc39/proposal-logical-assignment', + }, + { + name: 'Promise.any', + url: 'https://github.com/tc39/proposal-promise-any', + }, + { + name: 'RegExpMatchIndices', + url: 'https://github.com/tc39/proposal-regexp-match-Indices', + }, + { + name: 'String.prototype.replaceAll', + url: 'https://github.com/tc39/proposal-string-replaceall', + }, +].map(Object.freeze)); + +// #sec-agents +export class Agent { + constructor(options = {}) { + this.LittleEndian = Value.true; + this.CanBlock = true; + this.Signifier = Agent.Increment; + Agent.Increment += 1; + this.IsLockFree1 = true; + this.IsLockFree2 = true; + this.CandidateExecution = undefined; + + this.executionContextStack = []; + const stackPop = this.executionContextStack.pop; + this.executionContextStack.pop = function pop(ctx) { + if (!ctx.poppedForTailCall) { + const popped = stackPop.call(this); + Assert(popped === ctx); + } + }; + + this.jobQueue = []; + + this.hostDefinedOptions = { + ...options, + features: FEATURES.reduce((acc, { name }) => { + if (options.features) { + acc[name] = options.features.includes(name); + } else { + acc[name] = false; + } + return acc; + }, {}), + }; + + if (this.feature('WeakRefs')) { + this.KeptAlive = new Set(); + } + } + + // #sec-running-execution-context + get runningExecutionContext() { + return this.executionContextStack[this.executionContextStack.length - 1]; + } + + // #current-realm + get currentRealmRecord() { + return this.runningExecutionContext.Realm; + } + + // #active-function-object + get activeFunctionObject() { + return this.runningExecutionContext.Function; + } + + // Get an intrinsic by name for the current realm + intrinsic(name) { + return this.currentRealmRecord.Intrinsics[name]; + } + + // Generate a throw completion using message templates + Throw(type, template, ...templateArgs) { + if (type instanceof Value) { + return new ThrowCompletion(type); + } + const message = messages[template](...templateArgs); + const cons = this.currentRealmRecord.Intrinsics[`%${type}%`]; + let error; + if (type === 'AggregateError') { + error = X(Construct(cons, [ + Symbol.for('engine262.placeholder'), + new Value(message), + ])); + } else { + error = X(Construct(cons, [new Value(message)])); + } + return new ThrowCompletion(error); + } + + queueJob(queueName, job) { + const callerContext = this.runningExecutionContext; + const callerRealm = callerContext.Realm; + const callerScriptOrModule = GetActiveScriptOrModule(); + const pending = { + queueName, + job, + callerRealm, + callerScriptOrModule, + }; + this.jobQueue.push(pending); + } + + // NON-SPEC: Check if a feature is enabled in this agent. + feature(name) { + return this.hostDefinedOptions.features[name]; + } + + // NON-SPEC + mark(m) { + this.executionContextStack.forEach((e) => { + m(e); + }); + this.jobQueue.forEach((j) => { + m(j.callerRealm); + m(j.callerScriptOrModule); + }); + } +} +Agent.Increment = 0; + +export let surroundingAgent; +export function setSurroundingAgent(a) { + surroundingAgent = a; +} + +// #sec-execution-contexts +export class ExecutionContext { + constructor() { + this.codeEvaluationState = undefined; + this.Function = undefined; + this.Realm = undefined; + this.ScriptOrModule = undefined; + this.VariableEnvironment = undefined; + this.LexicalEnvironment = undefined; + + // NON-SPEC + this.callSite = new CallSite(this); + this.promiseCapability = undefined; + this.poppedForTailCall = false; + } + + copy() { + const e = new ExecutionContext(); + e.codeEvaluationState = this.codeEvaluationState; + e.Function = this.Function; + e.Realm = this.Realm; + e.ScriptOrModule = this.ScriptOrModule; + e.VariableEnvironment = this.VariableEnvironment; + e.LexicalEnvironment = this.LexicalEnvironment; + + e.callSite = this.callSite.clone(e); + e.promiseCapability = this.promiseCapability; + return e; + } + + // NON-SPEC + mark(m) { + m(this.Function); + m(this.Realm); + m(this.ScriptOrModule); + m(this.VariableEnvironment); + m(this.LexicalEnvironment); + m(this.promiseCapability); + } +} + +// 15.1.10 #sec-runtime-semantics-scriptevaluation +export function ScriptEvaluation(scriptRecord) { + const globalEnv = scriptRecord.Realm.GlobalEnv; + const scriptContext = new ExecutionContext(); + scriptContext.Function = Value.null; + scriptContext.Realm = scriptRecord.Realm; + scriptContext.ScriptOrModule = scriptRecord; + scriptContext.VariableEnvironment = globalEnv; + scriptContext.LexicalEnvironment = globalEnv; + scriptContext.HostDefined = scriptRecord.HostDefined; + // Suspend runningExecutionContext + surroundingAgent.executionContextStack.push(scriptContext); + const scriptBody = scriptRecord.ECMAScriptCode.body; + let result = EnsureCompletion(GlobalDeclarationInstantiation(scriptBody, globalEnv)); + + if (result.Type === 'normal') { + result = Evaluate_Script(scriptBody, globalEnv); + } + + if (result.Type === 'normal' && !result.Value) { + result = new NormalCompletion(Value.undefined); + } + + // Suspend scriptCtx + surroundingAgent.executionContextStack.pop(scriptContext); + // Resume(surroundingAgent.runningExecutionContext); + + return result; +} + +export function evaluateScript(sourceText, realm, hostDefined) { + const s = ParseScript(sourceText, realm, hostDefined); + if (Array.isArray(s)) { + return new ThrowCompletion(s[0]); + } + + return EnsureCompletion(ScriptEvaluation(s)); +} + +// #sec-hostenqueuepromisejob +export function HostEnqueuePromiseJob(job, _realm) { + surroundingAgent.queueJob('PromiseJobs', job); +} + +// 8.5 #sec-initializehostdefinedrealm +export function InitializeHostDefinedRealm() { + const realm = CreateRealm(); + const newContext = new ExecutionContext(); + newContext.Function = Value.null; + newContext.Realm = realm; + newContext.ScriptOrModule = Value.null; + surroundingAgent.executionContextStack.push(newContext); + const global = Value.undefined; + const thisValue = Value.undefined; + SetRealmGlobalObject(realm, global, thisValue); + SetDefaultGlobalBindings(realm); +} + +// 8.7.1 #sec-agentsignifier +export function AgentSignifier() { + const AR = surroundingAgent; + return AR.Signifier; +} + +export function HostEnsureCanCompileStrings(callerRealm, calleeRealm) { + if (surroundingAgent.hostDefinedOptions.ensureCanCompileStrings !== undefined) { + Q(surroundingAgent.hostDefinedOptions.ensureCanCompileStrings(callerRealm, calleeRealm)); + } + return new NormalCompletion(undefined); +} + +export function HostPromiseRejectionTracker(promise, operation) { + const realm = surroundingAgent.currentRealmRecord; + if (realm && realm.HostDefined.promiseRejectionTracker) { + X(realm.HostDefined.promiseRejectionTracker(promise, operation)); + } +} + +export function HostHasSourceTextAvailable(func) { + if (surroundingAgent.hostDefinedOptions.hasSourceTextAvailable) { + return X(surroundingAgent.hostDefinedOptions.hasSourceTextAvailable(func)); + } + return Value.true; +} + +export function HostResolveImportedModule(referencingScriptOrModule, specifier) { + const realm = referencingScriptOrModule.Realm || surroundingAgent.currentRealmRecord; + if (realm.HostDefined.resolveImportedModule) { + specifier = specifier.stringValue(); + if (referencingScriptOrModule !== Value.null) { + if (!referencingScriptOrModule.HostDefined.moduleMap) { + referencingScriptOrModule.HostDefined.moduleMap = new Map(); + } + if (referencingScriptOrModule.HostDefined.moduleMap.has(specifier)) { + return referencingScriptOrModule.HostDefined.moduleMap.get(specifier); + } + } + const publicModule = referencingScriptOrModule.HostDefined ? referencingScriptOrModule.HostDefined.public : null; + const apiModule = Q(realm.HostDefined.resolveImportedModule(publicModule, specifier)); + if (referencingScriptOrModule !== Value.null) { + referencingScriptOrModule.HostDefined.moduleMap.set(specifier, apiModule.module); + } + return apiModule.module; + } + return surroundingAgent.Throw('Error', 'CouldNotResolveModule', specifier); +} + +function FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion) { + if (completion instanceof AbruptCompletion) { + X(Call(promiseCapability.Reject, Value.undefined, [completion.Value])); + } else { + Assert(completion instanceof NormalCompletion); + const onFulfilled = X(CreateBuiltinFunction(([v = Value.undefined]) => { + Assert(v === Value.undefined); + const moduleRecord = X(HostResolveImportedModule(referencingScriptOrModule, specifier)); + // Assert: Evaluate has already been invoked on moduleRecord and successfully completed. + const namespace = EnsureCompletion(GetModuleNamespace(moduleRecord)); + if (namespace instanceof AbruptCompletion) { + X(Call(promiseCapability.Reject, Value.undefined, [namespace.Value])); + } else { + X(Call(promiseCapability.Resolve, Value.undefined, [namespace.Value])); + } + return Value.undefined; + }, [])); + const onRejected = X(CreateBuiltinFunction(([r = Value.undefined]) => { + X(Call(promiseCapability.Reject, Value.undefined, [r])); + return Value.undefined; + }, [])); + X(PerformPromiseThen(completion.Value, onFulfilled, onRejected)); + } +} + +export function HostImportModuleDynamically(referencingScriptOrModule, specifier, promiseCapability) { + surroundingAgent.queueJob('ImportModuleDynamicallyJobs', () => { + let completion = EnsureCompletion(HostResolveImportedModule(referencingScriptOrModule, specifier)); + if (!(completion instanceof AbruptCompletion)) { + const module = completion.Value; + if (module instanceof CyclicModuleRecord) { + if (module.HostDefined.cachedCompletion) { + completion = module.HostDefined.cachedCompletion; + } else { + if (module.Status !== 'linking' && module.Status !== 'evaluating') { + completion = EnsureCompletion(module.Link()); + } + if (!(completion instanceof AbruptCompletion)) { + completion = EnsureCompletion(module.Evaluate()); + module.HostDefined.cachedCompletion = completion; + } + } + } else { + completion = EnsureCompletion(module.Link()); + if (!(completion instanceof AbruptCompletion)) { + completion = EnsureCompletion(module.Evaluate()); + } + } + } + FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion); + }); + return new NormalCompletion(Value.undefined); +} + +// #sec-hostgetimportmetaproperties +export function HostGetImportMetaProperties(moduleRecord) { + const realm = surroundingAgent.currentRealmRecord; + if (realm.HostDefined.getImportMetaProperties) { + return X(realm.HostDefined.getImportMetaProperties(moduleRecord.HostDefined.public)); + } + return []; +} + +// #sec-hostfinalizeimportmeta +export function HostFinalizeImportMeta(importMeta, moduleRecord) { + const realm = surroundingAgent.currentRealmRecord; + if (realm.HostDefined.finalizeImportMeta) { + return X(realm.HostDefined.finalizeImportMeta(importMeta, moduleRecord.HostDefined.public)); + } + return Value.undefined; +} + +// https://tc39.es/proposal-weakrefs/#sec-host-cleanup-finalization-registry +const scheduledForCleanup = new Set(); +export function HostCleanupFinalizationRegistry(fg) { + if (surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry !== undefined) { + Q(surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry(fg)); + } else { + if (!scheduledForCleanup.has(fg)) { + scheduledForCleanup.add(fg); + surroundingAgent.queueJob('FinalizationCleanup', () => { + scheduledForCleanup.delete(fg); + CleanupFinalizationRegistry(fg); + }); + } + } + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/environment.mjs b/src/engine262/src/environment.mjs new file mode 100644 index 0000000..b4ca976 --- /dev/null +++ b/src/engine262/src/environment.mjs @@ -0,0 +1,1006 @@ +import { AbstractModuleRecord } from './modules.mjs'; +import { + Descriptor, + Reference, + Type, + Value, + wellKnownSymbols, +} from './value.mjs'; +import { surroundingAgent } from './engine.mjs'; +import { + Assert, + DefinePropertyOrThrow, + Get, + HasOwnProperty, + HasProperty, + IsDataDescriptor, + IsExtensible, + IsPropertyKey, + Set, + ToBoolean, + isECMAScriptFunctionObject, +} from './abstract-ops/all.mjs'; +import { NormalCompletion, Q, X } from './completion.mjs'; +import { ValueMap } from './helpers.mjs'; + +// #sec-lexical-environments +export class LexicalEnvironment { + constructor() { + this.EnvironmentRecord = undefined; + this.outerEnvironmentReference = undefined; + } + + // NON-SPEC + mark(m) { + m(this.EnvironmentRecord); + m(this.outerEnvironmentReference); + } +} + +// #sec-environment-records +export class EnvironmentRecord {} + +// #sec-declarative-environment-records +export class DeclarativeEnvironmentRecord extends EnvironmentRecord { + constructor() { + super(); + this.bindings = new ValueMap(); + } + + // #sec-declarative-environment-records-hasbinding-n + HasBinding(N) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. If envRec has a binding for the name that is the value of N, return true. + if (envRec.bindings.has(N)) { + return Value.true; + } + // 3. Return false. + return Value.false; + } + + // #sec-declarative-environment-records-createmutablebinding-n-d + CreateMutableBinding(N, D) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec does not already have a binding for N. + Assert(!envRec.bindings.has(N)); + // 3. Create a mutable binding in envRec for N and record that it is uninitialized. If D + // is true, record that the newly created binding may be delted by a subsequent + // DeleteBinding call. + this.bindings.set(N, { + indirect: false, + initialized: false, + mutable: true, + strict: undefined, + deletable: D === Value.true, + value: undefined, + mark(m) { + m(this.value); + }, + }); + // 4. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + // #sec-declarative-environment-records-createimmutablebinding-n-s + CreateImmutableBinding(N, S) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec does not already have a binding for N. + Assert(!envRec.bindings.has(N)); + // 3. Create an immutable binding in envRec for N and record that it is uninitialized. If + // S is true, record that the newly created binding is a strict binding. + this.bindings.set(N, { + indirect: false, + initialized: false, + mutable: false, + strict: S === Value.true, + deletable: false, + value: undefined, + mark(m) { + m(this.value); + }, + }); + // 4. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + // #sec-declarative-environment-records-initializebinding-n-v + InitializeBinding(N, V) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec must have an uninitialized binding for N. + const binding = envRec.bindings.get(N); + Assert(binding !== undefined && binding.initialized === false); + // 3. Set the bound value for N in envRec to V. + binding.value = V; + // 4. Record that the binding for N in envRec has been initialized. + binding.initialized = true; + // 5. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + // #sec-declarative-environment-records-setmutablebinding-n-v-s + SetMutableBinding(N, V, S) { + Assert(IsPropertyKey(N)); + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. If envRec does not have a binding for N, then + if (!envRec.bindings.has(N)) { + // a. If S is true, throw a ReferenceError exception. + if (S === Value.true) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + // b. Perform envRec.CreateMutableBinding(N, true). + envRec.CreateMutableBinding(N, true); + // c. Perform envRec.InitializeBinding(N, V). + envRec.InitializeBinding(N, V); + // d. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + const binding = this.bindings.get(N); + // 3. If the binding for N in envRec is a strict binding, set S to true. + if (binding.strict === true) { + S = Value.true; + } + // 4. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception. + if (binding.initialized === false) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + // 5. Else if the binding for N in envRec is a mutable binding, change its bound value to V. + if (binding.mutable === true) { + binding.value = V; + } else { + // a. Assert: This is an attempt to change the value of an immutable binding. + // b. If S is true, throw a TypeError exception. + if (S === Value.true) { + return surroundingAgent.Throw('TypeError', 'AssignmentToConstant', N); + } + } + // 7. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + // #sec-declarative-environment-records-getbindingvalue-n-s + GetBindingValue(N) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec has a binding for N. + const binding = envRec.bindings.get(N); + Assert(binding !== undefined); + // 3. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception. + if (binding.initialized === false) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + // 4. Return the value currently bound to N in envRec. + return binding.value; + } + + // #sec-declarative-environment-records-deletebinding-n + DeleteBinding(N) { + // 1. Let envRec be the declarative Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec has a binding for the name that is the value of N. + const binding = envRec.bindings.get(N); + Assert(binding !== undefined); + // 3. If the binding for N in envRec cannot be deleted, return false. + if (binding.deletable === false) { + return Value.false; + } + // 4. Remove the binding for N from envRec. + envRec.bindings.delete(N); + // 5. Return true. + return Value.true; + } + + // #sec-declarative-environment-records-hasthisbinding + HasThisBinding() { + // 1. Return false. + return Value.false; + } + + // #sec-declarative-environment-records-hassuperbinding + HasSuperBinding() { + // 1. Return false. + return Value.false; + } + + // #sec-declarative-environment-records-withbaseobject + WithBaseObject() { + // 1. Return undefined. + return Value.undefined; + } + + // NON-SPEC + mark(m) { + m(this.bindings); + } +} + +// #sec-object-environment-records +export class ObjectEnvironmentRecord extends EnvironmentRecord { + constructor(BindingObject) { + super(); + this.bindingObject = BindingObject; + this.withEnvironment = false; + } + + // #sec-object-environment-records-hasbinding-n + HasBinding(N) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Let bindings be the binding object for envRec. + const bindings = envRec.bindingObject; + // 3. Let foundBinding be ? HasProperty(bindings, N). + const foundBinding = Q(HasProperty(bindings, N)); + // 4. If foundBinding is false, return false. + if (foundBinding === Value.false) { + return Value.false; + } + // 5. If the withEnvironment flag of envRec i s false, return true. + if (envRec.withEnvironment === false) { + return Value.true; + } + // 6. Let unscopables be ? Get(bindings, @@unscopables). + const unscopables = Q(Get(bindings, wellKnownSymbols.unscopables)); + // 7. If Type(unscopables) is Object, then + if (Type(unscopables) === 'Object') { + // a. Let blocked be ! ToBoolean(? Get(unscopables, N)). + const blocked = X(ToBoolean(Q(Get(unscopables, N)))); + // b. If blocked is true, return false. + if (blocked === Value.true) { + return Value.false; + } + } + // 8. Return true. + return Value.true; + } + + // #sec-object-environment-records-createmutablebinding-n-d + CreateMutableBinding(N, D) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Let envRec be the object Environment Record for which the method was invoked. + const bindings = envRec.bindingObject; + // 3. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }). + return Q(DefinePropertyOrThrow(bindings, N, Descriptor({ + Value: Value.undefined, + Writable: Value.true, + Enumerable: Value.true, + Configurable: D, + }))); + } + + // #sec-object-environment-records-createimmutablebinding-n-s + CreateImmutableBinding(_N, _S) { + Assert(false, 'CreateImmutableBinding called on an Object Environment Record'); + } + + // #sec-object-environment-records-initializebinding-n-v + InitializeBinding(N, V) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec must have an uninitialized binding for N. + // 3. Record that the binding for N in envRec has been initialized. + // 4. Return ? envRec.SetMutableBinding(N, V, false). + return Q(envRec.SetMutableBinding(N, V, Value.false)); + } + + // #sec-object-environment-records-setmutablebinding-n-v-s + SetMutableBinding(N, V, S) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Let bindings be the binding object for envRec. + const bindings = envRec.bindingObject; + // 3. Return ? Set(bindings, N, V, S). + return Q(Set(bindings, N, V, S)); + } + + // #sec-object-environment-records-getbindingvalue-n-s + GetBindingValue(N, S) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Let bindings be the binding object for envRec. + const bindings = envRec.bindingObject; + // 3. Let value be ? HasProperty(bindings, N). + const value = Q(HasProperty(bindings, N)); + // 4. If value is false, then + if (value === Value.false) { + // a. If S is false, return the value undefined; otherwise throw a ReferenceError exception. + if (S === Value.false) { + return Value.undefined; + } else { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + } + // 5. Return ? Get(bindings, N). + return Q(Get(bindings, N)); + } + + // #sec-object-environment-records-deletebinding-n + DeleteBinding(N) { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. Let bindings be the binding object for envRec. + const bindings = envRec.bindingObject; + // 3. Return ? bindings.[[Delete]](N). + return Q(bindings.Delete(N)); + } + + // #sec-object-environment-records-hasthisbinding + HasThisBinding() { + // 1. Return false. + return Value.false; + } + + // #sec-object-environment-records-hassuperbinding + HasSuperBinding() { + // 1. Return falase. + return Value.false; + } + + // #sec-object-environment-records-withbaseobject + WithBaseObject() { + // 1. Let envRec be the object Environment Record for which the method was invoked. + const envRec = this; + // 2. If the withEnvironment flag of envRec is true, return the binding object for envRec. + if (envRec.withEnvironment === true) { + return envRec.bindingObject; + } + // 3. Otherwise, return undefined. + return Value.undefined; + } + + // NON-SPEC + mark(m) { + m(this.bindingObject); + } +} + +// #sec-function-environment-records +export class FunctionEnvironmentRecord extends DeclarativeEnvironmentRecord { + constructor() { + super(); + this.ThisValue = undefined; + this.ThisBindingValue = undefined; + this.FunctionObject = undefined; + this.HomeObject = Value.undefined; + this.NewTarget = undefined; + } + + // #sec-bindthisvalue + BindThisValue(V) { + // 1. Let envRec be the function Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec.[[ThisBindingStatus]] is not lexical. + Assert(envRec.ThisBindingStatus !== 'lexical'); + // 3. If envRec.[[ThisBindingStatus]] is initialized, throw a ReferenceError exception. + if (envRec.ThisBindingStatus === 'initialized') { + return surroundingAgent.Throw('ReferenceError', 'InvalidThis'); + } + // 4. Set envRec.[[ThisValue]] to V. + envRec.ThisValue = V; + // 5. Set envRec.[[ThisBindingStatus]] to initialized. + envRec.ThisBindingStatus = 'initialized'; + // 6. Return V. + return V; + } + + // #sec-function-environment-records-hasthisbinding + HasThisBinding() { + // 1. Let envRec be the function Environment Record for which the method was invoked. + const envRec = this; + // 2. If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true. + if (envRec.ThisBindingStatus === 'lexical') { + return Value.false; + } else { + return Value.true; + } + } + + // #sec-function-environment-records-hassuperbinding + HasSuperBinding() { + // 1. Let envRec be the function Environment Record for which the method was invoked. + const envRec = this; + // 2. If envRec.[[ThisBindingStatus]] is lexical, return false. + if (envRec.ThisBindingStatus === 'lexical') { + return Value.false; + } + // 3. If envRec.[[HomeObject]] has the value undefined, return false; otherwise, return true. + if (Type(envRec.HomeObject) === 'Undefined') { + return Value.false; + } else { + return Value.true; + } + } + + // #sec-function-environment-records-getthisbinding + GetThisBinding() { + // 1. Let envRec be the function Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec.[[ThisBindingStatus]] is not lexical. + Assert(envRec.ThisBindingStatus !== 'lexical'); + // 3. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception. + if (envRec.ThisBindingStatus === 'uninitialized') { + return surroundingAgent.Throw('ReferenceError', 'InvalidThis'); + } + // 4. Return envRec.[[ThisValue]]. + return envRec.ThisValue; + } + + // #sec-getsuperbase + GetSuperBase() { + // 1. Let envRec be the function Environment Record for which the method was invoked. + const envRec = this; + // 2. Let home be envRec.[[HomeObject]]. + const home = envRec.HomeObject; + // 3. If home has the value undefined, return undefined. + if (Type(home) === 'Undefined') { + return Value.undefined; + } + // 4. Assert: Type(home) is Object. + Assert(Type(home) === 'Object'); + // 5. Return ? home.[[GetPrototypeOf]](). + return Q(home.GetPrototypeOf()); + } + + mark(m) { + super.mark(m); + m(this.ThisValue); + m(this.ThisBindingValue); + m(this.FunctionObject); + m(this.HomeObject); + m(this.NewTarget); + } +} + +// #sec-global-environment-records +export class GlobalEnvironmentRecord extends EnvironmentRecord { + constructor() { + super(); + this.ObjectRecord = undefined; + this.GlobalThisValue = undefined; + this.DeclarativeRecord = undefined; + this.VarNames = undefined; + } + + // #sec-global-environment-records-hasbinding-n + HasBinding(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, return true. + if (DclRec.HasBinding(N) === Value.true) { + return Value.true; + } + // 4. If DclRec.HasBinding(N) is true, return true. + const ObjRec = envRec.ObjectRecord; + // 5. Let ObjRec be envRec.[[ObjectRecord]]. + return ObjRec.HasBinding(N); + } + + // #sec-global-environment-records-createmutablebinding-n-d + CreateMutableBinding(N, D) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, throw a TypeError exception. + if (DclRec.HasBinding(N) === Value.true) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N); + } + // 4. Return DclRec.CreateMutableBinding(N, D). + return DclRec.CreateMutableBinding(N, D); + } + + // #sec-global-environment-records-createimmutablebinding-n-s + CreateImmutableBinding(N, S) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, throw a TypeError exception. + if (DclRec.HasBinding(N) === Value.true) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N); + } + // Return DclRec.CreateImmutableBinding(N, S). + return DclRec.CreateImmutableBinding(N, S); + } + + // #sec-global-environment-records-initializebinding-n-v + InitializeBinding(N, V) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, then + if (DclRec.HasBinding(N) === Value.true) { + // a. Return DclRec.InitializeBinding(N, V). + return DclRec.InitializeBinding(N, V); + } + // 4. Assert: If the binding exists, it must be in the object Environment Record. + // 5. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 6. Return ? ObjRec.InitializeBinding(N, V). + return ObjRec.InitializeBinding(N, V); + } + + // #sec-global-environment-records-setmutablebinding-n-v-s + SetMutableBinding(N, V, S) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, then + if (DclRec.HasBinding(N) === Value.true) { + // a. Return DclRec.SetMutableBinding(N, V, S). + return DclRec.SetMutableBinding(N, V, S); + } + // 4. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 5. Return ? ObjRec.SetMutableBinding(N, V, S). + return Q(ObjRec.SetMutableBinding(N, V, S)); + } + + // #sec-global-environment-records-getbindingvalue-n-s + GetBindingValue(N, S) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = envRec.DeclarativeRecord; + // 3. If DclRec.HasBinding(N) is true, then + if (DclRec.HasBinding(N) === Value.true) { + // a. Return DclRec.GetBindingValue(N, S). + return DclRec.GetBindingValue(N, S); + } + // 4. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 5. Return ? ObjRec.GetBindingValue(N, S). + return Q(ObjRec.GetBindingValue(N, S)); + } + + // #sec-global-environment-records-deletebinding-n + DeleteBinding(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let DclRec be envRec.[[DeclarativeRecord]]. + const DclRec = this.DeclarativeRecord; + // 3. Let DclRec be envRec.[[DeclarativeRecord]]. + if (DclRec.HasBinding(N) === Value.true) { + // a. Return DclRec.DeleteBinding(N). + return Q(DclRec.DeleteBinding(N)); + } + // 4. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 5. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 6. Let existingProp be ? HasOwnProperty(globalObject, N). + const existingProp = Q(HasOwnProperty(globalObject, N)); + // 7. If existingProp is true, then + if (existingProp === Value.true) { + // a. Let status be ? ObjRec.DeleteBinding(N). + const status = Q(ObjRec.DeleteBinding(N)); + // b. If status is true, then + if (status === Value.true) { + // i. Let varNames be envRec.[[VarNames]]. + const varNames = envRec.VarNames; + // ii. If N is an element of varNames, remove that element from the varNames. + if (varNames.includes(N)) { + varNames.splice(varNames.indexOf(N), 1); + } + } + // c. Return status. + return status; + } + // 8. Return true. + return Value.true; + } + + // #sec-global-environment-records-hasthisbinding + HasThisBinding() { + // Return true. + return Value.true; + } + + // #sec-global-environment-records-hassuperbinding + HasSuperBinding() { + // 1. Return false. + return Value.false; + } + + // #sec-global-environment-records-withbaseobject + WithBaseObject() { + // 1. Return undefined. + return Value.undefined; + } + + // #sec-global-environment-records-getthisbinding + GetThisBinding() { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Return envRec.[[GlobalThisValue]]. + return envRec.GlobalThisValue; + } + + // #sec-hasvardeclaration + HasVarDeclaration(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let varDeclaredNames be envRec.[[VarNames]]. + const varDeclaredNames = envRec.VarNames; + // 3. If varDeclaredNames contains N, return true. + if (varDeclaredNames.includes(N)) { + return Value.true; + } + // 4. Return false. + return Value.false; + } + + // #sec-haslexicaldeclaration + HasLexicalDeclaration(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let envRec be the global Environment Record for which the method was invoked. + const DclRec = envRec.DeclarativeRecord; + // 3. Let DclRec be envRec.[[DeclarativeRecord]]. + return DclRec.HasBinding(N); + } + + // #sec-hasrestrictedglobalproperty + HasRestrictedGlobalProperty(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 3. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N). + const existingProp = Q(globalObject.GetOwnProperty(N)); + // 5. If existingProp is undefined, return false. + if (existingProp === Value.undefined) { + return Value.false; + } + // 6. If existingProp.[[Configurable]] is true, return false. + if (existingProp.Configurable === Value.true) { + return Value.false; + } + // Return true. + return Value.true; + } + + // #sec-candeclareglobalvar + CanDeclareGlobalVar(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 3. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 4. Let hasProperty be ? HasOwnProperty(globalObject, N). + const hasProperty = Q(HasOwnProperty(globalObject, N)); + // 5. If hasProperty is true, return true. + if (hasProperty === Value.true) { + return Value.true; + } + // 6. Return ? IsExtensible(globalObject). + return Q(IsExtensible(globalObject)); + } + + // #sec-candeclareglobalfunction + CanDeclareGlobalFunction(N) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 3. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N). + const existingProp = Q(globalObject.GetOwnProperty(N)); + // 5. If existingProp is undefined, return ? IsExtensible(globalObject). + if (existingProp === Value.undefined) { + return Q(IsExtensible(globalObject)); + } + // 6. If existingProp.[[Configurable]] is true, return true. + if (existingProp.Configurable === Value.true) { + return Value.true; + } + // 7. If IsDataDescriptor(existingProp) is true and existingProp has attribute values + // { [[Writable]]: true, [[Enumerable]]: true }, return true. + if (IsDataDescriptor(existingProp) === true + && existingProp.Writable === Value.true + && existingProp.Enumerable === Value.true) { + return Value.true; + } + // 8. Return false. + return Value.false; + } + + // #sec-createglobalvarbinding + CreateGlobalVarBinding(N, D) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 3. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 4. Let hasProperty be ? HasOwnProperty(globalObject, N). + const hasProperty = Q(HasOwnProperty(globalObject, N)); + // 5. Let extensible be ? IsExtensible(globalObject). + const extensible = Q(IsExtensible(globalObject)); + // 6. If hasProperty is false and extensible is true, then + if (hasProperty === Value.false && extensible === Value.true) { + // a. Perform ? ObjRec.CreateMutableBinding(N, D). + Q(ObjRec.CreateMutableBinding(N, D)); + // b. Perform ? ObjRec.InitializeBinding(N, undefined). + Q(ObjRec.InitializeBinding(N, Value.undefined)); + } + // 7. Let varDeclaredNames be envRec.[[VarNames]]. + const varDeclaredNames = envRec.VarNames; + // 8. If varDeclaredNames does not contain N, then + if (!varDeclaredNames.includes(N)) { + // a. Append N to varDeclaredNames. + varDeclaredNames.push(N); + } + // return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + // #sec-createglobalfunctionbinding + CreateGlobalFunctionBinding(N, V, D) { + // 1. Let envRec be the global Environment Record for which the method was invoked. + const envRec = this; + // 2. Let ObjRec be envRec.[[ObjectRecord]]. + const ObjRec = envRec.ObjectRecord; + // 3. Let globalObject be the binding object for ObjRec. + const globalObject = ObjRec.bindingObject; + // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N). + const existingProp = Q(globalObject.GetOwnProperty(N)); + // 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then + let desc; + if (existingProp === Value.undefined || existingProp.Configurable === Value.true) { + // a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }. + desc = Descriptor({ + Value: V, + Writable: Value.true, + Enumerable: Value.true, + Configurable: D, + }); + } else { + // a. Let desc be the PropertyDescriptor { [[Value]]: V }. + desc = Descriptor({ + Value: V, + }); + } + // 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc). + Q(DefinePropertyOrThrow(globalObject, N, desc)); + // 8. Record that the binding for N in ObjRec has been initialized. + // 9. Perform ? Set(globalObject, N, V, false). + Q(Set(globalObject, N, V, Value.false)); + // 10. Let varDeclaredNames be envRec.[[VarNames]]. + const varDeclaredNames = envRec.VarNames; + // 11. If varDeclaredNames does not contain N, then + if (!varDeclaredNames.includes(N)) { + // a. Append N to varDeclaredNames. + varDeclaredNames.push(N); + } + // 1. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } + + mark(m) { + m(this.ObjectRecord); + m(this.GlobalThisValue); + m(this.DeclarativeRecord); + } +} + +// #sec-module-environment-records +export class ModuleEnvironmentRecord extends DeclarativeEnvironmentRecord { + // #sec-module-environment-records-getbindingvalue-n-s + GetBindingValue(N, S) { + // 1. Assert: S is true. + Assert(S === Value.true); + // 2. Let envRec be the module Environment Record for which the method was invoked. + const envRec = this; + // 3. Assert: envRec has a binding for N. + const binding = envRec.bindings.get(N); + Assert(binding !== undefined); + // 4. If the binding for N is an indirect binding, then + if (binding.indirect === true) { + // a. Let M and N2 be the indirection values provided when this binding for N was created. + const [M, N2] = binding.target; + // b.Let targetEnv be M.[[Environment]]. + const targetEnv = M.Environment; + // c. If targetEnv is undefined, throw a ReferenceError exception. + if (targetEnv === Value.undefined) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + // d. Let targetER be targetEnv's EnvironmentRecord. + const targetER = targetEnv.EnvironmentRecord; + // e. Return ? targetER.GetBindingValue(N2, true). + return Q(targetER.GetBindingValue(N2, Value.true)); + } + // 5. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception. + if (binding.initialized === false) { + return surroundingAgent.Throw('ReferenceError', 'NotDefined', N); + } + // 6. Return the value currently bound to N in envRec. + return binding.value; + } + + // #sec-module-environment-records-deletebinding-n + DeleteBinding() { + Assert(false, 'This method is never invoked. See #sec-delete-operator-static-semantics-early-errors'); + } + + // #sec-module-environment-records-hasthisbinding + HasThisBinding() { + // Return true. + return Value.true; + } + + // #sec-module-environment-records-getthisbinding + GetThisBinding() { + // Return undefined. + return Value.undefined; + } + + // #sec-createimportbinding + CreateImportBinding(N, M, N2) { + // 1. Let envRec be the module Environment Record for which the method was invoked. + const envRec = this; + // 2. Assert: envRec does not already have a binding for N. + Assert(envRec.HasBinding(N) === Value.false); + // 3. Assert: M is a Module Record. + Assert(M instanceof AbstractModuleRecord); + // 4. Assert: When M.[[Environment]] is instantiated it will have a direct binding for N2. + // 5. Create an immutable indirect binding in envRec for N that references M and N2 as its target binding and record that the binding is initialized. + envRec.bindings.set(N, { + indirect: true, + target: [M, N2], + initialized: true, + mark(m) { + m(this.target[0]); + m(this.target[1]); + }, + }); + // 6. Return NormalCompletion(empty). + return NormalCompletion(undefined); + } +} + +// 8.1.2.1 #sec-getidentifierreference +export function GetIdentifierReference(lex, name, strict) { + // 1. If lex is the value null, then + if (lex === Value.null) { + // a. Return a value of type Reference whose base value component is undefined, whose + // referenced name component is name, and whose strict reference flag is strict. + return new Reference({ + BaseValue: Value.undefined, + ReferencedName: name, + StrictReference: strict, + }); + } + // 2. Let envRec be lex's EnvironmentRecord. + const envRec = lex.EnvironmentRecord; + // 3. Let exists be ? envRec.HasBinding(name). + const exists = Q(envRec.HasBinding(name)); + // 4. If exists is true, then + if (exists === Value.true) { + // a. Return a value of type Reference whose base value component is envRec, whose + // referenced name component is name, and whose strict reference flag is strict. + return new Reference({ + BaseValue: envRec, + ReferencedName: name, + StrictReference: strict, + }); + } else { + // a. Let outer be the value of lex's outer environment reference. + const outer = lex.outerEnvironmentReference; + // b. Return ? GetIdentifierReference(outer, name, strict). + return Q(GetIdentifierReference(outer, name, strict)); + } +} + +// 8.1.2.2 #sec-newdeclarativeenvironment +export function NewDeclarativeEnvironment(E) { + // 1. Let env be a new Lexical Environment. + const env = new LexicalEnvironment(); + // 2. Let envRec be a new declarative Environment Record containing no bindings. + const envRec = new DeclarativeEnvironmentRecord(); + // 3. Set env's EnvironmentRecord to envRec. + env.EnvironmentRecord = envRec; + // 4. Set env's EnvironmentRecord to envRec. + env.outerEnvironmentReference = E; + // 5. Return env. + return env; +} + +// 8.1.2.3 #sec-newobjectenvironment +export function NewObjectEnvironment(O, E) { + // 1. Let env be a new Lexical Environment. + const env = new LexicalEnvironment(); + // 2. Let envRec be a new object Environment Record containing O as the binding object. + const envRec = new ObjectEnvironmentRecord(O); + // 3. Set env's EnvironmentRecord to envRec. + env.EnvironmentRecord = envRec; + // 4. Set env's EnvironmentRecord to envRec. + env.outerEnvironmentReference = E; + // 5. Return env. + return env; +} + +// 8.1.2.4 #sec-newfunctionenvironment +export function NewFunctionEnvironment(F, newTarget) { + // 1. Assert: F is an ECMAScript function. + Assert(isECMAScriptFunctionObject(F)); + // 2. Assert: Type(newTarget) is Undefined or Object. + Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object'); + // 3. Let env be a new Lexical Environment. + const env = new LexicalEnvironment(); + // 4. Let envRec be a new function Environment Record containing no bindings. + const envRec = new FunctionEnvironmentRecord(); + // 5. Set envRec.[[FunctionObject]] to F. + envRec.FunctionObject = F; + // 6. If F.[[ThisMode]] is lexical, set envRec.[[ThisBindingStatus]] to lexical. + // 7. Else, set envRec.[[ThisBindingStatus]] to uninitialized. + if (F.ThisMode === 'lexical') { + envRec.ThisBindingStatus = 'lexical'; + } else { + envRec.ThisBindingStatus = 'uninitialized'; + } + // 8. Let home be F.[[HomeObject]]. + const home = F.HomeObject; + // 9. Set envRec.[[HomeObject]] to home. + envRec.HomeObject = home; + // 10. Set envRec.[[NewTarget]] to newTarget. + envRec.NewTarget = newTarget; + // 11. Set env's EnvironmentRecord to envRec. + env.EnvironmentRecord = envRec; + // 12. Set the outer lexical environment reference of env to F.[[Environment]]. + env.outerEnvironmentReference = F.Environment; + // 13. Return env. + return env; +} + +// #sec-newglobalenvironment +export function NewGlobalEnvironment(G, thisValue) { + // 1. Let env be a new Lexical Environment. + const env = new LexicalEnvironment(); + // 2. Let objRec be a new object Environment Record containing G as the binding object. + const objRec = new ObjectEnvironmentRecord(G); + // 3. Let dclRec be a new declarative Environment Record containing no bindings. + const dclRec = new DeclarativeEnvironmentRecord(); + // 4. Let dclRec be a new declarative Environment Record containing no bindings. + const globalRec = new GlobalEnvironmentRecord(); + // 5. Set globalRec.[[ObjectRecord]] to objRec. + globalRec.ObjectRecord = objRec; + // 6. Set globalRec.[[GlobalThisValue]] to thisValue. + globalRec.GlobalThisValue = thisValue; + // 7. Set globalRec.[[DeclarativeRecord]] to dclRec. + globalRec.DeclarativeRecord = dclRec; + // 8. Set globalRec.[[VarNames]] to a new empty List. + globalRec.VarNames = []; + // 9. Set env's EnvironmentRecord to globalRec. + env.EnvironmentRecord = globalRec; + // 10. Set the outer lexical environment reference of env to null. + env.outerEnvironmentReference = Value.null; + // 11. Return env. + return env; +} + +// #sec-newmoduleenvironment +export function NewModuleEnvironment(E) { + // 1. Let env be a new Lexical Environment. + const env = new LexicalEnvironment(); + // 2. Let envRec be a new module Environment Record containing no bindings. + const envRec = new ModuleEnvironmentRecord(); + // 3. Set env's EnvironmentRecord to envRec. + env.EnvironmentRecord = envRec; + // 4. Set the outer lexical environment reference of env to E. + env.outerEnvironmentReference = E; + // 5. Return env. + return env; +} diff --git a/src/engine262/src/evaluator.mjs b/src/engine262/src/evaluator.mjs new file mode 100644 index 0000000..f5e67e0 --- /dev/null +++ b/src/engine262/src/evaluator.mjs @@ -0,0 +1,516 @@ +import { + Completion, + EnsureCompletion, + NormalCompletion, + ReturnIfAbrupt, + UpdateEmpty, +} from './completion.mjs'; +import { + isActualAdditiveExpression, + isActualAssignmentExpression, + isActualBitwiseANDExpression, + isActualBitwiseORExpression, + isActualBitwiseXORExpression, + isActualCallExpression, + isActualCoalesceExpression, + isActualConditionalExpression, + isActualEqualityExpression, + isActualExponentiationExpression, + isActualLogicalANDExpression, + isActualLogicalORExpression, + isActualMemberExpression, + isActualMultiplicativeExpression, + isActualNewExpression, + isActualRelationalExpression, + isActualShiftExpression, + isActualUnaryExpression, + isActualUpdateExpression, + isArrayLiteral, + isArrowFunction, + isAsyncArrowFunction, + isAsyncFunctionExpression, + isAsyncGeneratorExpression, + isAwaitExpression, + isBlockStatement, + isBreakStatement, + isBreakableStatement, + isClassDeclaration, + isClassExpression, + isContinueStatement, + isDebuggerStatement, + isDeclaration, + isEmptyStatement, + isExportDeclaration, + isExpression, + isExpressionStatement, + isExpressionWithComma, + isFunctionExpression, + isImportCall, + isGeneratorExpression, + isHoistableDeclaration, + isIdentifierReference, + isIfStatement, + isImportDeclaration, + isLabelledStatement, + isLexicalDeclaration, + isLiteral, + isMetaProperty, + isObjectLiteral, + isOptionalExpression, + isParenthesizedExpression, + isRegularExpressionLiteral, + isReturnStatement, + isStatement, + isSuperCall, + isSuperProperty, + isTaggedTemplate, + isTemplateLiteral, + isThis, + isThrowStatement, + isTryStatement, + isVariableStatement, + isWithStatement, + isYieldExpression, +} from './ast.mjs'; +import { + EvaluateBinopValues_AdditiveExpression_Minus, + EvaluateBinopValues_AdditiveExpression_Plus, + EvaluateBinopValues_BitwiseANDExpression, + EvaluateBinopValues_BitwiseORExpression, + EvaluateBinopValues_BitwiseXORExpression, + EvaluateBinopValues_ExponentiationExpression, + EvaluateBinopValues_MultiplicativeExpression, + EvaluateBinopValues_ShiftExpression, + Evaluate_AdditiveExpression, + Evaluate_ArrayLiteral, + Evaluate_ArrowFunction, + Evaluate_AssignmentExpression, + Evaluate_AsyncArrowFunction, + Evaluate_AsyncFunctionExpression, + Evaluate_AsyncGeneratorExpression, + Evaluate_AwaitExpression, + Evaluate_BinaryBitwiseExpression, + Evaluate_BlockStatement, + Evaluate_BreakStatement, + Evaluate_BreakableStatement, + Evaluate_CallExpression, + Evaluate_ClassDeclaration, + Evaluate_ClassExpression, + Evaluate_CoalesceExpression, + Evaluate_ConditionalExpression, + Evaluate_ContinueStatement, + Evaluate_DebuggerStatement, + Evaluate_EmptyStatement, + Evaluate_EqualityExpression, + Evaluate_ExponentiationExpression, + Evaluate_ExpressionWithComma, + Evaluate_ExportDeclaration, + Evaluate_FunctionExpression, + Evaluate_GeneratorExpression, + Evaluate_HoistableDeclaration, + Evaluate_Identifier, + Evaluate_IfStatement, + Evaluate_ImportCall, + Evaluate_LabelledStatement, + Evaluate_LexicalDeclaration, + Evaluate_Literal, + Evaluate_LogicalANDExpression, + Evaluate_LogicalORExpression, + Evaluate_MemberExpression, + Evaluate_MetaProperty, + Evaluate_MultiplicativeExpression, + Evaluate_NewExpression, + Evaluate_ObjectLiteral, + Evaluate_OptionalExpression, + Evaluate_RegularExpressionLiteral, + Evaluate_RelationalExpression, + Evaluate_ReturnStatement, + Evaluate_ShiftExpression, + Evaluate_SuperCall, + Evaluate_SuperProperty, + Evaluate_TaggedTemplate, + Evaluate_TemplateLiteral, + Evaluate_ThisExpression, + Evaluate_ThrowStatement, + Evaluate_TryStatement, + Evaluate_UnaryExpression, + Evaluate_UpdateExpression, + Evaluate_VariableStatement, + Evaluate_WithStatement, + Evaluate_YieldExpression, +} from './runtime-semantics/all.mjs'; +import { Value } from './value.mjs'; +import { GetValue } from './abstract-ops/all.mjs'; +import { surroundingAgent } from './engine.mjs'; +import { unwind, OutOfRange } from './helpers.mjs'; + +// 13.2.13 #sec-block-runtime-semantics-evaluation +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function* Evaluate_StatementList(StatementList) { + if (StatementList.length === 0) { + return new NormalCompletion(undefined); + } + + let sl = yield* Evaluate(StatementList[0]); + if (StatementList.length === 1) { + return sl; + } + + for (const StatementListItem of StatementList.slice(1)) { + ReturnIfAbrupt(sl); + let s = yield* Evaluate(StatementListItem); + // We don't always return a Completion value, but here we actually need it + // to be a Completion. + s = EnsureCompletion(s); + sl = UpdateEmpty(s, sl); + } + + return sl; +} + +// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation +// ModuleItemList : +// ModuleItem +// ModuleItemList ModuleItem +export function* Evaluate_ModuleItemList(ModuleItemList) { + let sl = yield* Evaluate(ModuleItemList[0]); + if (ModuleItemList.length === 1) { + return sl; + } + + for (const ModuleItemListItem of ModuleItemList.slice(1)) { + ReturnIfAbrupt(sl); + let s = yield* Evaluate(ModuleItemListItem); + // We don't always return a Completion value, but here we actually need it + // to be a Completion. + s = EnsureCompletion(s); + sl = UpdateEmpty(s, sl); + } + + return sl; +} + +// (implicit) +// StatementListItem : +// Statement +// Declaration +// +// Statement : +// BlockStatement +// ExpressionStatement +// VariableStatement +// EmptyStatement +// ExpressionStatement +// IfStatement +// BreakableStatement +// ContinueStatement +// BreakStatement +// ReturnStatement +// WithStatement +// LabelledStatement +// ThrowStatement +// TryStatement +// DebuggerStatement +// +// Declaration : +// HoistableDeclaration +// ClassDeclaration +// LexicalDeclaration +function* Evaluate_StatementListItem(StatementListItem) { + switch (true) { + case isBlockStatement(StatementListItem): + return yield* Evaluate_BlockStatement(StatementListItem); + + case isVariableStatement(StatementListItem): + return yield* Evaluate_VariableStatement(StatementListItem); + + case isEmptyStatement(StatementListItem): + return Evaluate_EmptyStatement(StatementListItem); + + case isExpressionStatement(StatementListItem): + return yield* Evaluate_ExpressionStatement(StatementListItem); + + case isIfStatement(StatementListItem): + return yield* Evaluate_IfStatement(StatementListItem); + + case isBreakableStatement(StatementListItem): + return yield* Evaluate_BreakableStatement(StatementListItem); + + case isContinueStatement(StatementListItem): + return Evaluate_ContinueStatement(StatementListItem); + + case isBreakStatement(StatementListItem): + return Evaluate_BreakStatement(StatementListItem); + + case isReturnStatement(StatementListItem): + return yield* Evaluate_ReturnStatement(StatementListItem); + + case isWithStatement(StatementListItem): + return yield* Evaluate_WithStatement(StatementListItem); + + case isLabelledStatement(StatementListItem): + return yield* Evaluate_LabelledStatement(StatementListItem); + + case isThrowStatement(StatementListItem): + return yield* Evaluate_ThrowStatement(StatementListItem.argument); + + case isTryStatement(StatementListItem): + return yield* Evaluate_TryStatement(StatementListItem); + + case isDebuggerStatement(StatementListItem): + return Evaluate_DebuggerStatement(StatementListItem); + + case isHoistableDeclaration(StatementListItem): + return Evaluate_HoistableDeclaration(StatementListItem); + + case isClassDeclaration(StatementListItem): + return yield* Evaluate_ClassDeclaration(StatementListItem); + + case isLexicalDeclaration(StatementListItem): + return yield* Evaluate_LexicalDeclaration(StatementListItem); + + default: + throw new OutOfRange('Evaluate_StatementListItem', StatementListItem); + } +} + +export const Evaluate_Statement = Evaluate_StatementListItem; + +// 13.5.1 #sec-expression-statement-runtime-semantics-evaluation +// ExpressionStatement : Expression `;` +function* Evaluate_ExpressionStatement(ExpressionStatement) { + const exprRef = yield* Evaluate(ExpressionStatement.expression); + return GetValue(exprRef); +} + +export function EvaluateBinopValues(operator, lval, rval) { + switch (operator) { + case '*': + case '/': + case '%': + return EvaluateBinopValues_MultiplicativeExpression(operator, lval, rval); + + case '+': + return EvaluateBinopValues_AdditiveExpression_Plus(lval, rval); + + case '-': + return EvaluateBinopValues_AdditiveExpression_Minus(lval, rval); + + case '<<': + case '>>': + case '>>>': + return EvaluateBinopValues_ShiftExpression(operator, lval, rval); + + case '&': + return EvaluateBinopValues_BitwiseANDExpression(lval, rval); + case '^': + return EvaluateBinopValues_BitwiseXORExpression(lval, rval); + case '|': + return EvaluateBinopValues_BitwiseORExpression(lval, rval); + + case '**': + return EvaluateBinopValues_ExponentiationExpression(lval, rval); + + default: + throw new OutOfRange('EvaluateBinopValues', operator); + } +} + +export function* Evaluate_Expression(Expression) { + return EnsureCompletion(yield* Inner_Evaluate_Expression(Expression)); +} + +// (implicit) +function* Inner_Evaluate_Expression(Expression) { + switch (true) { + case isThis(Expression): + return Evaluate_ThisExpression(Expression); + + case isIdentifierReference(Expression): + return Evaluate_Identifier(Expression); + + case isLiteral(Expression): + return Evaluate_Literal(Expression); + + case isArrayLiteral(Expression): + return yield* Evaluate_ArrayLiteral(Expression); + + case isObjectLiteral(Expression): + return yield* Evaluate_ObjectLiteral(Expression); + + case isFunctionExpression(Expression): + return Evaluate_FunctionExpression(Expression); + + case isClassExpression(Expression): + return yield* Evaluate_ClassExpression(Expression); + + case isGeneratorExpression(Expression): + return Evaluate_GeneratorExpression(Expression); + + case isAsyncFunctionExpression(Expression): + return Evaluate_AsyncFunctionExpression(Expression); + + case isAsyncGeneratorExpression(Expression): + return Evaluate_AsyncGeneratorExpression(Expression); + + case isRegularExpressionLiteral(Expression): + return Evaluate_RegularExpressionLiteral(Expression); + + case isTemplateLiteral(Expression): + return yield* Evaluate_TemplateLiteral(Expression); + + case isActualMemberExpression(Expression): + return yield* Evaluate_MemberExpression(Expression); + + case isOptionalExpression(Expression): + return yield* Evaluate_OptionalExpression(Expression); + + case isSuperProperty(Expression): + return yield* Evaluate_SuperProperty(Expression); + + case isSuperCall(Expression): + return yield* Evaluate_SuperCall(Expression); + + case isImportCall(Expression): + return yield* Evaluate_ImportCall(Expression); + + case isTaggedTemplate(Expression): + return yield* Evaluate_TaggedTemplate(Expression); + + case isMetaProperty(Expression): + return yield* Evaluate_MetaProperty(Expression); + + case isActualNewExpression(Expression): + return yield* Evaluate_NewExpression(Expression); + + case isActualCallExpression(Expression): + return yield* Evaluate_CallExpression(Expression); + + case isActualUpdateExpression(Expression): + return yield* Evaluate_UpdateExpression(Expression); + + case isActualUnaryExpression(Expression): + return yield* Evaluate_UnaryExpression(Expression); + + case isAwaitExpression(Expression): + return yield* Evaluate_AwaitExpression(Expression); + + case isActualExponentiationExpression(Expression): + return yield* Evaluate_ExponentiationExpression(Expression); + + case isActualMultiplicativeExpression(Expression): + return yield* Evaluate_MultiplicativeExpression(Expression); + + case isActualAdditiveExpression(Expression): + return yield* Evaluate_AdditiveExpression(Expression); + + case isActualShiftExpression(Expression): + return yield* Evaluate_ShiftExpression(Expression); + + case isActualRelationalExpression(Expression): + return yield* Evaluate_RelationalExpression(Expression); + + case isActualEqualityExpression(Expression): + return yield* Evaluate_EqualityExpression(Expression); + + case isActualBitwiseANDExpression(Expression): + case isActualBitwiseXORExpression(Expression): + case isActualBitwiseORExpression(Expression): + return yield* Evaluate_BinaryBitwiseExpression(Expression); + + case isActualLogicalANDExpression(Expression): + return yield* Evaluate_LogicalANDExpression(Expression); + + case isActualLogicalORExpression(Expression): + return yield* Evaluate_LogicalORExpression(Expression); + + case isActualCoalesceExpression(Expression): + return yield* Evaluate_CoalesceExpression(Expression); + + case isActualConditionalExpression(Expression): + return yield* Evaluate_ConditionalExpression(Expression); + + case isYieldExpression(Expression): + return yield* Evaluate_YieldExpression(Expression); + + case isArrowFunction(Expression): + return Evaluate_ArrowFunction(Expression); + + case isAsyncArrowFunction(Expression): + return Evaluate_AsyncArrowFunction(Expression); + + case isActualAssignmentExpression(Expression): + return yield* Evaluate_AssignmentExpression(Expression); + + case isExpressionWithComma(Expression): + return yield* Evaluate_ExpressionWithComma(Expression); + + // 12.2.10.5 #sec-grouping-operator-runtime-semantics-evaluation + case isParenthesizedExpression(Expression): + return yield* Evaluate(Expression.expression); + + default: + throw new OutOfRange('Evaluate_Expression', Expression); + } +} + +// 15.1.7 #sec-script-semantics-runtime-semantics-evaluation +// Script : [empty] +// +// (implicit) +// Script : ScriptBody +// ScriptBody : StatementList +export function Evaluate_Script(Script) { + if (Script.length === 0) { + return new NormalCompletion(Value.undefined); + } + return unwind(Evaluate_StatementList(Script)); +} + +// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation +// ModuleBody : ModuleItemList +export function* Evaluate_ModuleBody(ModuleBody) { + const ModuleItemList = ModuleBody; + const result = EnsureCompletion(yield* Evaluate_ModuleItemList(ModuleItemList)); + if (result.Type === 'normal' && result.Value === undefined) { + return new NormalCompletion(Value.undefined); + } + return Completion(result); +} + +// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export function Evaluate_Module(Module) { + if (Module.length === 0) { + return new NormalCompletion(Value.undefined); + } + return unwind(Evaluate_ModuleBody(Module)); +} + +export function* Evaluate(Production) { + surroundingAgent.runningExecutionContext.callSite.setLocation(Production); + + if (surroundingAgent.hostDefinedOptions.onNodeEvaluation) { + surroundingAgent.hostDefinedOptions.onNodeEvaluation(Production, surroundingAgent.currentRealmRecord); + } + + switch (true) { + case isImportDeclaration(Production): + return new NormalCompletion(undefined); + case isExportDeclaration(Production): + return yield* Evaluate_ExportDeclaration(Production); + case isStatement(Production): + case isDeclaration(Production): + return yield* Evaluate_Statement(Production); + case isExpression(Production): + return yield* Evaluate_Expression(Production); + default: + throw new OutOfRange('Evaluate', Production); + } +} diff --git a/src/engine262/src/grammar/Scientific.mjs b/src/engine262/src/grammar/Scientific.mjs new file mode 100644 index 0000000..f922319 --- /dev/null +++ b/src/engine262/src/grammar/Scientific.mjs @@ -0,0 +1,212 @@ +/* eslint-disable no-bitwise */ + +// Divide a non-negative `num` by a positive `den`. The quotient is rounded to +// its nearest integer, or the even integer if there are two equally near +// integer. +function roundQuotientBigInt(num, den) { + const quo = num / den; + const rem = num % den; + const rem2 = rem * 2n; + if (rem2 > den || (rem2 === den && quo % 2n !== 0n)) { + return quo + 1n; + } else { + return quo; + } +} + +const throwawayArray = new Float64Array(1); +const throwawayArrayInt = new Uint32Array(throwawayArray.buffer); + +// Find out if the host's [[BigEndian]] is true or false, by checking the +// representation for -0. +throwawayArray[0] = -0; +const float64High = throwawayArrayInt[0] === 0 ? 1 : 0; + +// Return x * 2 ** exp where x is a Number, and exp is an integer. +// +// Derived from +// https://github.com/JuliaMath/openlibm/blob/0f22aeb0a9104c52106f42ce1fa8ebe96fb498f1/src/s_scalbn.c. +// +// License: +// +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +function scalb(x, exp) { + if (x === 0 || exp === 0 || !Number.isFinite(x)) { + return x; + } + if (exp >= 2000) { + return x * Infinity; + } else if (exp <= -2000) { + return x * 0; + } + + throwawayArray[0] = x; + let origExp = (throwawayArrayInt[float64High] >>> 20) & 0x7ff; + if (origExp === 0) { + // x is denormalized. Multiply x by 2**54 (1 + number of mantissa bits), + // and correspondingly reduce exp by 54. + throwawayArray[0] *= 18014398509481984; + exp -= 54; + if (exp === 0) { + return throwawayArray[0]; + } + origExp = (throwawayArrayInt[float64High] >>> 20) & 0x7ff; + } + const newExp = origExp + exp; + if (newExp > 0x7fe) { + // Overflow. Return Infinity, but of the appropriate sign. + return throwawayArray[0] * Infinity; + } + if (newExp > 0) { + // Normalized, okay. + throwawayArrayInt[float64High] ^= (origExp ^ newExp) << 20; + return throwawayArray[0]; + } + if (newExp <= -54) { + // Underflow. Return 0, but of the appropriate sign. + return throwawayArray[0] * 0; + } + // Denormalized result. Add 54 to newExp and multiply the resultant number by + // 2**-54. + throwawayArrayInt[float64High] ^= (origExp ^ (newExp + 54)) << 20; + return throwawayArray[0] * 5.55111512312578270212e-17; +} + +// Return the minimum number of bits it takes to store `bint`. This function +// assumes a host implementation on which a BigInt value cannot exceed 2^(4n), +// where n is the maximum length of a String value on that host (usually around +// 2^31, but can be up to 2^53 - 1 by spec). +function bitLengthBigInt(bint) { + if (bint < 0n) { + bint = -bint; + } + let increment = 0; + if (bint > ((1n << 32n) - 1n)) { + // This number is larger than 2^32 - 1, which is just huge. Let's form an + // estimate of how many bits it requires first, accurate to the nearest + // multiple of log2(16) = 4, by converting it to a hexadecimal string and + // measuring the resulting length. + const hexLength = bint.toString(16).length; + const estimatedBitLength = (hexLength - 1) * 4; + increment += estimatedBitLength; + bint >>= BigInt(estimatedBitLength); + } + // As we are sure that bint is within the range of an unsigned 32-bit + // integer, we can use Math.clz32(). + return 32 - Math.clz32(Number(bint)) + increment; +} + +function approximateLog10BigInt(bint) { + return bint.toString(10).length; +} + +// Number of mantissa bits in a IEEE 754-2008 binary64 value. +const MANTISSA_BITS = 53; + +// A class representing a decimal number in scientific notation, or otherwise +// known as a decimal floating-point number. +export default class Scientific { + constructor(num, exp = 0n) { + if (typeof num !== 'bigint') { // eslint-disable-line valid-typeof + throw new TypeError('Numerator must be a BigInt'); + } + if (typeof exp !== 'bigint') { // eslint-disable-line valid-typeof + throw new TypeError('Numerator must be a BigInt'); + } + this.num = num; + this.exp = exp; + } + + negate() { + return new this.constructor(-this.num, this.exp); + } + + convExp(exp) { + if (this.exp === exp) { + return this; + } else if (this.exp > exp) { + return new this.constructor(this.num * (10n ** (this.exp - exp)), exp); + } + throw new RangeError('Requested exponent must be less than or equal to the current exponent'); + } + + expAdd(e) { + return new this.constructor(this.num, this.exp + e); + } + + addSci(sci) { + const expectedExp = this.exp < sci.exp ? this.exp : sci.exp; + const conv1 = this.convExp(expectedExp); + const conv2 = sci.convExp(expectedExp); + return new this.constructor(conv1.num + conv2.num, expectedExp); + } + + // Derived from "Easy Accurate Reading and Writing of Floating-Point Numbers" + // by Aubrey Jaffer, . + toNumber() { + if (this.num === 0n) { + return 0; + } + + if (this.num < 0) { + return -new this.constructor(-this.num, this.exp).toNumber(); + } + + let { num, exp } = this; + + // According to V8, the "Maximum number of significant digits in decimal + // representation" for a binary64 value is 772. See [1]. Let's first make + // sure we have a reasonably small this.num (≤ 10**800) while not losing + // accuracy, so that we can fast-path numbers with astronomical exponents. + // + // [1]: https://cs.chromium.org/chromium/src/v8/src/conversions.cc?l=565-571&rcl=dadf4cbe89c1e9ee9fed6181216cb4d3ba647a68 + const approximateDecimalDigits = approximateLog10BigInt(this.num); + if (approximateDecimalDigits > 800) { + const comp = BigInt(approximateDecimalDigits - 800); + // We don't care about rounding as we still have quite a large margin of + // error. + num /= 10n ** comp; + exp += comp; + } + + if (exp > 310n) { + // Largest possible value is < 2e308. + return Infinity; + } else if (exp < -1150n) { + // Smallest possible value is 5e-324, but num may be at most 1e801, so we + // are slightly more careful and only fast-path truly miniscule + // exponents. + return 0; + } + + const expNum = Number(exp); + if (expNum >= 0) { + const numScaled = num * (5n ** exp); + const bex = bitLengthBigInt(numScaled) - MANTISSA_BITS; + if (bex <= 0) { + return scalb(Number(numScaled), expNum); + } + const quo = roundQuotientBigInt(numScaled, 1n << BigInt(bex)); + return scalb(Number(quo), bex + expNum); + } + const scl = 5n ** -exp; + let mantlen = MANTISSA_BITS; + let bex = bitLengthBigInt(num) - bitLengthBigInt(scl) - mantlen; + const tmp = bex + expNum + 1021 + mantlen; + if (tmp < 0) { + bex -= tmp + 1; + mantlen += tmp; + } + const numScaled = num << BigInt(-bex); + let quo = roundQuotientBigInt(numScaled, scl); + if (bitLengthBigInt(quo) > mantlen) { + bex += 1; + quo = roundQuotientBigInt(numScaled, scl << 1n); + } + return scalb(Number(quo), bex + expNum); + } +} diff --git a/src/engine262/src/grammar/StrNumericLiteral-gen.mjs b/src/engine262/src/grammar/StrNumericLiteral-gen.mjs new file mode 100644 index 0000000..412a359 --- /dev/null +++ b/src/engine262/src/grammar/StrNumericLiteral-gen.mjs @@ -0,0 +1,116 @@ +// Generated automatically by nearley, version 2.19.0 +// http://github.com/Hardmath123/nearley +function id(x) { return x[0]; } + +import Scientific from './Scientific.mjs'; +function c(val) { + return () => val; +} +let Lexer = undefined; +let ParserRules = [ + {"name": "StrNumericLiteral", "symbols": ["StrDecimalLiteral"], "postprocess": ([StrDecimalLiteral]) => StrDecimalLiteral}, + {"name": "StrNumericLiteral", "symbols": ["BinaryIntegerLiteral"], "postprocess": ([BinaryIntegerLiteral]) => BinaryIntegerLiteral}, + {"name": "StrNumericLiteral", "symbols": ["OctalIntegerLiteral"], "postprocess": ([OctalIntegerLiteral]) => OctalIntegerLiteral}, + {"name": "StrNumericLiteral", "symbols": ["HexIntegerLiteral"], "postprocess": ([HexIntegerLiteral]) => HexIntegerLiteral}, + {"name": "StrDecimalLiteral", "symbols": ["StrUnsignedDecimalLiteral"], "postprocess": ([StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral}, + {"name": "StrDecimalLiteral", "symbols": [{"literal":"+"}, "StrUnsignedDecimalLiteral"], "postprocess": ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral}, + {"name": "StrDecimalLiteral", "symbols": [{"literal":"-"}, "StrUnsignedDecimalLiteral"], "postprocess": ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral.negate()}, + {"name": "StrUnsignedDecimalLiteral$string$1", "symbols": [{"literal":"I"}, {"literal":"n"}, {"literal":"f"}, {"literal":"i"}, {"literal":"n"}, {"literal":"i"}, {"literal":"t"}, {"literal":"y"}], "postprocess": function joiner(d) {return d.join('');}}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["StrUnsignedDecimalLiteral$string$1"], "postprocess": () => new Scientific(1n, 10000n)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}], "postprocess": ([[DecimalDigits]]) => new Scientific(DecimalDigits)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "DecimalDigits"], "postprocess": ([[first], _, [second, n]]) => new Scientific(first).addSci(new Scientific(second, -n))}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "ExponentPart"], "postprocess": ([[DecimalDigits], _, e]) => new Scientific(DecimalDigits, e)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([[first], _, [second, n], e]) => (new Scientific(first).addSci(new Scientific(second, -n))).expAdd(e)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits"], "postprocess": ([[DecimalDigits]]) => new Scientific(DecimalDigits)}, + {"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", "ExponentPart"], "postprocess": ([[DecimalDigits], e]) => new Scientific(DecimalDigits, e)}, + {"name": "NumericLiteral", "symbols": ["DecimalLiteral"], "postprocess": ([DecimalLiteral]) => DecimalLiteral}, + {"name": "NumericLiteral", "symbols": ["BinaryIntegerLiteral"], "postprocess": ([BinaryIntegerLiteral]) => BinaryIntegerLiteral}, + {"name": "NumericLiteral", "symbols": ["OctalIntegerLiteral"], "postprocess": ([OctalIntegerLiteral]) => OctalIntegerLiteral}, + {"name": "NumericLiteral", "symbols": ["HexIntegerLiteral"], "postprocess": ([HexIntegerLiteral]) => HexIntegerLiteral}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}], "postprocess": ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral)}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "DecimalDigits"], "postprocess": ([DecimalIntegerLiteral, _, [DecimalDigits, n]]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n))}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, _, e]) => new Scientific(DecimalIntegerLiteral, e)}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, _, [DecimalDigits, n], e]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)).expAdd(e)}, + {"name": "DecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n)}, + {"name": "DecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n)}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral"], "postprocess": ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral)}, + {"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, e]) => new Scientific(DecimalIntegerLiteral, e)}, + {"name": "DecimalIntegerLiteral", "symbols": [{"literal":"0"}], "postprocess": c(0n)}, + {"name": "DecimalIntegerLiteral", "symbols": ["NonZeroDigit"], "postprocess": ([NonZeroDigit]) => NonZeroDigit}, + {"name": "DecimalIntegerLiteral", "symbols": ["NonZeroDigit", "DecimalDigits"], "postprocess": ([NonZeroDigit, [DecimalDigits, n]]) => NonZeroDigit * (10n ** n) + DecimalDigits}, + {"name": "DecimalDigits", "symbols": ["DecimalDigit"], "postprocess": ([DecimalDigit]) => [DecimalDigit, 1n]}, + {"name": "DecimalDigits", "symbols": ["DecimalDigits", "DecimalDigit"], "postprocess": ([[DecimalDigits, n], DecimalDigit]) => [DecimalDigits * 10n + DecimalDigit, n + 1n]}, + {"name": "DecimalDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)}, + {"name": "DecimalDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)}, + {"name": "NonZeroDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)}, + {"name": "ExponentPart", "symbols": ["ExponentIndicator", "SignedInteger"], "postprocess": ([_, SignedInteger]) => SignedInteger}, + {"name": "ExponentIndicator$subexpression$1", "symbols": [/[eE]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "ExponentIndicator", "symbols": ["ExponentIndicator$subexpression$1"]}, + {"name": "SignedInteger", "symbols": ["DecimalDigits"], "postprocess": ([[DecimalDigits]]) => DecimalDigits}, + {"name": "SignedInteger", "symbols": [{"literal":"+"}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits]]) => DecimalDigits}, + {"name": "SignedInteger", "symbols": [{"literal":"-"}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits]]) => -DecimalDigits}, + {"name": "BinaryIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[bB]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "BinaryIntegerLiteral", "symbols": ["BinaryIntegerLiteral$subexpression$1", "BinaryDigits"], "postprocess": ([_, BinaryDigits]) => new Scientific(BinaryDigits)}, + {"name": "BinaryDigits", "symbols": ["BinaryDigit"], "postprocess": ([BinaryDigit]) => BinaryDigit}, + {"name": "BinaryDigits", "symbols": ["BinaryDigits", "BinaryDigit"], "postprocess": ([BinaryDigits, BinaryDigit]) => BinaryDigits * 2n + BinaryDigit}, + {"name": "BinaryDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)}, + {"name": "BinaryDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)}, + {"name": "OctalIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[oO]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "OctalIntegerLiteral", "symbols": ["OctalIntegerLiteral$subexpression$1", "OctalDigits"], "postprocess": ([_, OctalDigits]) => new Scientific(OctalDigits)}, + {"name": "OctalDigits", "symbols": ["OctalDigit"], "postprocess": ([OctalDigit]) => OctalDigit}, + {"name": "OctalDigits", "symbols": ["OctalDigits", "OctalDigit"], "postprocess": ([OctalDigits, OctalDigit]) => OctalDigits * 8n + OctalDigit}, + {"name": "OctalDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)}, + {"name": "OctalDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)}, + {"name": "OctalDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)}, + {"name": "OctalDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)}, + {"name": "OctalDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)}, + {"name": "OctalDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)}, + {"name": "OctalDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)}, + {"name": "OctalDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)}, + {"name": "HexIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[xX]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexIntegerLiteral", "symbols": ["HexIntegerLiteral$subexpression$1", "HexDigits"], "postprocess": ([_, HexDigits]) => new Scientific(HexDigits)}, + {"name": "HexDigits", "symbols": ["HexDigit"], "postprocess": ([HexDigit]) => HexDigit}, + {"name": "HexDigits", "symbols": ["HexDigits", "HexDigit"], "postprocess": ([HexDigits, HexDigit]) => HexDigits * 16n + HexDigit}, + {"name": "HexDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)}, + {"name": "HexDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)}, + {"name": "HexDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)}, + {"name": "HexDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)}, + {"name": "HexDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)}, + {"name": "HexDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)}, + {"name": "HexDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)}, + {"name": "HexDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)}, + {"name": "HexDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)}, + {"name": "HexDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)}, + {"name": "HexDigit$subexpression$1", "symbols": [/[aA]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$1"], "postprocess": c(10n)}, + {"name": "HexDigit$subexpression$2", "symbols": [/[bB]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$2"], "postprocess": c(11n)}, + {"name": "HexDigit$subexpression$3", "symbols": [/[cC]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$3"], "postprocess": c(12n)}, + {"name": "HexDigit$subexpression$4", "symbols": [/[dD]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$4"], "postprocess": c(13n)}, + {"name": "HexDigit$subexpression$5", "symbols": [/[eE]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$5"], "postprocess": c(14n)}, + {"name": "HexDigit$subexpression$6", "symbols": [/[fF]/], "postprocess": function(d) {return d.join(""); }}, + {"name": "HexDigit", "symbols": ["HexDigit$subexpression$6"], "postprocess": c(15n)} +]; +let ParserStart = "StrNumericLiteral"; +export default { Lexer, ParserRules, ParserStart }; diff --git a/src/engine262/src/grammar/StrNumericLiteral.ne b/src/engine262/src/grammar/StrNumericLiteral.ne new file mode 100644 index 0000000..f994e7d --- /dev/null +++ b/src/engine262/src/grammar/StrNumericLiteral.ne @@ -0,0 +1,163 @@ +@preprocessor esmodule + +@{% +import Scientific from './Scientific.mjs'; +function c(val) { + return () => val; +} +%} + +################################################## +# 7.1.3.1 #sec-tonumber-applied-to-the-string-type + +StrNumericLiteral -> + StrDecimalLiteral {% ([StrDecimalLiteral]) => StrDecimalLiteral %} + | BinaryIntegerLiteral {% ([BinaryIntegerLiteral]) => BinaryIntegerLiteral %} + | OctalIntegerLiteral {% ([OctalIntegerLiteral]) => OctalIntegerLiteral %} + | HexIntegerLiteral {% ([HexIntegerLiteral]) => HexIntegerLiteral %} + +# #prod-StrDecimalLiteral +StrDecimalLiteral -> + StrUnsignedDecimalLiteral {% ([StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral %} + | "+" StrUnsignedDecimalLiteral {% ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral %} + | "-" StrUnsignedDecimalLiteral {% ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral.negate() %} + +# #prod-StrUnsignedDecimalLiteral +StrUnsignedDecimalLiteral -> + "Infinity" {% () => new Scientific(1n, 10000n) %} + | DecimalDigits "." {% ([[DecimalDigits]]) => new Scientific(DecimalDigits) %} + | DecimalDigits "." DecimalDigits {% ([[first], _, [second, n]]) => new Scientific(first).addSci(new Scientific(second, -n)) %} + | DecimalDigits "." ExponentPart {% ([[DecimalDigits], _, e]) => new Scientific(DecimalDigits, e) %} + | DecimalDigits "." DecimalDigits ExponentPart {% ([[first], _, [second, n], e]) => (new Scientific(first).addSci(new Scientific(second, -n))).expAdd(e) %} + | "." DecimalDigits {% ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n) %} + | "." DecimalDigits ExponentPart {% ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n) %} + | DecimalDigits {% ([[DecimalDigits]]) => new Scientific(DecimalDigits) %} + | DecimalDigits ExponentPart {% ([[DecimalDigits], e]) => new Scientific(DecimalDigits, e) %} + +####################################### +# 11.8.3 #sec-literals-numeric-literals + +# #prod-NumericLiteral +NumericLiteral -> + DecimalLiteral {% ([DecimalLiteral]) => DecimalLiteral %} + | BinaryIntegerLiteral {% ([BinaryIntegerLiteral]) => BinaryIntegerLiteral %} + | OctalIntegerLiteral {% ([OctalIntegerLiteral]) => OctalIntegerLiteral %} + | HexIntegerLiteral {% ([HexIntegerLiteral]) => HexIntegerLiteral %} + +# #prod-DecimalLiteral +DecimalLiteral -> + DecimalIntegerLiteral "." {% ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral) %} + | DecimalIntegerLiteral "." DecimalDigits {% ([DecimalIntegerLiteral, _, [DecimalDigits, n]]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)) %} + | DecimalIntegerLiteral "." ExponentPart {% ([DecimalIntegerLiteral, _, e]) => new Scientific(DecimalIntegerLiteral, e) %} + | DecimalIntegerLiteral "." DecimalDigits ExponentPart {% ([DecimalIntegerLiteral, _, [DecimalDigits, n], e]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)).expAdd(e) %} + | "." DecimalDigits {% ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n) %} + | "." DecimalDigits ExponentPart {% ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n) %} + | DecimalIntegerLiteral {% ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral) %} + | DecimalIntegerLiteral ExponentPart {% ([DecimalIntegerLiteral, e]) => new Scientific(DecimalIntegerLiteral, e) %} + +# #prod-DecimalIntegerLiteral +DecimalIntegerLiteral -> + "0" {% c(0n) %} + | NonZeroDigit {% ([NonZeroDigit]) => NonZeroDigit %} + | NonZeroDigit DecimalDigits {% ([NonZeroDigit, [DecimalDigits, n]]) => NonZeroDigit * (10n ** n) + DecimalDigits %} + +# #prod-DecimalDigits +DecimalDigits -> + DecimalDigit {% ([DecimalDigit]) => [DecimalDigit, 1n] %} + | DecimalDigits DecimalDigit {% ([[DecimalDigits, n], DecimalDigit]) => [DecimalDigits * 10n + DecimalDigit, n + 1n] %} + +# #prod-DecimalDigit +DecimalDigit -> + "0" {% c(0n) %} + | "1" {% c(1n) %} + | "2" {% c(2n) %} + | "3" {% c(3n) %} + | "4" {% c(4n) %} + | "5" {% c(5n) %} + | "6" {% c(6n) %} + | "7" {% c(7n) %} + | "8" {% c(8n) %} + | "9" {% c(9n) %} + +# #prod-NonZeroDigit +NonZeroDigit -> + "1" {% c(1n) %} + | "2" {% c(2n) %} + | "3" {% c(3n) %} + | "4" {% c(4n) %} + | "5" {% c(5n) %} + | "6" {% c(6n) %} + | "7" {% c(7n) %} + | "8" {% c(8n) %} + | "9" {% c(9n) %} + +# #prod-ExponentPart +ExponentPart -> ExponentIndicator SignedInteger {% ([_, SignedInteger]) => SignedInteger %} + +# #prod-ExponentIndicator +ExponentIndicator -> "e"i + +# #prod-SignedInteger +SignedInteger -> + DecimalDigits {% ([[DecimalDigits]]) => DecimalDigits %} + | "+" DecimalDigits {% ([_, [DecimalDigits]]) => DecimalDigits %} + | "-" DecimalDigits {% ([_, [DecimalDigits]]) => -DecimalDigits %} + +# #prod-BinaryIntegerLiteral +BinaryIntegerLiteral -> "0b"i BinaryDigits {% ([_, BinaryDigits]) => new Scientific(BinaryDigits) %} + +# #prod-BinaryDigits +BinaryDigits -> + BinaryDigit {% ([BinaryDigit]) => BinaryDigit %} + | BinaryDigits BinaryDigit {% ([BinaryDigits, BinaryDigit]) => BinaryDigits * 2n + BinaryDigit %} + +# #prod-BinaryDigit +BinaryDigit -> + "0" {% c(0n) %} + | "1" {% c(1n) %} + +# #prod-OctalIntegerLiteral +OctalIntegerLiteral -> "0o"i OctalDigits {% ([_, OctalDigits]) => new Scientific(OctalDigits) %} + +# #prod-OctalDigits +OctalDigits -> + OctalDigit {% ([OctalDigit]) => OctalDigit %} + | OctalDigits OctalDigit {% ([OctalDigits, OctalDigit]) => OctalDigits * 8n + OctalDigit %} + +# #prod-OctalDigit +OctalDigit -> + "0" {% c(0n) %} + | "1" {% c(1n) %} + | "2" {% c(2n) %} + | "3" {% c(3n) %} + | "4" {% c(4n) %} + | "5" {% c(5n) %} + | "6" {% c(6n) %} + | "7" {% c(7n) %} + +# #prod-HexIntegerLiteral +HexIntegerLiteral -> "0x"i HexDigits {% ([_, HexDigits]) => new Scientific(HexDigits) %} + +# #prod-HexDigits +HexDigits -> + HexDigit {% ([HexDigit]) => HexDigit %} + | HexDigits HexDigit {% ([HexDigits, HexDigit]) => HexDigits * 16n + HexDigit %} + +# #prod-HexDigit +HexDigit -> + "0" {% c(0n) %} + | "1" {% c(1n) %} + | "2" {% c(2n) %} + | "3" {% c(3n) %} + | "4" {% c(4n) %} + | "5" {% c(5n) %} + | "6" {% c(6n) %} + | "7" {% c(7n) %} + | "8" {% c(8n) %} + | "9" {% c(9n) %} + | "a"i {% c(10n) %} + | "b"i {% c(11n) %} + | "c"i {% c(12n) %} + | "d"i {% c(13n) %} + | "e"i {% c(14n) %} + | "f"i {% c(15n) %} diff --git a/src/engine262/src/grammar/util.mjs b/src/engine262/src/grammar/util.mjs new file mode 100644 index 0000000..c4f7bc0 --- /dev/null +++ b/src/engine262/src/grammar/util.mjs @@ -0,0 +1,36 @@ +import * as acorn from 'acorn'; + +const { isNewLine, nonASCIIwhitespace } = acorn; + +function isWhiteSpace(c) { + return c === '\x09' // CHARACTER TABULATION + || c === '\x0B' // LINE TABULATION + || c === '\x0C' // FORM FEED (FF) + || c === '\x20' // SPACE + || c === '\xA0' // NO-BREAK SPACE + || nonASCIIwhitespace.test(c); +} + +export const isLineTerminator = (c) => isNewLine(c.charCodeAt(0), false); + +export const isStrWhiteSpaceChar = (c) => isWhiteSpace(c) || isLineTerminator(c); + +// Returns index of first non-StrWhiteSpaceChar character. +export function searchNotStrWhiteSpaceChar(str) { + for (let i = 0; i < str.length; i += 1) { + if (!isStrWhiteSpaceChar(str[i])) { + return i; + } + } + return str.length; +} + +// Returns index of last non-StrWhiteSpaceChar character + 1. +export function reverseSearchNotStrWhiteSpaceChar(str) { + for (let i = str.length - 1; i >= 0; i -= 1) { + if (!isStrWhiteSpaceChar(str[i])) { + return i + 1; + } + } + return 0; +} diff --git a/src/engine262/src/helpers.mjs b/src/engine262/src/helpers.mjs new file mode 100644 index 0000000..71479b9 --- /dev/null +++ b/src/engine262/src/helpers.mjs @@ -0,0 +1,349 @@ +import { surroundingAgent } from './engine.mjs'; +import { Type, Value, Descriptor } from './value.mjs'; +import { ToString, DefinePropertyOrThrow, CreateBuiltinFunction } from './abstract-ops/all.mjs'; +import { X, AwaitFulfilledFunctions } from './completion.mjs'; + +function convertValueForKey(key) { + switch (Type(key)) { + case 'String': + return key.stringValue(); + case 'Number': + if (key.numberValue() === 0 && Object.is(key.numberValue(), -0)) { + return key; + } + return key.numberValue(); + default: + return key; + } +} + +export class ValueMap extends Map { + constructor(init) { + super(); + if (init !== null && init !== undefined) { + for (const [k, v] of init) { + this.set(convertValueForKey(k), v); + } + } + } + + get(key) { + return super.get(convertValueForKey(key)); + } + + set(key, value) { + return super.set(convertValueForKey(key), value); + } + + has(key) { + return super.has(convertValueForKey(key)); + } + + delete(key) { + return super.delete(convertValueForKey(key)); + } + + * keys() { + for (const [key] of this) { + yield key; + } + } + + * values() { + for (const [, value] of this) { + yield value; + } + } + + entries() { + return this[Symbol.iterator](); + } + + forEach(cb) { + for (const [key, value] of this) { + cb(value, key, this); + } + } + + * [Symbol.iterator]() { + for (const [key, value] of super.entries()) { + if (typeof key === 'string' || typeof key === 'number') { + yield [new Value(key), value]; + } else { + yield [key, value]; + } + } + } + + mark(m) { + for (const [k, v] of this.entries()) { + m(k); + m(v); + } + } +} + +export class ValueSet extends Set { + constructor(init) { + super(); + if (init !== undefined && init !== null) { + for (const item of init) { + this.add(item); + } + } + } + + add(item) { + return super.add(convertValueForKey(item)); + } + + has(item) { + return super.has(convertValueForKey(item)); + } + + delete(item) { + return super.delete(convertValueForKey(item)); + } + + keys() { + return this[Symbol.iterator](); + } + + values() { + return this[Symbol.iterator](); + } + + * [Symbol.iterator]() { + for (const key of super.values()) { + if (typeof key === 'string' || typeof key === 'number') { + yield new Value(key); + } else { + yield key; + } + } + } + + mark(m) { + for (const v of this.values()) { + m(v); + } + } +} + +export class OutOfRange extends RangeError { + constructor(fn, detail) { + super(`${fn}() argument out of range`); + + this.detail = detail; + } +} + +export function unwind(iterator, maxSteps = 1) { + let steps = 0; + while (true) { + const { done, value } = iterator.next('Unwind'); + if (done) { + return value; + } + steps += 1; + if (steps > maxSteps) { + throw new RangeError('Max steps exceeded'); + } + } +} + +const kSafeToResume = Symbol('kSameToResume'); + +export function handleInResume(fn, ...args) { + const bound = () => fn(...args); + bound[kSafeToResume] = true; + return bound; +} + +export function resume(context, completion) { + const { value } = context.codeEvaluationState.next(completion); + if (typeof value === 'function' && value[kSafeToResume] === true) { + return X(value()); + } + return value; +} + +export class CallSite { + constructor(context) { + this.context = context; + this.lastNode = null; + this.constructCall = false; + } + + clone(context = this.context) { + const c = new CallSite(context); + c.lastNode = this.lastNode; + c.constructCall = this.constructCall; + return c; + } + + isTopLevel() { + return this.context.Function === Value.null; + } + + isConstructCall() { + return this.constructCall; + } + + isAsync() { + if (this.context.Function !== Value.null) { + return this.context.Function.ECMAScriptCode && this.context.Function.ECMAScriptCode.async; + } + return false; + } + + isNative() { + return !!this.context.Function.nativeFunction; + } + + getFunctionName() { + if (this.context.Function !== Value.null) { + const name = this.context.Function.properties.get(new Value('name')); + if (name) { + return X(ToString(name.Value)).stringValue(); + } + } + return null; + } + + getSpecifier() { + if (this.context.ScriptOrModule !== Value.null) { + return this.context.ScriptOrModule.HostDefined.specifier; + } + return null; + } + + setLocation(node) { + this.lastNode = node; + } + + get lineNumber() { + if (this.lastNode) { + return this.lastNode.loc.start.line; + } + return null; + } + + get columnNumber() { + if (this.lastNode) { + return this.lastNode.loc.start.column; + } + return null; + } + + loc() { + if (this.isNative()) { + return 'native'; + } + let out = ''; + const specifier = this.getSpecifier(); + if (specifier) { + out += specifier; + } else { + out += ''; + } + if (this.lineNumber !== null) { + out += `:${this.lineNumber}`; + if (this.columnNumber !== null) { + out += `:${this.columnNumber}`; + } + } + return out.trim(); + } + + toString() { + const isAsync = this.isAsync(); + const functionName = this.getFunctionName(); + const isConstructCall = this.isConstructCall(); + const isMethodCall = !isConstructCall && !this.isTopLevel(); + + let string = isAsync ? 'async ' : ''; + + if (isConstructCall) { + string += 'new '; + } + + if (isMethodCall || isConstructCall) { + if (functionName) { + string += functionName; + } else { + string += ''; + } + } else if (functionName) { + string += functionName; + } else { + return `${string}${this.loc()}`; + } + + return `${string} (${this.loc()})`; + } +} + +function captureAsyncStack(stack) { + let promise = stack[0].context.promiseCapability.Promise; + for (let i = 0; i < 10; i += 1) { + if (promise.PromiseFulfillReactions.length !== 1) { + return; + } + const [reaction] = promise.PromiseFulfillReactions; + if (reaction.Handler.nativeFunction === AwaitFulfilledFunctions) { + const asyncContext = reaction.Handler.AsyncContext; + stack.push(asyncContext.callSite.clone()); + if ('PromiseState' in asyncContext.promiseCapability.Promise) { + promise = asyncContext.promiseCapability.Promise; + } else { + return; + } + } else if (reaction.Capability !== Value.undefined) { + if ('PromiseState' in reaction.Capability.Promise) { + promise = reaction.Capability.Promise; + } else { + return; + } + } + } +} + +export function captureStack(O) { + const stack = []; + for (let i = surroundingAgent.executionContextStack.length - 2; i >= 0; i -= 1) { + const e = surroundingAgent.executionContextStack[i]; + if (e.VariableEnvironment === undefined && e.Function === Value.null) { + break; + } + stack.push(e.callSite.clone()); + if (e.callSite.isAsync()) { + i -= 1; // skip original execution context which has no useful information. + } + } + + if (stack.length > 0 && stack[0].context.promiseCapability) { + captureAsyncStack(stack); + } + + let cache = null; + + X(DefinePropertyOrThrow(O, new Value('stack'), Descriptor({ + Get: CreateBuiltinFunction(() => { + if (cache === null) { + let errorString = X(ToString(O)).stringValue(); + stack.forEach((s) => { + errorString = `${errorString}\n at ${s.toString()}`; + }); + cache = new Value(errorString); + } + return cache; + }, []), + Set: CreateBuiltinFunction(([value = Value.undefined]) => { + cache = value; + return Value.undefined; + }, []), + Enumerable: Value.false, + Configurable: Value.true, + }))); +} diff --git a/src/engine262/src/inspect.mjs b/src/engine262/src/inspect.mjs new file mode 100644 index 0000000..a5d44c6 --- /dev/null +++ b/src/engine262/src/inspect.mjs @@ -0,0 +1,192 @@ +import { surroundingAgent } from './engine.mjs'; +import { Type, Value, wellKnownSymbols } from './value.mjs'; +import { Realm as APIRealm } from './api.mjs'; +import { + Call, IsArray, Get, LengthOfArrayLike, +} from './abstract-ops/all.mjs'; +import { Q, X } from './completion.mjs'; + +const bareKeyRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/; + +const getObjectTag = (value, wrap) => { + let s; + try { + s = X(Get(value, wellKnownSymbols.toStringTag)).stringValue(); + } catch {} + try { + const c = X(Get(value, new Value('constructor'))); + s = X(Get(c, new Value('name'))).stringValue(); + } catch {} + if (s) { + if (wrap) { + return `[${s}] `; + } + return s; + } + return ''; +}; + +const compactObject = (realm, value) => { + try { + const toString = X(Get(value, new Value('toString'))); + const objectToString = realm.Intrinsics['%Object.prototype.toString%']; + if (toString.nativeFunction === objectToString.nativeFunction) { + return X(Call(toString, value)).stringValue(); + } else { + const tag = getObjectTag(value, false) || 'Unknown'; + const ctor = X(Get(value, new Value('constructor'))); + if (Type(ctor) === 'Object') { + const ctorName = X(Get(ctor, new Value('name'))).stringValue(); + if (ctorName !== '') { + return `#<${ctorName}>`; + } + return `[object ${tag}]`; + } + return `[object ${tag}]`; + } + } catch (e) { + return '[object Unknown]'; + } +}; + +const INSPECTORS = { + Completion: (v, ctx, i) => i(v.Value), + Null: () => 'null', + Undefined: () => 'undefined', + Boolean: (v) => v.boolean.toString(), + Number: (v) => { + const n = v.numberValue(); + if (n === 0 && Object.is(n, -0)) { + return '-0'; + } + return n.toString(); + }, + BigInt: (v) => `${v.bigintValue()}n`, + String: (v, ctx) => (ctx.quote ? `'${v.stringValue().replace(/\n/g, '\\n')}'` : v.stringValue()), + Symbol: (v) => `Symbol(${v.Description === Value.undefined ? '' : v.Description.stringValue()})`, + Object: (v, ctx, i) => { + if (ctx.inspected.includes(v)) { + return '[Circular]'; + } + if ('PromiseState' in v) { + ctx.indent += 1; + const result = v.PromiseState === 'pending' ? 'undefined' : i(v.PromiseResult); + ctx.indent -= 1; + return `Promise { + [[PromiseState]]: ${v.PromiseState} + [[PromiseResult]]: ${result} +}`; + } + + if ('Call' in v) { + const name = v.properties.get(new Value('name')); + if (name !== undefined) { + return `[Function: ${name.Value.stringValue()}]`; + } + return '[Function]'; + } + + if ('ErrorData' in v) { + let e = Q(Get(v, new Value('stack'))); + if (!e.stringValue) { + const toString = Q(Get(v, new Value('toString'))); + e = X(Call(toString, v)); + } + return e.stringValue(); + } + + if ('BooleanData' in v) { + return `[Boolean ${i(v.BooleanData)}]`; + } + if ('NumberData' in v) { + return `[Number ${i(v.NumberData)}]`; + } + if ('BigIntData' in v) { + return `[BigInt ${i(v.BigIntData)}]`; + } + if ('StringData' in v) { + return `[String ${i(v.StringData)}]`; + } + if ('SymbolData' in v) { + return `[Symbol ${i(v.SymbolData)}]`; + } + + ctx.indent += 1; + ctx.inspected.push(v); + + try { + const isArray = IsArray(v) === Value.true; + const isTypedArray = 'TypedArrayName' in v; + if (isArray || isTypedArray) { + const length = X(LengthOfArrayLike(v)).numberValue(); + let holes = 0; + const out = []; + for (let j = 0; j < length; j += 1) { + const elem = X(v.GetOwnProperty(new Value(j.toString()))); + if (elem === Value.undefined) { + holes += 1; + } else { + if (holes > 0) { + out.push(`<${holes} empty items>`); + holes = 0; + } + if (elem.Value) { + out.push(i(elem.Value)); + } else { + out.push(''); + } + } + } + return `${isTypedArray ? `${v.TypedArrayName.stringValue()} ` : ''}[${out.join(', ')}]`; + } + + const keys = X(v.OwnPropertyKeys()); + const cache = []; + for (const key of keys) { + const C = X(v.GetOwnProperty(key)); + if (C.Enumerable === Value.true) { + cache.push([ + Type(key) === 'String' && bareKeyRe.test(key.stringValue()) ? key.stringValue() : i(key), + C.Value ? i(C.Value) : '', + ]); + } + } + + const tag = getObjectTag(v); + let out = tag && tag !== 'Object' ? `${tag} {` : '{'; + if (cache.length > 5) { + cache.forEach((c) => { + out = `${out}\n${' '.repeat(ctx.indent)}${c[0]}: ${c[1]},`; + }); + return `${out}\n${' '.repeat(ctx.indent - 1)}}`; + } else { + const oc = ctx.compact; + ctx.compact = true; + cache.forEach((c, index) => { + out = `${out}${index === 0 ? '' : ','} ${c[0]}: ${c[1]}`; + }); + ctx.compact = oc; + return `${out} }`; + } + } catch { + return compactObject(ctx, v); + } finally { + ctx.indent -= 1; + ctx.inspected.pop(); + } + }, +}; + +export function inspect(value, realm = surroundingAgent.currentRealmRecord) { + if (realm instanceof APIRealm) { + realm = realm.realm; + } + const context = { + realm, + indent: 0, + quote: true, + inspected: [], + }; + const inner = (v) => INSPECTORS[Type(v)](v, context, inner); + return inner(value); +} diff --git a/src/engine262/src/intrinsics/AggregateError.mjs b/src/engine262/src/intrinsics/AggregateError.mjs new file mode 100644 index 0000000..d7e1606 --- /dev/null +++ b/src/engine262/src/intrinsics/AggregateError.mjs @@ -0,0 +1,52 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { + CreateMethodProperty, + ToString, + IterableToList, + OrdinaryCreateFromConstructor, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { captureStack } from '../helpers.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-promise-any/#sec-aggregate-error +function AggregateErrorConstructor([errors = Value.undefined, message = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + let newTarget; + if (NewTarget === Value.undefined) { + newTarget = surroundingAgent.activeFunctionObject; + } else { + newTarget = NewTarget; + } + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]], [[AggregateErrors]] »). + const O = Q(OrdinaryCreateFromConstructor(newTarget, '%AggregateError.prototype%', [ + 'ErrorData', + 'AggregateErrors', + ])); + // 3. Let errorsList be ? IterableToList(errors). + const errorsList = errors === Symbol.for('engine262.placeholder') + ? [] + : Q(IterableToList(errors)); + // 4. Set O.[[AggregateErrors]] to errorsList. + O.AggregateErrors = errorsList; + // 5. If message is not undefined, then + if (message !== Value.undefined) { + // a. Let msg be ? ToString(message). + const msg = Q(ToString(message)); + // b. Perform ! CreateMethodProperty(O, "message", msg). + X(CreateMethodProperty(O, new Value('message'), msg)); + } + + // NON-SPEC + X(captureStack(O)); + + // 6. Return O. + return O; +} + +export function BootstrapAggregateError(realmRec) { + const c = BootstrapConstructor(realmRec, AggregateErrorConstructor, 'AggregateError', 2, realmRec.Intrinsics['%AggregateError.prototype%'], []); + c.Prototype = realmRec.Intrinsics['%Error%']; + realmRec.Intrinsics['%AggregateError%'] = c; +} diff --git a/src/engine262/src/intrinsics/AggregateErrorPrototype.mjs b/src/engine262/src/intrinsics/AggregateErrorPrototype.mjs new file mode 100644 index 0000000..6542ca3 --- /dev/null +++ b/src/engine262/src/intrinsics/AggregateErrorPrototype.mjs @@ -0,0 +1,26 @@ +import { Value } from '../value.mjs'; +import { RequireInternalSlot, CreateArrayFromList } from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-promise-any/#sec-aggregate-error.prototype.name +function AggregateErrorProto_errors(args, { thisValue }) { + // 1. Let E be the this value. + const E = thisValue; + // 2. If Type(E) is not Object, throw a TypeError exception. + // 3. If E does not have an [[ErrorData]] internal slot, throw a TypeError exception. + // 4. If E does not have an [[AggregateErrors]] internal slot, throw a TypeError exception. + Q(RequireInternalSlot(E, 'AggregateErrors')); + // 5. Return ! CreateArrayFromList(E.[[AggregateErrors]]). + return X(CreateArrayFromList(E.AggregateErrors)); +} + +export function BootstrapAggregateErrorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['name', new Value('AggregateError')], + ['message', new Value('')], + ['errors', [AggregateErrorProto_errors]], + ], realmRec.Intrinsics['%Error.prototype%'], 'AggregateError'); + + realmRec.Intrinsics['%AggregateError.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Array.mjs b/src/engine262/src/intrinsics/Array.mjs new file mode 100644 index 0000000..4bd707b --- /dev/null +++ b/src/engine262/src/intrinsics/Array.mjs @@ -0,0 +1,215 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + AbruptCompletion, + Q, + ThrowCompletion, X, +} from '../completion.mjs'; +import { + ArrayCreate, + Assert, + Call, + Construct, + CreateDataProperty, + CreateDataPropertyOrThrow, + Get, + GetIterator, + GetMethod, + GetPrototypeFromConstructor, + IsArray, + IsCallable, + IsConstructor, + IteratorClose, + IteratorStep, + IteratorValue, + Set, + LengthOfArrayLike, + ToObject, + ToString, + ToUint32, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// 22.1.1 #sec-array-constructor +function ArrayConstructor(argumentsList, { NewTarget }) { + const numberOfArgs = argumentsList.length; + if (numberOfArgs === 0) { + // 22.1.1.1 #sec-array-constructor-array + Assert(numberOfArgs === 0); + if (Type(NewTarget) === 'Undefined') { + NewTarget = surroundingAgent.activeFunctionObject; + } + const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%'); + return ArrayCreate(new Value(0), proto); + } else if (numberOfArgs === 1) { + // 22.1.1.2 #sec-array-len + const [len] = argumentsList; + Assert(numberOfArgs === 1); + if (Type(NewTarget) === 'Undefined') { + NewTarget = surroundingAgent.activeFunctionObject; + } + const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%'); + const array = ArrayCreate(new Value(0), proto); + let intLen; + if (Type(len) !== 'Number') { + const defineStatus = X(CreateDataProperty(array, new Value('0'), len)); + Assert(defineStatus === Value.true); + intLen = new Value(1); + } else { + intLen = ToUint32(len); + if (intLen.numberValue() !== len.numberValue()) { + return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', len); + } + } + Set(array, new Value('length'), intLen, Value.true); + return array; + } else if (numberOfArgs >= 2) { + // 22.1.1.3 #sec-array-items + const items = argumentsList; + Assert(numberOfArgs >= 2); + if (Type(NewTarget) === 'Undefined') { + NewTarget = surroundingAgent.activeFunctionObject; + } + const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%'); + const array = ArrayCreate(new Value(0), proto); + let k = 0; + while (k < numberOfArgs) { + const Pk = ToString(new Value(k)); + const itemK = items[k]; + const defineStatus = X(CreateDataProperty(array, Pk, itemK)); + Assert(defineStatus === Value.true); + k += 1; + } + Assert(X(Get(array, new Value('length'))).numberValue() === numberOfArgs); + return array; + } + + throw new OutOfRange('ArrayConstructor', numberOfArgs); +} + +// 22.1.2.1 #sec-array.from +function Array_from([items = Value.undefined, mapfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const C = thisValue; + let mapping; + let A; + if (mapfn === Value.undefined) { + mapping = false; + } else { + if (IsCallable(mapfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', mapfn); + } + mapping = true; + } + const usingIterator = Q(GetMethod(items, wellKnownSymbols.iterator)); + if (usingIterator !== Value.undefined) { + if (IsConstructor(C) === Value.true) { + A = Q(Construct(C)); + } else { + A = X(ArrayCreate(new Value(0))); + } + const iteratorRecord = Q(GetIterator(items, 'sync', usingIterator)); + let k = 0; + while (true) { // eslint-disable-line no-constant-condition + if (k >= (2 ** 53) - 1) { + const error = new ThrowCompletion(surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength').Value); + return Q(IteratorClose(iteratorRecord, error)); + } + const Pk = X(ToString(new Value(k))); + const next = Q(IteratorStep(iteratorRecord)); + if (next === Value.false) { + Q(Set(A, new Value('length'), new Value(k), Value.true)); + return A; + } + const nextValue = Q(IteratorValue(next)); + let mappedValue; + if (mapping) { + mappedValue = Call(mapfn, thisArg, [nextValue, new Value(k)]); + if (mappedValue instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, mappedValue)); + } + mappedValue = mappedValue.Value; + } else { + mappedValue = nextValue; + } + const defineStatus = CreateDataPropertyOrThrow(A, Pk, mappedValue); + if (defineStatus instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, defineStatus)); + } + k += 1; + } + } + const arrayLike = X(ToObject(items)); + const len = Q(LengthOfArrayLike(arrayLike)); + if (IsConstructor(C) === Value.true) { + A = Q(Construct(C, [len])); + } else { + A = Q(ArrayCreate(len)); + } + let k = 0; + while (k < len.numberValue()) { + const Pk = X(ToString(new Value(k))); + const kValue = Q(Get(arrayLike, Pk)); + let mappedValue; + if (mapping === true) { + mappedValue = Q(Call(mapfn, thisArg, [kValue, new Value(k)])); + } else { + mappedValue = kValue; + } + Q(CreateDataPropertyOrThrow(A, Pk, mappedValue)); + k += 1; + } + Q(Set(A, new Value('length'), len, Value.true)); + return A; +} + +// 22.1.2.2 #sec-array.isarray +function Array_isArray([arg = Value.undefined]) { + return Q(IsArray(arg)); +} + +// 22.1.2.3 #sec-array.of +function Array_of(items, { thisValue }) { + const len = items.length; + // Let items be the List of arguments passed to this function. + const C = thisValue; + let A; + if (IsConstructor(C) === Value.true) { + A = Q(Construct(C, [new Value(len)])); + } else { + A = Q(ArrayCreate(new Value(len))); + } + let k = 0; + while (k < len) { + const kValue = items[k]; + const Pk = X(ToString(new Value(k))); + Q(CreateDataPropertyOrThrow(A, Pk, kValue)); + k += 1; + } + Q(Set(A, new Value('length'), new Value(len), Value.true)); + return A; +} + +// 22.1.2.5 #sec-get-array-@@species +function Array_speciesGetter(args, { thisValue }) { + return thisValue; +} + +export function BootstrapArray(realmRec) { + const proto = realmRec.Intrinsics['%Array.prototype%']; + + const cons = BootstrapConstructor(realmRec, ArrayConstructor, 'Array', 1, proto, [ + ['from', Array_from, 1], + ['isArray', Array_isArray, 1], + ['of', Array_of, 0], + [wellKnownSymbols.species, [Array_speciesGetter]], + ]); + + realmRec.Intrinsics['%Array%'] = cons; +} diff --git a/src/engine262/src/intrinsics/ArrayBuffer.mjs b/src/engine262/src/intrinsics/ArrayBuffer.mjs new file mode 100644 index 0000000..28178ad --- /dev/null +++ b/src/engine262/src/intrinsics/ArrayBuffer.mjs @@ -0,0 +1,44 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value, wellKnownSymbols } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { ToIndex, AllocateArrayBuffer } from '../abstract-ops/all.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// #sec-arraybuffer-length +function ArrayBufferConstructor([length = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Let byteLength be ? ToIndex(length). + const byteLength = Q(ToIndex(length)); + // 3. Return ? AllocateArrayBuffer(NewTarget, byteLength). + return Q(AllocateArrayBuffer(NewTarget, byteLength)); +} + +// #sec-arraybuffer.isview +function ArrayBuffer_isView([arg = Value.undefined]) { + // 1. If Type(arg) is not Object, return false. + if (Type(arg) !== 'Object') { + return Value.false; + } + // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. + if ('ViewedArrayBuffer' in arg) { + return Value.true; + } + // 3. Return false. + return Value.false; +} + +// #sec-get-arraybuffer-@@species +function ArrayBuffer_species(a, { thisValue }) { + return thisValue; +} + +export function BootstrapArrayBuffer(realmRec) { + const c = BootstrapConstructor(realmRec, ArrayBufferConstructor, 'ArrayBuffer', 1, realmRec.Intrinsics['%ArrayBuffer.prototype%'], [ + ['isView', ArrayBuffer_isView, 1], + [wellKnownSymbols.species, [ArrayBuffer_species]], + ]); + realmRec.Intrinsics['%ArrayBuffer%'] = c; +} diff --git a/src/engine262/src/intrinsics/ArrayBufferPrototype.mjs b/src/engine262/src/intrinsics/ArrayBufferPrototype.mjs new file mode 100644 index 0000000..fa585a1 --- /dev/null +++ b/src/engine262/src/intrinsics/ArrayBufferPrototype.mjs @@ -0,0 +1,114 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { + RequireInternalSlot, IsDetachedBuffer, IsSharedArrayBuffer, + SpeciesConstructor, Construct, ToInteger, SameValue, CopyDataBlockBytes, +} from '../abstract-ops/all.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-get-arraybuffer.prototype.bytelength +function ArrayBufferProto_byteLength(args, { thisValue }) { + // 1. Let O be this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). + Q(RequireInternalSlot(O, 'ArrayBufferData')); + // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. + if (IsSharedArrayBuffer(O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferShared'); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. Let length be O.[[ArrayBufferByteLength]]. + const length = O.ArrayBufferByteLength; + // 6. Return length. + return length; +} + +function ArrayBufferProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). + Q(RequireInternalSlot(O, 'ArrayBufferData')); + // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. + if (IsSharedArrayBuffer(O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferShared'); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. Let len be O.[[ArrayBufferByteLength]]. + const len = O.ArrayBufferByteLength.numberValue(); + // 6. Let relativeStart be ? ToInteger(start). + const relativeStart = Q(ToInteger(start)).numberValue(); + let first; + // 7. If relativeStart < 0, let first be max((len + relativeStart), 0); else let first be min(relativeStart, len). + if (relativeStart < 0) { + first = Math.max(len + relativeStart, 0); + } else { + first = Math.min(relativeStart, len); + } + let relativeEnd; + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end). + if (end === Value.undefined) { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + let final; + // 9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len). + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + // 10. Let newLen be max(final - first, 0). + const newLen = Math.max(final - first, 0); + // 11. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%). + const ctor = Q(SpeciesConstructor(O, surroundingAgent.intrinsic('%ArrayBuffer%'))); + // 12. Let new be ? Construct(ctor, « newLen »). + const newO = Q(Construct(ctor, [new Value(newLen)])); + // 13. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]). + Q(RequireInternalSlot(newO, 'ArrayBufferData')); + // 14. If IsSharedArrayBuffer(new) is true, throw a TypeError exception. + if (IsSharedArrayBuffer(newO) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferShared'); + } + // 15. If IsDetachedBuffer(new) is true, throw a TypeError exception. + if (IsDetachedBuffer(newO) === Value.true) { + return surroundingAgent.Throe('TypeError', 'ArrayBufferDetached'); + } + // 16. If SameValue(new, O) is true, throw a TypeError exception. + if (SameValue(newO, O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'SubclassSameValue', newO); + } + // 17. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception. + if (newO.ArrayBufferByteLength.numberValue() < newLen) { + return surroundingAgent.Throw('TypeError', 'SubclassLengthTooSmall', newO); + } + // 18. NOTE: Side-effects of the above steps may have detached O. + // 19. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(O) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 20. Let fromBuf be O.[[ArrayBufferData]]. + const fromBuf = O.ArrayBufferData; + // 21. Let toBuf be new.[[ArrayBufferData]]. + const toBuf = newO.ArrayBufferData; + // 22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen). + CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen); + // 23. Return new. + return newO; +} + +export function BootstrapArrayBufferPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['byteLength', [ArrayBufferProto_byteLength]], + ['slice', ArrayBufferProto_slice, 2], + ], realmRec.Intrinsics['%Object.prototype%'], 'ArrayBuffer'); + + realmRec.Intrinsics['%ArrayBuffer.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/ArrayIteratorPrototype.mjs b/src/engine262/src/intrinsics/ArrayIteratorPrototype.mjs new file mode 100644 index 0000000..a7faf14 --- /dev/null +++ b/src/engine262/src/intrinsics/ArrayIteratorPrototype.mjs @@ -0,0 +1,71 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + Assert, + CreateArrayFromList, + CreateIterResultObject, + Get, + IsDetachedBuffer, + LengthOfArrayLike, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function ArrayIteratorPrototype_next(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O); + } + if (!('IteratedArrayLike' in O) + || !('ArrayLikeNextIndex' in O) + || !('ArrayLikeIterationKind' in O)) { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O); + } + const a = O.IteratedArrayLike; + if (Type(a) === 'Undefined') { + return CreateIterResultObject(Value.undefined, Value.true); + } + const index = O.ArrayLikeNextIndex; + const itemKind = O.ArrayLikeIterationKind; + let len; + if ('TypedArrayName' in a) { + if (IsDetachedBuffer(a.ViewedArrayBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + len = a.ArrayLength; + } else { + len = Q(LengthOfArrayLike(a)); + } + if (index >= len.numberValue()) { + O.IteratedArrayLike = Value.undefined; + return CreateIterResultObject(Value.undefined, Value.true); + } + O.ArrayLikeNextIndex = index + 1; + if (itemKind === 'key') { + return CreateIterResultObject(new Value(index), Value.false); + } + const elementKey = X(ToString(new Value(index))); + const elementValue = Q(Get(a, elementKey)); + let result; + if (itemKind === 'value') { + result = elementValue; + } else { + Assert(itemKind === 'key+value'); + result = X(CreateArrayFromList([new Value(index), elementValue])); + } + return CreateIterResultObject(result, Value.false); +} + +export function BootstrapArrayIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', ArrayIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'Array Iterator'); + + realmRec.Intrinsics['%ArrayIterator.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/ArrayPrototype.mjs b/src/engine262/src/intrinsics/ArrayPrototype.mjs new file mode 100644 index 0000000..f6be4d6 --- /dev/null +++ b/src/engine262/src/intrinsics/ArrayPrototype.mjs @@ -0,0 +1,592 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Descriptor, + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + ArrayCreate, + ArraySpeciesCreate, + Assert, + Call, + CreateArrayIterator, + CreateDataProperty, + CreateDataPropertyOrThrow, + DeletePropertyOrThrow, + Get, + HasProperty, + IsArray, + IsCallable, + IsConcatSpreadable, + Set, + SortCompare, + LengthOfArrayLike, + OrdinaryObjectCreate, + ToBoolean, + ToInteger, + ToObject, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { assignProps } from './Bootstrap.mjs'; +import { ArrayProto_sortBody, BootstrapArrayPrototypeShared } from './ArrayPrototypeShared.mjs'; + +// 22.1.3.1 #sec-array.prototype.concat +function ArrayProto_concat(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + const A = Q(ArraySpeciesCreate(O, new Value(0))); + let n = 0; + const items = [O, ...args]; + while (items.length > 0) { + const E = items.shift(); + const spreadable = Q(IsConcatSpreadable(E)); + if (spreadable === Value.true) { + let k = 0; + const len = Q(LengthOfArrayLike(E)).numberValue(); + if (n + len > (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength'); + } + while (k < len) { + const P = X(ToString(new Value(k))); + const exists = Q(HasProperty(E, P)); + if (exists === Value.true) { + const subElement = Q(Get(E, P)); + const nStr = X(ToString(new Value(n))); + Q(CreateDataPropertyOrThrow(A, nStr, subElement)); + } + n += 1; + k += 1; + } + } else { + if (n >= (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength'); + } + const nStr = X(ToString(new Value(n))); + Q(CreateDataPropertyOrThrow(A, nStr, E)); + n += 1; + } + } + Q(Set(A, new Value('length'), new Value(n), Value.true)); + return A; +} + +// 22.1.3.3 #sec-array.prototype.copywithin +function ArrayProto_copyWithin([target = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)); + const relativeTarget = Q(ToInteger(target)); + let to; + if (relativeTarget.numberValue() < 0) { + to = Math.max(len.numberValue() + relativeTarget.numberValue(), 0); + } else { + to = Math.min(relativeTarget.numberValue(), len.numberValue()); + } + const relativeStart = Q(ToInteger(start)); + let from; + if (relativeStart.numberValue() < 0) { + from = Math.max(len.numberValue() + relativeStart.numberValue(), 0); + } else { + from = Math.min(relativeStart.numberValue(), len.numberValue()); + } + let relativeEnd; + if (end === Value.undefined) { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)); + } + let final; + if (relativeEnd.numberValue() < 0) { + final = Math.max(len.numberValue() + relativeEnd.numberValue(), 0); + } else { + final = Math.min(relativeEnd.numberValue(), len.numberValue()); + } + let count = Math.min(final - from, len.numberValue() - to); + let direction; + if (from < to && to < from + count) { + direction = -1; + from += count - 1; + to += count - 1; + } else { + direction = 1; + } + while (count > 0) { + const fromKey = X(ToString(new Value(from))); + const toKey = X(ToString(new Value(to))); + const fromPresent = Q(HasProperty(O, fromKey)); + if (fromPresent === Value.true) { + const fromVal = Q(Get(O, fromKey)); + Q(Set(O, toKey, fromVal, Value.true)); + } else { + Q(DeletePropertyOrThrow(O, toKey)); + } + from += direction; + to += direction; + count -= 1; + } + return O; +} + +// 22.1.3.4 #sec-array.prototype.entries +function ArrayProto_entries(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + return CreateArrayIterator(O, 'key+value'); +} + +// 22.1.3.6 #sec-array.prototype.fill +function ArrayProto_fill([value = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + const relativeStart = Q(ToInteger(start)).numberValue(); + let k; + if (relativeStart < 0) { + k = Math.max(len + relativeStart, 0); + } else { + k = Math.min(relativeStart, len); + } + let relativeEnd; + if (Type(end) === 'Undefined') { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + let final; + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + while (k < final) { + const Pk = X(ToString(new Value(k))); + Q(Set(O, Pk, value, Value.true)); + k += 1; + } + return O; +} + +// 22.1.3.7 #sec-array.prototype.filter +function ArrayProto_filter([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + const A = Q(ArraySpeciesCreate(O, new Value(0))); + let k = 0; + let to = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + const selected = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]))); + if (selected === Value.true) { + Q(CreateDataPropertyOrThrow(A, ToString(new Value(to)), kValue)); + to += 1; + } + } + k += 1; + } + return A; +} + +// 22.1.3.10.1 #sec-flattenintoarray +function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) { + Assert(Type(target) === 'Object'); + Assert(Type(source) === 'Object'); + Assert(sourceLen >= 0); + Assert(start >= 0); + // Assert: _depth_ is an integer Number, *+∞*, or *-∞*. + // Assert(mapperFunction === undefined || (X(IsCallable(mapperFunction)) === Value.true && thisArg !== undefined && depth === 1)); + let targetIndex = start; + let sourceIndex = 0; + while (sourceIndex < sourceLen) { + const P = X(ToString(new Value(sourceIndex))); + const exists = Q(HasProperty(source, P)); + if (exists === Value.true) { + let element = Q(Get(source, P)); + if (mapperFunction) { + Assert(thisArg); + element = Q(Call(mapperFunction, thisArg, [element, new Value(sourceIndex), source])); + } + let shouldFlatten = Value.false; + if (depth > 0) { + shouldFlatten = Q(IsArray(element)); + } + if (shouldFlatten === Value.true) { + const elementLen = Q(LengthOfArrayLike(element)).numberValue(); + targetIndex = Q(FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1)); + } else { + if (targetIndex >= (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'OutOfRange', targetIndex); + } + Q(CreateDataPropertyOrThrow(target, X(ToString(new Value(targetIndex))), element)); + targetIndex += 1; + } + } + sourceIndex += 1; + } + return targetIndex; +} + +// 22.1.3.10 #sec-array.prototype.flat +function ArrayProto_flat([depth = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const sourceLen = Q(LengthOfArrayLike(O)).numberValue(); + let depthNum = 1; + if (depth !== Value.undefined) { + depthNum = Q(ToInteger(depth)).numberValue(); + } + const A = Q(ArraySpeciesCreate(O, new Value(0))); + Q(FlattenIntoArray(A, O, sourceLen, 0, depthNum)); + return A; +} + +// 22.1.3.11 #sec-array.prototype.flatmap +function ArrayProto_flatMap([mapperFunction = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const sourceLen = Q(LengthOfArrayLike(O)).numberValue(); + if (X(IsCallable(mapperFunction)) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', mapperFunction); + } + const A = Q(ArraySpeciesCreate(O, new Value(0))); + Q(FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg)); + return A; +} + +// 22.1.3.16 #sec-array.prototype.keys +function ArrayProto_keys(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + return CreateArrayIterator(O, 'key'); +} + +// 22.1.3.18 #sec-array.prototype.map +function ArrayProto_map([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + const A = Q(ArraySpeciesCreate(O, len)); + let k = 0; + while (k < len.numberValue()) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + const mappedValue = Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])); + Q(CreateDataPropertyOrThrow(A, Pk, mappedValue)); + } + k += 1; + } + return A; +} + +// 22.1.3.19 #sec-array.prototype.pop +function ArrayProto_pop(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + if (len === 0) { + Q(Set(O, new Value('length'), new Value(0), Value.true)); + return Value.undefined; + } else { + const newLen = len - 1; + const index = Q(ToString(new Value(newLen))); + const element = Q(Get(O, index)); + Q(DeletePropertyOrThrow(O, index)); + Q(Set(O, new Value('length'), new Value(newLen), Value.true)); + return element; + } +} + +// 22.1.3.20 #sec-array.prototype.push +function ArrayProto_push(items, { thisValue }) { + const O = Q(ToObject(thisValue)); + let len = Q(LengthOfArrayLike(O)).numberValue(); + const argCount = items.length; + if (len + argCount > (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength'); + } + while (items.length > 0) { + const E = items.shift(); + Q(Set(O, X(ToString(new Value(len))), E, Value.true)); + len += 1; + } + Q(Set(O, new Value('length'), new Value(len), Value.true)); + return new Value(len); +} + +// 22.1.3.24 #sec-array.prototype.shift +function ArrayProto_shift(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + if (len === 0) { + Q(Set(O, new Value('length'), new Value(0), Value.true)); + return Value.undefined; + } + const first = Q(Get(O, new Value('0'))); + let k = 1; + while (k < len) { + const from = X(ToString(new Value(k))); + const to = X(ToString(new Value(k - 1))); + const fromPresent = Q(HasProperty(O, from)); + if (fromPresent === Value.true) { + const fromVal = Q(Get(O, from)); + Q(Set(O, to, fromVal, Value.true)); + } else { + Q(DeletePropertyOrThrow(O, to)); + } + k += 1; + } + Q(DeletePropertyOrThrow(O, X(ToString(new Value(len - 1))))); + Q(Set(O, new Value('length'), new Value(len - 1), Value.true)); + return first; +} + +// 22.1.3.25 #sec-array.prototype.slice +function ArrayProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + const relativeStart = Q(ToInteger(start)).numberValue(); + let k; + if (relativeStart < 0) { + k = Math.max(len + relativeStart, 0); + } else { + k = Math.min(relativeStart, len); + } + let relativeEnd; + if (Type(end) === 'Undefined') { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + let final; + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + const count = Math.max(final - k, 0); + const A = Q(ArraySpeciesCreate(O, new Value(count))); + let n = 0; + while (k < final) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + const nStr = X(ToString(new Value(n))); + Q(CreateDataPropertyOrThrow(A, nStr, kValue)); + } + k += 1; + n += 1; + } + Q(Set(A, new Value('length'), new Value(n), Value.true)); + return A; +} + +// 22.1.3.27 #sec-array.prototype.sort +function ArrayProto_sort([comparefn = Value.undefined], { thisValue }) { + if (comparefn !== Value.undefined && IsCallable(comparefn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', comparefn); + } + const obj = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(obj)); + + return ArrayProto_sortBody(obj, len, (x, y) => SortCompare(x, y, comparefn)); +} + +// 22.1.3.28 #sec-array.prototype.splice +function ArrayProto_splice(args, { thisValue }) { + const [start = Value.undefined, deleteCount = Value.undefined, ...items] = args; + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + const relativeStart = Q(ToInteger(start)).numberValue(); + let actualStart; + if (relativeStart < 0) { + actualStart = Math.max(len + relativeStart, 0); + } else { + actualStart = Math.min(relativeStart, len); + } + let insertCount; + let actualDeleteCount; + if (args.length === 0) { + insertCount = 0; + actualDeleteCount = 0; + } else if (args.length === 1) { + insertCount = 0; + actualDeleteCount = len - actualStart; + } else { + insertCount = args.length - 2; + const dc = Q(ToInteger(deleteCount)).numberValue(); + actualDeleteCount = Math.min(Math.max(dc, 0), len - actualStart); + } + if (len + insertCount - actualDeleteCount > (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength'); + } + const A = Q(ArraySpeciesCreate(O, new Value(actualDeleteCount))); + let k = 0; + while (k < actualDeleteCount) { + const from = X(ToString(new Value(actualStart + k))); + const fromPresent = Q(HasProperty(O, from)); + if (fromPresent === Value.true) { + const fromValue = Q(Get(O, from)); + Q(CreateDataPropertyOrThrow(A, X(ToString(new Value(k))), fromValue)); + } + k += 1; + } + Q(Set(A, new Value('length'), new Value(actualDeleteCount), Value.true)); + const itemCount = items.length; + if (itemCount < actualDeleteCount) { + k = actualStart; + while (k < len - actualDeleteCount) { + const from = X(ToString(new Value(k + actualDeleteCount))); + const to = X(ToString(new Value(k + itemCount))); + const fromPresent = Q(HasProperty(O, from)); + if (fromPresent === Value.true) { + const fromValue = Q(Get(O, from)); + Q(Set(O, to, fromValue, Value.true)); + } else { + Q(DeletePropertyOrThrow(O, to)); + } + k += 1; + } + k = len; + while (k > len - actualDeleteCount + itemCount) { + Q(DeletePropertyOrThrow(O, X(ToString(new Value(k - 1))))); + k -= 1; + } + } else if (itemCount > actualDeleteCount) { + k = len - actualDeleteCount; + while (k > actualStart) { + const from = X(ToString(new Value(k + actualDeleteCount - 1))); + const to = X(ToString(new Value(k + itemCount - 1))); + const fromPresent = Q(HasProperty(O, from)); + if (fromPresent === Value.true) { + const fromValue = Q(Get(O, from)); + Q(Set(O, to, fromValue, Value.true)); + } else { + Q(DeletePropertyOrThrow(O, to)); + } + k -= 1; + } + } + k = actualStart; + while (items.length > 0) { + const E = items.shift(); + Q(Set(O, X(ToString(new Value(k))), E, Value.true)); + k += 1; + } + Q(Set(O, new Value('length'), new Value(len - actualDeleteCount + itemCount), Value.true)); + return A; +} + +// 22.1.3.30 #sec-array.prototype.tostring +function ArrayProto_toString(a, { thisValue }) { + const array = Q(ToObject(thisValue)); + let func = Q(Get(array, new Value('join'))); + if (IsCallable(func) === Value.false) { + func = surroundingAgent.intrinsic('%Object.prototype.toString%'); + } + return Q(Call(func, array)); +} + +// 22.1.3.31 #sec-array.prototype.unshift +function ArrayProto_unshift(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + const len = Q(LengthOfArrayLike(O)).numberValue(); + const argCount = args.length; + if (argCount > 0) { + if (len + argCount > (2 ** 53) - 1) { + return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength'); + } + let k = len; + while (k > 0) { + const from = X(ToString(new Value(k - 1))); + const to = X(ToString(new Value(k + argCount - 1))); + const fromPresent = Q(HasProperty(O, from)); + if (fromPresent === Value.true) { + const fromValue = Q(Get(O, from)); + Q(Set(O, to, fromValue, Value.true)); + } else { + Q(DeletePropertyOrThrow(O, to)); + } + k -= 1; + } + let j = 0; + const items = args; + while (items.length !== 0) { + const E = items.shift(); + const jStr = X(ToString(new Value(j))); + Q(Set(O, jStr, E, Value.true)); + j += 1; + } + } + Q(Set(O, new Value('length'), new Value(len + argCount), Value.true)); + return new Value(len + argCount); +} + +// 22.1.3.32 #sec-array.prototype.values +function ArrayProto_values(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + return CreateArrayIterator(O, 'value'); +} + +export function BootstrapArrayPrototype(realmRec) { + const proto = X(ArrayCreate(new Value(0), realmRec.Intrinsics['%Object.prototype%'])); + + assignProps(realmRec, proto, [ + ['concat', ArrayProto_concat, 1], + ['copyWithin', ArrayProto_copyWithin, 2], + ['entries', ArrayProto_entries, 0], + ['fill', ArrayProto_fill, 1], + ['filter', ArrayProto_filter, 1], + ['flat', ArrayProto_flat, 0], + ['flatMap', ArrayProto_flatMap, 1], + ['keys', ArrayProto_keys, 0], + ['map', ArrayProto_map, 1], + ['pop', ArrayProto_pop, 0], + ['push', ArrayProto_push, 1], + ['shift', ArrayProto_shift, 0], + ['slice', ArrayProto_slice, 2], + ['sort', ArrayProto_sort, 1], + ['splice', ArrayProto_splice, 2], + ['toString', ArrayProto_toString, 0], + ['unshift', ArrayProto_unshift, 1], + ['values', ArrayProto_values, 0], + ]); + + BootstrapArrayPrototypeShared( + realmRec, + proto, + () => {}, + (O) => Get(O, new Value('length')), + ); + + proto.DefineOwnProperty(wellKnownSymbols.iterator, proto.GetOwnProperty(new Value('values'))); + + { + const unscopableList = OrdinaryObjectCreate(Value.null); + Assert(X(CreateDataProperty(unscopableList, new Value('copyWithin'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('entries'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('fill'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('find'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('findIndex'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('flat'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('flatMap'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('includes'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('keys'), Value.true)) === Value.true); + Assert(X(CreateDataProperty(unscopableList, new Value('values'), Value.true)) === Value.true); + X(proto.DefineOwnProperty(wellKnownSymbols.unscopables, Descriptor({ + Value: unscopableList, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + + realmRec.Intrinsics['%Array.prototype%'] = proto; + + realmRec.Intrinsics['%Array.prototype.keys%'] = proto.Get(new Value('keys'), proto); + realmRec.Intrinsics['%Array.prototype.entries%'] = proto.Get(new Value('entries'), proto); + realmRec.Intrinsics['%Array.prototype.values%'] = proto.Get(new Value('values'), proto); +} diff --git a/src/engine262/src/intrinsics/ArrayPrototypeShared.mjs b/src/engine262/src/intrinsics/ArrayPrototypeShared.mjs new file mode 100644 index 0000000..d54daf7 --- /dev/null +++ b/src/engine262/src/intrinsics/ArrayPrototypeShared.mjs @@ -0,0 +1,561 @@ +import { + Assert, + Call, + DeletePropertyOrThrow, + Get, + HasOwnProperty, + HasProperty, + Invoke, + IsCallable, + SameValueZero, + Set, + StrictEqualityComparison, + ToBoolean, + ToInteger, + ToLength, + ToObject, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { assignProps } from './Bootstrap.mjs'; + +// Algorithms and methods shared between %Array.prototype% and +// %TypedArray.prototype%. + +// 22.1.3.27 #sec-array.prototype.sort +// 22.2.3.26 #sec-%typedarray%.prototype.sort +// +// If internalMethodsRestricted is true, then Asserts are used to ensure that +// "The only internal methods of the this object that the algorithm may call +// are [[Get]] and [[Set]]," a requirement of %TypedArray%.prototype.sort. +export function ArrayProto_sortBody(obj, len, SortCompare, internalMethodsRestricted = false) { + len = len.numberValue(); + + // Collect all elements. Count how many holes we have for error checking. + const collected = []; + let holes = 0; + for (let k = 0; k < len; k += 1) { + const curProp = X(ToString(new Value(k))); + const prop = Q(Get(obj, curProp)); + if (prop === Value.undefined) { + Assert(!internalMethodsRestricted); + const hasOwn = Q(HasOwnProperty(obj, curProp)); + if (hasOwn === Value.false) { + holes += 1; + } else { + collected.push(prop); + } + } else { + collected.push(prop); + } + } + if (internalMethodsRestricted) { + Assert(holes === 0); + } + Assert(collected.length + holes === len); + + // Get rid of holes by deleting properties at the end. + // See Note 1: Because non-existent property values always compare greater + // than undefined property values, and undefined always compares greater + // than any other value, undefined property values always sort to the end + // of the result, followed by non-existent property values. + for (let k = collected.length; k < len; k += 1) { + const curProp = X(ToString(new Value(k))); + Q(DeletePropertyOrThrow(obj, curProp)); + } + + // Mergesort. + const lBuffer = []; + const rBuffer = []; + for (let step = 1; step < collected.length; step *= 2) { + for (let start = 0; start < collected.length - 1; start += 2 * step) { + const sizeLeft = step; + const mid = start + sizeLeft; + const sizeRight = Math.min(step, collected.length - mid); + if (sizeRight < 0) { + continue; + } + + // Merge. + for (let l = 0; l < sizeLeft; l += 1) { + lBuffer[l] = collected[start + l]; + } + for (let r = 0; r < sizeRight; r += 1) { + rBuffer[r] = collected[mid + r]; + } + + { + let l = 0; + let r = 0; + let o = start; + while (l < sizeLeft && r < sizeRight) { + const cmp = Q(SortCompare(lBuffer[l], rBuffer[r])).numberValue(); + if (cmp <= 0) { + collected[o] = lBuffer[l]; + o += 1; + l += 1; + } else { + collected[o] = rBuffer[r]; + o += 1; + r += 1; + } + } + while (l < sizeLeft) { + collected[o] = lBuffer[l]; + o += 1; + l += 1; + } + while (r < sizeRight) { + collected[o] = rBuffer[r]; + o += 1; + r += 1; + } + } + } + } + + // Copy the sorted results back to the array. + for (let k = 0; k < collected.length; k += 1) { + const curProp = X(ToString(new Value(k))); + Q(Set(obj, curProp, collected[k], Value.true)); + } + + return obj; +} + +export function BootstrapArrayPrototypeShared(realmRec, proto, priorToEvaluatingAlgorithm, objectToLength) { + // 22.1.3.5 #sec-array.prototype.every + // 22.2.3.7 #sec-%typedarray%.prototype.every + function ArrayProto_every([callbackFn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)); + if (IsCallable(callbackFn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackFn); + } + let k = 0; + while (k < len.numberValue()) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + const testResult = ToBoolean(Q(Call(callbackFn, thisArg, [kValue, new Value(k), O]))); + if (testResult === Value.false) { + return Value.false; + } + } + k += 1; + } + return Value.true; + } + + // 22.1.3.8 #sec-array.prototype.find + // 22.2.3.10 #sec-%typedarray%.prototype.find + function ArrayProto_find([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(predicate) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate); + } + let k = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kValue = Q(Get(O, Pk)); + const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O]))); + if (testResult === Value.true) { + return kValue; + } + k += 1; + } + return Value.undefined; + } + + // 22.1.3.9 #sec-array.prototype.findindex + // 22.2.3.11 #sec-%typedarray%.prototype.findindex + function ArrayProto_findIndex([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(predicate) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate); + } + let k = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kValue = Q(Get(O, Pk)); + const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O]))); + if (testResult === Value.true) { + return new Value(k); + } + k += 1; + } + return new Value(-1); + } + + // 22.1.3.12 #sec-array.prototype.foreach + // 22.2.3.12 #sec-%typedarray%.prototype.foreach + function ArrayProto_forEach([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + let k = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])); + } + k += 1; + } + return Value.undefined; + } + + // 22.1.3.13 #sec-array.prototype.includes + // 22.2.3.13 #sec-%typedarray%.prototype.includes + function ArrayProto_includes([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (len === 0) { + return Value.false; + } + const n = Q(ToInteger(fromIndex)).numberValue(); + if (fromIndex === Value.undefined) { + Assert(n === 0); + } + let k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) { + k = 0; + } + } + while (k < len) { + const kStr = X(ToString(new Value(k))); + const elementK = Q(Get(O, kStr)); + if (SameValueZero(searchElement, elementK) === Value.true) { + return Value.true; + } + k += 1; + } + return Value.false; + } + + // 22.1.3.14 #sec-array.prototype.indexof + // 22.2.3.14 #sec-%typedarray%.prototype.indexof + function ArrayProto_indexOf([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (len === 0) { + return new Value(-1); + } + const n = Q(ToInteger(fromIndex)).numberValue(); + if (fromIndex === Value.undefined) { + Assert(n === 0); + } + if (n >= len) { + return new Value(-1); + } + let k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) { + k = 0; + } + } + while (k < len) { + const kStr = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, kStr)); + if (kPresent === Value.true) { + const elementK = Q(Get(O, kStr)); + const same = StrictEqualityComparison(searchElement, elementK); + if (same === Value.true) { + return new Value(k); + } + } + k += 1; + } + return new Value(-1); + } + + // 22.1.3.15 #sec-array.prototype.join + // 22.2.3.15 #sec-%typedarray%.prototype.join + function ArrayProto_join([separator = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + let sep; + if (Type(separator) === 'Undefined') { + sep = ','; + } else { + sep = Q(ToString(separator)).stringValue(); + } + let R = ''; + let k = 0; + while (k < len) { + if (k > 0) { + R = `${R}${sep}`; + } + const kStr = X(ToString(new Value(k))); + const element = Q(Get(O, kStr)); + let next; + if (Type(element) === 'Undefined' || Type(element) === 'Null') { + next = ''; + } else { + next = Q(ToString(element)).stringValue(); + } + R = `${R}${next}`; + k += 1; + } + return new Value(R); + } + + // 22.1.3.17 #sec-array.prototype.lastindexof + // 22.2.3.17 #sec-%typedarray%.prototype.lastindexof + function ArrayProto_lastIndexOf([searchElement = Value.undefined, fromIndex], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (len === 0) { + return new Value(-1); + } + let n; + if (fromIndex !== undefined) { + n = Q(ToInteger(fromIndex)).numberValue(); + } else { + n = len - 1; + } + let k; + if (n >= 0) { + k = Math.min(n, len - 1); + } else { + k = len + n; + } + while (k >= 0) { + const kStr = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, kStr)); + if (kPresent === Value.true) { + const elementK = Q(Get(O, kStr)); + const same = StrictEqualityComparison(searchElement, elementK); + if (same === Value.true) { + return new Value(k); + } + } + k -= 1; + } + return new Value(-1); + } + + // 22.1.3.21 #sec-array.prototype.reduce + // 22.2.3.20 #sec-%typedarray%.prototype.reduce + function ArrayProto_reduce([callbackfn = Value.undefined, initialValue], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + if (len === 0 && initialValue === undefined) { + return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce'); + } + let k = 0; + let accumulator = Value.undefined; + if (initialValue !== undefined) { + accumulator = initialValue; + } else { + let kPresent = false; + while (kPresent === false && k < len) { + const Pk = X(ToString(new Value(k))); + kPresent = Q(HasProperty(O, Pk)) === Value.true; + if (kPresent === true) { + accumulator = Q(Get(O, Pk)); + } + k += 1; + } + if (kPresent === false) { + return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce'); + } + } + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O])); + } + k += 1; + } + return accumulator; + } + + // 22.1.3.22 #sec-array.prototype.reduceright + // 22.2.3.21 #sec-%typedarray%.prototype.reduceright + function ArrayProto_reduceRight([callbackfn = Value.undefined, initialValue], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + if (len === 0 && initialValue === undefined) { + return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce'); + } + let k = len - 1; + let accumulator = Value.undefined; + if (initialValue !== undefined) { + accumulator = initialValue; + } else { + let kPresent = false; + while (kPresent === false && k >= 0) { + const Pk = X(ToString(new Value(k))); + kPresent = Q(HasProperty(O, Pk)) === Value.true; + if (kPresent === true) { + accumulator = Q(Get(O, Pk)); + } + k -= 1; + } + if (kPresent === false) { + return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce'); + } + } + while (k >= 0) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O])); + } + k -= 1; + } + return accumulator; + } + + // 22.1.3.23 #sec-array.prototype.reverse + // 22.2.3.22 #sec-%typedarray%.prototype.reverse + function ArrayProto_reverse(args, { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + const middle = Math.floor(len / 2); + let lower = 0; + while (lower !== middle) { + const upper = len - lower - 1; + const upperP = X(ToString(new Value(upper))); + const lowerP = X(ToString(new Value(lower))); + const lowerExists = Q(HasProperty(O, lowerP)); + let lowerValue; + let upperValue; + if (lowerExists === Value.true) { + lowerValue = Q(Get(O, lowerP)); + } + const upperExists = Q(HasProperty(O, upperP)); + if (upperExists === Value.true) { + upperValue = Q(Get(O, upperP)); + } + if (lowerExists === Value.true && upperExists === Value.true) { + Q(Set(O, lowerP, upperValue, Value.true)); + Q(Set(O, upperP, lowerValue, Value.true)); + } else if (lowerExists === Value.false && upperExists === Value.true) { + Q(Set(O, lowerP, upperValue, Value.true)); + Q(DeletePropertyOrThrow(O, upperP)); + } else if (lowerExists === Value.true && upperExists === Value.false) { + Q(DeletePropertyOrThrow(O, lowerP)); + Q(Set(O, upperP, lowerValue, Value.true)); + } else { + // no further action is required + } + lower += 1; + } + return O; + } + + // 22.1.3.26 #sec-array.prototype.some + // 22.2.3.25 #sec-%typedarray%.prototype.some + function ArrayProto_some([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const O = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(O)); + const len = Q(ToLength(lenProp)).numberValue(); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + let k = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kPresent = Q(HasProperty(O, Pk)); + if (kPresent === Value.true) { + const kValue = Q(Get(O, Pk)); + const testResult = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]))); + if (testResult === Value.true) { + return Value.true; + } + } + k += 1; + } + return Value.false; + } + + // 22.1.3.29 #sec-array.prototype.tolocalestring + // 22.2.3.28 #sec-%typedarray%.prototype.tolocalestring + function ArrayProto_toLocaleString(args, { thisValue }) { + Q(priorToEvaluatingAlgorithm(thisValue)); + const array = Q(ToObject(thisValue)); + const lenProp = Q(objectToLength(array)); + const len = Q(ToLength(lenProp)).numberValue(); + const separator = ', '; + let R = ''; + let k = 0; + while (k < len) { + if (k > 0) { + R = `${R}${separator}`; + } + const kStr = X(ToString(new Value(k))); + const nextElement = Q(Get(array, kStr)); + if (nextElement !== Value.undefined && nextElement !== Value.null) { + const S = Q(ToString(Q(Invoke(nextElement, new Value('toLocaleString'))))).stringValue(); + R = `${R}${S}`; + } + k += 1; + } + return new Value(R); + } + + assignProps(realmRec, proto, [ + ['every', ArrayProto_every, 1], + ['find', ArrayProto_find, 1], + ['findIndex', ArrayProto_findIndex, 1], + ['forEach', ArrayProto_forEach, 1], + ['includes', ArrayProto_includes, 1], + ['indexOf', ArrayProto_indexOf, 1], + ['join', ArrayProto_join, 1], + ['lastIndexOf', ArrayProto_lastIndexOf, 1], + ['reduce', ArrayProto_reduce, 1], + ['reduceRight', ArrayProto_reduceRight, 1], + ['reverse', ArrayProto_reverse, 0], + ['some', ArrayProto_some, 1], + ['toLocaleString', ArrayProto_toLocaleString, 0], + ]); +} diff --git a/src/engine262/src/intrinsics/AsyncFromSyncIteratorPrototype.mjs b/src/engine262/src/intrinsics/AsyncFromSyncIteratorPrototype.mjs new file mode 100644 index 0000000..4a29923 --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncFromSyncIteratorPrototype.mjs @@ -0,0 +1,81 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + AsyncFromSyncIteratorContinuation, + Call, + CreateIterResultObject, + GetMethod, + IteratorNext, + NewPromiseCapability, + Assert, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { IfAbruptRejectPromise, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// 25.1.4.2.1 #sec-%asyncfromsynciteratorprototype%.next +function AsyncFromSyncIteratorPrototype_next([value = Value.undefined], { thisValue }) { + const O = thisValue; + Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O); + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const syncIteratorRecord = O.SyncIteratorRecord; + const result = IteratorNext(syncIteratorRecord, value); + IfAbruptRejectPromise(result, promiseCapability); + return X(AsyncFromSyncIteratorContinuation(result, promiseCapability)); +} + +// 25.1.4.2.2 #sec-%asyncfromsynciteratorprototype%.return +function AsyncFromSyncIteratorPrototype_return([value = Value.undefined], { thisValue }) { + const O = thisValue; + Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O); + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const syncIterator = O.SyncIteratorRecord.Iterator; + const ret = GetMethod(syncIterator, new Value('return')); + IfAbruptRejectPromise(ret, promiseCapability); + if (ret === Value.undefined) { + const iterResult = X(CreateIterResultObject(value, Value.true)); + X(Call(promiseCapability.Resolve, Value.undefined, [iterResult])); + return promiseCapability.Promise; + } + const result = Call(ret, syncIterator, [value]); + IfAbruptRejectPromise(result, promiseCapability); + if (Type(result) !== 'Object') { + X(Call(promiseCapability.Reject, Value.undefined, [ + surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value, + ])); + return promiseCapability.Promise; + } + return X(AsyncFromSyncIteratorContinuation(result, promiseCapability)); +} + +// 25.1.4.2.3 #sec-%asyncfromsynciteratorprototype%.throw +function AsyncFromSyncIteratorPrototype_throw([value = Value.undefined], { thisValue }) { + const O = thisValue; + Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O); + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const syncIterator = O.SyncIteratorRecord.Iterator; + const thr = GetMethod(syncIterator, new Value('throw')); + IfAbruptRejectPromise(thr, promiseCapability); + if (thr === Value.undefined) { + X(Call(promiseCapability.Reject, Value.undefined, [value])); + return promiseCapability.Promise; + } + const result = Call(thr, syncIterator, [value]); + IfAbruptRejectPromise(result, promiseCapability); + if (Type(result) !== 'Object') { + X(Call(promiseCapability.Reject, Value.undefined, [ + surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value, + ])); + return promiseCapability.Promise; + } + return X(AsyncFromSyncIteratorContinuation(result, promiseCapability)); +} + +export function BootstrapAsyncFromSyncIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', AsyncFromSyncIteratorPrototype_next, 1], + ['return', AsyncFromSyncIteratorPrototype_return, 1], + ['throw', AsyncFromSyncIteratorPrototype_throw, 1], + ], realmRec.Intrinsics['%AsyncIteratorPrototype%']); + + realmRec.Intrinsics['%AsyncFromSyncIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/AsyncFunction.mjs b/src/engine262/src/intrinsics/AsyncFunction.mjs new file mode 100644 index 0000000..6289f91 --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncFunction.mjs @@ -0,0 +1,24 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Q } from '../completion.mjs'; +import { CreateDynamicFunction } from '../runtime-semantics/all.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function AsyncFunctionConstructor(args, { NewTarget }) { + const C = surroundingAgent.activeFunctionObject; + return Q(CreateDynamicFunction(C, NewTarget, 'async', args)); +} + +export function BootstrapAsyncFunction(realmRec) { + const cons = BootstrapConstructor(realmRec, AsyncFunctionConstructor, 'AsyncFunction', 1, realmRec.Intrinsics['%AsyncFunction.prototype%'], []); + + cons.DefineOwnProperty(new Value('prototype'), Descriptor({ + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + })); + + cons.Prototype = realmRec.Intrinsics['%Function%']; + + realmRec.Intrinsics['%AsyncFunction%'] = cons; +} diff --git a/src/engine262/src/intrinsics/AsyncFunctionPrototype.mjs b/src/engine262/src/intrinsics/AsyncFunctionPrototype.mjs new file mode 100644 index 0000000..f76de66 --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncFunctionPrototype.mjs @@ -0,0 +1,7 @@ +import { BootstrapPrototype } from './Bootstrap.mjs'; + +export function BootstrapAsyncFunctionPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [], realmRec.Intrinsics['%Function.prototype%'], 'AsyncFunction'); + + realmRec.Intrinsics['%AsyncFunction.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/AsyncGenerator.mjs b/src/engine262/src/intrinsics/AsyncGenerator.mjs new file mode 100644 index 0000000..d7db3cd --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncGenerator.mjs @@ -0,0 +1,18 @@ +import { X } from '../completion.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +export function BootstrapAsyncGenerator(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['prototype', realmRec.Intrinsics['%AsyncGenerator.prototype%'], undefined, { Writable: Value.false }], + ], realmRec.Intrinsics['%Function.prototype%'], 'AsyncGeneratorFunction'); + + X(realmRec.Intrinsics['%AsyncGenerator.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({ + Value: proto, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/AsyncGeneratorFunction.mjs b/src/engine262/src/intrinsics/AsyncGeneratorFunction.mjs new file mode 100644 index 0000000..eb38948 --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncGeneratorFunction.mjs @@ -0,0 +1,30 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Q, X } from '../completion.mjs'; +import { CreateDynamicFunction } from '../runtime-semantics/all.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function AsyncGeneratorFunctionConstructor(args, { NewTarget }) { + const C = surroundingAgent.activeFunctionObject; + return Q(CreateDynamicFunction(C, NewTarget, 'async generator', args)); +} + +export function BootstrapAsyncGeneratorFunction(realmRec) { + const cons = BootstrapConstructor(realmRec, AsyncGeneratorFunctionConstructor, 'AsyncGeneratorFunction', 1, realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'], []); + + X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({ + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + + X(realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({ + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + cons.Prototype = realmRec.Intrinsics['%Function%']; + + realmRec.Intrinsics['%AsyncGeneratorFunction%'] = cons; +} diff --git a/src/engine262/src/intrinsics/AsyncGeneratorPrototype.mjs b/src/engine262/src/intrinsics/AsyncGeneratorPrototype.mjs new file mode 100644 index 0000000..9ae047d --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncGeneratorPrototype.mjs @@ -0,0 +1,37 @@ +import { + X, + Completion, + NormalCompletion, + ThrowCompletion, +} from '../completion.mjs'; +import { Value } from '../value.mjs'; +import { AsyncGeneratorEnqueue } from '../abstract-ops/all.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function AsyncGeneratorPrototype_next([value = Value.undefined], { thisValue }) { + const generator = thisValue; + const completion = new NormalCompletion(value); + return X(AsyncGeneratorEnqueue(generator, completion)); +} + +function AsyncGeneratorPrototype_return([value = Value.undefined], { thisValue }) { + const generator = thisValue; + const completion = new Completion('return', value, undefined); + return X(AsyncGeneratorEnqueue(generator, completion)); +} + +function AsyncGeneratorPrototype_throw([exception = Value.undefined], { thisValue }) { + const generator = thisValue; + const completion = new ThrowCompletion(exception); + return X(AsyncGeneratorEnqueue(generator, completion)); +} + +export function BootstrapAsyncGeneratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', AsyncGeneratorPrototype_next, 1], + ['return', AsyncGeneratorPrototype_return, 1], + ['throw', AsyncGeneratorPrototype_throw, 1], + ], realmRec.Intrinsics['%AsyncIteratorPrototype%'], 'AsyncGenerator'); + + realmRec.Intrinsics['%AsyncGenerator.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/AsyncIteratorPrototype.mjs b/src/engine262/src/intrinsics/AsyncIteratorPrototype.mjs new file mode 100644 index 0000000..b5a4ea8 --- /dev/null +++ b/src/engine262/src/intrinsics/AsyncIteratorPrototype.mjs @@ -0,0 +1,14 @@ +import { wellKnownSymbols } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function AsyncIteratorPrototype_asyncIterator(args, { thisValue }) { + return thisValue; +} + +export function BootstrapAsyncIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + [wellKnownSymbols.asyncIterator, AsyncIteratorPrototype_asyncIterator, 0], + ], realmRec.Intrinsics['%Object.prototype%']); + + realmRec.Intrinsics['%AsyncIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/BigInt.mjs b/src/engine262/src/intrinsics/BigInt.mjs new file mode 100644 index 0000000..8a3d046 --- /dev/null +++ b/src/engine262/src/intrinsics/BigInt.mjs @@ -0,0 +1,52 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { ToPrimitive, ToBigInt, ToIndex } from '../abstract-ops/all.mjs'; +import { NumberToBigInt } from '../runtime-semantics/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function BigIntConstructor([value], { NewTarget }) { + // 1. If NewTarget is not undefined, throw a TypeError exception. + if (NewTarget !== Value.undefined) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', 'BigInt'); + } + // 2. Let prim be ? ToPrimitive(value, hint Number). + const prim = Q(ToPrimitive(value, 'Number')); + // 3. If Type(prim) is Number, return ? NumberToBigInt(prim). + // 4. Otherwise, return ? ToBigInt(value). + if (Type(prim) === 'Number') { + return Q(NumberToBigInt(prim)); + } else { + return Q(ToBigInt(value)); + } +} + +// #sec-bigint.asintn +function BigInt_asIntN([bits = Value.undefined, bigint = Value.undefined]) { + // 1. Set bits to ? ToIndex(bits). + bits = Q(ToIndex(bits)); + // 2. Set bigint to ? ToBigInt(bigint). + bigint = Q(ToBigInt(bigint)); + // 3. Let mod be the BigInt value that represents bigint modulo 2bits. + // 4. If mod ≥ 2^bits - 1, return mod - 2^bits; otherwise, return mod. + return new Value(BigInt.asIntN(bits.numberValue(), bigint.bigintValue())); +} + +// #sec-bigint.asuintn +function BigInt_asUintN([bits = Value.undefined, bigint = Value.undefined]) { + // 1. Set bits to ? ToIndex(bits). + bits = Q(ToIndex(bits)); + // 2. Set bigint to ? ToBigInt(bigint). + bigint = Q(ToBigInt(bigint)); + // 3. Return the BigInt value that represents bigint modulo 2^bits. + return new Value(BigInt.asUintN(bits.numberValue(), bigint.bigintValue())); +} + +export function BootstrapBigInt(realmRec) { + const bigintConstructor = BootstrapConstructor(realmRec, BigIntConstructor, 'BigInt', 1, realmRec.Intrinsics['%BigInt.prototype%'], [ + ['asIntN', BigInt_asIntN, 2], + ['asUintN', BigInt_asUintN, 2], + ]); + + realmRec.Intrinsics['%BigInt%'] = bigintConstructor; +} diff --git a/src/engine262/src/intrinsics/BigIntPrototype.mjs b/src/engine262/src/intrinsics/BigIntPrototype.mjs new file mode 100644 index 0000000..e2df0b7 --- /dev/null +++ b/src/engine262/src/intrinsics/BigIntPrototype.mjs @@ -0,0 +1,74 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { Assert, ToInteger, ToString } from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-thisbigintvalue +function thisBigIntValue(value) { + // 1. If Type(value) is BigInt, return value. + if (Type(value) === 'BigInt') { + return value; + } + // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then + if (Type(value) === 'Object' && 'BigIntData' in value) { + // a. Assert: Type(value.[[BigIntData]]) is BigInt. + Assert(Type(value.BigIntData) === 'BigInt'); + // b. Return value.[[BigIntData]]. + return value.BigIntData; + } + // 3. Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'BigInt', value); +} + +// #sec-bigint.prototype.tolocalestring +function BigIntProto_toLocalString(args, { thisValue }) { + return BigIntProto_toString(args, { thisValue }); +} + +// #sec-bigint.prototype.tostring +function BigIntProto_toString([radix], { thisValue }) { + // 1. Let x be ? thisBigIntValue(this value). + const x = Q(thisBigIntValue(thisValue)); + // 2. If radix is not present, let radixNumber be 10. + let radixNumber; + if (radix === undefined) { + radixNumber = 10; + } else if (radix === Value.undefined) { + // 3. Else if radix is undefined, let radixNumber be 10. + radixNumber = 10; + } else { + // 4. Else, let radixNumber be ? ToInteger(radix). + radixNumber = Q(ToInteger(radix)).numberValue(); + } + // 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. + if (radixNumber < 2 || radixNumber > 36) { + return surroundingAgent.Throw('RangeError', 'InvalidRadix'); + } + // 6. If radixNumber = 10, return ! ToString(x). + if (radixNumber === 10) { + return X(ToString(x)); + } + // 7. Return the String representation of this Number value using the radix specified by + // radixNumber. Letters a-z are used for digits with values 10 through 35. The precise + // algorithm is implementation-dependent, however the algorithm should be a + // generalization of that specified in 6.1.6.2.23. + // TODO: Implementation stringification + return new Value(x.bigintValue().toString(radixNumber)); +} + +// #sec-bigint.prototype.tostring +function BigIntProto_valueOf(args, { thisValue }) { + // Return ? thisBigIntValue(this value). + return Q(thisBigIntValue(thisValue)); +} + +export function BootstrapBigIntPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['toLocaleString', BigIntProto_toLocalString, 0], + ['toString', BigIntProto_toString, 0], + ['valueOf', BigIntProto_valueOf, 0], + ], realmRec.Intrinsics['%Object.prototype%'], 'BigInt'); + + realmRec.Intrinsics['%BigInt.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Boolean.mjs b/src/engine262/src/intrinsics/Boolean.mjs new file mode 100644 index 0000000..fa90a4f --- /dev/null +++ b/src/engine262/src/intrinsics/Boolean.mjs @@ -0,0 +1,26 @@ +import { + OrdinaryCreateFromConstructor, + ToBoolean, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function BooleanConstructor([value = Value.undefined], { NewTarget }) { + const b = ToBoolean(value); + if (Type(NewTarget) === 'Undefined') { + return b; + } + const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Boolean.prototype%', ['BooleanData'])); + O.BooleanData = b; + return O; +} + +export function BootstrapBoolean(realmRec) { + const cons = BootstrapConstructor( + realmRec, BooleanConstructor, 'Boolean', 1, + realmRec.Intrinsics['%Boolean.prototype%'], [], + ); + + realmRec.Intrinsics['%Boolean%'] = cons; +} diff --git a/src/engine262/src/intrinsics/BooleanPrototype.mjs b/src/engine262/src/intrinsics/BooleanPrototype.mjs new file mode 100644 index 0000000..c163c59 --- /dev/null +++ b/src/engine262/src/intrinsics/BooleanPrototype.mjs @@ -0,0 +1,48 @@ +import { + Type, + Value, +} from '../value.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { Assert } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +function thisBooleanValue(value) { + if (Type(value) === 'Boolean') { + return value; + } + + if (Type(value) === 'Object' && 'BooleanData' in value) { + const b = value.BooleanData; + Assert(Type(b) === 'Boolean'); + return b; + } + + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Boolean', value); +} + +function BooleanProto_toString(argList, { thisValue }) { + const b = Q(thisBooleanValue(thisValue)); + if (b === Value.true) { + return new Value('true'); + } + return new Value('false'); +} + +function BooleanProto_valueOf(argList, { thisValue }) { + return Q(thisBooleanValue(thisValue)); +} + +export function BootstrapBooleanPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['toString', BooleanProto_toString, 0], + ['valueOf', BooleanProto_valueOf, 0], + ], realmRec.Intrinsics['%Object.prototype%']); + + proto.BooleanData = Value.false; + + realmRec.Intrinsics['%Boolean.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Bootstrap.mjs b/src/engine262/src/intrinsics/Bootstrap.mjs new file mode 100644 index 0000000..3a9aeb0 --- /dev/null +++ b/src/engine262/src/intrinsics/Bootstrap.mjs @@ -0,0 +1,116 @@ +import { + Assert, + CreateBuiltinFunction, + OrdinaryObjectCreate, + SetFunctionLength, + SetFunctionName, +} from '../abstract-ops/all.mjs'; +import { + Descriptor, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { X } from '../completion.mjs'; + +// 17 #sec-ecmascript-standard-built-in-objects +export function assignProps(realmRec, obj, props) { + for (const item of props) { + if (item === undefined) { + continue; + } + const [n, v, len, descriptor] = item; + const name = n instanceof Value ? n : new Value(n); + if (Array.isArray(v)) { + // Every accessor property described in clauses 18 through 26 and in + // Annex B.2 has the attributes { [[Enumerable]]: false, + // [[Configurable]]: true } unless otherwise specified. If only a get + // accessor function is described, the set accessor function is the + // default value, undefined. If only a set accessor is described the get + // accessor is the default value, undefined. + let [ + getter = Value.undefined, + setter = Value.undefined, + ] = v; + if (typeof getter === 'function') { + getter = CreateBuiltinFunction(getter, [], realmRec); + X(SetFunctionName(getter, name, new Value('get'))); + X(SetFunctionLength(getter, new Value(0))); + } + if (typeof setter === 'function') { + setter = CreateBuiltinFunction(setter, [], realmRec); + X(SetFunctionName(setter, name, new Value('set'))); + X(SetFunctionLength(setter, new Value(1))); + } + X(obj.DefineOwnProperty(name, Descriptor({ + Get: getter, + Set: setter, + Enumerable: Value.false, + Configurable: Value.true, + ...descriptor, + }))); + } else { + // Every other data property described in clauses 18 through 26 and in + // Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: + // false, [[Configurable]]: true } unless otherwise specified. + let value; + if (typeof v === 'function') { + Assert(typeof len === 'number'); + value = CreateBuiltinFunction(v, [], realmRec); + X(SetFunctionName(value, name)); + X(SetFunctionLength(value, new Value(len))); + } else { + value = v; + } + obj.properties.set(name, Descriptor({ + Value: value, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + ...descriptor, + })); + } + } +} + +export function BootstrapPrototype(realmRec, props, Prototype, stringTag) { + Assert(Prototype !== undefined); + const proto = OrdinaryObjectCreate(Prototype); + + assignProps(realmRec, proto, props); + + if (stringTag !== undefined) { + X(proto.DefineOwnProperty(wellKnownSymbols.toStringTag, Descriptor({ + Value: new Value(stringTag), + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + + return proto; +} + +export function BootstrapConstructor(realmRec, Constructor, name, length, Prototype, props = []) { + const cons = CreateBuiltinFunction(Constructor, [], realmRec, undefined, Value.true); + + SetFunctionName(cons, new Value(name)); + SetFunctionLength(cons, new Value(length)); + + X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({ + Value: Prototype, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + + X(Prototype.DefineOwnProperty(new Value('constructor'), Descriptor({ + Value: cons, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + assignProps(realmRec, cons, props); + + return cons; +} diff --git a/src/engine262/src/intrinsics/DataView.mjs b/src/engine262/src/intrinsics/DataView.mjs new file mode 100644 index 0000000..f741f26 --- /dev/null +++ b/src/engine262/src/intrinsics/DataView.mjs @@ -0,0 +1,65 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + IsDetachedBuffer, + OrdinaryCreateFromConstructor, + ToIndex, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// #sec-dataview-constructor +function DataViewConstructor([buffer = Value.undefined, byteOffset = Value.undefined, byteLength = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]). + Q(RequireInternalSlot(buffer, 'ArrayBufferData')); + // 3. Let offset be ? ToIndex(byteOffset). + const offset = Q(ToIndex(byteOffset)).numberValue(); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + const bufferByteLength = buffer.ArrayBufferByteLength.numberValue(); + // 6. If offset > bufferByteLength, throw a RangeError exception. + if (offset > bufferByteLength) { + return surroundingAgent.Throw('RangeError', 'DataViewOOB'); + } + let viewByteLength; + // 7. If byteLength is undefined, then + if (byteLength === Value.undefined) { + // a. Let viewByteLength be bufferByteLength - offset. + viewByteLength = bufferByteLength - offset; + } else { + // a. Let viewByteLength be ? ToIndex(byteLength). + viewByteLength = Q(ToIndex(byteLength)).numberValue(); + // b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. + if (offset + viewByteLength > bufferByteLength) { + return surroundingAgent.Throw('RangeError', 'DataViewOOB'); + } + } + // 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »). + const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%DataView.prototype%', ['DataView', 'ViewedArrayBuffer', 'ByteLength', 'ByteOffset'])); + // 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 11. Set O.[[ViewedArrayBuffer]] to buffer. + O.ViewedArrayBuffer = buffer; + // 12. Set O.[[ByteLength]] to viewByteLength. + O.ByteLength = new Value(viewByteLength); + // 13. Set O.[[ByteOffset]] to offset. + O.ByteOffset = new Value(offset); + // 14. Return O. + return O; +} + +export function BootstrapDataView(realmRec) { + const dvConstructor = BootstrapConstructor(realmRec, DataViewConstructor, 'DataView', 1, realmRec.Intrinsics['%DataView.prototype%'], []); + + realmRec.Intrinsics['%DataView%'] = dvConstructor; +} diff --git a/src/engine262/src/intrinsics/DataViewPrototype.mjs b/src/engine262/src/intrinsics/DataViewPrototype.mjs new file mode 100644 index 0000000..86cfbef --- /dev/null +++ b/src/engine262/src/intrinsics/DataViewPrototype.mjs @@ -0,0 +1,275 @@ +import { + Assert, + GetViewValue, + SetViewValue, + IsDetachedBuffer, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-get-dataview.prototype.buffer +function DataViewProto_buffer(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + Q(RequireInternalSlot(O, 'DataView')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. Return buffer. + return buffer; +} + +// #sec-get-dataview.prototype.bytelength +function DataViewProto_byteLength(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + Q(RequireInternalSlot(O, 'DataView')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 6. Let size be O.[[ByteLength]]. + const size = O.ByteLength; + // 7. Return size. + return size; +} + +// #sec-get-dataview.prototype.byteoffset +function DataViewProto_byteOffset(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + Q(RequireInternalSlot(O, 'DataView')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 6. Let offset be O.[[ByteOffset]]. + const offset = O.ByteOffset; + // 7. Return offset. + return offset; +} + +// #sec-dataview.prototype.getbigint64 +function DataViewProto_getBigInt64([byteOffset = Value.undefined, littleEndian], { thisValue }) { + // 1. Let v be the this value. + const v = thisValue; + // 2. If littleEndian is not present, set littleEndian to undefined. + if (littleEndian === undefined) { + littleEndian = Value.undefined; + } + // 3. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + return Q(GetViewValue(v, byteOffset, littleEndian, 'BigInt64')); +} + +// #sec-dataview.prototype.getbiguint64 +function DataViewProto_getBigUint64([byteOffset = Value.undefined, littleEndian], { thisValue }) { + // 1. Let v be the this value. + const v = thisValue; + // 2. If littleEndian is not present, set littleEndian to undefined. + if (littleEndian === undefined) { + littleEndian = Value.undefined; + } + // 3. Return ? GetViewValue(v, byteOffset, littleEndian, BigUint64). + return Q(GetViewValue(v, byteOffset, littleEndian, 'BigUint64')); +} + +// 24.3.4.5 #sec-dataview.prototype.getfloat32 +function DataViewProto_getFloat32([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Float32')); +} + +// 24.3.4.6 #sec-dataview.prototype.getfloat64 +function DataViewProto_getFloat64([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Float64')); +} + +// 24.3.4.7 #sec-dataview.prototype.getint8 +function DataViewProto_getInt8([byteOffset = Value.undefined], { thisValue }) { + const v = thisValue; + return Q(GetViewValue(v, byteOffset, Value.true, 'Int8')); +} + +// 24.3.4.8 #sec-dataview.prototype.getint16 +function DataViewProto_getInt16([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Int16')); +} + +// 24.3.4.9 #sec-dataview.prototype.getint32 +function DataViewProto_getInt32([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Int32')); +} + +// 24.3.4.10 #sec-dataview.prototype.getuint8 +function DataViewProto_getUint8([byteOffset = Value.undefined], { thisValue }) { + const v = thisValue; + return Q(GetViewValue(v, byteOffset, Value.true, 'Uint8')); +} + +// 24.3.4.11 #sec-dataview.prototype.getuint16 +function DataViewProto_getUint16([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint16')); +} + +// 24.3.4.12 #sec-dataview.prototype.getuint32 +function DataViewProto_getUint32([byteOffset = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint32')); +} + +// #sec-dataview.prototype.setbigint64 +function DataViewProto_setBigInt64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + // 1. Let v be the this value. + const v = thisValue; + // 2. If littleEndian is not present, set littleEndian to undefined. + if (littleEndian === undefined) { + littleEndian = Value.undefined; + } + // 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigInt64, value). + return Q(SetViewValue(v, byteOffset, littleEndian, 'BigInt64', value)); +} + +// #sec-dataview.prototype.setbiguint64 +function DataViewProto_setBigUint64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + // 1. Let v be the this value. + const v = thisValue; + // 2. If littleEndian is not present, set littleEndian to undefined. + if (littleEndian === undefined) { + littleEndian = Value.undefined; + } + // 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value). + return Q(SetViewValue(v, byteOffset, littleEndian, 'BigUint64', value)); +} + +// 24.3.4.13 #sec-dataview.prototype.setfloat32 +function DataViewProto_setFloat32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Float32', value)); +} + +// 24.3.4.14 #sec-dataview.prototype.setfloat64 +function DataViewProto_setFloat64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Float64', value)); +} + +// 24.3.4.15 #sec-dataview.prototype.setint8 +function DataViewProto_setInt8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) { + const v = thisValue; + return Q(SetViewValue(v, byteOffset, Value.true, 'Int8', value)); +} + +// 24.3.4.16 #sec-dataview.prototype.setint16 +function DataViewProto_setInt16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Int16', value)); +} + +// 24.3.4.17 #sec-dataview.prototype.setint32 +function DataViewProto_setInt32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Int32', value)); +} + +// 24.3.4.18 #sec-dataview.prototype.setuint8 +function DataViewProto_setUint8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) { + const v = thisValue; + return Q(SetViewValue(v, byteOffset, Value.true, 'Uint8', value)); +} + +// 24.3.4.19 #sec-dataview.prototype.setuint16 +function DataViewProto_setUint16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint16', value)); +} + +// 24.3.4.20 #sec-dataview.prototype.setuint32 +function DataViewProto_setUint32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) { + const v = thisValue; + if (littleEndian === undefined) { + littleEndian = Value.false; + } + return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint32', value)); +} + +export function BootstrapDataViewPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['buffer', [DataViewProto_buffer]], + ['byteLength', [DataViewProto_byteLength]], + ['byteOffset', [DataViewProto_byteOffset]], + ['getBigInt64', DataViewProto_getBigInt64, 1], + ['getBigUint64', DataViewProto_getBigUint64, 1], + ['getFloat32', DataViewProto_getFloat32, 1], + ['getFloat64', DataViewProto_getFloat64, 1], + ['getInt8', DataViewProto_getInt8, 1], + ['getInt16', DataViewProto_getInt16, 1], + ['getInt32', DataViewProto_getInt32, 1], + ['getUint8', DataViewProto_getUint8, 1], + ['getUint16', DataViewProto_getUint16, 1], + ['getUint32', DataViewProto_getUint32, 1], + ['setBigInt64', DataViewProto_setBigInt64, 2], + ['setBigUint64', DataViewProto_setBigUint64, 2], + ['setFloat32', DataViewProto_setFloat32, 2], + ['setFloat64', DataViewProto_setFloat64, 2], + ['setInt8', DataViewProto_setInt8, 2], + ['setInt16', DataViewProto_setInt16, 2], + ['setInt32', DataViewProto_setInt32, 2], + ['setUint8', DataViewProto_setUint8, 2], + ['setUint16', DataViewProto_setUint16, 2], + ['setUint32', DataViewProto_setUint32, 2], + ], realmRec.Intrinsics['%Object.prototype%'], 'DataView'); + + realmRec.Intrinsics['%DataView.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Date.mjs b/src/engine262/src/intrinsics/Date.mjs new file mode 100644 index 0000000..1d82ee9 --- /dev/null +++ b/src/engine262/src/intrinsics/Date.mjs @@ -0,0 +1,203 @@ +import { + Assert, + OrdinaryCreateFromConstructor, + ToPrimitive, + ToNumber, + ToInteger, + ToString, + MakeDate, + MakeDay, + MakeTime, + UTC, + TimeClip, +} from '../abstract-ops/all.mjs'; +import { Value, Type } from '../value.mjs'; +import { + AbruptCompletion, + Q, X, +} from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; +import { ToDateString, thisTimeValue } from './DatePrototype.mjs'; + +function DateConstructor(args, { NewTarget }) { + const numberOfArgs = args.length; + if (numberOfArgs >= 2) { + // 20.3.2.1 #sec-date-year-month-date-hours-minutes-seconds-ms + const [year, month, date, hours, minutes, seconds, ms] = args; + Assert(numberOfArgs >= 2); + if (NewTarget === Value.undefined) { + const now = Date.now(); + return ToDateString(new Value(now)); + } else { + const y = Q(ToNumber(year)); + const m = Q(ToNumber(month)); + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = new Value(1); + } + let h; + if (hours !== undefined) { + h = Q(ToNumber(hours)); + } else { + h = new Value(0); + } + let min; + if (minutes !== undefined) { + min = Q(ToNumber(minutes)); + } else { + min = new Value(0); + } + let s; + if (seconds !== undefined) { + s = Q(ToNumber(seconds)); + } else { + s = new Value(0); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = new Value(0); + } + let yr; + if (y.isNaN()) { + yr = new Value(NaN); + } else { + const yi = X(ToInteger(y)).numberValue(); + if (yi >= 0 && yi <= 99) { + yr = new Value(1900 + yi); + } else { + yr = y; + } + } + const finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)); + const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue'])); + O.DateValue = TimeClip(UTC(finalDate)); + return O; + } + } else if (numberOfArgs === 1) { + const [value] = args; + // 20.3.2.2 #sec-date-value + Assert(numberOfArgs === 1); + if (NewTarget === Value.undefined) { + const now = Date.now(); + return ToDateString(new Value(now)); + } else { + let tv; + if (Type(value) === 'Object' && 'DateValue' in value) { + tv = thisTimeValue(value); + } else { + const v = Q(ToPrimitive(value)); + if (Type(v) === 'String') { + // Assert: The next step never returns an abrupt completion because Type(v) is String. + tv = parseDate(v); + } else { + tv = Q(ToNumber(v)); + } + } + const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue'])); + O.DateValue = TimeClip(tv); + return O; + } + } else { + // 20.3.2.3 #sec-date-constructor-date + Assert(numberOfArgs === 0); + if (NewTarget === Value.undefined) { + const now = Date.now(); + return ToDateString(new Value(now)); + } else { + const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue'])); + O.DateValue = new Value(Date.now()); + return O; + } + } +} + +// 20.3.3.1 #sec-date.now +function Date_now() { + const now = Date.now(); + return new Value(now); +} + +// 20.3.3.2 #sec-date.parse +function Date_parse([string = Value.undefined]) { + const str = ToString(string); + if (str instanceof AbruptCompletion) { + return str; + } + return parseDate(str); +} + +// 20.3.3.4 #sec-date.utc +function Date_UTC([year = Value.undefined, month, date, hours, minutes, seconds, ms]) { + const y = Q(ToNumber(year)); + let m; + if (month !== undefined) { + m = Q(ToNumber(month)); + } else { + m = new Value(0); + } + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = new Value(1); + } + let h; + if (hours !== undefined) { + h = Q(ToNumber(hours)); + } else { + h = new Value(0); + } + let min; + if (minutes !== undefined) { + min = Q(ToNumber(minutes)); + } else { + min = new Value(0); + } + let s; + if (seconds !== undefined) { + s = Q(ToNumber(seconds)); + } else { + s = new Value(0); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = new Value(0); + } + + let yr; + if (y.isNaN()) { + yr = new Value(NaN); + } else { + const yi = X(ToInteger(y)).numberValue(); + if (yi >= 0 && yi <= 99) { + yr = new Value(1900 + yi); + } else { + yr = y; + } + } + + return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))); +} + +function parseDate(dateTimeString) { + // 20.3.1.15 #sec-date-time-string-format + // TODO: implement parsing without the host. + const parsed = Date.parse(dateTimeString.stringValue()); + return new Value(parsed); +} + +export function BootstrapDate(realmRec) { + const cons = BootstrapConstructor(realmRec, DateConstructor, 'Date', 7, realmRec.Intrinsics['%Date.prototype%'], [ + ['now', Date_now, 0], + ['parse', Date_parse, 1], + ['UTC', Date_UTC, 7], + ]); + + realmRec.Intrinsics['%Date%'] = cons; +} diff --git a/src/engine262/src/intrinsics/DatePrototype.mjs b/src/engine262/src/intrinsics/DatePrototype.mjs new file mode 100644 index 0000000..1545bd6 --- /dev/null +++ b/src/engine262/src/intrinsics/DatePrototype.mjs @@ -0,0 +1,697 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + DateFromTime, + Day, + HourFromTime, + Invoke, + LocalTime, + LocalTZA, + MakeDate, + MakeDay, + MakeTime, + MinFromTime, + MonthFromTime, + msFromTime, + msPerMinute, + OrdinaryToPrimitive, + SecFromTime, + TimeClip, + TimeWithinDay, + ToNumber, + ToPrimitive, + ToObject, + UTC, + WeekDay, + YearFromTime, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { StringPad } from '../runtime-semantics/all.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +export function thisTimeValue(value) { + if (Type(value) === 'Object' && 'DateValue' in value) { + return value.DateValue; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', value); +} + +// 20.3.4.2 #sec-date.prototype.getdate +function DateProto_getDate(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return DateFromTime(LocalTime(t)); +} + +// 20.3.4.3 #sec-date.prototype.getday +function DateProto_getDay(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return WeekDay(LocalTime(t)); +} + +// 20.3.4.4 #sec-date.prototype.getfullyear +function DateProto_getFullYear(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return YearFromTime(LocalTime(t)); +} + +// 20.3.4.5 #sec-date.prototype.gethours +function DateProto_getHours(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return HourFromTime(LocalTime(t)); +} + +// 20.3.4.6 #sec-date.prototype.getmilliseconds +function DateProto_getMilliseconds(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return msFromTime(LocalTime(t)); +} + +// 20.3.4.7 #sec-date.prototype.getminutes +function DateProto_getMinutes(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return MinFromTime(LocalTime(t)); +} + +// 20.3.4.8 #sec-date.prototype.getmonth +function DateProto_getMonth(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return MonthFromTime(LocalTime(t)); +} + +// 20.3.4.9 #sec-date.prototype.getseconds +function DateProto_getSeconds(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return SecFromTime(LocalTime(t)); +} + +// 20.3.4.10 #sec-date.prototype.gettime +function DateProto_getTime(args, { thisValue }) { + return Q(thisTimeValue(thisValue)); +} + +// 20.3.4.11 #sec-date.prototype.gettimezoneoffset +function DateProto_getTimezoneOffset(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return new Value(t.numberValue() - LocalTime(t).numberValue() / msPerMinute); +} + +// 20.3.4.12 #sec-date.prototype.getutcdate +function DateProto_getUTCDate(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return DateFromTime(t); +} + +// 20.3.4.13 #sec-date.prototype.getutcday +function DateProto_getUTCDay(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return WeekDay(t); +} + +// 20.3.4.14 #sec-date.prototype.getutcfullyear +function DateProto_getUTCFullYear(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return YearFromTime(t); +} + +// 20.3.4.15 #sec-date.prototype.getutchours +function DateProto_getUTCHours(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return HourFromTime(t); +} + +// 20.3.4.16 #sec-date.prototype.getutcmilliseconds +function DateProto_getUTCMilliseconds(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return msFromTime(t); +} + +// 20.3.4.17 #sec-date.prototype.getutcminutes +function DateProto_getUTCMinutes(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return MinFromTime(t); +} + +// 20.3.4.18 #sec-date.prototype.getutcmonth +function DateProto_getUTCMonth(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return MonthFromTime(t); +} + +// 20.3.4.19 #sec-date.prototype.getutcseconds +function DateProto_getUTCSeconds(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + return new Value(NaN); + } + return SecFromTime(t); +} + +// 20.3.4.20 #sec-date.prototype.setdate +function DateProto_setDate([date = Value.undefined], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + const dt = Q(ToNumber(date)); + const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)); + const u = TimeClip(UTC(newDate)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.21 #sec-date.prototype.setfullyear +function DateProto_setFullYear([year = Value.undefined, month, date], { thisValue }) { + let t = Q(thisTimeValue(thisValue)); + t = t.isNaN() ? new Value(0) : LocalTime(t); + const y = Q(ToNumber(year)); + let m; + if (month !== undefined) { + m = Q(ToNumber(month)); + } else { + m = MonthFromTime(t); + } + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = DateFromTime(t); + } + const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)); + const u = TimeClip(UTC(newDate)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.22 #sec-date.prototype.sethours +function DateProto_setHours([hour = Value.undefined, min, sec, ms], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + const h = Q(ToNumber(hour)); + let m; + if (min !== undefined) { + m = Q(ToNumber(min)); + } else { + m = MinFromTime(t); + } + let s; + if (sec !== undefined) { + s = Q(ToNumber(sec)); + } else { + s = SecFromTime(t); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const date = MakeDate(Day(t), MakeTime(h, m, s, milli)); + const u = TimeClip(UTC(date)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.23 #sec-date.prototype.setmilliseconds +function DateProto_setMilliseconds([ms = Value.undefined], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + ms = Q(ToNumber(ms)); + const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms); + const u = TimeClip(UTC(MakeDate(Day(t), time))); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.24 #sec-date.prototype.setminutes +function DateProto_setMinutes([min = Value.undefined, sec, ms], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + const m = Q(ToNumber(min)); + let s; + if (sec !== undefined) { + s = Q(ToNumber(sec)); + } else { + s = SecFromTime(t); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); + const u = TimeClip(UTC(date)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.25 #sec-date.prototype.setmonth +function DateProto_setMonth([month = Value.undefined, date], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + const m = Q(ToNumber(month)); + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = DateFromTime(t); + } + const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)); + const u = TimeClip(UTC(newDate)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.26 #sec-date.prototype.setseconds +function DateProto_setSeconds([sec = Value.undefined, ms], { thisValue }) { + const t = LocalTime(Q(thisTimeValue(thisValue))); + const s = Q(ToNumber(sec)); + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); + const u = TimeClip(UTC(date)); + thisValue.DateValue = u; + return u; +} + +// 20.3.4.27 #sec-date.prototype.settime +function DateProto_setTime([time = Value.undefined], { thisValue }) { + Q(thisTimeValue(thisValue)); + const t = Q(ToNumber(time)); + const v = TimeClip(t); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.28 #sec-date.prototype.setutcdate +function DateProto_setUTCDate([date = Value.undefined], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const dt = Q(ToNumber(date)); + const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)); + const v = TimeClip(newDate); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.29 #sec-date.prototype.setutcfullyear +function DateProto_setUTCFullYear([year = Value.undefined, month, date], { thisValue }) { + let t = Q(thisTimeValue(thisValue)); + if (t.isNaN()) { + t = new Value(0); + } + const y = Q(ToNumber(year)); + let m; + if (month !== undefined) { + m = Q(ToNumber(month)); + } else { + m = MonthFromTime(t); + } + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = DateFromTime(t); + } + const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)); + const v = TimeClip(newDate); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.30 #sec-date.prototype.setutchours +function DateProto_setUTCHours([hour = Value.undefined, min, sec, ms], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const h = Q(ToNumber(hour)); + let m; + if (min !== undefined) { + m = Q(ToNumber(min)); + } else { + m = MinFromTime(t); + } + let s; + if (sec !== undefined) { + s = Q(ToNumber(sec)); + } else { + s = SecFromTime(t); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const newDate = MakeDate(Day(t), MakeTime(h, m, s, milli)); + const v = TimeClip(newDate); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.31 #sec-date.prototype.setutcmilliseconds +function DateProto_setUTCMilliseconds([ms = Value.undefined], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const milli = Q(ToNumber(ms)); + const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli); + const v = TimeClip(MakeDate(Day(t), time)); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.32 #sec-date.prototype.setutcminutes +function DateProto_setUTCMinutes([min = Value.undefined, sec, ms], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const m = Q(ToNumber(min)); + let s; + if (sec !== undefined) { + s = Q(ToNumber(sec)); + } else { + s = SecFromTime(t); + } + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); + const v = TimeClip(date); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.33 #sec-date.prototype.setutcmonth +function DateProto_setUTCMonth([month = Value.undefined, date], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const m = Q(ToNumber(month)); + let dt; + if (date !== undefined) { + dt = Q(ToNumber(date)); + } else { + dt = DateFromTime(t); + } + const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)); + const v = TimeClip(newDate); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.34 #sec-date.prototype.setutcseconds +function DateProto_setUTCSeconds([sec = Value.undefined, ms], { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + const s = Q(ToNumber(sec)); + let milli; + if (ms !== undefined) { + milli = Q(ToNumber(ms)); + } else { + milli = msFromTime(t); + } + const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); + const v = TimeClip(date); + thisValue.DateValue = v; + return v; +} + +// 20.3.4.35 #sec-date.prototype.todatestring +function DateProto_toDateString(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O); + } + const tv = Q(thisTimeValue(O)); + if (tv.isNaN()) { + return new Value('Invalid Date'); + } + const t = LocalTime(tv); + return DateString(t); +} + +// 20.3.4.36 #sec-date.prototype.toisostring +function DateProto_toISOString(args, { thisValue }) { + const t = Q(thisTimeValue(thisValue)); + if (!Number.isFinite(t.numberValue())) { + return surroundingAgent.Throw('RangeError', 'DateInvalidTime'); + } + const year = YearFromTime(t).numberValue(); + const month = MonthFromTime(t).numberValue() + 1; + const date = DateFromTime(t).numberValue(); + const hour = HourFromTime(t).numberValue(); + const min = MinFromTime(t).numberValue(); + const sec = SecFromTime(t).numberValue(); + const ms = msFromTime(t).numberValue(); + + // TODO: figure out if there can be invalid years. + let YYYY = String(year); + if (year < 0 || year > 9999) { + YYYY = year < 0 ? `-${String(year).padStart(6, '0')}` : `+${String(year).padStart(6, '0')}`; + } + const MM = String(month).padStart(2, '0'); + const DD = String(date).padStart(2, '0'); + const HH = String(hour).padStart(2, '0'); + const mm = String(min).padStart(2, '0'); + const ss = String(sec).padStart(2, '0'); + const sss = String(ms).padStart(3, '0'); + const format = `${YYYY}-${MM}-${DD}T${HH}:${mm}:${ss}.${sss}Z`; + return new Value(format); +} + +// 20.3.4.37 #sec-date.prototype.tojson +function DateProto_toJSON(args, { thisValue }) { + const O = Q(ToObject(thisValue)); + const tv = Q(ToPrimitive(O, 'Number')); + if (Type(tv) === 'Number' && !Number.isFinite(tv.numberValue())) { + return Value.null; + } + return Q(Invoke(O, new Value('toISOString'))); +} + +// 20.3.4.38 #sec-date.prototype.tolocaledatestring +function DateProto_toLocaleDateString() { + // TODO: implement this function. + return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleDateString is not implemented'); +} + +// 20.3.4.39 #sec-date.prototype.tolocalestring +function DateProto_toLocaleString() { + // TODO: implement this function. + return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleString is not implemented'); +} + +// 20.3.4.40 #sec-date.prototype.tolocaletimestring +function DateProto_toLocaleTimeString() { + // TODO: implement this function. + return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleTimeString is not implemented'); +} + +// 20.3.4.41 #sec-date.prototype.tostring +function DateProto_toString(args, { thisValue }) { + const tv = Q(thisTimeValue(thisValue)); + return ToDateString(tv); +} + +// 20.3.4.41.1 #sec-timestring +function TimeString(tv) { + Assert(Type(tv) === 'Number'); + Assert(!tv.isNaN()); + const hour = String(HourFromTime(tv).numberValue()).padStart(2, '0'); + const minute = String(MinFromTime(tv).numberValue()).padStart(2, '0'); + const second = String(SecFromTime(tv).numberValue()).padStart(2, '0'); + return new Value(`${hour}:${minute}:${second} GMT`); +} + +// Table 46 #sec-todatestring-day-names +const daysOfTheWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; +// Table 47 #sec-todatestring-month-names +const monthsOfTheYear = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +// 20.3.4.41.2 #sec-datestring +function DateString(tv) { + Assert(Type(tv) === 'Number'); + Assert(!tv.isNaN()); + const weekday = daysOfTheWeek[WeekDay(tv).numberValue()]; + const month = monthsOfTheYear[MonthFromTime(tv).numberValue()]; + const day = String(DateFromTime(tv).numberValue()).padStart(2, '0'); + const yv = YearFromTime(tv).numberValue(); + const yearSign = yv >= 0 ? '' : '-'; + const year = new Value(String(Math.abs(yv))); + const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue(); + return new Value(`${weekday} ${month} ${day} ${yearSign}${paddedYear}`); +} + +// 20.3.4.41.3 #sec-timezoneestring +export function TimeZoneString(tv) { + Assert(Type(tv) === 'Number'); + Assert(!tv.isNaN()); + const offset = LocalTZA(tv, true); + const offsetSign = offset >= 0 ? '+' : '-'; + const offsetMin = String(MinFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0'); + const offsetHour = String(HourFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0'); + const tzName = ''; + return new Value(`${offsetSign}${offsetHour}${offsetMin}${tzName}`); +} + +// 20.3.4.41.4 #sec-todatestring +export function ToDateString(tv) { + Assert(Type(tv) === 'Number'); + if (tv.isNaN()) { + return new Value('Invalid Date'); + } + const t = LocalTime(tv); + return new Value(`${DateString(t).stringValue()} ${TimeString(t).stringValue()}${TimeZoneString(t).stringValue()}`); +} + +// 20.3.4.42 #sec-date.prototype.totimestring +function DateProto_toTimeString(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O); + } + const tv = Q(thisTimeValue(O)); + if (tv.isNaN()) { + return new Value('Invalid Date'); + } + const t = LocalTime(tv); + return new Value(`${TimeString(t).stringValue()}${TimeZoneString(tv).stringValue()}`); +} + +// 20.3.4.43 #sec-date.prototype.toutcstring +function DateProto_toUTCString(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O); + } + const tv = Q(thisTimeValue(O)); + if (tv.isNaN()) { + return new Value('Invalid Date'); + } + const weekday = daysOfTheWeek[WeekDay(tv).numberValue()]; + const month = monthsOfTheYear[MonthFromTime(tv).numberValue()]; + const day = String(DateFromTime(tv).numberValue()).padStart(2, '0'); + const yv = YearFromTime(tv).numberValue(); + const yearSign = yv >= 0 ? '' : '-'; + const year = new Value(String(Math.abs(yv))); + const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue(); + return new Value(`${weekday}, ${day} ${month} ${yearSign}${paddedYear} ${TimeString(tv).stringValue()}`); +} + +// 20.3.4.44 #sec-date.prototype.valueof +function DateProto_valueOf(args, { thisValue }) { + return Q(thisTimeValue(thisValue)); +} + +// 20.3.4.45 #sec-date.prototype-@@toprimitive +function DateProto_toPrimitive([hint = Value.undefined], { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O); + } + let tryFirst; + if (Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'default')) { + tryFirst = new Value('string'); + } else if (Type(hint) === 'String' && hint.stringValue() === 'number') { + tryFirst = new Value('number'); + } else { + return surroundingAgent.Throw('TypeError', 'InvalidHint', hint); + } + return Q(OrdinaryToPrimitive(O, tryFirst)); +} + +export function BootstrapDatePrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['getDate', DateProto_getDate, 0], + ['getDay', DateProto_getDay, 0], + ['getFullYear', DateProto_getFullYear, 0], + ['getHours', DateProto_getHours, 0], + ['getMilliseconds', DateProto_getMilliseconds, 0], + ['getMinutes', DateProto_getMinutes, 0], + ['getMonth', DateProto_getMonth, 0], + ['getSeconds', DateProto_getSeconds, 0], + ['getTime', DateProto_getTime, 0], + ['getTimezoneOffset', DateProto_getTimezoneOffset, 0], + ['getUTCDate', DateProto_getUTCDate, 0], + ['getUTCDay', DateProto_getUTCDay, 0], + ['getUTCFullYear', DateProto_getUTCFullYear, 0], + ['getUTCHours', DateProto_getUTCHours, 0], + ['getUTCMilliseconds', DateProto_getUTCMilliseconds, 0], + ['getUTCMinutes', DateProto_getUTCMinutes, 0], + ['getUTCMonth', DateProto_getUTCMonth, 0], + ['getUTCSeconds', DateProto_getUTCSeconds, 0], + ['setDate', DateProto_setDate, 1], + ['setFullYear', DateProto_setFullYear, 3], + ['setHours', DateProto_setHours, 4], + ['setMilliseconds', DateProto_setMilliseconds, 1], + ['setMinutes', DateProto_setMinutes, 3], + ['setMonth', DateProto_setMonth, 2], + ['setSeconds', DateProto_setSeconds, 2], + ['setTime', DateProto_setTime, 1], + ['setUTCDate', DateProto_setUTCDate, 1], + ['setUTCFullYear', DateProto_setUTCFullYear, 3], + ['setUTCHours', DateProto_setUTCHours, 4], + ['setUTCMilliseconds', DateProto_setUTCMilliseconds, 1], + ['setUTCMinutes', DateProto_setUTCMinutes, 3], + ['setUTCMonth', DateProto_setUTCMonth, 2], + ['setUTCSeconds', DateProto_setUTCSeconds, 2], + ['toDateString', DateProto_toDateString, 0], + ['toISOString', DateProto_toISOString, 0], + ['toJSON', DateProto_toJSON, 1], + ['toLocaleDateString', DateProto_toLocaleDateString, 0], + ['toLocaleString', DateProto_toLocaleString, 0], + ['toLocaleTimeString', DateProto_toLocaleTimeString, 0], + ['toString', DateProto_toString, 0], + ['toTimeString', DateProto_toTimeString, 0], + ['toUTCString', DateProto_toUTCString, 0], + ['valueOf', DateProto_valueOf, 0], + [wellKnownSymbols.toPrimitive, DateProto_toPrimitive, 1, { Writable: Value.false, Enumerable: Value.false, Configurable: Value.true }], + ], realmRec.Intrinsics['%Object.prototype%']); + + realmRec.Intrinsics['%Date.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Error.mjs b/src/engine262/src/intrinsics/Error.mjs new file mode 100644 index 0000000..202a7e8 --- /dev/null +++ b/src/engine262/src/intrinsics/Error.mjs @@ -0,0 +1,44 @@ +import { + DefinePropertyOrThrow, + OrdinaryCreateFromConstructor, + ToString, +} from '../abstract-ops/all.mjs'; +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { captureStack } from '../helpers.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function ErrorConstructor([message = Value.undefined], { NewTarget }) { + let newTarget; + if (Type(NewTarget) === 'Undefined') { + newTarget = surroundingAgent.activeFunctionObject; + } else { + newTarget = NewTarget; + } + const O = Q(OrdinaryCreateFromConstructor(newTarget, '%Error.prototype%', ['ErrorData'])); + if (Type(message) !== 'Undefined') { + const msg = Q(ToString(message)); + const msgDesc = Descriptor({ + Value: msg, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }); + X(DefinePropertyOrThrow(O, new Value('message'), msgDesc)); + } + + X(captureStack(O)); // non-spec + + return O; +} + +export function BootstrapError(realmRec) { + const error = BootstrapConstructor(realmRec, ErrorConstructor, 'Error', 1, realmRec.Intrinsics['%Error.prototype%'], []); + + realmRec.Intrinsics['%Error%'] = error; +} diff --git a/src/engine262/src/intrinsics/ErrorPrototype.mjs b/src/engine262/src/intrinsics/ErrorPrototype.mjs new file mode 100644 index 0000000..13de930 --- /dev/null +++ b/src/engine262/src/intrinsics/ErrorPrototype.mjs @@ -0,0 +1,49 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + Get, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function ErrorProto_toString(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', O); + } + let name = Q(Get(O, new Value('name'))); + if (Type(name) === 'Undefined') { + name = new Value('Error'); + } else { + name = Q(ToString(name)); + } + let msg = Q(Get(O, new Value('message'))); + if (Type(msg) === 'Undefined') { + msg = new Value(''); + } else { + msg = Q(ToString(msg)); + } + if (name.stringValue() === '') { + return msg; + } + if (msg.stringValue() === '') { + return name; + } + return new Value(`${name.stringValue()}: ${msg.stringValue()}`); +} + +export function BootstrapErrorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['toString', ErrorProto_toString, 0], + ['message', new Value('')], + ['name', new Value('Error')], + ], realmRec.Intrinsics['%Object.prototype%']); + + realmRec.Intrinsics['%Error.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/FinalizationRegistry.mjs b/src/engine262/src/intrinsics/FinalizationRegistry.mjs new file mode 100644 index 0000000..73f51ee --- /dev/null +++ b/src/engine262/src/intrinsics/FinalizationRegistry.mjs @@ -0,0 +1,42 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { IsCallable, OrdinaryCreateFromConstructor } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-weakrefs/#sec-finalization-registry-cleanup-callback +function FinalizationRegistryConstructor([cleanupCallback = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', 'FinalizationRegistry'); + } + // 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception. + if (IsCallable(cleanupCallback) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', cleanupCallback); + } + // 3. Let finalizationGroup be ? OrdinaryCreateFromConstructor(NewTarget, "%FinalizationRegistryPrototype%", « [[Realm]], [[CleanupCallback]], [[Cells]] »). + const finalizationGroup = Q(OrdinaryCreateFromConstructor(NewTarget, '%FinalizationRegistry.prototype%', [ + 'Realm', + 'CleanupCallback', + 'Cells', + ])); + // 4. Let fn be the active function object. + const fn = surroundingAgent.activeFunctionObject; + // 5. Set finalizationGroup.[[Realm]] to fn.[[Realm]]. + finalizationGroup.Realm = fn.Realm; + // 6. Set finalizationGroup.[[CleanupCallback]] to cleanupCallback. + finalizationGroup.CleanupCallback = cleanupCallback; + // 7. Set finalizationGroup.[[Cells]] to be an empty List. + finalizationGroup.Cells = []; + // 8. Return finalizationGroup. + return finalizationGroup; +} + +export function BootstrapFinalizationRegistry(realmRec) { + const cons = BootstrapConstructor( + realmRec, FinalizationRegistryConstructor, 'FinalizationRegistry', 1, + realmRec.Intrinsics['%FinalizationRegistry.prototype%'], [], + ); + + realmRec.Intrinsics['%FinalizationRegistry%'] = cons; +} diff --git a/src/engine262/src/intrinsics/FinalizationRegistryPrototype.mjs b/src/engine262/src/intrinsics/FinalizationRegistryPrototype.mjs new file mode 100644 index 0000000..5b1a2a0 --- /dev/null +++ b/src/engine262/src/intrinsics/FinalizationRegistryPrototype.mjs @@ -0,0 +1,99 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value, Type } from '../value.mjs'; +import { + CleanupFinalizationRegistry, + RequireInternalSlot, + IsCallable, + SameValue, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.register +function FinalizationRegistryProto_register([target = Value.undefined, heldValue = Value.undefined, unregisterToken = Value.undefined], { thisValue }) { + // 1. Let finalizationRegistry be the this value. + const finalizationRegistry = thisValue; + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). + Q(RequireInternalSlot(finalizationRegistry, 'Cells')); + // 3. If Type(target) is not Object, throw a TypeError exception. + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + // 4. If SameValue(target, heldValue), throw a TypeError exception. + if (SameValue(target, heldValue) === Value.true) { + return surroundingAgent.Throw('TypeError', 'TargetMatchesHeldValue', heldValue); + } + // 5. If Type(unregisterToken) is not Object, + if (Type(unregisterToken) !== 'Object') { + // a. If unregisterToken is not undefined, throw a TypeError exception. + if (unregisterToken !== Value.undefined) { + return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken); + } + // b. Set unregisterToken to empty. + unregisterToken = undefined; + } + // 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }. + const cell = { + WeakRefTarget: target, + HeldValue: heldValue, + UnregisterToken: unregisterToken, + }; + // 7. Append cell to finalizationRegistry.[[Cells]]. + finalizationRegistry.Cells.push(cell); + // 8. Return undefined. + return Value.undefined; +} + +// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.unregister +function FinalizationRegistryProto_unregister([unregisterToken = Value.undefined], { thisValue }) { + // 1. Let finalizationRegistry be the this value. + const finalizationRegistry = thisValue; + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). + Q(RequireInternalSlot(finalizationRegistry, 'Cells')); + // 3. If Type(unregisterToken) is not Object, throw a TypeError exception. + if (Type(unregisterToken) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken); + } + // 4. Let removed be false. + let removed = Value.false; + // 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell that is an element of finalizationRegistry.[[Cells]], do + finalizationRegistry.Cells = finalizationRegistry.Cells.filter((cell) => { + let r = true; + // a. If SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then + if (cell.UnregisterToken !== undefined && SameValue(cell.UnregisterToken, unregisterToken) === Value.true) { + // i. Remove cell from finalizationRegistry.Cells. + r = false; + // ii. Set removed to true. + removed = Value.true; + } + return r; + }); + // 6. Return removed. + return removed; +} + +// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.cleanupSome +function FinalizationRegistryProto_cleanupSome([callback = Value.undefined], { thisValue }) { + // 1. Let finalizationRegistry be the this value. + const finalizationRegistry = thisValue; + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). + Q(RequireInternalSlot(finalizationRegistry, 'Cells')); + // 3. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception. + if (callback !== Value.undefined && IsCallable(callback) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callback); + } + // 4. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). + Q(CleanupFinalizationRegistry(finalizationRegistry, callback)); + // 5. Return undefined. + return Value.undefined; +} + +export function BootstrapFinalizationRegistryPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['register', FinalizationRegistryProto_register, 2], + ['unregister', FinalizationRegistryProto_unregister, 1], + ['cleanupSome', FinalizationRegistryProto_cleanupSome, 0], + ], realmRec.Intrinsics['%Object.prototype%'], 'FinalizationRegistry'); + + realmRec.Intrinsics['%FinalizationRegistry.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/ForInIteratorPrototype.mjs b/src/engine262/src/intrinsics/ForInIteratorPrototype.mjs new file mode 100644 index 0000000..8ace2ec --- /dev/null +++ b/src/engine262/src/intrinsics/ForInIteratorPrototype.mjs @@ -0,0 +1,104 @@ +import { Value, Type } from '../value.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + SameValue, + OrdinaryObjectCreate, + CreateIterResultObject, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-createforiniterator +export function CreateForInIterator(object) { + // 1. Assert: Type(object) is Object. + Assert(Type(object) === 'Object'); + // 2. Let iterator be ObjectCreate(%ForInIteratorPrototype%, « [[Object]], [[ObjectWasVisited]], [[VisitedKeys]], [[RemainingKeys]] »). + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ForInIteratorPrototype%'), [ + 'Object', + 'ObjectWasVisited', + 'VisitedKeys', + 'RemainingKeys', + ]); + // 3. Set iterator.[[Object]] to object. + iterator.Object = object; + // 4. Set iterator.[[ObjectWasVisited]] to false. + iterator.ObjectWasVisited = Value.false; + // 5. Set iterator.[[VisitedKeys]] to a new empty List. + iterator.VisitedKeys = []; + // 6. Set iterator.[[RemainingKeys]] to a new empty List. + iterator.RemainingKeys = []; + // 7. Return iterator. + return iterator; +} + +// #sec-%foriniteratorprototype%.next +function ForInIteratorPrototype_next(args, { thisValue }) { + // 1. Let O be this value. + const O = thisValue; + // 2. Assert: Type(O) is Object. + Assert(Type(O) === 'Object'); + // 3. Assert: O has all the internal slot sof a For-In Iterator Instance. + Assert('Object' in O && 'ObjectWasVisited' in O && 'VisitedKeys' in O && 'RemainingKeys in O'); + // 4. Let object be O.[[Object]]. + let object = O.Object; + // 5. Let visited be O.[[VisitedKeys]]. + const visited = O.VisitedKeys; + // 6. Let remaining be O.[[RemainingKeys]]. + const remaining = O.RemainingKeys; + // 7. Repeat, + while (true) { + // a. If O.[[ObjectWasVisited]] is false, then + if (O.ObjectWasVisited === Value.false) { + // i. Let keys be ? object.[[OwnPropertyKeys]](). + const keys = Q(object.OwnPropertyKeys()); + // ii. for each key of keys in List order, do + for (const key of keys) { + // 1. If Type(key) is String, then + if (Type(key) === 'String') { + // a. Append key to remaining. + remaining.push(key); + } + } + // iii. Set O.ObjectWasVisited to true. + O.ObjectWasVisited = Value.true; + } + // b. Repeat, while remaining is not empty, + while (remaining.length > 0) { + // i. Remove the first element from remaining and let r be the value of the element. + const r = remaining.shift(); + // ii. If there does not exist an element v of visisted such that SameValue(r, v) is true, then + if (!visited.find((v) => SameValue(r, v) === Value.true)) { + // 1. Let desc be ? object.[[GetOwnProperty]](r). + const desc = Q(object.GetOwnProperty(r)); + // 2. If desc is not undefined, then, + if (desc !== Value.undefined) { + // a. Append r to visited. + visited.push(r); + // b. If desc.[[Enumerable]] is true, return CreateIterResultObject(r, false). + if (desc.Enumerable === Value.true) { + return CreateIterResultObject(r, Value.false); + } + } + } + } + // c. Set object to ? object.[[GetPrototypeOf]](). + object = Q(object.GetPrototypeOf()); + // d. Set O.Object to object. + O.Object = object; + // e. Set O.ObjectWasVisited to false. + O.ObjectWasVisited = Value.false; + // f. If object is null, return CreateIterResultObject(undefined, true). + if (object === Value.null) { + return CreateIterResultObject(Value.undefined, Value.true); + } + } +} + +export function BootstrapForInIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', ForInIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%']); + + realmRec.Intrinsics['%ForInIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Function.mjs b/src/engine262/src/intrinsics/Function.mjs new file mode 100644 index 0000000..440a251 --- /dev/null +++ b/src/engine262/src/intrinsics/Function.mjs @@ -0,0 +1,14 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Q } from '../completion.mjs'; +import { CreateDynamicFunction } from '../runtime-semantics/all.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function FunctionConstructor(args, { NewTarget }) { + const C = surroundingAgent.activeFunctionObject; + return Q(CreateDynamicFunction(C, NewTarget, 'normal', args)); +} + +export function BootstrapFunction(realmRec) { + const cons = BootstrapConstructor(realmRec, FunctionConstructor, 'Function', 1, realmRec.Intrinsics['%Function.prototype%'], []); + realmRec.Intrinsics['%Function%'] = cons; +} diff --git a/src/engine262/src/intrinsics/FunctionPrototype.mjs b/src/engine262/src/intrinsics/FunctionPrototype.mjs new file mode 100644 index 0000000..b68bff7 --- /dev/null +++ b/src/engine262/src/intrinsics/FunctionPrototype.mjs @@ -0,0 +1,181 @@ +import { + surroundingAgent, + HostHasSourceTextAvailable, +} from '../engine.mjs'; +import { + Assert, + Call, + Construct, + CreateListFromArrayLike, + Get, + HasOwnProperty, + IsCallable, + IsConstructor, + OrdinaryHasInstance, + PrepareForTailCall, + SameValue, + SetFunctionLength, + SetFunctionName, + ToInteger, + CreateBuiltinFunction, + MakeBasicObject, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { assignProps } from './Bootstrap.mjs'; + +function FunctionProto_apply([thisArg = Value.undefined, argArray = Value.undefined], { thisValue: func }) { + if (IsCallable(func) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); + } + if (Type(argArray) === 'Undefined' || Type(argArray) === 'Null') { + PrepareForTailCall(); + return Q(Call(func, thisArg)); + } + const argList = Q(CreateListFromArrayLike(argArray)); + PrepareForTailCall(); + return Q(Call(func, thisArg, argList)); +} + +function BoundFunctionExoticObjectCall(thisArgument, argumentsList) { + const F = this; + + const target = F.BoundTargetFunction; + const boundThis = F.BoundThis; + const boundArgs = F.BoundArguments; + const args = [...boundArgs, ...argumentsList]; + return Q(Call(target, boundThis, args)); +} + +function BoundFunctionExoticObjectConstruct(argumentsList, newTarget) { + const F = this; + + const target = F.BoundTargetFunction; + Assert(IsConstructor(target) === Value.true); + const boundArgs = F.BoundArguments; + const args = [...boundArgs, ...argumentsList]; + if (SameValue(F, newTarget) === Value.true) { + newTarget = target; + } + return Q(Construct(target, args, newTarget)); +} + +// 9.4.1.3 #sec-boundfunctioncreate +function BoundFunctionCreate(targetFunction, boundThis, boundArgs) { + // 1. Assert: Type(targetFunction) is Object. + Assert(Type(targetFunction) === 'Object'); + // 2. Let proto be ? targetFunction.[[GetPrototypeOf]](). + const proto = Q(targetFunction.GetPrototypeOf()); + // 3. Let internalSlotsList be the internal slots listed in Table 30, plus [[Prototype]] and [[Extensible]]. + const internalSlotsList = [ + 'BoundTargetFunction', + 'BoundThis', + 'BoundArguments', + 'Prototype', + 'Extensible', + ]; + // 4. Let obj be ! MakeBasicObject(internalSlotsList). + const obj = X(MakeBasicObject(internalSlotsList)); + // 5. Set obj.[[Prototype]] to proto. + obj.Prototype = proto; + // 6. Set obj.[[Call]] as described in 9.4.1.1. + obj.Call = BoundFunctionExoticObjectCall; + // 7. If IsConstructor(targetFunction) is true, then + if (IsConstructor(targetFunction) === Value.true) { + // a. Set obj.[[Construct]] as described in 9.4.1.2. + obj.Construct = BoundFunctionExoticObjectConstruct; + } + // 8. Set obj.[[BoundTargetFunction]] to targetFunction. + obj.BoundTargetFunction = targetFunction; + // 9. Set obj.[[BoundThis]] to boundThis. + obj.BoundThis = boundThis; + // 10. Set obj.[[BoundArguments]] to boundArguments. + obj.BoundArguments = boundArgs; + // 11. Return obj. + return obj; +} + +function FunctionProto_bind([thisArg = Value.undefined, ...args], { thisValue }) { + const Target = thisValue; + if (IsCallable(Target) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', Target); + } + // Let args be a new (possibly empty) List consisting of all + // of the argument values provided after thisArg in order. + const F = Q(BoundFunctionCreate(Target, thisArg, args)); + const targetHasLength = Q(HasOwnProperty(Target, new Value('length'))); + let L; + if (targetHasLength === Value.true) { + let targetLen = Q(Get(Target, new Value('length'))); + if (Type(targetLen) !== 'Number') { + L = 0; + } else { + targetLen = Q(ToInteger(targetLen)).numberValue(); + L = Math.max(0, targetLen - args.length); + } + } else { + L = 0; + } + X(SetFunctionLength(F, new Value(L))); + let targetName = Q(Get(Target, new Value('name'))); + if (Type(targetName) !== 'String') { + targetName = new Value(''); + } + SetFunctionName(F, targetName, new Value('bound')); + return F; +} + +function FunctionProto_call([thisArg = Value.undefined, ...args], { thisValue: func }) { + if (IsCallable(func) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); + } + const argList = []; + for (const arg of args) { + argList.push(arg); + } + PrepareForTailCall(); + return Q(Call(func, thisArg, argList)); +} + +function FunctionProto_toString(args, { thisValue: func }) { + if ('BoundTargetFunction' in func || 'nativeFunction' in func) { + const name = func.properties.get(new Value('name')); + if (name !== undefined) { + return new Value(`function ${name.Value.stringValue()}() { [native code] }`); + } + return new Value('function() { [native code] }'); + } + if (Type(func) === 'Object' && 'SourceText' in func && Type(func.SourceText) === 'String' && X(HostHasSourceTextAvailable(func)) === Value.true) { + return func.SourceText; + } + if (Type(func) === 'Object' && IsCallable(func) === Value.true) { + return new Value('function() { [native code] }'); + } + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); +} + +function FunctionProto_hasInstance([V = Value.undefined], { thisValue }) { + const F = thisValue; + return Q(OrdinaryHasInstance(F, V)); +} + +export function BootstrapFunctionPrototype(realmRec) { + const proto = CreateBuiltinFunction(() => Value.undefined, [], realmRec, realmRec.Intrinsics['%Object.prototype%']); + realmRec.Intrinsics['%Function.prototype%'] = proto; + + SetFunctionLength(proto, new Value(0)); + SetFunctionName(proto, new Value('')); + + const readonly = { Writable: Value.false, Configurable: Value.false }; + assignProps(realmRec, proto, [ + ['apply', FunctionProto_apply, 2], + ['bind', FunctionProto_bind, 1], + ['call', FunctionProto_call, 1], + ['toString', FunctionProto_toString, 0], + [wellKnownSymbols.hasInstance, FunctionProto_hasInstance, 1, readonly], + ]); +} diff --git a/src/engine262/src/intrinsics/Generator.mjs b/src/engine262/src/intrinsics/Generator.mjs new file mode 100644 index 0000000..b0f6879 --- /dev/null +++ b/src/engine262/src/intrinsics/Generator.mjs @@ -0,0 +1,21 @@ +import { Descriptor, Value } from '../value.mjs'; +import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs'; +import { X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +export function BootstrapGenerator(realmRec) { + const generatorPrototype = realmRec.Intrinsics['%Generator.prototype%']; + + const generator = BootstrapPrototype(realmRec, [ + ['prototype', generatorPrototype, undefined, { Writable: Value.false }], + ], realmRec.Intrinsics['%Function.prototype%'], 'GeneratorFunction'); + + X(DefinePropertyOrThrow(generatorPrototype, new Value('constructor'), Descriptor({ + Value: generator, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + realmRec.Intrinsics['%Generator%'] = generator; +} diff --git a/src/engine262/src/intrinsics/GeneratorFunction.mjs b/src/engine262/src/intrinsics/GeneratorFunction.mjs new file mode 100644 index 0000000..11717a9 --- /dev/null +++ b/src/engine262/src/intrinsics/GeneratorFunction.mjs @@ -0,0 +1,26 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { CreateDynamicFunction } from '../runtime-semantics/all.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function GeneratorFunctionConstructor(args, { NewTarget }) { + const C = surroundingAgent.activeFunctionObject; + return Q(CreateDynamicFunction(C, NewTarget, 'generator', args)); +} + +export function BootstrapGeneratorFunction(realmRec) { + const generator = realmRec.Intrinsics['%Generator%']; + + const cons = BootstrapConstructor(realmRec, GeneratorFunctionConstructor, 'GeneratorFunction', 1, generator, []); + X(DefinePropertyOrThrow(cons, new Value('prototype'), Descriptor({ + Writable: Value.false, + Configurable: Value.false, + }))); + X(DefinePropertyOrThrow(generator, new Value('constructor'), Descriptor({ + Writable: Value.false, + }))); + + realmRec.Intrinsics['%GeneratorFunction%'] = cons; +} diff --git a/src/engine262/src/intrinsics/GeneratorPrototype.mjs b/src/engine262/src/intrinsics/GeneratorPrototype.mjs new file mode 100644 index 0000000..748c97f --- /dev/null +++ b/src/engine262/src/intrinsics/GeneratorPrototype.mjs @@ -0,0 +1,41 @@ +import { + GeneratorResume, + GeneratorResumeAbrupt, +} from '../abstract-ops/all.mjs'; +import { + Q, + ReturnCompletion, + ThrowCompletion, +} from '../completion.mjs'; +import { Value } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// 25.4.1.2 #sec-generator.prototype.next +function GeneratorProto_next([value = Value.undefined], { thisValue }) { + const g = thisValue; + return Q(GeneratorResume(g, value)); +} + +// 25.4.1.3 #sec-generator.prototype.return +function GeneratorProto_return([value = Value.undefined], { thisValue }) { + const g = thisValue; + const C = new ReturnCompletion(value); + return Q(GeneratorResumeAbrupt(g, C)); +} + +// 25.4.1.4 #sec-generator.prototype.throw +function GeneratorProto_throw([exception = Value.undefined], { thisValue }) { + const g = thisValue; + const C = new ThrowCompletion(exception); + return Q(GeneratorResumeAbrupt(g, C)); +} + +export function BootstrapGeneratorPrototype(realmRec) { + const generatorPrototype = BootstrapPrototype(realmRec, [ + ['next', GeneratorProto_next, 1], + ['return', GeneratorProto_return, 1], + ['throw', GeneratorProto_throw, 1], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'Generator'); + + realmRec.Intrinsics['%Generator.prototype%'] = generatorPrototype; +} diff --git a/src/engine262/src/intrinsics/IteratorPrototype.mjs b/src/engine262/src/intrinsics/IteratorPrototype.mjs new file mode 100644 index 0000000..2c7fa3e --- /dev/null +++ b/src/engine262/src/intrinsics/IteratorPrototype.mjs @@ -0,0 +1,15 @@ +import { wellKnownSymbols } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// 25.1.2.1 sec-%iteratorprototype%-@@iterator +function IteratorPrototype_iterator(args, { thisValue }) { + return thisValue; +} + +export function BootstrapIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + [wellKnownSymbols.iterator, IteratorPrototype_iterator, 0], + ], realmRec.Intrinsics['%Object.prototype%']); + + realmRec.Intrinsics['%IteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/JSON.mjs b/src/engine262/src/intrinsics/JSON.mjs new file mode 100644 index 0000000..c911e74 --- /dev/null +++ b/src/engine262/src/intrinsics/JSON.mjs @@ -0,0 +1,540 @@ +import { evaluateScript, surroundingAgent } from '../engine.mjs'; +import { + BooleanValue, + NullValue, + NumberValue, + ObjectValue, + StringValue, + Type, + Value, +} from '../value.mjs'; +import { + Assert, + Call, + CreateDataProperty, + CreateDataPropertyOrThrow, + EnumerableOwnPropertyNames, + Get, + GetV, + IsArray, + IsCallable, + OrdinaryObjectCreate, + LengthOfArrayLike, + ToInteger, + ToNumber, + ToString, + UTF16Encoding, +} from '../abstract-ops/all.mjs'; +import { + NormalCompletion, + Q, X, +} from '../completion.mjs'; +import { ValueSet } from '../helpers.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +const WHITESPACE = [' ', '\t', '\r', '\n']; +const NUMERIC = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; +const VALID_HEX = [...NUMERIC, 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f']; +const ESCAPABLE = ['"', '\\', '/', 'b', 'f', 'n', 'r', 't']; + +class JSONValidator { + constructor(input) { + this.input = input; + this.pos = 0; + this.char = input.charAt(0); + } + + validate() { + X(this.eatWhitespace()); + Q(this.parseValue()); + if (this.pos < this.input.length) { + return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken'); + } + return new NormalCompletion(undefined); + } + + advance() { + this.pos += 1; + if (this.pos === this.input.length) { + this.char = null; + } else if (this.pos > this.input.length) { + return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken'); + } else { + this.char = this.input.charAt(this.pos); + } + return this.char; + } + + eatWhitespace() { + while (this.eat(WHITESPACE)) { + // nothing + } + } + + eat(c) { + if (Array.isArray(c) && c.includes(this.char)) { + X(this.advance()); + return true; + } else if (this.char === c) { + X(this.advance()); + return true; + } + return false; + } + + expect(c) { + const { char } = this; + if (!this.eat(c)) { + return surroundingAgent.Throw('SyntaxError', 'JSONExpected', c, this.char); + } + return char; + } + + parseValue() { + switch (this.char) { + case '"': + return Q(this.parseString()); + case '{': + return Q(this.parseObject()); + case '[': + return Q(this.parseArray()); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return Q(this.parseNumber()); + case 'f': + X(this.expect('f')); + Q(this.expect('a')); + Q(this.expect('l')); + Q(this.expect('s')); + Q(this.expect('e')); + return X(this.eatWhitespace()); + case 't': + X(this.expect('t')); + Q(this.expect('r')); + Q(this.expect('u')); + Q(this.expect('e')); + return X(this.eatWhitespace()); + case 'n': + X(this.expect('n')); + Q(this.expect('u')); + Q(this.expect('l')); + Q(this.expect('l')); + return X(this.eatWhitespace()); + default: + return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char); + } + } + + parseString() { + Q(this.expect('"')); + while (!this.eat('"')) { + if (this.eat('\\')) { + if (!this.eat(ESCAPABLE)) { + Q(this.expect('u')); + Q(this.expect(VALID_HEX)); + Q(this.expect(VALID_HEX)); + Q(this.expect(VALID_HEX)); + Q(this.expect(VALID_HEX)); + } + } else { + if (this.char < ' ') { + return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char); + } + Q(this.advance()); + } + } + return X(this.eatWhitespace()); + } + + parseNumber() { + this.eat('-'); + if (!this.eat('0')) { + Q(this.expect(NUMERIC)); + while (this.eat(NUMERIC)) { + // nothing + } + } + if (this.eat('.')) { + Q(this.expect(NUMERIC)); + while (this.eat(NUMERIC)) { + // nothing + } + } + if (this.eat(['e', 'E'])) { + this.eat(['-', '+']); + Q(this.expect(NUMERIC)); + while (this.eat(NUMERIC)) { + // nothing + } + } + X(this.eatWhitespace()); + } + + parseObject() { + Q(this.expect('{')); + X(this.eatWhitespace()); + let first = true; + while (!this.eat('}')) { + if (first) { + first = false; + } else { + Q(this.expect(',')); + X(this.eatWhitespace()); + } + Q(this.parseString()); + X(this.eatWhitespace()); + Q(this.expect(':')); + X(this.eatWhitespace()); + Q(this.parseValue()); + X(this.eatWhitespace()); + } + X(this.eatWhitespace()); + } + + parseArray() { + Q(this.expect('[')); + X(this.eatWhitespace()); + let first = true; + while (!this.eat(']')) { + if (first) { + first = false; + } else { + Q(this.expect(',')); + X(this.eatWhitespace()); + } + Q(this.parseValue()); + X(this.eatWhitespace()); + } + X(this.eatWhitespace()); + } + + static validate(input) { + const v = new JSONValidator(input); + return v.validate(); + } +} + +function InternalizeJSONProperty(holder, name, reviver) { + const val = Q(Get(holder, name)); + if (Type(val) === 'Object') { + const isArray = Q(IsArray(val)); + if (isArray === Value.true) { + let I = 0; + const len = Q(LengthOfArrayLike(val)).numberValue(); + while (I < len) { + const Istr = X(ToString(new Value(I))); + const newElement = Q(InternalizeJSONProperty(val, Istr, reviver)); + if (Type(newElement) === 'Undefined') { + Q(val.Delete(Istr)); + } else { + Q(CreateDataProperty(val, Istr, newElement)); + } + I += 1; + } + } else { + const keys = Q(EnumerableOwnPropertyNames(val, 'key')); + for (const P of keys) { + const newElement = Q(InternalizeJSONProperty(val, P, reviver)); + if (Type(newElement) === 'Undefined') { + Q(val.Delete(P)); + } else { + Q(CreateDataProperty(val, P, newElement)); + } + } + } + } + return Q(Call(reviver, holder, [name, val])); +} + +function JSON_parse([text = Value.undefined, reviver = Value.undefined]) { + // 1. Let jsonString be ? ToString(text). + const jsonString = Q(ToString(text)); + // 2. Parse ! UTF16DecodeString(jsonString) as a JSON text as specified in ECMA-404. + // Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification. + Q(JSONValidator.validate(jsonString.stringValue())); + // 3. Let scriptString be the string-concatenation of "(", jsonString, and ");". + const scriptString = `(${jsonString.stringValue()});`; + // 4. Let completion be the result of parsing and evaluating + // ! UTF16DecodeString(scriptString) as if it was the source text of an ECMAScript Script. The + // extended PropertyDefinitionEvaluation semantics defined in B.3.1 must not be used during the evaluation. + const completion = evaluateScript(scriptString, surroundingAgent.currentRealmRecord); + // 5. Let unfiltered be completion.[[Value]]. + const unfiltered = completion.Value; + // 6. Assert: unfiltered is either a String, Number, Boolean, Null, or an Object that is defined by either an ArrayLiteral or an ObjectLiteral. + Assert(unfiltered instanceof StringValue + || unfiltered instanceof NumberValue + || unfiltered instanceof BooleanValue + || unfiltered instanceof NullValue + || unfiltered instanceof ObjectValue); + // 7. If IsCallable(reviver) is true, then + if (IsCallable(reviver) === Value.true) { + // a. Let root be OrdinaryObjectCreate(%Object.prototype%). + const root = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + // b. Let rootName be the empty String. + const rootName = new Value(''); + // c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). + X(CreateDataPropertyOrThrow(root, rootName, unfiltered)); + // d. Return ? InternalizeJSONProperty(root, rootName, reviver). + return Q(InternalizeJSONProperty(root, rootName, reviver)); + } else { + // a. Return unfiltered. + return unfiltered; + } +} + +const codeUnitTable = new Map([ + [0x0008, '\\b'], + [0x0009, '\\t'], + [0x000A, '\\n'], + [0x000C, '\\f'], + [0x000D, '\\r'], + [0x0022, '\\"'], + [0x005C, '\\\\'], +]); + +// #sec-serializejsonproperty +function SerializeJSONProperty(state, key, holder) { + let value = Q(Get(holder, key)); // eslint-disable-line no-shadow + if (Type(value) === 'Object' || Type(value) === 'BigInt') { + const toJSON = Q(GetV(value, new Value('toJSON'))); + if (IsCallable(toJSON) === Value.true) { + value = Q(Call(toJSON, value, [key])); + } + } + if (state.ReplacerFunction !== Value.undefined) { + value = Q(Call(state.ReplacerFunction, holder, [key, value])); + } + if (Type(value) === 'Object') { + if ('NumberData' in value) { + value = Q(ToNumber(value)); + } else if ('StringData' in value) { + value = Q(ToString(value)); + } else if ('BooleanData' in value) { + value = value.BooleanData; + } else if ('BigIntData' in value) { + value = value.BigIntData; + } + } + if (value === Value.null) { + return new Value('null'); + } + if (value === Value.true) { + return new Value('true'); + } + if (value === Value.false) { + return new Value('false'); + } + if (Type(value) === 'String') { + return QuoteJSONString(value); + } + if (Type(value) === 'Number') { + if (value.isFinite()) { + return X(ToString(value)); + } + return new Value('null'); + } + if (Type(value) === 'BigInt') { + return surroundingAgent.Throw('TypeError', 'CannotJSONSerializeBigInt'); + } + if (Type(value) === 'Object' && IsCallable(value) === Value.false) { + const isArray = Q(IsArray(value)); + if (isArray === Value.true) { + return Q(SerializeJSONArray(state, value)); + } + return Q(SerializeJSONObject(state, value)); + } + return Value.undefined; +} + +function UnicodeEscape(C) { + const n = C.charCodeAt(0); + Assert(n < 0xFFFF); + return `\u005Cu${n.toString(16).padStart(4, '0')}`; +} + +function QuoteJSONString(value) { // eslint-disable-line no-shadow + let product = '\u0022'; + const cpList = [...value.stringValue()].map((c) => c.codePointAt(0)); + for (const C of cpList) { + if (codeUnitTable.has(C)) { + product = `${product}${codeUnitTable.get(C)}`; + } else if (C < 0x0020 || (C >= 0xD800 && C <= 0xDBFF) || (C >= 0xDC00 && C <= 0xDFFF)) { + const unit = String.fromCodePoint(C); + product = `${product}${UnicodeEscape(unit)}`; + } else { + product = `${product}${String.fromCodePoint(...UTF16Encoding(C))}`; + } + } + product = `${product}\u0022`; + return new Value(product); +} + +// #sec-serializejsonobject +function SerializeJSONObject(state, value) { + if (state.Stack.includes(value)) { + return surroundingAgent.Throw('TypeError', 'JSONCircular'); + } + state.Stack.push(value); + const stepback = state.Indent; + state.Indent = `${state.Indent}${state.Gap}`; + let K; + if (state.PropertyList !== Value.undefined) { + K = state.PropertyList; + } else { + K = Q(EnumerableOwnPropertyNames(value, 'key')); + } + const partial = []; + for (const P of K) { + const strP = Q(SerializeJSONProperty(state, P, value)); + if (strP !== Value.undefined) { + let member = QuoteJSONString(P).stringValue(); + member = `${member}:`; + if (state.Gap !== '') { + member = `${member} `; + } + member = `${member}${strP.stringValue()}`; + partial.push(member); + } + } + let final; + if (partial.length === 0) { + final = new Value('{}'); + } else { + if (state.Gap === '') { + const properties = partial.join(','); + final = new Value(`{${properties}}`); + } else { + const separator = `,\u000A${state.Indent}`; + const properties = partial.join(separator); + final = new Value(`{\u000A${state.Indent}${properties}\u000A${stepback}}`); + } + } + state.Stack.pop(); + state.Indent = stepback; + return final; +} + +// #sec-serializejsonarray +function SerializeJSONArray(state, value) { + if (state.Stack.includes(value)) { + return surroundingAgent.Throw('TypeError', 'JSONCircular'); + } + state.Stack.push(value); + const stepback = state.Indent; + state.Indent = `${state.Indent}${state.Gap}`; + const partial = []; + const len = Q(LengthOfArrayLike(value)).numberValue(); + let index = 0; + while (index < len) { + const indexStr = X(ToString(new Value(index))); + const strP = Q(SerializeJSONProperty(state, indexStr, value)); + if (strP === Value.undefined) { + partial.push('null'); + } else { + partial.push(strP.stringValue()); + } + index += 1; + } + let final; + if (partial.length === 0) { + final = new Value('[]'); + } else { + if (state.Gap === '') { + const properties = partial.join(','); + final = new Value(`[${properties}]`); + } else { + const separator = `,\u000A${state.Indent}`; + const properties = partial.join(separator); + final = new Value(`[\u000A${state.Indent}${properties}\u000A${stepback}]`); + } + } + state.Stack.pop(); + state.Indent = stepback; + return final; +} + +function JSON_stringify([value = Value.undefined, replacer = Value.undefined, space = Value.undefined]) { + const stack = []; + const indent = ''; + let PropertyList = Value.undefined; + let ReplacerFunction = Value.undefined; + if (Type(replacer) === 'Object') { + if (IsCallable(replacer) === Value.true) { + ReplacerFunction = replacer; + } else { + const isArray = Q(IsArray(replacer)); + if (isArray === Value.true) { + PropertyList = new ValueSet(); + const len = Q(LengthOfArrayLike(replacer)).numberValue(); + let k = 0; + while (k < len) { + const vStr = X(ToString(new Value(k))); + const v = Q(Get(replacer, vStr)); + let item = Value.undefined; + if (Type(v) === 'String') { + item = v; + } else if (Type(v) === 'Number') { + item = X(ToString(v)); + } else if (Type(v) === 'Object') { + if ('StringData' in v || 'NumberData' in v) { + item = Q(ToString(v)); + } + } + if (item !== Value.undefined && !PropertyList.has(item)) { + PropertyList.add(item); + } + k += 1; + } + } + } + } + if (Type(space) === 'Object') { + if ('NumberData' in space) { + space = Q(ToNumber(space)); + } else if ('StringData' in space) { + space = Q(ToString(space)); + } + } + let gap; + if (Type(space) === 'Number') { + space = Math.min(10, X(ToInteger(space)).numberValue()); + if (space < 1) { + gap = ''; + } else { + gap = ' '.repeat(space); + } + } else if (Type(space) === 'String') { + if (space.stringValue().length <= 10) { + gap = space.stringValue(); + } else { + gap = space.stringValue().slice(0, 10); + } + } else { + gap = ''; + } + const wrapper = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + X(CreateDataPropertyOrThrow(wrapper, new Value(''), value)); + const state = { + ReplacerFunction, Stack: stack, Indent: indent, Gap: gap, PropertyList, + }; + return Q(SerializeJSONProperty(state, new Value(''), wrapper)); +} + +export function BootstrapJSON(realmRec) { + const json = BootstrapPrototype(realmRec, [ + ['parse', JSON_parse, 2], + ['stringify', JSON_stringify, 3], + ], realmRec.Intrinsics['%Object.prototype%'], 'JSON'); + + realmRec.Intrinsics['%JSON%'] = json; + realmRec.Intrinsics['%JSON.parse%'] = X(json.Get(new Value('parse'))); +} diff --git a/src/engine262/src/intrinsics/Map.mjs b/src/engine262/src/intrinsics/Map.mjs new file mode 100644 index 0000000..2e49d21 --- /dev/null +++ b/src/engine262/src/intrinsics/Map.mjs @@ -0,0 +1,78 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + Call, + Get, + GetIterator, + IsCallable, + IteratorClose, + IteratorStep, + IteratorValue, + OrdinaryCreateFromConstructor, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + AbruptCompletion, + Q, +} from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +export function AddEntriesFromIterable(target, iterable, adder) { + if (IsCallable(adder) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', adder); + } + Assert(iterable !== undefined && iterable !== Value.undefined && iterable !== Value.null); + const iteratorRecord = Q(GetIterator(iterable)); + while (true) { + const next = Q(IteratorStep(iteratorRecord)); + if (next === Value.false) { + return target; + } + const nextItem = Q(IteratorValue(next)); + if (Type(nextItem) !== 'Object') { + const error = surroundingAgent.Throw('TypeError', 'NotAnObject', nextItem); + return Q(IteratorClose(iteratorRecord, error)); + } + const k = Get(nextItem, new Value('0')); + if (k instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, k)); + } + const v = Get(nextItem, new Value('1')); + if (v instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, v)); + } + const status = Call(adder, target, [k.Value, v.Value]); + if (status instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, status)); + } + } +} + +function MapConstructor([iterable = Value.undefined], { NewTarget }) { + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + const map = Q(OrdinaryCreateFromConstructor(NewTarget, '%Map.prototype%', ['MapData'])); + map.MapData = []; + if (iterable === Value.undefined || iterable === Value.null) { + return map; + } + const adder = Q(Get(map, new Value('set'))); + return Q(AddEntriesFromIterable(map, iterable, adder)); +} + +function Map_speciesGetter(args, { thisValue }) { + return thisValue; +} + +export function BootstrapMap(realmRec) { + const mapConstructor = BootstrapConstructor(realmRec, MapConstructor, 'Map', 0, realmRec.Intrinsics['%Map.prototype%'], [ + [wellKnownSymbols.species, [Map_speciesGetter]], + ]); + + realmRec.Intrinsics['%Map%'] = mapConstructor; +} diff --git a/src/engine262/src/intrinsics/MapIteratorPrototype.mjs b/src/engine262/src/intrinsics/MapIteratorPrototype.mjs new file mode 100644 index 0000000..1bb18f0 --- /dev/null +++ b/src/engine262/src/intrinsics/MapIteratorPrototype.mjs @@ -0,0 +1,56 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + CreateArrayFromList, + CreateIterResultObject, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +function MapIteratorPrototype_next(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Map Iterator', O); + } + if (!('IteratedMap' in O && 'MapNextIndex' in O && 'MapIterationKind' in O)) { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Map Iterator', O); + } + const m = O.IteratedMap; + let index = O.MapNextIndex; + const itemKind = O.MapIterationKind; + if (m === Value.undefined) { + return CreateIterResultObject(Value.undefined, Value.true); + } + Assert('MapData' in m); + const entries = m.MapData; + const numEntries = entries.length; + while (index < numEntries) { + const e = entries[index]; + index += 1; + O.MapNextIndex = index; + if (e.Key !== undefined) { + let result; + if (itemKind === 'key') { + result = e.Key; + } else if (itemKind === 'value') { + result = e.Value; + } else { + Assert(itemKind === 'key+value'); + result = X(CreateArrayFromList([e.Key, e.Value])); + } + return CreateIterResultObject(result, Value.false); + } + } + O.IteratedMap = Value.undefined; + return CreateIterResultObject(Value.undefined, Value.true); +} + +export function BootstrapMapIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', MapIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'Map Iterator'); + + realmRec.Intrinsics['%MapIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/MapPrototype.mjs b/src/engine262/src/intrinsics/MapPrototype.mjs new file mode 100644 index 0000000..f55259d --- /dev/null +++ b/src/engine262/src/intrinsics/MapPrototype.mjs @@ -0,0 +1,166 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Call, + IsCallable, + OrdinaryObjectCreate, + SameValueZero, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +function CreateMapIterator(map, kind) { + Q(RequireInternalSlot(map, 'MapData')); + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%MapIteratorPrototype%'), [ + 'IteratedMap', + 'MapNextIndex', + 'MapIterationKind', + ]); + iterator.IteratedMap = map; + iterator.MapNextIndex = 0; + iterator.MapIterationKind = kind; + return iterator; +} + +function MapProto_clear(args, { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + for (const p of entries) { + p.Key = undefined; + p.Value = undefined; + } + return Value.undefined; +} + +function MapProto_delete([key = Value.undefined], { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + for (let i = 0; i < entries.length; i += 1) { + const p = entries[i]; + if (p.Key !== undefined && SameValueZero(p.Key, key) === Value.true) { + p.Key = undefined; + p.Value = undefined; + + // The value empty is used as a specification device to indicate that an + // entry has been deleted. Actual implementations may take other actions + // such as physically removing the entry from internal data structures. + // entries.splice(i, 1); + + return Value.true; + } + } + return Value.false; +} + +function MapProto_entries(args, { thisValue }) { + const M = thisValue; + return Q(CreateMapIterator(M, 'key+value')); +} + +function MapProto_forEach([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + const entries = M.MapData; + for (const e of entries) { + if (e.Key !== undefined) { + Q(Call(callbackfn, thisArg, [e.Value, e.Key, M])); + } + } + return Value.undefined; +} + +function MapProto_get([key = Value.undefined], { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + for (const p of entries) { + if (p.Key !== undefined && SameValueZero(p.Key, key) === Value.true) { + return p.Value; + } + } + return Value.undefined; +} + +function MapProto_has([key = Value.undefined], { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + for (const p of entries) { + if (p.Key !== undefined && SameValueZero(p.Key, key) === Value.true) { + return Value.true; + } + } + return Value.false; +} + +function MapProto_keys(args, { thisValue }) { + const M = thisValue; + return Q(CreateMapIterator(M, 'key')); +} + +function MapProto_set([key = Value.undefined, value = Value.undefined], { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + for (const p of entries) { + if (p.Key !== undefined && SameValueZero(p.Key, key) === Value.true) { + p.Value = value; + return M; + } + } + if (Type(key) === 'Number' && Object.is(key.numberValue(), -0)) { + key = new Value(0); + } + const p = { Key: key, Value: value }; + entries.push(p); + return M; +} + +function MapProto_sizeGetter(args, { thisValue }) { + const M = thisValue; + Q(RequireInternalSlot(M, 'MapData')); + const entries = M.MapData; + let count = 0; + for (const p of entries) { + if (p.Key !== undefined) { + count += 1; + } + } + return new Value(count); +} + +function MapProto_values(args, { thisValue }) { + const M = thisValue; + return Q(CreateMapIterator(M, 'value')); +} + +export function BootstrapMapPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['clear', MapProto_clear, 0], + ['delete', MapProto_delete, 1], + ['entries', MapProto_entries, 0], + ['forEach', MapProto_forEach, 1], + ['get', MapProto_get, 1], + ['has', MapProto_has, 1], + ['keys', MapProto_keys, 0], + ['set', MapProto_set, 2], + ['size', [MapProto_sizeGetter]], + ['values', MapProto_values, 0], + ], realmRec.Intrinsics['%Object.prototype%'], 'Map'); + + const entriesFunc = X(proto.GetOwnProperty(new Value('entries'))); + X(proto.DefineOwnProperty(wellKnownSymbols.iterator, entriesFunc)); + + realmRec.Intrinsics['%Map.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Math.mjs b/src/engine262/src/intrinsics/Math.mjs new file mode 100644 index 0000000..3b2e58f --- /dev/null +++ b/src/engine262/src/intrinsics/Math.mjs @@ -0,0 +1,135 @@ +import { + Descriptor, + Value, + NumberValue, +} from '../value.mjs'; +import { + CreateBuiltinFunction, + SetFunctionLength, + SetFunctionName, + ToNumber, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// 20.2.2.1 #sec-math.abs +function Math_abs([x = Value.undefined]) { + x = Q(ToNumber(x)); + if (x.isNaN()) { + return x; + } else if (Object.is(x.numberValue(), -0)) { + return new Value(0); + } else if (x.isInfinity()) { + return new Value(Infinity); + } + + if (x.numberValue() < 0) { + return new Value(-x.numberValue()); + } + return x; +} + +// 20.2.2.2 #sec-math.acos +function Math_acos([x = Value.undefined]) { + x = Q(ToNumber(x)); + if (x.isNaN()) { + return x; + } else if (x.numberValue() > 1) { + return new Value(NaN); + } else if (x.numberValue() < -1) { + return new Value(NaN); + } else if (x.numberValue() === 1) { + return new Value(+0); + } + + return new Value(Math.acos(x.numberValue())); +} + +// #sec-math.pow +function Math_pow([base = Value.undefined, exponent = Value.undefined]) { + // 1. Set base to ? ToNumber(base). + base = Q(ToNumber(base)); + // 2. Set exponent to ? ToNumber(exponent). + exponent = Q(ToNumber(exponent)); + // 3. Return ! Number::exponentiate(base, exponent). + return X(NumberValue.exponentiate(base, exponent)); +} + +// 20.2 #sec-math-object +export function BootstrapMath(realmRec) { + // 20.2.1 #sec-value-properties-of-the-math-object + const readonly = { Writable: Value.false, Configurable: Value.false }; + const valueProps = [ + ['E', 2.7182818284590452354], + ['LN10', 2.302585092994046], + ['LN2', 0.6931471805599453], + ['LOG10E', 0.4342944819032518], + ['LOG2E', 1.4426950408889634], + ['PI', 3.1415926535897932], + ['SQRT1_2', 0.7071067811865476], + ['SQRT2', 1.4142135623730951], + ].map(([name, value]) => [name, new Value(value), undefined, readonly]); + // @@toStringTag is handled in the BootstrapPrototype() call. + + const mathObj = BootstrapPrototype(realmRec, [ + ...valueProps, + ['abs', Math_abs, 1], + ['acos', Math_acos, 1], + ['pow', Math_pow, 2], + ], realmRec.Intrinsics['%Object.prototype%'], 'Math'); + + // 20.2.2 #sec-function-properties-of-the-math-object + + [ + ['acosh', 1], + ['asin', 1], + ['asinh', 1], + ['atan', 1], + ['atanh', 1], + ['atan2', 2], + ['cbrt', 1], + ['ceil', 1], + ['clz32', 1], + ['cos', 1], + ['cosh', 1], + ['exp', 1], + ['expm1', 1], + ['floor', 1], + ['fround', 1], + ['hypot', 2], + ['imul', 2], + ['log', 1], + ['log1p', 1], + ['log10', 1], + ['log2', 1], + ['max', 2], + ['min', 2], + ['random', 0], + ['round', 1], + ['sign', 1], + ['sin', 1], + ['sinh', 1], + ['sqrt', 1], + ['tan', 1], + ['tanh', 1], + ['trunc', 1], + ].forEach(([name, length]) => { + // TODO(18): Math + const func = CreateBuiltinFunction(((args) => { + for (let i = 0; i < args.length; i += 1) { + args[i] = Q(ToNumber(args[i])).numberValue(); + } + return new Value(Math[name](...args)); + }), [], realmRec); + X(SetFunctionName(func, new Value(name))); + X(SetFunctionLength(func, new Value(length))); + mathObj.DefineOwnProperty(new Value(name), Descriptor({ + Value: func, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + })); + }); + + realmRec.Intrinsics['%Math%'] = mathObj; +} diff --git a/src/engine262/src/intrinsics/NativeError.mjs b/src/engine262/src/intrinsics/NativeError.mjs new file mode 100644 index 0000000..ffc4817 --- /dev/null +++ b/src/engine262/src/intrinsics/NativeError.mjs @@ -0,0 +1,66 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + DefinePropertyOrThrow, + OrdinaryCreateFromConstructor, + ToString, +} from '../abstract-ops/all.mjs'; +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { captureStack } from '../helpers.mjs'; +import { BootstrapConstructor, BootstrapPrototype } from './Bootstrap.mjs'; + +export function BootstrapNativeError(realmRec) { + for (const name of [ + 'EvalError', + 'RangeError', + 'ReferenceError', + 'SyntaxError', + 'TypeError', + 'URIError', + ]) { + const proto = BootstrapPrototype(realmRec, [ + ['name', new Value(name)], + ['message', new Value('')], + ], realmRec.Intrinsics['%Error.prototype%']); + + const Constructor = ([message = Value.undefined], { NewTarget }) => { + let newTarget; + if (Type(NewTarget) === 'Undefined') { + newTarget = surroundingAgent.activeFunctionObject; + } else { + newTarget = NewTarget; + } + const O = Q(OrdinaryCreateFromConstructor(newTarget, `%${name}.prototype%`, ['ErrorData'])); + if (Type(message) !== 'Undefined') { + const msg = Q(ToString(message)); + const msgDesc = Descriptor({ + Value: msg, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }); + X(DefinePropertyOrThrow(O, new Value('message'), msgDesc)); + } + + X(captureStack(O)); // non-spec + + return O; + }; + Object.defineProperty(Constructor, 'name', { + value: `${name}Constructor`, + configurable: true, + }); + + const cons = BootstrapConstructor(realmRec, Constructor, name, 1, proto, []); + cons.Prototype = realmRec.Intrinsics['%Error%']; + + realmRec.Intrinsics[`%${name}.prototype%`] = proto; + realmRec.Intrinsics[`%${name}%`] = cons; + } +} diff --git a/src/engine262/src/intrinsics/Number.mjs b/src/engine262/src/intrinsics/Number.mjs new file mode 100644 index 0000000..309f14e --- /dev/null +++ b/src/engine262/src/intrinsics/Number.mjs @@ -0,0 +1,120 @@ +import { + IsInteger, + OrdinaryCreateFromConstructor, + ToNumeric, +} from '../abstract-ops/all.mjs'; +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// 20.1.1.1 #sec-number-constructor-number-value +function NumberConstructor([value], { NewTarget }) { + let n; + if (value !== undefined) { + const prim = Q(ToNumeric(value)); + if (Type(prim) === 'BigInt') { + n = new Value(Number(prim.bigintValue())); + } else { + n = prim; + } + } else { + n = new Value(0); + } + if (NewTarget === Value.undefined) { + return n; + } + const O = OrdinaryCreateFromConstructor(NewTarget, '%Number.prototype%', ['NumberData']); + O.NumberData = n; + return O; +} + +// 20.1.2.2 #sec-number.isfinite +function Number_isFinite([number = Value.undefined]) { + if (Type(number) !== 'Number') { + return Value.false; + } + + if (number.isNaN() || number.isInfinity()) { + return Value.false; + } + return Value.true; +} + +// 20.1.2.3 #sec-number.isinteger +function Number_isInteger([number = Value.undefined]) { + return X(IsInteger(number)); +} + +// 20.1.2.4 #sec-number.isnan +function Number_isNaN([number = Value.undefined]) { + if (Type(number) !== 'Number') { + return Value.false; + } + + if (number.isNaN()) { + return Value.true; + } + return Value.false; +} + +// 20.1.2.5 #sec-number.issafeinteger +function Number_isSafeInteger([number = Value.undefined]) { + if (Type(number) !== 'Number') { + return Value.false; + } + + if (X(IsInteger(number)) === Value.true) { + if (Math.abs(number.numberValue()) <= (2 ** 53) - 1) { + return Value.true; + } + } + + return Value.false; +} + +export function BootstrapNumber(realmRec) { + const override = { + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }; + const numberConstructor = BootstrapConstructor(realmRec, NumberConstructor, 'Number', 1, realmRec.Intrinsics['%Number.prototype%'], [ + ['EPSILON', new Value(Number.EPSILON), undefined, override], + ['MAX_SAFE_INTEGER', new Value(Number.MAX_SAFE_INTEGER), undefined, override], + ['MAX_VALUE', new Value(Number.MAX_VALUE), undefined, override], + ['MIN_SAFE_INTEGER', new Value(Number.MIN_SAFE_INTEGER), undefined, override], + ['MIN_VALUE', new Value(Number.MIN_VALUE), undefined, override], + ['NaN', new Value(NaN), undefined, override], + ['NEGATIVE_INFINITY', new Value(-Infinity), undefined, override], + ['POSITIVE_INFINITY', new Value(Infinity), undefined, override], + + ['isFinite', Number_isFinite, 1], + ['isInteger', Number_isInteger, 1], + ['isNaN', Number_isNaN, 1], + ['isSafeInteger', Number_isSafeInteger, 1], + ]); + + // 20.1.2.12 #sec-number.parsefloat + // The value of the Number.parseFloat data property is the same built-in function object that is the value of the parseFloat property of the global object defined in 18.2.4. + X(numberConstructor.DefineOwnProperty(new Value('parseFloat'), Descriptor({ + Value: realmRec.Intrinsics['%parseFloat%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + // 20.1.2.13 #sec-number.parseint + // The value of the Number.parseInt data property is the same built-in function object that is the value of the parseInt property of the global object defined in 18.2.5. + X(numberConstructor.DefineOwnProperty(new Value('parseInt'), Descriptor({ + Value: realmRec.Intrinsics['%parseInt%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + realmRec.Intrinsics['%Number%'] = numberConstructor; +} diff --git a/src/engine262/src/intrinsics/NumberPrototype.mjs b/src/engine262/src/intrinsics/NumberPrototype.mjs new file mode 100644 index 0000000..70259ae --- /dev/null +++ b/src/engine262/src/intrinsics/NumberPrototype.mjs @@ -0,0 +1,230 @@ +import { + Type, + Value, +} from '../value.mjs'; +import { + Assert, + ToInteger, + ToString, +} from '../abstract-ops/all.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function thisNumberValue(value) { + if (Type(value) === 'Number') { + return value; + } + if (Type(value) === 'Object' && 'NumberData' in value) { + const n = value.NumberData; + Assert(Type(n) === 'Number'); + return n; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Number', value); +} + +// 20.1.3.2 #sec-number.prototype.toexponential +function NumberProto_toExponential([fractionDigits = Value.undefined], { thisValue }) { + let x = Q(thisNumberValue(thisValue)).numberValue(); + const f = Q(ToInteger(fractionDigits)).numberValue(); + Assert(fractionDigits !== Value.undefined || f === 0); + if (Number.isNaN(x)) { + return new Value('NaN'); + } + let s = ''; + if (x < 0) { + s = '-'; + x = -x; + } + if (x === Infinity) { + return new Value(`${s}Infinity`); + } + if (f < 0 || f > 100) { + return surroundingAgent.Throw('RangeError', 'NumberFormatRange', 'toExponential'); + } + let m; + let e; + if (x === 0) { + m = '0'.repeat(f + 1); + e = 0; + } else { + let n; + if (fractionDigits !== Value.undefined) { + // TODO: compute e and n. + } else { + // TODO: compute e, n and f. + } + m = String(n); + return surroundingAgent.Throw('Error', 'Raw', 'Number.prototype.toExponential is not fully implemented'); + } + if (f !== 0) { + const a = m[0]; + const b = m.slice(1); + m = `${a}.${b}`; + } + let c; + let d; + if (e === 0) { + c = '+'; + d = '0'; + } else { + if (e > 0) { + c = '+'; + } else { + c = '-'; + e = -e; + } + d = String(e); + } + m = `${m}e${c}${d}`; + return new Value(`${s}${m}`); +} + +// 20.1.3.3 #sec-number.prototype.tofixed +function NumberProto_toFixed([fractionDigits = Value.undefined], { thisValue }) { + let x = Q(thisNumberValue(thisValue)).numberValue(); + const f = Q(ToInteger(fractionDigits)).numberValue(); + Assert(fractionDigits !== Value.undefined || f === 0); + if (f < 0 || f > 100) { + return surroundingAgent.Throw('RangeError', 'NumberFormatRange', 'toFixed'); + } + if (Number.isNaN(x)) { + return new Value('NaN'); + } + let s = ''; + if (x < 0) { + s = '-'; + x = -x; + } + let m; + if (x >= 10 ** 21) { + m = X(ToString(new Value(x))).stringValue(); + } else { + // TODO: compute n. + // if (n === 0) { + // m = '0'; + // } else { + // m = String(n); + // } + // if (f !== 0) { + // let k = m.length; + // if (k <= f) { + // const z = '0'.repeat(f + 1 - k); + // m = `${z}${m}`; + // k = f + 1; + // } + // const a = m.slice(0, k - f); + // const b = m.slice(k - f); + // m = `${a}.${b}`; + // } + return surroundingAgent.Throw('Error', 'Raw', 'Number.prototype.toFixed is not fully implemented'); + } + return new Value(`${s}${m}`); +} + +// 20.1.3.4 #sec-number.prototype.tolocalestring +function NumberProto_toLocaleString() { + return surroundingAgent.Throw('Error', 'Raw', 'Number.prototype.toLocaleString is not implemented'); +} + +// 20.1.3.5 #sec-number.prototype.toprecision +function NumberProto_toPrecision([precision = Value.undefined], { thisValue }) { + let x = Q(thisNumberValue(thisValue)).numberValue(); + if (precision === Value.undefined) { + return X(ToString(new Value(x))); + } + const p = Q(ToInteger(precision)).numberValue(); + if (Number.isNaN(x)) { + return new Value('NaN'); + } + let s = ''; + if (x < 0) { + s = '-'; + x = -x; + } + if (x === Infinity) { + return new Value(`${s}Infinity`); + } + if (p < 1 || p > 100) { + return surroundingAgent.Throw('RangeError', 'NumberFormatRange', 'toPrecision'); + } + let m; + let e; + if (x === 0) { + m = '0'.repeat(p); + e = 0; + } else { + // TODO: compute e and n. + // m = String(n); + // if (e < -6 || e >= p) { + // Assert(e !== 0); + // if (p !== 1) { + // const a = m[0]; + // const b = m.slice(1); + // m = `${a}.${b}`; + // } + // let c; + // if (e > 0) { + // c = '+'; + // } else { + // c = '-'; + // e = -e; + // } + // const d = String(e); + // return new Value(`${s}${m}e${c}${d}`); + // } + return surroundingAgent.Throw('Error', 'Raw', 'Number.prototype.toPrecision is not fully implemented'); + } + if (e === p - 1) { + return new Value(`${s}${m}`); + } + if (e >= 0) { + m = `${m.slice(0, e + 1)}.${m.slice(e + 1)}`; + } else { + m = `0.${'0'.repeat(-(e + 1))}${m}`; + } + return new Value(`${s}${m}`); +} + +// 20.1.3.6 #sec-number.prototype.tostring +function NumberProto_toString([radix = Value.undefined], { thisValue }) { + const x = Q(thisNumberValue(thisValue)); + let radixNumber; + if (radix === Value.undefined) { + radixNumber = 10; + } else { + radixNumber = Q(ToInteger(radix)).numberValue(); + } + if (radixNumber < 2 || radixNumber > 36) { + return surroundingAgent.Throw('TypeError', 'NumberFormatRange', 'toString'); + } + if (radixNumber === 10) { + return X(ToString(x)); + } + // FIXME(devsnek): Return the String representation of this Number + // value using the radix specified by radixNumber. Letters a-z are + // used for digits with values 10 through 35. The precise algorithm + // is implementation-dependent, however the algorithm should be a + // generalization of that specified in 7.1.12.1. + return new Value(x.numberValue().toString(radixNumber)); +} + +// 20.1.3.7 #sec-number.prototype.valueof +function NumberProto_valueOf(args, { thisValue }) { + return Q(thisNumberValue(thisValue)); +} + +export function BootstrapNumberPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['toExponential', NumberProto_toExponential, 1], + ['toFixed', NumberProto_toFixed, 1], + ['toLocaleString', NumberProto_toLocaleString, 0], + ['toPrecision', NumberProto_toPrecision, 1], + ['toString', NumberProto_toString, 0], + ['valueOf', NumberProto_valueOf, 0], + ], realmRec.Intrinsics['%Object.prototype%']); + + proto.NumberData = new Value(0); + + realmRec.Intrinsics['%Number.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Object.mjs b/src/engine262/src/intrinsics/Object.mjs new file mode 100644 index 0000000..a91091a --- /dev/null +++ b/src/engine262/src/intrinsics/Object.mjs @@ -0,0 +1,303 @@ +import { + Type, + Value, +} from '../value.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Assert, + CreateArrayFromList, + CreateDataProperty, + DefinePropertyOrThrow, + CreateDataPropertyOrThrow, + EnumerableOwnPropertyNames, + FromPropertyDescriptor, + Get, + IsExtensible, + OrdinaryObjectCreate, + OrdinaryCreateFromConstructor, + RequireObjectCoercible, + SameValue, + Set, + SetIntegrityLevel, + TestIntegrityLevel, + ToObject, + ToPropertyDescriptor, + ToPropertyKey, + CreateBuiltinFunction, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { AddEntriesFromIterable } from './Map.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function ObjectConstructor([value = Value.undefined], { NewTarget }) { + if (NewTarget !== Value.undefined && NewTarget !== surroundingAgent.activeFunctionObject) { + return OrdinaryCreateFromConstructor(NewTarget, '%Object.prototype%'); + } + if (value === Value.null || value === Value.undefined) { + return OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + } + return X(ToObject(value)); +} + +function Object_assign([target = Value.undefined, ...sources]) { + const to = Q(ToObject(target)); + if (sources.length === 0) { + return to; + } + // Let sources be the List of argument values starting with the second argument. + for (const nextSource of sources) { + if (Type(nextSource) !== 'Undefined' && Type(nextSource) !== 'Null') { + const from = X(ToObject(nextSource)); + const keys = Q(from.OwnPropertyKeys()); + for (const nextKey of keys) { + const desc = Q(from.GetOwnProperty(nextKey)); + if (Type(desc) !== 'Undefined' && desc.Enumerable === Value.true) { + const propValue = Q(Get(from, nextKey)); + Q(Set(to, nextKey, propValue, Value.true)); + } + } + } + } + return to; +} + +function Object_create([O = Value.undefined, Properties = Value.undefined]) { + if (Type(O) !== 'Object' && Type(O) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType'); + } + const obj = OrdinaryObjectCreate(O); + if (Properties !== Value.undefined) { + return Q(ObjectDefineProperties(obj, Properties)); + } + return obj; +} + +function Object_defineProperties([O = Value.undefined, Properties = Value.undefined]) { + return Q(ObjectDefineProperties(O, Properties)); +} + +// #sec-objectdefineproperties ObjectDefineProperties +function ObjectDefineProperties(O, Properties) { + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', O); + } + const props = Q(ToObject(Properties)); + const keys = Q(props.OwnPropertyKeys()); + const descriptors = []; + for (const nextKey of keys) { + const propDesc = Q(props.GetOwnProperty(nextKey)); + if (propDesc !== Value.undefined && propDesc.Enumerable === Value.true) { + const descObj = Q(Get(props, nextKey)); + const desc = Q(ToPropertyDescriptor(descObj)); + descriptors.push([nextKey, desc]); + } + } + for (const pair of descriptors) { + const P = pair[0]; + const desc = pair[1]; + Q(DefinePropertyOrThrow(O, P, desc)); + } + return O; +} + +function Object_defineProperty([O = Value.undefined, P = Value.undefined, Attributes = Value.undefined]) { + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', O); + } + const key = Q(ToPropertyKey(P)); + const desc = Q(ToPropertyDescriptor(Attributes)); + + Q(DefinePropertyOrThrow(O, key, desc)); + return O; +} + +function Object_entries([O = Value.undefined]) { + const obj = Q(ToObject(O)); + const nameList = Q(EnumerableOwnPropertyNames(obj, 'key+value')); + return CreateArrayFromList(nameList); +} + +function Object_freeze([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return O; + } + + const status = Q(SetIntegrityLevel(O, 'frozen')); + if (status === Value.false) { + return surroundingAgent.Throw('TypeError', 'UnableToFreeze', O); + } + return O; +} + +function CreateDataPropertyOnObjectFunctions([key, value], { thisValue }) { + const O = thisValue; + Assert(Type(O) === 'Object'); + Assert(O.Extensible === Value.true); + const propertyKey = Q(ToPropertyKey(key)); + X(CreateDataPropertyOrThrow(O, propertyKey, value)); +} + +function Object_fromEntries([iterable = Value.undefined]) { + Q(RequireObjectCoercible(iterable)); + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + Assert(obj.Extensible === Value.true && obj.properties.size === 0); + const stepsDefine = CreateDataPropertyOnObjectFunctions; + const adder = X(CreateBuiltinFunction(stepsDefine, [])); + return Q(AddEntriesFromIterable(obj, iterable, adder)); +} + +function Object_getOwnPropertyDescriptor([O = Value.undefined, P = Value.undefined]) { + const obj = Q(ToObject(O)); + const key = Q(ToPropertyKey(P)); + const desc = Q(obj.GetOwnProperty(key)); + return FromPropertyDescriptor(desc); +} + +function Object_getOwnPropertyDescriptors([O = Value.undefined]) { + const obj = Q(ToObject(O)); + const ownKeys = Q(obj.OwnPropertyKeys()); + const descriptors = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'))); + for (const key of ownKeys) { + const desc = Q(obj.GetOwnProperty(key)); + const descriptor = X(FromPropertyDescriptor(desc)); + if (descriptor !== Value.undefined) { + X(CreateDataProperty(descriptors, key, descriptor)); + } + } + return descriptors; +} + +function GetOwnPropertyKeys(O, type) { + const obj = Q(ToObject(O)); + const keys = Q(obj.OwnPropertyKeys()); + const nameList = []; + keys.forEach((nextKey) => { + if (Type(nextKey) === type) { + nameList.push(nextKey); + } + }); + return CreateArrayFromList(nameList); +} + +function Object_getOwnPropertyNames([O = Value.undefined]) { + return Q(GetOwnPropertyKeys(O, 'String')); +} + +function Object_getOwnPropertySymbols([O = Value.undefined]) { + return Q(GetOwnPropertyKeys(O, 'Symbol')); +} + +function Object_getPrototypeOf([O = Value.undefined]) { + const obj = Q(ToObject(O)); + return Q(obj.GetPrototypeOf()); +} + +function Object_is([value1 = Value.undefined, value2 = Value.undefined]) { + return SameValue(value1, value2); +} + +function Object_isExtensible([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return Value.false; + } + + return IsExtensible(O); +} + +function Object_isFrozen([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return Value.true; + } + + return Q(TestIntegrityLevel(O, 'frozen')); +} + +function Object_isSealed([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return Value.true; + } + + return Q(TestIntegrityLevel(O, 'sealed')); +} + +function Object_keys([O = Value.undefined]) { + const obj = Q(ToObject(O)); + const nameList = Q(EnumerableOwnPropertyNames(obj, 'key')); + return CreateArrayFromList(nameList); +} + +function Object_preventExtensions([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return O; + } + + const status = Q(O.PreventExtensions()); + if (status === Value.false) { + return surroundingAgent.Throw('TypeError', 'UnableToPreventExtensions', O); + } + return O; +} + +function Object_seal([O = Value.undefined]) { + if (Type(O) !== 'Object') { + return O; + } + + const status = Q(SetIntegrityLevel(O, 'sealed')); + if (status === Value.false) { + return surroundingAgent.Throw('TypeError', 'UnableToSeal', O); + } + return O; +} + +function Object_setPrototypeOf([O = Value.undefined, proto = Value.undefined]) { + O = Q(RequireObjectCoercible(O)); + if (Type(proto) !== 'Object' && Type(proto) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType'); + } + if (Type(O) !== 'Object') { + return O; + } + + const status = Q(O.SetPrototypeOf(proto)); + if (status === Value.false) { + return surroundingAgent.Throw('TypeError', 'ObjectSetPrototype'); + } + return O; +} + +function Object_values([O = Value.undefined]) { + const obj = Q(ToObject(O)); + const nameList = Q(EnumerableOwnPropertyNames(obj, 'value')); + return CreateArrayFromList(nameList); +} + +export function BootstrapObject(realmRec) { + const objectConstructor = BootstrapConstructor(realmRec, ObjectConstructor, 'Object', 1, realmRec.Intrinsics['%Object.prototype%'], [ + ['assign', Object_assign, 2], + ['create', Object_create, 2], + ['defineProperties', Object_defineProperties, 2], + ['defineProperty', Object_defineProperty, 3], + ['entries', Object_entries, 1], + ['freeze', Object_freeze, 1], + ['fromEntries', Object_fromEntries, 1], + ['getOwnPropertyDescriptor', Object_getOwnPropertyDescriptor, 2], + ['getOwnPropertyDescriptors', Object_getOwnPropertyDescriptors, 1], + ['getOwnPropertyNames', Object_getOwnPropertyNames, 1], + ['getOwnPropertySymbols', Object_getOwnPropertySymbols, 1], + ['getPrototypeOf', Object_getPrototypeOf, 1], + ['is', Object_is, 2], + ['isExtensible', Object_isExtensible, 1], + ['isFrozen', Object_isFrozen, 1], + ['isSealed', Object_isSealed, 1], + ['keys', Object_keys, 1], + ['preventExtensions', Object_preventExtensions, 1], + ['seal', Object_seal, 1], + ['setPrototypeOf', Object_setPrototypeOf, 2], + ['values', Object_values, 1], + ]); + + realmRec.Intrinsics['%Object%'] = objectConstructor; +} diff --git a/src/engine262/src/intrinsics/ObjectPrototype.mjs b/src/engine262/src/intrinsics/ObjectPrototype.mjs new file mode 100644 index 0000000..d45c2e6 --- /dev/null +++ b/src/engine262/src/intrinsics/ObjectPrototype.mjs @@ -0,0 +1,111 @@ +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + Get, + HasOwnProperty, + Invoke, + IsArray, + SameValue, + ToObject, + ToPropertyKey, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { assignProps } from './Bootstrap.mjs'; + +function ObjectProto_hasOwnProperty([V = Value.undefined], { thisValue }) { + const P = Q(ToPropertyKey(V)); + const O = Q(ToObject(thisValue)); + return HasOwnProperty(O, P); +} + +function ObjectProto_isPrototypeOf([V = Value.undefined], { thisValue }) { + if (Type(V) !== 'Object') { + return Value.false; + } + const O = Q(ToObject(thisValue)); + while (true) { + V = Q(V.GetPrototypeOf()); + if (Type(V) === 'Null') { + return Value.false; + } + if (SameValue(O, V) === Value.true) { + return Value.true; + } + } +} + +function ObjectProto_propertyIsEnumerable([V = Value.undefined], { thisValue }) { + const P = Q(ToPropertyKey(V)); + const O = Q(ToObject(thisValue)); + const desc = Q(O.GetOwnProperty(P)); + if (Type(desc) === 'Undefined') { + return Value.false; + } + return desc.Enumerable; +} + +function ObjectProto_toLocaleString(argList, { thisValue }) { + const O = thisValue; + return Q(Invoke(O, new Value('toString'))); +} + +function ObjectProto_toString(argList, { thisValue }) { + if (Type(thisValue) === 'Undefined') { + return new Value('[object Undefined]'); + } + if (Type(thisValue) === 'Null') { + return new Value('[object Null]'); + } + const O = X(ToObject(thisValue)); + const isArray = Q(IsArray(O)); + let builtinTag; + if (isArray === Value.true) { + builtinTag = 'Array'; + } else if ('ParameterMap' in O) { + builtinTag = 'Arguments'; + } else if ('Call' in O) { + builtinTag = 'Function'; + } else if ('ErrorData' in O) { + builtinTag = 'Error'; + } else if ('BooleanData' in O) { + builtinTag = 'Boolean'; + } else if ('NumberData' in O) { + builtinTag = 'Number'; + } else if ('StringData' in O) { + builtinTag = 'String'; + } else if ('DateValue' in O) { + builtinTag = 'Date'; + } else if ('RegExpMatcher' in O) { + builtinTag = 'RegExp'; + } else { + builtinTag = 'Object'; + } + let tag = Q(Get(O, wellKnownSymbols.toStringTag)); + if (Type(tag) !== 'String') { + tag = builtinTag; + } + return new Value(`[object ${tag.stringValue ? tag.stringValue() : tag}]`); +} + +function ObjectProto_valueOf(argList, { thisValue }) { + return Q(ToObject(thisValue)); +} + +export function BootstrapObjectPrototype(realmRec) { + const proto = realmRec.Intrinsics['%Object.prototype%']; + + assignProps(realmRec, proto, [ + ['hasOwnProperty', ObjectProto_hasOwnProperty, 1], + ['isPrototypeOf', ObjectProto_isPrototypeOf, 1], + ['propertyIsEnumerable', ObjectProto_propertyIsEnumerable, 1], + ['toLocaleString', ObjectProto_toLocaleString, 0], + ['toString', ObjectProto_toString, 0], + ['valueOf', ObjectProto_valueOf, 0], + ]); + + realmRec.Intrinsics['%Object.prototype.toString%'] = X(Get(proto, new Value('toString'))); + realmRec.Intrinsics['%Object.prototype.valueOf%'] = X(Get(proto, new Value('valueOf'))); +} diff --git a/src/engine262/src/intrinsics/Promise.mjs b/src/engine262/src/intrinsics/Promise.mjs new file mode 100644 index 0000000..454f058 --- /dev/null +++ b/src/engine262/src/intrinsics/Promise.mjs @@ -0,0 +1,513 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Descriptor, + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + Assert, + Call, + CreateArrayFromList, + CreateBuiltinFunction, + CreateDataProperty, + CreateResolvingFunctions, + Get, + GetIterator, + Invoke, + IsCallable, + IsConstructor, + IteratorClose, + IteratorStep, + IteratorValue, + NewPromiseCapability, + OrdinaryObjectCreate, + OrdinaryCreateFromConstructor, + PromiseCapabilityRecord, + PromiseResolve, + SetFunctionLength, + SetFunctionName, +} from '../abstract-ops/all.mjs'; +import { + AbruptCompletion, Completion, + ThrowCompletion, + IfAbruptRejectPromise, + ReturnIfAbrupt, + Q, X, +} from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function PromiseConstructor([executor = Value.undefined], { NewTarget }) { + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + if (IsCallable(executor) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', executor); + } + const promise = Q(OrdinaryCreateFromConstructor(NewTarget, '%Promise.prototype%', [ + 'PromiseState', + 'PromiseResult', + 'PromiseFulfillReactions', + 'PromiseRejectReactions', + 'PromiseIsHandled', + ])); + promise.PromiseState = 'pending'; + promise.PromiseFulfillReactions = []; + promise.PromiseRejectReactions = []; + promise.PromiseIsHandled = Value.false; + const resolvingFunctions = CreateResolvingFunctions(promise); + const completion = Call(executor, Value.undefined, [ + resolvingFunctions.Resolve, resolvingFunctions.Reject, + ]); + if (completion instanceof AbruptCompletion) { + Q(Call(resolvingFunctions.Reject, Value.undefined, [completion.Value])); + } + return promise; +} + +// 25.6.4.1.2 #sec-promise.all-resolve-element-functions +function PromiseAllResolveElementFunctions([x = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + const alreadyCalled = F.AlreadyCalled; + if (alreadyCalled.Value === true) { + return Value.undefined; + } + alreadyCalled.Value = true; + const index = F.Index; + const values = F.Values; + const promiseCapability = F.Capability; + const remainingElementsCount = F.RemainingElements; + values[index] = x; + remainingElementsCount.Value -= 1; + if (remainingElementsCount.Value === 0) { + const valuesArray = CreateArrayFromList(values); + return Q(Call(promiseCapability.Resolve, Value.undefined, [valuesArray])); + } + return Value.undefined; +} + +// 25.6.4.1.1 #sec-performpromiseall +function PerformPromiseAll(iteratorRecord, constructor, resultCapability) { + Assert(IsConstructor(constructor) === Value.true); + Assert(resultCapability instanceof PromiseCapabilityRecord); + const values = []; + const remainingElementsCount = { Value: 1 }; + const promiseResolve = Q(Get(constructor, new Value('resolve'))); + if (IsCallable(promiseResolve) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', promiseResolve); + } + let index = 0; + while (true) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + remainingElementsCount.Value -= 1; + if (remainingElementsCount.Value === 0) { + const valuesArray = CreateArrayFromList(values); + Q(Call(resultCapability.Resolve, Value.undefined, [valuesArray])); + } + return resultCapability.Promise; + } + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + values.push(Value.undefined); + const nextPromise = Q(Call(promiseResolve, constructor, [nextValue])); + const steps = PromiseAllResolveElementFunctions; + const resolveElement = X(CreateBuiltinFunction(steps, [ + 'AlreadyCalled', 'Index', 'Values', 'Capability', 'RemainingElements', + ])); + X(SetFunctionLength(resolveElement, new Value(1))); + X(SetFunctionName(resolveElement, new Value(''))); + resolveElement.AlreadyCalled = { Value: false }; + resolveElement.Index = index; + resolveElement.Values = values; + resolveElement.Capability = resultCapability; + resolveElement.RemainingElements = remainingElementsCount; + remainingElementsCount.Value += 1; + Q(Invoke(nextPromise, new Value('then'), [resolveElement, resultCapability.Reject])); + index += 1; + } +} + +function Promise_all([iterable = Value.undefined], { thisValue }) { + const C = thisValue; + const promiseCapability = Q(NewPromiseCapability(C)); + const iteratorRecord = GetIterator(iterable); + IfAbruptRejectPromise(iteratorRecord, promiseCapability); + let result = PerformPromiseAll(iteratorRecord, C, promiseCapability); + if (result instanceof AbruptCompletion) { + if (iteratorRecord.Done === Value.false) { + result = IteratorClose(iteratorRecord, result); + } + IfAbruptRejectPromise(result, promiseCapability); + } + return Completion(result); +} + +function PromiseAllSettledResolveElementFunctions([x = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + const alreadyCalled = F.AlreadyCalled; + if (alreadyCalled.Value === true) { + return Value.undefined; + } + alreadyCalled.Value = true; + const index = F.Index; + const values = F.Values; + const promiseCapability = F.Capability; + const remainingElementsCount = F.RemainingElements; + const obj = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'))); + X(CreateDataProperty(obj, new Value('status'), new Value('fulfilled'))); + X(CreateDataProperty(obj, new Value('value'), x)); + values[index] = obj; + remainingElementsCount.Value -= 1; + if (remainingElementsCount.Value === 0) { + const valuesArray = X(CreateArrayFromList(values)); + return Q(Call(promiseCapability.Resolve, Value.undefined, [valuesArray])); + } + return Value.undefined; +} + +function PromiseAllSettledRejectElementFunctions([x = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + const alreadyCalled = F.AlreadyCalled; + if (alreadyCalled.Value === true) { + return Value.undefined; + } + alreadyCalled.Value = true; + const index = F.Index; + const values = F.Values; + const promiseCapability = F.Capability; + const remainingElementsCount = F.RemainingElements; + const obj = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'))); + X(CreateDataProperty(obj, new Value('status'), new Value('rejected'))); + X(CreateDataProperty(obj, new Value('reason'), x)); + values[index] = obj; + remainingElementsCount.Value -= 1; + if (remainingElementsCount.Value === 0) { + const valuesArray = X(CreateArrayFromList(values)); + return Q(Call(promiseCapability.Resolve, Value.undefined, [valuesArray])); + } + return Value.undefined; +} + +function PerformPromiseAllSettled(iteratorRecord, constructor, resultCapability) { + Assert(X(IsConstructor(constructor) === Value.true)); + Assert(resultCapability instanceof PromiseCapabilityRecord); + const promiseResolve = Q(Get(constructor, new Value('resolve'))); + const values = []; + const remainingElementsCount = { Value: 1 }; + let index = 0; + while (true) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + remainingElementsCount.Value -= 1; + if (remainingElementsCount.Value === 0) { + const valuesArray = X(CreateArrayFromList(values)); + Q(Call(resultCapability.Resolve, Value.undefined, [valuesArray])); + } + return resultCapability.Promise; + } + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + values.push(Value.undefined); + const nextPromise = Q(Call(promiseResolve, constructor, [nextValue])); + const steps = PromiseAllSettledResolveElementFunctions; + const resolveElement = X(CreateBuiltinFunction(steps, [ + 'AlreadyCalled', + 'Index', + 'Values', + 'Capability', + 'RemainingElements', + ])); + X(SetFunctionLength(resolveElement, new Value(1))); + X(SetFunctionName(resolveElement, new Value(''))); + const alreadyCalled = { Value: false }; + resolveElement.AlreadyCalled = alreadyCalled; + resolveElement.Index = index; + resolveElement.Values = values; + resolveElement.Capability = resultCapability; + resolveElement.RemainingElements = remainingElementsCount; + const rejectSteps = PromiseAllSettledRejectElementFunctions; + const rejectElement = X(CreateBuiltinFunction(rejectSteps, [ + 'AlreadyCalled', + 'Index', + 'Values', + 'Capability', + 'RemainingElements', + ])); + X(SetFunctionLength(rejectElement, new Value(1))); + X(SetFunctionName(rejectElement, new Value(''))); + rejectElement.AlreadyCalled = alreadyCalled; + rejectElement.Index = index; + rejectElement.Values = values; + rejectElement.Capability = resultCapability; + rejectElement.RemainingElements = remainingElementsCount; + remainingElementsCount.Value += 1; + Q(Invoke(nextPromise, new Value('then'), [resolveElement, rejectElement])); + index += 1; + } +} + +function Promise_allSettled([iterable = Value.undefined], { thisValue }) { + const C = thisValue; + const promiseCapability = Q(NewPromiseCapability(C)); + const iteratorRecord = GetIterator(iterable); + IfAbruptRejectPromise(iteratorRecord, promiseCapability); + let result = PerformPromiseAllSettled(iteratorRecord, C, promiseCapability); + if (result instanceof AbruptCompletion) { + if (iteratorRecord.Done === Value.false) { + result = IteratorClose(iteratorRecord, result); + } + IfAbruptRejectPromise(result, promiseCapability); + } + return Completion(result); +} + +// https://tc39.es/proposal-promise-any/#sec-promise.any-reject-element-functions +function PromiseAnyRejectElementFunctions([x = Value.undefined]) { + // 1. Let F be the active function object. + const F = surroundingAgent.activeFunctionObject; + // 2. Let alreadyCalled be F.[[AlreadyCalled]]. + const alreadyCalled = F.AlreadyCalled; + // 3. If alreadyCalled.[[Value]] is true, return undefined. + if (alreadyCalled.Value) { + return Value.undefined; + } + // 4. Set alreadyCalled.[[Value]] to true. + alreadyCalled.Value = true; + // 5. Let index be F.[[Index]]. + const index = F.Index; + // 6. Let errors be F.[[Errors]]. + const errors = F.Errors; + // 7. Let promiseCapability be F.[[Capability]]. + const promiseCapability = F.Capability; + // 8. Let remainingElementsCount be F.[[RemainingElements]]. + const remainingElementsCount = F.RemainingElements; + // 9. Set errors[index] to x. + errors[index] = x; + // 10. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + remainingElementsCount.Value -= 1; + // 11. If remainingElementsCount.[[Value]] is 0, then + if (remainingElementsCount.Value === 0) { + // a. Let error be a newly created AggregateError object. + const error = surroundingAgent.Throw('AggregateError', 'PromiseAnyRejected').Value; + // b. Set error.[[AggregateErrors]] to errors. + error.AggregateErrors = errors; + // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »). + return Q(Call(promiseCapability.Reject, Value.undefined, [error])); + } + // 12. Return undefined. + return Value.undefined; +} + +// https://tc39.es/proposal-promise-any/#sec-performpromiseany +function PerformPromiseAny(iteratorRecord, constructor, resultCapability) { + // 1. Assert: ! IsConstructor(constructor) is true. + Assert(X(IsConstructor(constructor)) === Value.true); + // 2. Assert: resultCapability is a PromiseCapability Record. + Assert(resultCapability instanceof PromiseCapabilityRecord); + // 3. Let errors be a new empty List. + const errors = []; + // 4. Let remainingElementsCount be a new Record { [[Value]]: 1 }. + const remainingElementsCount = { Value: 1 }; + // 5. Let index be 0. + let index = 0; + // 6. Let promiseResolve be ? Get(constructor, "resolve"). + const promiseResolve = Q(Get(constructor, new Value('resolve'))); + // 7. If ! IsCallable(promiseResolve) is false, throw a TypeError exception. + if (X(IsCallable(promiseResolve)) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', promiseResolve); + } + // 8. Repeat, + while (true) { + // a. Let next be IteratorStep(iteratorRecord). + const next = IteratorStep(iteratorRecord); + // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + // c. ReturnIfAbrupt(next). + ReturnIfAbrupt(next); + // d. If next is false, then + if (next === Value.false) { + // i. Set iteratorRecord.[[Done]] to true. + iteratorRecord.Done = Value.true; + // ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + remainingElementsCount.Value -= 1; + // iii. If remainingElementsCount.[[Value]] is 0, then + if (remainingElementsCount.Value === 0) { + // 1. Let error be a newly created AggregateError object. + const error = surroundingAgent.Throw('AggregateError', 'PromiseAnyRejected').Value; + // 2. Set error.[[AggregateErrors]] to errors. + error.AggregateErrors = errors; + // 3. Return ThrowCompletion(error). + return ThrowCompletion(error); + } + // iv. Return resultCapability.[[Promise]]. + return resultCapability.Promise; + } + // e. Let nextValue be IteratorValue(next). + const nextValue = IteratorValue(next); + // f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + // g. ReturnIfAbrupt(nextValue). + ReturnIfAbrupt(nextValue); + // h. Append undefined to errors. + errors.push(Value.undefined); + // i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). + const nextPromise = Q(Call(promiseResolve, constructor, [nextValue])); + // j. Let steps be the algorithm steps defined in Promise.any Reject Element Functions. + const steps = PromiseAnyRejectElementFunctions; + // k. Let rejectElement be ! CreateBuiltinFunction(steps, « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »). + const rejectElement = X(CreateBuiltinFunction(steps, ['AlreadyCalled', 'Index', 'Errors', 'Capability', 'RemainingElements'])); + X(SetFunctionLength(rejectElement, new Value(1))); + X(SetFunctionName(rejectElement, new Value(''))); + // l. Set rejectElement.[[AlreadyCalled]] to a new Record { [[Value]]: false }. + rejectElement.AlreadyCalled = { Value: false }; + // m. Set rejectElement.[[Index]] to index. + rejectElement.Index = index; + // n. Set rejectElement.[[Errors]] to errors. + rejectElement.Errors = errors; + // o. Set rejectElement.[[Capability]] to resultCapability. + rejectElement.Capability = resultCapability; + // p. Set rejectElement.[[RemainingElements]] to remainingElementsCount. + rejectElement.RemainingElements = remainingElementsCount; + // q. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. + remainingElementsCount.Value += 1; + // r. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], rejectElement »). + Q(Invoke(nextPromise, new Value('then'), [resultCapability.Resolve, rejectElement])); + // s. Increase index by 1. + index += 1; + } +} + +// https://tc39.es/proposal-promise-any/#sec-promise.any +function Promise_any([iterable = Value.undefined], { thisValue }) { + // 1. Let C be the this value. + const C = thisValue; + // 2. Let promiseCapability be ? NewPromiseCapability(C). + const promiseCapability = Q(NewPromiseCapability(C)); + // 3. Let iteratorRecord be GetIterator(iterable). + const iteratorRecord = GetIterator(iterable); + // 4. IfAbruptRejectPromise(iteratorRecord, promiseCapability). + IfAbruptRejectPromise(iteratorRecord, promiseCapability); + // 5. Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + let result = PerformPromiseAny(iteratorRecord, C, promiseCapability); + // 6. If result is an abrupt completion, then + if (result instanceof AbruptCompletion) { + // a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result). + if (iteratorRecord.Done === Value.false) { + result = IteratorClose(iteratorRecord, result); + } + // b. IfAbruptRejectPromise(result, promiseCapability). + IfAbruptRejectPromise(result, promiseCapability); + } + // 1. Return Completion(result). + return Completion(result); +} + +function PerformPromiseRace(iteratorRecord, constructor, resultCapability) { + Assert(IsConstructor(constructor) === Value.true); + Assert(resultCapability instanceof PromiseCapabilityRecord); + const promiseResolve = Q(Get(constructor, new Value('resolve'))); + if (IsCallable(promiseResolve) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', promiseResolve); + } + while (true) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + return resultCapability.Promise; + } + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + const nextPromise = Q(Call(promiseResolve, constructor, [nextValue])); + Q(Invoke(nextPromise, new Value('then'), [resultCapability.Resolve, resultCapability.Reject])); + } +} + +function Promise_race([iterable = Value.undefined], { thisValue }) { + const C = thisValue; + const promiseCapability = Q(NewPromiseCapability(C)); + const iteratorRecord = GetIterator(iterable); + IfAbruptRejectPromise(iteratorRecord, promiseCapability); + let result = PerformPromiseRace(iteratorRecord, C, promiseCapability); + if (result instanceof AbruptCompletion) { + if (iteratorRecord.Done === Value.false) { + result = IteratorClose(iteratorRecord, result); + } + IfAbruptRejectPromise(result, promiseCapability); + } + return Completion(result); +} + +function Promise_reject([r = Value.undefined], { thisValue }) { + const C = thisValue; + const promiseCapability = Q(NewPromiseCapability(C)); + Q(Call(promiseCapability.Reject, Value.undefined, [r])); + return promiseCapability.Promise; +} + +function Promise_resolve([x = Value.undefined], { thisValue }) { + const C = thisValue; + if (Type(C) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'InvalidReceiver', 'Promise.resolve', C); + } + return Q(PromiseResolve(C, x)); +} + +function Promise_symbolSpecies(args, { thisValue }) { + return thisValue; +} + +export function BootstrapPromise(realmRec) { + const promiseConstructor = BootstrapConstructor(realmRec, PromiseConstructor, 'Promise', 1, realmRec.Intrinsics['%Promise.prototype%'], [ + ['all', Promise_all, 1], + ['allSettled', Promise_allSettled, 1], + surroundingAgent.feature('Promise.any') + ? ['any', Promise_any, 1] + : undefined, + ['race', Promise_race, 1], + ['reject', Promise_reject, 1], + ['resolve', Promise_resolve, 1], + [wellKnownSymbols.species, [Promise_symbolSpecies]], + ]); + + promiseConstructor.DefineOwnProperty(new Value('prototype'), Descriptor({ + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + })); + + realmRec.Intrinsics['%Promise.all%'] = X(Get(promiseConstructor, new Value('all'))); + realmRec.Intrinsics['%Promise.reject%'] = X(Get(promiseConstructor, new Value('reject'))); + realmRec.Intrinsics['%Promise.resolve%'] = X(Get(promiseConstructor, new Value('resolve'))); + + realmRec.Intrinsics['%Promise%'] = promiseConstructor; +} diff --git a/src/engine262/src/intrinsics/PromisePrototype.mjs b/src/engine262/src/intrinsics/PromisePrototype.mjs new file mode 100644 index 0000000..fcacb73 --- /dev/null +++ b/src/engine262/src/intrinsics/PromisePrototype.mjs @@ -0,0 +1,105 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + Call, + CreateBuiltinFunction, + Get, + Invoke, + IsCallable, + IsConstructor, + IsPromise, + NewPromiseCapability, + PerformPromiseThen, + PromiseResolve, + SetFunctionLength, + SetFunctionName, + SpeciesConstructor, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q, ThrowCompletion, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +function PromiseProto_catch([onRejected = Value.undefined], { thisValue }) { + const promise = thisValue; + return Q(Invoke(promise, new Value('then'), [Value.undefined, onRejected])); +} + +function ThenFinallyFunctions([value = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + const onFinally = F.OnFinally; + Assert(IsCallable(onFinally) === Value.true); + const result = Q(Call(onFinally, Value.undefined)); + const C = F.Constructor; + Assert(IsConstructor(C) === Value.true); + const promise = Q(PromiseResolve(C, result)); + const valueThunk = CreateBuiltinFunction(() => value, []); + SetFunctionLength(valueThunk, new Value(0)); + SetFunctionName(valueThunk, new Value('')); + return Q(Invoke(promise, new Value('then'), [valueThunk])); +} + +function CatchFinallyFunctions([reason = Value.undefined]) { + const F = surroundingAgent.activeFunctionObject; + const onFinally = F.OnFinally; + Assert(IsCallable(onFinally) === Value.true); + const result = Q(Call(onFinally, Value.undefined)); + const C = F.Constructor; + Assert(IsConstructor(C) === Value.true); + const promise = Q(PromiseResolve(C, result)); + const thrower = CreateBuiltinFunction(() => new ThrowCompletion(reason), []); + SetFunctionLength(thrower, new Value(0)); + SetFunctionName(thrower, new Value('')); + return Q(Invoke(promise, new Value('then'), [thrower])); +} + +function PromiseProto_finally([onFinally = Value.undefined], { thisValue }) { + const promise = thisValue; + if (Type(promise) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Promise', promise); + } + const C = SpeciesConstructor(promise, surroundingAgent.intrinsic('%Promise%')); + Assert(IsConstructor(C) === Value.true); + let thenFinally; + let catchFinally; + if (IsCallable(onFinally) === Value.false) { + thenFinally = onFinally; + catchFinally = onFinally; + } else { + const stepsThenFinally = ThenFinallyFunctions; + thenFinally = X(CreateBuiltinFunction(stepsThenFinally, ['Constructor', 'OnFinally'])); + SetFunctionLength(thenFinally, new Value(1)); + SetFunctionName(thenFinally, new Value('')); + thenFinally.Constructor = C; + thenFinally.OnFinally = onFinally; + const stepsCatchFinally = CatchFinallyFunctions; + catchFinally = X(CreateBuiltinFunction(stepsCatchFinally, ['Constructor', 'OnFinally'])); + SetFunctionLength(catchFinally, new Value(1)); + SetFunctionName(catchFinally, new Value('')); + catchFinally.Constructor = C; + catchFinally.OnFinally = onFinally; + } + return Q(Invoke(promise, new Value('then'), [thenFinally, catchFinally])); +} + +function PromiseProto_then([onFulfilled = Value.undefined, onRejected = Value.undefined], { thisValue }) { + const promise = thisValue; + if (IsPromise(promise) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Promise', promise); + } + const C = Q(SpeciesConstructor(promise, surroundingAgent.intrinsic('%Promise%'))); + const resultCapability = Q(NewPromiseCapability(C)); + return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability); +} + +export function BootstrapPromisePrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['catch', PromiseProto_catch, 1], + ['finally', PromiseProto_finally, 1], + ['then', PromiseProto_then, 2], + ], realmRec.Intrinsics['%Object.prototype%'], 'Promise'); + + realmRec.Intrinsics['%Promise.prototype.then%'] = X(Get(proto, new Value('then'))); + + realmRec.Intrinsics['%Promise.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Proxy.mjs b/src/engine262/src/intrinsics/Proxy.mjs new file mode 100644 index 0000000..e53e96c --- /dev/null +++ b/src/engine262/src/intrinsics/Proxy.mjs @@ -0,0 +1,82 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Assert, + CreateBuiltinFunction, + CreateDataProperty, + OrdinaryObjectCreate, + ProxyCreate, + SetFunctionLength, + SetFunctionName, + isProxyExoticObject, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { assignProps } from './Bootstrap.mjs'; + +// #sec-proxy-target-handler +function ProxyConstructor([target = Value.undefined, handler = Value.undefined], { NewTarget }) { + // 1. f NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Return ? ProxyCreate(target, handler). + return Q(ProxyCreate(target, handler)); +} + +// #sec-proxy-revocation-functions +function ProxyRevocationFunctions() { + // 1. Let F be the active function object. + const F = surroundingAgent.activeFunctionObject; + // 2. Let p be F.[[RevocableProxy]]. + const p = F.RevocableProxy; + // 3. If p is null, return undefined. + if (p === Value.null) { + return Value.undefined; + } + // 4. Set F.[[RevocableProxy]] to null. + F.RevocableProxy = Value.null; + // 5. Assert: p is a Proxy object. + Assert(isProxyExoticObject(p)); + // 6. Set p.[[ProxyTarget]] to null. + p.ProxyTarget = Value.null; + // 7. Set p.[[ProxyHandler]] to null. + p.ProxyHandler = Value.null; + // 8. Return undefined. + return Value.undefined; +} + +// #sec-proxy.revocable +function Proxy_revocable([target = Value.undefined, handler = Value.undefined]) { + // 1. Let p be ? ProxyCreate(target, handler). + const p = Q(ProxyCreate(target, handler)); + // 2. Let steps be the algorithm steps defined in #sec-proxy-revocation-functions. + const steps = ProxyRevocationFunctions; + // 3. Let revoker be ! CreateBuiltinFunction(steps, « [[RevocableProxy]] »). + const revoker = X(CreateBuiltinFunction(steps, ['RevocableProxy'])); + SetFunctionLength(revoker, new Value(0)); + SetFunctionName(revoker, new Value('')); + // 4. Set revoker.[[RevocableProxy]] to p. + revoker.RevocableProxy = p; + // 5. Let result be OrdinaryObjectCreate(%Object.prototype%). + const result = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + // 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p). + X(CreateDataProperty(result, new Value('proxy'), p)); + // 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker). + X(CreateDataProperty(result, new Value('revoke'), revoker)); + // 8. Return result. + return result; +} + +export function BootstrapProxy(realmRec) { + const proxyConstructor = CreateBuiltinFunction(ProxyConstructor, [], realmRec, undefined, Value.true); + SetFunctionName(proxyConstructor, new Value('Proxy')); + SetFunctionLength(proxyConstructor, new Value(2)); + + assignProps(realmRec, proxyConstructor, [ + ['revocable', Proxy_revocable, 2], + ]); + + realmRec.Intrinsics['%Proxy%'] = proxyConstructor; +} diff --git a/src/engine262/src/intrinsics/Reflect.mjs b/src/engine262/src/intrinsics/Reflect.mjs new file mode 100644 index 0000000..949f0d8 --- /dev/null +++ b/src/engine262/src/intrinsics/Reflect.mjs @@ -0,0 +1,153 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Call, + Construct, + CreateArrayFromList, + CreateListFromArrayLike, + FromPropertyDescriptor, + IsCallable, + IsConstructor, + PrepareForTailCall, + ToPropertyDescriptor, + ToPropertyKey, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function Reflect_apply([target = Value.undefined, thisArgument = Value.undefined, argumentsList = Value.undefined]) { + if (IsCallable(target) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', target); + } + const args = Q(CreateListFromArrayLike(argumentsList)); + PrepareForTailCall(); + return Q(Call(target, thisArgument, args)); +} + +function Reflect_construct([target = Value.undefined, argumentsList = Value.undefined, newTarget]) { + if (IsConstructor(target) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', target); + } + if (newTarget === undefined) { + newTarget = target; + } else if (IsConstructor(newTarget) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', newTarget); + } + const args = Q(CreateListFromArrayLike(argumentsList)); + return Q(Construct(target, args, newTarget)); +} + +function Reflect_defineProperty([target = Value.undefined, propertyKey = Value.undefined, attributes = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + const desc = Q(ToPropertyDescriptor(attributes)); + return Q(target.DefineOwnProperty(key, desc)); +} + +function Reflect_deleteProperty([target = Value.undefined, propertyKey = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + return Q(target.Delete(key)); +} + +function Reflect_get([target = Value.undefined, propertyKey = Value.undefined, receiver]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + if (receiver === undefined) { + receiver = target; + } + return Q(target.Get(key, receiver)); +} + +function Reflect_getOwnPropertyDescriptor([target = Value.undefined, propertyKey = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + const desc = Q(target.GetOwnProperty(key)); + return FromPropertyDescriptor(desc); +} + +function Reflect_getPrototypeOf([target = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + return Q(target.GetPrototypeOf()); +} + +function Reflect_has([target = Value.undefined, propertyKey = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + return Q(target.HasProperty(key)); +} + +function Reflect_isExtensible([target = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + return Q(target.IsExtensible()); +} + +function Reflect_ownKeys([target = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const keys = Q(target.OwnPropertyKeys()); + return CreateArrayFromList(keys); +} + +function Reflect_preventExtensions([target = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + return Q(target.PreventExtensions()); +} + +function Reflect_set([target = Value.undefined, propertyKey = Value.undefined, V = Value.undefined, receiver]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const key = Q(ToPropertyKey(propertyKey)); + if (receiver === undefined) { + receiver = target; + } + return Q(target.Set(key, V, receiver)); +} + +function Reflect_setPrototypeOf([target = Value.undefined, proto = Value.undefined]) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + if (Type(proto) !== 'Object' && Type(proto) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType'); + } + return Q(target.SetPrototypeOf(proto)); +} + +export function BootstrapReflect(realmRec) { + const reflect = BootstrapPrototype(realmRec, [ + ['apply', Reflect_apply, 3], + ['construct', Reflect_construct, 2], + ['defineProperty', Reflect_defineProperty, 3], + ['deleteProperty', Reflect_deleteProperty, 2], + ['get', Reflect_get, 2], + ['getOwnPropertyDescriptor', Reflect_getOwnPropertyDescriptor, 2], + ['getPrototypeOf', Reflect_getPrototypeOf, 1], + ['has', Reflect_has, 2], + ['isExtensible', Reflect_isExtensible, 1], + ['ownKeys', Reflect_ownKeys, 1], + ['preventExtensions', Reflect_preventExtensions, 1], + ['set', Reflect_set, 3], + ['setPrototypeOf', Reflect_setPrototypeOf, 2], + ], realmRec.Intrinsics['%Object.prototype%']); + + realmRec.Intrinsics['%Reflect%'] = reflect; +} diff --git a/src/engine262/src/intrinsics/RegExp.mjs b/src/engine262/src/intrinsics/RegExp.mjs new file mode 100644 index 0000000..a683a39 --- /dev/null +++ b/src/engine262/src/intrinsics/RegExp.mjs @@ -0,0 +1,71 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Get, + IsRegExp, + RegExpAlloc, + RegExpInitialize, + SameValue, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// 21.2.3 #sec-regexp-constructor +function RegExpConstructor([pattern = Value.undefined, flags = Value.undefined], { NewTarget }) { + const patternIsRegExp = Q(IsRegExp(pattern)); + let newTarget; + if (NewTarget === Value.undefined) { + newTarget = surroundingAgent.activeFunctionObject; + if (patternIsRegExp === Value.true && flags === Value.undefined) { + const patternConstructor = Q(Get(pattern, new Value('constructor'))); + if (SameValue(newTarget, patternConstructor) === Value.true) { + return pattern; + } + } + } else { + newTarget = NewTarget; + } + let P; + let F; + if (Type(pattern) === 'Object' && 'RegExpMatcher' in pattern) { + P = pattern.OriginalSource; + if (flags === Value.undefined) { + F = pattern.OriginalFlags; + } else { + F = flags; + } + } else if (patternIsRegExp === Value.true) { + P = Q(Get(pattern, new Value('source'))); + if (flags === Value.undefined) { + F = Q(Get(pattern, new Value('flags'))); + } else { + F = flags; + } + } else { + P = pattern; + F = flags; + } + const O = Q(RegExpAlloc(newTarget)); + return Q(RegExpInitialize(O, P, F)); +} + +// 21.2.4.2 #sec-get-regexp-@@species +function RegExp_speciesGetter(args, { thisValue }) { + return thisValue; +} + +export function BootstrapRegExp(realmRec) { + const proto = realmRec.Intrinsics['%RegExp.prototype%']; + + const cons = BootstrapConstructor(realmRec, RegExpConstructor, 'RegExp', 2, proto, [ + [wellKnownSymbols.species, [RegExp_speciesGetter]], + ]); + + realmRec.Intrinsics['%RegExp%'] = cons; +} diff --git a/src/engine262/src/intrinsics/RegExpPrototype.mjs b/src/engine262/src/intrinsics/RegExpPrototype.mjs new file mode 100644 index 0000000..de2dfe8 --- /dev/null +++ b/src/engine262/src/intrinsics/RegExpPrototype.mjs @@ -0,0 +1,912 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + ArrayCreate, + Assert, + Call, + CodePointAt, + Construct, + CreateArrayFromList, + CreateDataProperty, + EscapeRegExpPattern, + Get, + IsCallable, + MatchRecord, + OrdinaryObjectCreate, + SameValue, + Set, + SpeciesConstructor, + LengthOfArrayLike, + ToBoolean, + ToInteger, + ToLength, + ToString, + ToObject, + ToUint32, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { + GetSubstitution, + State, +} from '../runtime-semantics/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; +import { CreateRegExpStringIterator } from './RegExpStringIteratorPrototype.mjs'; + + +// 21.2.5.2 #sec-regexp.prototype.exec +function RegExpProto_exec([string = Value.undefined], { thisValue }) { + const R = thisValue; + Q(RequireInternalSlot(R, 'RegExpMatcher')); + const S = Q(ToString(string)); + return Q(RegExpBuiltinExec(R, S)); +} + +// 21.2.5.2.1 #sec-regexpexec +export function RegExpExec(R, S) { + Assert(Type(R) === 'Object'); + Assert(Type(S) === 'String'); + + const exec = Q(Get(R, new Value('exec'))); + if (IsCallable(exec) === Value.true) { + const result = Q(Call(exec, R, [S])); + if (Type(result) !== 'Object' && Type(result) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'RegExpExecNotObject', result); + } + return result; + } + Q(RequireInternalSlot(R, 'RegExpMatcher')); + return Q(RegExpBuiltinExec(R, S)); +} + +// 21.2.5.2.2 #sec-regexpbuiltinexec +function RegExpBuiltinExec(R, S) { + // 1. Assert: R is an initialized RegExp instance. + Assert('RegExpMatcher' in R); + // 2. Assert: Type(S) is String. + Assert(Type(S) === 'String'); + // 3. Let length be the number of code units in S. + const length = S.stringValue().length; + // 4. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). + let lastIndex = Q(ToLength(Q(Get(R, new Value('lastIndex'))))); + // 5. Let flags be R.[[OriginalFlags]]. + const flags = R.OriginalFlags.stringValue(); + // 6. If flags contains "g", let global be true; else let global be false. + const global = flags.includes('g'); + // 7. If flags contains "y", let sticky be true; else let sticky be false. + const sticky = flags.includes('y'); + // 8. If global is false and sticky is false, set lastIndex to 0. + if (!global && !sticky) { + lastIndex = new Value(0); + } + // 9. Let matcher be R.[[RegExpMatcher]]. + const matcher = R.RegExpMatcher; + // 10. If flags contains "u", let fullUnicode be true; else let fullUnicode be false. + const fullUnicode = flags.includes('u'); + // 11. Let matchSucceeded be false. + let matchSucceeded = false; + let r; + // 12. Repeat, while matchSucceeded is false + while (matchSucceeded === false) { + // a. If lastIndex > length, then + if (lastIndex.numberValue() > length) { + // i. If global is true or sticky is true, then + if (global || sticky) { + // 1. Perform ? Set(R, "lastIndex", 0, true). + Q(Set(R, new Value('lastIndex'), new Value(0), Value.true)); + } + // ii. Return null. + return Value.null; + } + // b. Let r be matcher(S, lastIndex). + r = matcher(S, lastIndex); + // c. If r is failure, then + if (r === 'failure') { + // i. If sticky is true, then + if (sticky) { + // 1. Perform ? Set(R, "lastIndex", 0, true). + Q(Set(R, new Value('lastIndex'), new Value(0), Value.true)); + // 2. Return null. + return Value.null; + } + // ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode). + lastIndex = AdvanceStringIndex(S, lastIndex, fullUnicode ? Value.true : Value.false); + } else { // d. Else, + // i. Assert: r is a State. + Assert(r instanceof State); + // ii. Set matchSucceeded to true. + matchSucceeded = true; + } + } + // 13. Let e be r's endIndex value. + let e = r.endIndex; + const Input = fullUnicode ? Array.from(S.stringValue()) : S.stringValue().split(''); + // 14. If fullUnicode is true, then + if (fullUnicode) { + // https://tc39.es/proposal-regexp-match-indices/#sec-regexpbuiltinexec + if (surroundingAgent.feature('RegExpMatchIndices')) { + // If fullUnicode is true, set e to ! GetStringIndex(S, Input, e). + e = X(GetStringIndex(S, Input, e)); + } else { + // a. e is an index into the Input character list, derived from S, matched by matcher. + // Let eUTF be the smallest index into S that corresponds to the character at element e of Input. + // If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S. + let eUTF = 0; + if (e >= Input.length) { + eUTF = S.stringValue().length; + } else { + for (let i = 0; i < e; i += 1) { + eUTF += Input[i].length; + } + } + // b. Set e to eUTF. + e = eUTF; + } + } + // 15. If global is true or sticky is true, then + if (global || sticky) { + // a. Perform ? Set(R, "lastIndex", e, true). + Q(Set(R, new Value('lastIndex'), new Value(e), Value.true)); + } + // 16. Let n be the number of elements in r's captures List. + const n = r.captures.length - 1; + // 17. Assert: n < 2^32 - 1. + Assert(n < (2 ** 32) - 1); + // 18. Let A be ! ArrayCreate(n + 1). + const A = X(ArrayCreate(new Value(n + 1))); + // 19. Assert: The value of A's "length" property is n + 1. + Assert(X(Get(A, new Value('length'))).numberValue() === n + 1); + // 20. Perform ! CreateDataPropertyOrThrow(A, "index", lastIndex). + X(CreateDataProperty(A, new Value('index'), lastIndex)); + // 21. Perform ! CreateDataPropertyOrThrow(A, "input", S). + X(CreateDataProperty(A, new Value('input'), S)); + const capturingParens = R.parsedRegExp.capturingParens; + // https://tc39.es/proposal-regexp-match-indices/#sec-regexpbuiltinexec + if (surroundingAgent.feature('RegExpMatchIndices')) { + // Let indices be a new empty List. + const indices = []; + // Let match be the Match { [[StartIndex]]: lastIndex, [[EndIndex]]: e }. + const match = new MatchRecord(lastIndex.numberValue(), e); + // Add match as the last element of indices. + indices.push(match); + // Let matchedValue be ! GetMatchString(S, match). + const matchedValue = X(GetMatchString(S, match)); + // Perform ! CreateDataProperty(A, "0", matchedValue). + X(CreateDataProperty(A, new Value('0'), matchedValue)); + let groups; + let groupNames; + // If R contains any GroupName, then + if (R.parsedRegExp.groupSpecifiers.size > 0) { + // Let groups be OrdinaryObjectCreate(null). + groups = OrdinaryObjectCreate(Value.null); + // Let groupNames be a new empty List. + groupNames = []; + // TODO: add this to spec text. + groupNames.push(Value.undefined); + } else { // Else, + // Let groups be undefined. + groups = Value.undefined; + // Let groupNames be undefined. + groupNames = Value.undefined; + } + // Perform ! CreateDataPropertyOrThrow(A, "groups", groups). + X(CreateDataProperty(A, new Value('groups'), groups)); + // For each integer i such that i > 0 and i ≤ n, do + for (let i = 1; i <= n; i += 1) { + // Let captureI be ith element of r's captures List. + const captureI = r.captures[i]; + let capturedValue; + // If captureI is undefined, then + if (captureI === Value.undefined) { + // Let capturedValue be undefined. + capturedValue = Value.undefined; + // Add undefined as the last element of indices. + indices.push(Value.undefined); + } else { // Else, + // Let captureStart be captureI's startIndex. + let captureStart = captureI.startIndex; + // Let captureEnd be captureI's endIndex. + let captureEnd = captureI.endIndex; + // If fullUnicode is true, then + if (fullUnicode) { + // Set captureStart to ! GetStringIndex(S, Input, captureStart). + captureStart = X(GetStringIndex(S, Input, captureStart)); + // Set captureEnd to ! GetStringIndex(S, Input, captureEnd). + captureEnd = X(GetStringIndex(S, Input, captureEnd)); + } + // Let capture be the Match { [[StartIndex]]: captureStart, [[EndIndex]:: captureEnd }. + const capture = new MatchRecord(captureStart, captureEnd); + // Append capture to indices. + indices.push(capture); + // Let capturedValue be ! GetMatchString(S, capture). + capturedValue = X(GetMatchString(S, capture)); + } + // Perform ! CreateDataPropertyOrThrow(A, ! ToString(i), capturedValue). + X(CreateDataProperty(A, X(ToString(new Value(i))), capturedValue)); + // If the ith capture of R was defined with a GroupName, then + if (capturingParens[i - 1].GroupSpecifier) { + // Let s be the StringValue of the corresponding RegExpIdentifierName. + const s = new Value(capturingParens[i - 1].GroupSpecifier); + // Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue). + X(CreateDataProperty(groups, s, capturedValue)); + // Assert: groupNames is a List. + Assert(Array.isArray(groupNames)); + // Append s to groupNames. + groupNames.push(s); + } else { // Else, + // If groupNames is a List, append undefined to groupNames. + if (Array.isArray(groupNames)) { + groupNames.push(Value.undefined); + } + } + } + // Let indicesArray be MakeIndicesArray(S, indices, groupNames). + const indicesArray = MakeIndicesArray(S, indices, groupNames); + // Perform ! CreateDataProperty(A, "indices", indicesArray). + X(CreateDataProperty(A, new Value('indices'), indicesArray)); + } else { + // 22. Let matchedSubstr be the matched substring (i.e. the portion of S between offset lastIndex inclusive and offset e exclusive). + const matchedSubstr = S.stringValue().substring(lastIndex.numberValue(), e); + // 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr). + X(CreateDataProperty(A, new Value('0'), new Value(matchedSubstr))); + let groups; + // 24. If R contains any GroupName, then + if (R.parsedRegExp.groupSpecifiers.size > 0) { + // a. Let groups be OrdinaryObjectCreate(null). + groups = OrdinaryObjectCreate(Value.null); + } else { // 25. Else, + // a. Let groups be undefined. + groups = Value.undefined; + } + // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). + X(CreateDataProperty(A, new Value('groups'), groups)); + // 27. For each integer i such that i > 0 and i ≤ n, do + for (let i = 1; i <= n; i += 1) { + // a. Let captureI be ith element of r's captures List. + const captureI = r.captures[i]; + let capturedValue; + // b. If captureI is undefined, let capturedValue be undefined. + if (captureI === Value.undefined) { + capturedValue = Value.undefined; + } else if (fullUnicode) { // c. Else if fullUnicode is true, then + // i. Assert: captureI is a List of code points. + // ii. Let capturedValue be ! UTF16Encode(captureI). + capturedValue = new Value(captureI.join('')); + } else { // d. Else, + // i. Assert: fullUnicode is false. + Assert(fullUnicode === false); + // ii. Assert: captureI is a List of code units. + // iii. Let capturedValue be the String value consisting of the code units of captureI. + capturedValue = new Value(captureI.join('')); + } + // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(i), capturedValue). + X(CreateDataProperty(A, X(ToString(new Value(i))), capturedValue)); + // f. If the ith capture of R was defined with a GroupName, then + if (capturingParens[i - 1].GroupSpecifier) { + // i. Let s be the StringValue of the corresponding RegExpIdentifierName. + const s = new Value(capturingParens[i - 1].GroupSpecifier); + // ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue). + X(CreateDataProperty(groups, s, capturedValue)); + } + } + } + // 28. Return A. + return A; +} + +// 21.2.5.2.3 #sec-advancestringindex +export function AdvanceStringIndex(S, index, unicode) { + Assert(Type(S) === 'String'); + index = index.numberValue(); + Assert(Number.isInteger(index) && index >= 0 && index <= (2 ** 53) - 1); + Assert(Type(unicode) === 'Boolean'); + + if (unicode === Value.false) { + return new Value(index + 1); + } + + const length = S.stringValue().length; + if (index + 1 >= length) { + return new Value(index + 1); + } + + const cp = X(CodePointAt(S, index)); + return new Value(index + cp.CodeUnitCount.numberValue()); +} + +// https://tc39.es/proposal-regexp-match-indices/#sec-getstringindex +function GetStringIndex(S, Input, e) { + // 1. Assert: Type(S) is String. + Assert(Type(S) === 'String'); + // 2. Assert: Input is a List of the code points of S interpreted as a UTF-16 encoded string. + Assert(Array.isArray(Input)); + // 3. Assert: e is an integer value ≥ 0 and < the number of elements in Input. + // TODO: fix spec text. + Assert(e >= 0/* && e < Input.length */); + // 4. Let eUTF be the smallest index into S that corresponds to the character at element e of Input. + // If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S. + let eUTF = 0; + if (e >= Input.length) { + eUTF = S.stringValue().length; + } else { + for (let i = 0; i < e; i += 1) { + eUTF += Input[i].length; + } + } + // 5. Return eUTF. + return eUTF; +} + +// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchstring +function GetMatchString(S, match) { + // 1. Assert: Type(S) is String. + Assert(Type(S) === 'String'); + // 2. Assert: match is a Match Record. + Assert(match instanceof MatchRecord); + // 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S. + // TODO: fix spec text. + Assert(Number.isInteger(match.StartIndex) && match.StartIndex >= 0 && match.StartIndex <= S.stringValue().length); + // 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S. + Assert(Number.isInteger(match.EndIndex) && match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length); + // 5. Return the portion of S between offset match.[[StartIndex]] inclusive and offset match.[[EndIndex]] exclusive. + return new Value(S.stringValue().slice(match.StartIndex, match.EndIndex)); +} + +// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchindicesarray +function GetMatchIndicesArray(S, match) { + // 1. Assert: Type(S) is String. + Assert(Type(S) === 'String'); + // 2. Assert: match is a Match Record. + Assert(match instanceof MatchRecord); + // 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S. + // TODO: fix spect text. + Assert(Number.isInteger(match.StartIndex) && match.StartIndex >= 0 && match.StartIndex <= S.stringValue().length); + // 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S. + Assert(Number.isInteger(match.EndIndex) && match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length); + // 5. Return CreateArrayFromList(« match.[[StartIndex]], match.[[EndIndex]] »). + return CreateArrayFromList([new Value(match.StartIndex), new Value(match.EndIndex)]); +} + +// https://tc39.es/proposal-regexp-match-indices/#sec-makeindicesarray +function MakeIndicesArray(S, indices, groupNames) { + // 1. Assert: Type(S) is String. + Assert(Type(S) === 'String'); + // 2. Assert: indices is a List. + Assert(Array.isArray(indices)); + // 3. Assert: groupNames is a List or is undefined. + Assert(Array.isArray(groupNames) || groupNames === Value.undefined); + // 4. Let n be the number of elements in indices. + const n = indices.length; + // 5. Assert: n < 2^32-1. + Assert(n < 2 ** 32 - 1); + // 6. Set A to ! ArrayCreate(n). + const A = X(ArrayCreate(new Value(n))); + // 7. Assert: The value of A's "length" property is n. + Assert(X(Get(A, new Value('length'))).numberValue() === n); + let groups; + // 8. If groupNames is not undefined, then + if (groupNames !== Value.undefined) { + // a. Let groups be ! ObjectCreate(null). + groups = X(OrdinaryObjectCreate(Value.null)); + } else { // 9. Else, + // b. Let groups be undefined. + groups = Value.undefined; + } + // 10. Perform ! CreateDataProperty(A, "groups", groups). + X(CreateDataProperty(A, new Value('groups'), groups)); + // 11. For each integer i such that i ≥ 0 and i < n, do + for (let i = 0; i < n; i += 1) { + // a. Let matchIndices be indices[i]. + const matchIndices = indices[i]; + let matchIndicesArray; + // b. If matchIndices is not undefined, then + if (matchIndices !== Value.undefined) { + // i. Let matchIndicesArray be ! GetMatchIndicesArray(S, matchIndices). + matchIndicesArray = X(GetMatchIndicesArray(S, matchIndices)); + } else { // c. Else, + // i. Let matchIndicesArray be undefined. + matchIndicesArray = Value.undefined; + } + // d. Perform ! CreateDataProperty(A, ! ToString(i), matchIndicesArray). + X(CreateDataProperty(A, X(ToString(new Value(i))), matchIndicesArray)); + // e. If groupNames is not undefined and groupNames[i] is not undefined, then + if (groupNames !== Value.undefined && groupNames[i] !== Value.undefined) { + // i. Perform ! CreateDataProperty(groups, groupNames[i], matchIndicesArray). + X(CreateDataProperty(groups, groupNames[i], matchIndicesArray)); + } + } + // 12. Return A. + return A; +} + +// 21.2.5.3 #sec-get-regexp.prototype.dotAll +function RegExpProto_dotAllGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('s')) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.4 #sec-get-regexp.prototype.flags +function RegExpProto_flagsGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + let result = ''; + const global = ToBoolean(Q(Get(R, new Value('global')))); + if (global === Value.true) { + result += 'g'; + } + const ignoreCase = ToBoolean(Q(Get(R, new Value('ignoreCase')))); + if (ignoreCase === Value.true) { + result += 'i'; + } + const multiline = ToBoolean(Q(Get(R, new Value('multiline')))); + if (multiline === Value.true) { + result += 'm'; + } + const dotAll = ToBoolean(Q(Get(R, new Value('dotAll')))); + if (dotAll === Value.true) { + result += 's'; + } + const unicode = ToBoolean(Q(Get(R, new Value('unicode')))); + if (unicode === Value.true) { + result += 'u'; + } + const sticky = ToBoolean(Q(Get(R, new Value('sticky')))); + if (sticky === Value.true) { + result += 'y'; + } + return new Value(result); +} + +// 21.2.5.5 #sec-get-regexp.prototype.global +function RegExpProto_globalGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('g')) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.6 #sec-get-regexp.prototype.ignorecase +function RegExpProto_ignoreCaseGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('i')) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.7 #sec-regexp.prototype-@@match +function RegExpProto_match([string = Value.undefined], { thisValue }) { + const rx = thisValue; + if (Type(rx) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', rx); + } + const S = Q(ToString(string)); + + const global = ToBoolean(Q(Get(rx, new Value('global')))); + if (global === Value.false) { + return Q(RegExpExec(rx, S)); + } else { + const fullUnicode = ToBoolean(Q(Get(rx, new Value('unicode')))); + Q(Set(rx, new Value('lastIndex'), new Value(0), Value.true)); + const A = X(ArrayCreate(new Value(0))); + let n = 0; + while (true) { + const result = Q(RegExpExec(rx, S)); + if (result === Value.null) { + if (n === 0) { + return Value.null; + } + return A; + } else { + const matchStr = Q(ToString(Q(Get(result, new Value('0'))))); + const status = CreateDataProperty(A, X(ToString(new Value(n))), matchStr); + Assert(status === Value.true); + if (matchStr.stringValue() === '') { + const thisIndex = Q(ToLength(Q(Get(rx, new Value('lastIndex'))))); + const nextIndex = AdvanceStringIndex(S, thisIndex, fullUnicode); + Q(Set(rx, new Value('lastIndex'), nextIndex, Value.true)); + } + n += 1; + } + } + } +} + +// 21.2.5.8 #sec-regexp-prototype-matchall +function RegExpProto_matchAll([string = Value.undefined], { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const S = Q(ToString(string)); + const C = Q(SpeciesConstructor(R, surroundingAgent.intrinsic('%RegExp%'))); + const flags = Q(ToString(Q(Get(R, new Value('flags'))))); + const matcher = Q(Construct(C, [R, flags])); + const lastIndex = Q(ToLength(Q(Get(R, new Value('lastIndex'))))); + Q(Set(matcher, new Value('lastIndex'), lastIndex, Value.true)); + let global; + if (flags.stringValue().includes('g')) { + global = Value.true; + } else { + global = Value.false; + } + let fullUnicode; + if (flags.stringValue().includes('u')) { + fullUnicode = Value.true; + } else { + fullUnicode = Value.false; + } + return X(CreateRegExpStringIterator(matcher, S, global, fullUnicode)); +} + +// 21.2.5.9 #sec-get-regexp.prototype.multiline +function RegExpProto_multilineGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('m')) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.10 #sec-regexp.prototype-@@replace +function RegExpProto_replace([string = Value.undefined, replaceValue = Value.undefined], { thisValue }) { + const rx = thisValue; + if (Type(rx) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', rx); + } + const S = Q(ToString(string)); + const lengthS = S.stringValue().length; + const functionalReplace = IsCallable(replaceValue); + if (functionalReplace === Value.false) { + replaceValue = Q(ToString(replaceValue)); + } + const global = ToBoolean(Q(Get(rx, new Value('global')))); + let fullUnicode; + if (global === Value.true) { + fullUnicode = ToBoolean(Q(Get(rx, new Value('unicode')))); + Q(Set(rx, new Value('lastIndex'), new Value(0), Value.true)); + } + + const results = []; + let done = false; + while (!done) { + const result = Q(RegExpExec(rx, S)); + if (result === Value.null) { + done = true; + } else { + results.push(result); + if (global === Value.false) { + done = true; + } else { + const matchStr = Q(ToString(Q(Get(result, new Value('0'))))); + if (matchStr.stringValue() === '') { + const thisIndex = Q(ToLength(Q(Get(rx, new Value('lastIndex'))))); + const nextIndex = AdvanceStringIndex(S, thisIndex, fullUnicode); + Q(Set(rx, new Value('lastIndex'), nextIndex, Value.true)); + } + } + } + } + + let accumulatedResult = ''; + let nextSourcePosition = 0; + for (const result of results) { + let nCaptures = Q(LengthOfArrayLike(result)).numberValue(); + nCaptures = Math.max(nCaptures - 1, 0); + + const matched = Q(ToString(Q(Get(result, new Value('0'))))); + const matchLength = matched.stringValue().length; + + let position = Q(ToInteger(Q(Get(result, new Value('index'))))); + position = new Value(Math.max(Math.min(position.numberValue(), lengthS), 0)); + + let n = 1; + const captures = []; + while (n <= nCaptures) { + let capN = Q(Get(result, X(ToString(new Value(n))))); + if (capN !== Value.undefined) { + capN = Q(ToString(capN)); + } + captures.push(capN); + n += 1; + } + + let namedCaptures = Q(Get(result, new Value('groups'))); + + let replacement; + if (functionalReplace === Value.true) { + const replacerArgs = [matched]; + replacerArgs.push(...captures); + replacerArgs.push(position, S); + if (namedCaptures !== Value.undefined) { + replacerArgs.push(namedCaptures); + } + const replValue = Q(Call(replaceValue, Value.undefined, replacerArgs)); + replacement = Q(ToString(replValue)); + } else { + if (namedCaptures !== Value.undefined) { + namedCaptures = Q(ToObject(namedCaptures)); + } + replacement = Q(GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue)); + } + + if (position.numberValue() >= nextSourcePosition) { + accumulatedResult = accumulatedResult + S.stringValue().substring(nextSourcePosition, position.numberValue()) + replacement.stringValue(); + nextSourcePosition = position.numberValue() + matchLength; + } + } + + if (nextSourcePosition >= lengthS) { + return new Value(accumulatedResult); + } + + return new Value(accumulatedResult + S.stringValue().substring(nextSourcePosition)); +} + +// 21.2.5.11 #sec-regexp.prototype-@@search +function RegExpProto_search([string = Value.undefined], { thisValue }) { + const rx = thisValue; + if (Type(rx) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', rx); + } + const S = Q(ToString(string)); + + const previousLastIndex = Q(Get(rx, new Value('lastIndex'))); + if (SameValue(previousLastIndex, new Value(0)) === Value.false) { + Q(Set(rx, new Value('lastIndex'), new Value(0), Value.true)); + } + + const result = Q(RegExpExec(rx, S)); + const currentLastIndex = Q(Get(rx, new Value('lastIndex'))); + if (SameValue(currentLastIndex, previousLastIndex) === Value.false) { + Q(Set(rx, new Value('lastIndex'), previousLastIndex, Value.true)); + } + + if (result === Value.null) { + return new Value(-1); + } + + return Q(Get(result, new Value('index'))); +} + +// 21.2.5.12 #sec-get-regexp.prototype.source +function RegExpProto_sourceGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalSource' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return new Value('(?:)'); + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + Assert('OriginalFlags' in R); + const src = R.OriginalSource; + const flags = R.OriginalFlags; + return EscapeRegExpPattern(src, flags); +} + +// 21.2.5.13 #sec-regexp.prototype-@@split +function RegExpProto_split([string = Value.undefined, limit = Value.undefined], { thisValue }) { + const rx = thisValue; + if (Type(rx) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', rx); + } + const S = Q(ToString(string)); + + const C = Q(SpeciesConstructor(rx, surroundingAgent.intrinsic('%RegExp%'))); + const flagsValue = Q(Get(rx, new Value('flags'))); + const flags = Q(ToString(flagsValue)).stringValue(); + const unicodeMatching = flags.includes('u') ? Value.true : Value.false; + const newFlags = flags.includes('y') ? new Value(flags) : new Value(`${flags}y`); + const splitter = Q(Construct(C, [rx, newFlags])); + + const A = X(ArrayCreate(new Value(0))); + let lengthA = 0; + + let lim; + if (limit === Value.undefined) { + lim = (2 ** 32) - 1; + } else { + lim = Q(ToUint32(limit)).numberValue(); + } + + const size = S.stringValue().length; + let p = 0; + + if (lim === 0) { + return A; + } + + if (size === 0) { + const z = Q(RegExpExec(splitter, S)); + if (z !== Value.null) { + return A; + } + X(CreateDataProperty(A, new Value('0'), S)); + return A; + } + + let q = new Value(p); + while (q.numberValue() < size) { + Q(Set(splitter, new Value('lastIndex'), q, Value.true)); + const z = Q(RegExpExec(splitter, S)); + if (z === Value.null) { + q = AdvanceStringIndex(S, q, unicodeMatching); + } else { + const lastIndex = Q(Get(splitter, new Value('lastIndex'))); + let e = Q(ToLength(lastIndex)); + e = new Value(Math.min(e.numberValue(), size)); + if (e.numberValue() === p) { + q = AdvanceStringIndex(S, q, unicodeMatching); + } else { + const T = new Value(S.stringValue().substring(p, q.numberValue())); + X(CreateDataProperty(A, X(ToString(new Value(lengthA))), T)); + lengthA += 1; + if (lengthA === lim) { + return A; + } + p = e.numberValue(); + let numberOfCaptures = Q(LengthOfArrayLike(z)).numberValue(); + numberOfCaptures = Math.max(numberOfCaptures - 1, 0); + let i = 1; + while (i <= numberOfCaptures) { + const nextCapture = Q(Get(z, X(ToString(new Value(i))))); + X(CreateDataProperty(A, X(ToString(new Value(lengthA))), nextCapture)); + i += 1; + lengthA += 1; + if (lengthA === lim) { + return A; + } + } + q = new Value(p); + } + } + } + + const T = new Value(S.stringValue().substring(p, size)); + X(CreateDataProperty(A, X(ToString(new Value(lengthA))), T)); + return A; +} + +// 21.2.5.14 #sec-get-regexp.prototype.sticky +function RegExpProto_stickyGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('y')) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.15 #sec-regexp.prototype.test +function RegExpProto_test([S = Value.undefined], { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const string = Q(ToString(S)); + const match = Q(RegExpExec(R, string)); + if (match !== Value.null) { + return Value.true; + } + return Value.false; +} + +// 21.2.5.16 #sec-regexp.prototype.tostring +function RegExpProto_toString(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const pattern = Q(ToString(Q(Get(R, new Value('source'))))); + const flags = Q(ToString(Q(Get(R, new Value('flags'))))); + const result = `/${pattern.stringValue()}/${flags.stringValue()}`; + return new Value(result); +} + +// 21.2.5.17 #sec-get-regexp.prototype.unicode +function RegExpProto_unicodeGetter(args, { thisValue }) { + const R = thisValue; + if (Type(R) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + if (!('OriginalFlags' in R)) { + if (SameValue(R, surroundingAgent.intrinsic('%RegExp.prototype%')) === Value.true) { + return Value.undefined; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp', R); + } + const flags = R.OriginalFlags; + if (flags.stringValue().includes('u')) { + return Value.true; + } + return Value.false; +} + +export function BootstrapRegExpPrototype(realmRec) { + const proto = BootstrapPrototype( + realmRec, + [ + ['exec', RegExpProto_exec, 1], + ['dotAll', [RegExpProto_dotAllGetter]], + ['flags', [RegExpProto_flagsGetter]], + ['global', [RegExpProto_globalGetter]], + ['ignoreCase', [RegExpProto_ignoreCaseGetter]], + [wellKnownSymbols.match, RegExpProto_match, 1], + [wellKnownSymbols.matchAll, RegExpProto_matchAll, 1], + ['multiline', [RegExpProto_multilineGetter]], + [wellKnownSymbols.replace, RegExpProto_replace, 2], + [wellKnownSymbols.search, RegExpProto_search, 1], + ['source', [RegExpProto_sourceGetter]], + [wellKnownSymbols.split, RegExpProto_split, 2], + ['sticky', [RegExpProto_stickyGetter]], + ['test', RegExpProto_test, 1], + ['toString', RegExpProto_toString, 0], + ['unicode', [RegExpProto_unicodeGetter]], + ], + realmRec.Intrinsics['%Object.prototype%'], + ); + + realmRec.Intrinsics['%RegExp.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/RegExpStringIteratorPrototype.mjs b/src/engine262/src/intrinsics/RegExpStringIteratorPrototype.mjs new file mode 100644 index 0000000..4a8fe6d --- /dev/null +++ b/src/engine262/src/intrinsics/RegExpStringIteratorPrototype.mjs @@ -0,0 +1,81 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { + Assert, + OrdinaryObjectCreate, + CreateIterResultObject, + ToString, + ToLength, + Get, + Set, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { RegExpExec, AdvanceStringIndex } from './RegExpPrototype.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +// 21.2.5.8.1 #sec-createregexpstringiterator +export function CreateRegExpStringIterator(R, S, global, fullUnicode) { + Assert(Type(S) === 'String'); + Assert(Type(global) === 'Boolean'); + Assert(Type(fullUnicode) === 'Boolean'); + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%RegExpStringIteratorPrototype%'), [ + 'IteratingRegExp', + 'IteratedString', + 'Global', + 'Unicode', + 'Done', + ]); + iterator.IteratingRegExp = R; + iterator.IteratedString = S; + iterator.Global = global; + iterator.Unicode = fullUnicode; + iterator.Done = Value.false; + return iterator; +} + +// 21.2.7.1.1 #sec-%regexpstringiteratorprototype%.next +function RegExpStringIteratorPrototype_next(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp String Iterator', O); + } + if (!('IteratingRegExp' in O && 'IteratedString' in O && 'Global' in O && 'Unicode' in O && 'Done' in O)) { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'RegExp String Iterator', O); + } + if (O.Done === Value.true) { + return X(CreateIterResultObject(Value.undefined, Value.true)); + } + const R = O.IteratingRegExp; + const S = O.IteratedString; + const global = O.Global; + const fullUnicode = O.Unicode; + const match = Q(RegExpExec(R, S)); + if (match === Value.null) { + O.Done = Value.true; + return X(CreateIterResultObject(Value.undefined, Value.true)); + } else { + if (global === Value.true) { + const matchStrValue = Q(Get(match, new Value('0'))); + const matchStr = Q(ToString(matchStrValue)); + if (matchStr.stringValue() === '') { + const thisIndexValue = Q(Get(R, new Value('lastIndex'))); + const thisIndex = Q(ToLength(thisIndexValue)); + const nextIndex = X(AdvanceStringIndex(S, thisIndex, fullUnicode)); + Q(Set(R, new Value('lastIndex'), nextIndex, Value.true)); + } + return Q(CreateIterResultObject(match, Value.false)); + } else { + O.Done = Value.true; + return Q(CreateIterResultObject(match, Value.false)); + } + } +} + +export function BootstrapRegExpStringIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', RegExpStringIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'RegExp String Iterator'); + + realmRec.Intrinsics['%RegExpStringIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Set.mjs b/src/engine262/src/intrinsics/Set.mjs new file mode 100644 index 0000000..b806265 --- /dev/null +++ b/src/engine262/src/intrinsics/Set.mjs @@ -0,0 +1,54 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Call, + Get, + GetIterator, + IsCallable, + IteratorClose, + IteratorStep, + IteratorValue, + OrdinaryCreateFromConstructor, +} from '../abstract-ops/all.mjs'; +import { Value, wellKnownSymbols } from '../value.mjs'; +import { AbruptCompletion, Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + + +function SetConstructor([iterable = Value.undefined], { NewTarget }) { + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + const set = Q(OrdinaryCreateFromConstructor(NewTarget, '%Set.prototype%', ['SetData'])); + set.SetData = []; + if (iterable === Value.undefined || iterable === Value.null) { + return set; + } + const adder = Q(Get(set, new Value('add'))); + if (IsCallable(adder) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', adder); + } + const iteratorRecord = Q(GetIterator(iterable)); + while (true) { + const next = Q(IteratorStep(iteratorRecord)); + if (next === Value.false) { + return set; + } + const nextValue = Q(IteratorValue(next)); + const status = Call(adder, set, [nextValue]); + if (status instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, status)); + } + } +} + +function Set_speciesGetter(args, { thisValue }) { + return thisValue; +} + +export function BootstrapSet(realmRec) { + const setConstructor = BootstrapConstructor(realmRec, SetConstructor, 'Set', 0, realmRec.Intrinsics['%Set.prototype%'], [ + [wellKnownSymbols.species, [Set_speciesGetter]], + ]); + + realmRec.Intrinsics['%Set%'] = setConstructor; +} diff --git a/src/engine262/src/intrinsics/SetIteratorPrototype.mjs b/src/engine262/src/intrinsics/SetIteratorPrototype.mjs new file mode 100644 index 0000000..386270a --- /dev/null +++ b/src/engine262/src/intrinsics/SetIteratorPrototype.mjs @@ -0,0 +1,48 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + CreateArrayFromList, + CreateIterResultObject, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function SetIteratorPrototype_next(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'InvalidReceiver', 'Set Iterator.prototype.next', O); + } + if (!('IteratedSet' in O && 'SetNextIndex' in O && 'SetIterationKind' in O)) { + return surroundingAgent.Throw('TypeError', 'InvalidReceiver', 'Set Iterator.prototype.next', O); + } + const s = O.IteratedSet; + let index = O.SetNextIndex; + const itemKind = O.SetIterationKind; + if (Type(s) === 'Undefined') { + return CreateIterResultObject(Value.undefined, Value.true); + } + Assert('SetData' in s); + const entries = s.SetData; + const numEntries = entries.length; + while (index < numEntries) { + const e = entries[index]; + index += 1; + O.SetNextIndex = index; + if (e !== undefined) { + if (itemKind === 'key+value') { + return CreateIterResultObject(CreateArrayFromList([e, e]), Value.false); + } + return CreateIterResultObject(e, Value.false); + } + } + O.IteratedSet = Value.undefined; + return CreateIterResultObject(Value.undefined, Value.true); +} + +export function BootstrapSetIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', SetIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'Set Iterator'); + + realmRec.Intrinsics['%SetIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/SetPrototype.mjs b/src/engine262/src/intrinsics/SetPrototype.mjs new file mode 100644 index 0000000..920eb1b --- /dev/null +++ b/src/engine262/src/intrinsics/SetPrototype.mjs @@ -0,0 +1,138 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Call, + IsCallable, + OrdinaryObjectCreate, + SameValueZero, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// 23.2.5.1 #sec-createsetiterator +function CreateSetIterator(set, kind) { + Q(RequireInternalSlot(set, 'SetData')); + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%SetIteratorPrototype%'), [ + 'IteratedSet', + 'SetNextIndex', + 'SetIterationKind', + ]); + iterator.IteratedSet = set; + iterator.SetNextIndex = 0; + iterator.SetIterationKind = kind; + return iterator; +} + +function SetProto_add([value = Value.undefined], { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + const entries = S.SetData; + for (const e of entries) { + if (e !== undefined && SameValueZero(e, value) === Value.true) { + return S; + } + } + if (Type(value) === 'Number' && Object.is(value.numberValue(), -0)) { + value = new Value(0); + } + entries.push(value); + return S; +} + +function SetProto_clear(args, { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + const entries = S.SetData; + for (let i = 0; i < entries.length; i += 1) { + entries[i] = undefined; + } + return Value.undefined; +} + +function SetProto_delete([value = Value.undefined], { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + const entries = S.SetData; + for (let i = 0; i < entries.length; i += 1) { + const e = entries[i]; + if (e !== undefined && SameValueZero(e, value) === Value.true) { + entries[i] = undefined; + return Value.true; + } + } + return Value.false; +} + +function SetProto_entries(args, { thisValue }) { + const S = thisValue; + return Q(CreateSetIterator(S, 'key+value')); +} + +function SetProto_forEach([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + const entries = S.SetData; + for (const e of entries) { + if (e !== undefined) { + Q(Call(callbackfn, thisArg, [e, e, S])); + } + } + return Value.undefined; +} + +function SetProto_has([value = Value.undefined], { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + const entries = S.SetData; + for (const e of entries) { + if (e !== undefined && SameValueZero(e, value) === Value.true) { + return Value.true; + } + } + return Value.false; +} + +function SetProto_values(args, { thisValue }) { + const S = thisValue; + return Q(CreateSetIterator(S, 'value')); +} + +function SetProto_sizeGetter(args, { thisValue }) { + const S = thisValue; + Q(RequireInternalSlot(S, 'SetData')); + const entries = S.SetData; + let count = 0; + for (const e of entries) { + if (e !== undefined) { + count += 1; + } + } + return new Value(count); +} + +export function BootstrapSetPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['add', SetProto_add, 1], + ['clear', SetProto_clear, 0], + ['delete', SetProto_delete, 1], + ['entries', SetProto_entries, 0], + ['forEach', SetProto_forEach, 1], + ['has', SetProto_has, 1], + ['size', [SetProto_sizeGetter]], + ['values', SetProto_values, 0], + ], realmRec.Intrinsics['%Object.prototype%'], 'Set'); + + const valuesFunc = X(proto.GetOwnProperty(new Value('values'))); + X(proto.DefineOwnProperty(new Value('keys'), valuesFunc)); + X(proto.DefineOwnProperty(wellKnownSymbols.iterator, valuesFunc)); + + realmRec.Intrinsics['%Set.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/String.mjs b/src/engine262/src/intrinsics/String.mjs new file mode 100644 index 0000000..22f532e --- /dev/null +++ b/src/engine262/src/intrinsics/String.mjs @@ -0,0 +1,114 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Get, + GetPrototypeFromConstructor, + IsInteger, + StringCreate, + SymbolDescriptiveString, + LengthOfArrayLike, + ToNumber, + ToObject, + ToString, + ToUint16, + UTF16Encoding, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// 21.1.1.1 #sec-string-constructor-string-value +function StringConstructor([value], { NewTarget }) { + let s; + if (value === undefined) { + s = new Value(''); + } else { + if (NewTarget === Value.undefined && Type(value) === 'Symbol') { + return X(SymbolDescriptiveString(value)); + } + s = Q(ToString(value)); + } + if (NewTarget === Value.undefined) { + return s; + } + return X(StringCreate(s, Q(GetPrototypeFromConstructor(NewTarget, '%String.prototype%')))); +} + +// 21.1.2.1 #sec-string.fromcharcode +function String_fromCharCode(codeUnits) { + const length = codeUnits.length; + const elements = []; + let nextIndex = 0; + while (nextIndex < length) { + const next = codeUnits[nextIndex]; + const nextCU = Q(ToUint16(next)); + elements.push(nextCU); + nextIndex += 1; + } + const result = elements.reduce((previous, current) => previous + String.fromCharCode(current.numberValue()), ''); + return new Value(result); +} + +// 21.1.2.2 #sec-string.fromcodepoint +function String_fromCodePoint(codePoints) { + const length = codePoints.length; + const elements = []; + let nextIndex = 0; + while (nextIndex < length) { + const next = codePoints[nextIndex]; + const nextCP = Q(ToNumber(next)); + if (X(IsInteger(nextCP)) === Value.false) { + return surroundingAgent.Throw('RangeError', 'StringCodePointInvalid', next); + } + if (nextCP.numberValue() < 0 || nextCP.numberValue() > 0x10FFFF) { + return surroundingAgent.Throw('RangeError', 'StringCodePointInvalid', nextCP); + } + elements.push(...UTF16Encoding(nextCP.numberValue())); + nextIndex += 1; + } + const result = elements.reduce((previous, current) => previous + String.fromCharCode(current), ''); + return new Value(result); +} + +// 21.1.2.4 #sec-string.raw +function String_raw([template = Value.undefined, ...substitutions]) { + const numberOfSubstitutions = substitutions.length; + const cooked = Q(ToObject(template)); + const raw = Q(ToObject(Q(Get(cooked, new Value('raw'))))); + const literalSegments = Q(LengthOfArrayLike(raw)).numberValue(); + if (literalSegments <= 0) { + return new Value(''); + } + // Not sure why the spec uses a List, but this is really just a String. + const stringElements = []; + let nextIndex = 0; + while (true) { + const nextKey = X(ToString(new Value(nextIndex))); + const nextSeg = Q(ToString(Q(Get(raw, nextKey)))); + stringElements.push(nextSeg.stringValue()); + if (nextIndex + 1 === literalSegments) { + return new Value(stringElements.join('')); + } + let next; + if (nextIndex < numberOfSubstitutions) { + next = substitutions[nextIndex]; + } else { + next = new Value(''); + } + const nextSub = Q(ToString(next)); + stringElements.push(nextSub.stringValue()); + nextIndex += 1; + } +} + +export function BootstrapString(realmRec) { + const stringConstructor = BootstrapConstructor(realmRec, StringConstructor, 'String', 1, realmRec.Intrinsics['%String.prototype%'], [ + ['fromCharCode', String_fromCharCode, 1], + ['fromCodePoint', String_fromCodePoint, 1], + ['raw', String_raw, 1], + ]); + + realmRec.Intrinsics['%String%'] = stringConstructor; +} diff --git a/src/engine262/src/intrinsics/StringIteratorPrototype.mjs b/src/engine262/src/intrinsics/StringIteratorPrototype.mjs new file mode 100644 index 0000000..708b1a3 --- /dev/null +++ b/src/engine262/src/intrinsics/StringIteratorPrototype.mjs @@ -0,0 +1,55 @@ +import { Type, Value } from '../value.mjs'; +import { + Assert, + CodePointAt, + CreateIterResultObject, + OrdinaryObjectCreate, +} from '../abstract-ops/all.mjs'; +import { X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + + +// 21.1.5.1 #sec-createstringiterator +export function CreateStringIterator(string) { + Assert(Type(string) === 'String'); + const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%StringIteratorPrototype%'), [ + 'IteratedString', + 'StringNextIndex', + ]); + iterator.IteratedString = string; + iterator.StringNextIndex = 0; + return iterator; +} + +function StringIteratorPrototype_next(args, { thisValue }) { + const O = thisValue; + if (Type(O) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'String Iterator', O); + } + if (!('IteratedString' in O && 'StringNextIndex' in O)) { + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'String Iterator', O); + } + const s = O.IteratedString; + if (s === Value.undefined) { + return CreateIterResultObject(Value.undefined, Value.true); + } + const position = O.StringNextIndex; + const len = s.stringValue().length; + if (position >= len) { + O.IteratedString = Value.undefined; + return CreateIterResultObject(Value.undefined, Value.true); + } + const cp = X(CodePointAt(s, position)); + const resultString = new Value(s.stringValue().substr(position, cp.CodeUnitCount.numberValue())); + O.StringNextIndex = position + cp.CodeUnitCount.numberValue(); + return CreateIterResultObject(resultString, Value.false); +} + +export function BootstrapStringIteratorPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['next', StringIteratorPrototype_next, 0], + ], realmRec.Intrinsics['%IteratorPrototype%'], 'String Iterator'); + + realmRec.Intrinsics['%StringIteratorPrototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/StringPrototype.mjs b/src/engine262/src/intrinsics/StringPrototype.mjs new file mode 100644 index 0000000..381915d --- /dev/null +++ b/src/engine262/src/intrinsics/StringPrototype.mjs @@ -0,0 +1,750 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + ArrayCreate, + Assert, + Call, + CodePointAt, + CreateDataProperty, + Get, + GetMethod, + Invoke, + IsCallable, + IsRegExp, + RegExpCreate, + RequireObjectCoercible, + ToInteger, + ToNumber, + ToString, + ToUint32, + StringCreate, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + GetSubstitution, + StringIndexOf, + StringPad, + TrimString, +} from '../runtime-semantics/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { CreateStringIterator } from './StringIteratorPrototype.mjs'; +import { assignProps } from './Bootstrap.mjs'; + + +function thisStringValue(value) { + if (Type(value) === 'String') { + return value; + } + if (Type(value) === 'Object' && 'StringData' in value) { + const s = value.StringData; + Assert(Type(s) === 'String'); + return s; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'String', value); +} + +// 21.1.3.1 #sec-string.prototype.charat +function StringProto_charAt([pos = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const position = Q(ToInteger(pos)).numberValue(); + const size = S.stringValue().length; + if (position < 0 || position >= size) { + return new Value(''); + } + return new Value(S.stringValue()[position]); +} + +// 21.1.3.2 #sec-string.prototype.charcodeat +function StringProto_charCodeAt([pos = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const position = Q(ToInteger(pos)).numberValue(); + const size = S.stringValue().length; + if (position < 0 || position >= size) { + return new Value(NaN); + } + return new Value(S.stringValue().charCodeAt(position)); +} + +// 21.1.3.3 #sec-string.prototype.codepointat +function StringProto_codePointAt([pos = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const position = Q(ToInteger(pos)).numberValue(); + const size = S.stringValue().length; + if (position < 0 || position >= size) { + return Value.undefined; + } + const cp = X(CodePointAt(S, position)); + return cp.CodePoint; +} + +// 21.1.3.4 #sec-string.prototype.concat +function StringProto_concat(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + let R = S.stringValue(); + while (args.length > 0) { + const next = args.shift(); + const nextString = Q(ToString(next)); + R = `${R}${nextString.stringValue()}`; + } + return new Value(R); +} + +// 21.1.3.6 #sec-string.prototype.endswith +function StringProto_endsWith([searchString = Value.undefined, endPosition = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const isRegExp = Q(IsRegExp(searchString)); + if (isRegExp === Value.true) { + return surroundingAgent.Throw('TypeError', 'RegExpArgumentNotAllowed', 'String.prototype.endsWith'); + } + const searchStr = Q(ToString(searchString)).stringValue(); + const len = S.length; + let pos; + if (endPosition === Value.undefined) { + pos = len; + } else { + pos = Q(ToInteger(endPosition)).numberValue(); + } + const end = Math.min(Math.max(pos, 0), len); + const searchLength = searchStr.length; + const start = end - searchLength; + if (start < 0) { + return Value.false; + } + for (let i = 0; i < searchLength; i += 1) { + if (S.charCodeAt(start + i) !== searchStr.charCodeAt(i)) { + return Value.false; + } + } + return Value.true; +} + +// 21.1.3.7 #sec-string.prototype.includes +function StringProto_includes([searchString = Value.undefined, position = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const isRegExp = Q(IsRegExp(searchString)); + if (isRegExp === Value.true) { + return surroundingAgent.Throw('TypeError', 'RegExpArgumentNotAllowed', 'String.prototype.includes'); + } + const searchStr = Q(ToString(searchString)).stringValue(); + const pos = Q(ToInteger(position)); + Assert(!(position === Value.undefined) || pos.numberValue() === 0); + const len = S.length; + const start = Math.min(Math.max(pos.numberValue(), 0), len); + const searchLen = searchStr.length; + let k = start; + while (k + searchLen <= len) { + let match = true; + for (let j = 0; j < searchLen; j += 1) { + if (searchStr[j] !== S[k + j]) { + match = false; + break; + } + } + if (match) { + return Value.true; + } + k += 1; + } + return Value.false; +} + +// 21.1.3.8 #sec-string.prototype.indexof +function StringProto_indexOf([searchString = Value.undefined, position = Value.undefined], { thisValue }) { + // 1. Let O be ? RequireObjectCoercible(this value). + const O = Q(RequireObjectCoercible(thisValue)); + // 2. Let S be ? ToString(O). + const S = Q(ToString(O)).stringValue(); + // 3. Let searchStr be ? ToString(searchString). + const searchStr = Q(ToString(searchString)); + // 4. Let pos be ? ToInteger(position). + const pos = Q(ToInteger(position)); + // 5. Assert: If position is undefined, then pos is 0. + Assert(!(position === Value.undefined) || pos.numberValue() === 0); + // 6. Let len be the length of S. + const len = S.length; + // 7. Let start be min(max(pos, 0), len). + const start = Math.min(Math.max(pos.numberValue(), 0), len); + // 8. Let searchLen be the length of searchStr. + const searchLen = searchStr.stringValue().length; + // https://tc39.es/proposal-string-replaceall/#sec-string.prototype.indexof + if (surroundingAgent.feature('String.prototype.replaceAll')) { + // Let position be ! StringIndexOf(S, searchStr, start). + position = X(StringIndexOf(new Value(S), searchStr, start)); + // Return position. + return position; + } else { + // 9. Return the smallest possible integer k not smaller than start such that k + searchLen is not greater than len, + // and for all nonnegative integers j less than searchLen, the code unit at index k + j within S is the same as the code unit at index j within searchStr; + // but if there is no such integer k, return the value -1. + let k = start; + while (k + searchLen <= len) { + let match = true; + for (let j = 0; j < searchLen; j += 1) { + if (searchStr[j] !== S[k + j]) { + match = false; + break; + } + } + if (match) { + return new Value(k); + } + k += 1; + } + return new Value(-1); + } +} + +// 21.1.3.9 #sec-string.prototype.lastindexof +function StringProto_lastIndexOf([searchString = Value.undefined, position = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const searchStr = Q(ToString(searchString)).stringValue(); + const numPos = Q(ToNumber(position)); + Assert(!(position === Value.undefined) || numPos.isNaN()); + let pos; + if (numPos.isNaN()) { + pos = new Value(Infinity); + } else { + pos = X(ToInteger(numPos)); + } + const len = S.length; + const start = Math.min(Math.max(pos.numberValue(), 0), len); + const searchLen = searchStr.length; + let k = start; + while (k >= 0) { + if (k + searchLen <= len) { + let match = true; + for (let j = 0; j < searchLen; j += 1) { + if (searchStr[j] !== S[k + j]) { + match = false; + break; + } + } + if (match) { + return new Value(k); + } + } + k -= 1; + } + return new Value(-1); +} + +// 21.1.3.10 #sec-string.prototype.localecompare +function StringProto_localeCompare([that = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const That = Q(ToString(that)).stringValue(); + if (S === That) { + return new Value(0); + } else if (S < That) { + return new Value(-1); + } else { + return new Value(1); + } +} + +// 21.1.3.11 #sec-string.prototype.match +function StringProto_match([regexp = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + + if (regexp !== Value.undefined && regexp !== Value.null) { + const matcher = Q(GetMethod(regexp, wellKnownSymbols.match)); + if (matcher !== Value.undefined) { + return Q(Call(matcher, regexp, [O])); + } + } + + const S = Q(ToString(O)); + const rx = Q(RegExpCreate(regexp, Value.undefined)); + return Q(Invoke(rx, wellKnownSymbols.match, [S])); +} + +// 21.1.3.12 #sec-string.prototype.matchall +function StringProto_matchAll([regexp = Value.undefined], { thisValue }) { + // 1. Let O be ? RequireObjectCoercible(this value). + const O = Q(RequireObjectCoercible(thisValue)); + // 2. If regexp is neither undefined nor null, then + if (regexp !== Value.undefined && regexp !== Value.null) { + // a. Let isRegExp be ? IsRegExp(regexp). + const isRegExp = Q(IsRegExp(regexp)); + // b. If isRegExp is true, then + if (isRegExp === Value.true) { + // i. Let flags be ? Get(regexp, "flags"). + const flags = Q(Get(regexp, new Value('flags'))); + // ii. Perform ? RequireObjectCoercible(flags). + Q(RequireObjectCoercible(flags)); + // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. + if (!Q(ToString(flags)).stringValue().includes('g')) { + return surroundingAgent.Throw('TypeError', 'StringPrototypeMethodGlobalRegExp', 'matchAll'); + } + } + // c. Let matcher be ? GetMethod(regexp, @@matchAll). + const matcher = Q(GetMethod(regexp, wellKnownSymbols.matchAll)); + // d. If matcher is not undefined, then + if (matcher !== Value.undefined) { + // i. Return ? Call(matcher, regexp, « O »). + return Q(Call(matcher, regexp, [O])); + } + } + // 3. Let S be ? ToString(O). + const S = Q(ToString(O)); + // 4. Let rx be ? RegExpCreate(regexp, "g"). + const rx = Q(RegExpCreate(regexp, new Value('g'))); + // 5. Return ? Invoke(rx, @@matchAll, « S »). + return Q(Invoke(rx, wellKnownSymbols.matchAll, [S])); +} + +// 21.1.3.13 #sec-string.prototype.normalize +function StringProto_normalize([form = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + if (form === Value.undefined) { + form = new Value('NFC'); + } else { + form = Q(ToString(form)); + } + const f = form.stringValue(); + if (!['NFC', 'NFD', 'NFKC', 'NFKD'].includes(f)) { + return surroundingAgent.Throw('RangeError', 'NormalizeInvalidForm'); + } + const ns = S.stringValue().normalize(f); + return new Value(ns); +} + +// 21.1.3.14 #sec-string.prototype.padend +function StringProto_padEnd([maxLength = Value.undefined, fillString = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + return Q(StringPad(O, maxLength, fillString, 'end')); +} + +// 21.1.3.15 #sec-string.prototype.padstart +function StringProto_padStart([maxLength = Value.undefined, fillString = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + return Q(StringPad(O, maxLength, fillString, 'start')); +} + +// 21.1.3.16 #sec-string.prototype.repeat +function StringProto_repeat([count = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const n = Q(ToInteger(count)); + if (n.numberValue() < 0) { + return surroundingAgent.Throw('RangeError', 'StringRepeatCount', n); + } + if (n.isInfinity()) { + return surroundingAgent.Throw('RangeError', 'StringRepeatCount', n); + } + if (n.numberValue() === 0) { + return new Value(''); + } + let T = ''; + for (let i = 0; i < n.numberValue(); i += 1) { + T += S.stringValue(); + } + return new Value(T); +} + +// 21.1.3.17 #sec-string.prototype.replace +function StringProto_replace([searchValue = Value.undefined, replaceValue = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + if (searchValue !== Value.undefined && searchValue !== Value.null) { + const replacer = Q(GetMethod(searchValue, wellKnownSymbols.replace)); + if (replacer !== Value.undefined) { + return Q(Call(replacer, searchValue, [O, replaceValue])); + } + } + const string = Q(ToString(O)); + const searchString = Q(ToString(searchValue)); + const functionalReplace = IsCallable(replaceValue); + if (functionalReplace === Value.false) { + replaceValue = Q(ToString(replaceValue)); + } + const pos = new Value(string.stringValue().indexOf(searchString.stringValue())); + const matched = searchString; + if (pos.numberValue() === -1) { + return string; + } + let replStr; + if (functionalReplace === Value.true) { + const replValue = Q(Call(replaceValue, Value.undefined, [matched, pos, string])); + replStr = Q(ToString(replValue)); + } else { + const captures = []; + replStr = X(GetSubstitution(matched, string, pos, captures, Value.undefined, replaceValue)); + } + const tailPos = pos.numberValue() + matched.stringValue().length; + const newString = string.stringValue().slice(0, pos.numberValue()) + replStr.stringValue() + string.stringValue().slice(tailPos); + return new Value(newString); +} + +// https://tc39.es/proposal-string-replaceall/#sec-string.prototype.replaceall +function StringProto_replaceAll([searchValue = Value.undefined, replaceValue = Value.undefined], { thisValue }) { + // 1. Let O be ? RequireObjectCoercible(this value). + const O = Q(RequireObjectCoercible(thisValue)); + // 2.If searchValue is neither undefined nor null, then + if (searchValue !== Value.undefined && searchValue !== Value.null) { + // a. Let isRegExp be ? IsRegExp(searchValue). + const isRegExp = Q(IsRegExp(searchValue)); + // b. If isRegExp is true, then + if (isRegExp === Value.true) { + // i. Let flags be ? Get(searchValue, "flags"). + const flags = Q(Get(searchValue, new Value('flags'))); + // ii. Perform ? RequireObjectCoercible(flags). + Q(RequireObjectCoercible(flags)); + // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. + if (!Q(ToString(flags)).stringValue().includes('g')) { + return surroundingAgent.Throw('TypeError', 'StringPrototypeMethodGlobalRegExp', 'replaceAll'); + } + } + // c. Let replacer be ? GetMethod(searchValue, @@replace). + const replacer = Q(GetMethod(searchValue, wellKnownSymbols.replace)); + // d. If replacer is not undefined, then + if (replacer !== Value.undefined) { + // i. Return ? Call(replacer, searchValue, « O, replaceValue »). + return Q(Call(replacer, searchValue, [O, replaceValue])); + } + } + // 3. Let string be ? ToString(O). + const string = Q(ToString(O)); + // 4. Let searchString be ? ToString(searchValue). + const searchString = Q(ToString(searchValue)); + // 5. Let functionalReplace be IsCallable(replaceValue). + const functionalReplace = IsCallable(replaceValue); + // 6. If functionalReplace is false, then + if (functionalReplace === Value.false) { + // a. Let replaceValue be ? ToString(replaceValue). + replaceValue = Q(ToString(replaceValue)); + } + // 7. Let searchLength be the length of searchString. + const searchLength = searchString.stringValue().length; + // 8. Let advanceBy be max(1, searchLength). + const advanceBy = Math.max(1, searchLength); + // 9. Let matchPositions be a new empty List. + const matchPositions = []; + // 10. Let position be ! StringIndexOf(string, searchString, 0). + let position = X(StringIndexOf(string, searchString, 0)).numberValue(); + // 11. Repeat, while position is not -1 + while (position !== -1) { + // a. Append position to the end of matchPositions. + matchPositions.push(position); + // b. Let position be ! StringIndexOf(string, searchString, position + advanceBy). + position = X(StringIndexOf(string, searchString, position + advanceBy)).numberValue(); + } + // 12. Let endOfLastMatch be 0. + let endOfLastMatch = 0; + // 13. Let result be the empty string value. + let result = ''; + // 14. For each position in matchPositions, do + for (position of matchPositions) { + let replacement; + // a. If functionalReplace is true, then + if (functionalReplace === Value.true) { + // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, position, string »). + replacement = Q(ToString(Q(Call(replaceValue, Value.undefined, [searchString, new Value(position), string])))); + } else { // b. Else, + // i. Assert: Type(replaceValue) is String. + Assert(Type(replaceValue) === 'String'); + // ii. Let captures be a new empty List. + const captures = []; + // iii. Let replacement be GetSubstitution(searchString, string, position, captures, undefined, replaceValue). + replacement = GetSubstitution(searchString, string, new Value(position), captures, Value.undefined, replaceValue); + } + // c. Let stringSlice be the substring of string consisting of the code units from endOfLastMatch (inclusive) up through position (exclusive). + const stringSlice = string.stringValue().slice(endOfLastMatch, position); + // d. Let result be the string-concatenation of result, stringSlice, and replacement. + result = result + stringSlice + replacement.stringValue(); + // e. Let endOfLastMatch be position + searchLength. + endOfLastMatch = position + searchLength; + } + // 15. If endOfLastMatch < the length of string, then + if (endOfLastMatch < string.stringValue().length) { + // a. Let result be the string-concatenation of result and the substring of string consisting of the code units from endOfLastMatch (inclusive) up through the final code unit of string (inclusive). + result += string.stringValue().slice(endOfLastMatch); + } + // 16. Return result. + return new Value(result); +} + +// 21.1.3.19 #sec-string.prototype.slice +function StringProto_search([regexp = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + + if (regexp !== Value.undefined && regexp !== Value.null) { + const searcher = Q(GetMethod(regexp, wellKnownSymbols.search)); + if (searcher !== Value.undefined) { + return Q(Call(searcher, regexp, [O])); + } + } + + const string = Q(ToString(O)); + const rx = Q(RegExpCreate(regexp, Value.undefined)); + return Q(Invoke(rx, wellKnownSymbols.search, [string])); +} + +// 21.1.3.19 #sec-string.prototype.slice +function StringProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const len = S.length; + const intStart = Q(ToInteger(start)).numberValue(); + let intEnd; + if (end === Value.undefined) { + intEnd = len; + } else { + intEnd = Q(ToInteger(end)).numberValue(); + } + let from; + if (intStart < 0) { + from = Math.max(len + intStart, 0); + } else { + from = Math.min(intStart, len); + } + let to; + if (intEnd < 0) { + to = Math.max(len + intEnd, 0); + } else { + to = Math.min(intEnd, len); + } + const span = Math.max(to - from, 0); + return new Value(S.slice(from, from + span)); +} + +// 21.1.3.20 #sec-string.prototype.split +function StringProto_split([separator = Value.undefined, limit = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + if (separator !== Value.undefined && separator !== Value.null) { + const splitter = Q(GetMethod(separator, wellKnownSymbols.split)); + if (splitter !== Value.undefined) { + return Q(Call(splitter, separator, [O, limit])); + } + } + const S = Q(ToString(O)); + const A = X(ArrayCreate(new Value(0))); + let lengthA = 0; + let lim; + if (limit === Value.undefined) { + lim = new Value((2 ** 32) - 1); + } else { + lim = Q(ToUint32(limit)); + } + const s = S.stringValue().length; + let p = 0; + const R = Q(ToString(separator)); + if (lim.numberValue() === 0) { + return A; + } + if (separator === Value.undefined) { + X(CreateDataProperty(A, new Value('0'), S)); + return A; + } + if (s === 0) { + const z = SplitMatch(S, 0, R); + if (z !== false) { + return A; + } + X(CreateDataProperty(A, new Value('0'), S)); + return A; + } + let q = p; + while (q !== s) { + const e = SplitMatch(S, q, R); + if (e === false) { + q += 1; + } else { + if (e === p) { + q += 1; + } else { + const T = new Value(S.stringValue().substring(p, q)); + X(CreateDataProperty(A, X(ToString(new Value(lengthA))), T)); + lengthA += 1; + if (lengthA === lim.numberValue()) { + return A; + } + p = e; + q = p; + } + } + } + const T = new Value(S.stringValue().substring(p, s)); + X(CreateDataProperty(A, X(ToString(new Value(lengthA))), T)); + return A; +} + +// 21.1.3.20.1 #sec-splitmatch +function SplitMatch(S, q, R) { + Assert(Type(R) === 'String'); + const r = R.stringValue().length; + const s = S.stringValue().length; + if (q + r > s) { + return false; + } + for (let i = 0; i < r; i += 1) { + if (S.stringValue().charCodeAt(q + i) !== R.stringValue().charCodeAt(i)) { + return false; + } + } + return q + r; +} + +// 21.1.3.21 #sec-string.prototype.startswith +function StringProto_startsWith([searchString = Value.undefined, position = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const isRegExp = Q(IsRegExp(searchString)); + if (isRegExp === Value.true) { + return surroundingAgent.Throw('TypeError', 'RegExpArgumentNotAllowed', 'String.prototype.startsWith'); + } + const searchStr = Q(ToString(searchString)).stringValue(); + const pos = Q(ToInteger(position)).numberValue(); + Assert(!(position === Value.undefined) || pos === 0); + const len = S.length; + const start = Math.min(Math.max(pos, 0), len); + const searchLength = searchStr.length; + if (searchLength + start > len) { + return Value.false; + } + for (let i = 0; i < searchLength; i += 1) { + if (S.charCodeAt(start + i) !== searchStr.charCodeAt(i)) { + return Value.false; + } + } + return Value.true; +} + +// 21.1.3.22 #sec-string.prototype.substring +function StringProto_substring([start = Value.undefined, end = Value.undefined], { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)).stringValue(); + const len = S.length; + const intStart = Q(ToInteger(start)).numberValue(); + let intEnd; + if (end === Value.undefined) { + intEnd = len; + } else { + intEnd = Q(ToInteger(end)).numberValue(); + } + const finalStart = Math.min(Math.max(intStart, 0), len); + const finalEnd = Math.min(Math.max(intEnd, 0), len); + const from = Math.min(finalStart, finalEnd); + const to = Math.max(finalStart, finalEnd); + return new Value(S.slice(from, to)); +} + +// 21.1.3.23 #sec-string.prototype.tolocalelowercase +function StringProto_toLocaleLowerCase(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const L = S.stringValue().toLocaleLowerCase(); + return new Value(L); +} + +// 21.1.3.24 #sec-string.prototype.tolocaleuppercase +function StringProto_toLocaleUpperCase(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const L = S.stringValue().toLocaleUpperCase(); + return new Value(L); +} + +// 21.1.3.25 #sec-string.prototype.tolowercase +function StringProto_toLowerCase(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const L = S.stringValue().toLowerCase(); + return new Value(L); +} + +// 21.1.3.26 #sec-string.prototype.tostring +function StringProto_toString(args, { thisValue }) { + return Q(thisStringValue(thisValue)); +} + +// 21.1.3.27 #sec-string.prototype.touppercase +function StringProto_toUpperCase(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + const L = S.stringValue().toUpperCase(); + return new Value(L); +} + +// 21.1.3.28 #sec-string.prototype.trim +function StringProto_trim(args, { thisValue }) { + const S = thisValue; + return Q(TrimString(S, 'start+end')); +} + +// 21.1.3.29 #sec-string.prototype.trimend +function StringProto_trimEnd(args, { thisValue }) { + const S = thisValue; + return Q(TrimString(S, 'end')); +} + +// 21.1.3.30 #sec-string.prototype.trimstart +function StringProto_trimStart(args, { thisValue }) { + const S = thisValue; + return Q(TrimString(S, 'start')); +} + +// 21.1.3.31 #sec-string.prototype.valueof +function StringProto_valueOf(args, { thisValue }) { + return Q(thisStringValue(thisValue)); +} + +// 21.1.3.32 #sec-string.prototype-@@iterator +function StringProto_iterator(args, { thisValue }) { + const O = Q(RequireObjectCoercible(thisValue)); + const S = Q(ToString(O)); + return Q(CreateStringIterator(S)); +} + +export function BootstrapStringPrototype(realmRec) { + const proto = StringCreate(new Value(''), realmRec.Intrinsics['%Object.prototype%']); + + assignProps(realmRec, proto, [ + ['charAt', StringProto_charAt, 1], + ['charCodeAt', StringProto_charCodeAt, 1], + ['codePointAt', StringProto_codePointAt, 1], + ['concat', StringProto_concat, 1], + ['endsWith', StringProto_endsWith, 1], + ['includes', StringProto_includes, 1], + ['indexOf', StringProto_indexOf, 1], + ['lastIndexOf', StringProto_lastIndexOf, 1], + ['localeCompare', StringProto_localeCompare, 1], + ['match', StringProto_match, 1], + ['matchAll', StringProto_matchAll, 1], + ['normalize', StringProto_normalize, 0], + ['padEnd', StringProto_padEnd, 1], + ['padStart', StringProto_padStart, 1], + ['repeat', StringProto_repeat, 1], + ['replace', StringProto_replace, 2], + surroundingAgent.feature('String.prototype.replaceAll') + ? ['replaceAll', StringProto_replaceAll, 2] + : undefined, + ['search', StringProto_search, 1], + ['slice', StringProto_slice, 2], + ['split', StringProto_split, 2], + ['startsWith', StringProto_startsWith, 1], + ['substring', StringProto_substring, 2], + ['toLocaleLowerCase', StringProto_toLocaleLowerCase, 0], + ['toLocaleUpperCase', StringProto_toLocaleUpperCase, 0], + ['toLowerCase', StringProto_toLowerCase, 0], + ['toString', StringProto_toString, 0], + ['toUpperCase', StringProto_toUpperCase, 0], + ['trim', StringProto_trim, 0], + ['trimEnd', StringProto_trimEnd, 0], + ['trimStart', StringProto_trimStart, 0], + ['valueOf', StringProto_valueOf, 0], + [wellKnownSymbols.iterator, StringProto_iterator, 0], + ]); + + realmRec.Intrinsics['%String.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/Symbol.mjs b/src/engine262/src/intrinsics/Symbol.mjs new file mode 100644 index 0000000..0c5dff3 --- /dev/null +++ b/src/engine262/src/intrinsics/Symbol.mjs @@ -0,0 +1,81 @@ +import { + Descriptor, + SymbolValue, + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { + SameValue, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +export const GlobalSymbolRegistry = []; + +function SymbolConstructor([description = Value.undefined], { NewTarget }) { + if (NewTarget !== Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + let descString; + if (description === Value.undefined) { + descString = Value.undefined; + } else { + descString = Q(ToString(description)); + } + return new SymbolValue(descString); +} + +function Symbol_for([key = Value.undefined]) { + const stringKey = Q(ToString(key)); + for (const e of GlobalSymbolRegistry) { + if (SameValue(e.Key, stringKey) === Value.true) { + return e.Symbol; + } + } + // Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey. + const newSymbol = new SymbolValue(stringKey); + GlobalSymbolRegistry.push({ Key: stringKey, Symbol: newSymbol }); + return newSymbol; +} + +function Symbol_keyFor([sym = Value.undefined]) { + if (Type(sym) !== 'Symbol') { + return surroundingAgent.Throw('TypeError', 'NotASymbol', sym); + } + for (const e of GlobalSymbolRegistry) { + if (SameValue(e.Symbol, sym) === Value.true) { + return e.Key; + } + } + return Value.undefined; +} + +export function BootstrapSymbol(realmRec) { + const symbolConstructor = BootstrapConstructor(realmRec, SymbolConstructor, 'Symbol', 0, realmRec.Intrinsics['%Symbol.prototype%'], [ + ['for', Symbol_for, 1], + ['keyFor', Symbol_keyFor, 1], + ]); + + for (const [name, sym] of Object.entries(wellKnownSymbols)) { + symbolConstructor.DefineOwnProperty(new Value(name), Descriptor({ + Value: sym, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + })); + } + + symbolConstructor.DefineOwnProperty(new Value('prototype'), Descriptor({ + Value: realmRec.Intrinsics['%Symbol.prototype%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + })); + + realmRec.Intrinsics['%Symbol%'] = symbolConstructor; +} diff --git a/src/engine262/src/intrinsics/SymbolPrototype.mjs b/src/engine262/src/intrinsics/SymbolPrototype.mjs new file mode 100644 index 0000000..3f97be4 --- /dev/null +++ b/src/engine262/src/intrinsics/SymbolPrototype.mjs @@ -0,0 +1,61 @@ +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { + surroundingAgent, +} from '../engine.mjs'; +import { + Assert, + SymbolDescriptiveString, +} from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function thisSymbolValue(value) { + if (Type(value) === 'Symbol') { + return value; + } + if (Type(value) === 'Object' && 'SymbolData' in value) { + const s = value.SymbolData; + Assert(Type(s) === 'Symbol'); + return s; + } + return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Symbol', value); +} + +function SymbolProto_toString(argList, { thisValue }) { + const sym = Q(thisSymbolValue(thisValue)); + return SymbolDescriptiveString(sym); +} + +function SymbolProto_descriptionGetter(argList, { thisValue }) { + const s = thisValue; + const sym = Q(thisSymbolValue(s)); + return sym.Description; +} + +function SymbolProto_valueOf(argList, { thisValue }) { + return Q(thisSymbolValue(thisValue)); +} + +function SymbolProto_toPrimitive(argList, { thisValue }) { + return Q(thisSymbolValue(thisValue)); +} + +export function BootstrapSymbolPrototype(realmRec) { + const override = { + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.true, + }; + const proto = BootstrapPrototype(realmRec, [ + ['toString', SymbolProto_toString, 0], + ['description', [SymbolProto_descriptionGetter]], + ['valueOf', SymbolProto_valueOf, 0], + [wellKnownSymbols.toPrimitive, SymbolProto_toPrimitive, 1, override], + ], realmRec.Intrinsics['%Object.prototype%'], 'Symbol'); + + realmRec.Intrinsics['%Symbol.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/ThrowTypeError.mjs b/src/engine262/src/intrinsics/ThrowTypeError.mjs new file mode 100644 index 0000000..e4f0429 --- /dev/null +++ b/src/engine262/src/intrinsics/ThrowTypeError.mjs @@ -0,0 +1,32 @@ +import { surroundingAgent } from '../engine.mjs'; +import { CreateBuiltinFunction } from '../abstract-ops/all.mjs'; +import { Value, Descriptor } from '../value.mjs'; +import { X } from '../completion.mjs'; + +// https://tc39.es/ecma262/#sec-%throwtypeerror% +export function BootstrapThrowTypeError(realmRec) { + const ThrowTypeError = X(CreateBuiltinFunction( + () => surroundingAgent.Throw('TypeError', 'StrictPoisonPill'), + [], realmRec, Value.null, + )); + + ThrowTypeError.Extensible = Value.false; + + ThrowTypeError.properties.set(new Value('length'), Descriptor({ + Value: new Value(0), + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + })); + + ThrowTypeError.properties.set(new Value('name'), Descriptor({ + Value: new Value(''), + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + })); + + ThrowTypeError.Prototype = realmRec.Intrinsics['%Function.prototype%']; + + realmRec.Intrinsics['%ThrowTypeError%'] = ThrowTypeError; +} diff --git a/src/engine262/src/intrinsics/TypedArray.mjs b/src/engine262/src/intrinsics/TypedArray.mjs new file mode 100644 index 0000000..2da44a5 --- /dev/null +++ b/src/engine262/src/intrinsics/TypedArray.mjs @@ -0,0 +1,145 @@ +import { Q, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Value, wellKnownSymbols } from '../value.mjs'; +import { + Assert, + Call, + Get, + GetMethod, + IsCallable, + IsConstructor, + IterableToList, + Set, + LengthOfArrayLike, + ToObject, + ToString, + TypedArrayCreate, +} from '../abstract-ops/all.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// #sec-%typedarray%-intrinsic-object +function TypedArrayConstructor() { + // 1. Throw a TypeError exception. + return surroundingAgent.Throw('TypeError', 'NotAConstructor', this); +} + +// #sec-%typedarray%.from +function TypedArray_from([source = Value.undefined, mapfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + // 1. Let C be the this value. + const C = thisValue; + // 2. If IsConstructor(C) is false, throw a TypeError exception. + if (IsConstructor(C) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', C); + } + // 3. If mapfn is undefined, let mapping be false. + let mapping; + if (mapfn === Value.undefined) { + mapping = false; + } else { + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + if (IsCallable(mapfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', mapfn); + } + // b. Let mapping be true. + mapping = true; + } + // 5. Let usingIterator be ? GetMethod(source, @@iterator). + const usingIterator = Q(GetMethod(source, wellKnownSymbols.iterator)); + // 6. If usingIterator is not undefined, then + if (usingIterator !== Value.undefined) { + const values = Q(IterableToList(source, usingIterator)); + const len = values.length; + const targetObj = Q(TypedArrayCreate(C, [new Value(len)])); + let k = 0; + while (k < len) { + const Pk = X(ToString(new Value(k))); + const kValue = values.shift(); + let mappedValue; + if (mapping) { + mappedValue = Q(Call(mapfn, thisArg, [kValue, new Value(k)])); + } else { + mappedValue = kValue; + } + Q(Set(targetObj, Pk, mappedValue, Value.true)); + k += 1; + } + Assert(values.length === 0); + return targetObj; + } + // 7. NOTE: source is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be ! ToObject(source). + const arrayLike = X(ToObject(source)); + // 9. Let len be ? LengthOfArrayLike(arrayLike). + const len = Q(LengthOfArrayLike(arrayLike)).numberValue(); + // 10. Let targetObj be ? TypedArrayCreate(C, « len »). + const targetObj = Q(TypedArrayCreate(C, [new Value(len)])); + // 11. Let k be 0. + let k = 0; + // 12. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Let kValue be ? Get(arrayLike, Pk). + const kValue = Q(Get(arrayLike, Pk)); + let mappedValue; + // c. If mapping is true, then + if (mapping) { + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + mappedValue = Q(Call(mapfn, thisArg, [kValue, new Value(k)])); + } else { + // d. Else, let mappedValue be kValue. + mappedValue = kValue; + } + // e. Perform ? Set(targetObj, Pk, mappedValue, true). + Q(Set(targetObj, Pk, mappedValue, Value.true)); + // f. Set k to k + 1. + k += 1; + } + // 13. Return targetObj. + return targetObj; +} + +// #sec-%typedarray%.of +function TypedArray_of(items, { thisValue }) { + // 1. Let len be the actual number of arguments passed to this function. + // 2. Let items be the List of arguments passed to this function. + const len = items.length; + // 3. Let C be the this value. + const C = thisValue; + // 4. If IsConstructor(C) is false, throw a TypeError exception. + if (IsConstructor(C) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', C); + } + // 5. Let newObj be ? TypedArrayCreate(C, « len »). + const newObj = Q(TypedArrayCreate(C, [new Value(len)])); + // 6. Let k be 0. + let k = 0; + // 7. Repeat, while k < len + while (k < len) { + // a. Let kValue be items[k]. + const kValue = items[k]; + // b. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // c. Perform ? Set(newObj, Pk, kValue, true). + Q(Set(newObj, Pk, kValue, Value.true)); + // d. Set k to k + 1. + k += 1; + } + // 8. Return newObj. + return newObj; +} + +// #sec-get-%typedarray%-@@species +function TypedArray_speciesGetter(args, { thisValue }) { + return thisValue; +} + +export function BootstrapTypedArray(realmRec) { + const typedArrayConstructor = BootstrapConstructor(realmRec, TypedArrayConstructor, 'TypedArray', 0, realmRec.Intrinsics['%TypedArray.prototype%'], [ + ['from', TypedArray_from, 1], + ['of', TypedArray_of, 0], + [wellKnownSymbols.species, [TypedArray_speciesGetter]], + ]); + + realmRec.Intrinsics['%TypedArray%'] = typedArrayConstructor; +} diff --git a/src/engine262/src/intrinsics/TypedArrayConstructors.mjs b/src/engine262/src/intrinsics/TypedArrayConstructors.mjs new file mode 100644 index 0000000..688f1a5 --- /dev/null +++ b/src/engine262/src/intrinsics/TypedArrayConstructors.mjs @@ -0,0 +1,286 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value, wellKnownSymbols } from '../value.mjs'; +import { + AllocateArrayBuffer, + AllocateTypedArray, + AllocateTypedArrayBuffer, + Assert, + CloneArrayBuffer, + Get, + GetMethod, + GetValueFromBuffer, + IsDetachedBuffer, + IsSharedArrayBuffer, + IterableToList, + SameValue, + Set, + SetValueInBuffer, + SpeciesConstructor, + LengthOfArrayLike, + ToIndex, + ToString, + typedArrayInfoByName, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// #sec-typedarray-constructors +export function BootstrapTypedArrayConstructors(realmRec) { + Object.entries(typedArrayInfoByName).forEach(([TypedArray, info]) => { + function TypedArrayConstructor(args, { NewTarget }) { + if (args.length === 0) { + // #sec-typedarray + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this TypedArray constructor. + const constructorName = new Value(TypedArray); + // 3. Return ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%", 0). + return Q(AllocateTypedArray(constructorName, NewTarget, `%${TypedArray}.prototype%`, new Value(0))); + } else if (Type(args[0]) !== 'Object') { + // #sec-typedarray-length + const [length] = args; + // 1. Assert: Type(length) is not Object. + Assert(Type(length) !== 'Object'); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 3. Let elementLength be ? ToIndex(length). + const elementLength = Q(ToIndex(length)); + // 4. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this TypedArray constructor. + const constructorName = new Value(TypedArray); + // 5. Return ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%", elementLength). + return Q(AllocateTypedArray(constructorName, NewTarget, `%${TypedArray}.prototype%`, elementLength)); + } else if ('TypedArrayName' in args[0]) { + // #sec-typedarray-typedarray + const [typedArray] = args; + // 1. Assert: Type(typedArray) is Object and typedArray has a [[TypedArrayName]] internal slot. + Assert(Type(typedArray) === 'Object' && 'TypedArrayName' in typedArray); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this TypedArray constructor. + const constructorName = new Value(TypedArray); + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). + const O = Q(AllocateTypedArray(constructorName, NewTarget, `%${TypedArray}.prototype%`)); + // 5. Let srcArray be typedArray. + const srcArray = typedArray; + // 6. Let srcData be srcArray.[[ViewedArrayBuffer]]. + const srcData = srcArray.ViewedArrayBuffer; + // 7. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcData) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 8. Let elementType be the Element Type value in Table 61 for constructorName. + const elementType = new Value(info.ElementType); + // 9. Let elementLength be srcArray.[[ArrayLength]]. + const elementLength = srcArray.ArrayLength; + // 10. Let srcName be the String value of srcArray.[[TypedArrayName]]. + const srcName = srcArray.TypedArrayName.stringValue(); + // 11. Let srcType be the Element Type value in Table 61 for srcName. + const srcType = new Value(typedArrayInfoByName[srcName].ElementType); + // 12. Let srcElementSize be the Element Size value specified in Table 61 for srcName. + const srcElementSize = typedArrayInfoByName[srcName].ElementSize; + // 13. Let srcByteOffset be srcArray.[[ByteOffset]]. + const srcByteOffset = srcArray.ByteOffset; + // 14. Let elementSize be the Element Size value specified in Table 61 for constructorName. + const elementSize = info.ElementSize; + // 15. Let byteLength be elementSize × elementLength. + const byteLength = new Value(elementSize * elementLength.numberValue()); + // 16. If IsSharedArrayBuffer(srcData) is false, then + let bufferConstructor; + if (IsSharedArrayBuffer(srcData) === Value.false) { + bufferConstructor = Q(SpeciesConstructor(srcData, surroundingAgent.intrinsic('%ArrayBuffer%'))); + } else { + // 17. Else, Let bufferConstructor be %ArrayBuffer%. + bufferConstructor = surroundingAgent.intrinsic('%ArrayBuffer%'); + } + // 18. If elementType is the same as srcType, then + let data; + if (SameValue(elementType, srcType) === Value.true) { + // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor). + data = Q(CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor)); + } else { + // a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength). + data = Q(AllocateArrayBuffer(bufferConstructor, byteLength)); + // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcData) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // c. If srcArray.[[ContentType]] is not equal to O.[[ContentType]], throw a TypeError exception. + if (srcArray.ContentType !== O.ContentType) { + return surroundingAgent.Throw('TypeError', 'BufferContentTypeMismatch'); + } + // d. Let srcByteIndex be srcByteOffset. + let srcByteIndex = srcByteOffset.numberValue(); + // e. Let targetByteIndex be 0. + let targetByteIndex = 0; + // f. Let count be elementLength. + let count = elementLength.numberValue(); + // g. Repeat, while count > 0 + while (count > 0) { + // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered). + const value = GetValueFromBuffer(srcData, new Value(srcByteIndex), srcType.stringValue(), true, 'Unordered'); + // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered). + SetValueInBuffer(data, new Value(targetByteIndex), elementType.stringValue(), value, true, 'Unordered'); + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + srcByteIndex += srcElementSize; + // iv. Set targetByteIndex to targetByteIndex + elementSize. + targetByteIndex += elementSize; + // v. Set count to count - 1. + count -= 1; + } + } + // 20. Set O.[[ViewedArrayBuffer]] to data. + O.ViewedArrayBuffer = data; + // 21. Set O.[[ByteLength]] to byteLength. + O.ByteLength = byteLength; + // 22. Set O.[[ByteOffset]] to 0. + O.ByteOffset = new Value(0); + // 23. Set O.[[ArrayLength]] to elementLength. + O.ArrayLength = elementLength; + // 24. Return O. + return O; + } else if (!('TypedArrayName' in args[0]) && !('ArrayBufferData' in args[0])) { + // 22.2.4.4 #sec-typedarray-object + const [object] = args; + // 1. Assert: Type(object) is Object and object does not have either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot. + Assert(Type(object) === 'Object' && !('TypedArrayName' in object) && !('ArrayBufferData' in object)); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this TypedArray constructor. + const constructorName = new Value(TypedArray); + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). + const O = Q(AllocateTypedArray(constructorName, NewTarget, `%${TypedArray}.prototype%`)); + // 5. Let usingIterator be ? GetMethod(object, @@iterator). + const usingIterator = Q(GetMethod(object, wellKnownSymbols.iterator)); + // 6. If usingIterator is not undefined, then + if (usingIterator !== Value.undefined) { + // a. Let values be ? IterableToList(object, usingIterator). + const values = Q(IterableToList(object, usingIterator)); + // b. Let len be the number of elements in values. + const len = values.length; + // c. Perform ? AllocateTypedArrayBuffer(O, len). + Q(AllocateTypedArrayBuffer(O, new Value(len))); + // d. Let k be 0. + let k = 0; + // e. Repeat, while k < len + while (k < len) { + // i. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // ii. Let kValue be the first element of values and remove that element from values. + const kValue = values.shift(); + // iii. Perform ? Set(O, Pk, kValue, true). + Q(Set(O, Pk, kValue, Value.true)); + // iv. Set k to k + 1. + k += 1; + } + // f. Assert: values is now an empty List. + Assert(values.length === 0); + // g. Return O. + return O; + } + // 7. NOTE: object is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be object. + const arrayLike = object; + // 9. Let len be ? LengthOfArrayLike(arrayLike). + const len = Q(LengthOfArrayLike(arrayLike)).numberValue(); + // 10. Perform ? AllocateTypedArrayBuffer(O, len). + Q(AllocateTypedArrayBuffer(O, new Value(len))); + // 11. Let k be 0. + let k = 0; + // 12. Repeat, while k < len. + while (k < len) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Let kValue be ? Get(arrayLike, Pk). + const kValue = Q(Get(arrayLike, Pk)); + // c. Perform ? Set(O, Pk, kValue, true). + Q(Set(O, Pk, kValue, Value.true)); + // d. Set k to k + 1. + k += 1; + } + // 13. Return O. + return O; + } else { + // #sec-typedarray-buffer-byteoffset-length + const [buffer = Value.undefined, byteOffset = Value.undefined, length = Value.undefined] = args; + // 1. Assert: Type(buffer) is Object and buffer has an [[ArrayBufferData]] internal slot. + Assert(Type(buffer) === 'Object' && 'ArrayBufferData' in buffer); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this TypedArray constructor. + const constructorName = new Value(TypedArray); + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). + const O = Q(AllocateTypedArray(constructorName, NewTarget, `%${TypedArray}.prototype%`)); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + const elementSize = info.ElementSize; + // 6. Let offset be ? ToIndex(byteOffset). + const offset = Q(ToIndex(byteOffset)); + // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. + if (offset.numberValue() % elementSize !== 0) { + return surroundingAgent.Throw('RangeError', 'TypedArrayOffsetAlignment', TypedArray, elementSize); + } + // 8. If length is not undefined, then + let newLength; + if (length !== Value.undefined) { + // Let newLength be ? ToIndex(length). + newLength = Q(ToIndex(length)).numberValue(); + } + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + const bufferByteLength = buffer.ArrayBufferByteLength.numberValue(); + // 11. If length is undefined, then + let newByteLength; + if (length === Value.undefined) { + // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. + if (bufferByteLength % elementSize !== 0) { + return surroundingAgent.Throw('RangeError', 'TypedArrayLengthAlignment', TypedArray, elementSize); + } + // b. Let newByteLength be bufferByteLength - offset. + newByteLength = bufferByteLength - offset.numberValue(); + // c. If newByteLength < 0, throw a RangeError exception. + if (newByteLength < 0) { + return surroundingAgent.Throw('RangeError', 'TypedArrayCreationOOB'); + } + } else { + // a. Let newByteLength be newLength × elementSize. + newByteLength = newLength * elementSize; + // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. + if (offset.numberValue() + newByteLength > bufferByteLength) { + return surroundingAgent.Throw('RangeError', 'TypedArrayCreationOOB'); + } + } + // 13. Set O.[[ViewedArrayBuffer]] to buffer. + O.ViewedArrayBuffer = buffer; + // 14. Set O.[[ByteLength]] to newByteLength. + O.ByteLength = new Value(newByteLength); + // 15. Set O.[[ByteOffset]] to offset. + O.ByteOffset = offset; + // 16. Set O.[[ArrayLength]] to newByteLength / elementSize. + O.ArrayLength = new Value(newByteLength / elementSize); + // 17. Return O. + return O; + } + } + + const taConstructor = BootstrapConstructor(realmRec, TypedArrayConstructor, TypedArray, 3, realmRec.Intrinsics[`%${TypedArray}.prototype%`], [ + ['BYTES_PER_ELEMENT', new Value(info.ElementSize), undefined, { + Writable: Value.false, + Configurable: Value.false, + }], + ]); + X(taConstructor.SetPrototypeOf(realmRec.Intrinsics['%TypedArray%'])); + realmRec.Intrinsics[`%${TypedArray}%`] = taConstructor; + }); +} diff --git a/src/engine262/src/intrinsics/TypedArrayPrototype.mjs b/src/engine262/src/intrinsics/TypedArrayPrototype.mjs new file mode 100644 index 0000000..78e1f1c --- /dev/null +++ b/src/engine262/src/intrinsics/TypedArrayPrototype.mjs @@ -0,0 +1,841 @@ +import { + Assert, + Call, + CloneArrayBuffer, + CreateArrayIterator, + Get, + GetValueFromBuffer, + IsCallable, + IsDetachedBuffer, + IsSharedArrayBuffer, + SameValue, + Set, + SetValueInBuffer, + LengthOfArrayLike, + ToBoolean, + ToBigInt, + ToInteger, + ToNumber, + ToObject, + ToString, + TypedArraySpeciesCreate, + ValidateTypedArray, + RequireInternalSlot, + typedArrayInfoByName, + typedArrayInfoByType, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + Descriptor, Type, Value, wellKnownSymbols, +} from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; +import { ArrayProto_sortBody, BootstrapArrayPrototypeShared } from './ArrayPrototypeShared.mjs'; + +// #sec-get-%typedarray%.prototype.buffer +function TypedArrayProto_buffer(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. Return buffer. + return buffer; +} + +// #sec-get-%typedarray%.prototype.bytelength +function TypedArrayProto_byteLength(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (IsDetachedBuffer(buffer) === Value.true) { + return new Value(0); + } + // 6. Let size be O.[[ByteLength]]. + const size = O.ByteLength; + // 7. Return size. + return size; +} + +// #sec-get-%typedarray%.prototype.byteoffset +function TypedArrayProto_byteOffset(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (IsDetachedBuffer(buffer) === Value.true) { + return new Value(0); + } + // 6. Let offset be O.[[ByteOffset]]. + const offset = O.ByteOffset; + // 7. Return offset. + return offset; +} + +// #sec-%typedarray%.prototype.copywithin +function TypedArrayProto_copyWithin([target = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Let len be O.[[ArrayLength]]. + const len = O.ArrayLength.numberValue(); + // 4. Let relativeTarget be ? ToInteger(target). + const relativeTarget = Q(ToInteger(target)).numberValue(); + // 5. If relativeTarget < 0, let to be max((len + relativeTarget), 0); else let to be min(relativeTarget, len). + let to; + if (relativeTarget < 0) { + to = Math.max(len + relativeTarget, 0); + } else { + to = Math.min(relativeTarget, len); + } + // 6. Let relativeStart be ? ToInteger(start). + const relativeStart = Q(ToInteger(start)).numberValue(); + // 7. If relativeStart < 0, let from be max((len + relativeStart), 0); else let from be min(relativeStart, len). + let from; + if (relativeStart < 0) { + from = Math.max(len + relativeStart, 0); + } else { + from = Math.min(relativeStart, len); + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end). + let relativeEnd; + if (end === Value.undefined) { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + // 9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len). + let final; + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + // 10. Let count be min(final - from, len - to). + const count = Math.min(final - from, len - to); + // 11. If count > 0, then + if (count > 0) { + // a. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data. + // b. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // d. Let typedArrayName be the String value of O.[[TypedArrayName]]. + const typedArrayName = O.TypedArrayName.stringValue(); + // e. Let elementSize be the Element Size value specified in Table 61 for typedArrayName. + const elementSize = typedArrayInfoByName[typedArrayName].ElementSize; + // f. Let byteOffset be O.[[ByteOffset]. + const byteOffset = O.ByteOffset.numberValue(); + // g. Let toByteIndex be to × elementSize + byteOffset. + let toByteIndex = to * elementSize + byteOffset; + // h. Let fromByteIndex be from × elementSize + byteOffset. + let fromByteIndex = from * elementSize + byteOffset; + // i. Let countBytes be count × elementSize. + let countBytes = count * elementSize; + // j. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then + let direction; + if (fromByteIndex < toByteIndex && toByteIndex < fromByteIndex + countBytes) { + // i. Let direction be -1. + direction = -1; + // ii. Set fromByteIndex to fromByteIndex + countBytes - 1. + fromByteIndex = fromByteIndex + countBytes - 1; + // iii. Set toByteIndex to toByteIndex + countBytes - 1. + toByteIndex = toByteIndex + countBytes - 1; + } else { + // i. Let direction be 1. + direction = 1; + } + // l. Repeat, while countBytes > 0 + while (countBytes > 0) { + // i. Let value be GetValueFromBuffer(buffer, fromByteIndex, Uint8, true, Unordered). + const value = GetValueFromBuffer(buffer, new Value(fromByteIndex), 'Uint8', Value.true, 'Unordered'); + // ii. Perform SetValueInBuffer(buffer, toByteIndex, Uint8, value, true, Unordered). + SetValueInBuffer(buffer, new Value(toByteIndex), 'Uint8', value, Value.true, 'Unordered'); + // iii. Set fromByteIndex to fromByteIndex + direction. + fromByteIndex += direction; + // iv. Set toByteIndex to toByteIndex + direction. + toByteIndex += direction; + // v. Set countBytes to countBytes - 1. + countBytes -= 1; + } + } + // 12. Return O. + return O; +} + +// #sec-%typedarray%.prototype.entries +function TypedArrayProto_entries(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Return CreateArrayIterator(O, key+value). + return CreateArrayIterator(O, 'key+value'); +} + +// #sec-%typedarray%.prototype.fill +function TypedArrayProto_fill([value = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Let len be O.[[ArrayLength]] + const len = O.ArrayLength.numberValue(); + // 4. If O.[[ContentType]] is BigInt, set value to ? ToBigInt(value). + // 5. Else, set value to ? ToNumber(value). + if (O.ContentType === 'BigInt') { + value = Q(ToBigInt(value)); + } else { + value = Q(ToNumber(value)); + } + // 6. Let relativeStart be ? ToInteger(start). + const relativeStart = Q(ToInteger(start)).numberValue(); + // 7. If relativeStart < 0, let k be max((len + relativeStart), 0); else let k be min(relativeStart, len). + let k; + if (relativeStart < 0) { + k = Math.max(len + relativeStart, 0); + } else { + k = Math.min(relativeStart, len); + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end). + let relativeEnd; + if (end === Value.undefined) { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + // 9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len). + let final; + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + // 10. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. + if (IsDetachedBuffer(O.ViewedArrayBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 11. Repeat, while k < final + while (k < final) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Perform ! Set(O, Pk, value, true). + X(Set(O, Pk, value, Value.true)); + // c. Set k to k + 1. + k += 1; + } + // 12. Return O. + return O; +} + +// #sec-%typedarray%.prototype.filter +function TypedArrayProto_filter([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Let len be O.[[ArrayLength]]. + const len = O.ArrayLength.numberValue(); + // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + // 5. Let kept be a new empty List. + const kept = []; + // 6. Let k be 0. + let k = 0; + // 7. Let captured be 0. + let captured = 0; + // 8. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Let kValue be ? Get(O, Pk). + const kValue = Q(Get(O, Pk)); + // c. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, k, O »)). + const selected = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]))); + // d. If selected is true, then + if (selected === Value.true) { + // i. Append kValue to the end of kept. + kept.push(kValue); + // ii. Setp captured to captured + 1. + captured += 1; + } + // e. Set k to k + 1. + k += 1; + } + // 9. Let A be ? TypedArraySpeciesCreate(O, « captured »). + const A = Q(TypedArraySpeciesCreate(O, [new Value(captured)])); + // 10. Let n be 0. + let n = 0; + // 11. For each element e of kept, do + for (const e of kept) { + // a. Perform ! Set(A, ! ToString(n), e, true). + X(Set(A, X(ToString(new Value(n))), e, Value.true)); + // b. Set n to n + 1. + n += 1; + } + // 12. Return A. + return A; +} + +// #sec-%typedarray%.prototype.keys +function TypedArrayProto_keys(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Return CreateArrayIterator(O, key). + return CreateArrayIterator(O, 'key'); +} + +// #sec-get-%typedarray%.prototype.length +function TypedArrayProto_length(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 3. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. + Assert('ViewedArrayBuffer' in O && 'ArrayLength' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (IsDetachedBuffer(buffer) === Value.true) { + return new Value(0); + } + // 6. Let length be O.[[ArrayLength]]. + const length = O.ArrayLength; + // 8. Return length. + return length; +} + +// #sec-%typedarray%.prototype.map +function TypedArrayProto_map([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Let len be O.[[ArrayLength]]. + const len = O.ArrayLength; + // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (IsCallable(callbackfn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn); + } + // 5. Let A be ? TypedArraySpeciesCreate(O, « len »). + const A = Q(TypedArraySpeciesCreate(O, [len])); + // 6. Let k be 0. + let k = 0; + // 7. Repeat, while k < len + while (k < len.numberValue()) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Let kValue be ? Get(O, Pk). + const kValue = Q(Get(O, Pk)); + // c. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, k, O »). + const mappedValue = Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])); + // d. Perform ? Set(A, Pk, mappedValue, true). + Q(Set(A, Pk, mappedValue, Value.true)); + // e. Set k to k + 1. + k += 1; + } + // 8. Return A. + return A; +} + +// #sec-%typedarray%.prototype.set-overloaded-offset +function TypedArrayProto_set([overloaded = Value.undefined, offset = Value.undefined], { thisValue }) { + if (Type(overloaded) !== 'Object' || !('TypedArrayName' in overloaded)) { + // #sec-%typedarray%.prototype.set-array-offset + const array = overloaded; + // 1. Assert: array is any ECMAScript language value other than an Object with a [[TypedArrayName]] internal slot. + // 2. Let target be the this value. + const target = thisValue; + // 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). + Q(RequireInternalSlot(target, 'TypedArrayName')); + // 4. Assert: target has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in target); + // 5. Let targetOffset be ? ToInteger(offset). + const targetOffset = Q(ToInteger(offset)).numberValue(); + // 6. If targetOffset < 0, throw a RangeError exception. + if (targetOffset < 0) { + return surroundingAgent.Throw('RangeError', 'NegativeIndex', 'Offset'); + } + // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. + const targetBuffer = target.ViewedArrayBuffer; + // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(targetBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 9. Let targetLength be target.[[ArrayLength]]. + const targetLength = target.ArrayLength.numberValue(); + // 10. Let targetName be the String value of target.[[TypedArrayName]]. + const targetName = target.TypedArrayName.stringValue(); + // 11. Let targetElementSize be the Element Size value specified in Table 61 for targetName. + const targetElementSize = typedArrayInfoByName[targetName].ElementSize; + // 12. Let targetType be the Element Type value in Table 61 for targetName. + const targetType = typedArrayInfoByName[targetName].ElementType; + // 13. Let targetByteOffset be target.[[ByteOffset]]. + const targetByteOffset = target.ByteOffset.numberValue(); + // 14. Let src be ? ToObject(array). + const src = Q(ToObject(array)); + // 15. Let srcLength be ? LengthOfArrayLike(src). + const srcLength = Q(LengthOfArrayLike(src)).numberValue(); + // 16. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLength + targetOffset > targetLength) { + return surroundingAgent.Throw('RangeError', 'TypedArrayOOB'); + } + // 17. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + let targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 18. Let k be 0. + let k = 0; + // 19. Let limit be targetByteIndex + targetElementSize × srcLength. + const limit = targetByteIndex + targetElementSize * srcLength; + // 20. Repeat, while targetByteIndex < limit + while (targetByteIndex < limit) { + // a. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // b. Let value be ? Get(src, Pk). + let value = Q(Get(src, Pk)); + // c. If target.[[ContentType]] is BigInt, set value to ? ToBigInt(value). + // d. Otherwise, set value to ? ToNumber(value). + if (target.ContentType === 'BigInt') { + value = Q(ToBigInt(value)); + } else { + value = Q(ToNumber(value)); + } + // e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(targetBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). + SetValueInBuffer(targetBuffer, new Value(targetByteIndex), targetType, value, Value.true, 'Unordered'); + // g. Set k to k + 1. + k += 1; + // h. Set targetByteIndex to targetByteIndex + targetElementSize. + targetByteIndex += targetElementSize; + } + // 21. Return undefined. + return Value.undefined; + } else { + // #sec-%typedarray%.prototype.set-typedarray-offset + const typedArray = overloaded; + // 1. Assert: typedArray has a [[TypedArrayName]] internal slot. + Assert('TypedArrayName' in typedArray); + // 2. Let target be the this value. + const target = thisValue; + // 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). + Q(RequireInternalSlot(target, 'TypedArrayName')); + // 4. Assert: target has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in target); + // 5. Let targetOffset be ? ToInteger(offset). + const targetOffset = Q(ToInteger(offset)).numberValue(); + // 6. If targetOffset < 0, throw a RangeError exception. + if (targetOffset < 0) { + return surroundingAgent.Throw('RangeError', 'NegativeIndex', 'Offset'); + } + // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. + const targetBuffer = target.ViewedArrayBuffer; + // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(targetBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 9. Let targetLength be target.[[ArrayLength]]. + const targetLength = target.ArrayLength.numberValue(); + // 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]]. + let srcBuffer = typedArray.ViewedArrayBuffer; + // 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // 12. Let targetName be the String value of target.[[TypedArrayName]]. + const targetName = target.TypedArrayName.stringValue(); + // 13. Let targetType be the Element Type value in Table 61 for targetName. + const targetType = typedArrayInfoByName[targetName].ElementType; + // 14. Let targetElementSize be the Element Size value specified in Table 61 for targetName. + const targetElementSize = typedArrayInfoByName[targetName].ElementSize; + // 15. Let targetByteOffset be target.[[ByteOffset]]. + const targetByteOffset = target.ByteOffset.numberValue(); + // 16. Let srcName be the String value of typedArray.[[TypedArrayName]]. + const srcName = typedArray.TypedArrayName.stringValue(); + // 17. Let srcType be the Element Type value in Table 61 for srcName. + const srcType = typedArrayInfoByName[srcName].ElementType; + // 18. Let srcElementSize be the Element Size value specified in Table 61 for srcName. + const srcElementSize = typedArrayInfoByName[srcName].ElementSize; + // 19. Let srcLength be typedArray.[[ArrayLength]]. + const srcLength = typedArray.ArrayLength.numberValue(); + // 20. Let srcByteOffset be typedArray.[[ByteOffset]]. + const srcByteOffset = typedArray.ByteOffset.numberValue(); + // 21. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLength + targetOffset > targetLength) { + return surroundingAgent.Throw('RangeError', 'TypedArrayOOB'); + } + // 22. If target.[[ContentType]] is not equal to typedArray.[[ContentType]], throw a TypeError exception. + if (target.ContentType !== typedArray.ContentType) { + return surroundingAgent.Throw('TypeError', 'BufferContentTypeMismatch'); + } + // 23. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then + let same; + if (IsSharedArrayBuffer(srcBuffer) === Value.true && IsSharedArrayBuffer(targetBuffer) === Value.true) { + Assert(false); + } else { + same = SameValue(srcBuffer, targetBuffer); + } + // 25. If same is true, then + let srcByteIndex; + if (same === Value.true) { + // a. Let srcByteLength be typedArray.[[ByteLength]]. + const srcByteLength = typedArray.ByteLength; + // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%). + srcBuffer = Q(CloneArrayBuffer(srcBuffer, new Value(srcByteOffset), srcByteLength, surroundingAgent.intrinsic('%ArrayBuffer%'))); + // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects. + // d. Let srcByteIndex be 0. + srcByteIndex = 0; + } else { + // 26. Else, let srcByteIndex be srcByteOffset. + srcByteIndex = srcByteOffset; + } + // 27. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + let targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 28. Let limit be targetByteIndex + targetElementSize × srcLength. + const limit = targetByteIndex + targetElementSize * srcLength; + // 29. If srcType is the same as targetType, then + if (srcType === targetType) { + // a. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data. + // b. Repeat, while targetByteIndex < limit + while (targetByteIndex < limit) { + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered). + const value = GetValueFromBuffer(srcBuffer, new Value(srcByteIndex), 'Uint8', Value.true, 'Unordered'); + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered). + SetValueInBuffer(targetBuffer, new Value(targetByteIndex), 'Uint8', value, Value.true, 'Unordered'); + // iii. Set srcByteIndex to srcByteIndex + 1. + srcByteIndex += 1; + // iv. Set targetByteIndex to targetByteIndex + 1. + targetByteIndex += 1; + } + } else { + // a. Repeat, while targetByteIndex < limit + while (targetByteIndex < limit) { + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered). + const value = GetValueFromBuffer(srcBuffer, new Value(srcByteIndex), srcType, Value.true, 'Unordered'); + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). + SetValueInBuffer(targetBuffer, new Value(targetByteIndex), targetType, value, Value.true, 'Unordered'); + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + srcByteIndex += srcElementSize; + // iv. Set targetByteIndex to targetByteIndex + targetElementSize. + targetByteIndex += targetElementSize; + } + } + // 31. Return undefined. + return Value.undefined; + } +} + +// #sec-%typedarray%.prototype.slice +function TypedArrayProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // 3. Let len be O.[[ArrayLength]]. + const len = O.ArrayLength.numberValue(); + // 4. Let relativeStart be ? ToInteger(start). + const relativeStart = Q(ToInteger(start)).numberValue(); + // 5. If relativeStart < 0, let k be max((len + relativeStart), 0); else let k be min(relativeStart, len). + let k; + if (relativeStart < 0) { + k = Math.max(len + relativeStart, 0); + } else { + k = Math.min(relativeStart, len); + } + // 6. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end). + let relativeEnd; + if (end === Value.undefined) { + relativeEnd = len; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + // 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len). + let final; + if (relativeEnd < 0) { + final = Math.max(len + relativeEnd, 0); + } else { + final = Math.min(relativeEnd, len); + } + // 8. Let count be max(final - k, 0). + const count = Math.max(final - k, 0); + // 9. Let A be ? TypedArraySpeciesCreate(O, « count »). + const A = Q(TypedArraySpeciesCreate(O, [new Value(count)])); + // 10. Let srcName be the String value of O.[[TypedArrayName]]. + const srcName = O.TypedArrayName.stringValue(); + // 11. Let srcType be the Element Type value in Table 61 for srcName. + const srcType = typedArrayInfoByName[srcName].ElementType; + // 12. Let targetName be the String value of A.[[TypedArrayName]]. + const targetName = A.TypedArrayName.stringValue(); + // 13. Let targetType be the Element Type value in Table 61 for targetName. + const targetType = typedArrayInfoByName[targetName].ElementType; + // 14. If srcType is different from targetType, then + if (srcType !== targetType) { + // a. Let n be 0. + let n = 0; + // b. Repeat, while k < final + while (k < final) { + // i. Let Pk be ! ToString(k). + const Pk = X(ToString(new Value(k))); + // ii. Let kValue be ? Get(O, Pk). + const kValue = Q(Get(O, Pk)); + // iii. Perform ! Set(A, ! ToString(n), kValue, true). + X(Set(A, X(ToString(new Value(n))), kValue, Value.true)); + // iv. Set k to k + 1. + k += 1; + // v. Set n to n + 1. + n += 1; + } + } else if (count > 0) { + // a. Let srcBuffer be O.[[ViewedArrayBuffer]]. + const srcBuffer = O.ViewedArrayBuffer; + // b. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // c. Let targetBuffer be A.[[ViewedArrayBuffer]]. + const targetBuffer = A.ViewedArrayBuffer; + // d. Let elementSize be the Element Size value specified in Table 61 for Element Type srcType. + const elementSize = typedArrayInfoByType[srcType].ElementSize; + // e. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data. + // f. Let srcByteOffet be O.[[ByteOffset]]. + const srcByteOffset = O.ByteOffset.numberValue(); + // g. Let targetByteIndex be A.[[ByteOffset]]. + let targetByteIndex = A.ByteOffset.numberValue(); + // h. Let srcByteIndex be (k × elementSize) + srcByteOffet. + let srcByteIndex = (k * elementSize) + srcByteOffset; + // i. Let limit be targetByteIndex + count × elementSize. + const limit = targetByteIndex + count * elementSize; + // j. Repeat, while targetByteIndex < limit + while (targetByteIndex < limit) { + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered). + const value = GetValueFromBuffer(srcBuffer, new Value(srcByteIndex), 'Uint8', Value.true, 'Unordered'); + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered). + SetValueInBuffer(targetBuffer, new Value(targetByteIndex), 'Uint8', value, Value.true, 'Unordered'); + // iii. Set srcByteIndex to srcByteIndex + 1. + srcByteIndex += 1; + // iv. Set targetByteIndex to targetByteIndex + 1. + targetByteIndex += 1; + } + } + // 16. Return A. + return A; +} + +// 22.2.3.26 #sec-%typedarray%.prototype.sort +function TypedArrayProto_sort([comparefn = Value.undefined], { thisValue }) { + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. + if (comparefn !== Value.undefined && IsCallable(comparefn) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', comparefn); + } + // 2. Let obj be the this value. + const obj = Q(ToObject(thisValue)); + // 3. Let buffer be ? ValidateTypedArray(obj). + const buffer = Q(ValidateTypedArray(obj)); + // 4. Let len be obj.[[ArrayLength]]. + const len = obj.ArrayLength; + + return ArrayProto_sortBody(obj, len, (x, y) => TypedArraySortCompare(x, y, comparefn, buffer), true); +} + +function TypedArraySortCompare(x, y, comparefn, buffer) { + // 1. Assert: Both Type(x) and Type(y) are Number or both are BigInt. + Assert((Type(x) === 'Number' && Type(y) === 'Number') + || (Type(x) === 'BigInt' && Type(y) === 'BigInt')); + // 2. If comparefn is not undefined, then + if (comparefn !== Value.undefined) { + // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)). + const v = Q(ToNumber(Q(Call(comparefn, Value.undefined, [x, y])))); + // b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer) === Value.true) { + return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached'); + } + // c. If v is NaN, return +0. + if (v.isNaN()) { + return new Value(+0); + } + // d. Return v. + return v; + } + // 3. If x and y are both NaN, return +0. + if (x.isNaN() && y.isNaN()) { + return new Value(+0); + } + // 4. If x is NaN, return 1. + if (x.isNaN()) { + return new Value(1); + } + // 5. If y is NaN, return -1. + if (y.isNaN()) { + return new Value(-1); + } + x = x.numberValue ? x.numberValue() : x.bigintValue(); + y = y.numberValue ? y.numberValue() : y.bigintValue(); + // 6. If x < y, return -1. + if (x < y) { + return new Value(-1); + } + // 7. If x > y, return 1. + if (x > y) { + return new Value(1); + } + // 8. If x is -0 and y is +0, return -1. + if (Object.is(x, -0) && Object.is(y, +0)) { + return new Value(-1); + } + // 9. If x is +0 and y is -0, return 1. + if (Object.is(x, +0) && Object.is(y, -0)) { + return new Value(1); + } + // 10. Return +0. + return new Value(+0); +} + +// #sec-%typedarray%.prototype.subarray +function TypedArrayProto_subarray([begin = Value.undefined, end = Value.undefined], { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + Q(RequireInternalSlot(O, 'TypedArrayName')); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + Assert('ViewedArrayBuffer' in O); + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = O.ViewedArrayBuffer; + // 5. Let srcLength be O.[[ArrayLength]]. + const srcLength = O.ArrayLength.numberValue(); + // 6. Let relativeBegin be ? ToInteger(begin). + const relativeBegin = Q(ToInteger(begin)).numberValue(); + // 7. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin), 0); else let beginIndex be min(relativeBegin, srcLength). + let beginIndex; + if (relativeBegin < 0) { + beginIndex = Math.max(srcLength + relativeBegin, 0); + } else { + beginIndex = Math.min(relativeBegin, srcLength); + } + // 8. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToInteger(end). + let relativeEnd; + if (end === Value.undefined) { + relativeEnd = srcLength; + } else { + relativeEnd = Q(ToInteger(end)).numberValue(); + } + // 9. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0); else let endIndex be min(relativeEnd, srcLength). + let endIndex; + if (relativeEnd < 0) { + endIndex = Math.max(srcLength + relativeEnd, 0); + } else { + endIndex = Math.min(relativeEnd, srcLength); + } + // 10. Let newLength be max(endIndex - beginIndex, 0). + const newLength = Math.max(endIndex - beginIndex, 0); + // 11. Let constructorName be the String value of O.[[TypedArrayName]]. + const constructorName = O.TypedArrayName.stringValue(); + // 12. Let elementSize be the Element Size value specified in Table 61 for constructorName. + const elementSize = typedArrayInfoByName[constructorName].ElementSize; + // 13. Let srcByteOffset be O.[[ByteOffset]]. + const srcByteOffset = O.ByteOffset.numberValue(); + // 14. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. + const beginByteOffset = srcByteOffset + beginIndex * elementSize; + // 15. Let argumentsList be « buffer, beginByteOffset, newLength ». + const argumentsList = [buffer, new Value(beginByteOffset), new Value(newLength)]; + // 16. Return ? TypedArraySpeciesCreate(O, argumentsList). + return Q(TypedArraySpeciesCreate(O, argumentsList)); +} + +// #sec-%typedarray%.prototype.values +function TypedArrayProto_values(args, { thisValue }) { + // 1. Let o be the this value. + const O = thisValue; + // 2. Perform ? ValidateTypedArray(O). + Q(ValidateTypedArray(O)); + // Return CreateArrayIterator(O, value). + return CreateArrayIterator(O, 'value'); +} + +// #sec-get-%typedarray%.prototype-@@tostringtag +function TypedArrayProto_toStringTag(args, { thisValue }) { + // 1. Let O be the this value. + const O = thisValue; + // 2. If Type(O) is not Object, return undefined. + if (Type(O) !== 'Object') { + return Value.undefined; + } + // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined. + if (!('TypedArrayName' in O)) { + return Value.undefined; + } + // 4. Let name be O.[[TypedArrayName]]. + const name = O.TypedArrayName; + // 5. Assert: Type(name) is String. + Assert(Type(name) === 'String'); + // 6. Return name. + return name; +} + +export function BootstrapTypedArrayPrototype(realmRec) { + const ArrayProto_toString = X(Get(realmRec.Intrinsics['%Array.prototype%'], new Value('toString'))); + Assert(Type(ArrayProto_toString) === 'Object'); + + const proto = BootstrapPrototype(realmRec, [ + ['buffer', [TypedArrayProto_buffer]], + ['byteLength', [TypedArrayProto_byteLength]], + ['byteOffset', [TypedArrayProto_byteOffset]], + ['copyWithin', TypedArrayProto_copyWithin, 2], + ['entries', TypedArrayProto_entries, 0], + ['fill', TypedArrayProto_fill, 1], + ['filter', TypedArrayProto_filter, 1], + ['keys', TypedArrayProto_keys, 0], + ['length', [TypedArrayProto_length]], + ['map', TypedArrayProto_map, 1], + ['set', TypedArrayProto_set, 1], + ['slice', TypedArrayProto_slice, 2], + ['sort', TypedArrayProto_sort, 1], + ['subarray', TypedArrayProto_subarray, 2], + ['values', TypedArrayProto_values, 0], + ['toString', ArrayProto_toString], + [wellKnownSymbols.toStringTag, [TypedArrayProto_toStringTag]], + ], realmRec.Intrinsics['%Object.prototype%']); + + BootstrapArrayPrototypeShared( + realmRec, + proto, + (thisValue) => { + Q(ValidateTypedArray(thisValue)); + }, + (O) => O.ArrayLength, + ); + + // 22.2.3.31 #sec-%typedarray%.prototype-@@iterator + { + const fn = X(Get(proto, new Value('values'))); + X(proto.DefineOwnProperty(wellKnownSymbols.iterator, Descriptor({ + Value: fn, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + + realmRec.Intrinsics['%TypedArray.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/TypedArrayPrototypes.mjs b/src/engine262/src/intrinsics/TypedArrayPrototypes.mjs new file mode 100644 index 0000000..a10bb1f --- /dev/null +++ b/src/engine262/src/intrinsics/TypedArrayPrototypes.mjs @@ -0,0 +1,16 @@ +import { typedArrayInfoByName } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-properties-of-typedarray-prototype-objects +export function BootstrapTypedArrayPrototypes(realmRec) { + Object.entries(typedArrayInfoByName).forEach(([TypedArray, info]) => { + const proto = BootstrapPrototype(realmRec, [ + ['BYTES_PER_ELEMENT', new Value(info.ElementSize), undefined, { + Writable: Value.false, + Configurable: Value.false, + }], + ], realmRec.Intrinsics['%TypedArray.prototype%']); + realmRec.Intrinsics[`%${TypedArray}.prototype%`] = proto; + }); +} diff --git a/src/engine262/src/intrinsics/WeakMap.mjs b/src/engine262/src/intrinsics/WeakMap.mjs new file mode 100644 index 0000000..9371838 --- /dev/null +++ b/src/engine262/src/intrinsics/WeakMap.mjs @@ -0,0 +1,38 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Get, + OrdinaryCreateFromConstructor, +} from '../abstract-ops/all.mjs'; +import { + Value, +} from '../value.mjs'; +import { + Q, +} from '../completion.mjs'; +import { AddEntriesFromIterable } from './Map.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +function WeakMapConstructor([iterable = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakMap.prototype%", « [[WeakMapData]] »). + const map = Q(OrdinaryCreateFromConstructor(NewTarget, '%WeakMap.prototype%', ['WeakMapData'])); + // 3. Set map.[[WeakMapData]] to a new empty List. + map.WeakMapData = []; + // 4. If iterable is either undefined or null, return map. + if (iterable === Value.undefined || iterable === Value.null) { + return map; + } + // 5. Let adder be ? Get(map, "set"). + const adder = Q(Get(map, new Value('set'))); + // 6. Return ? AddEntriesFromIterable(map, iterable, adder). + return Q(AddEntriesFromIterable(map, iterable, adder)); +} + +export function BootstrapWeakMap(realmRec) { + const c = BootstrapConstructor(realmRec, WeakMapConstructor, 'WeakMap', 0, realmRec.Intrinsics['%WeakMap.prototype%'], []); + + realmRec.Intrinsics['%WeakMap%'] = c; +} diff --git a/src/engine262/src/intrinsics/WeakMapPrototype.mjs b/src/engine262/src/intrinsics/WeakMapPrototype.mjs new file mode 100644 index 0000000..83a9556 --- /dev/null +++ b/src/engine262/src/intrinsics/WeakMapPrototype.mjs @@ -0,0 +1,123 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + SameValue, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +function WeakMapProto_delete([key = Value.undefined], { thisValue }) { + // 1. Let M be the this value. + const M = thisValue; + // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). + Q(RequireInternalSlot(M, 'WeakMapData')); + // 3. Let entries be the List that is M.[[WeakMapData]]. + const entries = M.WeakMapData; + // 4. If Type(key) is not Object, return false. + if (Type(key) !== 'Object') { + return Value.false; + } + // 5. For each Record { [[Key]], [[Value]] } p that is an element of entries, do + for (let i = 0; i < entries.length; i += 1) { + const p = entries[i]; + // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then + if (p.Key !== undefined && SameValue(p.Key, key) === Value.true) { + // i. Set p.[[Key]] to empty. + p.Key = undefined; + // ii. Set p.[[Value]] to empty. + p.Value = undefined; + // iii. return true. + return Value.true; + } + } + // 6. Return false. + return Value.false; +} + +function WeakMapProto_get([key = Value.undefined], { thisValue }) { + // 1. Let m be the this value. + const M = thisValue; + // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). + Q(RequireInternalSlot(M, 'WeakMapData')); + // 3. Let entries be the List that is M.[[WeakMapData]]. + const entries = M.WeakMapData; + // 4. If Type(key) is not Object, return undefined. + if (Type(key) !== 'Object') { + return Value.undefined; + } + // 5. For each Record { [[Key]], [[Value]] } p that is an element of entries, do + for (const p of entries) { + // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]]. + if (p.Key !== undefined && SameValue(p.Key, key) === Value.true) { + return p.Value; + } + } + // 6. Return undefined. + return Value.undefined; +} + +function WeakMapProto_has([key = Value.undefined], { thisValue }) { + // 1. Let M be the this value. + const M = thisValue; + // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). + Q(RequireInternalSlot(M, 'WeakMapData')); + // 3. Let entries be the List that is M.[[WeakMapData]]. + const entries = M.WeakMapData; + // 4. If Type(key) is not Object, return false. + if (Type(key) !== 'Object') { + return Value.false; + } + // 5. For each Record { [[Key]], [[Value]] } p that is an element of entries, do + for (const p of entries) { + // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return true. + if (p.Key !== undefined && SameValue(p.Key, key) === Value.true) { + return Value.true; + } + } + // 6. Return false. + return Value.false; +} + +function WeakMapProto_set([key = Value.undefined, value = Value.undefined], { thisValue }) { + // 1. Let M be the this value. + const M = thisValue; + // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]). + Q(RequireInternalSlot(M, 'WeakMapData')); + // 3. Let entries be the List that is M.[[WeakMapData]]. + const entries = M.WeakMapData; + // 4. If Type(key) is not Object, throw a TypeError exception. + if (Type(key) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'WeakCollectionNotObject', key); + } + // 5. For each Record { [[Key]], [[Value]] } p that is an element of entries, do + for (const p of entries) { + // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then + if (p.Key !== undefined && SameValue(p.Key, key) === Value.true) { + // i. Set p.[[Value]] to value. + p.Value = value; + // ii. Return M. + return M; + } + } + // 6. Let p be the Record { [[Key]]: key, [[Value]]: value }. + const p = { Key: key, Value: value }; + // 7. Append p as the last element of entries. + entries.push(p); + // 8. Return M. + return M; +} + +export function BootstrapWeakMapPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['delete', WeakMapProto_delete, 1], + ['get', WeakMapProto_get, 1], + ['has', WeakMapProto_has, 1], + ['set', WeakMapProto_set, 2], + ], realmRec.Intrinsics['%Object.prototype%'], 'WeakMap'); + + realmRec.Intrinsics['%WeakMap.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/WeakRef.mjs b/src/engine262/src/intrinsics/WeakRef.mjs new file mode 100644 index 0000000..313fbd9 --- /dev/null +++ b/src/engine262/src/intrinsics/WeakRef.mjs @@ -0,0 +1,31 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { AddToKeptObjects, OrdinaryCreateFromConstructor } from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-weakrefs/#sec-weak-ref-target +function WeakRefConstructor([target = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. If Type(target) is not Object, throw a TypeError exception. + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakRefPrototype%", « [[WeakRefTarget]] »). + const weakRef = Q(OrdinaryCreateFromConstructor(NewTarget, '%WeakRef.prototype%', ['WeakRefTarget'])); + // 4. Perfom ! AddToKeptObjects(target). + X(AddToKeptObjects(target)); + // 5. Set weakRef.[[WeakRefTarget]] to target. + weakRef.WeakRefTarget = target; + // 6. Return weakRef + return weakRef; +} + +export function BootstrapWeakRef(realmRec) { + const bigintConstructor = BootstrapConstructor(realmRec, WeakRefConstructor, 'WeakRef', 1, realmRec.Intrinsics['%WeakRef.prototype%'], []); + + realmRec.Intrinsics['%WeakRef%'] = bigintConstructor; +} diff --git a/src/engine262/src/intrinsics/WeakRefPrototype.mjs b/src/engine262/src/intrinsics/WeakRefPrototype.mjs new file mode 100644 index 0000000..c88f551 --- /dev/null +++ b/src/engine262/src/intrinsics/WeakRefPrototype.mjs @@ -0,0 +1,31 @@ +import { RequireInternalSlot, AddToKeptObjects } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// https://tc39.es/proposal-weakrefs/#sec-weak-ref.prototype.deref +function WeakRefProto_deref(args, { thisValue }) { + // 1. Let weakRef be the this value. + const weakRef = thisValue; + // 2. Perform ? RequireInternalSlot(weakRef, [[WeakRefTarget]]). + Q(RequireInternalSlot(weakRef, 'WeakRefTarget')); + // 3. Let target be the value of weakRef.[[WeakRefTarget]]. + const target = weakRef.WeakRefTarget; + // 4. If target is not empty, + if (target !== undefined) { + // a. Perform ! AddToKeptObjects(target). + X(AddToKeptObjects(target)); + // b. Return target. + return target; + } + // 5. Return undefined. + return Value.undefined; +} + +export function BootstrapWeakRefPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['deref', WeakRefProto_deref, 0], + ], realmRec.Intrinsics['%Object.prototype%'], 'WeakRef'); + + realmRec.Intrinsics['%WeakRef.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/WeakSet.mjs b/src/engine262/src/intrinsics/WeakSet.mjs new file mode 100644 index 0000000..f590f68 --- /dev/null +++ b/src/engine262/src/intrinsics/WeakSet.mjs @@ -0,0 +1,60 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { + IsCallable, + OrdinaryCreateFromConstructor, + Call, + Get, + GetIterator, + IteratorStep, + IteratorValue, + IteratorClose, +} from '../abstract-ops/all.mjs'; +import { Q, AbruptCompletion } from '../completion.mjs'; +import { BootstrapConstructor } from './Bootstrap.mjs'; + +// #sec-weakset-iterable +function WeakSetConstructor([iterable = Value.undefined], { NewTarget }) { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (NewTarget === Value.undefined) { + return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this); + } + // 2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakSet.prototype%", « [[WeakSetData]] »). + const set = Q(OrdinaryCreateFromConstructor(NewTarget, '%WeakSet.prototype%', ['WeakSetData'])); + // 3. Set set.[[WeakSetData]] to a new empty List. + set.WeakSetData = []; + // 4. If iterable is either undefined or null, return set. + if (iterable === Value.undefined || iterable === Value.null) { + return set; + } + // 5. Let adder be ? Get(set, "add"). + const adder = Q(Get(set, new Value('add'))); + // 6. If IsCallable(adder) is false, throw a TypeError exception. + if (IsCallable(adder) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', adder); + } + // 7. Let iteratorRecord be ? GetIterator(iterable). + const iteratorRecord = Q(GetIterator(iterable)); + // 8. Repeat, + while (true) { + // a. Let next be ? IteratorStep(iteratorRecord). + const next = Q(IteratorStep(iteratorRecord)); + // b. If next is false, return set. + if (next === Value.false) { + return set; + } + // c. Let nextValue be ? IteratorValue(next). + const nextValue = Q(IteratorValue(next)); + // d. Let status be Call(adder, set, « nextValue »). + const status = Call(adder, set, [nextValue]); + // e. If status is an abrupt completion, return ? IteratorClose(iteratorRecord, status). + if (status instanceof AbruptCompletion) { + return Q(IteratorClose(iteratorRecord, status)); + } + } +} + +export function BootstrapWeakSet(realmRec) { + const c = BootstrapConstructor(realmRec, WeakSetConstructor, 'WeakSet', 0, realmRec.Intrinsics['%WeakSet.prototype%'], []); + realmRec.Intrinsics['%WeakSet%'] = c; +} diff --git a/src/engine262/src/intrinsics/WeakSetPrototype.mjs b/src/engine262/src/intrinsics/WeakSetPrototype.mjs new file mode 100644 index 0000000..a5541af --- /dev/null +++ b/src/engine262/src/intrinsics/WeakSetPrototype.mjs @@ -0,0 +1,97 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + SameValue, + RequireInternalSlot, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { BootstrapPrototype } from './Bootstrap.mjs'; + +// #sec-weakset.prototype.add +function WeakSetProto_add([value = Value.undefined], { thisValue }) { + // 1. Let S be this value. + const S = thisValue; + // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]). + Q(RequireInternalSlot(S, 'WeakSetData')); + // 3. If Type(value) is not Object, throw a TypeError exception. + if (Type(value) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'WeakCollectionNotObject', value); + } + // 4. Let entries be the List that is S.[[WeakSetData]]. + const entries = S.WeakSetData; + // 5. For each e that is an element of entries, do + for (const e of entries) { + // a. If e is not empty and SameValue(e, value) is true, then + if (e !== undefined && SameValue(e, value) === Value.true) { + // i. Return S. + return S; + } + } + // 6. Append value as the last element of entries. + entries.push(value); + // 7. Return S. + return S; +} + +// #sec-weakset.prototype.delete +function WeakSetProto_delete([value = Value.undefined], { thisValue }) { + // 1. Let S be the this value.` + const S = thisValue; + // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]). + Q(RequireInternalSlot(S, 'WeakSetData')); + // 3. If Type(value) is not Object, return false. + if (Type(value) !== 'Object') { + return Value.false; + } + // 4. Let entries be the List that is S.[[WeakSetData]]. + const entries = S.WeakSetData; + // 5. For each e that is an element of entries, do + for (let i = 0; i < entries.length; i += 1) { + const e = entries[i]; + // i. If e is not empty and SameValue(e, value) is true, then + if (e !== undefined && SameValue(e, value) === Value.true) { + // i. Replace the element of entries whose value is e with an element whose value is empty. + entries[i] = undefined; + // ii. Return true. + return Value.true; + } + } + // 6. Return false. + return Value.false; +} + +// #sec-weakset.prototype.has +function WeakSetProto_has([value = Value.undefined], { thisValue }) { + // 1. Let S be the this value. + const S = thisValue; + // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]). + Q(RequireInternalSlot(S, 'WeakSetData')); + // 3. Let entries be the List that is S.[[WeakSetData]]. + const entries = S.WeakSetData; + // 4. If Type(value) is not Object, return false. + if (Type(value) !== 'Object') { + return Value.false; + } + // 5. For each e that is an element of entries, do + for (const e of entries) { + // a. If e is not empty and SameValue(e, value) is true, return true. + if (e !== undefined && SameValue(e, value) === Value.true) { + return Value.true; + } + } + // 6. Return false. + return Value.false; +} + +export function BootstrapWeakSetPrototype(realmRec) { + const proto = BootstrapPrototype(realmRec, [ + ['add', WeakSetProto_add, 1], + ['delete', WeakSetProto_delete, 1], + ['has', WeakSetProto_has, 1], + ], realmRec.Intrinsics['%Object.prototype%'], 'WeakSet'); + + realmRec.Intrinsics['%WeakSet.prototype%'] = proto; +} diff --git a/src/engine262/src/intrinsics/eval.mjs b/src/engine262/src/intrinsics/eval.mjs new file mode 100644 index 0000000..5109697 --- /dev/null +++ b/src/engine262/src/intrinsics/eval.mjs @@ -0,0 +1,26 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Q } from '../completion.mjs'; +import { Value } from '../value.mjs'; +import { + Assert, + CreateBuiltinFunction, + PerformEval, + SetFunctionLength, + SetFunctionName, + // GetThisEnvironment, +} from '../abstract-ops/all.mjs'; + +function TheEval([x = Value.undefined]) { + Assert(surroundingAgent.executionContextStack.length >= 2); + const callerContext = surroundingAgent.executionContextStack[surroundingAgent.executionContextStack.length - 2]; + const callerRealm = callerContext.Realm; + return Q(PerformEval(x, callerRealm, false, false)); +} + +export function BootstrapEval(realmRec) { + const it = CreateBuiltinFunction(TheEval, [], realmRec); + SetFunctionName(it, new Value('eval')); + SetFunctionLength(it, new Value(1)); + + realmRec.Intrinsics['%eval%'] = it; +} diff --git a/src/engine262/src/intrinsics/isFinite.mjs b/src/engine262/src/intrinsics/isFinite.mjs new file mode 100644 index 0000000..2b91012 --- /dev/null +++ b/src/engine262/src/intrinsics/isFinite.mjs @@ -0,0 +1,23 @@ +import { + ToNumber, + CreateBuiltinFunction, + SetFunctionName, + SetFunctionLength, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; + +function IsFinite([number = Value.undefined]) { + const num = Q(ToNumber(number)); + if (num.isNaN() || num.isInfinity()) { + return Value.false; + } + return Value.true; +} + +export function BootstrapIsFinite(realmRec) { + const fn = CreateBuiltinFunction(IsFinite, [], realmRec); + X(SetFunctionName(fn, new Value('isFinite'))); + X(SetFunctionLength(fn, new Value(1))); + realmRec.Intrinsics['%isFinite%'] = fn; +} diff --git a/src/engine262/src/intrinsics/isNaN.mjs b/src/engine262/src/intrinsics/isNaN.mjs new file mode 100644 index 0000000..07720dd --- /dev/null +++ b/src/engine262/src/intrinsics/isNaN.mjs @@ -0,0 +1,23 @@ +import { + ToNumber, + CreateBuiltinFunction, + SetFunctionName, + SetFunctionLength, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; + +function IsNaN([number = Value.undefined]) { + const num = Q(ToNumber(number)); + if (num.isNaN()) { + return Value.true; + } + return Value.false; +} + +export function BootstrapIsNaN(realmRec) { + const fn = CreateBuiltinFunction(IsNaN, [], realmRec); + X(SetFunctionName(fn, new Value('isNaN'))); + X(SetFunctionLength(fn, new Value(1))); + realmRec.Intrinsics['%isNaN%'] = fn; +} diff --git a/src/engine262/src/intrinsics/parseFloat.mjs b/src/engine262/src/intrinsics/parseFloat.mjs new file mode 100644 index 0000000..46b9ccd --- /dev/null +++ b/src/engine262/src/intrinsics/parseFloat.mjs @@ -0,0 +1,24 @@ +import { + CreateBuiltinFunction, + SetFunctionName, + SetFunctionLength, + ToString, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { Value } from '../value.mjs'; +import { MV_StrDecimalLiteral, TrimString } from '../runtime-semantics/all.mjs'; + +function ParseFloat([string = Value.undefined]) { + const inputString = Q(ToString(string)); + const trimmedString = X(TrimString(inputString, 'start')).stringValue(); + const mathFloat = MV_StrDecimalLiteral(trimmedString, true); + // MV_StrDecimalLiteral handles -0 automatically. + return mathFloat; +} + +export function BootstrapParseFloat(realmRec) { + const fn = CreateBuiltinFunction(ParseFloat, [], realmRec); + X(SetFunctionName(fn, new Value('parseFloat'))); + X(SetFunctionLength(fn, new Value(1))); + realmRec.Intrinsics['%parseFloat%'] = fn; +} diff --git a/src/engine262/src/intrinsics/parseInt.mjs b/src/engine262/src/intrinsics/parseInt.mjs new file mode 100644 index 0000000..0dd3f6f --- /dev/null +++ b/src/engine262/src/intrinsics/parseInt.mjs @@ -0,0 +1,103 @@ +import { + Assert, + CreateBuiltinFunction, + SetFunctionName, + SetFunctionLength, + ToInt32, + ToString, +} from '../abstract-ops/all.mjs'; +import { TrimString } from '../runtime-semantics/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { Value } from '../value.mjs'; + +function digitToNumber(digit) { + digit = digit.charCodeAt(0); + if (digit < 0x30 /* 0 */) { + return NaN; + } + if (digit <= 0x39 /* 9 */) { + return digit - 0x30; + } + // Convert to lower case. + digit &= ~0x20; // eslint-disable-line no-bitwise + if (digit < 0x41 /* A */) { + return NaN; + } + if (digit <= 0x5a /* Z */) { + return digit - 0x41 /* A */ + 10; + } + return NaN; +} + +function stringToRadixNumber(str, R) { + let num = 0; + for (let i = 0; i < str.length; i += 1) { + const power = str.length - i - 1; + const multiplier = R ** power; + const dig = digitToNumber(str[i]); + Assert(!Number.isNaN(dig) && dig < R); + num += dig * multiplier; + } + return num; +} + +function searchNotRadixDigit(str, R) { + for (let i = 0; i < str.length; i += 1) { + const num = digitToNumber(str[i]); + if (Number.isNaN(num) || num >= R) { + return i; + } + } + return str.length; +} + +function ParseInt([string = Value.undefined, radix = Value.undefined]) { + const inputString = Q(ToString(string)); + let S = X(TrimString(inputString, 'start')).stringValue(); + let sign = 1; + if (S !== '' && S[0] === '\x2D') { + sign = -1; + } + if (S !== '' && (S[0] === '\x2B' || S[0] === '\x2D')) { + S = S.slice(1); + } + + let R = Q(ToInt32(radix)).numberValue(); + let stripPrefix = true; + if (R !== 0) { + if (R < 2 || R > 36) { + return new Value(NaN); + } + if (R !== 16) { + stripPrefix = false; + } + } else { + R = 10; + } + if (stripPrefix === true) { + if (S.length >= 2 && (S.startsWith('0x') || S.startsWith('0X'))) { + S = S.slice(2); + R = 16; + } + } + const Z = S.slice(0, searchNotRadixDigit(S, R)); + if (Z === '') { + return new Value(NaN); + } + const mathInt = stringToRadixNumber(Z, R); + if (mathInt === 0) { + if (sign === -1) { + return new Value(-0); + } + return new Value(+0); + } + const number = mathInt; + return new Value(sign * number); +} + +export function BootstrapParseInt(realmRec) { + const fn = CreateBuiltinFunction(ParseInt, [], realmRec); + X(SetFunctionName(fn, new Value('parseInt'))); + X(SetFunctionLength(fn, new Value(2))); + realmRec.Intrinsics['%parseInt%'] = fn; +} diff --git a/src/engine262/src/messages.mjs b/src/engine262/src/messages.mjs new file mode 100644 index 0000000..4b5f5a6 --- /dev/null +++ b/src/engine262/src/messages.mjs @@ -0,0 +1,124 @@ +import { surroundingAgent } from './engine.mjs'; +import { Value } from './value.mjs'; +import { inspect } from './api.mjs'; + +function i(V) { + if (V instanceof Value) { + return inspect(V, surroundingAgent.currentRealmRecord, true); + } + return `${V}`; +} + +export const Raw = (s) => s; + +export const AlreadyDeclared = (n) => `${i(n)} is already declared`; +export const ArrayBufferDetached = () => 'Attempt to access detached ArrayBuffer'; +export const ArrayBufferShared = () => 'Attempt to access shared ArrayBuffer'; +export const ArrayPastSafeLength = () => 'Cannot make length of array-like object surpass the bounds of an integer index'; +export const ArrayEmptyReduce = () => 'Cannot reduce an empty array with no initial value'; +export const AssignmentToConstant = (n) => `Assignment to constant variable ${i(n)}`; +export const BigIntDivideByZero = () => 'Division by zero'; +export const BigIntNegativeExponent = () => 'Exponent must be positive'; +export const BigIntUnsignedRightShift = () => 'BigInt has no unsigned right shift, use >> instead'; +export const BufferContentTypeMismatch = () => 'Newly created TypedArray did not match exemplar\'s content type'; +export const BufferDetachKeyMismatch = (k, b) => `${i(k)} is not the [[ArrayBufferDetachKey]] of ${i(b)}`; +export const CannotAllocateDataBlock = () => 'Cannot allocate memory'; +export const CannotCreateProxyWith = (x, y) => `Cannot create a proxy with a ${x} as ${y}`; +export const CannotConvertDecimalToBigInt = (n) => `Cannot convert ${i(n)} to a BigInt because it is not an integer`; +export const CannotConvertSymbol = (t) => `Cannot convert a Symbol value to a ${t}`; +export const CannotConvertToBigInt = (v) => `Cannot convert ${i(v)} to a BigInt`; +export const CannotConvertToObject = (t) => `Cannot convert ${t} to object`; +export const CannotDefineProperty = (p) => `Cannot define property ${i(p)}`; +export const CannotDeleteProperty = (p) => `Cannot delete property ${i(p)}`; +export const CannotDeleteSuper = () => 'Cannot delete a super property'; +export const CannotJSONSerializeBigInt = () => 'Cannot serialize a BigInt to JSON'; +export const CannotMixBigInts = () => 'Cannot mix BigInt and other types, use explicit conversions'; +export const CannotResolvePromiseWithItself = () => 'Cannot resolve a promise with itself'; +export const CannotSetProperty = (p, o) => `Cannot set property ${i(p)} on ${i(o)}`; +export const ConstructorNonCallable = (f) => `${i(f)} cannot be invoked without new`; +export const CouldNotResolveModule = (s) => `Could not resolve module ${i(s)}`; +export const DataViewOOB = () => 'Offset is outside the bounds of the DataView'; +export const DateInvalidTime = () => 'Invalid time'; +export const DerivedConstructorReturnedNonObject = () => 'Derived constructors may only return object or undefined'; +export const GeneratorRunning = () => 'Cannot manipulate a running generator'; +export const InternalSlotMissing = (o, s) => `Internal slot ${s} is missing for ${i(o)}`; +export const InvalidArrayLength = (l) => `Invalid array length: ${i(l)}`; +export const InvalidHint = (v) => `Invalid hint: ${i(v)}`; +export const InvalidPropertyDescriptor = () => 'Invalid property descriptor. Cannot both specify accessors and a value or writable attribute'; +export const InvalidRadix = () => 'Radix must be between 2 and 36, inclusive'; +export const InvalidReceiver = (f, v) => `${f} called on invalid receiver: ${i(v)}`; +export const InvalidRegExpFlags = (f) => `Invalid RegExp flags: ${f}`; +export const InvalidThis = () => 'Invalid `this` access'; +export const IteratorThrowMissing = () => 'The iterator does not provide a throw method'; +export const JSONCircular = () => 'Cannot JSON stringify a circular structure'; +export const JSONUnexpectedToken = () => 'Unexpected token in JSON'; +export const JSONUnexpectedChar = (c) => `Unexpected character ${c} in JSON`; +export const JSONExpected = (e, a) => `Expected character ${e} but got ${a} in JSON`; +export const NegativeIndex = (n) => `${n} cannot be negative`; +export const NormalizeInvalidForm = () => 'Invalid normalization form'; +export const NotAConstructor = (v) => `${i(v)} is not a constructor`; +export const NotAFunction = (v) => `${i(v)} is not a function`; +export const NotATypeObject = (t, v) => `${i(v)} is not a ${t} object`; +export const NotAnObject = (v) => `${i(v)} is not an object`; +export const NotASymbol = (v) => `${i(v)} is not a symbol`; +export const NotDefined = (n) => `${i(n)} is not defined`; +export const NotPropertyName = (p) => `${i(p)} is not a valid property name`; +export const NumberFormatRange = (m) => `Invalid format range for ${m}`; +export const ObjectToPrimitive = () => 'Cannot convert object to primitive value'; +export const ObjectPrototypeType = () => 'Object prototype must be an Object or null'; +export const ObjectSetPrototype = () => 'Could not set prototype of object'; +export const OutOfRange = (n) => `${n} is out of range`; +export const PromiseAnyRejected = () => 'No promises passed to Promise.any were fulfilled'; +export const PromiseCapabilityFunctionAlreadySet = (f) => `Promise ${f} function already set`; +export const PromiseRejectFunction = (v) => `Promise reject function ${i(v)} is not callable`; +export const PromiseResolveFunction = (v) => `Promise resolve function ${i(v)} is not callable`; +export const ProxyRevoked = (n) => `Cannot perform '${n}' on a proxy that has been revoked`; +export const ProxyDefinePropertyNonConfigurable = (p) => `'defineProperty' on proxy: trap returned truish for defining non-configurable property ${i(p)} which is either non-existent or configurable in the proxy target`; +export const ProxyDefinePropertyNonConfigurableWritable = (p) => `'defineProperty' on proxy: trap returned truish for defining non-configurable property ${i(p)} which cannot be non-writable, unless there exists a corresponding non-configurable, non-writable own property of the target object.`; +export const ProxyDefinePropertyNonExtensible = (p) => `'defineProperty' on proxy: trap returned truish for adding property ${i(p)} to the non-extensible proxy target`; +export const ProxyDefinePropertyIncompatible = (p) => `'defineProperty' on proxy: trap returned truish for adding property ${i(p)} that is incompatible with the existing property in the proxy target`; +export const ProxyDeletePropertyNonConfigurable = (p) => `'deleteProperty' on proxy: trap returned truthy for property ${i(p)} which is non-configurable in the proxy target`; +export const ProxyDeletePropertyNonExtensible = (p) => `'deleteProperty' on proxy: trap returned truthy for property ${i(p)} but the proxy target is non-extensible`; +export const ProxyGetNonConfigurableData = (p) => `'get' on proxy: property ${i(p)} is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value`; +export const ProxyGetNonConfigurableAccessor = (p) => `'get' on proxy: property ${i(p)} is a non-configurable accessor property on the proxy target and does not have a getter function, but the trap did not return 'undefined'`; +export const ProxyGetPrototypeOfInvalid = () => '\'getPrototypeOf\' on proxy: trap returned neither object nor null'; +export const ProxyGetPrototypeOfNonExtensible = () => '\'getPrototypeOf\' on proxy: proxy target is non-extensible but the trap did not return its actual prototype'; +export const ProxyGetOwnPropertyDescriptorIncompatible = (p) => `'getOwnPropertyDescriptor' on proxy: trap returned descriptor for property ${i(p)} that is incompatible with the existing property in the proxy target`; +export const ProxyGetOwnPropertyDescriptorInvalid = (p) => `'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined for property ${i(p)}`; +export const ProxyGetOwnPropertyDescriptorUndefined = (p) => `'getOwnPropertyDescriptor' on proxy: trap returned undefined for property ${i(p)} which is non-configurable in the proxy target`; +export const ProxyGetOwnPropertyDescriptorNonExtensible = (p) => `'getOwnPropertyDescriptor' on proxy: trap returned undefined for property ${i(p)} which exists in the non-extensible target`; +export const ProxyGetOwnPropertyDescriptorNonConfigurable = (p) => `'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property ${i(p)} which is either non-existent or configurable in the proxy target`; +export const ProxyGetOwnPropertyDescriptorNonConfigurableWritable = (p) => `'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property ${i(p)} which is writable or configurable in the proxy target`; +export const ProxyHasNonConfigurable = (p) => `'has' on proxy: trap returned falsy for property ${i(p)} which exists in the proxy target as non-configurable`; +export const ProxyHasNonExtensible = (p) => `'has' on proxy: trap returned falsy for property ${i(p)} but the proxy target is not extensible`; +export const ProxyIsExtensibleInconsistent = (e) => `'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is ${i(e)})`; +export const ProxyOwnKeysMissing = (p) => `'ownKeys' on proxy: trap result did not include ${i(p)}`; +export const ProxyOwnKeysNonExtensible = () => '\'ownKeys\' on proxy: trap result returned extra keys but proxy target is non-extensible'; +export const ProxyOwnKeysDuplicateEntries = () => '\'ownKeys\' on proxy: trap returned duplicate entries'; +export const ProxyPreventExtensionsExtensible = () => '\'preventExtensions\' on proxy: trap returned truthy but the proxy target is extensible'; +export const ProxySetPrototypeOfNonExtensible = () => '\'setPrototypeOf\' on proxy: trap returned truthy for setting a new prototype on the non-extensible proxy target'; +export const ProxySetFrozenData = (p) => `'set' on proxy: trap returned truthy for property ${i(p)} which exists in the proxy target as a non-configurable and non-writable data property with a different value`; +export const ProxySetFrozenAccessor = (p) => `'set' on proxy: trap returned truish for property ${i(p)} which exists in the proxy target as a non-configurable and non-writable accessor property without a setter`; +export const RegExpArgumentNotAllowed = (m) => `First argument to ${m} must not be a regular expression`; +export const RegExpExecNotObject = (o) => `${i(o)} is not object or null`; +export const ResolutionNullOrAmbiguous = (r, n, m) => (r === null + ? `Could not resolve import ${i(n)} from ${m.HostDefined.specifier}` + : `Star export ${i(n)} from ${m.HostDefined.specifier} is ambiguous`); +export const SpeciesNotConstructor = () => 'object.constructor[Symbol.species] is not a constructor'; +export const StrictModeDelete = (n) => `Cannot not delete property ${i(n)}`; +export const StrictPoisonPill = () => 'The caller, callee, and arguments properties may not be accessed on functions or the arguments objects for calls to them'; +export const StringRepeatCount = (v) => `Count ${i(v)} is invalid`; +export const StringCodePointInvalid = (n) => `Invalid code point ${i(n)}`; +export const StringPrototypeMethodGlobalRegExp = (m) => `The RegExp passed to String.prototype.${m} must have the global flag`; +export const SubclassLengthTooSmall = (v) => `Subclass constructor returned a smaller-than-requested object ${i(v)}`; +export const SubclassSameValue = (v) => `Subclass constructor returned the same object ${i(v)}`; +export const TargetMatchesHeldValue = (v) => `heldValue ${i(v)} matches target`; +export const TypedArrayCreationOOB = () => 'Sum of start offset and byte length should be less than the size of underlying buffer'; +export const TypedArrayLengthAlignment = (n, m) => `Size of ${n} should be a multiple of ${m}`; +export const TypedArrayOOB = () => 'Sum of start offset and byte length should be less than the size of the TypedArray'; +export const TypedArrayOffsetAlignment = (n, m) => `Start offset of ${n} should be a multiple of ${m}`; +export const TypedArrayTooSmall = () => 'Derived TypedArray constructor created an array which was too small'; +export const UnableToSeal = (o) => `Unable to seal object ${i(o)}`; +export const UnableToFreeze = (o) => `Unable to freeze object ${i(o)}`; +export const UnableToPreventExtensions = (o) => `Unable to prevent extensions on object ${i(o)}`; +export const WeakCollectionNotObject = (v) => `${i(v)} is not a valid weak collectection entry object`; diff --git a/src/engine262/src/modules.mjs b/src/engine262/src/modules.mjs new file mode 100644 index 0000000..3111ea7 --- /dev/null +++ b/src/engine262/src/modules.mjs @@ -0,0 +1,400 @@ +import { NewModuleEnvironment } from './environment.mjs'; +import { Value, Type } from './value.mjs'; +import { ExecutionContext, HostResolveImportedModule, surroundingAgent } from './engine.mjs'; +import { + Assert, + Call, + NewPromiseCapability, + GetModuleNamespace, + InnerModuleEvaluation, + InnerModuleLinking, + SameValue, + GetAsyncCycleRoot, + AsyncBlockStart, + PromiseCapabilityRecord, +} from './abstract-ops/all.mjs'; +import { + Completion, + NormalCompletion, + AbruptCompletion, + Q, + X, +} from './completion.mjs'; +import { + isFunctionDeclaration, + isGeneratorDeclaration, + isAsyncFunctionDeclaration, + isAsyncGeneratorDeclaration, +} from './ast.mjs'; +import { Evaluate_Module } from './evaluator.mjs'; +import { + BoundNames_ModuleItem, + BoundNames_VariableDeclaration, + IsConstantDeclaration, + LexicallyScopedDeclarations_Module, + VarScopedDeclarations_ModuleBody, +} from './static-semantics/all.mjs'; +import { InstantiateFunctionObject } from './runtime-semantics/all.mjs'; + +// #importentry-record +export class ImportEntryRecord { + constructor({ + ModuleRequest, + ImportName, + LocalName, + }) { + Assert(Type(ModuleRequest) === 'String'); + Assert(Type(ImportName) === 'String'); + Assert(Type(LocalName) === 'String'); + this.ModuleRequest = ModuleRequest; + this.ImportName = ImportName; + this.LocalName = LocalName; + } +} + +// #exportentry-record +export class ExportEntryRecord { + constructor({ + ExportName, + ModuleRequest, + ImportName, + LocalName, + }) { + Assert(Type(ExportName) === 'String' || Type(ExportName) === 'Null'); + Assert(Type(ModuleRequest) === 'String' || Type(ModuleRequest) === 'Null'); + Assert(Type(ImportName) === 'String' || Type(ImportName) === 'Null'); + Assert(Type(LocalName) === 'String' || Type(LocalName) === 'Null'); + this.ExportName = ExportName; + this.ModuleRequest = ModuleRequest; + this.ImportName = ImportName; + this.LocalName = LocalName; + } +} + +// #resolvedbinding-record +export class ResolvedBindingRecord { + constructor({ Module, BindingName }) { + Assert(Module instanceof AbstractModuleRecord); + Assert(Type(BindingName) === 'String'); + this.Module = Module; + this.BindingName = BindingName; + } + + mark(m) { + m(this.Module); + } +} + +// 15.2.1.15 #sec-abstract-module-records +export class AbstractModuleRecord { + constructor({ + Realm, + Environment, + Namespace, + HostDefined, + }) { + this.Realm = Realm; + this.Environment = Environment; + this.Namespace = Namespace; + this.HostDefined = HostDefined; + } + + mark(m) { + m(this.Realm); + m(this.Environment); + m(this.Namespace); + } +} + +// 15.2.1.16 #sec-cyclic-module-records +export class CyclicModuleRecord extends AbstractModuleRecord { + constructor(init) { + super(init); + this.Status = init.Status; + this.EvaluationError = init.EvaluationError; + this.DFSIndex = init.DFSIndex; + this.DFSAncestorIndex = init.DFSAncestorIndex; + this.RequestedModules = init.RequestedModules; + this.Async = init.Async; + this.AsyncEvaluating = init.AsyncEvaluating; + this.TopLevelCapability = init.TopLevelCapability; + this.AsyncParentModules = init.AsyncParentModules; + this.PendingAsyncDependencies = init.PendingAsyncDependencies; + } + + // 15.2.1.16.1 #sec-moduledeclarationlinking + Link() { + const module = this; + Assert(module.Status !== 'linking' && module.Status !== 'evaluating'); + const stack = []; + const result = InnerModuleLinking(module, stack, 0); + if (result instanceof AbruptCompletion) { + for (const m of stack) { + Assert(m.Status === 'linking'); + m.Status = 'unlinked'; + m.Environment = Value.undefined; + m.DFSIndex = Value.undefined; + m.DFSAncestorIndex = Value.undefined; + } + Assert(module.Status === 'unlinked'); + return result; + } + Assert(module.Status === 'linked' || module.Status === 'evaluated'); + Assert(stack.length === 0); + return Value.undefined; + } + + // 15.2.1.16.2 #sec-moduleevaluation + Evaluate() { + let module = this; + Assert(module.Status === 'linked' || module.Status === 'evaluated'); + if (module.Status === 'evaluated') { + module = GetAsyncCycleRoot(module); + } + if (module.TopLevelCapability !== Value.undefined) { + return module.TopLevelCapability.Promise; + } + const stack = []; + const capability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + module.TopLevelCapability = capability; + const result = InnerModuleEvaluation(module, stack, 0); + if (result instanceof AbruptCompletion) { + for (const m of stack) { + Assert(m.Status === 'evaluating'); + m.Status = 'evaluated'; + m.EvaluationError = result; + } + Assert(module.Status === 'evaluated' && module.EvaluationError === result); + X(Call(capability.Reject, Value.undefined, [result.Value])); + } else { + Assert(module.Status === 'evaluated' && module.EvaluationError === Value.undefined); + if (module.AsyncEvaluating === Value.false) { + X(Call(capability.Resolve, Value.undefined, [Value.undefined])); + } + Assert(stack.length === 0); + } + return capability.Promise; + } + + mark(m) { + super.mark(m); + m(this.EvaluationError); + } +} + +// 15.2.1.17 #sec-source-text-module-records +export class SourceTextModuleRecord extends CyclicModuleRecord { + constructor(init) { + super(init); + + this.ImportMeta = init.ImportMeta; + this.ECMAScriptCode = init.ECMAScriptCode; + this.Context = init.Context; + this.ImportEntries = init.ImportEntries; + this.LocalExportEntries = init.LocalExportEntries; + this.IndirectExportEntries = init.IndirectExportEntries; + this.StarExportEntries = init.StarExportEntries; + } + + // 15.2.1.17.2 #sec-getexportednames + GetExportedNames(exportStarSet) { + const module = this; + if (!exportStarSet) { + exportStarSet = []; + } + Assert(Array.isArray(exportStarSet) && exportStarSet.every((e) => e instanceof SourceTextModuleRecord)); + if (exportStarSet.includes(module)) { + // Assert: We've reached the starting point of an import * circularity. + return []; + } + exportStarSet.push(module); + const exportedNames = []; + for (const e of module.LocalExportEntries) { + // Assert: module provides the direct binding for this export. + exportedNames.push(e.ExportName); + } + for (const e of module.IndirectExportEntries) { + // Assert: module imports a specific binding for this export. + exportedNames.push(e.ExportName); + } + for (const e of module.StarExportEntries) { + const requestedModule = Q(HostResolveImportedModule(module, e.ModuleRequest)); + const starNames = Q(requestedModule.GetExportedNames(exportStarSet)); + for (const n of starNames) { + if (SameValue(n, new Value('default')) === Value.false) { + if (!exportedNames.includes(n)) { + exportedNames.push(n); + } + } + } + } + return exportedNames; + } + + // 15.2.1.17.3 #sec-resolveexport + ResolveExport(exportName, resolveSet) { + const module = this; + if (!resolveSet) { + resolveSet = []; + } + Assert(Array.isArray(resolveSet) && resolveSet.every((e) => 'Module' in e && 'ExportName' in e)); + for (const r of resolveSet) { + if (module === r.Module && SameValue(exportName, r.ExportName) === Value.true) { + // Assert: This is a circular import request. + return null; + } + } + resolveSet.push({ Module: module, ExportName: exportName }); + for (const e of module.LocalExportEntries) { + if (SameValue(exportName, e.ExportName) === Value.true) { + // Assert: module provides the direct binding for this export. + return new ResolvedBindingRecord({ + Module: module, + BindingName: e.LocalName, + }); + } + } + for (const e of module.IndirectExportEntries) { + if (SameValue(exportName, e.ExportName) === Value.true) { + // Assert: module provides the direct binding for this export. + const importedModule = Q(HostResolveImportedModule(module, e.ModuleRequest)); + return importedModule.ResolveExport(e.ImportName, resolveSet); + } + } + if (SameValue(exportName, new Value('default')) === Value.true) { + // Assert: A default export was not explicitly defined by this module. + return null; + // NOTE: A default export cannot be provided by an export *. + } + let starResolution = null; + for (const e of module.StarExportEntries) { + const importedModule = Q(HostResolveImportedModule(module, e.ModuleRequest)); + const resolution = Q(importedModule.ResolveExport(exportName, resolveSet)); + if (resolution === 'ambiguous') { + return 'ambiguous'; + } + if (resolution !== null) { + Assert(resolution instanceof ResolvedBindingRecord); + if (starResolution === null) { + starResolution = resolution; + } else { + // Assert: There is more than one * import that includes the requested name. + if (resolution.Module !== starResolution.Module || SameValue(resolution.BindingName, starResolution.BindingName) === Value.false) { + return 'ambiguous'; + } + } + } + } + return starResolution; + } + + // 15.2.1.17.4 #sec-source-text-module-record-initialize-environment + InitializeEnvironment() { + const module = this; + for (const e of module.IndirectExportEntries) { + const resolution = Q(module.ResolveExport(e.ExportName)); + if (resolution === null || resolution === 'ambiguous') { + return surroundingAgent.Throw( + 'SyntaxError', + 'ResolutionNullOrAmbiguous', + resolution, + e.ExportName, + module, + ); + } + // Assert: resolution is a ResolvedBinding Record. + } + // Assert: All named exports from module are resolvable. + const realm = module.Realm; + Assert(realm !== Value.undefined); + const env = NewModuleEnvironment(realm.GlobalEnv); + module.Environment = env; + const envRec = env.EnvironmentRecord; + for (const ie of module.ImportEntries) { + const importedModule = X(HostResolveImportedModule(module, ie.ModuleRequest)); + if (ie.ImportName.stringValue() === '*') { + const namespace = Q(GetModuleNamespace(importedModule)); + X(envRec.CreateImmutableBinding(ie.LocalName, Value.true)); + envRec.InitializeBinding(ie.LocalName, namespace); + } else { + const resolution = Q(importedModule.ResolveExport(ie.ImportName)); + if (resolution === null || resolution === 'ambiguous') { + return surroundingAgent.Throw( + 'SyntaxError', + 'ResolutionNullOrAmbiguous', + resolution, + ie.ImportName, + importedModule, + ); + } + envRec.CreateImportBinding(ie.LocalName, resolution.Module, resolution.BindingName); + } + } + + const moduleContext = new ExecutionContext(); + moduleContext.Function = Value.null; + Assert(module.Realm !== Value.undefined); + moduleContext.Realm = module.Realm; + moduleContext.ScriptOrModule = module; + moduleContext.VariableEnvironment = module.Environment; + moduleContext.LexicalEnvironment = module.Environment; + module.Context = moduleContext; + surroundingAgent.executionContextStack.push(moduleContext); + + const code = module.ECMAScriptCode.body; + const varDeclarations = VarScopedDeclarations_ModuleBody(code); + const declaredVarNames = []; + for (const d of varDeclarations) { + for (const dn of BoundNames_VariableDeclaration(d)) { + if (!declaredVarNames.includes(dn)) { + X(envRec.CreateMutableBinding(new Value(dn), Value.false)); + envRec.InitializeBinding(new Value(dn), Value.undefined); + declaredVarNames.push(dn); + } + } + } + const lexDeclarations = LexicallyScopedDeclarations_Module(code); + for (const d of lexDeclarations) { + for (const dn of BoundNames_ModuleItem(d)) { + if (IsConstantDeclaration(d)) { + Q(envRec.CreateImmutableBinding(new Value(dn), Value.true)); + } else { + Q(envRec.CreateMutableBinding(new Value(dn), Value.false)); + } + if (isFunctionDeclaration(d) || isGeneratorDeclaration(d) + || isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d)) { + const fo = InstantiateFunctionObject(d, env); + envRec.InitializeBinding(new Value(dn), fo); + } + } + } + + surroundingAgent.executionContextStack.pop(moduleContext); + + return new NormalCompletion(undefined); + } + + // 15.2.1.17.5 #sec-source-text-module-record-execute-module + ExecuteModule(capability) { + const module = this; + const moduleContext = module.Context; + if (module.Async === Value.false) { + Assert(capability === undefined); + surroundingAgent.executionContextStack.push(moduleContext); + const result = Evaluate_Module(module.ECMAScriptCode.body); + surroundingAgent.executionContextStack.pop(moduleContext); + // Resume the context that is now on the top of the execution context stack as the running execution context. + return Completion(result); + } else { + Assert(capability instanceof PromiseCapabilityRecord); + X(AsyncBlockStart(capability, module.ECMAScriptCode.body, moduleContext)); + return Value.undefined; + } + } + + mark(m) { + super.mark(m); + m(this.ImportMeta); + m(this.Context); + } +} diff --git a/src/engine262/src/parse.mjs b/src/engine262/src/parse.mjs new file mode 100644 index 0000000..7025eb9 --- /dev/null +++ b/src/engine262/src/parse.mjs @@ -0,0 +1,1156 @@ +import * as acorn from 'acorn'; +import { surroundingAgent } from './engine.mjs'; +import { ExportEntryRecord, SourceTextModuleRecord } from './modules.mjs'; +import { Value } from './value.mjs'; +import { + ModuleRequests_ModuleItemList, + ImportEntries_ModuleItemList, + ExportEntries_ModuleItemList, + ImportedLocalNames, +} from './static-semantics/all.mjs'; +import { ValueSet } from './helpers.mjs'; + +const HasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); +function deepFreeze(o) { + Object.freeze(o); + Object.getOwnPropertyNames(o).forEach((prop) => { + if (HasOwnProperty(o, prop) + && o[prop] !== null + && (typeof o[prop] === 'object' || typeof o[prop] === 'function') + && !Object.isFrozen(o[prop])) { + deepFreeze(o[prop]); + } + }); + return o; +} + +// Copied from acorn/src/scopeflags.js. +const SCOPE_FUNCTION = 2; +const SCOPE_ASYNC = 4; +const SCOPE_GENERATOR = 8; + +function functionFlags(async, generator) { + // eslint-disable-next-line no-bitwise + return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0); +} + +const optionalChainToken = { label: '?.' }; +const nullishCoalescingToken = { label: '??', binop: 0 }; +const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g; + +function isSyntaxCharacter(ch) { + return ( + ch === 0x24 /* $ */ + || (ch >= 0x28 /* ( */ && ch <= 0x2B /* + */) + || ch === 0x2E /* . */ + || ch === 0x3F /* ? */ + || (ch >= 0x5B /* [ */ && ch <= 0x5E /* ^ */) + || (ch >= 0x7B /* { */ && ch <= 0x7D /* } */) + ); +} + +function isCharacterClassEscape(ch) { + return ( + ch === 0x64 /* d */ + || ch === 0x44 /* D */ + || ch === 0x73 /* s */ + || ch === 0x53 /* S */ + || ch === 0x77 /* w */ + || ch === 0x57 /* W */ + ); +} + +function isOctalDigit(ch) { + return ch >= 0x30 /* 0 */ && ch <= 0x37; /* 7 */ +} + +export const Parser = acorn.Parser.extend((P) => (class Parse262 extends P { + constructor(options = {}, source) { + super({ + ...options, + ecmaVersion: 2020, + // adds needed ParenthesizedExpression production + preserveParens: true, + locations: true, + }, source); + if (options.strict === true) { + this.strict = true; + } + this.containsTopLevelAwait = false; + } + + parse() { + const body = super.parse(); + body.containsTopLevelAwait = this.containsTopLevelAwait; + deepFreeze(body); + return body; + } + + finishNode(node, type) { + node.strict = this.strict; + const ret = super.finishNode(node, type); + node.sourceText = () => this.input.slice(node.start, node.end); + if (ret.type === 'MethodDefinition' && ret.static) { + ret.start += 7; // don't include `static` in the source text + } + if (ret.type === 'Literal' && typeof ret.value === 'bigint') { + if (/^0[^xbo]/.test(ret.bigint)) { + this.raise(ret.start, 'Invalid or unexpected token'); + } + } + return ret; + } + + getTokenFromCode(code) { + if (code === 63) { // ? + this.pos += 1; + const next = this.input.charCodeAt(this.pos); + if (next === 46) { // . + const nextNext = this.input.charCodeAt(this.pos + 1); + if (nextNext < 48 || nextNext > 57) { + this.pos += 1; + return this.finishToken(optionalChainToken); + } + } + if (next === 63) { // ?? + this.pos += 1; + const nextNext = this.input.charCodeAt(this.pos); + if (nextNext === 61 && surroundingAgent.feature('LogicalAssignment')) { // ??= + this.pos -= 2; + return this.finishOp(acorn.tokTypes.assign, 3); + } + return this.finishToken(nullishCoalescingToken, nullishCoalescingToken.label); + } + return this.finishToken(acorn.tokTypes.question); + } + return super.getTokenFromCode(code); + } + + readToken_pipe_amp(code) { + const next = this.input.charCodeAt(this.pos + 1); + if (next === code) { // || or && + const nextNext = this.input.charCodeAt(this.pos + 2); + // https://tc39.es/proposal-logical-assignment/#sec-assignment-operators + if (nextNext === 61 && surroundingAgent.feature('LogicalAssignment')) { // ||= or &&= + return this.finishOp(acorn.tokTypes.assign, 3); + } + return this.finishOp(code === 124 + ? acorn.tokTypes.logicalOR + : acorn.tokTypes.logicalAND, 2); + } + if (next === 61) { // |= or &= + return this.finishOp(acorn.tokTypes.assign, 2); + } + return this.finishOp(code === 124 + ? acorn.tokTypes.bitwiseOR + : acorn.tokTypes.bitwiseAND, 1); + } + + parseStatement(context, topLevel, exports) { + if (this.type === acorn.tokTypes._import) { // eslint-disable-line no-underscore-dangle + skipWhiteSpace.lastIndex = this.pos; + const skip = skipWhiteSpace.exec(this.input); + const next = this.pos + skip[0].length; + const nextCh = this.input.charCodeAt(next); + if (nextCh === 40 || nextCh === 46) { // '(' '.' + const node = this.startNode(); + return this.parseExpressionStatement(node, this.parseExpression()); + } + } + return super.parseStatement(context, topLevel, exports); + } + + parseExprImport() { + const node = this.startNode(); + const meta = this.parseIdent(true); + switch (this.type) { + case acorn.tokTypes.parenL: + return this.parseDynamicImport(node); + case acorn.tokTypes.dot: + if (!(this.inModule || this.allowImportExportAnywhere)) { + return this.unexpected(); + } + this.next(); + node.meta = meta; + node.property = this.parseIdent(true); + if (node.property.name !== 'meta' || this.containsEsc) { + return this.unexpected(); + } + return this.finishNode(node, 'MetaProperty'); + default: + return this.unexpected(); + } + } + + parseSubscripts(base, startPos, startLoc, noCalls) { + if (noCalls) { + return super.parseSubscripts(base, startPos, startLoc, noCalls); + } + + const maybeAsyncArrow = base.type === 'Identifier' + && base.name === 'async' + && this.lastTokEnd === base.end + && !this.canInsertSemicolon() + && this.input.slice(base.start, base.end) === 'async'; + + /** + * Optional chains are hard okay? + * + * a.b?.c + * @=> + * OptionalExpression a.b?.c + * MemberExpression a.b + * OptionalChain ?.c + * + * a.b?.c.d.e + * @=> + * OptionalExpression a.b?.c.d.e + * MemberExpression a.b + * OptionalChain ?.c.d.e + * OptionalChain ?.c.d + * OptionalChain ?.c + * Identifier .d + * Identifier .e + * + * a.b?.c.d + * @=> + * OptionalExpression a.b?.c.d + * MemberExpression a.b + * OptionalChain ?.c.d + * OptionalChain ?.c + * Identifier .d + * + * a.b?.c.d?.e.f + * @=> + * OptionalExpression a.b?.c.d?.e.f + * OptionalExpression a.b?.c.d + * MemberExpression a.b + * OptionalChain ?.c.d + * OptionalChain ?.c + * Identifier .d + * OptionalChain ?.e.f + * OptionalChain ?.e + * Identifier .f + */ + + while (true) { + if (this.eat(optionalChainToken)) { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.chain = this.parseOptionalChain(startPos, startLoc); + base = this.finishNode(node, 'OptionalExpression'); + } else { + const element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow); + if (element === base) { + break; + } + base = element; + } + } + return base; + } + + parseOptionalChain(startPos, startLoc) { + let base = this.startNodeAt(startPos, startLoc); + if (this.eat(acorn.tokTypes.bracketL)) { + base.property = this.parseExpression(); + this.expect(acorn.tokTypes.bracketR); + base.computed = true; + base = this.finishNode(base, 'OptionalChain'); + } else if (this.eat(acorn.tokTypes.parenL)) { + base.arguments = this.parseExprList(acorn.tokTypes.parenR, this.options.ecmaVersion >= 8, false, undefined); + } else { + base.property = this.parseIdent(true); + base.computed = false; + } + base.base = null; + base = this.finishNode(base, 'OptionalChain'); + + while (true) { + const computed = this.eat(acorn.tokTypes.bracketL); + if (computed || this.eat(acorn.tokTypes.dot)) { + const node = this.startNodeAt(startPos, startLoc); + node.base = base; + node.property = computed ? this.parseExpression() : this.parseIdent(true); + if (computed) { + this.expect(acorn.tokTypes.bracketR); + } + node.computed = computed; + base = this.finishNode(node, 'OptionalChain'); + } else if (this.eat(acorn.tokTypes.parenL)) { + const node = this.startNodeAt(startPos, startLoc); + node.base = base; + node.arguments = this.parseExprList(acorn.tokTypes.parenR, this.options.ecmaVersion >= 8, false, undefined); + base = this.finishNode(node, 'OptionalChain'); + } else if (this.eat(acorn.tokTypes.backQuote)) { + this.raise(this.start, 'Cannot tag an optional chain'); + } else { + break; + } + } + + return base; + } + + buildBinary(startPos, startLoc, left, right, op, logical) { + if (op === '??') { + if (left.type === 'LogicalExpression') { + this.raise(left.start, 'Cannot mix &&, ||, and ??'); + } + if (right.type === 'LogicalExpression') { + this.raise(right.start, 'Cannot mix &&, ||, and ??'); + } + } else if (logical) { + if (left.operator === '??') { + this.raise(left.start, 'Cannot mix &&, ||, and ??'); + } + if (right.operator === '??') { + this.raise(right.start, 'Cannot mix &&, ||, and ??'); + } + } + return super.buildBinary(startPos, startLoc, left, right, op, logical); + } + + parseAwait(...args) { + const node = super.parseAwait(...args); + if (!this.inFunction) { + this.containsTopLevelAwait = true; + } + return node; + } + + // Adapted from several different places in Acorn. + static parseFunctionBody(sourceText, async, generator) { + const parser = new Parser({ + sourceType: 'script', + }, sourceText); + + // Parser.prototype.parse() + const node = parser.startNode(); + parser.nextToken(); + + // Parser.prototype.parseFunction() + parser.initFunction(node); + parser.enterScope(functionFlags(async, generator)); + + // Parser.prototype.parseBlock() + const body = []; + while (!parser.eat(acorn.tokTypes.eof)) { + const stmt = parser.parseStatement(null); + body.push(stmt); + } + + // Parser.prototype.parseFunctionBody() + parser.adaptDirectivePrologue(body); + + deepFreeze(body); + + return body; + } + + // Acorn's RegExp parser is extended to create an interpretable AST. + // Some methods have to be entirely rewritten. + regexp_pattern(state) { + const Pattern = { + type: 'Pattern', + Disjunction: { + type: 'Disjunction', + Alternatives: [], + }, + }; + state.Pattern = Pattern; + state.capturingParens = []; + state.groupSpecifiers = new Map(); + state.Disjunction = Pattern.Disjunction; + state.Disjunctions = []; + state.Alternatives = []; + return super.regexp_pattern(state); + } + + regexp_disjunction(state) { + const Disjunction = state.Disjunction; + state.Disjunctions.unshift(Disjunction); + const ret = super.regexp_disjunction(state); + state.Disjunctions.shift(); + return ret; + } + + regexp_alternative(state) { + const Alternative = { + type: 'Alternative', + Terms: [], + }; + state.Disjunctions[0].Alternatives.push(Alternative); + state.Alternatives.unshift(Alternative); + const ret = super.regexp_alternative(state); + state.Alternatives.shift(); + return ret; + } + + regexp_eatTerm(state) { + const Term = { + type: 'Term', + }; + const Alternative = state.Alternatives[0]; + + const assertion = this.regexp_eatAssertion(state); + if (assertion) { + Term.subtype = 'Assertion'; + Term.Assertion = assertion; + Alternative.Terms.push(Term); + return true; + } + + const capturingParensBefore = state.capturingParens.length; + const atom = this.regexp_eatAtom(state); + if (atom) { + Term.subtype = 'Atom'; + Term.Atom = atom; + Term.capturingParensBefore = capturingParensBefore; + const quantifier = this.regexp_eatQuantifier(state); + if (quantifier) { + Term.subtype = 'AtomQuantifier'; + Term.Quantifier = quantifier; + } + Alternative.Terms.push(Term); + return true; + } + + return false; + } + + regexp_eatAssertion(state) { + const start = state.pos; + + // ^ + if (state.eat(0x5E /* ^ */)) { + return { + type: 'Assertion', + subtype: '^', + }; + } + + // $ + if (state.eat(0x24 /* $ */)) { + return { + type: 'Assertion', + subtype: '$', + }; + } + + // \b \B + if (state.eat(0x5C /* \ */)) { + if (state.eat(0x62 /* b */)) { + return { + type: 'Assertion', + subtype: '\\b', + }; + } + if (state.eat(0x42 /* B */)) { + return { + type: 'Assertion', + subtype: '\\B', + }; + } + state.pos = start; + } + + // Lookahead / Lookbehind + if (state.eat(0x28 /* ( */) && state.eat(0x3F /* ? */)) { + let lookbehind = false; + if (this.options.ecmaVersion >= 9) { + lookbehind = state.eat(0x3C /* < */); + } + let eaten = false; + let Assertion; + if (state.eat(0x3D /* = */)) { + eaten = true; + Assertion = { + type: 'Assertion', + subtype: lookbehind ? '(?<=' : '(?=', + }; + } else if (state.eat(0x21 /* ! */)) { + eaten = true; + Assertion = { + type: 'Assertion', + subtype: lookbehind ? '(?= 9) { + this.regexp_groupSpecifier(state); + result.GroupSpecifier = state.lastStringValue; + if (result.GroupSpecifier) { + state.groupSpecifiers.set(result.GroupSpecifier, state.capturingParens.length); + } + } else if (state.current() === 0x3F /* ? */) { + state.raise('Invalid group'); + } + const numCapturingParens = state.capturingParens.length; + state.Disjunction = result.Disjunction; + this.regexp_disjunction(state); + if (state.eat(0x29 /* ) */)) { + result.enclosedCapturingParens = state.capturingParens.length - numCapturingParens; + state.numCapturingParens += 1; + if (state.numCapturingParens >= 2 ** 32 - 1) { + state.raise('Too many capturing parens'); + } + return result; + } + state.raise('Unterminated group'); + } + return false; + } + + regexp_eatPatternCharacter(state) { + // Like regexp_eatPatternCharacters, but is not eager. + const ch = state.current(); + if (!isSyntaxCharacter(ch)) { + state.Atom = { + type: 'Atom', + subtype: 'PatternCharacter', + PatternCharacter: ch, + }; + state.advance(); + return true; + } + return false; + } + + regexp_eatAtomEscape(state) { + if (this.regexp_eatBackReference(state)) { + state.AtomEscape = { + type: 'AtomEscape', + subtype: 'DecimalEscape', + DecimalEscape: state.DecimalEscape, + }; + return true; + } + if (this.regexp_eatCharacterClassEscape(state)) { + state.AtomEscape = { + type: 'AtomEscape', + subtype: 'CharacterClassEscape', + CharacterClassEscape: state.CharacterClassEscape, + }; + return true; + } + if (this.regexp_eatCharacterEscape(state)) { + state.AtomEscape = { + type: 'AtomEscape', + subtype: 'CharacterEscape', + CharacterEscape: state.CharacterEscape, + }; + return true; + } + if (state.switchN && this.regexp_eatKGroupName(state)) { + state.AtomEscape = { + type: 'AtomEscape', + subtype: 'k', + GroupName: state.backReferenceNames[state.backReferenceNames.length - 1], + }; + return true; + } + if (state.switchU) { + // Make the same message as V8. + if (state.current() === 0x63 /* c */) { + state.raise('Invalid unicode escape'); + } + state.raise('Invalid escape'); + } + return false; + } + + regexp_eatBackReference(state) { + if (this.regexp_eatDecimalEscape(state)) { + const n = state.lastIntValue; + if (n > state.maxBackReference) { + state.maxBackReference = n; + } + return true; + } + return false; + } + + regexp_eatDecimalEscape(state) { + const ret = super.regexp_eatDecimalEscape(state); + if (ret) { + state.DecimalEscape = { + type: 'DecimalEscape', + CapturingGroupNumber: state.lastIntValue, + }; + } + return ret; + } + + regexp_eatCharacterClassEscape(state) { + const ch = state.current(); + + if (isCharacterClassEscape(ch)) { + state.lastIntValue = -1; + state.advance(); + state.CharacterClassEscape = { + type: 'CharacterClassEscape', + subtype: String.fromCharCode(ch), + }; + return true; + } + + if ( + state.switchU + && this.options.ecmaVersion >= 9 + && (ch === 0x50 /* P */ || ch === 0x70 /* p */) + ) { + state.lastIntValue = -1; + state.advance(); + if ( + state.eat(0x7B /* { */) + && this.regexp_eatUnicodePropertyValueExpression(state) + && state.eat(0x7D /* } */) + ) { + state.CharacterClassEscape = { + type: 'CharacterClassEscape', + subtype: ch === 0x50 ? 'P{' : 'p{', + UnicodePropertyValueExpression: state.UnicodePropertyValueExpression, + }; + return true; + } + state.raise('Invalid property name'); + } + + return false; + } + + regexp_validateUnicodePropertyNameAndValue(state, name, value) { + state.UnicodePropertyValueExpression = { + type: 'UnicodePropertyValueExpression', + subtype: 'UnicodePropertyNameAndValue', + UnicodePropertyName: name, + UnicodePropertyValue: value, + }; + return super.regexp_validateUnicodePropertyNameAndValue(state, name, value); + } + + regexp_validateUnicodePropertyNameOrValue(state, nameOrValue) { + state.UnicodePropertyValueExpression = { + type: 'UnicodePropertyValueExpression', + subtype: 'LoneUnicodePropertyNameOrValue', + LoneUnicodePropertyNameOrValue: nameOrValue, + }; + return super.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue); + } + + regexp_eatCharacterEscape(state) { + if ( + this.regexp_eatControlEscape(state) + || this.regexp_eatCControlLetter(state) + || this.regexp_eatZero(state) + || this.regexp_eatHexEscapeSequence(state) + || this.regexp_eatRegExpUnicodeEscapeSequence(state) + || this.regexp_eatIdentityEscape(state) + ) { + state.CharacterEscape = { + type: 'CharacterEscape', + CharacterValue: state.lastIntValue, + }; + return true; + } + return false; + } + + regexp_eatCharacterClass(state) { + if (state.eat(0x5B /* [ */)) { + state.CharacterClass = { + type: 'CharacterClass', + invert: false, + ClassRanges: [], + }; + if (state.eat(0x5E /* ^ */)) { + state.CharacterClass.invert = true; + } + this.regexp_classRanges(state); + if (state.eat(0x5D /* ] */)) { + return true; + } + // Unreachable since it threw "unterminated regular expression" error before. + state.raise('Unterminated character class'); + } + return false; + } + + regexp_classRanges(state) { + while (this.regexp_eatClassAtom(state)) { + const left = state.lastIntValue; + const leftClassAtom = state.ClassAtom; + if (state.eat(0x2D /* - */) && this.regexp_eatClassAtom(state)) { + const right = state.lastIntValue; + if (state.switchU && (left === -1 || right === -1)) { + state.raise('Invalid character class'); + } + if (left !== -1 && right !== -1 && left > right) { + state.raise('Range out of order in character class'); + } + state.CharacterClass.ClassRanges.push([leftClassAtom, state.ClassAtom]); + } else { + state.CharacterClass.ClassRanges.push(leftClassAtom); + } + } + } + + regexp_eatClassAtom(state) { + const start = state.pos; + + if (state.eat(0x5C /* \ */)) { + if (this.regexp_eatClassEscape(state)) { + state.ClassAtom = { + type: 'ClassAtom', + subtype: 'ClassEscape', + ClassEscape: state.ClassEscape, + }; + return true; + } + if (state.switchU) { + // Make the same message as V8. + const ch = state.current(); + if (ch === 0x63 /* c */ || isOctalDigit(ch)) { + state.raise('Invalid class escape'); + } + state.raise('Invalid escape'); + } + state.pos = start; + } + + const ch = state.current(); + if (ch !== 0x5D /* ] */) { + state.lastIntValue = ch; + state.advance(); + state.ClassAtom = { + type: 'ClassAtom', + subtype: 'character', + character: ch, + }; + return true; + } + + return false; + } + + regexp_eatClassEscape(state) { + if (state.eat(0x62 /* b */)) { + state.lastIntValue = 0x08; /* */ + state.ClassEscape = { + type: 'ClassEscape', + subtype: 'b', + }; + return true; + } + + if (state.switchU && state.eat(0x2D /* - */)) { + state.lastIntValue = 0x2D; /* - */ + state.ClassEscape = { + type: 'ClassEscape', + subtype: '-', + }; + return true; + } + + if (this.regexp_eatCharacterClassEscape(state)) { + state.ClassEscape = { + type: 'ClassEscape', + subtype: 'CharacterClassEscape', + CharacterClassEscape: state.CharacterClassEscape, + }; + return true; + } + + if (this.regexp_eatCharacterEscape(state)) { + state.ClassEscape = { + type: 'ClassEscape', + subtype: 'CharacterEscape', + CharacterEscape: state.CharacterEscape, + }; + return true; + } + + return false; + } +})); + +export function ParseAsFunctionBody(sourceText) { + return Parser.parseFunctionBody(sourceText, false, false); +} + +export function ParseAsGeneratorBody(sourceText) { + return Parser.parseFunctionBody(sourceText, false, true); +} + +export function ParseAsAsyncFunctionBody(sourceText) { + return Parser.parseFunctionBody(sourceText, true, false); +} + +export function ParseAsAsyncGeneratorBody(sourceText) { + return Parser.parseFunctionBody(sourceText, true, true); +} + +// Adapted from several different places in Acorn. +// `strict` refers to ContainsUseStrict of the corresponding function body. +export function ParseAsFormalParameters(sourceText, strict, enableAwait, enableYield) { + // Adapted from different places in Acorn. + const parser = new Parser({ + sourceType: 'script', + }, sourceText); + + parser.strict = strict; + + // Parser.prototype.parse() + const node = parser.startNode(); + parser.nextToken(); + + // Parser.prototype.parseFunction() + parser.initFunction(node); + parser.enterScope(functionFlags(enableAwait, enableYield)); + + // Parser.prototype.parseFunctionParams() + const params = parser.parseBindingList(acorn.tokTypes.eof, false, true); + parser.checkYieldAwaitInDefaultParams(); + + // Parser.prototype.parseFunctionBody() + const simple = parser.isSimpleParamList(params); + if (strict && !simple) { + parser.raiseRecoverable(node.start, 'Illegal \'use strict\' directive in function with non-simple parameter list'); + } + parser.checkParams({ params }, !strict && simple); + + deepFreeze(params); + + return params; +} + +export const emptyConstructorNode = Parser.parse('(class { constructor() {} })').body[0].expression.expression.body.body[0]; +export const forwardingConstructorNode = Parser.parse('(class extends X { constructor(...args) { super(...args); } })').body[0].expression.expression.body.body[0]; + +function forwardError(fn) { + try { + return fn(); + } catch (e) { + if (e.name === 'SyntaxError') { + return [surroundingAgent.Throw('SyntaxError', 'Raw', e.message).Value]; + } else { + throw e; + } + } +} + +export function ParseScript(sourceText, realm, hostDefined = {}, strict) { + const body = forwardError(() => Parser.parse(sourceText, { + sourceType: 'script', + strict, + })); + if (Array.isArray(body)) { + return body; + } + + return { + Realm: realm, + Environment: undefined, + ECMAScriptCode: body, + HostDefined: hostDefined, + mark(m) { + m(this.Realm); + m(this.Environment); + }, + }; +} + +export function ParseModule(sourceText, realm, hostDefined = {}) { + // Assert: sourceText is an ECMAScript source text (see clause 10). + const body = forwardError(() => Parser.parse(sourceText, { + sourceType: 'module', + allowAwaitOutsideFunction: surroundingAgent.feature('TopLevelAwait'), + })); + if (Array.isArray(body)) { + return body; + } + + const requestedModules = ModuleRequests_ModuleItemList(body.body); + const importEntries = ImportEntries_ModuleItemList(body.body); + const importedBoundNames = new ValueSet(ImportedLocalNames(importEntries)); + const indirectExportEntries = []; + const localExportEntries = []; + const starExportEntries = []; + const exportEntries = ExportEntries_ModuleItemList(body.body); + for (const ee of exportEntries) { + if (ee.ModuleRequest === Value.null) { + if (!importedBoundNames.has(ee.LocalName)) { + localExportEntries.push(ee); + } else { + const ie = importEntries.find((e) => e.LocalName.stringValue() === ee.LocalName.stringValue()); + if (ie.ImportName.stringValue() === '*') { + // Assert: This is a re-export of an imported module namespace object. + localExportEntries.push(ee); + } else { + indirectExportEntries.push(new ExportEntryRecord({ + ModuleRequest: ie.ModuleRequest, + ImportName: ie.ImportName, + LocalName: Value.null, + ExportName: ee.ExportName, + })); + } + } + } else if (ee.ImportName.stringValue() === '*') { + starExportEntries.push(ee); + } else { + indirectExportEntries.push(ee); + } + } + + return new SourceTextModuleRecord({ + Realm: realm, + Environment: Value.undefined, + Namespace: Value.undefined, + ImportMeta: undefined, + Async: body.containsTopLevelAwait ? Value.true : Value.false, + AsyncEvaluating: Value.false, + TopLevelCapability: Value.undefined, + AsyncParentModules: Value.undefined, + PendingAsyncDependencies: Value.undefined, + Status: 'unlinked', + EvaluationError: Value.undefined, + HostDefined: hostDefined, + ECMAScriptCode: body, + Context: undefined, + RequestedModules: requestedModules, + ImportEntries: importEntries, + LocalExportEntries: localExportEntries, + IndirectExportEntries: indirectExportEntries, + StarExportEntries: starExportEntries, + DFSIndex: Value.undefined, + DFSAncestorIndex: Value.undefined, + }); +} + +export function ParseRegExp(source, flags) { + const parser = new Parser({ + sourceType: 'script', + }, 'a/'); + + // Initialize RegExp state. + parser.readRegexp(); + + let escaped = false; + let inClass = false; + let pos = 0; + for (;;) { + if (pos >= source.length) { + if (inClass || escaped) { + parser.raise(0, 'Unterminated regular expression'); + } else { + break; + } + } + const ch = source.charAt(pos); + if (!escaped) { + if (ch === '[') { + inClass = true; + } else if (ch === ']' && inClass) { + inClass = false; + } + escaped = ch === '\\'; + } else { + escaped = false; + } + pos += 1; + } + + const state = parser.regexpState; + state.reset(0, source, flags); + + parser.validateRegExpPattern(state); + + return { + pattern: state.Pattern, + capturingParens: state.capturingParens, + groupSpecifiers: state.groupSpecifiers, + }; +} diff --git a/src/engine262/src/realm.mjs b/src/engine262/src/realm.mjs new file mode 100644 index 0000000..88d09e8 --- /dev/null +++ b/src/engine262/src/realm.mjs @@ -0,0 +1,391 @@ +import { surroundingAgent } from './engine.mjs'; +import { + Descriptor, + Value, +} from './value.mjs'; +import { + Assert, + DefinePropertyOrThrow, + OrdinaryObjectCreate, +} from './abstract-ops/all.mjs'; +import { NewGlobalEnvironment } from './environment.mjs'; +import { Q, X } from './completion.mjs'; +import { BootstrapObjectPrototype } from './intrinsics/ObjectPrototype.mjs'; +import { BootstrapObject } from './intrinsics/Object.mjs'; +import { BootstrapArrayPrototype } from './intrinsics/ArrayPrototype.mjs'; +import { BootstrapArray } from './intrinsics/Array.mjs'; +import { BootstrapBigInt } from './intrinsics/BigInt.mjs'; +import { BootstrapBigIntPrototype } from './intrinsics/BigIntPrototype.mjs'; +import { BootstrapBooleanPrototype } from './intrinsics/BooleanPrototype.mjs'; +import { BootstrapBoolean } from './intrinsics/Boolean.mjs'; +import { BootstrapNumberPrototype } from './intrinsics/NumberPrototype.mjs'; +import { BootstrapNumber } from './intrinsics/Number.mjs'; +import { BootstrapFunctionPrototype } from './intrinsics/FunctionPrototype.mjs'; +import { BootstrapFunction } from './intrinsics/Function.mjs'; +import { BootstrapSymbolPrototype } from './intrinsics/SymbolPrototype.mjs'; +import { BootstrapSymbol } from './intrinsics/Symbol.mjs'; +import { BootstrapMath } from './intrinsics/Math.mjs'; +import { BootstrapDatePrototype } from './intrinsics/DatePrototype.mjs'; +import { BootstrapDate } from './intrinsics/Date.mjs'; +import { BootstrapRegExpPrototype } from './intrinsics/RegExpPrototype.mjs'; +import { BootstrapRegExp } from './intrinsics/RegExp.mjs'; +import { BootstrapPromisePrototype } from './intrinsics/PromisePrototype.mjs'; +import { BootstrapPromise } from './intrinsics/Promise.mjs'; +import { BootstrapProxy } from './intrinsics/Proxy.mjs'; +import { BootstrapReflect } from './intrinsics/Reflect.mjs'; +import { BootstrapStringPrototype } from './intrinsics/StringPrototype.mjs'; +import { BootstrapString } from './intrinsics/String.mjs'; +import { BootstrapErrorPrototype } from './intrinsics/ErrorPrototype.mjs'; +import { BootstrapError } from './intrinsics/Error.mjs'; +import { BootstrapNativeError } from './intrinsics/NativeError.mjs'; +import { BootstrapIteratorPrototype } from './intrinsics/IteratorPrototype.mjs'; +import { BootstrapAsyncIteratorPrototype } from './intrinsics/AsyncIteratorPrototype.mjs'; +import { BootstrapArrayIteratorPrototype } from './intrinsics/ArrayIteratorPrototype.mjs'; +import { BootstrapMapIteratorPrototype } from './intrinsics/MapIteratorPrototype.mjs'; +import { BootstrapSetIteratorPrototype } from './intrinsics/SetIteratorPrototype.mjs'; +import { BootstrapStringIteratorPrototype } from './intrinsics/StringIteratorPrototype.mjs'; +import { BootstrapRegExpStringIteratorPrototype } from './intrinsics/RegExpStringIteratorPrototype.mjs'; +import { BootstrapForInIteratorPrototype } from './intrinsics/ForInIteratorPrototype.mjs'; +import { BootstrapMapPrototype } from './intrinsics/MapPrototype.mjs'; +import { BootstrapMap } from './intrinsics/Map.mjs'; +import { BootstrapSetPrototype } from './intrinsics/SetPrototype.mjs'; +import { BootstrapSet } from './intrinsics/Set.mjs'; +import { BootstrapGenerator } from './intrinsics/Generator.mjs'; +import { BootstrapGeneratorFunction } from './intrinsics/GeneratorFunction.mjs'; +import { BootstrapGeneratorPrototype } from './intrinsics/GeneratorPrototype.mjs'; +import { BootstrapAsyncFunctionPrototype } from './intrinsics/AsyncFunctionPrototype.mjs'; +import { BootstrapAsyncFunction } from './intrinsics/AsyncFunction.mjs'; +import { BootstrapAsyncGenerator } from './intrinsics/AsyncGenerator.mjs'; +import { BootstrapAsyncGeneratorFunction } from './intrinsics/AsyncGeneratorFunction.mjs'; +import { BootstrapAsyncGeneratorPrototype } from './intrinsics/AsyncGeneratorPrototype.mjs'; +import { BootstrapAsyncFromSyncIteratorPrototype } from './intrinsics/AsyncFromSyncIteratorPrototype.mjs'; +import { BootstrapArrayBuffer } from './intrinsics/ArrayBuffer.mjs'; +import { BootstrapArrayBufferPrototype } from './intrinsics/ArrayBufferPrototype.mjs'; +import { BootstrapJSON } from './intrinsics/JSON.mjs'; +import { BootstrapEval } from './intrinsics/eval.mjs'; +import { BootstrapIsFinite } from './intrinsics/isFinite.mjs'; +import { BootstrapIsNaN } from './intrinsics/isNaN.mjs'; +import { BootstrapParseFloat } from './intrinsics/parseFloat.mjs'; +import { BootstrapParseInt } from './intrinsics/parseInt.mjs'; +import { BootstrapThrowTypeError } from './intrinsics/ThrowTypeError.mjs'; +import { BootstrapTypedArray } from './intrinsics/TypedArray.mjs'; +import { BootstrapTypedArrayPrototype } from './intrinsics/TypedArrayPrototype.mjs'; +import { BootstrapTypedArrayConstructors } from './intrinsics/TypedArrayConstructors.mjs'; +import { BootstrapTypedArrayPrototypes } from './intrinsics/TypedArrayPrototypes.mjs'; +import { BootstrapDataView } from './intrinsics/DataView.mjs'; +import { BootstrapDataViewPrototype } from './intrinsics/DataViewPrototype.mjs'; +import { BootstrapWeakMapPrototype } from './intrinsics/WeakMapPrototype.mjs'; +import { BootstrapWeakMap } from './intrinsics/WeakMap.mjs'; +import { BootstrapWeakSetPrototype } from './intrinsics/WeakSetPrototype.mjs'; +import { BootstrapWeakSet } from './intrinsics/WeakSet.mjs'; +// Flagged features +import { BootstrapAggregateError } from './intrinsics/AggregateError.mjs'; +import { BootstrapAggregateErrorPrototype } from './intrinsics/AggregateErrorPrototype.mjs'; +import { BootstrapFinalizationRegistryPrototype } from './intrinsics/FinalizationRegistryPrototype.mjs'; +import { BootstrapFinalizationRegistry } from './intrinsics/FinalizationRegistry.mjs'; +import { BootstrapWeakRefPrototype } from './intrinsics/WeakRefPrototype.mjs'; +import { BootstrapWeakRef } from './intrinsics/WeakRef.mjs'; + +// 8.2 #sec-code-realms +export class Realm { + constructor() { + this.Intrinsics = undefined; + this.GlobalObject = undefined; + this.GlobalEnv = undefined; + this.TemplateMap = undefined; + this.HostDefined = undefined; + } + + mark(m) { + m(this.GlobalObject); + m(this.GlobalEnv); + for (const v of Object.values(this.Intrinsics)) { + m(v); + } + for (const v of Object.values(this.TemplateMap)) { + m(v); + } + } +} + +// 8.2.1 #sec-createrealm +export function CreateRealm() { + const realmRec = new Realm(); + CreateIntrinsics(realmRec); + realmRec.GlobalObject = Value.undefined; + realmRec.GlobalEnv = Value.undefined; + realmRec.TemplateMap = []; + return realmRec; +} + +function AddRestrictedFunctionProperties(F, realm) { + Assert(realm.Intrinsics['%ThrowTypeError%']); + const thrower = realm.Intrinsics['%ThrowTypeError%']; + X(DefinePropertyOrThrow(F, new Value('caller'), Descriptor({ + Get: thrower, + Set: thrower, + Enumerable: Value.false, + Configurable: Value.true, + }))); + X(DefinePropertyOrThrow(F, new Value('arguments'), Descriptor({ + Get: thrower, + Set: thrower, + Enumerable: Value.false, + Configurable: Value.true, + }))); +} + +// 8.2.2 #sec-createintrinsics +export function CreateIntrinsics(realmRec) { + const intrinsics = Object.create(null); + realmRec.Intrinsics = intrinsics; + + intrinsics['%Object.prototype%'] = OrdinaryObjectCreate(Value.null); + + BootstrapFunctionPrototype(realmRec); + BootstrapObjectPrototype(realmRec); + BootstrapThrowTypeError(realmRec); + + BootstrapEval(realmRec); + BootstrapIsFinite(realmRec); + BootstrapIsNaN(realmRec); + BootstrapParseFloat(realmRec); + BootstrapParseInt(realmRec); + + BootstrapObject(realmRec); + + BootstrapErrorPrototype(realmRec); + BootstrapError(realmRec); + BootstrapNativeError(realmRec); + + BootstrapFunction(realmRec); + + BootstrapIteratorPrototype(realmRec); + BootstrapAsyncIteratorPrototype(realmRec); + BootstrapArrayIteratorPrototype(realmRec); + BootstrapMapIteratorPrototype(realmRec); + BootstrapSetIteratorPrototype(realmRec); + BootstrapStringIteratorPrototype(realmRec); + BootstrapRegExpStringIteratorPrototype(realmRec); + BootstrapForInIteratorPrototype(realmRec); + + BootstrapStringPrototype(realmRec); + BootstrapString(realmRec); + + BootstrapArrayPrototype(realmRec); + BootstrapArray(realmRec); + + BootstrapBooleanPrototype(realmRec); + BootstrapBoolean(realmRec); + + BootstrapNumberPrototype(realmRec); + BootstrapNumber(realmRec); + + BootstrapBigIntPrototype(realmRec); + BootstrapBigInt(realmRec); + + BootstrapSymbolPrototype(realmRec); + BootstrapSymbol(realmRec); + + BootstrapPromisePrototype(realmRec); + BootstrapPromise(realmRec); + + BootstrapProxy(realmRec); + + BootstrapReflect(realmRec); + + BootstrapMath(realmRec); + + BootstrapDatePrototype(realmRec); + BootstrapDate(realmRec); + + BootstrapRegExpPrototype(realmRec); + BootstrapRegExp(realmRec); + + BootstrapSetPrototype(realmRec); + BootstrapSet(realmRec); + + BootstrapMapPrototype(realmRec); + BootstrapMap(realmRec); + + BootstrapGeneratorPrototype(realmRec); + BootstrapGenerator(realmRec); + BootstrapGeneratorFunction(realmRec); + + BootstrapAsyncFunctionPrototype(realmRec); + BootstrapAsyncFunction(realmRec); + + BootstrapAsyncGeneratorPrototype(realmRec); + BootstrapAsyncGenerator(realmRec); + BootstrapAsyncGeneratorFunction(realmRec); + + BootstrapAsyncFromSyncIteratorPrototype(realmRec); + + BootstrapArrayBufferPrototype(realmRec); + BootstrapArrayBuffer(realmRec); + + BootstrapTypedArrayPrototype(realmRec); + BootstrapTypedArray(realmRec); + BootstrapTypedArrayPrototypes(realmRec); + BootstrapTypedArrayConstructors(realmRec); + + BootstrapDataViewPrototype(realmRec); + BootstrapDataView(realmRec); + + BootstrapJSON(realmRec); + + BootstrapWeakMapPrototype(realmRec); + BootstrapWeakMap(realmRec); + BootstrapWeakSetPrototype(realmRec); + BootstrapWeakSet(realmRec); + + if (surroundingAgent.feature('Promise.any')) { + BootstrapAggregateErrorPrototype(realmRec); + BootstrapAggregateError(realmRec); + } + + if (surroundingAgent.feature('WeakRefs')) { + BootstrapWeakRefPrototype(realmRec); + BootstrapWeakRef(realmRec); + + BootstrapFinalizationRegistryPrototype(realmRec); + BootstrapFinalizationRegistry(realmRec); + } + + AddRestrictedFunctionProperties(intrinsics['%Function.prototype%'], realmRec); + + return intrinsics; +} + +// 8.2.3 #sec-setrealmglobalobject +export function SetRealmGlobalObject(realmRec, globalObj, thisValue) { + const intrinsics = realmRec.Intrinsics; + if (globalObj === Value.undefined) { + globalObj = OrdinaryObjectCreate(intrinsics['%Object.prototype%']); + } + if (thisValue === Value.undefined) { + thisValue = globalObj; + } + realmRec.GlobalObject = globalObj; + const newGlobalEnv = NewGlobalEnvironment(globalObj, thisValue); + realmRec.GlobalEnv = newGlobalEnv; + return realmRec; +} + +// 8.2.4 #sec-setdefaultglobalbindings +export function SetDefaultGlobalBindings(realmRec) { + const global = realmRec.GlobalObject; + + // Value Properties of the Global Object + [ + ['Infinity', new Value(Infinity)], + ['NaN', new Value(NaN)], + ['undefined', Value.undefined], + ].forEach(([name, value]) => { + Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({ + Value: value, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + }); + + Q(DefinePropertyOrThrow(global, new Value('globalThis'), Descriptor({ + Value: realmRec.GlobalEnv.EnvironmentRecord.GlobalThisValue, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + [ + // Function Properties of the Global Object + 'eval', + 'isFinite', + 'isNaN', + 'parseFloat', + 'parseInt', + // 'decodeURI', + // 'decodeURIComponent', + // 'encodeURI', + // 'encodeURIComponent', + + // Constructor Properties of the Global Object + 'Array', + 'ArrayBuffer', + 'Boolean', + 'BigInt', + 'BigInt64Array', + 'BigUint64Array', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Float32Array', + 'Float64Array', + 'Function', + 'Int8Array', + 'Int16Array', + 'Int32Array', + 'Map', + 'Number', + 'Object', + 'Promise', + 'Proxy', + 'RangeError', + 'ReferenceError', + 'RegExp', + 'Set', + // 'SharedArrayBuffer', + 'String', + 'Symbol', + 'SyntaxError', + 'TypeError', + 'Uint8Array', + 'Uint8ClampedArray', + 'Uint16Array', + 'Uint32Array', + 'URIError', + 'WeakMap', + 'WeakSet', + + // Other Properties of the Global Object + // 'Atomics', + 'JSON', + 'Math', + 'Reflect', + ].forEach((name) => { + Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({ + Value: realmRec.Intrinsics[`%${name}%`], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + }); + + if (surroundingAgent.feature('Promise.any')) { + Q(DefinePropertyOrThrow(global, new Value('AggregateError'), Descriptor({ + Value: realmRec.Intrinsics['%AggregateError%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + + if (surroundingAgent.feature('WeakRefs')) { + Q(DefinePropertyOrThrow(global, new Value('WeakRef'), Descriptor({ + Value: realmRec.Intrinsics['%WeakRef%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + + Q(DefinePropertyOrThrow(global, new Value('FinalizationRegistry'), Descriptor({ + Value: realmRec.Intrinsics['%FinalizationRegistry%'], + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.true, + }))); + } + + return global; +} diff --git a/src/engine262/src/runtime-semantics/AdditiveExpression.mjs b/src/engine262/src/runtime-semantics/AdditiveExpression.mjs new file mode 100644 index 0000000..90c3b99 --- /dev/null +++ b/src/engine262/src/runtime-semantics/AdditiveExpression.mjs @@ -0,0 +1,83 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + isAdditiveExpressionWithMinus, + isAdditiveExpressionWithPlus, +} from '../ast.mjs'; +import { + GetValue, + ToNumeric, + ToPrimitive, + ToString, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { + Type, + TypeNumeric, + Value, +} from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +export function EvaluateBinopValues_AdditiveExpression_Plus(lval, rval) { + const lprim = Q(ToPrimitive(lval)); + const rprim = Q(ToPrimitive(rval)); + if (Type(lprim) === 'String' || Type(rprim) === 'String') { + const lstr = Q(ToString(lprim)); + const rstr = Q(ToString(rprim)); + return new Value(lstr.stringValue() + rstr.stringValue()); + } + const lnum = Q(ToNumeric(lprim)); + const rnum = Q(ToNumeric(rprim)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + return T.add(lnum, rnum); +} + +// 12.8.3.1 #sec-addition-operator-plus-runtime-semantics-evaluation +// AdditiveExpression : AdditiveExpression + MultiplicativeExpression +function* Evaluate_AdditiveExpression_Plus(AdditiveExpression, MultiplicativeExpression) { + const lref = yield* Evaluate(AdditiveExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(MultiplicativeExpression); + const rval = Q(GetValue(rref)); + return EvaluateBinopValues_AdditiveExpression_Plus(lval, rval); +} + +export function EvaluateBinopValues_AdditiveExpression_Minus(lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + return T.subtract(lnum, rnum); +} + +// 12.8.4.1 #sec-subtraction-operator-minus-runtime-semantics-evaluation +function* Evaluate_AdditiveExpression_Minus( + AdditiveExpression, MultiplicativeExpression, +) { + const lref = yield* Evaluate(AdditiveExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(MultiplicativeExpression); + const rval = Q(GetValue(rref)); + return EvaluateBinopValues_AdditiveExpression_Minus(lval, rval); +} + +export function* Evaluate_AdditiveExpression(AdditiveExpression) { + switch (true) { + case isAdditiveExpressionWithPlus(AdditiveExpression): + return yield* Evaluate_AdditiveExpression_Plus( + AdditiveExpression.left, AdditiveExpression.right, + ); + case isAdditiveExpressionWithMinus(AdditiveExpression): + return yield* Evaluate_AdditiveExpression_Minus( + AdditiveExpression.left, AdditiveExpression.right, + ); + + default: + throw new OutOfRange('Evaluate_AdditiveExpression', AdditiveExpression); + } +} diff --git a/src/engine262/src/runtime-semantics/ArgumentListEvaluation.mjs b/src/engine262/src/runtime-semantics/ArgumentListEvaluation.mjs new file mode 100644 index 0000000..d2191ca --- /dev/null +++ b/src/engine262/src/runtime-semantics/ArgumentListEvaluation.mjs @@ -0,0 +1,119 @@ +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { + isExpression, + isNoSubstitutionTemplate, + isSubstitutionTemplate, + isTemplateLiteral, + unrollTemplateLiteral, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + Assert, + GetIterator, + GetValue, + IteratorStep, + IteratorValue, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { GetTemplateObject } from './all.mjs'; + +// 12.2.9.5 #sec-runtime-semantics-substitutionevaluation +// TemplateSpans : +// TemplateTail +// TemplateMiddleList TemplateTail +// +// TemplateMiddleList : +// TemplateMiddle Expression +// TemplateMiddleList TemplateMiddle Expression +function* SubstitutionEvaluation_TemplateSpans(TemplateSpans) { + const preceding = []; + for (let i = 1; i < TemplateSpans.length; i += 2) { + const Expression = TemplateSpans[i]; + const nextRef = yield* Evaluate(Expression); + const next = Q(GetValue(nextRef)); + preceding.push(next); + } + return preceding; +} + +// 12.2.9.3 #sec-template-literals-runtime-semantics-argumentlistevaluation +// TemplateLiteral : NoSubstitutionTemplate +// +// https://github.com/tc39/ecma262/pull/1402 +// TemplateLiteral : SubstitutionTemplate +export function* ArgumentListEvaluation_TemplateLiteral(TemplateLiteral) { + switch (true) { + case isNoSubstitutionTemplate(TemplateLiteral): { + const templateLiteral = TemplateLiteral; + const siteObj = GetTemplateObject(templateLiteral); + return [siteObj]; + } + + case isSubstitutionTemplate(TemplateLiteral): { + const templateLiteral = TemplateLiteral; + const siteObj = GetTemplateObject(templateLiteral); + const [/* TemplateHead */, first/* Expression */, ...rest/* TemplateSpans */] = unrollTemplateLiteral(templateLiteral); + const firstSubRef = yield* Evaluate(first); + const firstSub = Q(GetValue(firstSubRef)); + const restSub = Q(yield* SubstitutionEvaluation_TemplateSpans(rest)); + Assert(Array.isArray(restSub)); + return [siteObj, firstSub, ...restSub]; + } + + default: + throw new OutOfRange('ArgumentListEvaluation_TemplateLiteral', TemplateLiteral); + } +} + +// 12.3.6.1 #sec-argument-lists-runtime-semantics-argumentlistevaluation +// Arguments : `(` `)` +// ArgumentList : +// AssignmentExpression +// `...` AssignmentExpression +// ArgumentList `,` AssignmentExpression +// ArgumentList `,` `...` AssignmentExpression +// +// (implicit) +// Arguments : +// `(` ArgumentList `)` +// `(` ArgumentList `,` `)` +export function* ArgumentListEvaluation_Arguments(Arguments) { + const precedingArgs = []; + for (const AssignmentExpressionOrSpreadElement of Arguments) { + if (AssignmentExpressionOrSpreadElement.type === 'SpreadElement') { + const AssignmentExpression = AssignmentExpressionOrSpreadElement.argument; + const spreadRef = yield* Evaluate(AssignmentExpression); + const spreadObj = Q(GetValue(spreadRef)); + const iteratorRecord = Q(GetIterator(spreadObj)); + while (true) { + const next = Q(IteratorStep(iteratorRecord)); + if (next === Value.false) { + break; + } + const nextArg = Q(IteratorValue(next)); + precedingArgs.push(nextArg); + } + } else { + const AssignmentExpression = AssignmentExpressionOrSpreadElement; + Assert(isExpression(AssignmentExpression)); + const ref = yield* Evaluate(AssignmentExpression); + const arg = Q(GetValue(ref)); + precedingArgs.push(arg); + } + } + return precedingArgs; +} + +export function ArgumentListEvaluation(ArgumentsOrTemplateLiteral) { + switch (true) { + case isTemplateLiteral(ArgumentsOrTemplateLiteral): + return ArgumentListEvaluation_TemplateLiteral(ArgumentsOrTemplateLiteral); + + case Array.isArray(ArgumentsOrTemplateLiteral): + return ArgumentListEvaluation_Arguments(ArgumentsOrTemplateLiteral); + + default: + throw new OutOfRange('ArgumentListEvaluation', ArgumentsOrTemplateLiteral); + } +} diff --git a/src/engine262/src/runtime-semantics/ArrayLiteral.mjs b/src/engine262/src/runtime-semantics/ArrayLiteral.mjs new file mode 100644 index 0000000..12261a5 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ArrayLiteral.mjs @@ -0,0 +1,80 @@ +import { + ArrayCreate, + CreateDataPropertyOrThrow, + GetIterator, + GetValue, + IteratorStep, + IteratorValue, + Set, + ToString, + ToUint32, +} from '../abstract-ops/all.mjs'; +import { + isExpression, + isSpreadElement, +} from '../ast.mjs'; +import { Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +function* ArrayAccumulation_SpreadElement(SpreadElement, array, nextIndex) { + const spreadRef = yield* Evaluate(SpreadElement.argument); + const spreadObj = Q(GetValue(spreadRef)); + const iteratorRecord = Q(GetIterator(spreadObj)); + while (true) { + const next = Q(IteratorStep(iteratorRecord)); + if (next === Value.false) { + return nextIndex; + } + const nextValue = Q(IteratorValue(next)); + const nextIndexStr = X(ToString(new Value(nextIndex))); + X(CreateDataPropertyOrThrow(array, nextIndexStr, nextValue)); + nextIndex += 1; + } +} + +function* ArrayAccumulation_AssignmentExpression(AssignmentExpression, array, nextIndex) { + const initResult = yield* Evaluate(AssignmentExpression); + const initValue = Q(GetValue(initResult)); + const initIndex = X(ToString(new Value(nextIndex))); + X(CreateDataPropertyOrThrow(array, initIndex, initValue)); + return nextIndex + 1; +} + +function* ArrayAccumulation(ElementList, array, nextIndex) { + let postIndex = nextIndex; + for (const element of ElementList) { + switch (true) { + case !element: + // Elision + postIndex += 1; + break; + + case isExpression(element): + postIndex = Q(yield* ArrayAccumulation_AssignmentExpression(element, array, postIndex)); + break; + + case isSpreadElement(element): + postIndex = Q(yield* ArrayAccumulation_SpreadElement(element, array, postIndex)); + break; + + default: + throw new OutOfRange('ArrayAccumulation', element); + } + } + return postIndex; +} + +// 12.2.5.3 #sec-array-initializer-runtime-semantics-evaluation +// ArrayLiteral : +// `[` Elision `]` +// `[` ElementList `]` +// `[` ElementList `,` Elision `]` +export function* Evaluate_ArrayLiteral(ArrayLiteral) { + const array = X(ArrayCreate(new Value(0))); + const len = Q(yield* ArrayAccumulation(ArrayLiteral.elements, array, 0)); + X(Set(array, new Value('length'), ToUint32(new Value(len)), Value.false)); + // NOTE: The above Set cannot fail because of the nature of the object returned by ArrayCreate. + return array; +} diff --git a/src/engine262/src/runtime-semantics/ArrowFunction.mjs b/src/engine262/src/runtime-semantics/ArrowFunction.mjs new file mode 100644 index 0000000..66a2b83 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ArrowFunction.mjs @@ -0,0 +1,23 @@ +import { Value } from '../value.mjs'; +import { GetValue } from '../abstract-ops/all.mjs'; +import { Q, ReturnCompletion } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { NamedEvaluation_ArrowFunction } from './all.mjs'; + +// #sec-arrow-function-definitions-runtime-semantics-evaluation +// ArrowFunction : ArrowParameters `=>` ConciseBody +export function Evaluate_ArrowFunction(ArrowFunction) { + return NamedEvaluation_ArrowFunction(ArrowFunction, new Value('')); +} + +// #sec-arrow-function-definitions-runtime-semantics-evaluation +// ExpressionBody : AssignmentExpression +export function* Evaluate_ExpressionBody(ExpressionBody) { + const AssignmentExpression = ExpressionBody; + // 1. Let exprRef be the result of evaluating |AssignmentExpression|. + const exprRef = yield* Evaluate(AssignmentExpression); + // 2. Let exprValue be ? GetValue(exprRef). + const exprValue = Q(GetValue(exprRef)); + // 3. Return Completion { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }. + return new ReturnCompletion(exprValue); +} diff --git a/src/engine262/src/runtime-semantics/AssignmentExpression.mjs b/src/engine262/src/runtime-semantics/AssignmentExpression.mjs new file mode 100644 index 0000000..22f5e03 --- /dev/null +++ b/src/engine262/src/runtime-semantics/AssignmentExpression.mjs @@ -0,0 +1,120 @@ +import { Value } from '../value.mjs'; +import { Q, X, ReturnIfAbrupt } from '../completion.mjs'; +import { + GetReferencedName, + GetValue, + PutValue, + ToBoolean, +} from '../abstract-ops/all.mjs'; +import { + IsAnonymousFunctionDefinition, + IsIdentifierRef, +} from '../static-semantics/all.mjs'; +import { isAssignmentPattern } from '../ast.mjs'; +import { EvaluateBinopValues, Evaluate } from '../evaluator.mjs'; +import { + DestructuringAssignmentEvaluation_AssignmentPattern, + NamedEvaluation_Expression, +} from './all.mjs'; + +// 12.15.4 #sec-assignment-operators-runtime-semantics-evaluation +// AssignmentExpression : +// LeftHandSideExpression `=` AssignmentExpression +// LeftHandSideExpression AssignmentOperator AssignmentExpression +// https://tc39.es/proposal-logical-assignment/#sec-assignment-operators-runtime-semantics-evaluation +// LeftHandSideExpression `&&=` AssignmentExpression +// LeftHandSideExpression `||=` AssignmentExpression +// LeftHandSideExpression `??=` AssignmentExpression +export function* Evaluate_AssignmentExpression(node) { + const LeftHandSideExpression = node.left; + const AssignmentExpression = node.right; + if (node.operator === '=') { + if (!isAssignmentPattern(LeftHandSideExpression)) { + const lref = yield* Evaluate(LeftHandSideExpression); + ReturnIfAbrupt(lref); + let rval; + if (IsAnonymousFunctionDefinition(AssignmentExpression) && IsIdentifierRef(LeftHandSideExpression)) { + rval = yield* NamedEvaluation_Expression(AssignmentExpression, GetReferencedName(lref)); + } else { + const rref = yield* Evaluate(AssignmentExpression); + rval = Q(GetValue(rref)); + } + Q(PutValue(lref, rval)); + return rval; + } + const assignmentPattern = LeftHandSideExpression; + const rref = yield* Evaluate(AssignmentExpression); + const rval = Q(GetValue(rref)); + Q(yield* DestructuringAssignmentEvaluation_AssignmentPattern(assignmentPattern, rval)); + return rval; + } else if (node.operator === '&&=') { + // 1. Let lref be the result of evaluating LeftHandSideExpression. + const lref = yield* Evaluate(LeftHandSideExpression); + // 2. Let lval be ? GetValue(lref). + const lval = Q(GetValue(lref)); + // 3. Let lbool be ! ToBoolean(lval). + const lbool = X(ToBoolean(lval)); + // 4. If lbool is false, return lval. + if (lbool === Value.false) { + return lval; + } + // 5. Let rref be the result of evaluating AssignmentExpression. + const rref = yield* Evaluate(AssignmentExpression); + // 6. Let rval be ? GetValue(rref). + const rval = Q(GetValue(rref)); + // 7. Perform ? PutValue(lref, rval). + Q(PutValue(lref, rval)); + // 8. Return rval. + return rval; + } else if (node.operator === '||=') { + // 1. Let lref be the result of evaluating LeftHandSideExpression. + const lref = yield* Evaluate(LeftHandSideExpression); + // 2. Let lval be ? GetValue(lref). + const lval = Q(GetValue(lref)); + // 3. Let lbool be ! ToBoolean(lval). + const lbool = X(ToBoolean(lval)); + // 4. If lbool is true, return lval. + if (lbool === Value.true) { + return lval; + } + // 5. Let rref be the result of evaluating AssignmentExpression. + const rref = yield* Evaluate(AssignmentExpression); + // 6. Let rval be ? GetValue(rref). + const rval = Q(GetValue(rref)); + // 7. Perform ? PutValue(lref, rval). + Q(PutValue(lref, rval)); + // 8. Return rval. + return rval; + } else if (node.operator === '??=') { + // 1.Let lref be the result of evaluating LeftHandSideExpression. + const lref = yield* Evaluate(LeftHandSideExpression); + // 2. Let lval be ? GetValue(lref). + const lval = Q(GetValue(lref)); + // 3. If lval is not undefined nor null, return lval. + if (lval !== Value.undefined && lval !== Value.null) { + return lval; + } + // 4. Let rref be the result of evaluating AssignmentExpression. + const rref = yield* Evaluate(AssignmentExpression); + // 5. Let rval be ? GetValue(rref). + const rval = Q(GetValue(rref)); + // 6. Perform ? PutValue(lref, rval). + Q(PutValue(lref, rval)); + // 7. Return rval. + return rval; + } else { + const AssignmentOperator = node.operator; + + const lref = yield* Evaluate(LeftHandSideExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(AssignmentExpression); + const rval = Q(GetValue(rref)); + // Let op be the @ where AssignmentOperator is @=. + const op = AssignmentOperator.slice(0, -1); + // Let r be the result of applying op to lval and rval + // as if evaluating the expression lval op rval. + const r = EvaluateBinopValues(op, lval, rval); + Q(PutValue(lref, r)); + return r; + } +} diff --git a/src/engine262/src/runtime-semantics/AsyncArrowFunction.mjs b/src/engine262/src/runtime-semantics/AsyncArrowFunction.mjs new file mode 100644 index 0000000..a950fab --- /dev/null +++ b/src/engine262/src/runtime-semantics/AsyncArrowFunction.mjs @@ -0,0 +1,10 @@ +import { Value } from '../value.mjs'; +import { NamedEvaluation_AsyncArrowFunction } from './all.mjs'; + +// 14.8.16 #sec-async-arrow-function-definitions-runtime-semantics-evaluation +// AsyncArrowFunction : +// `async` AsyncArrowBindingIdentifier `=>` AsyncConciseBody +// CoverCallExpressionAndAsyncArrowHead `=>` AsyncConciseBody +export function Evaluate_AsyncArrowFunction(AsyncArrowFunction) { + return NamedEvaluation_AsyncArrowFunction(AsyncArrowFunction, new Value('')); +} diff --git a/src/engine262/src/runtime-semantics/AsyncFunctionExpression.mjs b/src/engine262/src/runtime-semantics/AsyncFunctionExpression.mjs new file mode 100644 index 0000000..6085c92 --- /dev/null +++ b/src/engine262/src/runtime-semantics/AsyncFunctionExpression.mjs @@ -0,0 +1,37 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + isAsyncFunctionExpressionWithBindingIdentifier, +} from '../ast.mjs'; +import { + OrdinaryFunctionCreate, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { Value } from '../value.mjs'; +import { X } from '../completion.mjs'; +import { NamedEvaluation_AsyncFunctionExpression } from './all.mjs'; + +function Evaluate_AsyncFunctionExpression_BindingIdentifier(AsyncFunctionExpression) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = AsyncFunctionExpression; + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const funcEnv = NewDeclarativeEnvironment(scope); + const envRec = funcEnv.EnvironmentRecord; + const name = new Value(BindingIdentifier.name); + X(envRec.CreateImmutableBinding(name, Value.false)); + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncFunction.prototype%'), FormalParameters, AsyncFunctionExpression, 'non-lexical-this', funcEnv)); + X(SetFunctionName(closure, name)); + X(envRec.InitializeBinding(name, closure)); + closure.SourceText = sourceTextMatchedBy(AsyncFunctionExpression); + return closure; +} + +export function Evaluate_AsyncFunctionExpression(AsyncFunctionExpression) { + if (isAsyncFunctionExpressionWithBindingIdentifier(AsyncFunctionExpression)) { + return Evaluate_AsyncFunctionExpression_BindingIdentifier(AsyncFunctionExpression); + } + return NamedEvaluation_AsyncFunctionExpression(AsyncFunctionExpression, new Value('')); +} diff --git a/src/engine262/src/runtime-semantics/AsyncGeneratorExpression.mjs b/src/engine262/src/runtime-semantics/AsyncGeneratorExpression.mjs new file mode 100644 index 0000000..d2bd340 --- /dev/null +++ b/src/engine262/src/runtime-semantics/AsyncGeneratorExpression.mjs @@ -0,0 +1,47 @@ +import { + DefinePropertyOrThrow, + OrdinaryFunctionCreate, + OrdinaryObjectCreate, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { NamedEvaluation_AsyncGeneratorExpression } from './all.mjs'; + +// 14.4.14 #sec-generator-function-definitions-runtime-semantics-evaluation +// AsyncGeneratorExpression : +// `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +export function Evaluate_AsyncGeneratorExpression(AsyncGeneratorExpression) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = AsyncGeneratorExpression; + if (!BindingIdentifier) { + return NamedEvaluation_AsyncGeneratorExpression(AsyncGeneratorExpression, new Value('')); + } + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const funcEnv = NewDeclarativeEnvironment(scope); + const envRec = funcEnv.EnvironmentRecord; + const name = new Value(BindingIdentifier.name); + envRec.CreateImmutableBinding(name, Value.false); + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncGeneratorFunction.prototype%'), FormalParameters, AsyncGeneratorExpression, 'non-lexical-this', funcEnv)); + X(SetFunctionName(closure, name)); + const prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncGenerator.prototype%')); + X(DefinePropertyOrThrow( + closure, + new Value('prototype'), + Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }), + )); + closure.SourceText = sourceTextMatchedBy(AsyncGeneratorExpression); + envRec.InitializeBinding(name, closure); + return closure; +} diff --git a/src/engine262/src/runtime-semantics/AwaitExpression.mjs b/src/engine262/src/runtime-semantics/AwaitExpression.mjs new file mode 100644 index 0000000..c631627 --- /dev/null +++ b/src/engine262/src/runtime-semantics/AwaitExpression.mjs @@ -0,0 +1,11 @@ +import { GetValue } from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Await, Q } from '../completion.mjs'; + +// #prod-AwaitExpression +// AwaitExpression : `await` UnaryExpression +export function* Evaluate_AwaitExpression({ argument: UnaryExpression }) { + const exprRef = yield* Evaluate(UnaryExpression); + const value = Q(GetValue(exprRef)); + return Q(yield* Await(value)); +} diff --git a/src/engine262/src/runtime-semantics/BindingInitialization.mjs b/src/engine262/src/runtime-semantics/BindingInitialization.mjs new file mode 100644 index 0000000..dbf8e1c --- /dev/null +++ b/src/engine262/src/runtime-semantics/BindingInitialization.mjs @@ -0,0 +1,146 @@ +import { + Assert, + GetIterator, + IteratorClose, + PutValue, + RequireObjectCoercible, + ResolveBinding, +} from '../abstract-ops/all.mjs'; +import { + isArrayBindingPattern, + isBindingIdentifier, + isBindingPattern, + isBindingRestProperty, + isObjectBindingPattern, +} from '../ast.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + NormalCompletion, + Q, +} from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + IteratorBindingInitialization_ArrayBindingPattern, + PropertyBindingInitialization_BindingPropertyList, + RestBindingInitialization_BindingRestProperty, +} from './all.mjs'; + +// 12.1.5.1 #sec-initializeboundname +export function InitializeBoundName(name, value, environment) { + Assert(Type(name) === 'String'); + if (Type(environment) !== 'Undefined') { + const env = environment.EnvironmentRecord; + env.InitializeBinding(name, value); + return new NormalCompletion(Value.undefined); + } else { + const lhs = ResolveBinding(name, undefined, false); + return Q(PutValue(lhs, value)); + } +} + +// 12.1.5 #sec-identifiers-runtime-semantics-bindinginitialization +// BindingIdentifier : +// Identifier +// `yield` +// `await` +export function BindingInitialization_BindingIdentifier(BindingIdentifier, value, environment) { + const name = new Value(BindingIdentifier.name); + return Q(InitializeBoundName(name, value, environment)); +} + +// 13.3.3.5 #sec-destructuring-binding-patterns-runtime-semantics-bindinginitialization +// BindingPattern : +// ObjectBindingPattern +// ArrayBindingPattern +export function* BindingInitialization_BindingPattern(BindingPattern, value, environment) { + switch (true) { + case isObjectBindingPattern(BindingPattern): + Q(RequireObjectCoercible(value)); + return yield* BindingInitialization_ObjectBindingPattern(BindingPattern, value, environment); + + case isArrayBindingPattern(BindingPattern): { + const iteratorRecord = Q(GetIterator(value)); + const result = yield* IteratorBindingInitialization_ArrayBindingPattern( + BindingPattern, iteratorRecord, environment, + ); + if (iteratorRecord.Done === Value.false) { + return Q(IteratorClose(iteratorRecord, result)); + } + return result; + } + + default: + throw new OutOfRange('BindingInitialization_BindingPattern', BindingPattern); + } +} + +// (implicit) +// ForBinding : +// BindingIdentifier +// BindingPattern +export function* BindingInitialization_ForBinding(ForBinding, value, environment) { + switch (true) { + case isBindingIdentifier(ForBinding): + return BindingInitialization_BindingIdentifier(ForBinding, value, environment); + + case isBindingPattern(ForBinding): + return yield* BindingInitialization_BindingPattern(ForBinding, value, environment); + + default: + throw new OutOfRange('BindingInitialization_ForBinding', ForBinding); + } +} + +// 13.3.3.5 #sec-destructuring-binding-patterns-runtime-semantics-bindinginitialization +// ObjectBindingPattern : +// `{` `}` +// `{` BindingPropertyList `}` +// `{` BindingPropertyList `,` `}` +// `{` BindingRestProperty `}` +// `{` BindingPropertyList `,` BindingRestProperty `}` +function* BindingInitialization_ObjectBindingPattern(ObjectBindingPattern, value, environment) { + if (ObjectBindingPattern.properties.length === 0) { + return new NormalCompletion(undefined); + } + + let BindingRestProperty; + let BindingPropertyList = ObjectBindingPattern.properties; + const last = ObjectBindingPattern.properties[ObjectBindingPattern.properties.length - 1]; + if (isBindingRestProperty(last)) { + BindingRestProperty = last; + BindingPropertyList = BindingPropertyList.slice(0, -1); + } + + const excludedNames = Q(yield* PropertyBindingInitialization_BindingPropertyList( + BindingPropertyList, value, environment, + )); + if (BindingRestProperty === undefined) { + return new NormalCompletion(undefined); + } + + return RestBindingInitialization_BindingRestProperty( + BindingRestProperty, value, environment, excludedNames, + ); +} + +export function* BindingInitialization_CatchParameter(CatchParameter, value, environment) { + switch (true) { + case isBindingIdentifier(CatchParameter): + return BindingInitialization_BindingIdentifier(CatchParameter, value, environment); + + case isBindingPattern(CatchParameter): + return yield* BindingInitialization_BindingPattern(CatchParameter, value, environment); + + default: + throw new OutOfRange('BindingInitialization_CatchParameter', CatchParameter); + } +} + +// 13.7.5.9 #sec-for-in-and-for-of-statements-runtime-semantics-bindinginitialization +// ForDeclaration : LetOrConst ForBinding +export function* BindingInitialization_ForDeclaration(ForDeclaration, value, environment) { + return yield* BindingInitialization_ForBinding(ForDeclaration.declarations[0].id, value, environment); +} diff --git a/src/engine262/src/runtime-semantics/BitwiseOperators.mjs b/src/engine262/src/runtime-semantics/BitwiseOperators.mjs new file mode 100644 index 0000000..ac3b16c --- /dev/null +++ b/src/engine262/src/runtime-semantics/BitwiseOperators.mjs @@ -0,0 +1,59 @@ +import { surroundingAgent } from '../engine.mjs'; +import { GetValue, ToNumeric } from '../abstract-ops/all.mjs'; +import { Type, TypeNumeric } from '../value.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +/* eslint-disable no-bitwise */ + +export function EvaluateBinopValues_BitwiseANDExpression(lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + return T.bitwiseAND(lnum, rnum); +} + +export function EvaluateBinopValues_BitwiseXORExpression(lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + return T.bitwiseXOR(lnum, rnum); +} + +export function EvaluateBinopValues_BitwiseORExpression(lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + return T.bitwiseOR(lnum, rnum); +} + +// 12.12.3 #sec-binary-bitwise-operators-runtime-semantics-evaluation +export function* Evaluate_BinaryBitwiseExpression({ left: A, operator, right: B }) { + const lref = yield* Evaluate(A); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(B); + const rval = Q(GetValue(rref)); + + // Return the result of applying the bitwise operator @ to lnum and rnum. + switch (operator) { + case '&': + return EvaluateBinopValues_BitwiseANDExpression(lval, rval); + case '^': + return EvaluateBinopValues_BitwiseXORExpression(lval, rval); + case '|': + return EvaluateBinopValues_BitwiseORExpression(lval, rval); + + default: + throw new OutOfRange('Evaluate_BinaryBiwise', operator); + } +} diff --git a/src/engine262/src/runtime-semantics/BlockStatement.mjs b/src/engine262/src/runtime-semantics/BlockStatement.mjs new file mode 100644 index 0000000..85640c4 --- /dev/null +++ b/src/engine262/src/runtime-semantics/BlockStatement.mjs @@ -0,0 +1,76 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + BoundNames_Declaration, + IsConstantDeclaration, + LexicallyScopedDeclarations_StatementList, +} from '../static-semantics/all.mjs'; +import { + isAsyncFunctionDeclaration, + isAsyncGeneratorDeclaration, + isFunctionDeclaration, + isGeneratorDeclaration, +} from '../ast.mjs'; +import { + Assert, +} from '../abstract-ops/all.mjs'; +import { + DeclarativeEnvironmentRecord, + NewDeclarativeEnvironment, +} from '../environment.mjs'; +import { + Value, +} from '../value.mjs'; +import { + NormalCompletion, + X, +} from '../completion.mjs'; +import { Evaluate_StatementList } from '../evaluator.mjs'; +import { + InstantiateFunctionObject, +} from './all.mjs'; + +// 13.2.14 #sec-blockdeclarationinstantiation +export function BlockDeclarationInstantiation(code, env) { + const envRec = env.EnvironmentRecord; + Assert(envRec instanceof DeclarativeEnvironmentRecord); + const declarations = LexicallyScopedDeclarations_StatementList(code); + for (const d of declarations) { + for (const dn of BoundNames_Declaration(d).map(Value)) { + if (IsConstantDeclaration(d)) { + X(envRec.CreateImmutableBinding(dn, Value.true)); + } else { + X(envRec.CreateMutableBinding(dn, false)); + } + if (isFunctionDeclaration(d) || isGeneratorDeclaration(d) + || isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d)) { + const fn = BoundNames_Declaration(d)[0]; + const fo = InstantiateFunctionObject(d, env); + envRec.InitializeBinding(new Value(fn), fo); + } + } + } +} + +// 13.2.13 #sec-block-runtime-semantics-evaluation +// Block : +// `{` `}` +// `{` StatementList `}` +export function* Evaluate_Block(Block) { + const StatementList = Block.body; + + if (StatementList.length === 0) { + return new NormalCompletion(undefined); + } + + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const blockEnv = NewDeclarativeEnvironment(oldEnv); + BlockDeclarationInstantiation(StatementList, blockEnv); + surroundingAgent.runningExecutionContext.LexicalEnvironment = blockEnv; + const blockValue = yield* Evaluate_StatementList(StatementList); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return blockValue; +} + +export const Evaluate_BlockStatement = Evaluate_Block; diff --git a/src/engine262/src/runtime-semantics/BreakStatement.mjs b/src/engine262/src/runtime-semantics/BreakStatement.mjs new file mode 100644 index 0000000..441d011 --- /dev/null +++ b/src/engine262/src/runtime-semantics/BreakStatement.mjs @@ -0,0 +1,15 @@ +import { Value } from '../value.mjs'; +import { BreakCompletion } from '../completion.mjs'; + +// 13.9.3 #sec-break-statement-runtime-semantics-evaluation +// BreakStatement : +// `break` `;` +// `break` LabelIdentifier `;` +export function Evaluate_BreakStatement({ label: LabelIdentifier }) { + if (LabelIdentifier) { + const label = new Value(LabelIdentifier.name); + return new BreakCompletion(label); + } else { + return new BreakCompletion(); + } +} diff --git a/src/engine262/src/runtime-semantics/BreakableStatement.mjs b/src/engine262/src/runtime-semantics/BreakableStatement.mjs new file mode 100644 index 0000000..4ef5c42 --- /dev/null +++ b/src/engine262/src/runtime-semantics/BreakableStatement.mjs @@ -0,0 +1,61 @@ +import { + isIterationStatement, + isSwitchStatement, +} from '../ast.mjs'; +import { + Completion, + EnsureCompletion, + NormalCompletion, +} from '../completion.mjs'; +import { ValueSet, OutOfRange } from '../helpers.mjs'; +import { Value } from '../value.mjs'; +import { + Evaluate_SwitchStatement, + LabelledEvaluation_IterationStatement, +} from './all.mjs'; + +// 13.1.8 #sec-statement-semantics-runtime-semantics-evaluation +// BreakableStatement : +// IterationStatement +// SwitchStatement +export function* Evaluate_BreakableStatement(BreakableStatement) { + const newLabelSet = new ValueSet(); + return yield* LabelledEvaluation_BreakableStatement(BreakableStatement, newLabelSet); +} + +// 13.1.7 #sec-statement-semantics-runtime-semantics-labelledevaluation +// BreakableStatement : IterationStatement +export function* LabelledEvaluation_BreakableStatement(BreakableStatement, labelSet) { + switch (true) { + case isIterationStatement(BreakableStatement): { + let stmtResult = EnsureCompletion(yield* LabelledEvaluation_IterationStatement(BreakableStatement, labelSet)); + if (stmtResult.Type === 'break') { + if (stmtResult.Target === undefined) { + if (stmtResult.Value === undefined) { + stmtResult = new NormalCompletion(Value.undefined); + } else { + stmtResult = new NormalCompletion(stmtResult.Value); + } + } + } + return Completion(stmtResult); + } + + case isSwitchStatement(BreakableStatement): { + let stmtResult = EnsureCompletion(yield* Evaluate_SwitchStatement(BreakableStatement, labelSet)); + if (stmtResult.Type === 'break') { + if (stmtResult.Target === undefined) { + if (stmtResult.Value === undefined) { + stmtResult = new NormalCompletion(Value.undefined); + } else { + stmtResult = new NormalCompletion(stmtResult.Value); + } + } + } + return Completion(stmtResult); + } + + default: + throw new OutOfRange('LabelledEvaluation_BreakableStatement', BreakableStatement); + } +} diff --git a/src/engine262/src/runtime-semantics/CallExpression.mjs b/src/engine262/src/runtime-semantics/CallExpression.mjs new file mode 100644 index 0000000..0b3e05b --- /dev/null +++ b/src/engine262/src/runtime-semantics/CallExpression.mjs @@ -0,0 +1,118 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { + Assert, + Call, + GetBase, + GetReferencedName, + GetThisValue, + GetValue, + IsCallable, + IsPropertyReference, + PerformEval, + PrepareForTailCall, + SameValue, +} from '../abstract-ops/all.mjs'; +import { IsInTailPosition } from '../static-semantics/all.mjs'; +import { + AbruptCompletion, + Completion, + Q, +} from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { EnvironmentRecord } from '../environment.mjs'; +import { ArgumentListEvaluation, ArgumentListEvaluation_Arguments } from './all.mjs'; + +// #sec-evaluatecall +export function* EvaluateCall(func, ref, args, tailPosition) { + // 1. If Type(ref) is Reference, then + let thisValue; + if (Type(ref) === 'Reference') { + // a. If IsPropertyReference(ref) is true, then + if (IsPropertyReference(ref) === Value.true) { + // i. Let thisValue be GetThisValue(ref). + thisValue = GetThisValue(ref); + } else { + // i. Assert: the base of ref is an Environment Record. + Assert(ref.BaseValue instanceof EnvironmentRecord); + // ii. Let envRef be GetBase(ref). + const refEnv = GetBase(ref); + // iii. Let thisValue be envRef.WithBaseObject(). + thisValue = refEnv.WithBaseObject(); + } + } else { + // a. Let thisValue be undefined. + thisValue = Value.undefined; + } + // 3. Let argList be ? ArgumentListEvaluation of arguments. + const argList = Q(yield* ArgumentListEvaluation(args)); + // 4. If Type(func) is not Object, throw a TypeError exception. + if (Type(func) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); + } + // 5. If IsCallable(func) is false, throw a TypeError exception. + if (IsCallable(func) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', func); + } + // 6. If tailPosition is true, perform PrepareForTailCall(). + if (tailPosition) { + PrepareForTailCall(); + } + // 7. Let result be Call(func, thisValue, argList). + const result = Call(func, thisValue, argList); + // 8. Assert: If tailPosition is true, the above call will not return here but instead + // evaluation will continue as if the following return has already occurred. + Assert(!tailPosition); + // 9. Assert: If result is not an abrupt completion, then Type(result) is an ECMAScript language type. + if (!(result instanceof AbruptCompletion)) { + Assert(result instanceof Value || result instanceof Completion); + } + // 10. Return result. + return result; +} + +// #sec-function-calls-runtime-semantics-evaluation +// CallExpression : +// CoverCallExpressionAndAsyncArrowHead +// CallExpression Arguments +export function* Evaluate_CallExpression(CallExpression) { + // 1. Let expr be CoveredCallExpression of CoverCallExpressionAndAsyncArrowHead. + const expr = CallExpression; + // 2. Let memberExpr be the MemberExpression of expr. + const memberExpr = expr.callee; + // 3. Let arguments be the Arguments of expr. + const args = expr.arguments; + // 4. Let ref be the result of evaluating memberExpr. + const ref = yield* Evaluate(memberExpr); + // 5. Let func be ? GetValue(ref). + const func = Q(GetValue(ref)); + // 6. If Type(ref) is Reference, IsPropertyReference(ref) is false, and GetReferencedName(ref) is "eval", then + if (Type(ref) === 'Reference' + && IsPropertyReference(ref) === Value.false + && (Type(GetReferencedName(ref)) === 'String' + && GetReferencedName(ref).stringValue() === 'eval')) { + // a. If SameValue(func, %eval%) is true, then + if (SameValue(func, surroundingAgent.intrinsic('%eval%')) === Value.true) { + // i. Let argList be ? ArgumentListEvaluation of arguments. + const argList = Q(yield* ArgumentListEvaluation_Arguments(args)); + // ii. If argList has no elements, return undefined. + if (argList.length === 0) { + return Value.undefined; + } + // iii. Let evalText be the first element of argList. + const evalText = argList[0]; + // iv. If the source code matching this CallExpression is strict mode code, let strictCaller be true. Otherwise let strictCaller be false. + const strictCaller = CallExpression.strict; + // v. Let evalRealm be the current Realm Record. + const evalRealm = surroundingAgent.currentRealmRecord; + // vi. Return ? PerformEval(evalText, evalRealm, strictCaller, true). + return Q(PerformEval(evalText, evalRealm, strictCaller, true)); + } + } + // 7. Let thisCall be this CallExpression. + const thisCall = CallExpression; + // 8. Let tailCall be IsInTailPosition(thisCall). + const tailCall = IsInTailPosition(thisCall); + // 9. Return ? EvaluateCall(func, ref, arguments, tailCall). + return Q(yield* EvaluateCall(func, ref, args, tailCall)); +} diff --git a/src/engine262/src/runtime-semantics/ClassDefinition.mjs b/src/engine262/src/runtime-semantics/ClassDefinition.mjs new file mode 100644 index 0000000..eda3846 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ClassDefinition.mjs @@ -0,0 +1,181 @@ +import { surroundingAgent } from '../engine.mjs'; +import { emptyConstructorNode, forwardingConstructorNode } from '../parse.mjs'; +import { + Assert, + CreateMethodProperty, + Get, + GetValue, + IsConstructor, + MakeClassConstructor, + MakeConstructor, + OrdinaryObjectCreate, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Type, Value } from '../value.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { + ConstructorMethod_ClassBody, + IsStatic_ClassElement, + NonConstructorMethodDefinitions_ClassBody, +} from '../static-semantics/all.mjs'; +import { + AbruptCompletion, + Completion, + NormalCompletion, + Q, +} from '../completion.mjs'; +import { + DefineMethod, + InitializeBoundName, + PropertyDefinitionEvaluation_ClassElement, +} from './all.mjs'; + +// 14.6.13 #sec-runtime-semantics-classdefinitionevaluation +// ClassTail : ClassHeritage `{` ClassBody `}` +export function* ClassDefinitionEvaluation_ClassTail({ ClassHeritage, ClassBody }, classBinding, className) { + const lex = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const classScope = NewDeclarativeEnvironment(lex); + const classScopeEnvRec = classScope.EnvironmentRecord; + if (classBinding !== Value.undefined) { + classScopeEnvRec.CreateImmutableBinding(classBinding, Value.true); + } + let protoParent; + let constructorParent; + if (!ClassHeritage) { + protoParent = surroundingAgent.intrinsic('%Object.prototype%'); + constructorParent = surroundingAgent.intrinsic('%Function.prototype%'); + } else { + surroundingAgent.runningExecutionContext.LexicalEnvironment = classScope; + const superclassRef = yield* Evaluate(ClassHeritage); + surroundingAgent.runningExecutionContext.LexicalEnvironment = lex; + const superclass = Q(GetValue(superclassRef)); + if (Type(superclass) === 'Null') { + protoParent = Value.null; + constructorParent = surroundingAgent.intrinsic('%Function.prototype%'); + } else if (IsConstructor(superclass) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', superclass); + } else { + protoParent = Q(Get(superclass, new Value('prototype'))); + if (Type(protoParent) !== 'Object' && Type(protoParent) !== 'Null') { + return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType'); + } + constructorParent = superclass; + } + } + const proto = OrdinaryObjectCreate(protoParent); + let constructor; + if (!ClassBody) { + constructor = undefined; + } else { + constructor = ConstructorMethod_ClassBody(ClassBody); + } + if (constructor === undefined) { + if (ClassHeritage) { + // Set constructor to the result of parsing the source text `constructor(...args) { super(...args); }` + constructor = forwardingConstructorNode; + } else { + // Set constructor to the result of parsing the source text `constructor() {}` + constructor = emptyConstructorNode; + } + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = classScope; + const constructorInfo = yield* DefineMethod(constructor, proto, constructorParent); + Assert(!(constructorInfo instanceof AbruptCompletion)); + const F = constructorInfo.Closure; + SetFunctionName(F, className); + MakeConstructor(F, false, proto); + if (ClassHeritage) { + F.ConstructorKind = 'derived'; + } + MakeClassConstructor(F); + CreateMethodProperty(proto, new Value('constructor'), F); + let methods; + if (!ClassBody) { + methods = []; + } else { + methods = NonConstructorMethodDefinitions_ClassBody(ClassBody); + } + for (const m of methods) { + let status; + if (IsStatic_ClassElement(m) === false) { + status = yield* PropertyDefinitionEvaluation_ClassElement(m, proto, false); + } else { + status = yield* PropertyDefinitionEvaluation_ClassElement(m, F, false); + } + if (status instanceof AbruptCompletion) { + surroundingAgent.runningExecutionContext.LexicalEnvironment = lex; + return Completion(status); + } + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = lex; + if (classBinding !== Value.undefined) { + classScopeEnvRec.InitializeBinding(classBinding, F); + } + return F; +} + +// 14.6.16 #sec-class-definitions-runtime-semantics-evaluation +// ClassExpression : `class` BindingIdentifier ClassTail +export function* Evaluate_ClassExpression(ClassExpression) { + const { + id: BindingIdentifier, + body, + superClass, + } = ClassExpression; + const ClassTail = { + ClassHeritage: superClass, + ClassBody: body.body, + }; + + let className; + if (!BindingIdentifier) { + className = new Value(''); + } else { + className = new Value(BindingIdentifier.name); + } + const value = Q(yield* ClassDefinitionEvaluation_ClassTail(ClassTail, className, className)); + value.SourceText = sourceTextMatchedBy(ClassExpression); + return value; +} + +// 14.6.14 #sec-runtime-semantics-bindingclassdeclarationevaluation +// ClassDeclaration : +// `class` BindingIdentifier ClassTail +// `class` ClassTail +export function* BindingClassDeclarationEvaluation_ClassDeclaration(ClassDeclaration) { + const { + id: BindingIdentifier, + body, + superClass: ClassHeritage, + } = ClassDeclaration; + const ClassTail = { + ClassHeritage, + ClassBody: body.body, + }; + + let classBinding; + let className; + if (!BindingIdentifier) { + classBinding = Value.undefined; + className = new Value('default'); + } else { + classBinding = new Value(BindingIdentifier.name); + className = classBinding; + } + const value = Q(yield* ClassDefinitionEvaluation_ClassTail(ClassTail, classBinding, className)); + value.SourceText = sourceTextMatchedBy(ClassDeclaration); + if (BindingIdentifier) { + const env = surroundingAgent.runningExecutionContext.LexicalEnvironment; + Q(InitializeBoundName(className, value, env)); + } + return value; +} + +// 14.6.16 #sec-class-definitions-runtime-semantics-evaluation +// ClassDeclaration : `class` BindingIdentifier ClassTail +export function* Evaluate_ClassDeclaration(ClassDeclaration) { + Q(yield* BindingClassDeclarationEvaluation_ClassDeclaration(ClassDeclaration)); + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/CoalesceExpression.mjs b/src/engine262/src/runtime-semantics/CoalesceExpression.mjs new file mode 100644 index 0000000..4b1da07 --- /dev/null +++ b/src/engine262/src/runtime-semantics/CoalesceExpression.mjs @@ -0,0 +1,25 @@ +import { Q } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { GetValue } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; + +// #sec-binary-logical-operators-runtime-semantics-evaluation +// CoalesceExpression : CoalesceExpressionHead `??` BitwiseORExpression +export function* Evaluate_CoalesceExpression({ + left: CoalesceExpressionHead, + right: BitwiseORExpression, +}) { + // 1. Let lref be the result of evaluating |CoalesceExpressionHead|. + const lref = yield* Evaluate(CoalesceExpressionHead); + // 2. Let lval be ? GetValue(lref). + const lval = Q(GetValue(lref)); + // 3. If lval is *undefined* or *null*, + if (lval === Value.undefined || lval === Value.null) { + // a. Let rref be the result of evaluating |BitwiseORExpression|. + const rref = yield* Evaluate(BitwiseORExpression); + // b. Return ? GetValue(rref). + return Q(GetValue(rref)); + } + // 4. Otherwise, return lval. + return lval; +} diff --git a/src/engine262/src/runtime-semantics/ConditionalExpression.mjs b/src/engine262/src/runtime-semantics/ConditionalExpression.mjs new file mode 100644 index 0000000..ec0052d --- /dev/null +++ b/src/engine262/src/runtime-semantics/ConditionalExpression.mjs @@ -0,0 +1,29 @@ +import { Evaluate } from '../evaluator.mjs'; +import { Value } from '../value.mjs'; +import { GetValue, ToBoolean } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; + +// 12.14.3 #sec-conditional-operator-runtime-semantics-evaluation +// ConditionalExpression : ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression +export function* Evaluate_ConditionalExpression({ + test: ShortCircuitExpression, + consequent: FirstAssignmentExpression, + alternate: SecondAssignmentExpression, +}) { + // 1. Let lref be the result of evaluating ShortCircuitExpression. + const lref = yield* Evaluate(ShortCircuitExpression); + // 2. Let lval be ! ToBoolean(? GetValue(lref)). + const lval = ToBoolean(Q(GetValue(lref))); + // 3. If lval is true, then + if (lval === Value.true) { + // a. Let trueRef be the result of evaluating the first AssignmentExpression. + const trueRef = yield* Evaluate(FirstAssignmentExpression); + // b. Return ? GetValue(trueRef). + return Q(GetValue(trueRef)); + } else { + // a. Let falseRef be the result of evaluating the second AssignmentExpression. + const falseRef = yield* Evaluate(SecondAssignmentExpression); + // b. Return ? GetValue(falseRef). + return Q(GetValue(falseRef)); + } +} diff --git a/src/engine262/src/runtime-semantics/ContinueStatement.mjs b/src/engine262/src/runtime-semantics/ContinueStatement.mjs new file mode 100644 index 0000000..8477a42 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ContinueStatement.mjs @@ -0,0 +1,15 @@ +import { Value } from '../value.mjs'; +import { ContinueCompletion } from '../completion.mjs'; + +// 13.8.3 #sec-continue-statement-runtime-semantics-evaluation +// ContinueStatement : +// `continue` `;` +// `continue` LabelIdentifier `;` +export function Evaluate_ContinueStatement({ label: LabelIdentifier }) { + if (LabelIdentifier) { + const label = new Value(LabelIdentifier.name); + return new ContinueCompletion(label); + } else { + return new ContinueCompletion(undefined); + } +} diff --git a/src/engine262/src/runtime-semantics/CreateDynamicFunction.mjs b/src/engine262/src/runtime-semantics/CreateDynamicFunction.mjs new file mode 100644 index 0000000..74e848c --- /dev/null +++ b/src/engine262/src/runtime-semantics/CreateDynamicFunction.mjs @@ -0,0 +1,189 @@ +import { + Assert, + DefinePropertyOrThrow, + GetPrototypeFromConstructor, + MakeConstructor, + OrdinaryFunctionCreate, + OrdinaryObjectCreate, + SetFunctionName, + ToString, +} from '../abstract-ops/all.mjs'; +import { + Q, X, +} from '../completion.mjs'; +import { + HostEnsureCanCompileStrings, + surroundingAgent, +} from '../engine.mjs'; +import { + ParseAsAsyncFunctionBody, + ParseAsAsyncGeneratorBody, + ParseAsFormalParameters, + ParseAsFunctionBody, + ParseAsGeneratorBody, +} from '../parse.mjs'; +import { + BoundNames_FormalParameters, + ContainsUseStrict_FunctionBody, + LexicallyDeclaredNames_FunctionBody, +} from '../static-semantics/all.mjs'; +import { + Descriptor, + Type, + Value, +} from '../value.mjs'; +import { ValueSet } from '../helpers.mjs'; + +function hasIntersection(reference, check) { + if (reference.length === 0 || check.length === 0) { + return false; + } + const refSet = new ValueSet(reference); + for (const el of check) { + if (refSet.has(el)) { + return el; + } + } + return false; +} + +// #table-dynamic-function-sourcetext-prefixes +const DynamicFunctionSourceTextPrefixes = { + 'normal': 'function', + 'generator': 'function*', + 'async': 'async function', + 'async generator': 'async function*', +}; + +// 19.2.1.1.1 #sec-createdynamicfunction +export function CreateDynamicFunction(constructor, newTarget, kind, args) { + Assert(surroundingAgent.executionContextStack.length >= 2); + const callerContext = surroundingAgent.executionContextStack[surroundingAgent.executionContextStack.length - 2]; + const callerRealm = callerContext.Realm; + const calleeRealm = surroundingAgent.currentRealmRecord; + Q(HostEnsureCanCompileStrings(callerRealm, calleeRealm)); + if (Type(newTarget) === 'Undefined') { + newTarget = constructor; + } + let bodyParser; + let enableYield; + let enableAwait; + let fallbackProto; + if (kind === 'normal') { + bodyParser = ParseAsFunctionBody; + enableYield = false; + enableAwait = false; + fallbackProto = '%Function.prototype%'; + } else if (kind === 'generator') { + bodyParser = ParseAsGeneratorBody; + enableYield = true; + enableAwait = false; + fallbackProto = '%Generator%'; + } else if (kind === 'async') { + bodyParser = ParseAsAsyncFunctionBody; + enableYield = false; + enableAwait = true; + fallbackProto = '%AsyncFunction.prototype%'; + } else if (kind === 'async generator') { + bodyParser = ParseAsAsyncGeneratorBody; + enableYield = true; + enableAwait = true; + fallbackProto = '%AsyncGeneratorFunction.prototype%'; + } + const argCount = args.length; + let P = ''; + let bodyText; + if (argCount === 0) { + bodyText = new Value(''); + } else if (argCount === 1) { + bodyText = args[0]; + } else { + const firstArg = args[0]; + P = Q(ToString(firstArg)).stringValue(); + let k = 1; + while (k < argCount - 1) { + const nextArg = args[k]; + const nextArgString = Q(ToString(nextArg)); + P = `${P},${nextArgString.stringValue()}`; + k += 1; + } + bodyText = args[k]; + } + bodyText = `\u000A${Q(ToString(bodyText)).stringValue()}\u000A`; + + let body; + try { + body = bodyParser(bodyText); + } catch (err) { + return surroundingAgent.Throw('SyntaxError', 'Raw', err.message); + } + const strict = ContainsUseStrict_FunctionBody(body); + let parameters; + try { + parameters = ParseAsFormalParameters(P, strict, enableAwait, enableYield); + } catch (err) { + return surroundingAgent.Throw('SyntaxError', 'Raw', err.message); + } + + // These steps are included in ParseAsFormalParameters: + // 20. If strict is true, the Early Error rules for UniqueFormalParameters : FormalParameters are applied. + // 21. If strict is true and IsSimpleParameterList of parameters is false, throw a SyntaxError exception. + // 24. If parameters Contains SuperCall is true, throw a SyntaxError exception. + // 26. If parameters Contains SuperProperty is true, throw a SyntaxError exception. + // 27. If kind is "generator" or "async generator", then + // a. If parameters Contains YieldExpression is true, throw a SyntaxError exception. + // 28. If kind is "async" or "async generator", then + // a. If parameters Contains AwaitExpression is true, throw a SyntaxError exception. + // 29. If strict is true, then + // a. If BoundNames of parameters contains any duplicate elements, throw a SyntaxError exception. + + // 22. If any element of the BoundNames of parameters also occurs in the LexicallyDeclaredNames of body, throw a SyntaxError exception. + const intersected = hasIntersection(BoundNames_FormalParameters(parameters), LexicallyDeclaredNames_FunctionBody(body)); + if (intersected !== false) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', intersected); + } + + const fabricatedFunctionNode = { + type: 'FunctionExpression', + id: null, + generator: enableYield, + expression: false, + async: enableAwait, + params: parameters, + strict, + body: { + type: 'BlockStatement', + body, + strict, + }, + }; + + const proto = Q(GetPrototypeFromConstructor(newTarget, fallbackProto)); + const realmF = surroundingAgent.currentRealmRecord; + const scope = realmF.GlobalEnv; + const F = X(OrdinaryFunctionCreate(proto, parameters, fabricatedFunctionNode, 'Normal', scope)); + SetFunctionName(F, new Value('anonymous')); + if (kind === 'generator') { + const prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Generator.prototype%')); + X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + } else if (kind === 'async generator') { + const prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncGenerator.prototype%')); + X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + } else if (kind === 'normal') { + MakeConstructor(F); + } + const prefix = DynamicFunctionSourceTextPrefixes[kind]; + const sourceText = `${prefix} anonymous(${P}\u000A) {${bodyText}}`; + F.SourceText = new Value(sourceText); + return F; +} diff --git a/src/engine262/src/runtime-semantics/DebuggerStatement.mjs b/src/engine262/src/runtime-semantics/DebuggerStatement.mjs new file mode 100644 index 0000000..cea835a --- /dev/null +++ b/src/engine262/src/runtime-semantics/DebuggerStatement.mjs @@ -0,0 +1,19 @@ +import { surroundingAgent } from '../engine.mjs'; +import { NormalCompletion, EnsureCompletion } from '../completion.mjs'; + +// 13.16.1 #sec-debugger-statement-runtime-semantics-evaluation +// DebuggerStatement : `debugger` `;` +export function Evaluate_DebuggerStatement() { + let result; + // 1. If an implementation-defined debugging facility is available and enabled, then + if (surroundingAgent.hostDefinedOptions.onDebugger) { + // a. Perform an implementation-defined debugging action. + // b. Let result be an implementation-defined Completion value. + result = EnsureCompletion(surroundingAgent.hostDefinedOptions.onDebugger()); + } else { + // a. Let result be NormalCompletion(empty). + result = NormalCompletion(undefined); + } + // 2. Return result. + return result; +} diff --git a/src/engine262/src/runtime-semantics/DefineMethod.mjs b/src/engine262/src/runtime-semantics/DefineMethod.mjs new file mode 100644 index 0000000..e0fd8a8 --- /dev/null +++ b/src/engine262/src/runtime-semantics/DefineMethod.mjs @@ -0,0 +1,32 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + OrdinaryFunctionCreate, + MakeMethod, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { ReturnIfAbrupt, X } from '../completion.mjs'; +import { Evaluate_PropertyName } from './all.mjs'; + +// 14.3.7 #sec-runtime-semantics-definemethod +// MethodDefinition : PropertyName `(` UniqueFormalParameters `)` `{` FunctionBody `}` +export function* DefineMethod(MethodDefinition, object, functionPrototype) { + const PropertyName = MethodDefinition.key; + const UniqueFormalParameters = MethodDefinition.value.params; + + const propKey = yield* Evaluate_PropertyName(PropertyName, MethodDefinition.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + let prototype; + if (functionPrototype !== undefined) { + prototype = functionPrototype; + } else { + prototype = surroundingAgent.intrinsic('%Function.prototype%'); + } + const closure = X(OrdinaryFunctionCreate(prototype, UniqueFormalParameters, MethodDefinition.value, 'non-lexical-this', scope)); + X(MakeMethod(closure, object)); + closure.SourceText = sourceTextMatchedBy(MethodDefinition); + return { + Key: propKey, + Closure: closure, + }; +} diff --git a/src/engine262/src/runtime-semantics/DestructuringAssignmentEvaluation.mjs b/src/engine262/src/runtime-semantics/DestructuringAssignmentEvaluation.mjs new file mode 100644 index 0000000..ecdd437 --- /dev/null +++ b/src/engine262/src/runtime-semantics/DestructuringAssignmentEvaluation.mjs @@ -0,0 +1,390 @@ +import { + ArrayCreate, + Assert, + CopyDataProperties, + CreateDataProperty, + GetIterator, + GetReferencedName, + GetV, + GetValue, + IteratorClose, + IteratorStep, + IteratorValue, + OrdinaryObjectCreate, + PutValue, + RequireObjectCoercible, + ResolveBinding, + ToString, +} from '../abstract-ops/all.mjs'; +import { + isArrayAssignmentPattern, + isAssignmentPattern, + isAssignmentRestProperty, + isObjectAssignmentPattern, +} from '../ast.mjs'; +import { + AbruptCompletion, Completion, + NormalCompletion, + Q, + ReturnIfAbrupt, + X, +} from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { + IsAnonymousFunctionDefinition, + IsIdentifierRef, +} from '../static-semantics/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { Evaluate_PropertyName, NamedEvaluation_Expression } from './all.mjs'; + +// (implicit) +// AssignmentPattern : +// ObjectAssignmentPattern +// ArrayAssignmentPattern +export function* DestructuringAssignmentEvaluation_AssignmentPattern(AssignmentPattern, value) { + switch (true) { + case isObjectAssignmentPattern(AssignmentPattern): + return yield* DestructuringAssignmentEvaluation_ObjectAssignmentPattern(AssignmentPattern, value); + + case isArrayAssignmentPattern(AssignmentPattern): + return yield* DestructuringAssignmentEvaluation_ArrayAssignmentPattern(AssignmentPattern, value); + + default: + throw new OutOfRange('DestructuringAssignmentEvaluation_AssignmentPattern', AssignmentPattern); + } +} + +// 12.15.5.2 #sec-runtime-semantics-destructuringassignmentevaluation +// ObjectAssignmentPattern : +// `{` `}` +// `{` AssignmentRestProperty `}` +// `{` AssignmentPropertyList `}` +// `{` AssignmentPropertyList `,` `}` +// `{` AssignmentPropertyList `,` AssignmentRestProperty `}` +function* DestructuringAssignmentEvaluation_ObjectAssignmentPattern(ObjectAssignmentPattern, value) { + let AssignmentPropertyList = ObjectAssignmentPattern.properties; + let AssignmentRestProperty; + // Members of the AssignmentPropertyList may be null, so add a truthyness check. + if (AssignmentPropertyList.length > 0 && AssignmentPropertyList[AssignmentPropertyList.length - 1] + && isAssignmentRestProperty(AssignmentPropertyList[AssignmentPropertyList.length - 1])) { + AssignmentRestProperty = AssignmentPropertyList[AssignmentPropertyList.length - 1]; + AssignmentPropertyList = AssignmentPropertyList.slice(0, -1); + } + + Q(RequireObjectCoercible(value)); + let excludedNames = []; + if (AssignmentPropertyList.length > 0) { + excludedNames = Q(yield* PropertyDestructuringAssignmentEvaluation_AssignmentPropertyList( + AssignmentPropertyList, value, + )); + } + if (AssignmentRestProperty === undefined) { + return new NormalCompletion(undefined); + } + return yield* RestDestructuringAssignmentEvaluation_AssignmentRestProperty(AssignmentRestProperty, value, excludedNames); +} + +// 12.15.5.2 #sec-runtime-semantics-destructuringassignmentevaluation +// ArrayAssignmentPattern : +// `[` `]` +// `[` Elision `]` +// `[` Elision_opt AssignmentRestProperty `]` +// `[` AssignmentElementList `]` +// `[` AssignmentElementList `,` Elision_opt AssignmentRestProperty_opt `]` +function* DestructuringAssignmentEvaluation_ArrayAssignmentPattern(ArrayAssignmentPattern, value) { + let Elision; + let AssignmentElementList = ArrayAssignmentPattern.elements; + let AssignmentRestProperty; + // Members of the AssignmentElementList may be null, so add a truthyness check. + if (AssignmentElementList.length > 0 && AssignmentElementList[AssignmentElementList.length - 1] + && isAssignmentRestProperty(AssignmentElementList[AssignmentElementList.length - 1])) { + AssignmentRestProperty = AssignmentElementList[AssignmentElementList.length - 1]; + AssignmentElementList = AssignmentElementList.slice(0, -1); + } + if (AssignmentElementList.length > 0) { + let begin; + for (begin = AssignmentElementList.length; begin > 0; begin -= 1) { + if (AssignmentElementList[begin - 1] !== null) { + break; + } + } + if (begin !== AssignmentElementList.length) { + Elision = AssignmentElementList.slice(begin); + AssignmentElementList = AssignmentElementList.slice(0, begin); + } + } + + const iteratorRecord = Q(GetIterator(value)); + // ArrayAssignmentPattern : `[` `]` + if (AssignmentElementList.length === 0 && Elision === undefined && AssignmentRestProperty === undefined) { + return Q(IteratorClose(iteratorRecord, new NormalCompletion(undefined))); + } + let status; + if (AssignmentElementList.length > 0) { + status = yield* IteratorDestructuringAssignmentEvaluation_AssignmentElementList(AssignmentElementList, iteratorRecord); + if (status instanceof AbruptCompletion) { + if (iteratorRecord.Done === Value.false) { + return Q(IteratorClose(iteratorRecord, status)); + } + return Completion(status); + } + } + if (Elision !== undefined) { + status = IteratorDestructuringAssignmentEvaluation_Elision(Elision, iteratorRecord); + if (AssignmentRestProperty === undefined) { + // ArrayAssignmentPattern : `[` Elision `]` + } else { + // ArrayAssignmentPattern : + // `[` Elision AssignmentRestElement `]` + // `[` AssignmentElementList `,` Elision AssignmentRestElement_opt `]` + if (status instanceof AbruptCompletion) { + Assert(iteratorRecord.Done === Value.true); + return Completion(status); + } + } + } + if (AssignmentRestProperty !== undefined) { + status = yield* IteratorDestructuringAssignmentEvaluation_AssignmentRestProperty(AssignmentRestProperty, iteratorRecord); + } + if (iteratorRecord.Done === Value.false) { + return Q(IteratorClose(iteratorRecord, status)); + } + return Completion(status); +} + +// 12.15.5.3 #sec-runtime-semantics-propertydestructuringassignmentevaluation +// AssignmentPropertyList : AssignmentPropertyList `,` AssignmentProperty +// +// (implicit) +// AssignmentPropertyList : AssignmentProperty +function* PropertyDestructuringAssignmentEvaluation_AssignmentPropertyList(AssignmentPropertyList, value) { + const propertyNames = []; + for (const AssignmentProperty of AssignmentPropertyList) { + const nextNames = Q(yield* PropertyDestructuringAssignmentEvaluation_AssignmentProperty(AssignmentProperty, value)); + propertyNames.push(...nextNames); + } + return propertyNames; +} + +// 12.15.5.3 #sec-runtime-semantics-propertydestructuringassignmentevaluation +// AssignmentProperty : +// IdentifierReference Initializer_opt +// PropertyName `:` AssignmentElement +function* PropertyDestructuringAssignmentEvaluation_AssignmentProperty(AssignmentProperty, value) { + if (AssignmentProperty.shorthand) { + // AssignmentProperty : IdentifierReference Initializer_opt + const IdentifierReference = AssignmentProperty.key; + let Initializer; + if (AssignmentProperty.value.type === 'AssignmentPattern') { + Initializer = AssignmentProperty.value.right; + } + + const P = new Value(IdentifierReference.name); + const lref = Q(ResolveBinding(P, undefined, IdentifierReference.strict)); + let v = Q(GetV(value, P)); + if (Initializer !== undefined && Type(v) === 'Undefined') { + if (IsAnonymousFunctionDefinition(Initializer)) { + v = yield* NamedEvaluation_Expression(Initializer, P); + } else { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + } + Q(PutValue(lref, v)); + return [P]; + } + + const { + key: PropertyName, + value: AssignmentElement, + } = AssignmentProperty; + + const name = yield* Evaluate_PropertyName(PropertyName, AssignmentProperty.computed); + ReturnIfAbrupt(name); + Q(yield* KeyedDestructuringAssignmentEvaluation_AssignmentElement(AssignmentElement, value, name)); + return [name]; +} + +// 12.15.5.4 #sec-runtime-semantics-restdestructuringassignmentevaluation +// AssignmentRestProperty : `...` DestructuringAssignmentTarget +function* RestDestructuringAssignmentEvaluation_AssignmentRestProperty(AssignmentRestProperty, value, excludedNames) { + const DestructuringAssignmentTarget = AssignmentRestProperty.argument; + const lref = yield* Evaluate(DestructuringAssignmentTarget); + ReturnIfAbrupt(lref); + const restObj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + Q(CopyDataProperties(restObj, value, excludedNames)); + return PutValue(lref, restObj); +} + +// 12.15.5.5 #sec-runtime-semantics-iteratordestructuringassignmentevaluation +// AssignmentElementList : +// AssignmentElisionElement +// AssignmentElementList `,` AssignmentElisionElement +function* IteratorDestructuringAssignmentEvaluation_AssignmentElementList(AssignmentElementList, iteratorRecord) { + Assert(AssignmentElementList.length > 0); + let result; + for (const AssignmentElisionElement of AssignmentElementList) { + result = Q(yield* IteratorDestructuringAssignmentEvaluation_AssignmentElisionElement(AssignmentElisionElement, iteratorRecord)); + } + return result; +} + +// 12.15.5.5 #sec-runtime-semantics-iteratordestructuringassignmentevaluation +// AssignmentElisionElement : +// AssignmentElement +// Elision AssignmentElement +function* IteratorDestructuringAssignmentEvaluation_AssignmentElisionElement(AssignmentElisionElement, iteratorRecord) { + if (!AssignmentElisionElement) { + // This is an elision. + return IteratorDestructuringAssignmentEvaluation_Elision([AssignmentElisionElement], iteratorRecord); + } + return yield* IteratorDestructuringAssignmentEvaluation_AssignmentElement(AssignmentElisionElement, iteratorRecord); +} + +// 12.15.5.5 #sec-runtime-semantics-iteratordestructuringassignmentevaluation +// AssignmentElement : DestructuringAssignmentTarget Initializer_opt +function* IteratorDestructuringAssignmentEvaluation_AssignmentElement(AssignmentElement, iteratorRecord) { + let DestructuringAssignmentTarget = AssignmentElement; + let Initializer; + if (AssignmentElement.type === 'AssignmentPattern') { + DestructuringAssignmentTarget = AssignmentElement.left; + Initializer = AssignmentElement.right; + } + + let lref; + if (!isAssignmentPattern(DestructuringAssignmentTarget)) { + lref = yield* Evaluate(DestructuringAssignmentTarget); + ReturnIfAbrupt(lref); + } + let value; + if (iteratorRecord.Done === Value.false) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } else { + value = IteratorValue(next); + if (value instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(value); + } + } + if (iteratorRecord.Done === Value.true) { + value = Value.undefined; + } + let v; + if (Initializer !== undefined && value === Value.undefined) { + if (IsAnonymousFunctionDefinition(Initializer) + && IsIdentifierRef(DestructuringAssignmentTarget)) { + v = yield* NamedEvaluation_Expression(Initializer, GetReferencedName(lref)); + } else { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + } else { + v = value; + } + if (isAssignmentPattern(DestructuringAssignmentTarget)) { + const nestedAssignmentPattern = DestructuringAssignmentTarget; + return yield* DestructuringAssignmentEvaluation_AssignmentPattern(nestedAssignmentPattern, v); + } + return Q(PutValue(lref, v)); +} + +// 12.15.5.5 #sec-runtime-semantics-iteratordestructuringassignmentevaluation +// Elision : +// `,` +// Elision `,` +export function IteratorDestructuringAssignmentEvaluation_Elision(Elision, iteratorRecord) { + let remaining = Elision.length; + while (remaining > 0 && iteratorRecord.Done === Value.false) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } + remaining -= 1; + } + return new NormalCompletion(undefined); +} + +// 12.15.5.5 #sec-runtime-semantics-iteratordestructuringassignmentevaluation +// AssignmentRestElement : `...` DestructuringAssignmentTarget +function* IteratorDestructuringAssignmentEvaluation_AssignmentRestProperty(AssignmentRestProperty, iteratorRecord) { + const DestructuringAssignmentTarget = AssignmentRestProperty.argument; + let lref; + if (!isAssignmentPattern(DestructuringAssignmentTarget)) { + lref = yield* Evaluate(DestructuringAssignmentTarget); + ReturnIfAbrupt(lref); + } + const A = X(ArrayCreate(new Value(0))); + let n = 0; + while (iteratorRecord.Done === Value.false) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } else { + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + const status = X(CreateDataProperty(A, ToString(new Value(n)), nextValue)); + Assert(status === Value.true); + n += 1; + } + } + if (!isAssignmentPattern(DestructuringAssignmentTarget)) { + return Q(PutValue(lref, A)); + } + const nestedAssignmentPattern = DestructuringAssignmentTarget; + return yield* DestructuringAssignmentEvaluation_AssignmentPattern(nestedAssignmentPattern, A); +} + +// 12.15.5.6 #sec-runtime-semantics-keyeddestructuringassignmentevaluation +// AssignmentElement : DestructuringAssignmentTarget Initializer_opt +function* KeyedDestructuringAssignmentEvaluation_AssignmentElement(AssignmentElement, value, propertyName) { + let DestructuringAssignmentTarget = AssignmentElement; + let Initializer; + if (AssignmentElement.type === 'AssignmentPattern') { + DestructuringAssignmentTarget = AssignmentElement.left; + Initializer = AssignmentElement.right; + } + + let lref; + if (!isAssignmentPattern(DestructuringAssignmentTarget)) { + lref = yield* Evaluate(DestructuringAssignmentTarget); + ReturnIfAbrupt(lref); + } + const v = Q(GetV(value, propertyName)); + let rhsValue; + if (Initializer !== undefined && v === Value.undefined) { + if (IsAnonymousFunctionDefinition(Initializer) + && IsIdentifierRef(DestructuringAssignmentTarget)) { + rhsValue = yield* NamedEvaluation_Expression(Initializer, GetReferencedName(lref)); + } else { + const defaultValue = yield* Evaluate(Initializer); + rhsValue = Q(GetValue(defaultValue)); + } + } else { + rhsValue = v; + } + if (isAssignmentPattern(DestructuringAssignmentTarget)) { + const assignmentPattern = DestructuringAssignmentTarget; + return yield* DestructuringAssignmentEvaluation_AssignmentPattern(assignmentPattern, rhsValue); + } + return Q(PutValue(lref, rhsValue)); +} diff --git a/src/engine262/src/runtime-semantics/EmptyStatement.mjs b/src/engine262/src/runtime-semantics/EmptyStatement.mjs new file mode 100644 index 0000000..0bd8e10 --- /dev/null +++ b/src/engine262/src/runtime-semantics/EmptyStatement.mjs @@ -0,0 +1,7 @@ +import { NormalCompletion } from '../completion.mjs'; + +// 13.4.1 #sec-empty-statement-runtime-semantics-evaluation +// EmptyStatement : `;` +export function Evaluate_EmptyStatement(/* EmptyStatement */) { + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/EqualityExpression.mjs b/src/engine262/src/runtime-semantics/EqualityExpression.mjs new file mode 100644 index 0000000..0012a40 --- /dev/null +++ b/src/engine262/src/runtime-semantics/EqualityExpression.mjs @@ -0,0 +1,52 @@ +import { + AbstractEqualityComparison, + GetValue, + StrictEqualityComparison, +} from '../abstract-ops/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Value } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 12.11.3 #sec-equality-operators-runtime-semantics-evaluation +// EqualityExpression : +// EqualityExpression `==` RelationalExpression +// EqualityExpression `!=` RelationalExpression +// EqualityExpression `===` RelationalExpression +// EqualityExpression `!==` RelationalExpression +export function* Evaluate_EqualityExpression({ + left: EqualityExpression, + operator, + right: RelationalExpression, +}) { + const lref = yield* Evaluate(EqualityExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(RelationalExpression); + const rval = Q(GetValue(rref)); + + switch (operator) { + case '==': + return AbstractEqualityComparison(rval, lval); + case '!=': { + const r = Q(AbstractEqualityComparison(rval, lval)); + if (r === Value.true) { + return Value.false; + } else { + return Value.true; + } + } + case '===': + return StrictEqualityComparison(rval, lval); + case '!==': { + const r = X(StrictEqualityComparison(rval, lval)); + if (r === Value.true) { + return Value.false; + } else { + return Value.true; + } + } + + default: + throw new OutOfRange('Evaluate_EqualityExpression', operator); + } +} diff --git a/src/engine262/src/runtime-semantics/EvaluateBody.mjs b/src/engine262/src/runtime-semantics/EvaluateBody.mjs new file mode 100644 index 0000000..b5030dd --- /dev/null +++ b/src/engine262/src/runtime-semantics/EvaluateBody.mjs @@ -0,0 +1,486 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + AsyncFunctionStart, + Call, + CreateListIteratorRecord, + CreateMappedArgumentsObject, + CreateUnmappedArgumentsObject, + GeneratorStart, + NewPromiseCapability, + OrdinaryCreateFromConstructor, + AsyncGeneratorStart, +} from '../abstract-ops/all.mjs'; +import { + isArrowFunction, + isAsyncArrowFunction, + isAsyncFunctionDeclaration, + isAsyncFunctionExpression, + isAsyncGeneratorDeclaration, + isAsyncGeneratorExpression, + isBindingIdentifier, + isForBinding, + isFunctionDeclaration, + isFunctionExpression, + isGeneratorDeclaration, + isGeneratorExpression, + isVariableDeclaration, +} from '../ast.mjs'; +import { + BoundNames_Declaration, + BoundNames_FormalParameters, + BoundNames_FunctionDeclaration, + ContainsExpression_FormalParameters, + IsConstantDeclaration, + IsSimpleParameterList_FormalParameters, + LexicallyDeclaredNames_AsyncFunctionBody, + LexicallyDeclaredNames_ConciseBody, + LexicallyDeclaredNames_FunctionBody, + LexicallyDeclaredNames_GeneratorBody, + LexicallyScopedDeclarations_AsyncFunctionBody, + LexicallyScopedDeclarations_ConciseBody, + LexicallyScopedDeclarations_FunctionBody, + LexicallyScopedDeclarations_GeneratorBody, + VarDeclaredNames_AsyncFunctionBody, + VarDeclaredNames_ConciseBody, + VarDeclaredNames_FunctionBody, + VarDeclaredNames_GeneratorBody, + VarScopedDeclarations_AsyncFunctionBody, + VarScopedDeclarations_ConciseBody, + VarScopedDeclarations_FunctionBody, + VarScopedDeclarations_GeneratorBody, +} from '../static-semantics/all.mjs'; +import { + AbruptCompletion, + Completion, + NormalCompletion, + Q, + ReturnCompletion, X, +} from '../completion.mjs'; +import { + NewDeclarativeEnvironment, +} from '../environment.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { Value } from '../value.mjs'; +import { + Evaluate_ExpressionBody, + Evaluate_FunctionStatementList, + InstantiateFunctionObject, + IteratorBindingInitialization_FormalParameters, +} from './all.mjs'; + +// #sec-functiondeclarationinstantiation +export function* FunctionDeclarationInstantiation(func, argumentsList) { + // 1. Let calleeContext be the running execution context. + const calleeContext = surroundingAgent.runningExecutionContext; + // 2. Let code be func.[[ECMAScriptCode]]. + const code = func.ECMAScriptCode; + // 3. Let strict be func.[[Strict]]. + const strict = func.Strict; + // 4. Let formals be func.[[FormalParameters]]. + const formals = func.FormalParameters; + // 5. Let parameterNames be BoundNames of formals. + const parameterNames = BoundNames_FormalParameters(formals); + // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false. + const hasDuplicates = parameterNames.some((e) => parameterNames.indexOf(e) !== parameterNames.lastIndexOf(e)); + // 7. Let simpleParameterList be IsSimpleParameterList of formals. + const simpleParameterList = IsSimpleParameterList_FormalParameters(formals); + // 8. Let hasParameterExpressions be ContainsExpression of formals. + const hasParameterExpressions = ContainsExpression_FormalParameters(formals); + + // 9. Let varNames be the VarDeclaredNames of code. + // 10. Let varDeclarations be the VarScopedDeclarations of code. + // 11. Let lexicalNames be the LexicallyDeclaredNames of code. + let varNames; + let varDeclarations; + let lexicalNames; + + switch (getFunctionBodyType(code)) { + case 'FunctionBody': + varNames = VarDeclaredNames_FunctionBody(code.body.body); + varDeclarations = VarScopedDeclarations_FunctionBody(code.body.body); + lexicalNames = LexicallyDeclaredNames_FunctionBody(code.body.body); + break; + case 'ConciseBody_ExpressionBody': + case 'ConciseBody_FunctionBody': + case 'AsyncConciseBody_AsyncFunctionBody': + case 'AsyncConciseBody_ExpressionBody': + case 'AsyncGeneratorBody': + varNames = VarDeclaredNames_ConciseBody(code.body); + varDeclarations = VarScopedDeclarations_ConciseBody(code.body); + lexicalNames = LexicallyDeclaredNames_ConciseBody(code.body); + break; + case 'GeneratorBody': + varNames = VarDeclaredNames_GeneratorBody(code.body.body); + varDeclarations = VarScopedDeclarations_GeneratorBody(code.body.body); + lexicalNames = LexicallyDeclaredNames_GeneratorBody(code.body.body); + break; + case 'AsyncFunctionBody': + varNames = VarDeclaredNames_AsyncFunctionBody(code.body.body); + varDeclarations = VarScopedDeclarations_AsyncFunctionBody(code.body.body); + lexicalNames = LexicallyDeclaredNames_AsyncFunctionBody(code.body.body); + break; + default: + throw new OutOfRange('FunctionDeclarationInstantiation', code); + } + + // 12. Let functionNames be a new empty List. + const functionNames = []; + // 13. Let functionNames be a new empty List. + const functionsToInitialize = []; + + // 14. For each d in varDeclarations, in reverse list order, do + for (const d of [...varDeclarations].reverse()) { + // a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then + if (!isVariableDeclaration(d) && !isForBinding(d) && !isBindingIdentifier(d)) { + // i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration. + Assert(isFunctionDeclaration(d) || isGeneratorDeclaration(d) + || isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d)); + // ii. Let fn be the sole element of the BoundNames of d. + const fn = BoundNames_FunctionDeclaration(d)[0]; + // iii. If fn is not an element of functionNames, then + if (!functionNames.includes(fn)) { + // 1. Insert fn as the first element of functionNames. + functionNames.unshift(fn); + // 2. NOTE: If there are multiple function declarations for the same name, the last declaration is used. + // 3. Insert d as the first element of functionsToInitialize. + functionsToInitialize.unshift(d); + } + } + } + + // 15. Let argumentsObjectNeeded be true. + let argumentsObjectNeeded = true; + // If func.[[ThisMode]] is lexical, then + if (func.ThisMode === 'lexical') { + // a. NOTE: Arrow functions never have an arguments objects. + // b. Set argumentsObjectNeeded to false. + argumentsObjectNeeded = false; + } else if (parameterNames.includes('arguments')) { + // a. Set argumentsObjectNeeded to false. + argumentsObjectNeeded = false; + } else if (hasParameterExpressions === false) { + // a. If "arguments" is an element of functionNames or if "arguments" is an element of lexicalNames, then + if (functionNames.includes('arguments') || lexicalNames.includes('arguments')) { + // i. Set argumentsObjectNeeded to false. + argumentsObjectNeeded = false; + } + } + + let env; + let envRec; + // 19. If strict is true or if hasParameterExpressions is false, then + if (strict || hasParameterExpressions === false) { + // a. NOTE: Only a single lexical environment is needed for the parameters and top-level vars. + // b. Let env be the LexicalEnvironment of calleeContext. + env = calleeContext.LexicalEnvironment; + // c. Let envRec be env's EnvironmentRecord. + envRec = env.EnvironmentRecord; + } else { + // a. NOTE: A separate Environment Record is needed to ensure that bindings created by direct eval + // calls in the formal parameter list are outside the environment where parameters are declared. + // b. Let calleeEnv be the LexicalEnvironment of calleeContext. + const calleeEnv = calleeContext.LexicalEnvironment; + // c. Let env be NewDeclarativeEnvironment(calleeEnv). + env = NewDeclarativeEnvironment(calleeEnv); + // d. Let envRec be env's EnvironmentRecord. + envRec = env.EnvironmentRecord; + // e. Assert: The VariableEnvironment of calleeContext is calleeEnv. + Assert(calleeContext.VariableEnvironment === calleeEnv); + // f. Set the LexicalEnvironment of calleeContext to env. + calleeContext.LexicalEnvironment = env; + } + + // 21. For each String paramName in parameterNames, do + for (const paramName of parameterNames) { + // a. Let alreadyDeclared be envRec.HasBinding(paramName). + const alreadyDeclared = envRec.HasBinding(new Value(paramName)); + // b. NOTE: Early errors ensure that duplicate parameter names can only occur in + // non-strict functions that do not have parameter default values or rest parameters. + // c. If alreadyDeclared is false, then + if (alreadyDeclared === Value.false) { + // i. Perform ! envRec.CreateMutableBinding(paramName, false). + X(envRec.CreateMutableBinding(new Value(paramName), false)); + // ii. If hasDuplicates is true, then + if (hasDuplicates === true) { + // 1. Perform ! envRec.InitializeBinding(paramName, undefined). + X(envRec.InitializeBinding(new Value(paramName), Value.undefined)); + } + } + } + + // 22. If argumentsObjectNeeded is true, then + let parameterBindings; + if (argumentsObjectNeeded === true) { + let ao; + // a. If strict is true or if simpleParameterList is false, then + if (strict || simpleParameterList === false) { + // i. Let ao be CreateUnmappedArgumentsObject(argumentsList). + ao = CreateUnmappedArgumentsObject(argumentsList); + } else { + // i. NOTE: mapped argument object is only provided for non-strict functions + // that don't have a rest parameter, any parameter default value initializers, + // or any destructured parameters. + // ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, envRec). + ao = CreateMappedArgumentsObject(func, formals, argumentsList, envRec); + } + // c. If strict is true, then + if (strict) { + // i. Perform ! envRec.CreateImmutableBinding("arguments", false). + X(envRec.CreateImmutableBinding(new Value('arguments'), Value.false)); + } else { + // i. Perform ! envRec.CreateMutableBinding("arguments", false). + X(envRec.CreateMutableBinding(new Value('arguments'), Value.false)); + } + // e. Call envRec.InitializeBinding("arguments", ao). + envRec.InitializeBinding(new Value('arguments'), ao); + // f. Let parameterBindings be a new List of parameterNames with "arguments" appended. + parameterBindings = [...parameterNames, 'arguments']; + } else { + // a. Let parameterBindings be parameterNames. + parameterBindings = parameterNames; + } + + // 24. Let iteratorRecord be CreateListIteratorRecord(argumentsList). + const iteratorRecord = CreateListIteratorRecord(argumentsList); + // 25. If hasDuplicates is true, then + if (hasDuplicates) { + // a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments. + Q(yield* IteratorBindingInitialization_FormalParameters(formals, iteratorRecord, Value.undefined)); + } else { + // a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments. + Q(yield* IteratorBindingInitialization_FormalParameters(formals, iteratorRecord, env)); + } + + let varEnv; + let varEnvRec; + // 27. If hasParameterExpressions is false, then + if (hasParameterExpressions === false) { + // a. NOTE: Only a single lexical environment is needed for the parameters and top-level vars. + // b. Let instantiatedVarNames be a copy of the List parameterBindings. + const instantiatedVarNames = [...parameterBindings]; + // c. Let instantiatedVarNames be a copy of the List parameterBindings. + for (const n of varNames) { + // i. If n is not an element of instantiatedVarNames, then + if (!instantiatedVarNames.includes(n)) { + // 1. Append n to instantiatedVarNames. + instantiatedVarNames.push(n); + // 2. Perform ! envRec.CreateMutableBinding(n, false). + X(envRec.CreateMutableBinding(new Value(n), Value.false)); + // 3. Call envRec.InitializeBinding(n, undefined). + envRec.InitializeBinding(new Value(n), Value.undefined); + } + } + // d. Let varEnv be env. + varEnv = env; + // e. Let varEnvRec be envRec. + varEnvRec = envRec; + } else { + // a. NOTE: A separate Environment Record is needed to ensure that closures created by expressions + // in the formal parameter list do not have visibility of declarations in the function body. + // b. Let varEnv be NewDeclarativeEnvironment(env). + varEnv = NewDeclarativeEnvironment(env); + // c. Let varEnvRec be varEnv's EnvironmentRecord. + varEnvRec = varEnv.EnvironmentRecord; + // d. Set the VariableEnvironment of calleeContext to varEnv. + calleeContext.VariableEnvironment = varEnv; + // e. Let instantiatedVarNames be a new empty List. + const instantiatedVarNames = []; + // For each n in varNames, do + for (const n of varNames) { + // If n is not an element of instantiatedVarNames, then + if (!instantiatedVarNames.includes(n)) { + // 1. Append n to instantiatedVarNames. + instantiatedVarNames.push(n); + // 2. Perform ! varEnvRec.CreateMutableBinding(n, false). + X(varEnvRec.CreateMutableBinding(new Value(n), Value.false)); + let initialValue; + // 3. If n is not an element of parameterBindings or if n is an element of functionNames, let initialValue be undefined. + if (!parameterBindings.includes(n) || functionNames.includes(n)) { + initialValue = Value.undefined; + } else { + // a. Let initialValue be ! envRec.GetBindingValue(n, false). + initialValue = X(envRec.GetBindingValue(new Value(n), Value.false)); + } + // 5. Call varEnvRec.InitializeBinding(n, initialValue). + varEnvRec.InitializeBinding(new Value(n), initialValue); + // 6. NOTE: vars whose names are the same as a formal parameter, initially have the same value as the corresponding initialized parameter. + } + } + } + + // 29. NOTE: Annex B.3.3.1 adds additional steps at this point. + + let lexEnv; + // 30. If strict is false, then + if (strict === false) { + // a. Let lexEnv be NewDeclarativeEnvironment(varEnv). + lexEnv = NewDeclarativeEnvironment(varEnv); + // b. NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations + // so that a direct eval can determine whether any var scoped declarations introduced by the eval code + // conflict with pre-existing top-level lexically scoped declarations. This is not needed for strict functions + // because a strict direct eval always places all declarations into a new Environment Record. + } else { + // a. Else, let lexEnv be varEnv. + lexEnv = varEnv; + } + + // 32. Let lexEnvRec be lexEnv's EnvironmentRecord. + const lexEnvRec = lexEnv.EnvironmentRecord; + // 33. Set the LexicalEnvironment of calleeContext to lexEnv. + calleeContext.LexicalEnvironment = lexEnv; + + // 34. Let lexDeclarations be the LexicallyScopedDeclarations of code. + let lexDeclarations; + switch (getFunctionBodyType(code)) { + case 'FunctionBody': + lexDeclarations = LexicallyScopedDeclarations_FunctionBody(code.body.body); + break; + case 'ConciseBody_ExpressionBody': + case 'ConciseBody_FunctionBody': + case 'AsyncConciseBody_ExpressionBody': + case 'AsyncConciseBody_AsyncFunctionBody': + lexDeclarations = LexicallyScopedDeclarations_ConciseBody(code.body); + break; + case 'GeneratorBody': + lexDeclarations = LexicallyScopedDeclarations_GeneratorBody(code.body.body); + break; + case 'AsyncFunctionBody': + case 'AsyncGeneratorBody': + lexDeclarations = LexicallyScopedDeclarations_AsyncFunctionBody(code.body.body); + break; + default: + throw new OutOfRange('FunctionDeclarationInstantiation', code); + } + // 35. For each element d in lexDeclarations, do + for (const d of lexDeclarations) { + // a. NOTE: A lexically declared name cannot be the same as a function/generator declaration, formal + // parameter, or a var name. Lexically declared names are only instantiated here but not initialized. + // b. For each element dn of the BoundNames of d, do + for (const dn of BoundNames_Declaration(d)) { + // i. If IsConstantDeclaration of d is true, then + if (IsConstantDeclaration(d)) { + // 1. Perform ! lexEnvRec.CreateImmutableBinding(dn, true). + X(lexEnvRec.CreateImmutableBinding(new Value(dn), Value.true)); + } else { + // 1. Perform ! lexEnvRec.CreateMutableBinding(dn, false). + X(lexEnvRec.CreateMutableBinding(new Value(dn), Value.false)); + } + } + } + + // 36. For each Parse Node f in functionsToInitialize, do + for (const f of functionsToInitialize) { + // a. Let fn be the sole element of the BoundNames of f. + const fn = BoundNames_FunctionDeclaration(f)[0]; + // b. Let fo be InstantiateFunctionObject of f with argument lexEnv. + const fo = InstantiateFunctionObject(f, lexEnv); + // c. Perform ! varEnvRec.SetMutableBinding(fn, fo, false). + X(varEnvRec.SetMutableBinding(new Value(fn), fo, Value.false)); + } + + // 37. Return NormalCompletion(empty). + return new NormalCompletion(undefined); +} + +export function getFunctionBodyType(ECMAScriptCode) { + switch (true) { + // FunctionBody : FunctionStatementList + case isFunctionDeclaration(ECMAScriptCode) + || isFunctionExpression(ECMAScriptCode): // includes MethodDefinitions + return 'FunctionBody'; + + // ConciseBody : `{` FunctionBody `}` + case isArrowFunction(ECMAScriptCode) && !ECMAScriptCode.expression: + return 'ConciseBody_FunctionBody'; + + // ConciseBody : ExpressionBody + case isArrowFunction(ECMAScriptCode) && ECMAScriptCode.expression: + return 'ConciseBody_ExpressionBody'; + + // AsyncConciseBody : `{` AsyncFunctionBody `}` + case isAsyncArrowFunction(ECMAScriptCode) && !ECMAScriptCode.expression: + return 'AsyncConciseBody_AsyncFunctionBody'; + + // AsyncConciseBody : ExpressionBody + case isAsyncArrowFunction(ECMAScriptCode) && ECMAScriptCode.expression: + return 'AsyncConciseBody_ExpressionBody'; + + // GeneratorBody : FunctionBody + case isGeneratorDeclaration(ECMAScriptCode) + || isGeneratorExpression(ECMAScriptCode): + return 'GeneratorBody'; + + // AsyncFunctionBody : FunctionBody + case isAsyncFunctionDeclaration(ECMAScriptCode) + || isAsyncFunctionExpression(ECMAScriptCode): + return 'AsyncFunctionBody'; + + case isAsyncGeneratorDeclaration(ECMAScriptCode) + || isAsyncGeneratorExpression(ECMAScriptCode): + return 'AsyncGeneratorBody'; + + default: + throw new OutOfRange('getFunctionBodyType', ECMAScriptCode); + } +} + +// 14.2.15 #sec-arrow-function-definitions-runtime-semantics-evaluatebody +// ConciseBody : ExpressionBody +export function* EvaluateBody_ConciseBody_ExpressionBody(ExpressionBody, functionObject, argumentsList) { + Q(yield* FunctionDeclarationInstantiation(functionObject, argumentsList)); + return yield* Evaluate_ExpressionBody(ExpressionBody); +} + +// 14.1.18 #sec-function-definitions-runtime-semantics-evaluatebody +// FunctionBody : FunctionStatementList +export function* EvaluateBody_FunctionBody(FunctionStatementList, functionObject, argumentsList) { + Q(yield* FunctionDeclarationInstantiation(functionObject, argumentsList)); + return yield* Evaluate_FunctionStatementList(FunctionStatementList); +} + +// 14.4.10 #sec-generator-function-definitions-runtime-semantics-evaluatebody +// GeneratorBody : FunctionBody +export function* EvaluateBody_GeneratorBody(GeneratorBody, functionObject, argumentsList) { + Q(yield* FunctionDeclarationInstantiation(functionObject, argumentsList)); + const G = Q(OrdinaryCreateFromConstructor(functionObject, '%Generator.prototype%', ['GeneratorState', 'GeneratorContext'])); + GeneratorStart(G, GeneratorBody); + return new ReturnCompletion(G); +} + +// 14.7.11 #sec-async-function-definitions-EvaluateBody +// AsyncFunctionBody : FunctionBody +export function* EvaluateBody_AsyncFunctionBody(FunctionBody, functionObject, argumentsList) { + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const declResult = yield* FunctionDeclarationInstantiation(functionObject, argumentsList); + if (!(declResult instanceof AbruptCompletion)) { + X(AsyncFunctionStart(promiseCapability, FunctionBody)); + } else { + X(Call(promiseCapability.Reject, Value.undefined, [declResult.Value])); + } + return new Completion('return', promiseCapability.Promise, undefined); +} + +// 14.8.14 #sec-async-arrow-function-definitions-EvaluateBody +// AsyncConciseBody : ExpressionBody +export function* EvaluateBody_AsyncConciseBody_ExpressionBody(ExpressionBody, functionObject, argumentsList) { + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const declResult = yield* FunctionDeclarationInstantiation(functionObject, argumentsList); + if (!(declResult instanceof AbruptCompletion)) { + X(AsyncFunctionStart(promiseCapability, ExpressionBody)); + } else { + X(Call(promiseCapability.Reject, Value.undefined, [declResult.Value])); + } + return new Completion('return', promiseCapability.Promise, undefined); +} + +export function* EvaluateBody_AsyncGeneratorBody(FunctionBody, functionObject, argumentsList) { + Q(yield* FunctionDeclarationInstantiation(functionObject, argumentsList)); + const generator = Q(OrdinaryCreateFromConstructor(functionObject, '%AsyncGenerator.prototype%', [ + 'AsyncGeneratorState', + 'AsyncGeneratorContext', + 'AsyncGeneratorQueue', + ])); + X(AsyncGeneratorStart(generator, FunctionBody)); + return new Completion('return', generator, undefined); +} diff --git a/src/engine262/src/runtime-semantics/EvaluatePropertyAccess.mjs b/src/engine262/src/runtime-semantics/EvaluatePropertyAccess.mjs new file mode 100644 index 0000000..79108e8 --- /dev/null +++ b/src/engine262/src/runtime-semantics/EvaluatePropertyAccess.mjs @@ -0,0 +1,43 @@ +import { + RequireObjectCoercible, GetValue, ToPropertyKey, Assert, +} from '../abstract-ops/all.mjs'; +import { Value, Reference } from '../value.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { isIdentifier } from '../ast.mjs'; + +// #sec-evaluate-expression-key-property-access +export function* EvaluatePropertyAccessWithExpressionKey(baseValue, expression, strict) { + // 1. Let propertyNameReference be the result of evaluating expression. + const propertyNameReference = yield* Evaluate(expression); + // 2. Let propertyNameValue be ? GetValue(propertyNameReference). + const propertyNameValue = Q(GetValue(propertyNameReference)); + // 3. Let bv be ? RequireObjectCoercible(baseValue). + const bv = Q(RequireObjectCoercible(baseValue)); + // 4. Let propertyKey be ? ToPropertyKey(propertyNameValue). + const propertyKey = Q(ToPropertyKey(propertyNameValue)); + // 5. Return a value of type Reference whose base value component is bv, whose + // referenced name component is propertyKey, and whose strict reference flag is strict. + return new Reference({ + BaseValue: bv, + ReferencedName: propertyKey, + StrictReference: strict ? Value.true : Value.false, + }); +} + +// #sec-evaluate-identifier-key-property-access +export function EvaluatePropertyAccessWithIdentifierKey(baseValue, identifierName, strict) { + // 1. Assert: identifierName is an IdentifierName. + Assert(isIdentifier(identifierName)); + // 2. Let bv be ? RequireObjectCoercible(baseValue). + const bv = Q(RequireObjectCoercible(baseValue)); + // 3. Let propertyNameString be StringValue of IdentifierName + const propertyNameString = new Value(identifierName.name); + // 4. Return a value of type Reference whose base value component is bv, whose + // referenced name component is propertyNameString, and whose strict reference flag is strict. + return new Reference({ + BaseValue: bv, + ReferencedName: propertyNameString, + StrictReference: strict ? Value.true : Value.false, + }); +} diff --git a/src/engine262/src/runtime-semantics/ExponentiationExpression.mjs b/src/engine262/src/runtime-semantics/ExponentiationExpression.mjs new file mode 100644 index 0000000..a6e211a --- /dev/null +++ b/src/engine262/src/runtime-semantics/ExponentiationExpression.mjs @@ -0,0 +1,28 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, TypeNumeric } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { GetValue, ToNumeric } from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; + + +export function EvaluateBinopValues_ExponentiationExpression(lval, rval) { + const base = Q(ToNumeric(lval)); + const exponent = Q(ToNumeric(rval)); + if (Type(base) !== Type(exponent)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + return Q(TypeNumeric(base).exponentiate(base, exponent)); +} + +// 12.6.3 #sec-exp-operator-runtime-semantics-evaluation +// ExponentiationExpression : UpdateExpression ** ExponentiationExpression +export function* Evaluate_ExponentiationExpression({ + left: UpdateExpression, + right: ExponentiationExpression, +}) { + const left = yield* Evaluate(UpdateExpression); + const leftValue = Q(GetValue(left)); + const right = yield* Evaluate(ExponentiationExpression); + const rightValue = Q(GetValue(right)); + return EvaluateBinopValues_ExponentiationExpression(leftValue, rightValue); +} diff --git a/src/engine262/src/runtime-semantics/ExportDeclaration.mjs b/src/engine262/src/runtime-semantics/ExportDeclaration.mjs new file mode 100644 index 0000000..85a5942 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ExportDeclaration.mjs @@ -0,0 +1,63 @@ +import { + isExportDeclarationWithStar, + isExportDeclarationWithVariable, + isExportDeclarationWithDeclaration, + isExportDeclarationWithExport, + isExportDeclarationWithExportAndFrom, + isExportDeclarationWithDefaultAndHoistable, + isExportDeclarationWithDefaultAndClass, + isExportDeclarationWithDefaultAndExpression, +} from '../ast.mjs'; +import { BoundNames_ClassDeclaration, IsAnonymousFunctionDefinition } from '../static-semantics/all.mjs'; +import { GetValue } from '../abstract-ops/all.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Value } from '../value.mjs'; +import { NormalCompletion, ReturnIfAbrupt, Q } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + BindingClassDeclarationEvaluation_ClassDeclaration, + InitializeBoundName, + NamedEvaluation_Expression, +} from './all.mjs'; + +export function* Evaluate_ExportDeclaration(ExportDeclaration) { + switch (true) { + case isExportDeclarationWithStar(ExportDeclaration): + case isExportDeclarationWithExportAndFrom(ExportDeclaration): + case isExportDeclarationWithExport(ExportDeclaration): + return new NormalCompletion(undefined); + case isExportDeclarationWithVariable(ExportDeclaration): + case isExportDeclarationWithDeclaration(ExportDeclaration): + case isExportDeclarationWithDefaultAndHoistable(ExportDeclaration): + return yield* Evaluate(ExportDeclaration.declaration); + case isExportDeclarationWithDefaultAndClass(ExportDeclaration): { + const ClassDeclaration = ExportDeclaration.declaration; + + const value = Q(yield* BindingClassDeclarationEvaluation_ClassDeclaration(ClassDeclaration)); + const className = BoundNames_ClassDeclaration(ClassDeclaration)[0]; + if (className === '*default*') { + const env = surroundingAgent.runningExecutionContext.LexicalEnvironment; + Q(InitializeBoundName(new Value('*default*'), value, env)); + } + return new NormalCompletion(undefined); + } + case isExportDeclarationWithDefaultAndExpression(ExportDeclaration): { + const AssignmentExpression = ExportDeclaration.declaration; + + let value; + if (IsAnonymousFunctionDefinition(AssignmentExpression)) { + value = yield* NamedEvaluation_Expression(AssignmentExpression, new Value('default')); + ReturnIfAbrupt(value); // https://github.com/tc39/ecma262/issues/1605 + } else { + const rhs = yield* Evaluate(AssignmentExpression); + value = Q(GetValue(rhs)); + } + const env = surroundingAgent.runningExecutionContext.LexicalEnvironment; + Q(InitializeBoundName(new Value('*default*'), value, env)); + return new NormalCompletion(undefined); + } + default: + throw new OutOfRange('Evaluate_ExportDeclaration', ExportDeclaration); + } +} diff --git a/src/engine262/src/runtime-semantics/ExpressionWithComma.mjs b/src/engine262/src/runtime-semantics/ExpressionWithComma.mjs new file mode 100644 index 0000000..7f0091d --- /dev/null +++ b/src/engine262/src/runtime-semantics/ExpressionWithComma.mjs @@ -0,0 +1,16 @@ +import { Evaluate } from '../evaluator.mjs'; +import { GetValue } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; + +// 12.16.3 #sec-comma-operator-runtime-semantics-evaluation +// Expression : Expression `,` AssignmentExpression +export function* Evaluate_ExpressionWithComma(ExpressionWithComma) { + const expressions = [...ExpressionWithComma.expressions]; + const AssignmentExpression = expressions.pop(); + for (const Expression of expressions) { + const lref = yield* Evaluate(Expression); + Q(GetValue(lref)); + } + const rref = yield* Evaluate(AssignmentExpression); + return Q(GetValue(rref)); +} diff --git a/src/engine262/src/runtime-semantics/ForStatement.mjs b/src/engine262/src/runtime-semantics/ForStatement.mjs new file mode 100644 index 0000000..616ad6a --- /dev/null +++ b/src/engine262/src/runtime-semantics/ForStatement.mjs @@ -0,0 +1,454 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Type, Value } from '../value.mjs'; +import { + Assert, + Call, + GetIterator, + GetValue, + InitializeReferencedBinding, + IteratorClose, + IteratorComplete, + IteratorValue, + PutValue, + ResolveBinding, + ToBoolean, + ToObject, + AsyncIteratorClose, +} from '../abstract-ops/all.mjs'; +import { CreateForInIterator } from '../intrinsics/ForInIteratorPrototype.mjs'; +import { + AbruptCompletion, BreakCompletion, Completion, + EnsureCompletion, + NormalCompletion, + Q, + ReturnIfAbrupt, + UpdateEmpty, + X, + Await, +} from '../completion.mjs'; +import { + isAssignmentPattern, + isDoWhileStatement, + isForBinding, + isForDeclaration, + isForInStatementWithExpression, + isForInStatementWithForDeclaration, + isForInStatementWithVarForBinding, + isForOfStatementWithExpression, + isForOfStatementWithForDeclaration, + isForOfStatementWithVarForBinding, + isForStatementWithExpression, + isForStatementWithLexicalDeclaration, + isForStatementWithVariableStatement, + isWhileStatement, +} from '../ast.mjs'; +import { + BoundNames_ForBinding, + BoundNames_ForDeclaration, + BoundNames_LexicalDeclaration, + IsConstantDeclaration, + IsDestructuring_ForDeclaration, + IsDestructuring_LeftHandSideExpression, +} from '../static-semantics/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { + DeclarativeEnvironmentRecord, + NewDeclarativeEnvironment, +} from '../environment.mjs'; +import { ValueSet, OutOfRange } from '../helpers.mjs'; +import { + BindingInitialization_ForBinding, + BindingInitialization_ForDeclaration, + DestructuringAssignmentEvaluation_AssignmentPattern, +} from './all.mjs'; + +// 13.7.1.2 #sec-loopcontinues +function LoopContinues(completion, labelSet) { + if (completion.Type === 'normal') { + return true; + } + if (completion.Type !== 'continue') { + return false; + } + if (completion.Target === undefined) { + return true; + } + if (labelSet.has(completion.Target)) { + return true; + } + return false; +} + +// 13.7.4.8 #sec-forbodyevaluation +function* ForBodyEvaluation(test, increment, stmt, perIterationBindings, labelSet) { + let V = Value.undefined; + Q(CreatePerIterationEnvironment(perIterationBindings)); + while (true) { + if (test) { + const testRef = yield* Evaluate(test); + const testValue = Q(GetValue(testRef)); + if (ToBoolean(testValue) === Value.false) { + return new NormalCompletion(V); + } + } + const result = EnsureCompletion(yield* Evaluate(stmt)); + if (LoopContinues(result, labelSet) === false) { + return Completion(UpdateEmpty(result, V)); + } + if (result.Value !== undefined) { + V = result.Value; + } + Q(CreatePerIterationEnvironment(perIterationBindings)); + if (increment) { + const incRef = yield* Evaluate(increment); + Q(GetValue(incRef)); + } + } +} + +// 13.7.4.9 #sec-createperiterationenvironment +function CreatePerIterationEnvironment(perIterationBindings) { + if (perIterationBindings.length > 0) { + const lastIterationEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const lastIterationEnvRec = lastIterationEnv.EnvironmentRecord; + const outer = lastIterationEnv.outerEnvironmentReference; + Assert(Type(outer) !== 'Null'); + const thisIterationEnv = NewDeclarativeEnvironment(outer); + const thisIterationEnvRec = thisIterationEnv.EnvironmentRecord; + for (const bn of perIterationBindings) { + X(thisIterationEnvRec.CreateMutableBinding(bn, false)); + const lastValue = Q(lastIterationEnvRec.GetBindingValue(bn, Value.true)); + thisIterationEnvRec.InitializeBinding(bn, lastValue); + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = thisIterationEnv; + } + return Value.undefined; +} + +// 13.7.5.10 #sec-runtime-semantics-bindinginstantiation +function BindingInstantiation_ForDeclaration(ForDeclaration, environment) { + const envRec = environment.EnvironmentRecord; + Assert(envRec instanceof DeclarativeEnvironmentRecord); + const ForBinding = ForDeclaration.declarations[0].id; + for (const name of BoundNames_ForBinding(ForBinding).map(Value)) { + if (IsConstantDeclaration(ForDeclaration)) { + X(envRec.CreateImmutableBinding(name, Value.true)); + } else { + X(envRec.CreateMutableBinding(name, false)); + } + } +} + +// 13.7.5.12 #sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind +function* ForInOfHeadEvaluation(TDZnames, expr, iterationKind) { + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + if (TDZnames.length > 0) { + Assert(new ValueSet(TDZnames).size === TDZnames.length); + const TDZ = NewDeclarativeEnvironment(oldEnv); + const TDZEnvRec = TDZ.EnvironmentRecord; + for (const name of TDZnames) { + X(TDZEnvRec.CreateMutableBinding(name, false)); + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = TDZ; + } + const exprRef = yield* Evaluate(expr); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + const exprValue = Q(GetValue(exprRef)); + if (iterationKind === 'enumerate') { + if (Type(exprValue) === 'Undefined' || Type(exprValue) === 'Null') { + return new BreakCompletion(undefined); + } + const obj = X(ToObject(exprValue)); + return Q(EnumerateObjectProperties(obj)); + } else { + Assert(iterationKind === 'iterate' || iterationKind === 'async-iterate'); + const iteratorHint = iterationKind === 'async-iterate' ? 'async' : 'sync'; + return Q(GetIterator(exprValue, iteratorHint)); + } +} + +// 13.7.5.13 #sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +function* ForInOfBodyEvaluation(lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet, iteratorKind = 'sync', strict) { + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + let V = Value.undefined; + const destructuring = lhs.type === 'VariableDeclaration' + ? IsDestructuring_ForDeclaration(lhs) : IsDestructuring_LeftHandSideExpression(lhs); + let assignmentPattern; + if (destructuring && lhsKind === 'assignment') { + assignmentPattern = lhs; + Assert(isAssignmentPattern(assignmentPattern)); + } + while (true) { + let nextResult = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator)); + if (iteratorKind === 'async') { + nextResult = Q(yield* Await(nextResult)); + } + if (Type(nextResult) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', nextResult); + } + const done = Q(IteratorComplete(nextResult)); + if (done === Value.true) { + return new NormalCompletion(V); + } + + const nextValue = Q(IteratorValue(nextResult)); + let iterationEnv; + let lhsRef; + if (lhsKind === 'assignment' || lhsKind === 'varBinding') { + if (!destructuring) { + lhsRef = yield* Evaluate(lhs); + } + } else { + Assert(lhsKind === 'lexicalBinding'); + Assert(isForDeclaration(lhs)); + iterationEnv = NewDeclarativeEnvironment(oldEnv); + BindingInstantiation_ForDeclaration(lhs, iterationEnv); + surroundingAgent.runningExecutionContext.LexicalEnvironment = iterationEnv; + if (!destructuring) { + const lhsNames = BoundNames_ForDeclaration(lhs); + Assert(lhsNames.length === 1); + const lhsName = new Value(lhsNames[0]); + lhsRef = X(ResolveBinding(lhsName, undefined, strict)); + } + } + let status; + if (!destructuring) { + if (lhsRef instanceof AbruptCompletion) { + status = lhsRef; + } else if (lhsKind === 'lexicalBinding') { + status = InitializeReferencedBinding(lhsRef, nextValue); + } else { + status = PutValue(lhsRef, nextValue); + } + } else { + if (lhsKind === 'assignment') { + status = yield* DestructuringAssignmentEvaluation_AssignmentPattern(assignmentPattern, nextValue); + } else if (lhsKind === 'varBinding') { + Assert(isForBinding(lhs)); + status = yield* BindingInitialization_ForBinding(lhs, nextValue, Value.undefined); + } else { + Assert(lhsKind === 'lexicalBinding'); + Assert(isForDeclaration(lhs)); + status = yield* BindingInitialization_ForDeclaration(lhs, nextValue, iterationEnv); + } + } + if (status instanceof AbruptCompletion) { + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + if (iteratorKind === 'async') { + return Q(yield* AsyncIteratorClose(iteratorRecord, status)); + } + if (iterationKind === 'enumerate') { + return status; + } else { + Assert(iterationKind === 'iterate'); + return Q(IteratorClose(iteratorRecord, status)); + } + } + const result = EnsureCompletion(yield* Evaluate(stmt)); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + if (LoopContinues(result, labelSet) === false) { + if (iterationKind === 'enumerate') { + return Completion(UpdateEmpty(result, V)); + } else { + Assert(iterationKind === 'iterate'); + status = UpdateEmpty(result, V); + if (iteratorKind === 'async') { + return Q(yield* AsyncIteratorClose(iteratorRecord, status)); + } + return Q(IteratorClose(iteratorRecord, status)); + } + } + if (result.Value !== undefined) { + V = result.Value; + } + } +} + +// 13.7.2.6 #sec-do-while-statement-runtime-semantics-labelledevaluation +// IterationStatement : `do` Statement `while` `(` Expression `)` `;` +// +// 13.7.3.6 #sec-while-statement-runtime-semantics-labelledevaluation +// IterationStatement : `while` `(` Expression `)` Statement +// +// 13.7.4.7 #sec-for-statement-runtime-semantics-labelledevaluation +// IterationStatement : +// `for` `(` Expression `;` Expression `;` Expression `)` Statement +// `for` `(` `var` VariableDeclarationList `;` Expression `;` Expression `)` Statement +// `for` `(` LexicalDeclarationExpression `;` Expression `)` Statement +// +// 13.7.5.11 #sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation +// IterationStatement : +// `for` `(` LeftHandSideExpression `in` Expression `)` Statement +// `for` `(` `var` ForBinding `in` Expression `)` Statement +// `for` `(` ForDeclaration `in` Expression `)` Statement +// `for` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `(` ForDeclaration `of` AssignmentExpression `)` Statement +// `for` `await` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `await` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `await` `(` ForDeclaration `of` AssignmentExpression `)` Statement +export function* LabelledEvaluation_IterationStatement(IterationStatement, labelSet) { + switch (true) { + case isDoWhileStatement(IterationStatement): { + const Statement = IterationStatement.body; + const Expression = IterationStatement.test; + + let V = Value.undefined; + while (true) { + const stmtResult = EnsureCompletion(yield* Evaluate(Statement)); + if (!LoopContinues(stmtResult, labelSet)) { + return Completion(UpdateEmpty(stmtResult, V)); + } + if (stmtResult.Value !== undefined) { + V = stmtResult.Value; + } + const exprRef = yield* Evaluate(Expression); + const exprValue = Q(GetValue(exprRef)); + if (ToBoolean(exprValue) === Value.false) { + return new NormalCompletion(V); + } + } + } + + case isWhileStatement(IterationStatement): { + const Expression = IterationStatement.test; + const Statement = IterationStatement.body; + + let V = Value.undefined; + while (true) { + const exprRef = yield* Evaluate(Expression); + const exprValue = Q(GetValue(exprRef)); + if (ToBoolean(exprValue) === Value.false) { + return new NormalCompletion(V); + } + const stmtResult = EnsureCompletion(yield* Evaluate(Statement)); + if (!LoopContinues(stmtResult, labelSet)) { + return Completion(UpdateEmpty(stmtResult, V)); + } + if (stmtResult.Value !== undefined) { + V = stmtResult.Value; + } + } + } + + case isForStatementWithExpression(IterationStatement): + if (IterationStatement.init) { + const exprRef = yield* Evaluate(IterationStatement.init); + Q(GetValue(exprRef)); + } + return Q(yield* ForBodyEvaluation(IterationStatement.test, IterationStatement.update, IterationStatement.body, [], labelSet)); + + case isForStatementWithVariableStatement(IterationStatement): { + const varDcl = yield* Evaluate(IterationStatement.init); + ReturnIfAbrupt(varDcl); + return Q(yield* ForBodyEvaluation(IterationStatement.test, IterationStatement.update, IterationStatement.body, [], labelSet)); + } + + case isForStatementWithLexicalDeclaration(IterationStatement): { + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const loopEnv = NewDeclarativeEnvironment(oldEnv); + const loopEnvRec = loopEnv.EnvironmentRecord; + const isConst = IsConstantDeclaration(IterationStatement.init); + const boundNames = BoundNames_LexicalDeclaration(IterationStatement.init).map(Value); + for (const dn of boundNames) { + if (isConst) { + X(loopEnvRec.CreateImmutableBinding(dn, Value.true)); + } else { + X(loopEnvRec.CreateMutableBinding(dn, true)); + } + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = loopEnv; + const forDcl = yield* Evaluate(IterationStatement.init); + if (forDcl instanceof AbruptCompletion) { + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return Completion(forDcl); + } + const perIterationLets = isConst ? [] : boundNames; + const bodyResult = yield* ForBodyEvaluation(IterationStatement.test, IterationStatement.update, IterationStatement.body, perIterationLets, labelSet); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return Completion(bodyResult); + } + + case isForInStatementWithExpression(IterationStatement): { + const { + left: LeftHandSideExpression, + right: Expression, + body: Statement, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation([], Expression, 'enumerate')); + return Q(yield* ForInOfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, 'enumerate', 'assignment', labelSet, 'sync', strict)); + } + + case isForInStatementWithVarForBinding(IterationStatement): { + const { + left: { + declarations: [{ id: ForBinding }], + }, + right: Expression, + body: Statement, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation([], Expression, 'enumerate')); + return Q(yield* ForInOfBodyEvaluation(ForBinding, Statement, keyResult, 'enumerate', 'varBinding', labelSet, 'sync', strict)); + } + + case isForInStatementWithForDeclaration(IterationStatement): { + const { + left: ForDeclaration, + right: Expression, + body: Statement, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation(BoundNames_ForDeclaration(ForDeclaration).map(Value), Expression, 'enumerate')); + return Q(yield* ForInOfBodyEvaluation(ForDeclaration, Statement, keyResult, 'enumerate', 'lexicalBinding', labelSet, 'sync', strict)); + } + + case isForOfStatementWithExpression(IterationStatement): { + const { + left: LeftHandSideExpression, + right: AssignmentExpression, + body: Statement, + await: isAwait, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation([], AssignmentExpression, isAwait ? 'async-iterate' : 'iterate')); + return Q(yield* ForInOfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, 'iterate', 'assignment', labelSet, isAwait ? 'async' : 'sync', strict)); + } + + case isForOfStatementWithVarForBinding(IterationStatement): { + const { + left: { + declarations: [{ id: ForBinding }], + }, + right: AssignmentExpression, + body: Statement, + await: isAwait, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation([], AssignmentExpression, isAwait ? 'async-iterate' : 'iterate')); + return Q(yield* ForInOfBodyEvaluation(ForBinding, Statement, keyResult, 'iterate', 'varBinding', labelSet, isAwait ? 'async' : 'sync', strict)); + } + + case isForOfStatementWithForDeclaration(IterationStatement): { + const { + left: ForDeclaration, + right: AssignmentExpression, + body: Statement, + await: isAwait, + strict, + } = IterationStatement; + const keyResult = Q(yield* ForInOfHeadEvaluation(BoundNames_ForDeclaration(ForDeclaration).map(Value), AssignmentExpression, isAwait ? 'async-iterate' : 'iterate')); + return Q(yield* ForInOfBodyEvaluation(ForDeclaration, Statement, keyResult, 'iterate', 'lexicalBinding', labelSet, isAwait ? 'async' : 'sync', strict)); + } + + default: + throw new OutOfRange('LabelledEvaluation_IterationStatement', IterationStatement); + } +} + +// #sec-enumerate-object-properties +function EnumerateObjectProperties(O) { + const it = CreateForInIterator(O); + return X(GetIterator(it)); +} diff --git a/src/engine262/src/runtime-semantics/FunctionDeclaration.mjs b/src/engine262/src/runtime-semantics/FunctionDeclaration.mjs new file mode 100644 index 0000000..972ea37 --- /dev/null +++ b/src/engine262/src/runtime-semantics/FunctionDeclaration.mjs @@ -0,0 +1,11 @@ +import { + NormalCompletion, +} from '../completion.mjs'; + +// 14.1.22 #sec-function-definitions-runtime-semantics-evaluation +// FunctionDeclaration : +// function BindingIdentifier ( FormalParameters ) { FunctionBody } +// function ( FormalParameters ) { FunctionBody } +export function Evaluate_FunctionDeclaration() { + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/FunctionExpression.mjs b/src/engine262/src/runtime-semantics/FunctionExpression.mjs new file mode 100644 index 0000000..d677889 --- /dev/null +++ b/src/engine262/src/runtime-semantics/FunctionExpression.mjs @@ -0,0 +1,41 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + isFunctionExpressionWithBindingIdentifier, +} from '../ast.mjs'; +import { + OrdinaryFunctionCreate, + MakeConstructor, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { Value } from '../value.mjs'; +import { X } from '../completion.mjs'; +import { + NamedEvaluation_FunctionExpression, +} from './all.mjs'; + +function Evaluate_FunctionExpression_BindingIdentifier(FunctionExpression) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = FunctionExpression; + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const funcEnv = NewDeclarativeEnvironment(scope); + const envRec = funcEnv.EnvironmentRecord; + const name = new Value(BindingIdentifier.name); + envRec.CreateImmutableBinding(name, Value.false); + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), FormalParameters, FunctionExpression, 'non-lexical-this', funcEnv)); + SetFunctionName(closure, name); + MakeConstructor(closure); + closure.SourceText = sourceTextMatchedBy(FunctionExpression); + envRec.InitializeBinding(name, closure); + return closure; +} + +export function Evaluate_FunctionExpression(FunctionExpression) { + if (isFunctionExpressionWithBindingIdentifier(FunctionExpression)) { + return Evaluate_FunctionExpression_BindingIdentifier(FunctionExpression); + } + return NamedEvaluation_FunctionExpression(FunctionExpression, new Value('')); +} diff --git a/src/engine262/src/runtime-semantics/FunctionStatementList.mjs b/src/engine262/src/runtime-semantics/FunctionStatementList.mjs new file mode 100644 index 0000000..b760ce0 --- /dev/null +++ b/src/engine262/src/runtime-semantics/FunctionStatementList.mjs @@ -0,0 +1,12 @@ +import { Evaluate_StatementList } from '../evaluator.mjs'; + +// 14.1.22 #sec-function-definitions-runtime-semantics-evaluation +// FunctionStatementList : [empty] +// +// (implicit) +// FunctionStatementList : StatementList +export const Evaluate_FunctionStatementList = Evaluate_StatementList; + +// (implicit) +// FunctionBody : FunctionStatementList +export const Evaluate_FunctionBody = Evaluate_FunctionStatementList; diff --git a/src/engine262/src/runtime-semantics/GeneratorExpression.mjs b/src/engine262/src/runtime-semantics/GeneratorExpression.mjs new file mode 100644 index 0000000..f855069 --- /dev/null +++ b/src/engine262/src/runtime-semantics/GeneratorExpression.mjs @@ -0,0 +1,47 @@ +import { + DefinePropertyOrThrow, + OrdinaryFunctionCreate, + OrdinaryObjectCreate, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { NamedEvaluation_GeneratorExpression } from './all.mjs'; + +// 14.4.14 #sec-generator-function-definitions-runtime-semantics-evaluation +// GeneratorExpression : +// `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}` +// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}` +export function Evaluate_GeneratorExpression(GeneratorExpression) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = GeneratorExpression; + if (!BindingIdentifier) { + return NamedEvaluation_GeneratorExpression(GeneratorExpression, new Value('')); + } + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const funcEnv = NewDeclarativeEnvironment(scope); + const envRec = funcEnv.EnvironmentRecord; + const name = new Value(BindingIdentifier.name); + envRec.CreateImmutableBinding(name, Value.false); + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Generator%'), FormalParameters, GeneratorExpression, 'non-lexical-this', funcEnv)); + X(SetFunctionName(closure, name)); + const prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Generator.prototype%')); + X(DefinePropertyOrThrow( + closure, + new Value('prototype'), + Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }), + )); + closure.SourceText = sourceTextMatchedBy(GeneratorExpression); + envRec.InitializeBinding(name, closure); + return closure; +} diff --git a/src/engine262/src/runtime-semantics/GetSubstitution.mjs b/src/engine262/src/runtime-semantics/GetSubstitution.mjs new file mode 100644 index 0000000..54484bd --- /dev/null +++ b/src/engine262/src/runtime-semantics/GetSubstitution.mjs @@ -0,0 +1,105 @@ +import { + Assert, + Get, + ToString, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { Q } from '../completion.mjs'; + +// 21.1.3.17.1 #sec-getsubstitution +export function GetSubstitution(matched, str, position, captures, namedCaptures, replacement) { + Assert(Type(matched) === 'String'); + const matchLength = matched.stringValue().length; + Assert(Type(str) === 'String'); + const stringLength = str.stringValue().length; + Assert(Type(position) === 'Number' && Number.isInteger(position.numberValue()) && position.numberValue() >= 0); + Assert(position.numberValue() <= stringLength); + Assert(Array.isArray(captures) && captures.every((value) => Type(value) === 'String' || Type(value) === 'Undefined')); + Assert(Type(replacement) === 'String'); + const tailPos = position.numberValue() + matchLength; + const m = captures.length; + const replacementStr = replacement.stringValue(); + let result = ''; + let i = 0; + while (i < replacementStr.length) { + const currentChar = replacementStr[i]; + if (currentChar === '$' && i < replacementStr.length - 1) { + const nextChar = replacementStr[i + 1]; + if (nextChar === '$') { + result += '$'; + i += 2; + } else if (nextChar === '&') { + result += matched.stringValue(); + i += 2; + } else if (nextChar === '`') { + if (position.numberValue() === 0) { + // Replacement is the empty String + } else { + result += str.stringValue().substring(0, position.numberValue()); + } + i += 2; + } else if (nextChar === '\'') { + if (tailPos >= stringLength) { + // Replacement is the empty String + } else { + result += str.stringValue().substring(tailPos); + } + i += 2; + } else if ('123456789'.includes(nextChar) && (i === replacementStr.length - 2 || !'0123456789'.includes(replacementStr[i + 2]))) { + const n = Number(nextChar); + if (n <= m) { + const capture = captures[n - 1]; + if (capture !== Value.undefined) { + result += capture.stringValue(); + } + } else { + result += `$${nextChar}`; + } + i += 2; + } else if (i < replacementStr.length - 2 && '0123456789'.includes(nextChar) && '0123456789'.includes(replacementStr[i + 2])) { + const nextNextChar = replacementStr[i + 2]; + const n = Number(nextChar + nextNextChar); + if (n !== 0 && n <= m) { + const capture = captures[n - 1]; + if (capture !== Value.undefined) { + result += capture.stringValue(); + } + } else { + result += `$${nextChar}${nextNextChar}`; + } + i += 3; + } else if (nextChar === '<') { + if (namedCaptures === Value.undefined) { + result += '$<'; + i += 2; + } else { + Assert(Type(namedCaptures) === 'Object'); + const nextSign = replacementStr.indexOf('>', i); + if (nextSign === -1) { + result += '$<'; + i += 2; + } else { + const groupName = new Value(replacementStr.substring(i + 2, nextSign)); + const capture = Q(Get(namedCaptures, groupName)); + if (capture === Value.undefined) { + // Replace the text with the empty string + } else { + result += Q(ToString(capture)).stringValue(); + } + i = nextSign + 1; + } + } + } else { + result += '$'; + i += 1; + } + } else { + result += currentChar; + i += 1; + } + } + return new Value(result); +} diff --git a/src/engine262/src/runtime-semantics/GlobalDeclarationInstantiation.mjs b/src/engine262/src/runtime-semantics/GlobalDeclarationInstantiation.mjs new file mode 100644 index 0000000..2d571c9 --- /dev/null +++ b/src/engine262/src/runtime-semantics/GlobalDeclarationInstantiation.mjs @@ -0,0 +1,138 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + EnvironmentRecord, +} from '../environment.mjs'; +import { Assert } from '../abstract-ops/all.mjs'; +import { + BoundNames_BindingIdentifier, + BoundNames_Declaration, + BoundNames_ForBinding, + BoundNames_FunctionDeclaration, + BoundNames_VariableDeclaration, + IsConstantDeclaration, + LexicallyDeclaredNames_ScriptBody, + LexicallyScopedDeclarations_ScriptBody, + VarDeclaredNames_ScriptBody, + VarScopedDeclarations_ScriptBody, +} from '../static-semantics/all.mjs'; +import { + isAsyncFunctionDeclaration, + isAsyncGeneratorDeclaration, + isBindingIdentifier, + isForBinding, + isFunctionDeclaration, + isGeneratorDeclaration, + isVariableDeclaration, +} from '../ast.mjs'; +import { Value } from '../value.mjs'; +import { + NormalCompletion, + Q, +} from '../completion.mjs'; +import { + InstantiateFunctionObject, +} from './all.mjs'; + + +// 15.1.11 #sec-globaldeclarationinstantiation +export function GlobalDeclarationInstantiation(script, env) { + const envRec = env.EnvironmentRecord; + Assert(envRec instanceof EnvironmentRecord); + + const lexNames = LexicallyDeclaredNames_ScriptBody(script).map(Value); + const varNames = VarDeclaredNames_ScriptBody(script).map(Value); + + for (const name of lexNames) { + if (envRec.HasVarDeclaration(name) === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + } + if (envRec.HasLexicalDeclaration(name) === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + } + const hasRestrictedGlobal = Q(envRec.HasRestrictedGlobalProperty(name)); + if (hasRestrictedGlobal === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + } + } + + for (const name of varNames) { + if (envRec.HasLexicalDeclaration(name) === Value.true) { + return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name); + } + } + + const varDeclarations = VarScopedDeclarations_ScriptBody(script); + + const functionsToInitialize = []; + const declaredFunctionNames = []; + + for (const d of [...varDeclarations].reverse()) { + if (!isVariableDeclaration(d) && !isForBinding(d) && !isBindingIdentifier(d)) { + Assert(isFunctionDeclaration(d) || isGeneratorDeclaration(d) + || isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d)); + const fn = BoundNames_FunctionDeclaration(d)[0]; + if (!declaredFunctionNames.includes(fn)) { + const fnDefinable = Q(envRec.CanDeclareGlobalFunction(new Value(fn))); + if (fnDefinable === Value.false) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', fn); + } + declaredFunctionNames.push(fn); + functionsToInitialize.unshift(d); + } + } + } + + const declaredVarNames = []; + + for (const d of varDeclarations) { + let boundNames; + if (isVariableDeclaration(d)) { + boundNames = BoundNames_VariableDeclaration(d); + } else if (isForBinding(d)) { + boundNames = BoundNames_ForBinding(d); + } else if (isBindingIdentifier(d)) { + boundNames = BoundNames_BindingIdentifier(d); + } + if (boundNames !== undefined) { + for (const vn of boundNames.map(Value)) { + if (!declaredFunctionNames.includes(vn)) { + const vnDefinable = Q(envRec.CanDeclareGlobalVar(vn)); + if (vnDefinable === Value.false) { + return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', vn); + } + if (!declaredVarNames.includes(vn)) { + declaredVarNames.push(vn); + } + } + } + } + } + + // NOTE: Annex B.3.3.2 adds additional steps at this point. + // TODO(devsnek): Annex B.3.3.2 + + const lexDeclarations = LexicallyScopedDeclarations_ScriptBody(script); + for (const d of lexDeclarations) { + for (const dn of BoundNames_Declaration(d).map(Value)) { + if (IsConstantDeclaration(d)) { + Q(envRec.CreateImmutableBinding(dn, Value.true)); + } else { + Q(envRec.CreateMutableBinding(dn, Value.false)); + } + } + } + + for (const f of functionsToInitialize) { + const fn = new Value(BoundNames_FunctionDeclaration(f)[0]); + const fo = InstantiateFunctionObject(f, env); + Q(envRec.CreateGlobalFunctionBinding(fn, fo, Value.false)); + } + + for (const vn of declaredVarNames) { + Q(envRec.CreateGlobalVarBinding(vn, Value.false)); + } + + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/HoistableDeclaration.mjs b/src/engine262/src/runtime-semantics/HoistableDeclaration.mjs new file mode 100644 index 0000000..3fd7058 --- /dev/null +++ b/src/engine262/src/runtime-semantics/HoistableDeclaration.mjs @@ -0,0 +1,30 @@ +import { + isAsyncFunctionDeclaration, + isAsyncGeneratorDeclaration, + isFunctionDeclaration, + isGeneratorDeclaration, +} from '../ast.mjs'; +import { NormalCompletion } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { Evaluate_FunctionDeclaration } from './all.mjs'; + +// 13.1.8 #sec-statement-semantics-runtime-semantics-evaluation +// HoistableDeclaration : +// GeneratorDeclaration +// AsyncFunctionDeclaration +// AsyncGeneratorDeclaration +// FunctionDeclaration +export function Evaluate_HoistableDeclaration(HoistableDeclaration) { + switch (true) { + case isGeneratorDeclaration(HoistableDeclaration): + case isAsyncFunctionDeclaration(HoistableDeclaration): + case isAsyncGeneratorDeclaration(HoistableDeclaration): + return new NormalCompletion(undefined); + + case isFunctionDeclaration(HoistableDeclaration): + return Evaluate_FunctionDeclaration(HoistableDeclaration); + + default: + throw new OutOfRange('Evaluate_HoistableDeclaration', HoistableDeclaration); + } +} diff --git a/src/engine262/src/runtime-semantics/Identifier.mjs b/src/engine262/src/runtime-semantics/Identifier.mjs new file mode 100644 index 0000000..059d58a --- /dev/null +++ b/src/engine262/src/runtime-semantics/Identifier.mjs @@ -0,0 +1,12 @@ +import { Value } from '../value.mjs'; +import { ResolveBinding } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; + +// 12.1.6 #sec-identifiers-runtime-semantics-evaluation +// IdentifierReference : +// Identifier +// yield +// await +export function Evaluate_Identifier(Identifier) { + return Q(ResolveBinding(new Value(Identifier.name), undefined, Identifier.strict)); +} diff --git a/src/engine262/src/runtime-semantics/IfStatement.mjs b/src/engine262/src/runtime-semantics/IfStatement.mjs new file mode 100644 index 0000000..33150de --- /dev/null +++ b/src/engine262/src/runtime-semantics/IfStatement.mjs @@ -0,0 +1,43 @@ +import { Evaluate } from '../evaluator.mjs'; +import { + GetValue, + ToBoolean, +} from '../abstract-ops/all.mjs'; +import { + Completion, + EnsureCompletion, + NormalCompletion, + Q, + UpdateEmpty, +} from '../completion.mjs'; +import { Value } from '../value.mjs'; + +// 13.6.7 #sec-if-statement-runtime-semantics-evaluation +// IfStatement : +// `if` `(` Expression `)` Statement `else` Statement +// `if` `(` Expression `)` Statement +export function* Evaluate_IfStatement({ + test: Expression, + consequent: Statement, + alternate: AlternateStatement, +}) { + const exprRef = yield* Evaluate(Expression); + const exprValue = ToBoolean(Q(GetValue(exprRef))); + + if (AlternateStatement !== null) { + let stmtCompletion; + if (exprValue === Value.true) { + stmtCompletion = EnsureCompletion(yield* Evaluate(Statement)); + } else { + stmtCompletion = EnsureCompletion(yield* Evaluate(AlternateStatement)); + } + return Completion(UpdateEmpty(stmtCompletion, Value.undefined)); + } else { + if (exprValue === Value.false) { + return new NormalCompletion(Value.undefined); + } else { + const stmtCompletion = EnsureCompletion(yield* Evaluate(Statement)); + return Completion(UpdateEmpty(stmtCompletion, Value.undefined)); + } + } +} diff --git a/src/engine262/src/runtime-semantics/ImportCall.mjs b/src/engine262/src/runtime-semantics/ImportCall.mjs new file mode 100644 index 0000000..31b88dd --- /dev/null +++ b/src/engine262/src/runtime-semantics/ImportCall.mjs @@ -0,0 +1,22 @@ +import { surroundingAgent, HostImportModuleDynamically } from '../engine.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { + GetValue, + ToString, + NewPromiseCapability, + GetActiveScriptOrModule, +} from '../abstract-ops/all.mjs'; +import { Q, X, IfAbruptRejectPromise } from '../completion.mjs'; + +// #sec-import-calls +// ImportCall : `import` `(` AssignmentExpression `)` +export function* Evaluate_ImportCall({ source: AssignmentExpression }) { + const referencingScriptOrModule = X(GetActiveScriptOrModule()); + const argRef = yield* Evaluate(AssignmentExpression); + const specifier = Q(GetValue(argRef)); + const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%'))); + const specifierString = ToString(specifier); + IfAbruptRejectPromise(specifierString, promiseCapability); + X(HostImportModuleDynamically(referencingScriptOrModule, specifierString, promiseCapability)); + return promiseCapability.Promise; +} diff --git a/src/engine262/src/runtime-semantics/InstantiateFunctionObject.mjs b/src/engine262/src/runtime-semantics/InstantiateFunctionObject.mjs new file mode 100644 index 0000000..685ed99 --- /dev/null +++ b/src/engine262/src/runtime-semantics/InstantiateFunctionObject.mjs @@ -0,0 +1,108 @@ +import { + DefinePropertyOrThrow, + MakeConstructor, + OrdinaryObjectCreate, + SetFunctionName, + OrdinaryFunctionCreate, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { + isAsyncFunctionDeclaration, + isFunctionDeclaration, + isGeneratorDeclaration, + isAsyncGeneratorDeclaration, +} from '../ast.mjs'; +import { X } from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { Descriptor, Value } from '../value.mjs'; + +// 14.1.20 #sec-function-definitions-runtime-semantics-instantiatefunctionobject +// FunctionDeclaration : +// `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}` +// `function` `(` FormalParameters `)` `{` FunctionBody `}` +export function InstantiateFunctionObject_FunctionDeclaration(FunctionDeclaration, scope) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = FunctionDeclaration; + const name = new Value(BindingIdentifier ? BindingIdentifier.name : 'default'); + const F = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), FormalParameters, FunctionDeclaration, 'non-lexical-this', scope)); + SetFunctionName(F, name); + MakeConstructor(F); + F.SourceText = sourceTextMatchedBy(FunctionDeclaration); + return F; +} + +// 14.4.11 #sec-generator-function-definitions-runtime-semantics-instantiatefunctionobject +// GeneratorDeclaration : +// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}` +// `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}` +export function InstantiateFunctionObject_GeneratorDeclaration(GeneratorDeclaration, scope) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = GeneratorDeclaration; + const name = new Value(BindingIdentifier ? BindingIdentifier.name : 'default'); + const F = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Generator%'), FormalParameters, GeneratorDeclaration, 'non-lexical-this', scope)); + SetFunctionName(F, name); + const prototype = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Generator.prototype%'))); + X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + F.SourceText = sourceTextMatchedBy(GeneratorDeclaration); + return F; +} + +export function InstantiateFunctionObject_AsyncFunctionDeclaration(AsyncFunctionDeclaration, scope) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = AsyncFunctionDeclaration; + const name = new Value(BindingIdentifier ? BindingIdentifier.name : 'default'); + const F = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncFunction.prototype%'), FormalParameters, AsyncFunctionDeclaration, 'non-lexical-this', scope)); + SetFunctionName(F, name); + F.SourceText = sourceTextMatchedBy(AsyncFunctionDeclaration); + return F; +} + +export function InstantiateFunctionObject_AsyncGeneratorDeclaration(AsyncGeneratorDeclaration, scope) { + const { + id: BindingIdentifier, + params: FormalParameters, + } = AsyncGeneratorDeclaration; + const name = new Value(BindingIdentifier ? BindingIdentifier.name : 'default'); + const F = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncGeneratorFunction.prototype%'), FormalParameters, AsyncGeneratorDeclaration, 'non-lexical-this', scope)); + SetFunctionName(F, name); + const prototype = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncGenerator.prototype%'))); + X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + F.SourceText = sourceTextMatchedBy(AsyncGeneratorDeclaration); + return F; +} + +export function InstantiateFunctionObject(AnyFunctionDeclaration, scope) { + switch (true) { + case isFunctionDeclaration(AnyFunctionDeclaration): + return InstantiateFunctionObject_FunctionDeclaration(AnyFunctionDeclaration, scope); + + case isGeneratorDeclaration(AnyFunctionDeclaration): + return InstantiateFunctionObject_GeneratorDeclaration(AnyFunctionDeclaration, scope); + + case isAsyncFunctionDeclaration(AnyFunctionDeclaration): + return InstantiateFunctionObject_AsyncFunctionDeclaration(AnyFunctionDeclaration, scope); + + case isAsyncGeneratorDeclaration(AnyFunctionDeclaration): + return InstantiateFunctionObject_AsyncGeneratorDeclaration(AnyFunctionDeclaration, scope); + + default: + throw new OutOfRange('InstantiateFunctionObject', AnyFunctionDeclaration); + } +} diff --git a/src/engine262/src/runtime-semantics/IteratorBindingInitialization.mjs b/src/engine262/src/runtime-semantics/IteratorBindingInitialization.mjs new file mode 100644 index 0000000..cc6c694 --- /dev/null +++ b/src/engine262/src/runtime-semantics/IteratorBindingInitialization.mjs @@ -0,0 +1,354 @@ +import { + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isBindingRestElement, + isFormalParameter, + isFunctionRestParameter, + isSingleNameBinding, +} from '../ast.mjs'; +import { + ArrayCreate, + Assert, + CreateDataProperty, + GetValue, + InitializeReferencedBinding, + IteratorStep, + IteratorValue, + PutValue, + ResolveBinding, + ToString, +} from '../abstract-ops/all.mjs'; +import { + AbruptCompletion, + NormalCompletion, + Q, + ReturnIfAbrupt, + X, +} from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + IsAnonymousFunctionDefinition, +} from '../static-semantics/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + BindingInitialization_BindingPattern, + IteratorDestructuringAssignmentEvaluation_Elision, + NamedEvaluation_Expression, +} from './all.mjs'; + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// ArrayBindingPattern : +// `[` `]` +// `[` Elision `]` +// `[` Elision BindingRestElement `]` +// `[` BindingElementList `]` +// `[` BindingElementList `,` `]` +// `[` BindingElementList `,` Elision `]` +// `[` BindingElementList `,` Elision BindingRestElement `]` +export function* IteratorBindingInitialization_ArrayBindingPattern(ArrayBindingPattern, iteratorRecord, environment) { + let Elision; + let BindingElementList = ArrayBindingPattern.elements; + let BindingRestElement; + // Members of the BindingElementList may be null, so add a truthyness check. + if (BindingElementList.length > 0 && BindingElementList[BindingElementList.length - 1] + && isBindingRestElement(BindingElementList[BindingElementList.length - 1])) { + BindingRestElement = BindingElementList[BindingElementList.length - 1]; + BindingElementList = BindingElementList.slice(0, -1); + } + if (BindingElementList.length > 0) { + let begin; + for (begin = BindingElementList.length; begin > 0; begin -= 1) { + if (BindingElementList[begin - 1] !== null) { + break; + } + } + if (begin !== BindingElementList.length) { + Elision = BindingElementList.slice(begin); + BindingElementList = BindingElementList.slice(0, begin); + } + } + + let status = new NormalCompletion(undefined); + if (BindingElementList.length > 0) { + status = Q(yield* IteratorBindingInitialization_BindingElementList(BindingElementList, iteratorRecord, environment)); + } + if (Elision !== undefined) { + status = Q(IteratorDestructuringAssignmentEvaluation_Elision(Elision, iteratorRecord)); + } + if (BindingRestElement !== undefined) { + status = Q(yield* IteratorBindingInitialization_BindingRestElement(BindingRestElement, iteratorRecord, environment)); + } + return status; +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingElement : BindingPattern Initializer +function* IteratorBindingInitialization_BindingElement_BindingPattern(BindingElement, iteratorRecord, environment) { + let BindingPattern; + let Initializer; + switch (true) { + case isBindingPattern(BindingElement): + BindingPattern = BindingElement; + Initializer = undefined; + break; + case isBindingPatternAndInitializer(BindingElement): + BindingPattern = BindingElement.left; + Initializer = BindingElement.right; + break; + default: + throw new OutOfRange( + 'IteratorBindingInitialization_BindingElement_BindingPattern', BindingElement, + ); + } + let v; + if (iteratorRecord.Done === Value.false) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } else { + v = IteratorValue(next); + if (v instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(v); + } + } + if (iteratorRecord.Done === Value.true) { + v = Value.undefined; + } + if (Initializer !== undefined && Type(v) === 'Undefined') { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + return yield* BindingInitialization_BindingPattern(BindingPattern, v, environment); +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// SingleNameBinding : BindingIdentifier Initializer +function* IteratorBindingInitialization_SingleNameBinding(SingleNameBinding, iteratorRecord, environment) { + let BindingIdentifier; + let Initializer; + switch (true) { + case isBindingIdentifier(SingleNameBinding): + BindingIdentifier = SingleNameBinding; + Initializer = undefined; + break; + case isBindingIdentifierAndInitializer(SingleNameBinding): + BindingIdentifier = SingleNameBinding.left; + Initializer = SingleNameBinding.right; + break; + default: + throw new OutOfRange('IteratorBindingInitialization_SingleNameBinding', SingleNameBinding); + } + const bindingId = new Value(BindingIdentifier.name); + const lhs = Q(ResolveBinding(bindingId, environment, BindingIdentifier.strict)); + let v; + if (iteratorRecord.Done === Value.false) { + const next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } else { + v = IteratorValue(next); + if (v instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(v); + } + } + if (iteratorRecord.Done === Value.true) { + v = Value.undefined; + } + if (Initializer !== undefined && v === Value.undefined) { + if (IsAnonymousFunctionDefinition(Initializer)) { + v = yield* NamedEvaluation_Expression(Initializer, bindingId); + } else { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + } + if (Type(environment) === 'Undefined') { + return Q(PutValue(lhs, v)); + } + return InitializeReferencedBinding(lhs, v); +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingElementList : BindingElementList `,` BindingElisionElement +// +// (implicit) +// BindingElementList : BindingElisionElement +function* IteratorBindingInitialization_BindingElementList(BindingElementList, iteratorRecord, environment) { + Assert(BindingElementList.length > 0); + let result; + for (const BindingElisionElement of BindingElementList) { + result = Q(yield* IteratorBindingInitialization_BindingElisionElement(BindingElisionElement, iteratorRecord, environment)); + } + return result; +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingElisionElement : +// BindingElement +// Elision BindingElement +function* IteratorBindingInitialization_BindingElisionElement(BindingElisionElement, iteratorRecord, environment) { + if (!BindingElisionElement) { + // This is an elision. + return Q(IteratorDestructuringAssignmentEvaluation_Elision([BindingElisionElement], iteratorRecord)); + } + return yield* IteratorBindingInitialization_BindingElement(BindingElisionElement, iteratorRecord, environment); +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingElement : SingleNameBinding +function* IteratorBindingInitialization_BindingElement(BindingElement, iteratorRecord, environment) { + switch (true) { + case isSingleNameBinding(BindingElement): + return yield* IteratorBindingInitialization_SingleNameBinding(BindingElement, iteratorRecord, environment); + case isBindingPattern(BindingElement) || isBindingPatternAndInitializer(BindingElement): + return yield* IteratorBindingInitialization_BindingElement_BindingPattern(BindingElement, iteratorRecord, environment); + default: + throw new OutOfRange('IteratorBindingInitialization_BindingElement', BindingElement); + } +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingRestElement : `...` BindingIdentifier +function IteratorBindingInitialization_BindingRestElement_Identifier(BindingRestElement, iteratorRecord, environment) { + const BindingIdentifier = BindingRestElement.argument; + const lhs = Q(ResolveBinding(new Value(BindingIdentifier.name), environment, BindingIdentifier.strict)); + const A = X(ArrayCreate(new Value(0))); + let n = 0; + while (true) { + let next; + if (iteratorRecord.Done === Value.false) { + next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } + } + if (iteratorRecord.Done === Value.true) { + if (Type(environment) === 'Undefined') { + return Q(PutValue(lhs, A)); + } + return InitializeReferencedBinding(lhs, A); + } + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + const nStr = X(ToString(new Value(n))); + const status = X(CreateDataProperty(A, nStr, nextValue)); + Assert(status === Value.true); + n += 1; + } +} + +// 13.3.3.8 #sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization +// BindingRestElement : +// `...` BindingPattern +function* IteratorBindingInitialization_BindingRestElement_Pattern(BindingRestElement, iteratorRecord, environment) { + const BindingPattern = BindingRestElement.argument; + const A = X(ArrayCreate(new Value(0))); + let n = 0; + while (true) { + let next; + if (iteratorRecord.Done === Value.false) { + next = IteratorStep(iteratorRecord); + if (next instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(next); + if (next === Value.false) { + iteratorRecord.Done = Value.true; + } + } + if (iteratorRecord.Done === Value.true) { + return yield* BindingInitialization_BindingPattern(BindingPattern, A, environment); + } + const nextValue = IteratorValue(next); + if (nextValue instanceof AbruptCompletion) { + iteratorRecord.Done = Value.true; + } + ReturnIfAbrupt(nextValue); + const nStr = X(ToString(new Value(n))); + const status = X(CreateDataProperty(A, nStr, nextValue)); + Assert(status === Value.true); + n += 1; + } +} + +function* IteratorBindingInitialization_BindingRestElement(BindingRestElement, iteratorRecord, environment) { + switch (true) { + case isBindingIdentifier(BindingRestElement.argument): + return IteratorBindingInitialization_BindingRestElement_Identifier(BindingRestElement, iteratorRecord, environment); + case isBindingPattern(BindingRestElement.argument): + return yield* IteratorBindingInitialization_BindingRestElement_Pattern(BindingRestElement, iteratorRecord, environment); + default: + throw new OutOfRange('IteratorBindingInitialization_BindingRestElement', BindingRestElement); + } +} + +// 14.1.19 #sec-function-definitions-runtime-semantics-iteratorbindinginitialization +// FormalParameter : BindingElement +function IteratorBindingInitialization_FormalParameter(FormalParameter, iteratorRecord, environment) { + const BindingElement = FormalParameter; + return IteratorBindingInitialization_BindingElement(BindingElement, iteratorRecord, environment); +} + +// 14.1.19 #sec-function-definitions-runtime-semantics-iteratorbindinginitialization +// FunctionRestParameter : BindingRestElement +function IteratorBindingInitialization_FunctionRestParameter(FunctionRestParameter, iteratorRecord, environment) { + const BindingRestElement = FunctionRestParameter; + return IteratorBindingInitialization_BindingRestElement(BindingRestElement, iteratorRecord, environment); +} + +// 14.1.19 #sec-function-definitions-runtime-semantics-iteratorbindinginitialization +// FormalParameters : +// [empty] +// FormalParameterList `,` FunctionRestParameter +// FormalParameterList : FormalParameterList `,` FormalParameter +// +// (implicit) +// FormalParameters : +// FunctionRestParameter +// FormalParameterList +// FormalParameterList `,` +// FormalParameterList : FormalParameter +export function* IteratorBindingInitialization_FormalParameters( + FormalParameters, iteratorRecord, environment, +) { + if (FormalParameters.length === 0) { + return new NormalCompletion(undefined); + } + + for (const FormalParameter of FormalParameters.slice(0, -1)) { + Assert(isFormalParameter(FormalParameter)); + Q(yield* IteratorBindingInitialization_FormalParameter(FormalParameter, iteratorRecord, environment)); + } + + const last = FormalParameters[FormalParameters.length - 1]; + if (isFunctionRestParameter(last)) { + return yield* IteratorBindingInitialization_FunctionRestParameter(last, iteratorRecord, environment); + } + Assert(isFormalParameter(last)); + return yield* IteratorBindingInitialization_FormalParameter(last, iteratorRecord, environment); +} diff --git a/src/engine262/src/runtime-semantics/KeyedBindingInitialization.mjs b/src/engine262/src/runtime-semantics/KeyedBindingInitialization.mjs new file mode 100644 index 0000000..4f0b6bc --- /dev/null +++ b/src/engine262/src/runtime-semantics/KeyedBindingInitialization.mjs @@ -0,0 +1,94 @@ +import { + GetV, + GetValue, + InitializeReferencedBinding, + PutValue, + ResolveBinding, +} from '../abstract-ops/all.mjs'; +import { + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isSingleNameBinding, +} from '../ast.mjs'; +import { Q } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + IsAnonymousFunctionDefinition, +} from '../static-semantics/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + BindingInitialization_BindingPattern, + NamedEvaluation_Expression, +} from './all.mjs'; + +// 13.3.3.9 #sec-runtime-semantics-keyedbindinginitialization +// BindingElement : BindingPattern Initializer +// +// (implicit) +// BindingElement : SingleNameBinding +export function* KeyedBindingInitialization_BindingElement(BindingElement, value, environment, propertyName) { + let BindingPattern; + let Initializer; + switch (true) { + case isSingleNameBinding(BindingElement): + return yield* KeyedBindingInitialization_SingleNameBinding(BindingElement, value, environment, propertyName); + case isBindingPattern(BindingElement): + BindingPattern = BindingElement; + Initializer = undefined; + break; + case isBindingPatternAndInitializer(BindingElement): + BindingPattern = BindingElement.left; + Initializer = BindingElement.right; + break; + default: + throw new OutOfRange('KeyedBindingInitialization_BindingElement', BindingElement); + } + + let v = Q(GetV(value, propertyName)); + if (Initializer !== undefined && Type(v) === 'Undefined') { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + return yield* BindingInitialization_BindingPattern(BindingPattern, v, environment); +} + +// 13.3.3.9 #sec-runtime-semantics-keyedbindinginitialization +// SingleNameBinding : BindingIdentifier Initializer +export function* KeyedBindingInitialization_SingleNameBinding(SingleNameBinding, value, environment, propertyName) { + let BindingIdentifier; + let Initializer; + switch (true) { + case isBindingIdentifier(SingleNameBinding): + BindingIdentifier = SingleNameBinding; + Initializer = undefined; + break; + case isBindingIdentifierAndInitializer(SingleNameBinding): + BindingIdentifier = SingleNameBinding.left; + Initializer = SingleNameBinding.right; + break; + default: + throw new OutOfRange('KeyedBindingInitialization_SingleNameBinding', SingleNameBinding); + } + + const bindingId = new Value(BindingIdentifier.name); + const lhs = Q(ResolveBinding(bindingId, environment, BindingIdentifier.strict)); + let v = Q(GetV(value, propertyName)); + if (Initializer !== undefined && Type(v) === 'Undefined') { + if (IsAnonymousFunctionDefinition(Initializer)) { + v = yield* NamedEvaluation_Expression(Initializer, bindingId); + } else { + const defaultValue = yield* Evaluate(Initializer); + v = Q(GetValue(defaultValue)); + } + } + if (Type(environment) === 'Undefined') { + return Q(PutValue(lhs, v)); + } + return InitializeReferencedBinding(lhs, v); +} diff --git a/src/engine262/src/runtime-semantics/LabelledStatement.mjs b/src/engine262/src/runtime-semantics/LabelledStatement.mjs new file mode 100644 index 0000000..432444c --- /dev/null +++ b/src/engine262/src/runtime-semantics/LabelledStatement.mjs @@ -0,0 +1,44 @@ +import { Value } from '../value.mjs'; +import { Completion, EnsureCompletion, NormalCompletion } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { isBreakableStatement, isLabelledStatement, isStatement } from '../ast.mjs'; +import { SameValue } from '../abstract-ops/all.mjs'; +import { ValueSet, OutOfRange } from '../helpers.mjs'; +import { + LabelledEvaluation_BreakableStatement, +// LabelledEvaluation_IterationStatement, +} from './all.mjs'; + +// 13.13.14 #sec-labelled-statements-runtime-semantics-labelledevaluation +function* LabelledEvaluation({ + label: LabelIdentifier, + body: LabelledItem, +}, labelSet) { + const label = new Value(LabelIdentifier.name); + labelSet.add(label); + let stmtResult; + switch (true) { + case isBreakableStatement(LabelledItem): + stmtResult = yield* LabelledEvaluation_BreakableStatement(LabelledItem, labelSet); + break; + case isLabelledStatement(LabelledItem): + stmtResult = yield* LabelledEvaluation(LabelledItem, labelSet); + break; + case isStatement(LabelledItem): + stmtResult = yield* Evaluate(LabelledItem); + break; + default: + throw new OutOfRange('LabelledEvaluation', LabelledItem); + } + stmtResult = EnsureCompletion(stmtResult); + if (stmtResult.Type === 'break' && SameValue(stmtResult.Target, label) === Value.true) { + stmtResult = new NormalCompletion(stmtResult.Value); + } + return Completion(stmtResult); +} + +// 13.13.15 #sec-labelled-statements-runtime-semantics-evaluation +export function* Evaluate_LabelledStatement(LabelledStatement) { + const newLabelSet = new ValueSet(); + return yield* LabelledEvaluation(LabelledStatement, newLabelSet); +} diff --git a/src/engine262/src/runtime-semantics/LexicalDeclaration.mjs b/src/engine262/src/runtime-semantics/LexicalDeclaration.mjs new file mode 100644 index 0000000..1df7f79 --- /dev/null +++ b/src/engine262/src/runtime-semantics/LexicalDeclaration.mjs @@ -0,0 +1,92 @@ +import { Evaluate } from '../evaluator.mjs'; +import { + NormalCompletion, + Q, + ReturnIfAbrupt, + X, +} from '../completion.mjs'; +import { + isBindingIdentifier, + isBindingPattern, +} from '../ast.mjs'; +import { Value } from '../value.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + GetValue, + InitializeReferencedBinding, + ResolveBinding, +} from '../abstract-ops/all.mjs'; +import { IsAnonymousFunctionDefinition } from '../static-semantics/all.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + BindingInitialization_BindingPattern, + NamedEvaluation_Expression, +} from './all.mjs'; + +// 13.3.1.4 #sec-let-and-const-declarations-runtime-semantics-evaluation +// LexicalBinding : +// BindingIdentifier +// BindingIdentifier Initializer +function* Evaluate_LexicalBinding_BindingIdentifier(LexicalBinding) { + const { id: BindingIdentifier, init: Initializer, strict } = LexicalBinding; + const bindingId = new Value(BindingIdentifier.name); + const lhs = X(ResolveBinding(bindingId, undefined, strict)); + + if (Initializer) { + let value; + if (IsAnonymousFunctionDefinition(Initializer)) { + value = yield* NamedEvaluation_Expression(Initializer, bindingId); + } else { + const rhs = yield* Evaluate(Initializer); + value = Q(GetValue(rhs)); + } + return InitializeReferencedBinding(lhs, value); + } else { + return InitializeReferencedBinding(lhs, Value.undefined); + } +} + +// 13.3.1.4 #sec-let-and-const-declarations-runtime-semantics-evaluation +// LexicalBinding : BindingPattern Initializer +function* Evaluate_LexicalBinding_BindingPattern(LexicalBinding) { + const { id: BindingPattern, init: Initializer } = LexicalBinding; + const rhs = yield* Evaluate(Initializer); + const value = Q(GetValue(rhs)); + const env = surroundingAgent.runningExecutionContext.LexicalEnvironment; + return yield* BindingInitialization_BindingPattern(BindingPattern, value, env); +} + +export function* Evaluate_LexicalBinding(LexicalBinding) { + switch (true) { + case isBindingIdentifier(LexicalBinding.id): + return yield* Evaluate_LexicalBinding_BindingIdentifier(LexicalBinding); + + case isBindingPattern(LexicalBinding.id): + return yield* Evaluate_LexicalBinding_BindingPattern(LexicalBinding); + + default: + throw new OutOfRange('Evaluate_LexicalBinding', LexicalBinding.id); + } +} + +// 13.3.1.4 #sec-let-and-const-declarations-runtime-semantics-evaluation +// BindingList : BindingList `,` LexicalBinding +// +// (implicit) +// BindingList : LexicalBinding +export function* Evaluate_BindingList(BindingList) { + let last; + for (const LexicalBinding of BindingList) { + last = yield* Evaluate_LexicalBinding(LexicalBinding); + ReturnIfAbrupt(last); + } + return last; +} + +// 13.3.1.4 #sec-let-and-const-declarations-runtime-semantics-evaluation +// LexicalDeclaration : LetOrConst BindingList `;` +export function* Evaluate_LexicalDeclaration({ declarations: BindingList }) { + const next = yield* Evaluate_BindingList(BindingList); + ReturnIfAbrupt(next); + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/Literal.mjs b/src/engine262/src/runtime-semantics/Literal.mjs new file mode 100644 index 0000000..9b7103e --- /dev/null +++ b/src/engine262/src/runtime-semantics/Literal.mjs @@ -0,0 +1,33 @@ +import { OutOfRange } from '../helpers.mjs'; +import { MV_NumericLiteral } from '../static-semantics/all.mjs'; +import { Value } from '../value.mjs'; + +// 12.2.4.1 #sec-literals-runtime-semantics-evaluation +// Literal : StringLiteral +// Literal : BooleanLiteral +// Literal : NumericLiteral +export function Evaluate_Literal(Literal) { + switch (true) { + case Literal.raw === 'null': + return Value.null; + + case Literal.raw === 'true': + return Value.true; + + case Literal.raw === 'false': + return Value.false; + + case typeof Literal.value === 'number': + return new Value(MV_NumericLiteral(Literal.raw)); + + case typeof Literal.value === 'bigint': + // TODO: Run MV parser on Literal.raw. + return new Value(Literal.value); + + case typeof Literal.value === 'string': + return new Value(Literal.value); + + default: + throw new OutOfRange('Evaluate_Literal', Literal); + } +} diff --git a/src/engine262/src/runtime-semantics/LogicalANDExpression.mjs b/src/engine262/src/runtime-semantics/LogicalANDExpression.mjs new file mode 100644 index 0000000..d755460 --- /dev/null +++ b/src/engine262/src/runtime-semantics/LogicalANDExpression.mjs @@ -0,0 +1,20 @@ +import { Evaluate } from '../evaluator.mjs'; +import { GetValue, ToBoolean } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; + +// 12.13.3 #sec-binary-logical-operators-runtime-semantics-evaluation +// LogicalANDExpression : LogicalANDExpression `&&` BitwiseORExpression +export function* Evaluate_LogicalANDExpression({ + left: LogicalANDExpression, + right: BitwiseORExpression, +}) { + const lref = yield* Evaluate(LogicalANDExpression); + const lval = Q(GetValue(lref)); + const lbool = ToBoolean(lval); + if (lbool === Value.false) { + return lval; + } + const rref = yield* Evaluate(BitwiseORExpression); + return Q(GetValue(rref)); +} diff --git a/src/engine262/src/runtime-semantics/LogicalORExpression.mjs b/src/engine262/src/runtime-semantics/LogicalORExpression.mjs new file mode 100644 index 0000000..8fc3fca --- /dev/null +++ b/src/engine262/src/runtime-semantics/LogicalORExpression.mjs @@ -0,0 +1,20 @@ +import { Evaluate } from '../evaluator.mjs'; +import { GetValue, ToBoolean } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; + +// 12.13.3 #sec-binary-logical-operators-runtime-semantics-evaluation +// LogicalORExpression : LogicalORExpression `||` LogicalANDExpression +export function* Evaluate_LogicalORExpression({ + left: LogicalORExpression, + right: LogicalANDExpression, +}) { + const lref = yield* Evaluate(LogicalORExpression); + const lval = Q(GetValue(lref)); + const lbool = ToBoolean(lval); + if (lbool === Value.true) { + return lval; + } + const rref = yield* Evaluate(LogicalANDExpression); + return Q(GetValue(rref)); +} diff --git a/src/engine262/src/runtime-semantics/MV.mjs b/src/engine262/src/runtime-semantics/MV.mjs new file mode 100644 index 0000000..e761fb8 --- /dev/null +++ b/src/engine262/src/runtime-semantics/MV.mjs @@ -0,0 +1,110 @@ +import nearley from 'nearley'; +import { Assert } from '../abstract-ops/all.mjs'; +import { searchNotStrWhiteSpaceChar, reverseSearchNotStrWhiteSpaceChar } from '../grammar/util.mjs'; +import grammar from '../grammar/StrNumericLiteral-gen.mjs'; +import { Value } from '../value.mjs'; + +const { ParserRules } = grammar; + +const StrNumericLiteralGrammar = nearley.Grammar.fromCompiled({ + ParserRules, + ParserStart: 'StrNumericLiteral', +}); + +const StrDecimalLiteralGrammar = nearley.Grammar.fromCompiled({ + ParserRules, + ParserStart: 'StrDecimalLiteral', +}); + +// 7.1.3.1.1 #sec-runtime-semantics-mv-s +// Once the exact MV for a String numeric literal has been determined, it is +// then rounded to a value of the Number type. If the MV is 0, then the rounded +// value is +0 unless the first non white space code point in the String +// numeric literal is "-", in which case the rounded value is -0. Otherwise, +// the rounded value must be the Number value for the MV… +function convertScientificMVToNumber(scientific, strWithoutWhitespace) { + const prelimMV = scientific.toNumber(); + if (prelimMV === 0) { + if (strWithoutWhitespace[0] === '-') { + return new Value(-0); + } else { + return new Value(+0); + } + } + return new Value(prelimMV); +} + +// 7.1.3.1.1 #sec-runtime-semantics-mv-s +// StringNumericLiteral ::: +// [empty] +// StrWhiteSpace +// StrWhiteSpace_opt StrNumericLiteral StrWhiteSpace_opt +export function MV_StringNumericLiteral(StringNumericLiteral) { + if (StringNumericLiteral === '') { + // StringNumericLiteral ::: [empty] + return new Value(0); + } + + const leadingWhitespaceStripped = StringNumericLiteral.slice(searchNotStrWhiteSpaceChar(StringNumericLiteral)); + + if (leadingWhitespaceStripped === '') { + // StringNumericLiteral ::: StrWhiteSpace + return new Value(0); + } + + // StringNumericLiteral ::: StrWhiteSpace_opt StrNumericLiteral StrWhiteSpace_opt + const StrNumericLiteral = leadingWhitespaceStripped.slice(0, reverseSearchNotStrWhiteSpaceChar(leadingWhitespaceStripped)); + return MV_StrNumericLiteral(StrNumericLiteral); +} + +// 7.1.3.1.1 #sec-runtime-semantics-mv-s +// StrNumericLiteral ::: +// StrDecimalLiteral +// BinaryIntegerLiteral +// OctalIntegerLiteral +// HexIntegerLiteral +function MV_StrNumericLiteral(StrNumericLiteral) { + const parser = new nearley.Parser(StrNumericLiteralGrammar); + try { + parser.feed(StrNumericLiteral); + } catch (err) { + return new Value(NaN); + } + if (parser.results.length === 0) { + return new Value(NaN); + } + Assert(parser.results.length === 1); + return convertScientificMVToNumber(parser.results[0], StrNumericLiteral); +} + +// 7.1.3.1.1 #sec-runtime-semantics-mv-s +// StrDecimalLiteral ::: +// StrUnsignedDecimalLiteral +// `+` StrUnsignedDecimalLiteral +// `-` StrUnsignedDecimalLiteral +export function MV_StrDecimalLiteral(StrDecimalLiteral, prefixOk = false) { + const parser = new nearley.Parser(StrDecimalLiteralGrammar, { keepHistory: prefixOk }); + try { + parser.feed(StrDecimalLiteral); + } catch (err) { + if (!prefixOk) { + return new Value(NaN); + } + } + if (prefixOk) { + // Backtrack until we find a prefix of StrDecimalLiteral that is indeed a + // StrDecimalLiteral. + while (parser.table[parser.current]) { + parser.restore(parser.table[parser.current]); + if (parser.results.length !== 0) { + break; + } + parser.current -= 1; + } + if (parser.results.length === 0) { + return new Value(NaN); + } + } + Assert(parser.results.length === 1); + return convertScientificMVToNumber(parser.results[0], StrDecimalLiteral); +} diff --git a/src/engine262/src/runtime-semantics/MemberExpression.mjs b/src/engine262/src/runtime-semantics/MemberExpression.mjs new file mode 100644 index 0000000..e431963 --- /dev/null +++ b/src/engine262/src/runtime-semantics/MemberExpression.mjs @@ -0,0 +1,62 @@ +import { + isActualMemberExpressionWithBrackets, + isActualMemberExpressionWithDot, +} from '../ast.mjs'; +import { GetValue } from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + EvaluatePropertyAccessWithExpressionKey, + EvaluatePropertyAccessWithIdentifierKey, +} from './all.mjs'; + +// 12.3.2.1 #sec-property-accessors-runtime-semantics-evaluation +// MemberExpression : MemberExpression `[` Expression `]` +// CallExpression : CallExpression `[` Expression `]` +function* Evaluate_MemberExpression_Expression(MemberExpression, Expression) { + // 1. Let baseReference be the result of evaluating |MemberExpression|. + const baseReference = yield* Evaluate(MemberExpression); + // 2. Let baseValue be ? GetValue(baseReference). + const baseValue = Q(GetValue(baseReference)); + // 3. If the code matched by this |MemberExpression| is strict mode code, let strict be true; else let strict be false. + const strict = MemberExpression.strict; + // 4. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, |Expression|, strict). + return Q(yield* EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict)); +} + +// 12.3.2.1 #sec-property-accessors-runtime-semantics-evaluation +// MemberExpression : MemberExpression `.` IdentifierName +// CallExpression : CallExpression `.` IdentifierName +function* Evaluate_MemberExpression_IdentifierName(MemberExpression, IdentifierName) { + // 1. Let baseReference be the result of evaluating |MemberExpression|. + const baseReference = yield* Evaluate(MemberExpression); + // 2. Let baseValue be ? GetValue(baseReference). + const baseValue = Q(GetValue(baseReference)); + // 3. If the code matched by this |MemberExpression| is strict mode code, let strict be true; else let strict be false. + const strict = MemberExpression.strict; + // 4. Return ? EvaluatePropertyAccessWithIdentifierKey(baseValue, |IdentifierName|, strict). + return Q(EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict)); +} + +// 12.3.2.1 #sec-property-accessors-runtime-semantics-evaluation +// MemberExpression : +// MemberExpression `[` Expression `]` +// MemberExpression `.` IdentifierName +// CallExpression : +// CallExpression `[` Expression `]` +// CallExpression `.` IdentifierName +export function* Evaluate_MemberExpression(MemberExpression) { + switch (true) { + case isActualMemberExpressionWithBrackets(MemberExpression): + return yield* Evaluate_MemberExpression_Expression( + MemberExpression.object, MemberExpression.property, + ); + case isActualMemberExpressionWithDot(MemberExpression): + return yield* Evaluate_MemberExpression_IdentifierName( + MemberExpression.object, MemberExpression.property, + ); + default: + throw new OutOfRange('Evaluate_MemberExpression', MemberExpression); + } +} diff --git a/src/engine262/src/runtime-semantics/MetaProperty.mjs b/src/engine262/src/runtime-semantics/MetaProperty.mjs new file mode 100644 index 0000000..17e0041 --- /dev/null +++ b/src/engine262/src/runtime-semantics/MetaProperty.mjs @@ -0,0 +1,67 @@ +import { HostGetImportMetaProperties, HostFinalizeImportMeta } from '../engine.mjs'; +import { isNewTarget, isImportMeta } from '../ast.mjs'; +import { + Assert, + GetNewTarget, + GetActiveScriptOrModule, + OrdinaryObjectCreate, + CreateDataProperty, +} from '../abstract-ops/all.mjs'; +import { SourceTextModuleRecord } from '../modules.mjs'; +import { X } from '../completion.mjs'; +import { Type, Value } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// #sec-meta-properties-runtime-semantics-evaluation +// NewTarget : `new` `.` `target` +function Evaluate_NewTarget() { + // 1. Return GetNewTarget(). + return GetNewTarget(); +} + +// #sec-meta-properties-runtime-semantics-evaluation +// ImportMeta : `import` `.` `meta` +function Evaluate_ImportMeta() { + // 1. Let module be GetActiveScriptOrModule(). + const module = GetActiveScriptOrModule(); + // 2. Assert: module is a Source Text Module Record; + Assert(module instanceof SourceTextModuleRecord); + // 3. Let importMeta be module.ImportMeta. + let importMeta = module.ImportMeta; + // 4. If importMeta is empty, then + if (importMeta === undefined) { + // a. Set importMeta to ! OrdinaryObjectCreate(null). + importMeta = X(OrdinaryObjectCreate(Value.null)); + // b. Let importMetaValues be ! HostGetImportMetaProperties(module). + const importMetaValues = X(HostGetImportMetaProperties(module)); + // c. For each Record { [[Key]], [[Value]] } p that is an element of importMetaValues, + for (const p of importMetaValues) { + // i. Perform ! CreateDataPropertyOrThrow(importMeta, p.[[Key]], p.[[Value]]). + X(CreateDataProperty(importMeta, p.Key, p.Value)); + } + // d. Perform ! HostFinalizeImportMeta(importMeta, module). + X(HostFinalizeImportMeta(importMeta, module)); + // e. Set module.[[ImportMeta]] to importMeta. + module.ImportMeta = importMeta; + // f. Return importMeta. + return importMeta; + } else { + // a. Assert: Type(importMeta) is Object. + Assert(Type(importMeta) === 'Object'); + // b. Return importMeta. + return importMeta; + } +} + +// #prod-MetaProperty +// MetaProperty : NewTarget +export function* Evaluate_MetaProperty(MetaProperty) { // eslint-disable-line require-yield + switch (true) { + case isNewTarget(MetaProperty): + return Evaluate_NewTarget(); + case isImportMeta(MetaProperty): + return Evaluate_ImportMeta(); + default: + throw new OutOfRange('Evaluate_MetaProperty', MetaProperty); + } +} diff --git a/src/engine262/src/runtime-semantics/MultiplicativeExpression.mjs b/src/engine262/src/runtime-semantics/MultiplicativeExpression.mjs new file mode 100644 index 0000000..a36f00d --- /dev/null +++ b/src/engine262/src/runtime-semantics/MultiplicativeExpression.mjs @@ -0,0 +1,43 @@ +import { surroundingAgent } from '../engine.mjs'; +import { GetValue, ToNumeric } from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { Type, TypeNumeric } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +export function EvaluateBinopValues_MultiplicativeExpression(MultiplicativeOperator, lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + + const T = TypeNumeric(lnum); + + switch (MultiplicativeOperator) { + case '*': + return T.multiply(lnum, rnum); + case '/': + return T.divide(lnum, rnum); + case '%': + return T.remainder(lnum, rnum); + + default: + throw new OutOfRange('EvaluateBinopValues_MultiplicativeExpression', MultiplicativeOperator); + } +} + +export function* Evaluate_MultiplicativeExpression({ + left: MultiplicativeExpression, + operator: MultiplicativeOperator, + right: ExponentiationExpression, +}) { + const left = yield* Evaluate(MultiplicativeExpression); + const leftValue = Q(GetValue(left)); + const right = yield* Evaluate(ExponentiationExpression); + const rightValue = Q(GetValue(right)); + return EvaluateBinopValues_MultiplicativeExpression( + MultiplicativeOperator, leftValue, rightValue, + ); +} diff --git a/src/engine262/src/runtime-semantics/NamedEvaluation.mjs b/src/engine262/src/runtime-semantics/NamedEvaluation.mjs new file mode 100644 index 0000000..d4e32a4 --- /dev/null +++ b/src/engine262/src/runtime-semantics/NamedEvaluation.mjs @@ -0,0 +1,202 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + DefinePropertyOrThrow, + SetFunctionName, + OrdinaryFunctionCreate, + OrdinaryObjectCreate, + MakeConstructor, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { + isArrowFunction, + isAsyncArrowFunction, + isAsyncFunctionExpression, + isAsyncGeneratorExpression, + isClassExpression, + isFunctionExpression, + isGeneratorExpression, + isParenthesizedExpression, +} from '../ast.mjs'; +import { X, ReturnIfAbrupt } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { IsAnonymousFunctionDefinition } from '../static-semantics/all.mjs'; +import { Value, Descriptor } from '../value.mjs'; +import { ClassDefinitionEvaluation_ClassTail } from './all.mjs'; + +// 12.2.10.4 #sec-grouping-operator-runtime-semantics-namedevaluation +// ParenthesizedExpression : `(` Expression `)` +function* NamedEvaluation_ParenthesizedExpression(ParenthesizedExpression, name) { + const { expression: Expression } = ParenthesizedExpression; + Assert(IsAnonymousFunctionDefinition(Expression)); + return yield* NamedEvaluation_Expression(Expression, name); +} + +// 14.1.21 #sec-function-definitions-runtime-semantics-namedevaluation +// FunctionExpression : `function` `(` FormalParameters `)` `{` FunctionBody `}` +export function NamedEvaluation_FunctionExpression(FunctionExpression, name) { + const { params: FormalParameters } = FunctionExpression; + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let closure be OrdinaryFunctionCreate(%Function.prototype%, FormalParameters, FunctionBody, lexical-this, scope). + const closure = OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), FormalParameters, FunctionExpression, 'non-lexical-this', scope); + // 3. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 4. Perform MakeConstructor(closure). + MakeConstructor(closure); + // 5. Set closure.[[SourceText]] to the source text matched by FunctionExpression. + closure.SourceText = sourceTextMatchedBy(FunctionExpression); + // 6. Return closure. + return closure; +} + +// 14.2.16 #sec-arrow-function-definitions-runtime-semantics-namedevaluation +// ArrowFunction : ArrowParameters `=>` ConciseBody +export function NamedEvaluation_ArrowFunction(ArrowFunction, name) { + const { params: ArrowParameters } = ArrowFunction; + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let parameters be CoveredFormalsList of ArrowParameters. + const parameters = ArrowParameters; + // 3. Let closure be OrdinaryFunctionCreate(%Function.prototype%, parameters, ConciseBody, lexical-this, scope). + const closure = OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), parameters, ArrowFunction, 'lexical-this', scope); + // 4. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 5. Set closure.[[SourceText]] to the source text matched by ArrowFunction. + closure.SourceText = sourceTextMatchedBy(ArrowFunction); + // 6. Return closure. + return closure; +} + +// 14.4.13 #sec-generator-function-definitions-runtime-semantics-namedevaluation +// GeneratorExpression : `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}` +export function NamedEvaluation_GeneratorExpression(GeneratorExpression, name) { + const { params: FormalParameters } = GeneratorExpression; + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let closure be ! OrdinaryFunctionCreate(%Generator%, FormalParameters, GeneratorBody, non-lexical-this, scope). + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Generator%'), FormalParameters, GeneratorExpression, 'non-lexical-this', scope)); + // 3. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 4. Let prototype be ! OrdinaryObjectCreate(%Generator.prototype%). + const prototype = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Generator.prototype%'))); + // 5. Perform ! DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). + X(DefinePropertyOrThrow(closure, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + // 6. Set closure.[[SourceText]] to the source text matched by GeneratorExpression. + closure.SourceText = sourceTextMatchedBy(GeneratorExpression); + // 7. Return closure. + return closure; +} + +// 14.5.13 #sec-asyncgenerator-definitions-namedevaluation +// AsyncGeneratorExpression : +// `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +export function NamedEvaluation_AsyncGeneratorExpression(AsyncGeneratorExpression, name) { + const { params: FormalParameters } = AsyncGeneratorExpression; + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let closure be ! OrdinaryFunctionCreate(%AsyncGenerator%, FormalParameters, AsyncGeneratorBody, non-lexical-this, scope). + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncGeneratorFunction.prototype%'), FormalParameters, AsyncGeneratorExpression, 'non-lexical-this', scope)); + // 3. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 4. Let prototype be ! OrdinaryObjectCreate(%AsyncGenerator.prototype%). + const prototype = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncGenerator.prototype%'))); + // 5. Perform ! DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). + X(DefinePropertyOrThrow(closure, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + // 6. Set closure.[[SourceText]] to the source text matched by AsyncGeneratorExpression. + closure.SourceText = sourceTextMatchedBy(AsyncGeneratorExpression); + // 7. Return closure. + return closure; +} + +// 14.6.15 #sec-class-definitions-runtime-semantics-namedevaluation +// ClassExpression : `class` ClassTail +function* NamedEvaluation_ClassExpression(ClassExpression, name) { + const { body, superClass } = ClassExpression; + const ClassTail = { + ClassHeritage: superClass, + ClassBody: body.body, + }; + const value = yield* ClassDefinitionEvaluation_ClassTail(ClassTail, Value.undefined, name); + ReturnIfAbrupt(value); + value.SourceText = sourceTextMatchedBy(ClassExpression); + return value; +} + +// 14.7.13 #sec-async-function-definitions-runtime-semantics-namedevaluation +// AsyncFunctionExpression : +// `async` `function` `(` FormalParameters `)` `{` AsyncFunctionBody `}` +export function NamedEvaluation_AsyncFunctionExpression(AsyncFunctionExpression, name) { + const { params: FormalParameters } = AsyncFunctionExpression; + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let closure be ! OrdinaryFunctionCreate(%AsyncFunction.prototype%, FormalParameters, AsyncFunctionBody, non-lexical-this, scope). + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncFunction.prototype%'), FormalParameters, AsyncFunctionExpression, 'non-lexical-this', scope)); + // 3. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 4. Set closure.[[SourceText]] to the source text matched by AsyncFunctionExpression. + closure.SourceText = sourceTextMatchedBy(AsyncFunctionExpression); + // 5. Return closure. + return closure; +} + +// 14.8.15 #sec-async-arrow-function-definitions-runtime-semantics-namedevaluation +// AsyncArrowFunction : +// `async` AsyncArrowBindingIdentifier `=>` AsyncConciseBody +// CoverCallExpressionAndAsyncArrowHead `=>` AsyncConciseBody +export function NamedEvaluation_AsyncArrowFunction(AsyncArrowFunction, name) { + // 1. Let scope be the LexicalEnvironment of the running execution context. + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + // 2. Let parameters be AsyncArrowBindingIdentifier. + const parameters = AsyncArrowFunction.params; + // 3. Let closure be ! OrdinaryFunctionCreate(%AsyncFunction.prototype%, parameters, AsyncConciseBody, lexical-this, scope). + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncFunction.prototype%'), parameters, AsyncArrowFunction, 'lexical-this', scope)); + // 4. Perform SetFunctionName(closure, name). + SetFunctionName(closure, name); + // 5. Set closure.[[SourceText]] to the source text matched by AsyncArrowFunction. + closure.SourceText = sourceTextMatchedBy(AsyncArrowFunction); + // 6. Return closure. + return closure; +} + +// (implicit) +export function* NamedEvaluation_Expression(Expression, name) { + switch (true) { + case isFunctionExpression(Expression): + return NamedEvaluation_FunctionExpression(Expression, name); + + case isClassExpression(Expression): + return yield* NamedEvaluation_ClassExpression(Expression, name); + + case isGeneratorExpression(Expression): + return NamedEvaluation_GeneratorExpression(Expression, name); + + case isAsyncFunctionExpression(Expression): + return NamedEvaluation_AsyncFunctionExpression(Expression, name); + + case isAsyncGeneratorExpression(Expression): + return NamedEvaluation_AsyncGeneratorExpression(Expression, name); + + case isArrowFunction(Expression): + return NamedEvaluation_ArrowFunction(Expression, name); + + case isAsyncArrowFunction(Expression): + return NamedEvaluation_AsyncArrowFunction(Expression, name); + + case isParenthesizedExpression(Expression): + return yield* NamedEvaluation_ParenthesizedExpression(Expression, name); + + default: + throw new OutOfRange('NamedEvaluation_Expression', Expression); + } +} diff --git a/src/engine262/src/runtime-semantics/NewExpression.mjs b/src/engine262/src/runtime-semantics/NewExpression.mjs new file mode 100644 index 0000000..e26523d --- /dev/null +++ b/src/engine262/src/runtime-semantics/NewExpression.mjs @@ -0,0 +1,35 @@ +import { surroundingAgent } from '../engine.mjs'; +import { isActualNewExpression } from '../ast.mjs'; +import { + Assert, + Construct, + GetValue, + IsConstructor, +} from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { ArgumentListEvaluation } from './all.mjs'; + + +// 12.3.3.1.1 #sec-evaluatenew +function* EvaluateNew(constructExpr, args = []) { + Assert(isActualNewExpression(constructExpr)); + Assert(Array.isArray(args)); + const ref = yield* Evaluate(constructExpr.callee); + const constructor = Q(GetValue(ref)); + // We convert empty to [] as part of the default parameter. + const argList = Q(yield* ArgumentListEvaluation(args)); + if (IsConstructor(constructor) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', constructor); + } + return Q(Construct(constructor, argList)); +} + +// 12.3.3.1 #sec-new-operator-runtime-semantics-evaluation +// NewExpression : +// `new` NewExpression +// `new` MemberExpression Arguments +export function* Evaluate_NewExpression(NewExpression) { + return yield* EvaluateNew(NewExpression, NewExpression.arguments); +} diff --git a/src/engine262/src/runtime-semantics/NumberToBigInt.mjs b/src/engine262/src/runtime-semantics/NumberToBigInt.mjs new file mode 100644 index 0000000..b229b69 --- /dev/null +++ b/src/engine262/src/runtime-semantics/NumberToBigInt.mjs @@ -0,0 +1,12 @@ +import { surroundingAgent } from '../engine.mjs'; +import { IsInteger, Assert } from '../abstract-ops/all.mjs'; +import { Value, Type } from '../value.mjs'; + + +export function NumberToBigInt(number) { + Assert(Type(number) === 'Number'); + if (IsInteger(number) === Value.false) { + return surroundingAgent.Throw('RangeError', 'CannotConvertDecimalToBigInt', number); + } + return new Value(BigInt(number.numberValue())); +} diff --git a/src/engine262/src/runtime-semantics/ObjectLiteral.mjs b/src/engine262/src/runtime-semantics/ObjectLiteral.mjs new file mode 100644 index 0000000..7126822 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ObjectLiteral.mjs @@ -0,0 +1,25 @@ +import { + OrdinaryObjectCreate, +} from '../abstract-ops/all.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Q } from '../completion.mjs'; +import { + PropertyDefinitionEvaluation_PropertyDefinitionList, +} from './all.mjs'; + +// 12.2.6.7 #sec-object-initializer-runtime-semantics-evaluation +// ObjectLiteral : +// `{` `}` +// `{` PropertyDefintionList `}` +// `{` PropertyDefintionList `,` `}` +export function* Evaluate_ObjectLiteral(ObjectLiteral) { + if (ObjectLiteral.properties.length === 0) { + return OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + } + + const PropertyDefintionList = ObjectLiteral.properties; + + const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + Q(yield* PropertyDefinitionEvaluation_PropertyDefinitionList(PropertyDefintionList, obj, true)); + return obj; +} diff --git a/src/engine262/src/runtime-semantics/OptionalExpression.mjs b/src/engine262/src/runtime-semantics/OptionalExpression.mjs new file mode 100644 index 0000000..667598e --- /dev/null +++ b/src/engine262/src/runtime-semantics/OptionalExpression.mjs @@ -0,0 +1,86 @@ +import { + isOptionalChain, + isOptionalChainWithExpression, + isOptionalChainWithIdentifierName, + isOptionalChainWithArguments, + isOptionalChainWithOptionalChain, +} from '../ast.mjs'; +import { GetValue, Assert } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { IsInTailPosition } from '../static-semantics/all.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + EvaluateCall, + EvaluatePropertyAccessWithIdentifierKey, + EvaluatePropertyAccessWithExpressionKey, +} from './all.mjs'; + +// #prod-OptionalExpression +// OptionalExpression : +// MemberExpression OptionalChain +// CallExpression OptionalChain +// OptionalExpression OptionalChain +export function* Evaluate_OptionalExpression({ object: MemberExpression, chain: OptionalChain }) { + // 1. Let baseReference be the result of evaluating MemberExpression. + const baseReference = yield* Evaluate(MemberExpression); + // 2. Let baseValue be ? GetValue(baseReference). + const baseValue = Q(GetValue(baseReference)); + // 3. If baseValue is undefined or null, then + if (baseValue === Value.undefined || baseValue === Value.null) { + // a. Return undefined. + return Value.undefined; + } + // Return the result of performing ChainEvaluation of OptionalChain with arguments baseValue and baseReference. + return Q(yield* ChainEvaluation(OptionalChain, baseValue, baseReference)); +} + +// #sec-optional-chaining-chain-evaluation +// OptionalChain : +// `?.` `[` Expression `]` +// `?.` IdentifierName +// `?.` Arguments +// OptionalChain `[` Expression `]` +// OptionalChain `.` IdentifierName +// OptionalChain Arguments +function* ChainEvaluation(OptionalChain, baseValue, baseReference) { + const strict = OptionalChain.strict; + + if (isOptionalChainWithOptionalChain(OptionalChain)) { + Assert(isOptionalChain(OptionalChain.base)); + const newReference = yield* ChainEvaluation(OptionalChain.base, baseValue, baseReference); + const newValue = Q(GetValue(newReference)); + switch (true) { + // OptionalChain : OptionalChain `?.` `[` Expression `]` + case isOptionalChainWithExpression(OptionalChain): + return Q(yield* EvaluatePropertyAccessWithExpressionKey(newValue, OptionalChain.property, strict)); + // OptionalChain : OptionalChain `?.` IdentifierName + case isOptionalChainWithIdentifierName(OptionalChain): + return Q(EvaluatePropertyAccessWithIdentifierKey(newValue, OptionalChain.property, strict)); + // OptionalChain : OptionalChain `?.` Arguments + case isOptionalChainWithArguments(OptionalChain): { + const tailCall = IsInTailPosition(OptionalChain); + return Q(yield* EvaluateCall(newValue, newReference, OptionalChain.arguments, tailCall)); + } + default: + throw new OutOfRange('ChainEvaluation', OptionalChain); + } + } + + switch (true) { + // OptionalChain : `?.` `[` Expression `]` + case isOptionalChainWithExpression(OptionalChain): + return Q(yield* EvaluatePropertyAccessWithExpressionKey(baseValue, OptionalChain.property, strict)); + // OptionalChain : `?.` IdentifierName + case isOptionalChainWithIdentifierName(OptionalChain): + return Q(EvaluatePropertyAccessWithIdentifierKey(baseValue, OptionalChain.property, strict)); + // OptionalChain : `?.` Arguments + case isOptionalChainWithArguments(OptionalChain): { + const tailCall = IsInTailPosition(OptionalChain); + return Q(yield* EvaluateCall(baseValue, baseReference, OptionalChain.arguments, tailCall)); + } + default: + throw new OutOfRange('ChainEvaluation', OptionalChain); + } +} diff --git a/src/engine262/src/runtime-semantics/PropertyBindingInitialization.mjs b/src/engine262/src/runtime-semantics/PropertyBindingInitialization.mjs new file mode 100644 index 0000000..fb6e0cc --- /dev/null +++ b/src/engine262/src/runtime-semantics/PropertyBindingInitialization.mjs @@ -0,0 +1,56 @@ +import { + isBindingPropertyWithColon, + isBindingPropertyWithSingleNameBinding, +} from '../ast.mjs'; +import { + Q, + ReturnIfAbrupt, +} from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { Value } from '../value.mjs'; +import { + Evaluate_PropertyName, + KeyedBindingInitialization_BindingElement, + KeyedBindingInitialization_SingleNameBinding, +} from './all.mjs'; + +// 13.3.3.6 #sec-destructuring-binding-patterns-runtime-semantics-propertybindinginitialization +// BindingPropertyList : BindingPropertyList `,` BindingProperty +// +// (implicit) +// BindingPropertyList : BindingProperty +export function* PropertyBindingInitialization_BindingPropertyList( + BindingPropertyList, value, environment, +) { + const boundNames = []; + for (const BindingProperty of BindingPropertyList) { + const nextNames = Q(yield* PropertyBindingInitialization_BindingProperty( + BindingProperty, value, environment, + )); + boundNames.push(...nextNames); + } + return boundNames; +} + +// 13.3.3.6 #sec-destructuring-binding-patterns-runtime-semantics-propertybindinginitialization +// BindingProperty : +// SingleNameBinding +// PropertyName `:` BindingElement +export function* PropertyBindingInitialization_BindingProperty(BindingProperty, value, environment) { + switch (true) { + case isBindingPropertyWithSingleNameBinding(BindingProperty): { + const name = new Value(BindingProperty.key.name); + Q(yield* KeyedBindingInitialization_SingleNameBinding(BindingProperty.value, value, environment, name)); + return [name]; + } + case isBindingPropertyWithColon(BindingProperty): { + const { key: PropertyName, value: BindingElement } = BindingProperty; + const P = yield* Evaluate_PropertyName(PropertyName, BindingProperty.computed); + ReturnIfAbrupt(P); + Q(yield* KeyedBindingInitialization_BindingElement(BindingElement, value, environment, P)); + return [P]; + } + default: + throw new OutOfRange('PropertyBindingInitialization_BindingProperty', BindingProperty); + } +} diff --git a/src/engine262/src/runtime-semantics/PropertyDefinitionEvaluation.mjs b/src/engine262/src/runtime-semantics/PropertyDefinitionEvaluation.mjs new file mode 100644 index 0000000..e449282 --- /dev/null +++ b/src/engine262/src/runtime-semantics/PropertyDefinitionEvaluation.mjs @@ -0,0 +1,312 @@ +import { + IsAnonymousFunctionDefinition, +} from '../static-semantics/all.mjs'; +import { + isAsyncMethod, + isAsyncGeneratorMethod, + isGeneratorMethod, + isMethodDefinition, + isMethodDefinitionGetter, + isMethodDefinitionRegularFunction, + isMethodDefinitionSetter, + isPropertyDefinitionIdentifierReference, + isPropertyDefinitionKeyValue, + isPropertyDefinitionSpread, +} from '../ast.mjs'; +import { + Assert, + CopyDataProperties, + CreateDataPropertyOrThrow, + DefinePropertyOrThrow, + OrdinaryFunctionCreate, + GetValue, + MakeMethod, + OrdinaryObjectCreate, + SetFunctionName, + sourceTextMatchedBy, +} from '../abstract-ops/all.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { Q, ReturnIfAbrupt, X } from '../completion.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + DefineMethod, + Evaluate_PropertyName, + NamedEvaluation_Expression, +} from './all.mjs'; + +function hasNonConfigurableProperties(obj) { + for (const desc of obj.properties.values()) { + if (desc.Configurable === Value.false) { + return true; + } + } + return false; +} + +// 12.2.6.8 #sec-object-initializer-runtime-semantics-propertydefinitionevaluation +// PropertyDefinitionList : PropertyDefinitionList `,` PropertyDefinition +// +// (implicit) +// PropertyDefinitionList : PropertyDefinition +export function* PropertyDefinitionEvaluation_PropertyDefinitionList( + PropertyDefinitionList, object, enumerable, +) { + Assert(PropertyDefinitionList.length > 0); + + let lastReturn; + for (const PropertyDefinition of PropertyDefinitionList) { + lastReturn = Q(yield* PropertyDefinitionEvaluation_PropertyDefinition( + PropertyDefinition, object, enumerable, + )); + } + return lastReturn; +} + +// 12.2.6.8 #sec-object-initializer-runtime-semantics-propertydefinitionevaluation +// PropertyDefinition : `...` AssignmentExpression +function* PropertyDefinitionEvaluation_PropertyDefinition_Spread(PropertyDefinition, object) { + const AssignmentExpression = PropertyDefinition.argument; + + const exprValue = yield* Evaluate(AssignmentExpression); + const fromValue = Q(GetValue(exprValue)); + const excludedNames = []; + return Q(CopyDataProperties(object, fromValue, excludedNames)); +} + +// 12.2.6.8 #sec-object-initializer-runtime-semantics-propertydefinitionevaluation +// PropertyDefinition : IdentifierReference +function* PropertyDefinitionEvaluation_PropertyDefinition_IdentifierReference( + PropertyDefinition, object, enumerable, +) { + const IdentifierReference = PropertyDefinition.key; + const propName = new Value(IdentifierReference.name); + const exprValue = yield* Evaluate(IdentifierReference); + const propValue = Q(GetValue(exprValue)); + Assert(enumerable); + // Assert: object is an ordinary object. + Assert(object.Extensible === Value.true); + Assert(!hasNonConfigurableProperties(object)); + return X(CreateDataPropertyOrThrow(object, propName, propValue)); +} + +// 12.2.6.8 #sec-object-initializer-runtime-semantics-propertydefinitionevaluation +// PropertyDefinition : PropertyName `:` AssignmentExpression +function* PropertyDefinitionEvaluation_PropertyDefinition_KeyValue( + PropertyDefinition, object, enumerable, +) { + const { key: PropertyName, value: AssignmentExpression } = PropertyDefinition; + const propKey = yield* Evaluate_PropertyName(PropertyName, PropertyDefinition.computed); + ReturnIfAbrupt(propKey); + let propValue; + if (IsAnonymousFunctionDefinition(AssignmentExpression)) { + propValue = yield* NamedEvaluation_Expression(AssignmentExpression, propKey); + ReturnIfAbrupt(propValue); // https://github.com/tc39/ecma262/issues/1605 + } else { + const exprValueRef = yield* Evaluate(AssignmentExpression); + propValue = Q(GetValue(exprValueRef)); + } + Assert(enumerable); + // Assert: object is an ordinary object. + Assert(object.Extensible === Value.true); + Assert(!hasNonConfigurableProperties(object)); + return X(CreateDataPropertyOrThrow(object, propKey, propValue)); +} + +// 14.3.8 #sec-method-definitions-runtime-semantics-propertydefinitionevaluation +// MethodDefinition : +// PropertyName `(` UniqueFormalParameters `)` `{` FunctionBody `}` +// `get` PropertyName `(` `)` `{` FunctionBody `}` +// `set` PropertyName `(` PropertySetParameterList `)` `{` FunctionBody `}` +// +// (implicit) +// MethodDefinition : GeneratorMethod +export function* PropertyDefinitionEvaluation_MethodDefinition(MethodDefinition, object, enumerable) { + switch (true) { + case isMethodDefinitionRegularFunction(MethodDefinition): { + const methodDef = Q(yield* DefineMethod(MethodDefinition, object)); + X(SetFunctionName(methodDef.Closure, methodDef.Key)); + const desc = Descriptor({ + Value: methodDef.Closure, + Writable: Value.true, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, methodDef.Key, desc)); + } + + case isGeneratorMethod(MethodDefinition): + return yield* PropertyDefinitionEvaluation_GeneratorMethod(MethodDefinition, object, enumerable); + + case isAsyncMethod(MethodDefinition): + return yield* PropertyDefinitionEvaluation_AsyncMethod(MethodDefinition, object, enumerable); + + case isAsyncGeneratorMethod(MethodDefinition): + return yield* PropertyDefinitionEvaluation_AsyncGeneratorMethod(MethodDefinition, object, enumerable); + + case isMethodDefinitionGetter(MethodDefinition): { + const PropertyName = MethodDefinition.key; + + const propKey = yield* Evaluate_PropertyName(PropertyName, MethodDefinition.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const formalParameterList = []; + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), formalParameterList, MethodDefinition.value, 'non-lexical-this', scope)); + X(SetFunctionName(closure, propKey, new Value('get'))); + X(MakeMethod(closure, object)); + closure.SourceText = sourceTextMatchedBy(MethodDefinition); + const desc = Descriptor({ + Get: closure, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, propKey, desc)); + } + + case isMethodDefinitionSetter(MethodDefinition): { + const PropertyName = MethodDefinition.key; + const PropertySetParameterList = MethodDefinition.value.params; + + const propKey = yield* Evaluate_PropertyName(PropertyName, MethodDefinition.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Function.prototype%'), PropertySetParameterList, MethodDefinition.value, 'non-lexical-this', scope)); + X(SetFunctionName(closure, propKey, new Value('set'))); + X(MakeMethod(closure, object)); + closure.SourceText = sourceTextMatchedBy(MethodDefinition); + const desc = Descriptor({ + Set: closure, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, propKey, desc)); + } + default: + throw new OutOfRange('PropertyDefinitionEvaluation_MethodDefinition', MethodDefinition); + } +} + +// (implicit) +// ClassElement : +// MethodDefinition +// `static` MethodDefinition +export const PropertyDefinitionEvaluation_ClassElement = PropertyDefinitionEvaluation_MethodDefinition; + +// 14.4.12 #sec-generator-function-definitions-runtime-semantics-propertydefinitionevaluation +// GeneratorMethod : `*` PropertyName `(` UniqueFormalParameters `)` `{` GeneratorBody `}` +function* PropertyDefinitionEvaluation_GeneratorMethod(GeneratorMethod, object, enumerable) { + const { + key: PropertyName, + value: GeneratorExpression, + } = GeneratorMethod; + const UniqueFormalParameters = GeneratorExpression.params; + + const propKey = yield* Evaluate_PropertyName(PropertyName, GeneratorMethod.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%Generator%'), UniqueFormalParameters, GeneratorExpression, 'non-lexical-this', scope)); + MakeMethod(closure, object); + X(SetFunctionName(closure, propKey)); + const prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Generator.prototype%')); + X(DefinePropertyOrThrow( + closure, + new Value('prototype'), + Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }), + )); + closure.SourceText = sourceTextMatchedBy(GeneratorExpression); + const desc = Descriptor({ + Value: closure, + Writable: Value.true, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, propKey, desc)); +} + +// AsyncMethod : `async` PropertyName `(` UniqueFormalParameters `)` `{` AsyncFunctionBody `}` +function* PropertyDefinitionEvaluation_AsyncMethod(AsyncMethod, object, enumerable) { + const { + key: PropertyName, + value: AsyncExpression, + } = AsyncMethod; + const UniqueFormalParameters = AsyncExpression.params; + + const propKey = yield* Evaluate_PropertyName(PropertyName, AsyncMethod.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncFunction.prototype%'), UniqueFormalParameters, AsyncExpression, 'non-lexical-this', scope)); + X(MakeMethod(closure, object)); + X(SetFunctionName(closure, propKey)); + closure.SourceText = sourceTextMatchedBy(AsyncMethod); + const desc = Descriptor({ + Value: closure, + Writable: Value.true, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, propKey, desc)); +} + +// AsyncGeneratorMethod : `async` `*` PropertyName `(` UniqueFormalParameters `)` `{` AsyncGeneratorFunctionBody `}` +function* PropertyDefinitionEvaluation_AsyncGeneratorMethod(AsyncGeneratorMethod, object, enumerable) { + const { + key: PropertyName, + value: AsyncGeneratorExpression, + } = AsyncGeneratorMethod; + const UniqueFormalParameters = AsyncGeneratorExpression.params; + + const propKey = yield* Evaluate_PropertyName(PropertyName, AsyncGeneratorMethod.computed); + ReturnIfAbrupt(propKey); + const scope = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const closure = X(OrdinaryFunctionCreate(surroundingAgent.intrinsic('%AsyncGeneratorFunction.prototype%'), UniqueFormalParameters, AsyncGeneratorExpression, 'non-lexical-this', scope)); + X(MakeMethod(closure, object)); + X(SetFunctionName(closure, propKey)); + const prototype = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncGenerator.prototype%'))); + X(DefinePropertyOrThrow(closure, new Value('prototype'), Descriptor({ + Value: prototype, + Writable: Value.true, + Enumerable: Value.false, + Configurable: Value.false, + }))); + closure.SourceText = sourceTextMatchedBy(AsyncGeneratorMethod); + const desc = Descriptor({ + Value: closure, + Writable: Value.true, + Enumerable: enumerable ? Value.true : Value.false, + Configurable: Value.true, + }); + return Q(DefinePropertyOrThrow(object, propKey, desc)); +} + +// (implicit) +// PropertyDefinition : MethodDefinition +// +// Note: PropertyDefinition : CoverInitializedName is an early error. +function* PropertyDefinitionEvaluation_PropertyDefinition(PropertyDefinition, object, enumerable) { + switch (true) { + case isPropertyDefinitionIdentifierReference(PropertyDefinition): + return yield* PropertyDefinitionEvaluation_PropertyDefinition_IdentifierReference( + PropertyDefinition, object, enumerable, + ); + + case isPropertyDefinitionKeyValue(PropertyDefinition): + return yield* PropertyDefinitionEvaluation_PropertyDefinition_KeyValue(PropertyDefinition, object, enumerable); + + case isMethodDefinition(PropertyDefinition): + return yield* PropertyDefinitionEvaluation_MethodDefinition(PropertyDefinition, object, enumerable); + + case isPropertyDefinitionSpread(PropertyDefinition): + return yield* PropertyDefinitionEvaluation_PropertyDefinition_Spread( + PropertyDefinition, object, enumerable, + ); + + default: + throw new OutOfRange('PropertyDefinitionEvaluation_PropertyDefinition', PropertyDefinition); + } +} diff --git a/src/engine262/src/runtime-semantics/PropertyName.mjs b/src/engine262/src/runtime-semantics/PropertyName.mjs new file mode 100644 index 0000000..4bc37ec --- /dev/null +++ b/src/engine262/src/runtime-semantics/PropertyName.mjs @@ -0,0 +1,57 @@ +import { + GetValue, + ToPropertyKey, + ToString, +} from '../abstract-ops/all.mjs'; +import { + isIdentifierName, + isNumericLiteral, + isStringLiteral, +} from '../ast.mjs'; +import { Q, X } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { Value } from '../value.mjs'; + +// 12.2.6.7 #sec-object-initializer-runtime-semantics-evaluation +// LiteralPropertyName : +// IdentifierName +// StringLiteral +// NumericLiteral +function Evaluate_LiteralPropertyName(LiteralPropertyName) { + switch (true) { + case isIdentifierName(LiteralPropertyName): + return new Value(LiteralPropertyName.name); + case isStringLiteral(LiteralPropertyName): + return new Value(LiteralPropertyName.value); + case isNumericLiteral(LiteralPropertyName): { + const nbr = new Value(LiteralPropertyName.value); + return X(ToString(nbr)); + } + + default: + throw new OutOfRange('Evaluate_LiteralPropertyName', LiteralPropertyName); + } +} + +// 12.2.6.7 #sec-object-initializer-runtime-semantics-evaluation +// ComputedPropertyName : `[` AssignmentExpression `]` +function* Evaluate_ComputedPropertyName(ComputedPropertyName) { + const AssignmentExpression = ComputedPropertyName; + const exprValue = yield* Evaluate(AssignmentExpression); + const propName = Q(GetValue(exprValue)); + return Q(ToPropertyKey(propName)); +} + +// 12.2.6.7 #sec-object-initializer-runtime-semantics-evaluation +// PropertyName : +// LiteralPropertyName +// ComputedPropertyName +// +// Note: We need some out-of-band information on whether the PropertyName is +// computed. +export function* Evaluate_PropertyName(PropertyName, computed) { + return computed + ? (yield* Evaluate_ComputedPropertyName(PropertyName)) + : Evaluate_LiteralPropertyName(PropertyName); +} diff --git a/src/engine262/src/runtime-semantics/RegExp.mjs b/src/engine262/src/runtime-semantics/RegExp.mjs new file mode 100644 index 0000000..c55c861 --- /dev/null +++ b/src/engine262/src/runtime-semantics/RegExp.mjs @@ -0,0 +1,865 @@ +import unicodeCaseFoldingCommon from 'unicode-13.0.0/Case_Folding/C/symbols.js'; +import unicodeCaseFoldingSimple from 'unicode-13.0.0/Case_Folding/S/symbols.js'; +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + IsNonNegativeInteger, +} from '../abstract-ops/all.mjs'; +import { X } from '../completion.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + isLineTerminator, + isStrWhiteSpaceChar, +} from '../grammar/util.mjs'; + +// 21.2.2.1 #sec-notation +// https://github.com/tc39/proposal-regexp-match-Indices +class Range { + constructor(startIndex, endIndex) { + this.startIndex = startIndex; + this.endIndex = endIndex; + } +} + +// 21.2.2.1 #sec-notation +export class State { + constructor(endIndex, captures) { + this.endIndex = endIndex; + this.captures = captures; + } +} + +// 21.2.2.1 #sec-notation +export function getMatcher(parsedRegex, flags) { + const { + pattern, + capturingParens, + groupSpecifiers, + } = parsedRegex; + const DotAll = flags.includes('s'); + const IgnoreCase = flags.includes('i'); + const Multiline = flags.includes('m'); + const Unicode = flags.includes('u'); + + const NcapturingParens = capturingParens.length; + const internalRegExpFlags = `${Unicode ? 'u' : ''}${IgnoreCase ? 'i' : ''}`; + + // 21.2.2.2 #sec-pattern + return function patternMatcher(str, mainIndex) { + const mainM = Evaluate_Disjunction(pattern.Disjunction, 1); + Assert(Type(str) === 'String'); + Assert(X(IsNonNegativeInteger(mainIndex)) === Value.true && mainIndex.numberValue() <= str.stringValue().length); + + // c. If Unicode is true, let Input be a List consisting of the sequence of code points of ! UTF16DecodeString(str). + // Otherwise, let Input be a List consisting of the sequence of code units that are the elements of str. + const Input = Unicode ? Array.from(str.stringValue()) : str.stringValue().split(''); + + const InputLength = Input.length; + + // d. Let listIndex be the index into Input of the character that was obtained from element index of str. + let listIndex = 0; + let seenChars = 0; + for (const char of Input) { + seenChars += char.length; + if (seenChars > mainIndex.numberValue()) { + break; + } + listIndex += 1; + } + + function mainC(y) { + Assert(y instanceof State); + return y; + } + const mainCap = new Array(NcapturingParens + 1).fill(Value.undefined); + const mainX = new State(listIndex, mainCap); + return mainM(mainX, mainC); + + // 21.2.2.3 #sec-disjunction + function Evaluate_Disjunction(Disjunction, direction) { + if (Disjunction.Alternatives.length === 1) { + const m = Evaluate_Alternative(Disjunction.Alternatives[0], direction); + return m; + } else { + const M = Disjunction.Alternatives.map((Alternative) => Evaluate_Alternative(Alternative, direction)); + return function disjunctionAlternativeDisjunctionMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + for (const m of M) { + const r = m(x, c); + if (r !== 'failure') { + return r; + } + } + return 'failure'; + }; + } + } + + // 21.2.2.4 #sec-alternative + function Evaluate_Alternative(Alternative, direction) { + if (Alternative.Terms.length === 0) { + return function alternativeEmptyMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + return c(x); + }; + } + + if (Alternative.Terms.length === 1) { + return Evaluate_Term(Alternative.Terms[0], direction); + } else { + const M = Alternative.Terms.map((Term) => Evaluate_Term(Term, direction)); + if (direction === 1) { + return function alternativePositiveDirectionMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const d = M.slice(1).reduceRight((prev, cur) => function alternativePositiveContinuation(y) { + Assert(y instanceof State); + return cur(y, prev); + }, c); + return M[0](x, d); + }; + } else { + Assert(direction === -1); + return function alternativeNegativeDirectionMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const d = M.slice(0, -1).reduce((prev, cur) => function alternativeNegativeContinuation(y) { + Assert(y instanceof State); + return cur(y, prev); + }, c); + return M[M.length - 1](x, d); + }; + } + } + } + + // 21.2.2.5 #sec-term + function Evaluate_Term(Term, direction) { + if (Term.subtype === 'Assertion') { + return Evaluate_Assertion(Term.Assertion); + } + + if (Term.subtype === 'Atom') { + return Evaluate_Atom(Term.Atom, direction); + } + + if (Term.subtype === 'AtomQuantifier') { + const m = Evaluate_Atom(Term.Atom, direction); + const { min, max, greedy } = Evaluate_Quantifier(Term.Quantifier); + Assert(!Number.isFinite(max) || max >= min); + const parenIndex = Term.capturingParensBefore; + const parenCount = Term.Atom.enclosedCapturingParens; + return function termAtomQuantifierMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + return RepeatMatcher(m, min, max, greedy, x, c, parenIndex, parenCount); + }; + } + + throw new Error('unreachable'); + } + + // 21.2.2.5.1 #sec-runtime-semantics-repeatmatcher-abstract-operation + function RepeatMatcher(m, min, max, greedy, x, c, parenIndex, parenCount) { + if (max === 0) { + return c(x); + } + + const d = function repeatMatcherContinuation(y) { + Assert(y instanceof State); + if (min === 0 && y.endIndex === x.endIndex) { + return 'failure'; + } + const min2 = min === 0 ? 0 : min - 1; + const max2 = max === Infinity ? Infinity : max - 1; + return RepeatMatcher(m, min2, max2, greedy, y, c, parenIndex, parenCount); + }; + + const cap = x.captures.slice(); + for (let k = parenIndex + 1; k <= parenIndex + parenCount; k += 1) { + cap[k] = Value.undefined; + } + const e = x.endIndex; + const xr = new State(e, cap); + if (min !== 0) { + return m(xr, d); + } + if (greedy === false) { + const z = c(x); + if (z !== 'failure') { + return z; + } + return m(xr, d); + } + const z = m(xr, d); + if (z !== 'failure') { + return z; + } + return c(x); + } + + // 21.2.2.6 #sec-assertion + function Evaluate_Assertion(Assertion) { + if (Assertion.subtype === '^') { + return function assertionStartMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const e = x.endIndex; + if (e === 0 || (Multiline === true && isLineTerminator(Input[e - 1]))) { + return c(x); + } + return 'failure'; + }; + } + + if (Assertion.subtype === '$') { + return function assertionEndMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const e = x.endIndex; + if (e === InputLength || (Multiline === true && isLineTerminator(Input[e]))) { + return c(x); + } + return 'failure'; + }; + } + + if (Assertion.subtype === '\\b') { + return function assertionWordBoundaryMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const e = x.endIndex; + const a = IsWordChar(e - 1); + const b = IsWordChar(e); + if ((a === true && b === false) || (a === false && b === true)) { + return c(x); + } + return 'failure'; + }; + } + + if (Assertion.subtype === '\\B') { + return function assertionNonWordBoundaryMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const e = x.endIndex; + const a = IsWordChar(e - 1); + const b = IsWordChar(e); + if ((a === true && b === true) || (a === false && b === false)) { + return c(x); + } + return 'failure'; + }; + } + + if (Assertion.subtype === '(?=') { + const m = Evaluate_Disjunction(Assertion.Disjunction, 1); + return function assertionPositiveLookaheadMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const d = function assertionPositiveLookaheadContinuation(y) { + Assert(y instanceof State); + return y; + }; + const r = m(x, d); + if (r === 'failure') { + return 'failure'; + } + const y = r; + const cap = y.captures; + const xe = x.endIndex; + const z = new State(xe, cap); + return c(z); + }; + } + + if (Assertion.subtype === '(?!') { + const m = Evaluate_Disjunction(Assertion.Disjunction, 1); + return function assertionNegativeLookaheadMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const d = function assertionNegativeLookaheadContinuation(y) { + Assert(y instanceof State); + return y; + }; + const r = m(x, d); + if (r !== 'failure') { + return 'failure'; + } + return c(x); + }; + } + + if (Assertion.subtype === '(?<=') { + const m = Evaluate_Disjunction(Assertion.Disjunction, -1); + return function assertionPositiveLookbehindMatcher(x, c) { + Assert(x instanceof State); + Assert(typeof c === 'function' && c.length === 1); + const d = function assertionPositiveLookbehindContinuation(y) { + Assert(y instanceof State); + return y; + }; + const r = m(x, d); + if (r === 'failure') { + return 'failure'; + } + const y = r; + const cap = y.captures; + const xe = x.endIndex; + const z = new State(xe, cap); + return c(z); + }; + } + + if (Assertion.subtype === '(? InputLength) { + return 'failure'; + } + const index = Math.min(e, f); + const ch = Input[index]; + const cc = Canonicalize(ch); + if (invert === false) { + if (!A(cc)) { + return 'failure'; + } + } else { + Assert(invert === true); + if (A(cc)) { + return 'failure'; + } + } + const cap = x.captures; + const y = new State(f, cap); + return c(y); + }; + } + + // 21.2.2.8.2 #sec-runtime-semantics-canonicalize-ch + function Canonicalize(ch) { + if (IgnoreCase === false) { + return ch; + } + + if (Unicode === true) { + if (unicodeCaseFoldingSimple.has(ch)) { + return unicodeCaseFoldingSimple.get(ch); + } + if (unicodeCaseFoldingCommon.has(ch)) { + return unicodeCaseFoldingCommon.get(ch); + } + return ch; + } else { + // Assert: ch is a UTF-16 code unit. + Assert(ch.length === 1); + const s = ch; + const u = s.toUpperCase(); + if (u.length !== 1) { + return ch; + } + const cu = u; + if (ch.codePointAt(0) >= 128 && cu.codePointAt(0) < 128) { + return ch; + } + return cu; + } + } + + // 21.2.2.9 #sec-atomescape + function Evaluate_AtomEscape(AtomEscape, direction) { + if (AtomEscape.subtype === 'DecimalEscape') { + const n = Evaluate_DecimalEscape(AtomEscape.DecimalEscape); + Assert(n <= NcapturingParens); + return BackreferenceMatcher(n, direction); + } + + if (AtomEscape.subtype === 'CharacterEscape') { + const ch = Evaluate_CharacterEscape(AtomEscape.CharacterEscape); + const A = singleCharSet(ch); + return CharacterSetMatcher(A, false, direction); + } + + if (AtomEscape.subtype === 'CharacterClassEscape') { + const A = Evaluate_CharacterClassEscape(AtomEscape.CharacterClassEscape); + return CharacterSetMatcher(A, false, direction); + } + + if (AtomEscape.subtype === 'k') { + const groupSpecifierParens = groupSpecifiers.get(AtomEscape.GroupName); + Assert(typeof groupSpecifierParens === 'number'); + const parenIndex = groupSpecifierParens; + return BackreferenceMatcher(parenIndex, direction); + } + + throw new Error('unreachable'); + } + + // 21.2.2.9.1 #sec-backreference-matcher + function BackreferenceMatcher(n, direction) { + // 1. Return a new Matcher with parameters (x, c) that captures n and direction and performs the following steps when called: + return function backreferenceMatcher(x, c) { + // a. Assert: x is a State. + Assert(x instanceof State); + // b. Assert: c is a Continuation. + Assert(typeof c === 'function' && c.length === 1); + // c. Let cap be x's captures List. + const cap = x.captures; + let f; + // https://tc39.es/proposal-regexp-match-indices/#sec-backreference-matcher + if (surroundingAgent.feature('RegExpMatchIndices')) { + // Let r be cap[n]. + const r = cap[n]; + // If r is undefined, return c(x). + if (r === Value.undefined) { + return c(x); + } + // Let e be x's endIndex. + const e = x.endIndex; + // Let rs be r's startIndex. + const rs = r.startIndex; + // Let re be r's endIndex. + const re = r.endIndex; + // Let len be re - rs. + const len = re - rs; + // Let f be e + direction × len. + f = e + direction * len; + // If f < 0 or f > InputLength, return failure. + if (f < 0 || f > InputLength) { + return 'failure'; + } + // Let g be min(e, f). + const g = Math.min(e, f); + // If there exists an integer i between 0 (inclusive) and len (exclusive) such that Canonicalize(Input[rs + i]) is not the same character value as Canonicalize(Input[g + i]), return failure. + for (let i = 0; i < len; i += 1) { + if (Canonicalize(Input[rs + i]) !== Canonicalize(Input[g + i])) { + return 'failure'; + } + } + } else { + // d. Let s be cap[n]. + const s = cap[n]; + // e. If s is undefined, return c(x). + if (s === Value.undefined) { + return c(x); + } + // f. Let e be x's endIndex. + const e = x.endIndex; + // g. Let len be the number of elements in s. + const len = s.length; + // h. Let f be e + direction × len. + f = e + direction * len; + // i. If f < 0 or f > InputLength, return failure. + if (f < 0 || f > InputLength) { + return 'failure'; + } + // j. Let g be min(e, f). + const g = Math.min(e, f); + // k. If there exists an integer i between 0 (inclusive) and len (exclusive) such that Canonicalize(s[i]) is not the same character value as Canonicalize(Input[g + i]), return failure. + for (let i = 0; i < len; i += 1) { + if (Canonicalize(s[i]) !== Canonicalize(Input[g + i])) { + return 'failure'; + } + } + } + // l. Let y be the State (f, cap). + const y = new State(f, cap); + // m. Call c(y) and return its result. + return c(y); + }; + } + + // 21.2.2.10 #sec-characterescape + function Evaluate_CharacterEscape(CharacterEscape) { + return CharacterEscape.CharacterValue; + } + + // 21.2.2.11 #sec-decimalescape + function Evaluate_DecimalEscape(DecimalEscape) { + return DecimalEscape.CapturingGroupNumber; + } + + // 21.2.2.12 #sec-characterclassescape + function Evaluate_CharacterClassEscape(CharacterClassEscape) { + if (CharacterClassEscape.subtype === 'd') { + return numberCharSet(); + } + + if (CharacterClassEscape.subtype === 'D') { + return invertCharSet(numberCharSet()); + } + + if (CharacterClassEscape.subtype === 's') { + return whitespaceCharSet(); + } + + if (CharacterClassEscape.subtype === 'S') { + return invertCharSet(whitespaceCharSet()); + } + + if (CharacterClassEscape.subtype === 'w') { + return WordCharacters(); + } + + if (CharacterClassEscape.subtype === 'W') { + return invertCharSet(WordCharacters()); + } + + if (CharacterClassEscape.subtype === 'p{') { + return Evaluate_UnicodePropertyValueExpression(CharacterClassEscape.UnicodePropertyValueExpression); + } + + if (CharacterClassEscape.subtype === 'P{') { + return invertCharSet(Evaluate_UnicodePropertyValueExpression(CharacterClassEscape.UnicodePropertyValueExpression)); + } + + throw new Error('unreachable'); + } + + function Evaluate_UnicodePropertyValueExpression(UnicodePropertyValueExpression) { + let value; + if (UnicodePropertyValueExpression.subtype === 'UnicodePropertyNameAndValue') { + value = `${UnicodePropertyValueExpression.UnicodePropertyName}=${UnicodePropertyValueExpression.UnicodePropertyValue}`; + } else if (UnicodePropertyValueExpression.subtype === 'LoneUnicodePropertyNameOrValue') { + value = UnicodePropertyValueExpression.LoneUnicodePropertyNameOrValue; + } + + const regexp = new RegExp(`^\\p{${value}}$`, internalRegExpFlags); + return function testUnicodePropertyValue(cc) { + return regexp.test(cc); + }; + } + + // 21.2.2.13 #sec-characterclass + function Evaluate_CharacterClass(CharacterClass) { + if (!CharacterClass.invert) { + const A = Evaluate_ClassRanges(CharacterClass.ClassRanges); + return { A, invert: false }; + } else { + const A = Evaluate_ClassRanges(CharacterClass.ClassRanges); + return { A, invert: true }; + } + } + + // 21.2.2.14 #sec-classranges + function Evaluate_ClassRanges(ClassRanges) { + if (ClassRanges.length === 0) { + return emptyCharSet(); + } + + const charSets = ClassRanges.map((range) => { + if (Array.isArray(range)) { + if (range.length === 2) { + const classAtom1 = getClassAtom(range[0]); + const classAtom2 = getClassAtom(range[1]); + return CharacterRange(classAtom1, classAtom2); + } + } else { + return classRangeAtomCharSet(range); + } + throw new Error('unreachable'); + }); + + return combinedCharSet(charSets); + } + + // 21.2.2.15.1 #sec-runtime-semantics-characterrange-abstract-operation + function CharacterRange(A, B) { + // 1. Assert: A and B each contain exactly one character. + Assert(typeof A === 'number' && typeof B === 'number'); + const i = A; + const j = B; + Assert(i <= j); + const set = new Set(); + for (let codePoint = A; codePoint <= B; codePoint += 1) { + set.add(Canonicalize(String.fromCodePoint(codePoint))); + } + return function testCharacterRange(cc) { + return set.has(cc); + }; + } + + // 21.2.2.19 #sec-classescape + function Evaluate_ClassEscape(ClassEscape) { + if (ClassEscape.subtype === 'b') { + return '\b'.charCodeAt(0); + } + + if (ClassEscape.subtype === '-') { + return '-'.charCodeAt(0); + } + + if (ClassEscape.subtype === 'CharacterEscape') { + return Evaluate_CharacterEscape(ClassEscape.CharacterEscape); + } + + if (ClassEscape.subtype === 'CharacterClassEscape') { + return Evaluate_CharacterClassEscape(ClassEscape.CharacterClassEscape); + } + + throw new Error('unreachable'); + } + + function singleCharSet(char) { + char = Canonicalize(String.fromCodePoint(char)); + return function testSingleCharSet(cc) { + return char === cc; + }; + } + + function allCharSet() { + return function testAllCharSet() { + return true; + }; + } + + function noLineTerminatorCharSet() { + return function testNoLineTerminatorCharSet(cc) { + return !isLineTerminator(cc); + }; + } + + function numberCharSet() { + return function testNumberCharSet(cc) { + return /[0-9]/.test(cc); + }; + } + + function whitespaceCharSet() { + return function testWhitespaceCharSet(cc) { + return isStrWhiteSpaceChar(cc); + }; + } + + function emptyCharSet() { + return function testEmptyCharSet() { + return false; + }; + } + + function invertCharSet(charSet) { + return function testInvertCharSet(cc) { + return !charSet(cc); + }; + } + + function combinedCharSet(charSets) { + return function testCombinedCharSet(cc) { + return charSets.some((charSet) => charSet(cc)); + }; + } + + function classRangeAtomCharSet(classRange) { + const classAtom = getClassAtom(classRange); + if (typeof classAtom === 'function') { + return classAtom; + } + return singleCharSet(classAtom); + } + + function getClassAtom(ClassAtom) { + if (ClassAtom.subtype === 'character') { + return ClassAtom.character; + } else { + return Evaluate_ClassEscape(ClassAtom.ClassEscape); + } + } + }; +} diff --git a/src/engine262/src/runtime-semantics/RegularExpressionLiteral.mjs b/src/engine262/src/runtime-semantics/RegularExpressionLiteral.mjs new file mode 100644 index 0000000..25384b2 --- /dev/null +++ b/src/engine262/src/runtime-semantics/RegularExpressionLiteral.mjs @@ -0,0 +1,11 @@ +import { Value } from '../value.mjs'; +import { RegExpCreate } from '../abstract-ops/all.mjs'; + +// 12.2.8.2 #sec-regular-expression-literals-runtime-semantics-evaluation +// (implicit) +// PrimaryExpression : RegularExpressionLiteral +export function Evaluate_RegularExpressionLiteral(RegularExpressionLiteral) { + const pattern = new Value(RegularExpressionLiteral.regex.pattern); + const flags = new Value(RegularExpressionLiteral.regex.flags); + return RegExpCreate(pattern, flags); +} diff --git a/src/engine262/src/runtime-semantics/RelationalOperators.mjs b/src/engine262/src/runtime-semantics/RelationalOperators.mjs new file mode 100644 index 0000000..fa770d4 --- /dev/null +++ b/src/engine262/src/runtime-semantics/RelationalOperators.mjs @@ -0,0 +1,94 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + AbstractRelationalComparison, + Call, + GetMethod, + GetValue, + HasProperty, + IsCallable, + OrdinaryHasInstance, + ToBoolean, + ToPropertyKey, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, + wellKnownSymbols, +} from '../value.mjs'; +import { Q, ReturnIfAbrupt } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +export function InstanceofOperator(V, target) { + if (Type(target) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', target); + } + const instOfHandler = Q(GetMethod(target, wellKnownSymbols.hasInstance)); + if (Type(instOfHandler) !== 'Undefined') { + return ToBoolean(Q(Call(instOfHandler, target, [V]))); + } + if (IsCallable(target) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAFunction', target); + } + return Q(OrdinaryHasInstance(target, V)); +} + +export function* Evaluate_RelationalExpression({ + left: RelationalExpression, + right: ShiftExpression, + operator, +}) { + const lref = yield* Evaluate(RelationalExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(ShiftExpression); + const rval = Q(GetValue(rref)); + + switch (operator) { + case '<': { + const r = AbstractRelationalComparison(lval, rval); + ReturnIfAbrupt(r); + if (Type(r) === 'Undefined') { + return Value.false; + } + return r; + } + case '>': { + const r = AbstractRelationalComparison(rval, lval, false); + ReturnIfAbrupt(r); + if (Type(r) === 'Undefined') { + return Value.false; + } + return r; + } + case '<=': { + const r = AbstractRelationalComparison(rval, lval, false); + ReturnIfAbrupt(r); + if (Type(r) === 'Undefined' || r === Value.true) { + return Value.false; + } + return Value.true; + } + case '>=': { + const r = AbstractRelationalComparison(lval, rval); + ReturnIfAbrupt(r); + if (Type(r) === 'Undefined' || r === Value.true) { + return Value.false; + } + return Value.true; + } + + case 'instanceof': + return Q(InstanceofOperator(lval, rval)); + + case 'in': + if (Type(rval) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', rval); + } + return Q(HasProperty(rval, ToPropertyKey(lval))); + + default: + throw new OutOfRange('Evaluate_RelationalExpression', operator); + } +} diff --git a/src/engine262/src/runtime-semantics/RestBindingInitialization.mjs b/src/engine262/src/runtime-semantics/RestBindingInitialization.mjs new file mode 100644 index 0000000..6c42c40 --- /dev/null +++ b/src/engine262/src/runtime-semantics/RestBindingInitialization.mjs @@ -0,0 +1,32 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + CopyDataProperties, + InitializeReferencedBinding, + OrdinaryObjectCreate, + PutValue, + ResolveBinding, +} from '../abstract-ops/all.mjs'; +import { + Type, + Value, +} from '../value.mjs'; +import { + Q, +} from '../completion.mjs'; + +// 13.3.3.7 #sec-destructuring-binding-patterns-runtime-semantics-restbindinginitialization +// BindingRestProperty : `...` BindingIdentifier +export function RestBindingInitialization_BindingRestProperty( + BindingRestProperty, value, environment, excludedNames, +) { + const BindingIdentifier = BindingRestProperty.argument; + const lhs = Q(ResolveBinding(new Value(BindingIdentifier.name), environment, BindingIdentifier.strict)); + const restObj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); + Q(CopyDataProperties(restObj, value, excludedNames)); + if (Type(environment) === 'Undefined') { + return PutValue(lhs, restObj); + } + return InitializeReferencedBinding(lhs, restObj); +} diff --git a/src/engine262/src/runtime-semantics/ReturnStatement.mjs b/src/engine262/src/runtime-semantics/ReturnStatement.mjs new file mode 100644 index 0000000..eb72ff3 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ReturnStatement.mjs @@ -0,0 +1,27 @@ +import { Value } from '../value.mjs'; +import { + Await, Q, + ReturnCompletion, + X, +} from '../completion.mjs'; +import { + GetGeneratorKind, + GetValue, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; + +// 13.10.1 #sec-return-statement-runtime-semantics-evaluation +export function* Evaluate_ReturnStatement({ argument: Expression }) { + if (Expression === null) { + // ReturnStatement : return `;` + return new ReturnCompletion(Value.undefined); + } else { + // ReturnStatement : return Expression `;` + const exprRef = yield* Evaluate(Expression); + let exprValue = Q(GetValue(exprRef)); + if (X(GetGeneratorKind()) === 'async') { + exprValue = Q(yield* Await(exprValue)); + } + return new ReturnCompletion(exprValue); + } +} diff --git a/src/engine262/src/runtime-semantics/ShiftExpression.mjs b/src/engine262/src/runtime-semantics/ShiftExpression.mjs new file mode 100644 index 0000000..35a73f1 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ShiftExpression.mjs @@ -0,0 +1,47 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q } from '../completion.mjs'; +import { GetValue, ToNumeric } from '../abstract-ops/all.mjs'; +import { Type, TypeNumeric } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +/* eslint-disable no-bitwise */ + +export function EvaluateBinopValues_ShiftExpression(operator, lval, rval) { + const lnum = Q(ToNumeric(lval)); + const rnum = Q(ToNumeric(rval)); + if (Type(lnum) !== Type(rnum)) { + return surroundingAgent.Throw('TypeError', 'CannotMixBigInts'); + } + const T = TypeNumeric(lnum); + + switch (operator) { + case '<<': + return T.leftShift(lnum, rnum); + + case '>>': + return T.signedRightShift(lnum, rnum); + + case '>>>': + return T.unsignedRightShift(lnum, rnum); + + default: + throw new OutOfRange('EvaluateBinopValues_ShiftExpression', operator); + } +} + +// ShiftExpression : +// ShiftExpression << AdditiveExpression +// ShiftExpression >> AdditiveExpression +// ShiftExpression >>> AdditiveExpression +export function* Evaluate_ShiftExpression({ + left: ShiftExpression, + operator, + right: AdditiveExpression, +}) { + const lref = yield* Evaluate(ShiftExpression); + const lval = Q(GetValue(lref)); + const rref = yield* Evaluate(AdditiveExpression); + const rval = Q(GetValue(rref)); + return EvaluateBinopValues_ShiftExpression(operator, lval, rval); +} diff --git a/src/engine262/src/runtime-semantics/StringIndexOf.mjs b/src/engine262/src/runtime-semantics/StringIndexOf.mjs new file mode 100644 index 0000000..18192b7 --- /dev/null +++ b/src/engine262/src/runtime-semantics/StringIndexOf.mjs @@ -0,0 +1,48 @@ +import { + Type, + Value, +} from '../value.mjs'; +import { + Assert, +} from '../abstract-ops/all.mjs'; + +// https://tc39.es/proposal-string-replaceall/#sec-stringindexof +export function StringIndexOf(string, searchValue, fromIndex) { + // 1. Assert: Type(string) is String. + Assert(Type(string) === 'String'); + // 2. Assert: Type(searchValue) is String. + Assert(Type(searchValue) === 'String'); + // 3. Assert: fromIndex is a nonnegative integer. + Assert(Number.isInteger(fromIndex) && fromIndex >= 0); + const stringStr = string.stringValue(); + const searchStr = searchValue.stringValue(); + // 4. Let len be the length of string. + const len = stringStr.length; + // 5. If searchValue is the empty string, and fromIndex <= len, return fromIndex. + if (searchStr === '' && fromIndex <= len) { + return new Value(fromIndex); + } + // 6. Let searchLen be the length of searchValue. + const searchLen = searchStr.length; + // 7. If there exists any integer k such that fromIndex ≤ k ≤ len - searchLen and for all nonnegative integers j less than searchLen, + // the code unit at index k + j within string is the same as the code unit at index j within searchValue, let pos be the smallest (closest to -∞) such integer. + // Otherwise, let pos be -1. + let k = fromIndex; + let pos = -1; + while (k + searchLen <= len) { + let match = true; + for (let j = 0; j < searchLen; j += 1) { + if (searchStr[j] !== stringStr[k + j]) { + match = false; + break; + } + } + if (match) { + pos = k; + break; + } + k += 1; + } + // 8. Return pos. + return new Value(pos); +} diff --git a/src/engine262/src/runtime-semantics/StringPad.mjs b/src/engine262/src/runtime-semantics/StringPad.mjs new file mode 100644 index 0000000..e69912c --- /dev/null +++ b/src/engine262/src/runtime-semantics/StringPad.mjs @@ -0,0 +1,31 @@ +import { Value } from '../value.mjs'; +import { Assert, ToString, ToLength } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; + +// 21.1.3.15.1 #sec-stringpad +export function StringPad(O, maxLength, fillString, placement) { + Assert(placement === 'start' || placement === 'end'); + const S = Q(ToString(O)); + const intMaxLength = Q(ToLength(maxLength)).numberValue(); + const stringLength = S.stringValue().length; + if (intMaxLength <= stringLength) { + return S; + } + let filler; + if (fillString === Value.undefined) { + filler = ' '; + } else { + filler = Q(ToString(fillString)).stringValue(); + } + if (filler === '') { + return S; + } + const fillLen = intMaxLength - stringLength; + const stringFiller = filler.repeat(Math.ceil(fillLen / filler.length)); + const truncatedStringFiller = stringFiller.slice(0, fillLen); + if (placement === 'start') { + return new Value(truncatedStringFiller + S.stringValue()); + } else { + return new Value(S.stringValue() + truncatedStringFiller); + } +} diff --git a/src/engine262/src/runtime-semantics/SuperCall.mjs b/src/engine262/src/runtime-semantics/SuperCall.mjs new file mode 100644 index 0000000..e5e9902 --- /dev/null +++ b/src/engine262/src/runtime-semantics/SuperCall.mjs @@ -0,0 +1,39 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + Assert, + Construct, + GetNewTarget, + GetThisEnvironment, + IsConstructor, + isECMAScriptFunctionObject, +} from '../abstract-ops/all.mjs'; +import { Type, Value } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; +import { FunctionEnvironmentRecord } from '../environment.mjs'; +import { ArgumentListEvaluation } from './all.mjs'; + + +// 12.3.5.2 #sec-getsuperconstructor +function GetSuperConstructor() { + const envRec = GetThisEnvironment(); + Assert(envRec instanceof FunctionEnvironmentRecord); + const activeFunction = envRec.FunctionObject; + Assert(isECMAScriptFunctionObject(activeFunction)); + const superConstructor = X(activeFunction.GetPrototypeOf()); + return superConstructor; +} + +// 12.3.5.1 #sec-super-keyword-runtime-semantics-evaluation +// SuperCall : `super` Arguments +export function* Evaluate_SuperCall({ arguments: Arguments }) { + const newTarget = GetNewTarget(); + Assert(Type(newTarget) === 'Object'); + const func = X(GetSuperConstructor()); + const argList = Q(yield* ArgumentListEvaluation(Arguments)); + if (IsConstructor(func) === Value.false) { + return surroundingAgent.Throw('TypeError', 'NotAConstructor', func); + } + const result = Q(Construct(func, argList, newTarget)); + const thisER = GetThisEnvironment(); + return Q(thisER.BindThisValue(result)); +} diff --git a/src/engine262/src/runtime-semantics/SuperProperty.mjs b/src/engine262/src/runtime-semantics/SuperProperty.mjs new file mode 100644 index 0000000..b1704c8 --- /dev/null +++ b/src/engine262/src/runtime-semantics/SuperProperty.mjs @@ -0,0 +1,50 @@ +import { Evaluate } from '../evaluator.mjs'; +import { + Assert, + GetThisEnvironment, + GetValue, + RequireObjectCoercible, + ToPropertyKey, +} from '../abstract-ops/all.mjs'; +import { SuperReference, Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; + +// 12.3.5.3 #sec-makesuperpropertyreference +function MakeSuperPropertyReference(actualThis, propertyKey, strict) { + const env = GetThisEnvironment(); + Assert(env.HasSuperBinding() === Value.true); + const baseValue = Q(env.GetSuperBase()); + const bv = Q(RequireObjectCoercible(baseValue)); + return new SuperReference({ + BaseValue: bv, + ReferencedName: propertyKey, + thisValue: actualThis, + StrictReference: strict ? Value.true : Value.false, + }); +} + +// 12.3.5.1 #sec-super-keyword-runtime-semantics-evaluation +// SuperProperty : +// `super` `[` Expression `]` +// `super` `.` IdentifierName +export function* Evaluate_SuperProperty(SuperProperty) { + if (SuperProperty.computed) { + const Expression = SuperProperty.property; + + const env = GetThisEnvironment(); + const actualThis = Q(env.GetThisBinding()); + const propertyNameReference = yield* Evaluate(Expression); + const propertyNameValue = Q(GetValue(propertyNameReference)); + const propertyKey = Q(ToPropertyKey(propertyNameValue)); + const strict = SuperProperty.strict; + return Q(MakeSuperPropertyReference(actualThis, propertyKey, strict)); + } else { + const IdentifierName = SuperProperty.property; + + const env = GetThisEnvironment(); + const actualThis = Q(env.GetThisBinding()); + const propertyKey = new Value(IdentifierName.name); + const strict = SuperProperty.strict; + return Q(MakeSuperPropertyReference(actualThis, propertyKey, strict)); + } +} diff --git a/src/engine262/src/runtime-semantics/SwitchStatement.mjs b/src/engine262/src/runtime-semantics/SwitchStatement.mjs new file mode 100644 index 0000000..d6add5b --- /dev/null +++ b/src/engine262/src/runtime-semantics/SwitchStatement.mjs @@ -0,0 +1,146 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate, Evaluate_StatementList } from '../evaluator.mjs'; +import { NewDeclarativeEnvironment } from '../environment.mjs'; +import { GetValue, StrictEqualityComparison } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { + AbruptCompletion, + Completion, + EnsureCompletion, + NormalCompletion, + Q, + UpdateEmpty, +} from '../completion.mjs'; +import { BlockDeclarationInstantiation } from './BlockStatement.mjs'; + +// 13.12.10 #sec-runtime-semantics-caseclauseisselected +function* CaseClauseIsSelected(C, input) { + // Assert: C is an instance of the production CaseClause : `case` Expression : StatementList. + const exprRef = yield* Evaluate(C.test); + const clauseSelector = Q(GetValue(exprRef)); + return StrictEqualityComparison(input, clauseSelector); +} + +// 13.12.9 #sec-runtime-semantics-caseblockevaluation +// CaseBlock : +// `{` `}` +// `{` CaseClauses `}` +// `{` CaseClauses DefaultClause CaseClauses `}` +function* CaseBlockEvaluation(CaseBlock, input) { + if (CaseBlock.length === 0) { + return new NormalCompletion(Value.undefined); + } + + const defaultIndex = CaseBlock.findIndex((c) => c.test === null); + if (defaultIndex !== -1) { + // CaseBlock : `{` CaseClauses DefaultClause CaseClauses `}` + const firstCaseClauses = CaseBlock.slice(0, defaultIndex); + const secondCaseClauses = CaseBlock.slice(defaultIndex + 1); + const DefaultClause = CaseBlock[defaultIndex]; + + let V = Value.undefined; + let A; + if (firstCaseClauses.length > 0) { + A = firstCaseClauses; + } else { + A = []; + } + let found = false; + for (const C of A) { + if (found === false) { + found = Q(yield* CaseClauseIsSelected(C, input)) === Value.true; + } + if (found === true) { + const R = EnsureCompletion(yield* Evaluate_StatementList(C.consequent)); + if (R.Value !== undefined) { + V = R.Value; + } + if (R instanceof AbruptCompletion) { + return Completion(UpdateEmpty(R, V)); + } + } + } + let foundInB = false; + let B; + if (secondCaseClauses.length > 0) { + B = secondCaseClauses; + } else { + B = []; + } + if (found === false) { + for (const C of B) { + if (foundInB === false) { + foundInB = Q(yield* CaseClauseIsSelected(C, input)) === Value.true; + } + if (foundInB === true) { + const R = EnsureCompletion(yield* Evaluate_StatementList(C.consequent)); + if (R.Value !== undefined) { + V = R.Value; + } + if (R instanceof AbruptCompletion) { + return Completion(UpdateEmpty(R, V)); + } + } + } + } + if (foundInB === true) { + return new NormalCompletion(V); + } + const R = EnsureCompletion(yield* Evaluate_StatementList(DefaultClause.consequent)); + if (R.Value !== undefined) { + V = R.Value; + } + if (R instanceof AbruptCompletion) { + return Completion(UpdateEmpty(R, V)); + } + for (const C of B) { + const R = EnsureCompletion(yield* Evaluate_StatementList(C.consequent)); // eslint-disable-line no-shadow + if (R.Value !== undefined) { + V = R.Value; + } + if (R instanceof AbruptCompletion) { + return Completion(UpdateEmpty(R, V)); + } + } + return new NormalCompletion(V); + } else { + // CaseBlock : `{` CaseClauses `}` + let V = Value.undefined; + // Let A be the List of CaseClause items in CaseClauses, in source text order. + const A = CaseBlock; + let found = false; + for (const C of A) { + if (found === false) { + found = Q(yield* CaseClauseIsSelected(C, input)) === Value.true; + } + if (found === true) { + const R = EnsureCompletion(yield* Evaluate_StatementList(C.consequent)); + if (R.Value !== undefined) { + V = R.Value; + } + if (R instanceof AbruptCompletion) { + return Completion(UpdateEmpty(R, V)); + } + } + } + + return new NormalCompletion(V); + } +} + +// 13.12.11 #sec-switch-statement-runtime-semantics-evaluation +// SwitchStatement : `switch` `(` Expression `)` CaseBlock +export function* Evaluate_SwitchStatement({ + discriminant: Expression, + cases: CaseBlock, +}) { + const exprRef = yield* Evaluate(Expression); + const switchValue = Q(GetValue(exprRef)); + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const blockEnv = NewDeclarativeEnvironment(oldEnv); + BlockDeclarationInstantiation(CaseBlock, blockEnv); + surroundingAgent.runningExecutionContext.LexicalEnvironment = blockEnv; + const R = yield* CaseBlockEvaluation(CaseBlock, switchValue); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return R; +} diff --git a/src/engine262/src/runtime-semantics/TaggedTemplate.mjs b/src/engine262/src/runtime-semantics/TaggedTemplate.mjs new file mode 100644 index 0000000..4e55301 --- /dev/null +++ b/src/engine262/src/runtime-semantics/TaggedTemplate.mjs @@ -0,0 +1,71 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Descriptor, Value } from '../value.mjs'; +import { + ArrayCreate, + Assert, + GetValue, + SetIntegrityLevel, + ToString, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { IsInTailPosition, TemplateStrings_TemplateLiteral } from '../static-semantics/all.mjs'; +import { Q, X } from '../completion.mjs'; +import { EvaluateCall } from './all.mjs'; + +// 12.2.9.4 #sec-gettemplateobject +export function GetTemplateObject(templateLiteral) { + const rawStrings = TemplateStrings_TemplateLiteral(templateLiteral, true).map(Value); + const realm = surroundingAgent.currentRealmRecord; + const templateRegistry = realm.TemplateMap; + for (const e of templateRegistry) { + if (e.Site === templateLiteral) { + return e.Array; + } + } + const cookedStrings = TemplateStrings_TemplateLiteral(templateLiteral, false).map((v) => (v === undefined ? Value.undefined : new Value(v))); + const count = cookedStrings.length; + Assert(count < (2 ** 32) - 1); + const template = X(ArrayCreate(new Value(count))); + const rawObj = X(ArrayCreate(new Value(count))); + let index = 0; + while (index < count) { + const prop = X(ToString(new Value(index))); + const cookedValue = cookedStrings[index]; + X(template.DefineOwnProperty(prop, Descriptor({ + Value: cookedValue, + Writable: Value.false, + Enumerable: Value.true, + Configurable: Value.false, + }))); + const rawValue = rawStrings[index]; + X(rawObj.DefineOwnProperty(prop, Descriptor({ + Value: rawValue, + Writable: Value.false, + Enumerable: Value.true, + Configurable: Value.false, + }))); + index += 1; + } + X(SetIntegrityLevel(rawObj, 'frozen')); + X(template.DefineOwnProperty(new Value('raw'), Descriptor({ + Value: rawObj, + Writable: Value.false, + Enumerable: Value.false, + Configurable: Value.false, + }))); + X(SetIntegrityLevel(template, 'frozen')); + templateRegistry.push({ Site: templateLiteral, Array: template }); + return template; +} + +// 12.3.8.1 #sec-tagged-templates-runtime-semantics-evaluation +export function* Evaluate_TaggedTemplate({ + tag: Expression, + quasi: TemplateLiteral, +}) { + const tagRef = yield* Evaluate(Expression); + const tagFunc = Q(GetValue(tagRef)); + const thisCall = Expression; + const tailCall = IsInTailPosition(thisCall); + return Q(yield* EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall)); +} diff --git a/src/engine262/src/runtime-semantics/TemplateLiteral.mjs b/src/engine262/src/runtime-semantics/TemplateLiteral.mjs new file mode 100644 index 0000000..b82c869 --- /dev/null +++ b/src/engine262/src/runtime-semantics/TemplateLiteral.mjs @@ -0,0 +1,34 @@ +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; +import { + GetValue, + ToString, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; + +// 12.2.9.6 #sec-template-literals-runtime-semantics-evaluation +// TemplateLiteral : NoSubstitutionTemplate +// SubstitutionTemplate : TemplateHead Expression TemplateSpans +// TemplateSpans : TemplateTail +// TemplateSpans : TemplateMiddleList TemplateTail +// TemplateMiddleList : TemplateMiddle Expression +// TemplateMiddleList : TemplateMiddleList TemplateMiddle Expression +// +// (implicit) +// TemplateLiteral : SubstitutionTemplate +export function* Evaluate_TemplateLiteral(TemplateLiteral) { + let str = ''; + for (let i = 0; i < TemplateLiteral.quasis.length - 1; i += 1) { + const TemplateHead = TemplateLiteral.quasis[i]; + const Expression = TemplateLiteral.expressions[i]; + const head = TemplateHead.value.cooked; + const subRef = yield* Evaluate(Expression); + const sub = Q(GetValue(subRef)); + const middle = Q(ToString(sub)); + str += head; + str += middle.stringValue(); + } + const TemplateTail = TemplateLiteral.quasis[TemplateLiteral.quasis.length - 1]; + const tail = TemplateTail.value.cooked; + return new Value(str + tail); +} diff --git a/src/engine262/src/runtime-semantics/ThisExpression.mjs b/src/engine262/src/runtime-semantics/ThisExpression.mjs new file mode 100644 index 0000000..7875c45 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ThisExpression.mjs @@ -0,0 +1,8 @@ +import { ResolveThisBinding } from '../abstract-ops/all.mjs'; +import { Q } from '../completion.mjs'; + +// 12.2.2 #sec-this-keyword +// PrimaryExpression : this +export function Evaluate_ThisExpression() { + return Q(ResolveThisBinding()); +} diff --git a/src/engine262/src/runtime-semantics/ThrowStatement.mjs b/src/engine262/src/runtime-semantics/ThrowStatement.mjs new file mode 100644 index 0000000..e53ca62 --- /dev/null +++ b/src/engine262/src/runtime-semantics/ThrowStatement.mjs @@ -0,0 +1,17 @@ +import { + Evaluate, +} from '../evaluator.mjs'; +import { + GetValue, +} from '../abstract-ops/all.mjs'; +import { + Q, + ThrowCompletion, +} from '../completion.mjs'; + +// ThrowStatement : throw Expression ; +export function* Evaluate_ThrowStatement(Expression) { + const exprRef = yield* Evaluate(Expression); + const exprValue = Q(GetValue(exprRef)); + return new ThrowCompletion(exprValue); +} diff --git a/src/engine262/src/runtime-semantics/TrimString.mjs b/src/engine262/src/runtime-semantics/TrimString.mjs new file mode 100644 index 0000000..f554a82 --- /dev/null +++ b/src/engine262/src/runtime-semantics/TrimString.mjs @@ -0,0 +1,19 @@ +import { Assert, RequireObjectCoercible, ToString } from '../abstract-ops/all.mjs'; +import { Value } from '../value.mjs'; +import { Q } from '../completion.mjs'; + +// 21.1.3.28.1 #sec-trimstring +export function TrimString(string, where) { + const str = Q(RequireObjectCoercible(string)); + const S = Q(ToString(str)).stringValue(); + let T; + if (where === 'start') { + T = S.trimStart(); + } else if (where === 'end') { + T = S.trimEnd(); + } else { + Assert(where === 'start+end'); + T = S.trim(); + } + return new Value(T); +} diff --git a/src/engine262/src/runtime-semantics/TryStatement.mjs b/src/engine262/src/runtime-semantics/TryStatement.mjs new file mode 100644 index 0000000..566563c --- /dev/null +++ b/src/engine262/src/runtime-semantics/TryStatement.mjs @@ -0,0 +1,118 @@ +import { + surroundingAgent, +} from '../engine.mjs'; +import { + isTryStatementWithCatch, + isTryStatementWithFinally, +} from '../ast.mjs'; +import { + BoundNames_CatchParameter, +} from '../static-semantics/all.mjs'; +import { + Value, +} from '../value.mjs'; +import { + AbruptCompletion, + Completion, + EnsureCompletion, + UpdateEmpty, + X, +} from '../completion.mjs'; +import { + NewDeclarativeEnvironment, +} from '../environment.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + BindingInitialization_CatchParameter, + Evaluate_Block, +} from './all.mjs'; + +// 13.15.7 #sec-runtime-semantics-catchclauseevaluation +// With parameter thrownValue. +// Catch : +// `catch` `(` CatchParameter `)` Block +// `catch` Block +function* CatchClauseEvaluation({ param: CatchParameter, body: Block }, thrownValue) { + if (!CatchParameter) { + // Catch : `catch` Block + return yield* Evaluate_Block(Block); + } + + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const catchEnv = NewDeclarativeEnvironment(oldEnv); + const catchEnvRec = catchEnv.EnvironmentRecord; + for (const argName of BoundNames_CatchParameter(CatchParameter)) { + X(catchEnvRec.CreateMutableBinding(new Value(argName), false)); + } + surroundingAgent.runningExecutionContext.LexicalEnvironment = catchEnv; + const status = yield* BindingInitialization_CatchParameter(CatchParameter, thrownValue, catchEnv); + if (status instanceof AbruptCompletion) { + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return status; + } + const B = yield* Evaluate_Block(Block); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return B; +} + +// (implicit) +// Finally : `finally` Block +const Evaluate_Finally = Evaluate_Block; + +// 13.15.8 #sec-try-statement-runtime-semantics-evaluation +// TryStatement : `try` Block Catch +function* Evaluate_TryStatement_Catch(Block, Catch) { + const B = EnsureCompletion(yield* Evaluate_Block(Block)); + let C; + if (B.Type === 'throw') { + C = EnsureCompletion(yield* CatchClauseEvaluation(Catch, B.Value)); + } else { + C = B; + } + return Completion(UpdateEmpty(C, Value.undefined)); +} + +// 13.15.8 #sec-try-statement-runtime-semantics-evaluation +// TryStatement : `try` Block Finally +function* Evaluate_TryStatement_Finally(Block, Finally) { + const B = EnsureCompletion(yield* Evaluate_Block(Block)); + let F = EnsureCompletion(yield* Evaluate_Finally(Finally)); + if (F.Type === 'normal') { + F = B; + } + return Completion(UpdateEmpty(F, Value.undefined)); +} + +// 13.15.8 #sec-try-statement-runtime-semantics-evaluation +// TryStatement : `try` Block Catch Finally +function* Evaluate_TryStatement_CatchFinally(Block, Catch, Finally) { + const B = EnsureCompletion(yield* Evaluate_Block(Block)); + let C; + if (B.Type === 'throw') { + C = EnsureCompletion(yield* CatchClauseEvaluation(Catch, B.Value)); + } else { + C = B; + } + let F = EnsureCompletion(yield* Evaluate_Finally(Finally)); + if (F.Type === 'normal') { + F = C; + } + return Completion(UpdateEmpty(F, Value.undefined)); +} + +// 13.15.8 #sec-try-statement-runtime-semantics-evaluation +export function* Evaluate_TryStatement(Expression) { + switch (true) { + case isTryStatementWithCatch(Expression) && isTryStatementWithFinally(Expression): + return yield* Evaluate_TryStatement_CatchFinally( + Expression.block, Expression.handler, Expression.finalizer, + ); + case isTryStatementWithCatch(Expression): + return yield* Evaluate_TryStatement_Catch(Expression.block, Expression.handler); + case isTryStatementWithFinally(Expression): + return yield* Evaluate_TryStatement_Finally(Expression.block, Expression.finalizer); + + default: + throw new OutOfRange('Evaluate_TryStatement', Expression); + } +} diff --git a/src/engine262/src/runtime-semantics/UnaryExpression.mjs b/src/engine262/src/runtime-semantics/UnaryExpression.mjs new file mode 100644 index 0000000..c3043a4 --- /dev/null +++ b/src/engine262/src/runtime-semantics/UnaryExpression.mjs @@ -0,0 +1,172 @@ +import { surroundingAgent } from '../engine.mjs'; +import { + isUnaryExpressionWithBang, + isUnaryExpressionWithDelete, + isUnaryExpressionWithMinus, + isUnaryExpressionWithPlus, + isUnaryExpressionWithTilde, + isUnaryExpressionWithTypeof, + isUnaryExpressionWithVoid, +} from '../ast.mjs'; +import { + Assert, + GetBase, + GetReferencedName, + GetValue, + IsCallable, + IsPropertyReference, + IsStrictReference, + IsSuperReference, + IsUnresolvableReference, + ToBoolean, + ToNumber, + ToObject, + ToNumeric, +} from '../abstract-ops/all.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { Q, ReturnIfAbrupt, X } from '../completion.mjs'; +import { Type, TypeNumeric, Value } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 12.5.3.2 #sec-delete-operator-runtime-semantics-evaluation +// UnaryExpression : `delete` UnaryExpression +function* Evaluate_UnaryExpression_Delete(UnaryExpression) { + const ref = yield* Evaluate(UnaryExpression); + ReturnIfAbrupt(ref); + if (Type(ref) !== 'Reference') { + return Value.true; + } + if (IsUnresolvableReference(ref) === Value.true) { + Assert(IsStrictReference(ref) === Value.false); + return Value.true; + } + if (IsPropertyReference(ref) === Value.true) { + if (IsSuperReference(ref) === Value.true) { + return surroundingAgent.Throw('ReferenceError', 'CannotDeleteSuper'); + } + const baseObj = X(ToObject(GetBase(ref))); + const deleteStatus = Q(baseObj.Delete(GetReferencedName(ref))); + if (deleteStatus === Value.false && IsStrictReference(ref) === Value.true) { + return surroundingAgent.Throw('TypeError', 'StrictModeDelete', GetReferencedName(ref)); + } + return deleteStatus; + } else { + const bindings = GetBase(ref); + return Q(bindings.DeleteBinding(GetReferencedName(ref))); + } +} + +// 12.5.4.1 #sec-void-operator-runtime-semantics-evaluation +// UnaryExpression : `void` UnaryExpression +function* Evaluate_UnaryExpression_Void(UnaryExpression) { + const expr = yield* Evaluate(UnaryExpression); + Q(GetValue(expr)); + return Value.undefined; +} + +// 12.5.5.1 #sec-typeof-operator-runtime-semantics-evaluation +// UnaryExpression : `typeof` UnaryExpression +function* Evaluate_UnaryExpression_Typeof(UnaryExpression) { + let val = yield* Evaluate(UnaryExpression); + if (Type(val) === 'Reference') { + if (IsUnresolvableReference(val) === Value.true) { + return new Value('undefined'); + } + } + val = Q(GetValue(val)); + + // Return a String according to Table 37. + + const type = Type(val); + + switch (type) { + case 'Undefined': + return new Value('undefined'); + case 'Null': + return new Value('object'); + case 'Boolean': + return new Value('boolean'); + case 'Number': + return new Value('number'); + case 'String': + return new Value('string'); + case 'BigInt': + return new Value('bigint'); + case 'Symbol': + return new Value('symbol'); + case 'Object': + if (IsCallable(val) === Value.true) { + return new Value('function'); + } + return new Value('object'); + + default: + throw new OutOfRange('Evaluate_UnaryExpression_Typeof', type); + } +} + +// 12.5.6.1 #sec-unary-plus-operator-runtime-semantics-evaluation +// UnaryExpression : `+` UnaryExpression +function* Evaluate_UnaryExpression_Plus(UnaryExpression) { + const expr = yield* Evaluate(UnaryExpression); + return Q(ToNumber(Q(GetValue(expr)))); +} + +// 12.5.7.1 #sec-unary-minus-operator-runtime-semantics-evaluation +// UnaryExpression : `-` UnaryExpression +function* Evaluate_UnaryExpression_Minus(UnaryExpression) { + // 1. Let expr be the result of evaluating UnaryExpression. + const expr = yield* Evaluate(UnaryExpression); + // 2. Let oldValue be ? ToNumeric(? GetValue(expr)). + const oldValue = Q(ToNumeric(Q(GetValue(expr)))); + // 3. Let T be Type(oldValue). + const T = TypeNumeric(oldValue); + // 4. Return ! T::unaryMinus(oldValue). + return X(T.unaryMinus(oldValue)); +} + +// 12.5.8.1 #sec-bitwise-not-operator-runtime-semantics-evaluation +// UnaryExpression : `~` UnaryExpression +function* Evaluate_UnaryExpression_Tilde(UnaryExpression) { + // 1. Let expr be the result of evaluating UnaryExpression. + const expr = yield* Evaluate(UnaryExpression); + // 2. Let oldValue be ? ToNumeric(? GetValue(expr)). + const oldValue = Q(ToNumeric(Q(GetValue(expr)))); + // 3. Let T be Type(oldValue). + const T = TypeNumeric(oldValue); + // 4. Return ! T::bitwiseNOT(oldValue). + return X(T.bitwiseNOT(oldValue)); +} + +// 12.5.9.1 #sec-logical-not-operator-runtime-semantics-evaluation +// UnaryExpression : `!` UnaryExpression +function* Evaluate_UnaryExpression_Bang(UnaryExpression) { + const expr = yield* Evaluate(UnaryExpression); + const oldValue = ToBoolean(Q(GetValue(expr))); + if (oldValue === Value.true) { + return Value.false; + } + return Value.true; +} + +export function* Evaluate_UnaryExpression(UnaryExpression) { + switch (true) { + case isUnaryExpressionWithDelete(UnaryExpression): + return yield* Evaluate_UnaryExpression_Delete(UnaryExpression.argument); + case isUnaryExpressionWithVoid(UnaryExpression): + return yield* Evaluate_UnaryExpression_Void(UnaryExpression.argument); + case isUnaryExpressionWithTypeof(UnaryExpression): + return yield* Evaluate_UnaryExpression_Typeof(UnaryExpression.argument); + case isUnaryExpressionWithPlus(UnaryExpression): + return yield* Evaluate_UnaryExpression_Plus(UnaryExpression.argument); + case isUnaryExpressionWithMinus(UnaryExpression): + return yield* Evaluate_UnaryExpression_Minus(UnaryExpression.argument); + case isUnaryExpressionWithTilde(UnaryExpression): + return yield* Evaluate_UnaryExpression_Tilde(UnaryExpression.argument); + case isUnaryExpressionWithBang(UnaryExpression): + return yield* Evaluate_UnaryExpression_Bang(UnaryExpression.argument); + + default: + throw new OutOfRange('Evaluate_UnaryExpression', UnaryExpression); + } +} diff --git a/src/engine262/src/runtime-semantics/UpdateExpression.mjs b/src/engine262/src/runtime-semantics/UpdateExpression.mjs new file mode 100644 index 0000000..160bc5c --- /dev/null +++ b/src/engine262/src/runtime-semantics/UpdateExpression.mjs @@ -0,0 +1,64 @@ +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + GetValue, + PutValue, + ToNumeric, +} from '../abstract-ops/all.mjs'; +import { TypeNumeric } from '../value.mjs'; +import { Q, X } from '../completion.mjs'; + +export function* Evaluate_UpdateExpression({ + operator, + prefix, + argument, +}) { + switch (true) { + // UpdateExpression : LeftHandSideExpression `++` + case operator === '++' && !prefix: { + const LeftHandSideExpression = argument; + + const lhs = yield* Evaluate(LeftHandSideExpression); + const oldValue = Q(ToNumeric(Q(GetValue(lhs)))); + const newValue = X(TypeNumeric(oldValue).add(oldValue, TypeNumeric(oldValue).unit)); + Q(PutValue(lhs, newValue)); + return oldValue; + } + + // UpdateExpression : LeftHandSideExpression `--` + case operator === '--' && !prefix: { + const LeftHandSideExpression = argument; + + const lhs = yield* Evaluate(LeftHandSideExpression); + const oldValue = Q(ToNumeric(Q(GetValue(lhs)))); + const newValue = X(TypeNumeric(oldValue).subtract(oldValue, TypeNumeric(oldValue).unit)); + Q(PutValue(lhs, newValue)); + return oldValue; + } + + // UpdateExpression : `++` UnaryExpression + case operator === '++' && prefix: { + const UnaryExpression = argument; + + const expr = yield* Evaluate(UnaryExpression); + const oldValue = Q(ToNumeric(Q(GetValue(expr)))); + const newValue = X(TypeNumeric(oldValue).add(oldValue, TypeNumeric(oldValue).unit)); + Q(PutValue(expr, newValue)); + return newValue; + } + + // UpdateExpression : `--` UnaryExpression + case operator === '--' && prefix: { + const UnaryExpression = argument; + + const expr = yield* Evaluate(UnaryExpression); + const oldValue = Q(ToNumeric(Q(GetValue(expr)))); + const newValue = X(TypeNumeric(oldValue).subtract(oldValue, TypeNumeric(oldValue).unit)); + Q(PutValue(expr, newValue)); + return newValue; + } + + default: + throw new OutOfRange('Evaluate_UpdateExpression', operator, prefix); + } +} diff --git a/src/engine262/src/runtime-semantics/VariableStatement.mjs b/src/engine262/src/runtime-semantics/VariableStatement.mjs new file mode 100644 index 0000000..c0b9520 --- /dev/null +++ b/src/engine262/src/runtime-semantics/VariableStatement.mjs @@ -0,0 +1,82 @@ +import { + GetValue, + PutValue, + ResolveBinding, +} from '../abstract-ops/all.mjs'; +import { + isBindingIdentifier, + isBindingPattern, +} from '../ast.mjs'; +import { NormalCompletion, Q, ReturnIfAbrupt } from '../completion.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { IsAnonymousFunctionDefinition } from '../static-semantics/all.mjs'; +import { Value } from '../value.mjs'; +import { + BindingInitialization_BindingPattern, + NamedEvaluation_Expression, +} from './all.mjs'; + +// 13.3.2.4 #sec-variable-statement-runtime-semantics-evaluation +// VariableDeclaration : +// BindingIdentifier +// BindingIdentifier Initializer +// BindingPattern Initializer +export function* Evaluate_VariableDeclaration(VariableDeclaration) { + switch (true) { + case isBindingIdentifier(VariableDeclaration.id) && VariableDeclaration.init === null: + return new NormalCompletion(undefined); + + case isBindingIdentifier(VariableDeclaration.id) && VariableDeclaration.init !== null: { + const { + id: BindingIdentifier, + init: Initializer, + } = VariableDeclaration; + const bindingId = new Value(BindingIdentifier.name); + const lhs = Q(ResolveBinding(bindingId, undefined, BindingIdentifier.strict)); + let value; + if (IsAnonymousFunctionDefinition(Initializer)) { + value = yield* NamedEvaluation_Expression(Initializer, bindingId); + } else { + const rhs = yield* Evaluate(Initializer); + value = Q(GetValue(rhs)); + } + return Q(PutValue(lhs, value)); + } + + case isBindingPattern(VariableDeclaration.id) && VariableDeclaration.init !== null: { + const { + id: BindingPattern, + init: Initializer, + } = VariableDeclaration; + const rhs = yield* Evaluate(Initializer); + const rval = Q(GetValue(rhs)); + return yield* BindingInitialization_BindingPattern(BindingPattern, rval, Value.undefined); + } + + default: + throw new OutOfRange('Evaluate_VariableDeclaration', VariableDeclaration); + } +} + +// 13.3.2.4 #sec-variable-statement-runtime-semantics-evaluation +// VariableDeclarationList : VariableDeclarationList `,` VariableDeclaration +// +// (implicit) +// VariableDeclarationList : VariableDeclaration +export function* Evaluate_VariableDeclarationList(VariableDeclarationList) { + let next; + for (const VariableDeclaration of VariableDeclarationList) { + next = yield* Evaluate_VariableDeclaration(VariableDeclaration); + ReturnIfAbrupt(next); + } + return next; +} + +// 13.3.2.4 #sec-variable-statement-runtime-semantics-evaluation +// VariableStatement : `var` VariableDeclarationList `;` +export function* Evaluate_VariableStatement(VariableStatement) { + const next = yield* Evaluate_VariableDeclarationList(VariableStatement.declarations); + ReturnIfAbrupt(next); + return new NormalCompletion(undefined); +} diff --git a/src/engine262/src/runtime-semantics/WithStatement.mjs b/src/engine262/src/runtime-semantics/WithStatement.mjs new file mode 100644 index 0000000..8a93606 --- /dev/null +++ b/src/engine262/src/runtime-semantics/WithStatement.mjs @@ -0,0 +1,28 @@ +import { surroundingAgent } from '../engine.mjs'; +import { Evaluate } from '../evaluator.mjs'; +import { NewObjectEnvironment } from '../environment.mjs'; +import { Value } from '../value.mjs'; +import { GetValue, ToObject } from '../abstract-ops/all.mjs'; +import { + Completion, + EnsureCompletion, + Q, + UpdateEmpty, +} from '../completion.mjs'; + +// 13.11.7 #sec-with-statement-runtime-semantics-evaluation +// WithStatement : `with` `(` Expression `)` Statement +export function* Evaluate_WithStatement({ + object: Expression, + body: Statement, +}) { + const val = yield* Evaluate(Expression); + const obj = Q(ToObject(Q(GetValue(val)))); + const oldEnv = surroundingAgent.runningExecutionContext.LexicalEnvironment; + const newEnv = NewObjectEnvironment(obj, oldEnv); + newEnv.EnvironmentRecord.withEnvironment = true; + surroundingAgent.runningExecutionContext.LexicalEnvironment = newEnv; + const C = EnsureCompletion(yield* Evaluate(Statement)); + surroundingAgent.runningExecutionContext.LexicalEnvironment = oldEnv; + return Completion(UpdateEmpty(C, Value.undefined)); +} diff --git a/src/engine262/src/runtime-semantics/YieldExpression.mjs b/src/engine262/src/runtime-semantics/YieldExpression.mjs new file mode 100644 index 0000000..1c2ce1f --- /dev/null +++ b/src/engine262/src/runtime-semantics/YieldExpression.mjs @@ -0,0 +1,141 @@ +import { isYieldExpressionWithStar } from '../ast.mjs'; +import { + Assert, + Call, + CreateIterResultObject, + GeneratorYield, + GetGeneratorKind, + GetIterator, + GetMethod, + GetValue, + IteratorClose, + IteratorComplete, + IteratorValue, + AsyncGeneratorYield, + AsyncIteratorClose, +} from '../abstract-ops/all.mjs'; +import { + Completion, EnsureCompletion, + NormalCompletion, + Q, + ReturnCompletion, + X, + Await, +} from '../completion.mjs'; +import { surroundingAgent } from '../engine.mjs'; +import { + Evaluate, +} from '../evaluator.mjs'; +import { Type, Value } from '../value.mjs'; + +// 14.4.14 #sec-generator-function-definitions-runtime-semantics-evaluation +// YieldExpression : +// `yield` +// `yield` AssignmentExpression +function* Evaluate_YieldExpression_WithoutStar(YieldExpression) { + const generatorKind = X(GetGeneratorKind()); + let value = Value.undefined; + if (YieldExpression.argument) { + const AssignmentExpression = YieldExpression.argument; + const exprRef = yield* Evaluate(AssignmentExpression); + value = Q(GetValue(exprRef)); + } + if (generatorKind === 'async') { + return Q(yield* AsyncGeneratorYield(value)); + } + Assert(generatorKind === 'sync'); + return Q(yield* GeneratorYield(CreateIterResultObject(value, Value.false))); +} + +// 14.4.14 #sec-generator-function-definitions-runtime-semantics-evaluation +// YieldExpression : +// `yield` `*` AssignmentExpression +function* Evaluate_YieldExpression_Star({ argument: AssignmentExpression }) { + const generatorKind = X(GetGeneratorKind()); + const exprRef = yield* Evaluate(AssignmentExpression); + const value = Q(GetValue(exprRef)); + const iteratorRecord = Q(GetIterator(value, generatorKind)); + const iterator = iteratorRecord.Iterator; + let received = new NormalCompletion(Value.undefined); + while (true) { + if (received.Type === 'normal') { + let innerResult = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator, [received.Value])); + if (generatorKind === 'async') { + innerResult = Q(yield* Await(innerResult)); + } + if (Type(innerResult) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult); + } + const done = Q(IteratorComplete(innerResult)); + if (done === Value.true) { + return Q(IteratorValue(innerResult)); + } + if (generatorKind === 'async') { + received = EnsureCompletion(yield* AsyncGeneratorYield(Q(IteratorValue(innerResult)))); + } else { + received = EnsureCompletion(yield* GeneratorYield(innerResult)); + } + } else if (received.Type === 'throw') { + const thr = Q(GetMethod(iterator, new Value('throw'))); + if (Type(thr) !== 'Undefined') { + let innerResult = Q(Call(thr, iterator, [received.Value])); + if (generatorKind === 'async') { + innerResult = Q(yield* Await(innerResult)); + } + if (Type(innerResult) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult); + } + const done = Q(IteratorComplete(innerResult)); + if (done === Value.true) { + return Q(IteratorValue(innerResult)); + } + if (generatorKind === 'async') { + received = EnsureCompletion(yield* AsyncGeneratorYield(Q(IteratorValue(innerResult)))); + } else { + received = EnsureCompletion(yield* GeneratorYield(innerResult)); + } + } else { + const closeCompletion = new NormalCompletion(undefined); + if (generatorKind === 'async') { + Q(yield* AsyncIteratorClose(iteratorRecord, closeCompletion)); + } else { + Q(IteratorClose(iteratorRecord, closeCompletion)); + } + return surroundingAgent.Throw('TypeError', 'IteratorThrowMissing'); + } + } else { + Assert(received.Type === 'return'); + const ret = Q(GetMethod(iterator, new Value('return'))); + if (Type(ret) === 'Undefined') { + if (generatorKind === 'async') { + received.Value = Q(yield* Await(received.Value)); + } + return Completion(received); + } + let innerReturnResult = Q(Call(ret, iterator, [received.Value])); + if (generatorKind === 'async') { + innerReturnResult = Q(yield* Await(innerReturnResult)); + } + if (Type(innerReturnResult) !== 'Object') { + return surroundingAgent.Throw('TypeError', 'NotAnObject', innerReturnResult); + } + const done = Q(IteratorComplete(innerReturnResult)); + if (done === Value.true) { + const innerValue = Q(IteratorValue(innerReturnResult)); + return new ReturnCompletion(innerValue); + } + if (generatorKind === 'async') { + received = EnsureCompletion(yield* AsyncGeneratorYield(Q(IteratorValue(innerReturnResult)))); + } else { + received = EnsureCompletion(yield* GeneratorYield(innerReturnResult)); + } + } + } +} + +export function* Evaluate_YieldExpression(YieldExpression) { + if (isYieldExpressionWithStar(YieldExpression)) { + return yield* Evaluate_YieldExpression_Star(YieldExpression); + } + return yield* Evaluate_YieldExpression_WithoutStar(YieldExpression); +} diff --git a/src/engine262/src/runtime-semantics/all.mjs b/src/engine262/src/runtime-semantics/all.mjs new file mode 100644 index 0000000..49184e3 --- /dev/null +++ b/src/engine262/src/runtime-semantics/all.mjs @@ -0,0 +1,83 @@ +export * from './AdditiveExpression.mjs'; +export * from './ArgumentListEvaluation.mjs'; +export * from './ArrayLiteral.mjs'; +export * from './ArrowFunction.mjs'; +export * from './AssignmentExpression.mjs'; +export * from './AsyncArrowFunction.mjs'; +export * from './AsyncFunctionExpression.mjs'; +export * from './AsyncGeneratorExpression.mjs'; +export * from './AwaitExpression.mjs'; +export * from './BindingInitialization.mjs'; +export * from './BitwiseOperators.mjs'; +export * from './BlockStatement.mjs'; +export * from './BreakStatement.mjs'; +export * from './BreakableStatement.mjs'; +export * from './CallExpression.mjs'; +export * from './ClassDefinition.mjs'; +export * from './CoalesceExpression.mjs'; +export * from './ConditionalExpression.mjs'; +export * from './ContinueStatement.mjs'; +export * from './CreateDynamicFunction.mjs'; +export * from './DebuggerStatement.mjs'; +export * from './DefineMethod.mjs'; +export * from './DestructuringAssignmentEvaluation.mjs'; +export * from './EmptyStatement.mjs'; +export * from './EqualityExpression.mjs'; +export * from './EvaluateBody.mjs'; +export * from './EvaluatePropertyAccess.mjs'; +export * from './ExponentiationExpression.mjs'; +export * from './ExpressionWithComma.mjs'; +export * from './ExportDeclaration.mjs'; +export * from './ForStatement.mjs'; +export * from './FunctionDeclaration.mjs'; +export * from './FunctionExpression.mjs'; +export * from './FunctionStatementList.mjs'; +export * from './GeneratorExpression.mjs'; +export * from './GetSubstitution.mjs'; +export * from './GlobalDeclarationInstantiation.mjs'; +export * from './HoistableDeclaration.mjs'; +export * from './Identifier.mjs'; +export * from './IfStatement.mjs'; +export * from './ImportCall.mjs'; +export * from './InstantiateFunctionObject.mjs'; +export * from './IteratorBindingInitialization.mjs'; +export * from './KeyedBindingInitialization.mjs'; +export * from './LabelledStatement.mjs'; +export * from './LexicalDeclaration.mjs'; +export * from './Literal.mjs'; +export * from './LogicalANDExpression.mjs'; +export * from './LogicalORExpression.mjs'; +export * from './MemberExpression.mjs'; +export * from './MetaProperty.mjs'; +export * from './MV.mjs'; +export * from './MultiplicativeExpression.mjs'; +export * from './NamedEvaluation.mjs'; +export * from './NewExpression.mjs'; +export * from './NumberToBigInt.mjs'; +export * from './ObjectLiteral.mjs'; +export * from './OptionalExpression.mjs'; +export * from './PropertyBindingInitialization.mjs'; +export * from './PropertyDefinitionEvaluation.mjs'; +export * from './PropertyName.mjs'; +export * from './RegularExpressionLiteral.mjs'; +export * from './RelationalOperators.mjs'; +export * from './RestBindingInitialization.mjs'; +export * from './RegExp.mjs'; +export * from './ReturnStatement.mjs'; +export * from './ShiftExpression.mjs'; +export * from './StringIndexOf.mjs'; +export * from './StringPad.mjs'; +export * from './SuperCall.mjs'; +export * from './SuperProperty.mjs'; +export * from './SwitchStatement.mjs'; +export * from './TaggedTemplate.mjs'; +export * from './TemplateLiteral.mjs'; +export * from './ThisExpression.mjs'; +export * from './ThrowStatement.mjs'; +export * from './TrimString.mjs'; +export * from './TryStatement.mjs'; +export * from './UnaryExpression.mjs'; +export * from './UpdateExpression.mjs'; +export * from './VariableStatement.mjs'; +export * from './WithStatement.mjs'; +export * from './YieldExpression.mjs'; diff --git a/src/engine262/src/static-semantics/BoundNames.mjs b/src/engine262/src/static-semantics/BoundNames.mjs new file mode 100644 index 0000000..b42ce72 --- /dev/null +++ b/src/engine262/src/static-semantics/BoundNames.mjs @@ -0,0 +1,496 @@ +import { + isArrayBindingPattern, + isBindingElement, + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isBindingProperty, + isBindingPropertyWithColon, + isBindingPropertyWithSingleNameBinding, + isBindingRestElement, + isBindingRestProperty, + isClassDeclaration, + isDeclaration, + isExportDeclaration, + isExportDeclarationWithDeclaration, + isExportDeclarationWithDefaultAndClass, + isExportDeclarationWithDefaultAndExpression, + isExportDeclarationWithDefaultAndHoistable, + isExportDeclarationWithExport, + isExportDeclarationWithExportAndFrom, + isExportDeclarationWithStar, + isExportDeclarationWithVariable, + isFormalParameter, + isFunctionRestParameter, + isHoistableDeclaration, + isImportDeclaration, + isImportDeclarationWithClause, + isImportDeclarationWithSpecifierOnly, + isLexicalDeclaration, + isObjectBindingPattern, + isSingleNameBinding, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 12.1.2 #sec-identifiers-static-semantics-boundnames +// BindingIdentifier : +// Identifier +// `yield` +// `await` +export function BoundNames_BindingIdentifier(BindingIdentifier) { + return [BindingIdentifier.name]; +} + +// 13.3.1.2 #sec-let-and-const-declarations-static-semantics-boundnames +// LexicalDeclaration : LetOrConst BindingList `;` +// BindingList : BindingList `,` LexicalBinding +// LexicalBinding : +// BindingIdentifier Initializer +// BindingPattern Initializer +// +// (implicit) +// BindingList : LexicalBinding +export function BoundNames_LexicalDeclaration(LexicalDeclaration) { + const names = []; + for (const declarator of LexicalDeclaration.declarations) { + switch (true) { + case isBindingIdentifier(declarator.id): + names.push(...BoundNames_BindingIdentifier(declarator.id)); + break; + case isBindingPattern(declarator.id): + names.push(...BoundNames_BindingPattern(declarator.id)); + break; + default: + throw new OutOfRange('BoundNames_LexicalDeclaration', LexicalDeclaration); + } + } + return names; +} + +// 13.3.2.1 #sec-variable-statement-static-semantics-boundnames +// VariableDeclarationList : VariableDeclarationList `,` VariableDeclaration +// +// (implicit) +// VariableDeclarationList : VariableDeclaration +export function BoundNames_VariableDeclarationList(VariableDeclarationList) { + const names = []; + for (const VariableDeclaration of VariableDeclarationList) { + names.push(...BoundNames_VariableDeclaration(VariableDeclaration)); + } + return names; +} + +// 13.3.2.1 #sec-variable-statement-static-semantics-boundnames +// VariableDeclaration : +// BindingIdentifier Initializer +// BindingPattern Initializer +export function BoundNames_VariableDeclaration(VariableDeclaration) { + switch (true) { + // FIXME: This condition is a hack, formalize it + case VariableDeclaration.id === undefined: + return [VariableDeclaration.name]; + case isBindingIdentifier(VariableDeclaration.id): + return BoundNames_BindingIdentifier(VariableDeclaration.id); + case isBindingPattern(VariableDeclaration.id): + return BoundNames_BindingPattern(VariableDeclaration.id); + default: + throw new OutOfRange('BoundNames_VariableDeclaration', VariableDeclaration); + } +} + +// (implicit) +// VariableStatement : `var` VariableDeclarationList `;` +export function BoundNames_VariableStatement(VariableStatement) { + return BoundNames_VariableDeclarationList(VariableStatement.declarations); +} + +// 13.3.3.1 #sec-destructuring-binding-patterns-static-semantics-boundnames +// SingleNameBinding : BindingIdentifier Initializer +// +// (implicit) +// SingleNameBinding : BindingIdentifier +export function BoundNames_SingleNameBinding(SingleNameBinding) { + switch (true) { + case isBindingIdentifier(SingleNameBinding): + return BoundNames_BindingIdentifier(SingleNameBinding); + case isBindingIdentifierAndInitializer(SingleNameBinding): + return BoundNames_BindingIdentifier(SingleNameBinding.left); + default: + throw new OutOfRange('BoundNames_SingleNameBinding', SingleNameBinding); + } +} + +// 13.3.3.1 #sec-destructuring-binding-patterns-static-semantics-boundnames +// BindingElement : BindingPattern Initializer +// +// (implicit) +// BindingElement : +// SingleNameBinding +// BindingPattern +export function BoundNames_BindingElement(BindingElement) { + switch (true) { + case isSingleNameBinding(BindingElement): + return BoundNames_SingleNameBinding(BindingElement); + case isBindingPattern(BindingElement): + return BoundNames_BindingPattern(BindingElement); + case isBindingPatternAndInitializer(BindingElement): + return BoundNames_BindingPattern(BindingElement.left); + default: + throw new OutOfRange('BoundNames_BindingElement', BindingElement); + } +} + +// (implicit) +// BindingRestElement : +// `...` BindingIdentifier +// `...` BindingPattern +export function BoundNames_BindingRestElement(BindingRestElement) { + switch (true) { + case isBindingIdentifier(BindingRestElement.argument): + return BoundNames_BindingIdentifier(BindingRestElement.argument); + case isBindingPattern(BindingRestElement.argument): + return BoundNames_BindingPattern(BindingRestElement.argument); + default: + throw new OutOfRange('BoundNames_BindingRestElement argument', BindingRestElement.argument); + } +} + +// 13.3.3.1 #sec-destructuring-binding-patterns-static-semantics-boundnames +// ArrayBindingPattern : +// `[` Elision `]` +// `[` Elision BindingRestElement `]` +// `[` BindingElementList `,` Elision `]` +// `[` BindingElementList `,` Elision BindingRestElement `]` +// BindingElementList : BindingElementList `,` BindingElisionElement +// BindingElisionElement : Elision BindingElement +export function BoundNames_ArrayBindingPattern(ArrayBindingPattern) { + const names = []; + for (const BindingElisionElementOrBindingRestElement of ArrayBindingPattern.elements) { + switch (true) { + case BindingElisionElementOrBindingRestElement === null: + // This is an elision. + break; + + case isBindingElement(BindingElisionElementOrBindingRestElement): { + const BindingElement = BindingElisionElementOrBindingRestElement; + names.push(...BoundNames_BindingElement(BindingElement)); + break; + } + case isBindingRestElement(BindingElisionElementOrBindingRestElement): { + const BindingRestElement = BindingElisionElementOrBindingRestElement; + names.push(...BoundNames_BindingRestElement(BindingRestElement)); + break; + } + default: + throw new OutOfRange('BoundNames_ArrayBindingPattern element', BindingElisionElementOrBindingRestElement); + } + } + return names; +} + +// 13.3.3.1 #sec-destructuring-binding-patterns-static-semantics-boundnames +// BindingProperty : PropertyName `:` BindingElement +// +// (implicit) +// BindingProperty : SingleNameBinding +export function BoundNames_BindingProperty(BindingProperty) { + switch (true) { + case isBindingPropertyWithSingleNameBinding(BindingProperty): + return BoundNames_SingleNameBinding(BindingProperty.value); + case isBindingPropertyWithColon(BindingProperty): + return BoundNames_BindingElement(BindingProperty.value); + default: + throw new OutOfRange('BoundNames_BindingProperty', BindingProperty); + } +} + +// (implicit) +// BindingRestProperty : `...` BindingIdentifier +export function BoundNames_BindingRestProperty(BindingRestProperty) { + if (!isBindingIdentifier(BindingRestProperty.argument)) { + throw new OutOfRange('BoundNames_BindingRestProperty argument', BindingRestProperty.argument); + } + return BoundNames_BindingIdentifier(BindingRestProperty.argument); +} + +// 13.3.3.1 #sec-destructuring-binding-patterns-static-semantics-boundnames +// ObjectBindingPattern : +// `{` `}` +// `{` BindingRestProperty `}` +// BindingPropertyList : BindingPropertyList `,` BindingProperty +// +// (implicit) +// ObjectBindingPattern : +// `{` BindingPropertyList `}` +// `{` BindingPropertyList `,` `}` +// `{` BindingPropertyList `,` BindingRestProperty `}` +function BoundNames_ObjectBindingPattern(ObjectBindingPattern) { + const names = []; + for (const BindingPropertyOrBindingRestProperty of ObjectBindingPattern.properties) { + switch (true) { + case isBindingProperty(BindingPropertyOrBindingRestProperty): { + const BindingProperty = BindingPropertyOrBindingRestProperty; + names.push(...BoundNames_BindingProperty(BindingProperty)); + break; + } + case isBindingRestProperty(BindingPropertyOrBindingRestProperty): { + const BindingRestProperty = BindingPropertyOrBindingRestProperty; + names.push(...BoundNames_BindingRestProperty(BindingRestProperty)); + break; + } + default: + throw new OutOfRange('BoundNames_ObjectBindingPattern property', BindingPropertyOrBindingRestProperty); + } + } + return names; +} + +// (implicit) +// BindingPattern : +// ObjectBindingPattern +// ArrayBindingPattern +function BoundNames_BindingPattern(BindingPattern) { + switch (true) { + case isObjectBindingPattern(BindingPattern): + return BoundNames_ObjectBindingPattern(BindingPattern); + case isArrayBindingPattern(BindingPattern): + return BoundNames_ArrayBindingPattern(BindingPattern); + default: + throw new OutOfRange('BoundNames_BindingPattern', BindingPattern); + } +} + +// 13.7.5.2 #sec-for-in-and-for-of-statements-static-semantics-boundnames +// ForDeclaration : LetOrConst ForBinding +export function BoundNames_ForDeclaration(ForDeclaration) { + const ForBinding = ForDeclaration.declarations[0].id; + return BoundNames_ForBinding(ForBinding); +} + +function BoundNames_BindingIdentifierOrBindingPattern( + targetTypeForErrorMessage, + BindingIdentifierOrBindingPattern, +) { + switch (true) { + case isBindingIdentifier(BindingIdentifierOrBindingPattern): + return BoundNames_BindingIdentifier(BindingIdentifierOrBindingPattern); + case isBindingPattern(BindingIdentifierOrBindingPattern): + return BoundNames_BindingPattern(BindingIdentifierOrBindingPattern); + default: + throw new OutOfRange(`BoundNames_BindingIdentifierOrBindingPattern ${targetTypeForErrorMessage}`, BindingIdentifierOrBindingPattern); + } +} + +// (implicit) +// ForBinding : +// BindingIdentifier +// BindingPattern +export function BoundNames_ForBinding(node) { + return BoundNames_BindingIdentifierOrBindingPattern('ForBinding', node); +} + +// (implicit) +// CatchParameter : +// BindingIdentifier +// BindingPattern +export function BoundNames_CatchParameter(node) { + return BoundNames_BindingIdentifierOrBindingPattern('CatchParameter', node); +} + +// (implicit) +// FormalParameter : BindingElement +export const BoundNames_FormalParameter = BoundNames_BindingElement; + +// (implicit) +// FunctionRestParameter : BindingRestElement +export const BoundNames_FunctionRestParameter = BoundNames_BindingRestElement; + +// 14.1.3 #sec-function-definitions-static-semantics-boundnames +// FormalParameters : +// [empty] +// FormalParameterList `,` FunctionRestParameter +// +// FormalParameterList : +// FormalParameterList `,` FormalParameter +// +// (implicit) +// FormalParameters : +// FunctionRestParameter +// FormalParameterList +// FormalParameterList `,` +// +// FormalParameterList : FormalParameter +export function BoundNames_FormalParameters(FormalParameters) { + const names = []; + for (const FormalParameterOrFunctionRestParameter of FormalParameters) { + switch (true) { + case isFormalParameter(FormalParameterOrFunctionRestParameter): + names.push(...BoundNames_FormalParameter(FormalParameterOrFunctionRestParameter)); + break; + + case isFunctionRestParameter(FormalParameterOrFunctionRestParameter): + names.push(...BoundNames_FunctionRestParameter(FormalParameterOrFunctionRestParameter)); + break; + + default: + throw new OutOfRange('BoundNames_FormalParameters element', FormalParameterOrFunctionRestParameter); + } + } + return names; +} + +// 14.1.3 #sec-function-definitions-static-semantics-boundnames +// FunctionDeclaration : +// `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}` +// `function` `(` FormalParameters `)` `{` FunctionBody `}` +// +// 14.4.2 #sec-generator-function-definitions-static-semantics-boundnames +// GeneratorDeclaration : +// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}` +// `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}` +// +// 14.5.2 #sec-async-generator-function-definitions-static-semantics-boundnames +// AsyncGeneratorDeclaration : +// `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// +// 14.7.2 #sec-async-function-definitions-static-semantics-BoundNames +// AsyncFunctionDeclaration : +// `async` [no LineTerminator here] `function` BindingIdentifier `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// `async` [no LineTerminator here] `function` `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// +// (implicit) +// HoistableDeclaration : +// FunctionDeclaration +// GeneratorDeclaration +// AsyncFunctionDeclaration +// AsyncGeneratorDeclaration +export function BoundNames_HoistableDeclaration(HoistableDeclaration) { + if (HoistableDeclaration.id === null) { + return ['*default*']; + } + return BoundNames_BindingIdentifier(HoistableDeclaration.id); +} + +export const BoundNames_FunctionDeclaration = BoundNames_HoistableDeclaration; +export const BoundNames_GeneratorDeclaration = BoundNames_HoistableDeclaration; +export const BoundNames_AsyncFunctionDeclaration = BoundNames_HoistableDeclaration; +export const BoundNames_AsyncGeneratorDeclaration = BoundNames_HoistableDeclaration; + +// 14.6.2 #sec-class-definitions-static-semantics-boundnames +// ClassDeclaration : +// `class` BindingIdentifier ClassTail +// `class` ClassTail +export function BoundNames_ClassDeclaration(ClassDeclaration) { + if (ClassDeclaration.id === null) { + return ['*default*']; + } + return BoundNames_BindingIdentifier(ClassDeclaration.id); +} + +// (implicit) +// Declaration : +// HoistableDeclaration +// ClassDeclaration +// LexicalDeclaration +export function BoundNames_Declaration(Declaration) { + switch (true) { + case isHoistableDeclaration(Declaration): + return BoundNames_HoistableDeclaration(Declaration); + case isClassDeclaration(Declaration): + return BoundNames_ClassDeclaration(Declaration); + case isLexicalDeclaration(Declaration): + return BoundNames_LexicalDeclaration(Declaration); + default: + throw new OutOfRange('BoundNames_Declaration', Declaration); + } +} + +// (implict) +// ImportedBinding : BindingIdentifier +export const BoundNames_ImportedBinding = BoundNames_BindingIdentifier; + +// 15.2.2.2 #sec-imports-static-semantics-boundnames +// ImportDeclaration : +// `import` ImportClause FromClause `;` +// `import` ModuleSpecifier `;` +export function BoundNames_ImportDeclaration(ImportDeclaration) { + switch (true) { + case isImportDeclarationWithClause(ImportDeclaration): + // return BoundNames_ImportClause(ImportDeclaration.specifiers); + return ImportDeclaration.specfiers.map((s) => s.local); + + case isImportDeclarationWithSpecifierOnly(ImportDeclaration): + return []; + + default: + throw new OutOfRange('BoundNames_ImportDeclaration', ImportDeclaration); + } +} + +// 15.2.3.2 #sec-exports-static-semantics-boundnames +// ExportDeclaration : +// `export` `*` FromClause `;` +// `export` ExportClause FromClause `;` +// `export` ExportClause `;` +// `export` VariableStatement +// `export` Declaration +// `export` `default` HoistableDeclaration +// `export` `default` ClassDeclaration +// `export` `default` AssignmentExpression `;` +export function BoundNames_ExportDeclaration(ExportDeclaration) { + switch (true) { + case isExportDeclarationWithStar(ExportDeclaration): + case isExportDeclarationWithExportAndFrom(ExportDeclaration): + case isExportDeclarationWithExport(ExportDeclaration): + return []; + case isExportDeclarationWithVariable(ExportDeclaration): + return BoundNames_VariableStatement(ExportDeclaration.declaration); + case isExportDeclarationWithDeclaration(ExportDeclaration): + return BoundNames_Declaration(ExportDeclaration.declaration); + case isExportDeclarationWithDefaultAndHoistable(ExportDeclaration): { + const declarationNames = BoundNames_HoistableDeclaration(ExportDeclaration.declaration); + if (!declarationNames.includes('*default*')) { + declarationNames.push('*default*'); + } + return declarationNames; + } + case isExportDeclarationWithDefaultAndClass(ExportDeclaration): { + const declarationNames = BoundNames_ClassDeclaration(ExportDeclaration.declaration); + if (!declarationNames.includes('*default*')) { + declarationNames.push('*default*'); + } + return declarationNames; + } + case isExportDeclarationWithDefaultAndExpression(ExportDeclaration): + return ['*default*']; + default: + throw new OutOfRange('BoundNames_ExportDeclaration', ExportDeclaration); + } +} + +// (implicit) +// ModuleItem : +// ImportDeclaration +// ExportDeclaration +// StatementListItem +// +// StatementListItem : Declaration +export function BoundNames_ModuleItem(ModuleItem) { + switch (true) { + case isImportDeclaration(ModuleItem): + return BoundNames_ImportDeclaration(ModuleItem); + + case isExportDeclaration(ModuleItem): + return BoundNames_ExportDeclaration(ModuleItem); + + case isDeclaration(ModuleItem): + return BoundNames_Declaration(ModuleItem); + + default: + throw new OutOfRange('BoundNames_ModuleItem', ModuleItem); + } +} diff --git a/src/engine262/src/static-semantics/ConstructorMethod.mjs b/src/engine262/src/static-semantics/ConstructorMethod.mjs new file mode 100644 index 0000000..6248d55 --- /dev/null +++ b/src/engine262/src/static-semantics/ConstructorMethod.mjs @@ -0,0 +1,11 @@ +// 14.6.3 #sec-static-semantics-constructormethod +// ClassElementList : +// ClassElement +// ClassElementList ClassElement +function ConstructorMethod_ClassElementList(ClassElementList) { + return ClassElementList.find((ClassElement) => ClassElement.kind === 'constructor'); +} + +// (implicit) +// ClassBody : ClassElementList +export const ConstructorMethod_ClassBody = ConstructorMethod_ClassElementList; diff --git a/src/engine262/src/static-semantics/ContainsExpression.mjs b/src/engine262/src/static-semantics/ContainsExpression.mjs new file mode 100644 index 0000000..022ff6c --- /dev/null +++ b/src/engine262/src/static-semantics/ContainsExpression.mjs @@ -0,0 +1,245 @@ +import { + isArrayBindingPattern, + isBindingElement, + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isBindingProperty, + isBindingPropertyWithColon, + isBindingPropertyWithSingleNameBinding, + isBindingRestElement, + isBindingRestProperty, + isFormalParameter, + isFunctionRestParameter, + isObjectBindingPattern, + isSingleNameBinding, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// SingleNameBinding : +// BindingIdentifier +// BindingIdentifier Initializer +export function ContainsExpression_SingleNameBinding(SingleNameBinding) { + switch (true) { + case isBindingIdentifier(SingleNameBinding): + return false; + case isBindingIdentifierAndInitializer(SingleNameBinding): + return true; + default: + throw new OutOfRange('ContainsExpression_SingleNameBinding', SingleNameBinding); + } +} + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// BindingElement : BindingPattern Initializer +// +// (implicit) +// BindingElement : +// SingleNameBinding +// BindingPattern +export function ContainsExpression_BindingElement(BindingElement) { + switch (true) { + case isSingleNameBinding(BindingElement): + return ContainsExpression_SingleNameBinding(BindingElement); + case isBindingPattern(BindingElement): + return ContainsExpression_BindingPattern(BindingElement); + case isBindingPatternAndInitializer(BindingElement): + return true; + default: + throw new OutOfRange('ContainsExpression_BindingElement', BindingElement); + } +} + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// BindingRestElement : +// `...` BindingIdentifier +// `...` BindingPattern +export function ContainsExpression_BindingRestElement(BindingRestElement) { + switch (true) { + case isBindingIdentifier(BindingRestElement.argument): + return false; + case isBindingPattern(BindingRestElement.argument): + return ContainsExpression_BindingPattern(BindingRestElement.argument); + default: + throw new OutOfRange('ContainsExpression_BindingRestElement', BindingRestElement); + } +} + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// ArrayBindingPattern : +// `[` Elision `]` +// `[` Elision BindingRestElement `]` +// `[` BindingElementList `,` Elision `]` +// `[` BindingElementList `,` Elision BindingRestElement `]` +// BindingElementList : BindingElementList `,` BindingElisionElement +// BindingElisionElement : Elision BindingElement +// +// (implicit) +// BindingElementList : BindingElisionElement +// BindingElisionElement : BindingElement +export function ContainsExpression_ArrayBindingPattern(ArrayBindingPattern) { + for (const BindingElisionElementOrBindingRestElement of ArrayBindingPattern.elements) { + switch (true) { + case BindingElisionElementOrBindingRestElement === null: + // This is an elision. + break; + + case isBindingElement(BindingElisionElementOrBindingRestElement): { + const BindingElement = BindingElisionElementOrBindingRestElement; + const has = ContainsExpression_BindingElement(BindingElement); + if (has === true) { + return true; + } + break; + } + case isBindingRestElement(BindingElisionElementOrBindingRestElement): { + const BindingRestElement = BindingElisionElementOrBindingRestElement; + const has = ContainsExpression_BindingRestElement(BindingRestElement); + if (has === true) { + return true; + } + break; + } + default: + throw new OutOfRange('ContainsExpression_ArrayBindingPattern element', BindingElisionElementOrBindingRestElement); + } + } + return false; +} + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// BindingProperty : PropertyName `:` BindingElement +// +// (implicit) +// BindingProperty : SingleNameBinding +export function ContainsExpression_BindingProperty(BindingProperty) { + switch (true) { + case isBindingPropertyWithColon(BindingProperty): { + const has = BindingProperty.computed; + if (has === true) { + return true; + } + return ContainsExpression_BindingElement(BindingProperty.value); + } + + case isBindingPropertyWithSingleNameBinding(BindingProperty): + return ContainsExpression_SingleNameBinding(BindingProperty.value); + + default: + throw new OutOfRange('ContainsExpression_BindingProperty', BindingProperty); + } +} + +// https://github.com/tc39/ecma262/pull/1301 +// BindingRestProperty : `...` BindingIdentifier +export function ContainsExpression_BindingRestProperty(BindingRestProperty) { + if (!isBindingIdentifier(BindingRestProperty.argument)) { + throw new OutOfRange('ContainsExpression_BindingRestProperty argument', BindingRestProperty.argument); + } + return false; +} + +// 13.3.3.2 #sec-destructuring-binding-patterns-static-semantics-containsexpression +// ObjectBindingPattern : `{` `}` +// +// BindingPropertyList : BindingPropertyList `,` BindingProperty +// +// (implicit) +// ObjectBindingPattern : +// `{` BindingRestProperty `}` +// `{` BindingPropertyList `}` +// `{` BindingPropertyList `,` `}` +// +// BindingPropertyList : BindingProperty +// +// https://github.com/tc39/ecma262/pull/1301 +// ObjectBindingPattern : `{` BindingPropertyList `,` BindingRestProperty `}` +export function ContainsExpression_ObjectBindingPattern(ObjectBindingPattern) { + for (const prop of ObjectBindingPattern.properties) { + switch (true) { + case isBindingProperty(prop): { + const BindingProperty = prop; + const has = ContainsExpression_BindingProperty(BindingProperty); + if (has === true) { + return true; + } + break; + } + + case isBindingRestProperty(prop): { + const BindingRestProperty = prop; + const has = ContainsExpression_BindingRestProperty(BindingRestProperty); + if (has === true) { + return true; + } + break; + } + + default: + throw new OutOfRange('ContainsExpression_ObjectBindingPattern property', prop); + } + } + return false; +} + +// (implicit) +// BindingPattern : +// ObjectBindingPattern +// ArrayBindingPattern +function ContainsExpression_BindingPattern(BindingPattern) { + switch (true) { + case isObjectBindingPattern(BindingPattern): + return ContainsExpression_ObjectBindingPattern(BindingPattern); + case isArrayBindingPattern(BindingPattern): + return ContainsExpression_ArrayBindingPattern(BindingPattern); + default: + throw new OutOfRange('ContainsExpression_BindingPattern', BindingPattern); + } +} + +// (implicit) +// FormalParameter : BindingElement +export const ContainsExpression_FormalParameter = ContainsExpression_BindingElement; + +// (implicit) +// FunctionRestParameter : BindingRestElement +export const ContainsExpression_FunctionRestParameter = ContainsExpression_BindingRestElement; + +// 14.1.5 #sec-function-definitions-static-semantics-containsexpression +// FormalParameters : +// [empty] +// FormalParameterList `,` FunctionRestParameter +// +// FormalParameterList : +// FormalParameterList `,` FormalParameter +// +// (implicit) +// FormalParameters : +// FunctionRestParameter +// FormalParameterList +// FormalParameterList `,` +// +// FormalParameterList : FormalParameter +export function ContainsExpression_FormalParameters(FormalParameters) { + for (const FormalParameterOrFunctionRestParameter of FormalParameters) { + switch (true) { + case isFormalParameter(FormalParameterOrFunctionRestParameter): + if (ContainsExpression_FormalParameter(FormalParameterOrFunctionRestParameter) === true) { + return true; + } + break; + + case isFunctionRestParameter(FormalParameterOrFunctionRestParameter): + if (ContainsExpression_FunctionRestParameter(FormalParameterOrFunctionRestParameter) === true) { + return true; + } + break; + + default: + throw new OutOfRange('ContainsExpression_FormalParameters element', FormalParameterOrFunctionRestParameter); + } + } + return false; +} diff --git a/src/engine262/src/static-semantics/ContainsUseStrict.mjs b/src/engine262/src/static-semantics/ContainsUseStrict.mjs new file mode 100644 index 0000000..5ae2e64 --- /dev/null +++ b/src/engine262/src/static-semantics/ContainsUseStrict.mjs @@ -0,0 +1,28 @@ +import { + directivePrologueContainsUseStrictDirective, + isBlockStatement, + isExpressionBody, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 14.1.6 #sec-function-definitions-static-semantics-containsusestrict +// FunctionBody : FunctionStatementList +export function ContainsUseStrict_FunctionBody(FunctionBody) { + return directivePrologueContainsUseStrictDirective(FunctionBody); +} + +// 14.2.5 #sec-arrow-function-definitions-static-semantics-containsusestrict +// ConciseBody : ExpressionBody +// +// (implicit) +// ConciseBody : `{` FunctionBody `}` +export function ContainsUseStrict_ConciseBody(ConciseBody) { + switch (true) { + case isExpressionBody(ConciseBody): + return false; + case isBlockStatement(ConciseBody): + return ContainsUseStrict_FunctionBody(ConciseBody.body); + default: + throw new OutOfRange('ContainsUseStrict_ConciseBody', ConciseBody); + } +} diff --git a/src/engine262/src/static-semantics/DeclarationPart.mjs b/src/engine262/src/static-semantics/DeclarationPart.mjs new file mode 100644 index 0000000..bc396b5 --- /dev/null +++ b/src/engine262/src/static-semantics/DeclarationPart.mjs @@ -0,0 +1,25 @@ +// 13.1.4 #sec-static-semantics-declarationpart +// HoistableDeclaration : +// FunctionDeclaration +// GeneratorDeclaration +// AsyncFunctionDeclaration +// AsyncGeneratorDeclaration +// Declaration : +// ClassDeclaration +// LexicalDeclaration +// +// (implicit) +// Declaration : HoistableDeclaration +// +// What a weird set of static semantics… +export function DeclarationPart_Declaration(Declaration) { + return Declaration; +} + +export const DeclarationPart_HoistableDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_FunctionDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_GeneratorDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_AsyncFunctionDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_AsyncGeneratorDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_ClassDeclaration = DeclarationPart_Declaration; +export const DeclarationPart_LexicalDeclaration = DeclarationPart_Declaration; diff --git a/src/engine262/src/static-semantics/ExpectedArgumentCount.mjs b/src/engine262/src/static-semantics/ExpectedArgumentCount.mjs new file mode 100644 index 0000000..9739288 --- /dev/null +++ b/src/engine262/src/static-semantics/ExpectedArgumentCount.mjs @@ -0,0 +1,67 @@ +import { Assert } from '../abstract-ops/all.mjs'; +import { + isBindingElement, + isFunctionRestParameter, +} from '../ast.mjs'; +import { HasInitializer_BindingElement } from './all.mjs'; + +// 14.1.7 #sec-function-definitions-static-semantics-expectedargumentcount +// FormalParameters : +// [empty] +// FormalParameterList `,` FunctionRestParameter +// +// FormalParameterList : FormalParameterList `,` FormalParameter +// +// (implicit) +// FormalParameters : +// FunctionRestParameter +// FormalParameterList +// FormalParameterList `,` +// +// FormalParameterList : FormalParameter +export function ExpectedArgumentCount_FormalParameters(FormalParameters) { + if (FormalParameters.length === 0) { + return 0; + } + + let count = 0; + for (const FormalParameter of FormalParameters.slice(0, -1)) { + Assert(isBindingElement(FormalParameter)); + const BindingElement = FormalParameter; + if (HasInitializer_BindingElement(BindingElement)) { + return count; + } + count += 1; + } + + const last = FormalParameters[FormalParameters.length - 1]; + if (isFunctionRestParameter(last)) { + return count; + } + Assert(isBindingElement(last)); + if (HasInitializer_BindingElement(last)) { + return count; + } + return count + 1; +} + +// 14.2.6 #sec-arrow-function-definitions-static-semantics-expectedargumentcount +// ArrowParameters : BindingIdentifier +// +// (implicit) +// ArrowParameters : CoverParenthesizedExpressionAndArrowParameterList +// ArrowFormalParameters : `(` UniqueFormalParameters `)` +// UniqueFormalParameters : FormalParameters +export const ExpectedArgumentCount_ArrowParameters = ExpectedArgumentCount_FormalParameters; + +// 14.3.3 #sec-method-definitions-static-semantics-expectedargumentcount +// PropertySetParameterList : FormalParameter +// +// Not implemented. Use ExpectedArgumentCount_FormalParameters instead. + +// 14.8.6 #sec-async-arrow-function-definitions-static-semantics-ExpectedArgumentCount +// AsyncArrowBindingIdentifier : BindingIdentifier +// +// Not implemented. Use ExpectedArgumentCount_ArrowParameters instead. + +export const ExpectedArgumentCount = ExpectedArgumentCount_FormalParameters; diff --git a/src/engine262/src/static-semantics/ExportEntries.mjs b/src/engine262/src/static-semantics/ExportEntries.mjs new file mode 100644 index 0000000..a6bd50b --- /dev/null +++ b/src/engine262/src/static-semantics/ExportEntries.mjs @@ -0,0 +1,181 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { + isExportDeclaration, + isExportDeclarationWithStar, + isExportDeclarationWithExportAndFrom, + isExportDeclarationWithExport, + isExportDeclarationWithVariable, + isExportDeclarationWithDeclaration, + isExportDeclarationWithDefaultAndHoistable, + isExportDeclarationWithDefaultAndClass, + isExportDeclarationWithDefaultAndExpression, + isImportDeclaration, + isStatementListItem, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { ExportEntryRecord } from '../modules.mjs'; +import { Value } from '../value.mjs'; +import { + BoundNames_ClassDeclaration, + BoundNames_Declaration, + BoundNames_HoistableDeclaration, + BoundNames_VariableStatement, +} from './BoundNames.mjs'; +import { ExportEntriesForModule_ExportClause } from './ExportEntriesForModule.mjs'; +import { ModuleRequests_FromClause } from './ModuleRequests.mjs'; + +// 15.2.1.7 #sec-module-semantics-static-semantics-exportentries +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function ExportEntries_ModuleItemList(ModuleItemList) { + const entries = []; + for (const ModuleItem of ModuleItemList) { + entries.push(...ExportEntries_ModuleItem(ModuleItem)); + } + return entries; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const ExportEntries_ModuleBody = ExportEntries_ModuleItemList; + +// 15.2.1.7 #sec-module-semantics-static-semantics-exportentries +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const ExportEntries_Module = ExportEntries_ModuleBody; + +// 15.2.1.7 #sec-module-semantics-static-semantics-exportentries +// ModuleItem : +// ImportDeclaration +// StatementListItem +// +// (implicit) +// ModuleItem : ExportDeclaration +export function ExportEntries_ModuleItem(ModuleItem) { + switch (true) { + case isImportDeclaration(ModuleItem): + case isStatementListItem(ModuleItem): + return []; + + case isExportDeclaration(ModuleItem): + return ExportEntries_ExportDeclaration(ModuleItem); + + default: + throw new OutOfRange('ExportEntries_ModuleItem', ModuleItem); + } +} + +// 15.2.3.5 #sec-exports-static-semantics-exportentries +// ExportDeclaration : +// `export` `*` FromClause `;` +// `export` ExportClause FromClause `;` +// `export` ExportClause `;` +// `export` VariableStatement +// `export` Declaration +// `export` `default` HoistableDeclaration +// `export` `default` ClassDeclaration +// `export` `default` AssignmentExpression `;` +export function ExportEntries_ExportDeclaration(ExportDeclaration) { + switch (true) { + case isExportDeclarationWithStar(ExportDeclaration): { + const FromClause = ExportDeclaration.source; + const modules = ModuleRequests_FromClause(FromClause); + Assert(modules.length === 1); + const [module] = modules; + const entry = new ExportEntryRecord({ + ModuleRequest: module, + ImportName: new Value('*'), + LocalName: Value.null, + ExportName: Value.null, + }); + return [entry]; + } + + case isExportDeclarationWithExportAndFrom(ExportDeclaration): { + const { + specifiers: ExportClause, + source: FromClause, + } = ExportDeclaration; + const modules = ModuleRequests_FromClause(FromClause); + Assert(modules.length === 1); + const [module] = modules; + return ExportEntriesForModule_ExportClause(ExportClause, module); + } + + case isExportDeclarationWithExport(ExportDeclaration): { + const ExportClause = ExportDeclaration.specifiers; + return ExportEntriesForModule_ExportClause(ExportClause, Value.null); + } + + case isExportDeclarationWithVariable(ExportDeclaration): { + const VariableStatement = ExportDeclaration.declaration; + const entries = []; + const names = BoundNames_VariableStatement(VariableStatement); + for (const name of names) { + entries.push(new ExportEntryRecord({ + ModuleRequest: Value.null, + ImportName: Value.null, + LocalName: new Value(name), + ExportName: new Value(name), + })); + } + return entries; + } + + case isExportDeclarationWithDeclaration(ExportDeclaration): { + const Declaration = ExportDeclaration.declaration; + const entries = []; + const names = BoundNames_Declaration(Declaration); + for (const name of names) { + entries.push(new ExportEntryRecord({ + ModuleRequest: Value.null, + ImportName: Value.null, + LocalName: new Value(name), + ExportName: new Value(name), + })); + } + return entries; + } + + case isExportDeclarationWithDefaultAndHoistable(ExportDeclaration): { + const HoistableDeclaration = ExportDeclaration.declaration; + const names = BoundNames_HoistableDeclaration(HoistableDeclaration); + Assert(names.length === 1); + const [localName] = names; + return [new ExportEntryRecord({ + ModuleRequest: Value.null, + ImportName: Value.null, + LocalName: new Value(localName), + ExportName: new Value('default'), + })]; + } + + case isExportDeclarationWithDefaultAndClass(ExportDeclaration): { + const ClassDeclaration = ExportDeclaration.declaration; + const names = BoundNames_ClassDeclaration(ClassDeclaration); + Assert(names.length === 1); + const [localName] = names; + return [new ExportEntryRecord({ + ModuleRequest: Value.null, + ImportName: Value.null, + LocalName: new Value(localName), + ExportName: new Value('default'), + })]; + } + + case isExportDeclarationWithDefaultAndExpression(ExportDeclaration): + return [new ExportEntryRecord({ + ModuleRequest: Value.null, + ImportName: Value.null, + LocalName: new Value('*default*'), + ExportName: new Value('default'), + })]; + + default: + throw new OutOfRange('ExportEntries_ExportDeclaration', ExportDeclaration); + } +} diff --git a/src/engine262/src/static-semantics/ExportEntriesForModule.mjs b/src/engine262/src/static-semantics/ExportEntriesForModule.mjs new file mode 100644 index 0000000..6e90406 --- /dev/null +++ b/src/engine262/src/static-semantics/ExportEntriesForModule.mjs @@ -0,0 +1,48 @@ +import { ExportEntryRecord } from '../modules.mjs'; +import { Value } from '../value.mjs'; + +// 15.2.3.6 #sec-static-semantics-exportentriesformodule +// ExportList : ExportList `,` ExportSpecifier +// +// (implicit) +// ExportList : ExportSpecifier +export function ExportEntriesForModule_ExportList(ExportList, module) { + const specs = []; + for (const ExportSpecifier of ExportList) { + specs.push(...ExportEntriesForModule_ExportSpecifier(ExportSpecifier, module)); + } + return specs; +} + +// 15.2.3.6 #sec-static-semantics-exportentriesformodule +// ExportClause : `{` `}` +// +// (implicit) +// ExportClause : +// `{` ExportList `}` +// `{` ExportList `,` `}` +export const ExportEntriesForModule_ExportClause = ExportEntriesForModule_ExportList; + +// 15.2.3.6 #sec-static-semantics-exportentriesformodule +// ExportSpecifier : +// IdentifierName +// IdentifierName `as` IdentifierName +export function ExportEntriesForModule_ExportSpecifier(ExportSpecifier, module) { + const sourceName = new Value(ExportSpecifier.local.name); + const exportName = new Value(ExportSpecifier.exported.name); + let localName; + let importName; + if (module === Value.null) { + localName = sourceName; + importName = Value.null; + } else { + localName = Value.null; + importName = sourceName; + } + return [new ExportEntryRecord({ + ModuleRequest: module, + ImportName: importName, + LocalName: localName, + ExportName: exportName, + })]; +} diff --git a/src/engine262/src/static-semantics/HasInitializer.mjs b/src/engine262/src/static-semantics/HasInitializer.mjs new file mode 100644 index 0000000..b5ed4a0 --- /dev/null +++ b/src/engine262/src/static-semantics/HasInitializer.mjs @@ -0,0 +1,52 @@ +import { + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isSingleNameBinding, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 13.3.3.3 #sec-destructuring-binding-patterns-static-semantics-hasinitializer +// SingleNameBinding : +// BindingIdentifier +// BindingIdentifier Initializer +export function HasInitializer_SingleNameBinding(SingleNameBinding) { + switch (true) { + case isBindingIdentifier(SingleNameBinding): + return false; + + case isBindingIdentifierAndInitializer(SingleNameBinding): + return true; + + default: + throw new OutOfRange('HasInitializer_SingleNameBinding', SingleNameBinding); + } +} + +// 13.3.3.3 #sec-destructuring-binding-patterns-static-semantics-hasinitializer +// BindingElement : +// BindingPattern +// BindingPattern Initializer +// +// (implicit) +// BindingElement : SingleNameBinding +export function HasInitializer_BindingElement(BindingElement) { + switch (true) { + case isBindingPattern(BindingElement): + return false; + + case isBindingPatternAndInitializer(BindingElement): + return true; + + case isSingleNameBinding(BindingElement): + return HasInitializer_SingleNameBinding(BindingElement); + + default: + throw new OutOfRange('HasInitializer_BindingElement', BindingElement); + } +} + +// 14.1.8 #sec-function-definitions-static-semantics-hasinitializer +// FormalParameterList : FormalParameterList `,` FormalParameter +// is implemented directly as part of ExpectedArgumentCount for FormalParameters. diff --git a/src/engine262/src/static-semantics/HasName.mjs b/src/engine262/src/static-semantics/HasName.mjs new file mode 100644 index 0000000..fbe5b6a --- /dev/null +++ b/src/engine262/src/static-semantics/HasName.mjs @@ -0,0 +1,98 @@ +import { + isArrowFunction, + isAsyncArrowFunction, + isAsyncFunctionExpression, + isAsyncGeneratorExpression, + isClassExpression, + isFunctionExpression, + isGeneratorExpression, + isParenthesizedExpression, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { IsFunctionDefinition_Expression } from './all.mjs'; + +// 12.2.1.2 #sec-semantics-static-semantics-hasname +// PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList +// +// 14.1.9 #sec-function-definitions-static-semantics-hasname +// FunctionExpression : +// `function` `(` FormalParameters `)` `{` FunctionBody `}` +// `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}` +// +// 14.2.7 #sec-arrow-function-definitions-static-semantics-hasname +// ArrowFunction : ArrowParameters `=>` ConciseBody +// +// 14.4.6 #sec-generator-function-definitions-static-semantics-hasname +// GeneratorExpression : +// `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}` +// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}` +// +// 14.5.6 #sec-async-generator-function-definitions-static-semantics-hasname +// AsyncGeneratorExpression : +// `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// +// 14.6.6 #sec-class-definitions-static-semantics-hasname +// ClassExpression : +// `class` ClassTail +// `class` BindingIdentifier ClassTail +// +// 14.7.6 #sec-async-function-definitions-static-semantics-HasName +// AsyncFunctionExpression : +// `async` [no LineTerminator here] `function` `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// `async` [no LineTerminator here] `function` BindingIdentifier `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// +// 14.8.7 #sec-async-arrow-function-definitions-static-semantics-HasName +// AsyncArrowFunction: +// `async` [no LineTerminator here] AsyncArrowBindingIdentifier [no LineTerminator here] +// `=>` AsyncConciseBody +// CoverCallExpressionAndAsyncArrowHead [no LineTerminator here] `=>` AsyncConciseBody +// +// (implicit) +// ParenthesizedExpression : `(` Expression `)` +// +// PrimaryExpression : +// FunctionExpression +// ClassExpression +// GeneratorExpression +// AsyncFunctionExpression +// AsyncGeneratorExpression +// ArrowFunction +// +// MemberExpression : PrimaryExpression +// +// (From MemberExpression to ConditionalExpression omitted.) +// +// AssignmentExpression : +// ConditionalExpression +// ArrowFunction +// AsyncArrowFunction +// +// Expression : AssignmentExpression +export function HasName_Expression(Expression) { + switch (true) { + case isFunctionExpression(Expression): + case isGeneratorExpression(Expression): + case isAsyncGeneratorExpression(Expression): + case isClassExpression(Expression): + case isAsyncFunctionExpression(Expression): + return Expression.id !== null; + + case isArrowFunction(Expression): + case isAsyncArrowFunction(Expression): + return false; + + case isParenthesizedExpression(Expression): { + const expr = Expression.expression; + if (!IsFunctionDefinition_Expression(expr)) { + return false; + } + return HasName_Expression(expr); + } + + default: + throw new OutOfRange('HasName_Expression', Expression); + } +} diff --git a/src/engine262/src/static-semantics/ImportEntries.mjs b/src/engine262/src/static-semantics/ImportEntries.mjs new file mode 100644 index 0000000..c37c450 --- /dev/null +++ b/src/engine262/src/static-semantics/ImportEntries.mjs @@ -0,0 +1,81 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { + isExportDeclaration, + isImportDeclaration, + isImportDeclarationWithClause, + isImportDeclarationWithSpecifierOnly, + isStatementListItem, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { ImportEntriesForModule_ImportClause } from './ImportEntriesForModule.mjs'; +import { ModuleRequests_FromClause } from './ModuleRequests.mjs'; + +// 15.2.1.8 #sec-module-semantics-static-semantics-importentries +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function ImportEntries_ModuleItemList(ModuleItemList) { + const entries = []; + for (const ModuleItem of ModuleItemList) { + entries.push(...ImportEntries_ModuleItem(ModuleItem)); + } + return entries; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const ImportEntries_ModuleBody = ImportEntries_ModuleItemList; + +// 15.2.1.8 #sec-module-semantics-static-semantics-importentries +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const ImportEntries_Module = ImportEntries_ModuleBody; + +// 15.2.1.8 #sec-module-semantics-static-semantics-importentries +// ModuleItem : +// ExportDeclaration +// StatementListItem +// +// (implicit) +// ModuleItem : ImportDeclaration +export function ImportEntries_ModuleItem(ModuleItem) { + switch (true) { + case isExportDeclaration(ModuleItem): + case isStatementListItem(ModuleItem): + return []; + + case isImportDeclaration(ModuleItem): + return ImportEntries_ImportDeclaration(ModuleItem); + + default: + throw new OutOfRange('ImportEntries_ModuleItem', ModuleItem); + } +} + +// 15.2.2.3 #sec-imports-static-semantics-importentries +// ImportDeclaration : +// `import` ImportClause FromClause `;` +// `import` ModuleSpecifier `;` +export function ImportEntries_ImportDeclaration(ImportDeclaration) { + switch (true) { + case isImportDeclarationWithClause(ImportDeclaration): { + const { + specifiers: ImportClause, + source: FromClause, + } = ImportDeclaration; + const reqs = ModuleRequests_FromClause(FromClause); + Assert(reqs.length === 1); + const [module] = reqs; + return ImportEntriesForModule_ImportClause(ImportClause, module); + } + + case isImportDeclarationWithSpecifierOnly(ImportDeclaration): + return []; + + default: + throw new OutOfRange('ImportEntries_ImportDeclaration', ImportDeclaration); + } +} diff --git a/src/engine262/src/static-semantics/ImportEntriesForModule.mjs b/src/engine262/src/static-semantics/ImportEntriesForModule.mjs new file mode 100644 index 0000000..7ff3784 --- /dev/null +++ b/src/engine262/src/static-semantics/ImportEntriesForModule.mjs @@ -0,0 +1,104 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { + isImportedDefaultBinding, + isNameSpaceImport, + isImportSpecifier, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { ImportEntryRecord } from '../modules.mjs'; +import { Value } from '../value.mjs'; +import { BoundNames_ImportedBinding } from './BoundNames.mjs'; + +// 15.2.2.4 #sec-static-semantics-importentriesformodule +// ImportClause : +// ImportedDefaultBinding `,` NameSpaceImport +// ImportedDefaultBinding `,` NamedImports +// +// NamedImports : `{` `}` +// +// ImportsList : ImportsList `,` ImportSpecifier +// +// (implicit) +// ImportClause : +// ImportedDefaultBinding +// NameSpaceImport +// NamedImports +// +// NamedImports : +// `{` ImportsList `}` +// `{` ImportsList `,` `}` +// +// ImportsList : ImportSpecifier +export function ImportEntriesForModule_ImportClause(ImportClause, module) { + const entries = []; + for (const binding of ImportClause) { + switch (true) { + case isImportedDefaultBinding(binding): + entries.push(...ImportEntriesForModule_ImportedDefaultBinding(binding, module)); + break; + + case isNameSpaceImport(binding): + entries.push(...ImportEntriesForModule_NameSpaceImport(binding, module)); + break; + + case isImportSpecifier(binding): + entries.push(...ImportEntriesForModule_ImportSpecifier(binding, module)); + break; + + default: + throw new OutOfRange('ImportEntriesForModule_ImportClause binding', binding); + } + } + return entries; +} + +// 15.2.2.4 #sec-static-semantics-importentriesformodule +// ImportedDefaultBinding : ImportedBinding +export function ImportEntriesForModule_ImportedDefaultBinding(ImportedDefaultBinding, module) { + const ImportedBinding = ImportedDefaultBinding.local; + const localNames = BoundNames_ImportedBinding(ImportedBinding); + Assert(localNames.length === 1); + const [localName] = localNames; + const defaultEntry = new ImportEntryRecord({ + ModuleRequest: module, + ImportName: new Value('default'), + LocalName: new Value(localName), + }); + return [defaultEntry]; +} + +// 15.2.2.4 #sec-static-semantics-importentriesformodule +// NameSpaceImport : `*` `as` ImportedBinding +export function ImportEntriesForModule_NameSpaceImport(NameSpaceImport, module) { + const ImportedBinding = NameSpaceImport.local; + const localNames = BoundNames_ImportedBinding(ImportedBinding); + Assert(localNames.length === 1); + const [localName] = localNames; + const entry = new ImportEntryRecord({ + ModuleRequest: module, + ImportName: new Value('*'), + LocalName: new Value(localName), + }); + return [entry]; +} + +// 15.2.2.4 #sec-static-semantics-importentriesformodule +// ImportSpecifier : +// ImportedBinding +// IdentifierName `as` ImportedBinding +export function ImportEntriesForModule_ImportSpecifier(ImportSpecifier, module) { + const { + imported: IdentifierName, + local: ImportedBinding, + } = ImportSpecifier; + + const importName = IdentifierName.name; + const localName = ImportedBinding.name; + + const entry = new ImportEntryRecord({ + ModuleRequest: module, + ImportName: new Value(importName), + LocalName: new Value(localName), + }); + return [entry]; +} diff --git a/src/engine262/src/static-semantics/ImportedLocalNames.mjs b/src/engine262/src/static-semantics/ImportedLocalNames.mjs new file mode 100644 index 0000000..2ecc534 --- /dev/null +++ b/src/engine262/src/static-semantics/ImportedLocalNames.mjs @@ -0,0 +1,8 @@ +// 15.2.1.9 #sec-importedlocalnames +export function ImportedLocalNames(importEntries) { + const localNames = []; + for (const i of importEntries) { + localNames.push(i.LocalName); + } + return localNames; +} diff --git a/src/engine262/src/static-semantics/IsAnonymousFunctionDefinition.mjs b/src/engine262/src/static-semantics/IsAnonymousFunctionDefinition.mjs new file mode 100644 index 0000000..256bc84 --- /dev/null +++ b/src/engine262/src/static-semantics/IsAnonymousFunctionDefinition.mjs @@ -0,0 +1,16 @@ +import { + HasName_Expression, + IsFunctionDefinition_Expression, +} from './all.mjs'; + +// 14.1.10 #sec-isanonymousfunctiondefinition +export function IsAnonymousFunctionDefinition(expr) { + if (IsFunctionDefinition_Expression(expr) === false) { + return false; + } + const hasName = HasName_Expression(expr); + if (hasName === true) { + return false; + } + return true; +} diff --git a/src/engine262/src/static-semantics/IsConstantDeclaration.mjs b/src/engine262/src/static-semantics/IsConstantDeclaration.mjs new file mode 100644 index 0000000..83f5887 --- /dev/null +++ b/src/engine262/src/static-semantics/IsConstantDeclaration.mjs @@ -0,0 +1,3 @@ +export function IsConstantDeclaration(node) { + return node.kind === 'const'; +} diff --git a/src/engine262/src/static-semantics/IsDestructuring.mjs b/src/engine262/src/static-semantics/IsDestructuring.mjs new file mode 100644 index 0000000..d67cd36 --- /dev/null +++ b/src/engine262/src/static-semantics/IsDestructuring.mjs @@ -0,0 +1,60 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { + isBindingIdentifier, + isBindingPattern, + isExpression, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 12.3.1.4 #sec-static-semantics-static-semantics-isdestructuring +// MemberExpression : +// PrimaryExpression +// MemberExpression `[` Expression `]` +// MemberExpression `.` IdentifierName +// MemberExpression TemplateLiteral +// SuperProperty +// MetaProperty +// `new` MemberExpression Arguments +// +// NewExpression : `new` NewExpression +// +// LeftHandSideExpression : CallExpression +// +// (implicit) +// NewExpression : MemberExpression +// +// LeftHandSideExpression : NewExpression +export function IsDestructuring_LeftHandSideExpression(LeftHandSideExpression) { + switch (true) { + case isExpression(LeftHandSideExpression): + Assert(!isBindingPattern(LeftHandSideExpression)); + return false; + + case isBindingPattern(LeftHandSideExpression): + return true; + + default: + throw new OutOfRange('IsDestructuring_LeftHandSideExpression', LeftHandSideExpression); + } +} + +// 13.7.5.6 #sec-for-in-and-for-of-statements-static-semantics-isdestructuring +// ForDeclaration : LetOrConst ForBinding +export function IsDestructuring_ForDeclaration(ForDeclaration) { + return IsDestructuring_ForBinding(ForDeclaration.declarations[0].id); +} + +// 13.7.5.6 #sec-for-in-and-for-of-statements-static-semantics-isdestructuring +// ForBinding : +// BindingIdentifier +// BindingPattern +export function IsDestructuring_ForBinding(ForBinding) { + switch (true) { + case isBindingIdentifier(ForBinding): + return false; + case isBindingPattern(ForBinding): + return true; + default: + throw new OutOfRange('IsDestructuring_ForBinding', ForBinding); + } +} diff --git a/src/engine262/src/static-semantics/IsFunctionDefinition.mjs b/src/engine262/src/static-semantics/IsFunctionDefinition.mjs new file mode 100644 index 0000000..4930ef1 --- /dev/null +++ b/src/engine262/src/static-semantics/IsFunctionDefinition.mjs @@ -0,0 +1,81 @@ +import { + isArrowFunction, + isAsyncArrowFunction, + isAsyncFunctionExpression, + isAsyncGeneratorExpression, + isClassExpression, + isFunctionExpression, + isGeneratorExpression, + isParenthesizedExpression, +} from '../ast.mjs'; + +// At the time of implementation, only the following productions return true +// for this static semantic: +// +// 12.15.2 #sec-assignment-operators-static-semantics-isfunctiondefinition +// AssignmentExpression : +// ArrowFunction +// AsyncArrowFunction +// +// 14.1.12 #sec-function-definitions-static-semantics-isfunctiondefinition +// FunctionExpression : +// `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}` +// +// 14.4.8 #sec-generator-function-definitions-static-semantics-isfunctiondefinition +// GeneratorExpression : +// `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}` +// +// 14.5.8 #sec-async-generator-function-definitions-static-semantics-isfunctiondefinition +// AsyncGeneratorExpression : +// `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}` +// +// 14.6.8 #sec-class-definitions-static-semantics-isfunctiondefinition +// ClassExpression : `class` BindingIdentifier_opt ClassTail +// +// 14.7.8 #sec-async-function-definitions-static-semantics-IsFunctionDefinition +// AsyncFunctionExpression : +// `async` [no LineTerminator here] `function` `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// `async` [no LineTerminator here] `function` BindingIdentifier `(` FormalParameters `)` +// `{` AsyncFunctionBody `}` +// +// The following sections contain other special cases for +// ParenthesizedExpressions: +// +// 12.2.1.3 #sec-semantics-static-semantics-isfunctiondefinition +// PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList +// +// 12.2.10.2 #sec-grouping-operator-static-semantics-isfunctiondefinition +// ParenthesizedExpression : `(` Expression `)` +// +// All other explicit and implicit productions return false, including those +// specified at the following anchors: +// +// 12.2.1.3 #sec-semantics-static-semantics-isfunctiondefinition +// 12.3.1.3 #sec-static-semantics-static-semantics-isfunctiondefinition +// 12.4.2 #sec-update-expressions-static-semantics-isfunctiondefinition +// 12.5.1 #sec-unary-operators-static-semantics-isfunctiondefinition +// 12.6.1 #sec-exp-operator-static-semantics-isfunctiondefinition +// 12.7.1 #sec-multiplicative-operators-static-semantics-isfunctiondefinition +// 12.8.1 #sec-additive-operators-static-semantics-isfunctiondefinition +// 12.9.1 #sec-bitwise-shift-operators-static-semantics-isfunctiondefinition +// 12.10.1 #sec-relational-operators-static-semantics-isfunctiondefinition +// 12.11.1 #sec-equality-operators-static-semantics-isfunctiondefinition +// 12.12.1 #sec-binary-bitwise-operators-static-semantics-isfunctiondefinition +// 12.13.1 #sec-binary-logical-operators-static-semantics-isfunctiondefinition +// 12.14.1 #sec-conditional-operator-static-semantics-isfunctiondefinition +// 12.15.2 #sec-assignment-operators-static-semantics-isfunctiondefinition +// 12.16.1 #sec-comma-operator-static-semantics-isfunctiondefinition +export function IsFunctionDefinition_Expression(Expression) { + if (isParenthesizedExpression(Expression)) { + const expr = Expression.expression; + return IsFunctionDefinition_Expression(expr); + } + return isArrowFunction(Expression) + || isAsyncArrowFunction(Expression) + || isFunctionExpression(Expression) + || isGeneratorExpression(Expression) + || isAsyncGeneratorExpression(Expression) + || isClassExpression(Expression) + || isAsyncFunctionExpression(Expression); +} diff --git a/src/engine262/src/static-semantics/IsIdentifierRef.mjs b/src/engine262/src/static-semantics/IsIdentifierRef.mjs new file mode 100644 index 0000000..7388a41 --- /dev/null +++ b/src/engine262/src/static-semantics/IsIdentifierRef.mjs @@ -0,0 +1,14 @@ +import { + isIdentifierReference, +} from '../ast.mjs'; + +// 12.2.1.4 #sec-semantics-static-semantics-isidentifierref +// PrimaryExpression : +// IdentifierReference +// ... (omitted) +// +// 12.3.1.5 #sec-static-semantics-static-semantics-isidentifierref +// ... (omitted) +export function IsIdentifierRef(node) { + return isIdentifierReference(node); +} diff --git a/src/engine262/src/static-semantics/IsInTailPosition.mjs b/src/engine262/src/static-semantics/IsInTailPosition.mjs new file mode 100644 index 0000000..5b0e41e --- /dev/null +++ b/src/engine262/src/static-semantics/IsInTailPosition.mjs @@ -0,0 +1,5 @@ +// 14.9.1 #sec-isintailposition +// TODO(TCO) +export function IsInTailPosition() { + return false; +} diff --git a/src/engine262/src/static-semantics/IsSimpleParameterList.mjs b/src/engine262/src/static-semantics/IsSimpleParameterList.mjs new file mode 100644 index 0000000..2a424a4 --- /dev/null +++ b/src/engine262/src/static-semantics/IsSimpleParameterList.mjs @@ -0,0 +1,85 @@ +import { + isBindingIdentifier, + isBindingIdentifierAndInitializer, + isBindingPattern, + isBindingPatternAndInitializer, + isFunctionRestParameter, + isSingleNameBinding, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 13.3.3.4 #sec-destructuring-binding-patterns-static-semantics-issimpleparameterlist +// BindingElement : +// BindingPattern +// BindingPattern Initializer +// +// (implicit) +// BindingElement : SingleNameBinding +export function IsSimpleParameterList_BindingElement(BindingElement) { + switch (true) { + case isSingleNameBinding(BindingElement): + return IsSimpleParameterList_SingleNameBinding(BindingElement); + + case isBindingPattern(BindingElement): + case isBindingPatternAndInitializer(BindingElement): + return false; + + default: + throw new OutOfRange('IsSimpleParameterList_BindingElement', BindingElement); + } +} + +// 13.3.3.4 #sec-destructuring-binding-patterns-static-semantics-issimpleparameterlist +// SingleNameBinding : +// BindingIdentifier +// BindingIdentifier Initializer +export function IsSimpleParameterList_SingleNameBinding(SingleNameBinding) { + switch (true) { + case isBindingIdentifier(SingleNameBinding): + return true; + case isBindingIdentifierAndInitializer(SingleNameBinding): + return false; + default: + throw new OutOfRange('IsSimpleParameterList_SingleNameBinding', SingleNameBinding); + } +} + +// 14.1.13 #sec-function-definitions-static-semantics-issimpleparameterlist +// FormalParameters : +// [empty] +// FormalParameterList `,` FunctionRestParameter +// +// (implicit) +// FormalParameters : +// FormalParameterList +// FormalParameterList `,` +// +// https://github.com/tc39/ecma262/pull/1301 +// FormalParameters : FunctionRestParameter +export function IsSimpleParameterList_FormalParameters(FormalParameters) { + if (FormalParameters.length === 0) { + return true; + } + if (isFunctionRestParameter(FormalParameters[FormalParameters.length - 1])) { + return false; + } + return IsSimpleParameterList_FormalParameterList(FormalParameters); +} + +// 14.1.13 #sec-function-definitions-static-semantics-issimpleparameterlist +// FormalParameterList : +// FormalParameter +// FormalParameterList `,` FormalParameter +export function IsSimpleParameterList_FormalParameterList(FormalParameterList) { + for (const FormalParameter of FormalParameterList) { + if (IsSimpleParameterList_FormalParameter(FormalParameter) === false) { + return false; + } + } + return true; +} + +// TODO(TimothyGu): does not need to be explicitly declared +// 14.1.13 #sec-function-definitions-static-semantics-issimpleparameterlist +// FormalParameter : BindingElement +export const IsSimpleParameterList_FormalParameter = IsSimpleParameterList_BindingElement; diff --git a/src/engine262/src/static-semantics/IsStatic.mjs b/src/engine262/src/static-semantics/IsStatic.mjs new file mode 100644 index 0000000..0cb48a0 --- /dev/null +++ b/src/engine262/src/static-semantics/IsStatic.mjs @@ -0,0 +1,8 @@ +// 14.6.9 #sec-static-semantics-isstatic +// ClassElement : +// MethodDefinition +// `static` MethodDefinition +// `;` +export function IsStatic_ClassElement(ClassElement) { + return ClassElement.static; +} diff --git a/src/engine262/src/static-semantics/IsStrict.mjs b/src/engine262/src/static-semantics/IsStrict.mjs new file mode 100644 index 0000000..74c9493 --- /dev/null +++ b/src/engine262/src/static-semantics/IsStrict.mjs @@ -0,0 +1,5 @@ +import { isStrictModeCode } from '../abstract-ops/all.mjs'; + +export function IsStrict(node) { + return isStrictModeCode(node); +} diff --git a/src/engine262/src/static-semantics/LexicallyDeclaredNames.mjs b/src/engine262/src/static-semantics/LexicallyDeclaredNames.mjs new file mode 100644 index 0000000..6da93d1 --- /dev/null +++ b/src/engine262/src/static-semantics/LexicallyDeclaredNames.mjs @@ -0,0 +1,121 @@ +import { + isBlockStatement, + isDeclaration, + isExpressionBody, + isFunctionDeclaration, + isLabelledStatement, + isStatement, +} from '../ast.mjs'; +import { + TopLevelLexicallyDeclaredNames_StatementList, +} from './TopLevelLexicallyDeclaredNames.mjs'; +import { + BoundNames_Declaration, + BoundNames_FunctionDeclaration, +} from './BoundNames.mjs'; +import { + VarDeclaredNames_StatementListItem, +} from './VarDeclaredNames.mjs'; + +// 13.2.5 #sec-block-static-semantics-lexicallydeclarednames +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function LexicallyDeclaredNames_StatementList(StatementList) { + const names = []; + for (const StatementListItem of StatementList) { + names.push(...LexicallyDeclaredNames_StatementListItem(StatementListItem)); + } + return names; +} + +// 13.2.5 #sec-block-static-semantics-lexicallydeclarednames +// StatementListItem : +// Statement +// Declaration +export function LexicallyDeclaredNames_StatementListItem(StatementListItem) { + switch (true) { + case isStatement(StatementListItem): + if (isLabelledStatement(StatementListItem)) { + return LexicallyDeclaredNames_LabelledStatement(StatementListItem); + } + return VarDeclaredNames_StatementListItem(StatementListItem); + case isDeclaration(StatementListItem): + return BoundNames_Declaration(StatementListItem); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.13.10 #sec-labelled-statements-static-semantics-toplevelvardeclarednames +// LabelledStatement : LabelIdentifier `:` LabelledItem +export function LexicallyDeclaredNames_LabelledStatement(LabelledStatement) { + return LexicallyDeclaredNames_LabelledItem(LabelledStatement.body); +} + +// 13.13.10 #sec-labelled-statements-static-semantics-toplevelvardeclarednames +// LabelledItem : +// Statement +// FunctionDeclaration +export function LexicallyDeclaredNames_LabelledItem(LabelledItem) { + switch (true) { + case isStatement(LabelledItem): + return []; + case isFunctionDeclaration(LabelledItem): + return BoundNames_FunctionDeclaration(LabelledItem); + default: + throw new TypeError(`Unexpected LabelledItem: ${LabelledItem.type}`); + } +} + +// 14.1.14 #sec-function-definitions-static-semantics-lexicallydeclarednames +// FunctionStatementList : +// [empty] +// StatementList +export const + LexicallyDeclaredNames_FunctionStatementList = TopLevelLexicallyDeclaredNames_StatementList; + +// (implicit) +// FunctionBody : FunctionStatementList +export const LexicallyDeclaredNames_FunctionBody = LexicallyDeclaredNames_FunctionStatementList; + +// (implicit) +// GeneratorBody : FunctionBody +export const LexicallyDeclaredNames_GeneratorBody = LexicallyDeclaredNames_FunctionBody; + +// (implicit) +// AsyncFunctionBody : FunctionBody +export const LexicallyDeclaredNames_AsyncFunctionBody = LexicallyDeclaredNames_FunctionBody; + +// (implicit) +// AsyncGeneratorBody : FunctionBody +export const LexicallyDeclaredNames_AsyncGeneratorBody = LexicallyDeclaredNames_FunctionBody; + +// 14.2.10 #sec-arrow-function-definitions-static-semantics-lexicallydeclarednames +// ConciseBody : ExpressionBody +// +// (implicit) +// ConciseBody : `{` FunctionBody `}` +export function LexicallyDeclaredNames_ConciseBody(ConciseBody) { + switch (true) { + case isExpressionBody(ConciseBody): + return []; + case isBlockStatement(ConciseBody): + return LexicallyDeclaredNames_FunctionBody(ConciseBody.body); + default: + throw new TypeError(`Unexpected ConciseBody: ${ConciseBody.type}`); + } +} + +// 14.8.9 #sec-async-arrow-function-definitions-static-semantics-LexicallyDeclaredNames +// AsyncConciseBody : [lookahead ≠ `{`] ExpressionBody +// +// (implicit) +// AsyncConciseBody : `{` AsyncFunctionBody `}` +// AsyncFunctionBody : FunctionBody +export const LexicallyDeclaredNames_AsyncConciseBody = LexicallyDeclaredNames_ConciseBody; + +// 15.1.3 #sec-scripts-static-semantics-lexicallydeclarednames +// ScriptBody : StatementList +export const LexicallyDeclaredNames_ScriptBody = TopLevelLexicallyDeclaredNames_StatementList; diff --git a/src/engine262/src/static-semantics/LexicallyScopedDeclarations.mjs b/src/engine262/src/static-semantics/LexicallyScopedDeclarations.mjs new file mode 100644 index 0000000..c41ad39 --- /dev/null +++ b/src/engine262/src/static-semantics/LexicallyScopedDeclarations.mjs @@ -0,0 +1,199 @@ +import { + isBlockStatement, + isDeclaration, + isExpressionBody, + isFunctionDeclaration, + isLabelledStatement, + isStatement, + isSwitchCase, + isImportDeclaration, + isExportDeclaration, + isExportDeclarationWithStar, + isExportDeclarationWithVariable, + isExportDeclarationWithDeclaration, + isExportDeclarationWithExport, + isExportDeclarationWithExportAndFrom, + isExportDeclarationWithDefaultAndHoistable, + isExportDeclarationWithDefaultAndClass, + isExportDeclarationWithDefaultAndExpression, + isStatementListItem, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + DeclarationPart_Declaration, + DeclarationPart_HoistableDeclaration, + TopLevelLexicallyScopedDeclarations_StatementList, +} from './all.mjs'; + +// 13.2.6 #sec-block-static-semantics-lexicallyscopeddeclarations +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function LexicallyScopedDeclarations_StatementList(StatementList) { + const declarations = []; + for (const StatementListItem of StatementList) { + declarations.push(...LexicallyScopedDeclarations_StatementListItem(StatementListItem)); + } + return declarations; +} + +// 13.2.6 #sec-block-static-semantics-lexicallyscopeddeclarations +// StatementListItem : +// Statement +// Declaration +export function LexicallyScopedDeclarations_StatementListItem(StatementListItem) { + switch (true) { + case isStatement(StatementListItem): + if (isLabelledStatement(StatementListItem)) { + return LexicallyScopedDeclarations_LabelledStatement(StatementListItem); + } + return []; + case isDeclaration(StatementListItem): + return [DeclarationPart_Declaration(StatementListItem)]; + case isSwitchCase(StatementListItem): + return LexicallyScopedDeclarations_StatementList(StatementListItem.consequent); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.13.7 #sec-labelled-statements-static-semantics-lexicallyscopeddeclarations +// LabelledStatement : LabelIdentifier `:` LabelledItem +export function LexicallyScopedDeclarations_LabelledStatement(LabelledStatement) { + return LexicallyScopedDeclarations_LabelledItem(LabelledStatement.body); +} + +// 13.13.7 #sec-labelled-statements-static-semantics-lexicallyscopeddeclarations +// LabelledItem : +// Statement +// FunctionDeclaration +export function LexicallyScopedDeclarations_LabelledItem(LabelledItem) { + switch (true) { + case isStatement(LabelledItem): + return []; + case isFunctionDeclaration(LabelledItem): + return [LabelledItem]; + default: + throw new TypeError(`Unexpected LabelledItem: ${LabelledItem.type}`); + } +} + +// 14.1.14 #sec-function-definitions-static-semantics-lexicallydeclarednames +// FunctionStatementList : +// [empty] +// StatementList +export const // eslint-disable-next-line max-len + LexicallyScopedDeclarations_FunctionStatementList = TopLevelLexicallyScopedDeclarations_StatementList; + +// (implicit) +// FunctionBody : FunctionStatementList +export const LexicallyScopedDeclarations_FunctionBody = LexicallyScopedDeclarations_FunctionStatementList; + +// (implicit) +// GeneratorBody : FunctionBody +// AsyncFunctionBody : FunctionBody +export const LexicallyScopedDeclarations_GeneratorBody = LexicallyScopedDeclarations_FunctionBody; +export const LexicallyScopedDeclarations_AsyncFunctionBody = LexicallyScopedDeclarations_FunctionBody; + +// 14.2.11 #sec-arrow-function-definitions-static-semantics-lexicallyscopeddeclarations +// ConciseBody : ExpressionBody +// +// (implicit) +// ConciseBody : `{` FunctionBody `}` +export function LexicallyScopedDeclarations_ConciseBody(ConciseBody) { + switch (true) { + case isExpressionBody(ConciseBody): + return []; + case isBlockStatement(ConciseBody): + return LexicallyScopedDeclarations_FunctionBody(ConciseBody.body); + default: + throw new TypeError(`Unexpected ConciseBody: ${ConciseBody.type}`); + } +} + +// 14.8.10 #sec-async-arrow-function-definitions-static-semantics-LexicallyScopedDeclarations +// AsyncConciseBody : [lookahead ≠ `{`] ExpressionBody +// +// (implicit) +// AsyncConciseBody : `{` AsyncFunctionBody `}` +// AsyncFunctionBody : FunctionBody +export const LexicallyScopedDeclarations_AsyncConciseBody = LexicallyScopedDeclarations_ConciseBody; + +// 15.1.4 #sec-scripts-static-semantics-lexicallyscopeddeclarations +// ScriptBody : StatementList +export const LexicallyScopedDeclarations_ScriptBody = TopLevelLexicallyScopedDeclarations_StatementList; + +// 15.2.3.8 #sec-exports-static-semantics-lexicallyscopeddeclarations +// ExportDeclaration : +// `export` `*` FromClause `;` +// `export` ExportClause FromClause `;` +// `export` ExportClause `;` +// `export` VariableStatement +// `export` Declaration +// `export` `default` HoistableDeclaration +// `export` `default` ClassDeclaration +// `export` `default` AssignmentExpression `;` +export function LexicallyScopedDeclarations_ExportDeclaration(ExportDeclaration) { + switch (true) { + case isExportDeclarationWithStar(ExportDeclaration): + case isExportDeclarationWithExportAndFrom(ExportDeclaration): + case isExportDeclarationWithExport(ExportDeclaration): + case isExportDeclarationWithVariable(ExportDeclaration): + return []; + case isExportDeclarationWithDeclaration(ExportDeclaration): + return [DeclarationPart_Declaration(ExportDeclaration.declaration)]; + case isExportDeclarationWithDefaultAndHoistable(ExportDeclaration): + return [DeclarationPart_HoistableDeclaration(ExportDeclaration.declaration)]; + case isExportDeclarationWithDefaultAndClass(ExportDeclaration): + return [ExportDeclaration.declaration]; + case isExportDeclarationWithDefaultAndExpression(ExportDeclaration): + return [ExportDeclaration]; + default: + throw new OutOfRange('LexicallyScopedDeclarations_ExportDeclaration', ExportDeclaration); + } +} + +// 15.2.1.12 #sec-module-semantics-static-semantics-lexicallyscopeddeclarations +// ModuleItem : ImportDeclaration +// +// (implicit) +// ModuleItem : +// ExportDeclaration +// StatementListItem +export function LexicallyScopedDeclarations_ModuleItem(ModuleItem) { + switch (true) { + case isImportDeclaration(ModuleItem): + return []; + case isExportDeclaration(ModuleItem): + return LexicallyScopedDeclarations_ExportDeclaration(ModuleItem); + case isStatementListItem(ModuleItem): + return LexicallyScopedDeclarations_StatementListItem(ModuleItem); + default: + throw new OutOfRange('LexicallyScopedDeclarations_ModuleItem', ModuleItem); + } +} + +// 15.2.1.12 #sec-module-semantics-static-semantics-lexicallyscopeddeclarations +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function LexicallyScopedDeclarations_ModuleItemList(ModuleItemList) { + const declarations = []; + for (const ModuleItem of ModuleItemList) { + declarations.push(...LexicallyScopedDeclarations_ModuleItem(ModuleItem)); + } + return declarations; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const LexicallyScopedDeclarations_ModuleBody = LexicallyScopedDeclarations_ModuleItemList; + +// 15.2.1.12 #sec-module-semantics-static-semantics-lexicallyscopeddeclarations +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const LexicallyScopedDeclarations_Module = LexicallyScopedDeclarations_ModuleBody; diff --git a/src/engine262/src/static-semantics/MV.mjs b/src/engine262/src/static-semantics/MV.mjs new file mode 100644 index 0000000..7d048e4 --- /dev/null +++ b/src/engine262/src/static-semantics/MV.mjs @@ -0,0 +1,27 @@ +import nearley from 'nearley'; +import { Assert } from '../abstract-ops/all.mjs'; +import grammar from '../grammar/StrNumericLiteral-gen.mjs'; + +const { ParserRules } = grammar; + +const NumericLiteralGrammar = nearley.Grammar.fromCompiled({ + ParserRules, + ParserStart: 'NumericLiteral', +}); + +// 11.8.3.1 #sec-static-semantics-mv +// NumericLiteral :: +// DecimalLiteral +// BinaryIntegerLiteral +// OctalIntegerLiteral +// HexIntegerLiteral +export function MV_NumericLiteral(NumericLiteral) { + const parser = new nearley.Parser(NumericLiteralGrammar); + try { + parser.feed(NumericLiteral); + } catch (err) { + return NaN; + } + Assert(parser.results.length === 1); + return parser.results[0].toNumber(); +} diff --git a/src/engine262/src/static-semantics/ModuleRequests.mjs b/src/engine262/src/static-semantics/ModuleRequests.mjs new file mode 100644 index 0000000..ac3ba52 --- /dev/null +++ b/src/engine262/src/static-semantics/ModuleRequests.mjs @@ -0,0 +1,108 @@ +import { + isExportDeclaration, + isExportDeclarationWithDeclaration, + isExportDeclarationWithDefaultAndClass, + isExportDeclarationWithDefaultAndExpression, + isExportDeclarationWithDefaultAndHoistable, + isExportDeclarationWithExport, + isExportDeclarationWithExportAndFrom, + isExportDeclarationWithStar, + isExportDeclarationWithVariable, + isImportDeclaration, + isStatementListItem, +} from '../ast.mjs'; +import { Value } from '../value.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 15.2.1.10 #sec-module-semantics-static-semantics-modulerequests +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function ModuleRequests_ModuleItemList(ModuleItemList) { + const moduleNames = new Set(); + for (const ModuleItem of ModuleItemList) { + for (const additionalName of ModuleRequests_ModuleItem(ModuleItem)) { + moduleNames.add(additionalName); + } + } + return [...moduleNames]; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const ModuleRequests_ModuleBody = ModuleRequests_ModuleItemList; + +// 15.2.1.10 #sec-module-semantics-static-semantics-modulerequests +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const ModuleRequests_Module = ModuleRequests_ModuleBody; + +// 15.2.1.10 #sec-module-semantics-static-semantics-modulerequests +// ModuleItem : StatementListItem +// +// (implicit) +// ModuleItem : ImportDeclaration +// ModuleItem : ExportDeclaration +export function ModuleRequests_ModuleItem(ModuleItem) { + switch (true) { + case isStatementListItem(ModuleItem): + return []; + + case isImportDeclaration(ModuleItem): + return ModuleRequests_ImportDeclaration(ModuleItem); + + case isExportDeclaration(ModuleItem): + return ModuleRequests_ExportDeclaration(ModuleItem); + + default: + throw new OutOfRange('ModuleRequests_ModuleItem', ModuleItem); + } +} + +// 15.2.2.5 #sec-imports-static-semantics-modulerequests +// ImportDeclaration : `import` ImportClause FromClause `;` +export function ModuleRequests_ImportDeclaration(ImportDeclaration) { + const { source: FromClause } = ImportDeclaration; + return ModuleRequests_FromClause(FromClause); +} + +// 15.2.2.5 #sec-imports-static-semantics-modulerequests +// ModuleSpecifier : StringLiteral +// +// (implicit) +// FromClause : `from` ModuleSpecifier +export function ModuleRequests_FromClause(FromClause) { + return [new Value(FromClause.value)]; +} + +// 15.2.3.9 #sec-exports-static-semantics-modulerequests +// ExportDeclaration : +// `export` `*` FromClause `;` +// `export` ExportClause FromClause `;` +// `export` ExportClause `;` +// `export` VariableStatement +// `export` Declaration +// `export` `default` HoistableDeclaration +// `export` `default` ClassDeclaration +// `export` `default` AssignmentExpression `;` +export function ModuleRequests_ExportDeclaration(ExportDeclaration) { + switch (true) { + case isExportDeclarationWithStar(ExportDeclaration): + case isExportDeclarationWithExportAndFrom(ExportDeclaration): + return ModuleRequests_FromClause(ExportDeclaration.source); + + case isExportDeclarationWithExport(ExportDeclaration): + case isExportDeclarationWithVariable(ExportDeclaration): + case isExportDeclarationWithDeclaration(ExportDeclaration): + case isExportDeclarationWithDefaultAndHoistable(ExportDeclaration): + case isExportDeclarationWithDefaultAndClass(ExportDeclaration): + case isExportDeclarationWithDefaultAndExpression(ExportDeclaration): + return []; + + default: + throw new OutOfRange('ModuleRequests_ExportDeclaration', ExportDeclaration); + } +} diff --git a/src/engine262/src/static-semantics/NonConstructorMethodDefinitions.mjs b/src/engine262/src/static-semantics/NonConstructorMethodDefinitions.mjs new file mode 100644 index 0000000..0bd257d --- /dev/null +++ b/src/engine262/src/static-semantics/NonConstructorMethodDefinitions.mjs @@ -0,0 +1,11 @@ +// 14.6.10 #sec-static-semantics-nonconstructormethoddefinitions +// ClassElementList : +// ClassElement +// ClassElementList ClassElement +function NonConstructorMethodDefinitions_ClassElementList(ClassElementList) { + return ClassElementList.filter((ClassElement) => ClassElement.kind !== 'constructor'); +} + +// (implicit) +// ClassBody : ClassElementList +export const NonConstructorMethodDefinitions_ClassBody = NonConstructorMethodDefinitions_ClassElementList; diff --git a/src/engine262/src/static-semantics/TRV.mjs b/src/engine262/src/static-semantics/TRV.mjs new file mode 100644 index 0000000..823aed9 --- /dev/null +++ b/src/engine262/src/static-semantics/TRV.mjs @@ -0,0 +1,40 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { OutOfRange } from '../helpers.mjs'; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// NoSubstitutionTemplate :: +// `\`` `\`` +// `\`` TemplateCharacters `\`` +export function TRV_NoSubstitutionTemplate(NoSubstitutionTemplate) { + if (NoSubstitutionTemplate.quasis.length !== 1) { + throw new OutOfRange('TRV_NoSubstitutionTemplate', NoSubstitutionTemplate); + } + return TRV_TemplateCharacters(NoSubstitutionTemplate.quasis[0]); +} + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateCharacters :: +// TemplateCharacter +// TemplateCharacter TemplateCharacters +export function TRV_TemplateCharacters(TemplateCharacters) { + Assert(typeof TemplateCharacters.value.raw === 'string'); + return TemplateCharacters.value.raw; +} + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateHead :: +// `\`` `${` +// `\`` TemplateCharacters `${` +export const TRV_TemplateHead = TRV_TemplateCharacters; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateMiddle :: +// `}` `${` +// `}` TemplateCharacters `${` +export const TRV_TemplateMiddle = TRV_TemplateCharacters; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateTail :: +// `}` `\`` +// `}` TemplateCharacters `\`` +export const TRV_TemplateTail = TRV_TemplateCharacters; diff --git a/src/engine262/src/static-semantics/TV.mjs b/src/engine262/src/static-semantics/TV.mjs new file mode 100644 index 0000000..7da760d --- /dev/null +++ b/src/engine262/src/static-semantics/TV.mjs @@ -0,0 +1,41 @@ +import { OutOfRange } from '../helpers.mjs'; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// NoSubstitutionTemplate :: +// `\`` `\`` +// `\`` TemplateCharacters `\`` +export function TV_NoSubstitutionTemplate(NoSubstitutionTemplate) { + if (NoSubstitutionTemplate.quasis.length !== 1) { + throw new OutOfRange('TV_NoSubstitutionTemplate', NoSubstitutionTemplate); + } + return TV_TemplateCharacters(NoSubstitutionTemplate.quasis[0]); +} + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateCharacters :: +// TemplateCharacter +// TemplateCharacter TemplateCharacters +export function TV_TemplateCharacters(TemplateCharacters) { + if (TemplateCharacters.value.cooked === null) { + return undefined; + } + return TemplateCharacters.value.cooked; +} + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateHead :: +// `\`` `${` +// `\`` TemplateCharacters `${` +export const TV_TemplateHead = TV_TemplateCharacters; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateMiddle :: +// `}` `${` +// `}` TemplateCharacters `${` +export const TV_TemplateMiddle = TV_TemplateCharacters; + +// 11.8.6.1 #sec-static-semantics-tv-and-trv +// TemplateTail :: +// `}` `\`` +// `}` TemplateCharacters `\`` +export const TV_TemplateTail = TV_TemplateCharacters; diff --git a/src/engine262/src/static-semantics/TemplateStrings.mjs b/src/engine262/src/static-semantics/TemplateStrings.mjs new file mode 100644 index 0000000..4ce02ca --- /dev/null +++ b/src/engine262/src/static-semantics/TemplateStrings.mjs @@ -0,0 +1,99 @@ +import { Assert } from '../abstract-ops/notational-conventions.mjs'; +import { + isNoSubstitutionTemplate, + isSubstitutionTemplate, + unrollTemplateLiteral, +} from '../ast.mjs'; +import { OutOfRange } from '../helpers.mjs'; +import { + TV_NoSubstitutionTemplate, + TV_TemplateHead, + TV_TemplateMiddle, + TV_TemplateTail, + TRV_NoSubstitutionTemplate, + TRV_TemplateHead, + TRV_TemplateMiddle, + TRV_TemplateTail, +} from './all.mjs'; + +// 12.2.9.2 #sec-static-semantics-templatestrings +// TemplateLiteral : NoSubstitutionTemplate +// +// (implicit) +// TemplateLiteral : SubstitutionTemplate +export function TemplateStrings_TemplateLiteral(TemplateLiteral, raw) { + switch (true) { + case isNoSubstitutionTemplate(TemplateLiteral): { + let string; + if (raw === false) { + string = TV_NoSubstitutionTemplate(TemplateLiteral); + } else { + string = TRV_NoSubstitutionTemplate(TemplateLiteral); + } + return [string]; + } + + case isSubstitutionTemplate(TemplateLiteral): + return TemplateStrings_SubstitutionTemplate(TemplateLiteral, raw); + + default: + throw new OutOfRange('TemplateStrings_TemplateLiteral', TemplateLiteral); + } +} + +// 12.2.9.2 #sec-static-semantics-templatestrings +// SubstitutionTemplate : TemplateHead Expression TemplateSpans +export function TemplateStrings_SubstitutionTemplate(SubstitutionTemplate, raw) { + const [TemplateHead, /* Expression */, ...TemplateSpans] = unrollTemplateLiteral(SubstitutionTemplate); + + let head; + if (raw === false) { + head = TV_TemplateHead(TemplateHead); + } else { + head = TRV_TemplateHead(TemplateHead); + } + const tail = TemplateStrings_TemplateSpans(TemplateSpans, raw); + return [head, ...tail]; +} + +// 12.2.9.2 #sec-static-semantics-templatestrings +// TemplateSpans : +// TemplateTail +// TemplateMiddleList TemplateTail +export function TemplateStrings_TemplateSpans(TemplateSpans, raw) { + let middle = []; + Assert(TemplateSpans.length % 2 === 1); + if (TemplateSpans.length > 1) { + middle = TemplateStrings_TemplateMiddleList(TemplateSpans.slice(0, -1), raw); + } + + const TemplateTail = TemplateSpans[TemplateSpans.length - 1]; + let tail; + if (raw === false) { + tail = TV_TemplateTail(TemplateTail); + } else { + tail = TRV_TemplateTail(TemplateTail); + } + + return [...middle, tail]; +} + +// 12.2.9.2 #sec-static-semantics-templatestrings +// TemplateMiddleList : +// TemplateMiddle Expression +// TemplateMiddleList TemplateMiddle Expression +export function TemplateStrings_TemplateMiddleList(TemplateMiddleList, raw) { + const front = []; + Assert(TemplateMiddleList.length % 2 === 0); + for (let i = 0; i < TemplateMiddleList.length; i += 2) { + const TemplateMiddle = TemplateMiddleList[i]; + let last; + if (raw === false) { + last = TV_TemplateMiddle(TemplateMiddle); + } else { + last = TRV_TemplateMiddle(TemplateMiddle); + } + front.push(last); + } + return front; +} diff --git a/src/engine262/src/static-semantics/TopLevelLexicallyDeclaredNames.mjs b/src/engine262/src/static-semantics/TopLevelLexicallyDeclaredNames.mjs new file mode 100644 index 0000000..d51d1ea --- /dev/null +++ b/src/engine262/src/static-semantics/TopLevelLexicallyDeclaredNames.mjs @@ -0,0 +1,39 @@ +import { + isDeclaration, + isHoistableDeclaration, + isStatement, +} from '../ast.mjs'; +import { + BoundNames_Declaration, +} from './BoundNames.mjs'; + +// 13.2.7 #sec-block-static-semantics-toplevellexicallydeclarednames +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function TopLevelLexicallyDeclaredNames_StatementList(StatementList) { + const names = []; + for (const StatementListItem of StatementList) { + names.push(...TopLevelLexicallyDeclaredNames_StatementListItem(StatementListItem)); + } + return names; +} + +// 13.2.7 #sec-block-static-semantics-toplevellexicallydeclarednames +// StatementListItem : +// Statement +// Declaration +export function TopLevelLexicallyDeclaredNames_StatementListItem(StatementListItem) { + switch (true) { + case isStatement(StatementListItem): + return []; + case isDeclaration(StatementListItem): + if (isHoistableDeclaration(StatementListItem)) { + return []; + } + return BoundNames_Declaration(StatementListItem); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} diff --git a/src/engine262/src/static-semantics/TopLevelLexicallyScopedDeclarations.mjs b/src/engine262/src/static-semantics/TopLevelLexicallyScopedDeclarations.mjs new file mode 100644 index 0000000..6f1f1c2 --- /dev/null +++ b/src/engine262/src/static-semantics/TopLevelLexicallyScopedDeclarations.mjs @@ -0,0 +1,36 @@ +import { + isDeclaration, + isHoistableDeclaration, + isStatement, +} from '../ast.mjs'; + +// 13.2.8 #sec-block-static-semantics-toplevellexicallyscopeddeclarations +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function TopLevelLexicallyScopedDeclarations_StatementList(StatementList) { + const declarations = []; + for (const StatementListItem of StatementList) { + declarations.push(...TopLevelLexicallyScopedDeclarations_StatementListItem(StatementListItem)); + } + return declarations; +} + +// 13.2.8 #sec-block-static-semantics-toplevellexicallyscopeddeclarations +// StatementListItem : +// Statement +// Declaration +export function TopLevelLexicallyScopedDeclarations_StatementListItem(StatementListItem) { + switch (true) { + case isStatement(StatementListItem): + return []; + case isDeclaration(StatementListItem): + if (isHoistableDeclaration(StatementListItem)) { + return []; + } + return [StatementListItem]; + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} diff --git a/src/engine262/src/static-semantics/TopLevelVarDeclaredNames.mjs b/src/engine262/src/static-semantics/TopLevelVarDeclaredNames.mjs new file mode 100644 index 0000000..67e4488 --- /dev/null +++ b/src/engine262/src/static-semantics/TopLevelVarDeclaredNames.mjs @@ -0,0 +1,67 @@ +import { + isDeclaration, + isFunctionDeclaration, + isHoistableDeclaration, + isLabelledStatement, + isStatement, +} from '../ast.mjs'; +import { + BoundNames_FunctionDeclaration, + BoundNames_HoistableDeclaration, +} from './BoundNames.mjs'; +import { VarDeclaredNames_Statement } from './VarDeclaredNames.mjs'; + +// 13.2.9 #sec-block-static-semantics-toplevelvardeclarednames +// StatementList : StatementList StatementListItem +export function TopLevelVarDeclaredNames_StatementList(StatementList) { + const names = []; + for (const StatementListItem of StatementList) { + names.push(...TopLevelVarDeclaredNames_StatementListItem(StatementListItem)); + } + return names; +} + +// 13.2.9 #sec-block-static-semantics-toplevelvardeclarednames +// StatementListItem : +// Declaration +// Statement +export function TopLevelVarDeclaredNames_StatementListItem(StatementListItem) { + switch (true) { + case isDeclaration(StatementListItem): + if (isHoistableDeclaration(StatementListItem)) { + return BoundNames_HoistableDeclaration(StatementListItem); + } + return []; + case isStatement(StatementListItem): + if (isLabelledStatement(StatementListItem)) { + return TopLevelVarDeclaredNames_LabelledStatement(StatementListItem); + } + return VarDeclaredNames_Statement(StatementListItem); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.13.10 #sec-labelled-statements-static-semantics-toplevelvardeclarednames +// LabelledStatement : LabelIdentifier `:` LabelledItem +export function TopLevelVarDeclaredNames_LabelledStatement(LabelledStatement) { + return TopLevelVarDeclaredNames_LabelledItem(LabelledStatement.body); +} + +// 13.13.10 #sec-labelled-statements-static-semantics-toplevelvardeclarednames +// LabelledItem : +// Statement +// FunctionDeclaration +export function TopLevelVarDeclaredNames_LabelledItem(LabelledItem) { + switch (true) { + case isStatement(LabelledItem): + if (isLabelledStatement(LabelledItem)) { + return TopLevelVarDeclaredNames_LabelledItem(LabelledItem.body); + } + return VarDeclaredNames_Statement(LabelledItem); + case isFunctionDeclaration(LabelledItem): + return BoundNames_FunctionDeclaration(LabelledItem); + default: + throw new TypeError(`Unexpected LabelledItem: ${LabelledItem.type}`); + } +} diff --git a/src/engine262/src/static-semantics/TopLevelVarScopedDeclarations.mjs b/src/engine262/src/static-semantics/TopLevelVarScopedDeclarations.mjs new file mode 100644 index 0000000..6945a88 --- /dev/null +++ b/src/engine262/src/static-semantics/TopLevelVarScopedDeclarations.mjs @@ -0,0 +1,64 @@ +import { + isDeclaration, + isFunctionDeclaration, + isHoistableDeclaration, + isLabelledStatement, + isStatement, +} from '../ast.mjs'; +import { DeclarationPart_Declaration } from './DeclarationPart.mjs'; +import { VarScopedDeclarations_Statement } from './VarScopedDeclarations.mjs'; + +// 13.2.10 #sec-block-static-semantics-toplevelvarscopeddeclarations +// StatementList : StatementList StatementListItem +export function TopLevelVarScopedDeclarations_StatementList(StatementList) { + const declarations = []; + for (const StatementListItem of StatementList) { + declarations.push(...TopLevelVarScopedDeclarations_StatementListItem(StatementListItem)); + } + return declarations; +} + +// 13.2.10 #sec-block-static-semantics-toplevelvarscopeddeclarations +// StatementListItem : +// Statement +// Declaration +export function TopLevelVarScopedDeclarations_StatementListItem(StatementListItem) { + switch (true) { + case isStatement(StatementListItem): + if (isLabelledStatement(StatementListItem)) { + return TopLevelVarScopedDeclarations_LabelledStatement(StatementListItem); + } + return VarScopedDeclarations_Statement(StatementListItem); + case isDeclaration(StatementListItem): + if (isHoistableDeclaration(StatementListItem)) { + return [DeclarationPart_Declaration(StatementListItem)]; + } + return []; + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.13.11 #sec-labelled-statements-static-semantics-toplevelvarscopeddeclarations +// LabelledStatement : LabelIdentifier `:` LabelledItem +export function TopLevelVarScopedDeclarations_LabelledStatement(LabelledStatement) { + return TopLevelVarScopedDeclarations_LabelledItem(LabelledStatement.body); +} + +// 13.13.11 #sec-labelled-statements-static-semantics-toplevelvarscopeddeclarations +// LabelledItem : +// Statement +// FunctionDeclaration +export function TopLevelVarScopedDeclarations_LabelledItem(LabelledItem) { + switch (true) { + case isStatement(LabelledItem): + if (isLabelledStatement(LabelledItem)) { + return TopLevelVarScopedDeclarations_LabelledItem(LabelledItem.body); + } + return VarScopedDeclarations_Statement(LabelledItem); + case isFunctionDeclaration(LabelledItem): + return [LabelledItem]; + default: + throw new TypeError(`Unexpected LabelledItem: ${LabelledItem.type}`); + } +} diff --git a/src/engine262/src/static-semantics/VarDeclaredNames.mjs b/src/engine262/src/static-semantics/VarDeclaredNames.mjs new file mode 100644 index 0000000..affed79 --- /dev/null +++ b/src/engine262/src/static-semantics/VarDeclaredNames.mjs @@ -0,0 +1,371 @@ +import { + isBlockStatement, + isBreakStatement, + isContinueStatement, + isDebuggerStatement, + isDeclaration, + isEmptyStatement, + isExportDeclaration, + isExportDeclarationWithVariable, + isExpressionBody, + isExpressionStatement, + isFunctionDeclaration, + isIfStatement, + isImportDeclaration, + isIterationStatement, + isLabelledStatement, + isReturnStatement, + isStatement, + isSwitchStatement, + isThrowStatement, + isTryStatement, + isVariableStatement, + isWithStatement, +} from '../ast.mjs'; +import { + BoundNames_ForBinding, + BoundNames_VariableDeclarationList, + BoundNames_VariableStatement, +} from './BoundNames.mjs'; +import { + TopLevelVarDeclaredNames_StatementList, +} from './TopLevelVarDeclaredNames.mjs'; + +// 13.1.5 #sec-statement-semantics-static-semantics-vardeclarednames +// Statement : +// EmptyStatement +// ExpressionStatement +// ContinueStatement +// ContinueStatement +// BreakStatement +// ReturnStatement +// ThrowStatement +// DebuggerStatement +// +// (implicit) +// Statement : +// BlockStatement +// VariableStatement +// IfStatement +// BreakableStatement +// WithStatement +// LabelledStatement +// TryStatement +// BreakableStatement : +// IterationStatement +// SwitchStatement +export function VarDeclaredNames_Statement(Statement) { + switch (true) { + case isEmptyStatement(Statement): + case isExpressionStatement(Statement): + case isContinueStatement(Statement): + case isBreakStatement(Statement): + case isReturnStatement(Statement): + case isThrowStatement(Statement): + case isDebuggerStatement(Statement): + return []; + + case isBlockStatement(Statement): + return VarDeclaredNames_BlockStatement(Statement); + case isVariableStatement(Statement): + return VarDeclaredNames_VariableStatement(Statement); + case isIfStatement(Statement): + return VarDeclaredNames_IfStatement(Statement); + case isWithStatement(Statement): + return VarDeclaredNames_WithStatement(Statement); + case isLabelledStatement(Statement): + return VarDeclaredNames_LabelledStatement(Statement); + case isTryStatement(Statement): + return VarDeclaredNames_TryStatement(Statement); + case isIterationStatement(Statement): + return VarDeclaredNames_IterationStatement(Statement); + case isSwitchStatement(Statement): + return VarDeclaredNames_SwitchStatement(Statement); + + default: + throw new TypeError(`Invalid Statement: ${Statement.type}`); + } +} + +// 13.2.11 #sec-block-static-semantics-vardeclarednames +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function VarDeclaredNames_StatementList(StatementList) { + const names = []; + for (const StatementListItem of StatementList) { + names.push(...VarDeclaredNames_StatementListItem(StatementListItem)); + } + return names; +} + +// 13.2.11 #sec-block-static-semantics-vardeclarednames +// Block : `{` `}` +// +// (implicit) +// Block : `{` StatementList `}` +export function VarDeclaredNames_Block(Block) { + return VarDeclaredNames_StatementList(Block.body); +} + +// (implicit) +// BlockStatement : Block +export const VarDeclaredNames_BlockStatement = VarDeclaredNames_Block; + +// 13.2.11 #sec-block-static-semantics-vardeclarednames +// StatementListItem : Declaration +// +// (implicit) +// StatementListItem : Statement +export function VarDeclaredNames_StatementListItem(StatementListItem) { + switch (true) { + case isDeclaration(StatementListItem): + return []; + case isStatement(StatementListItem): + return VarDeclaredNames_Statement(StatementListItem); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.3.2.2 #sec-variable-statement-static-semantics-vardeclarednames +// VariableStatement : `var` VariableDeclarationList `;` +export const VarDeclaredNames_VariableStatement = BoundNames_VariableStatement; + +// 13.6.5 #sec-if-statement-static-semantics-vardeclarednames +// IfStatement : +// `if` `(` Expression `)` Statement `else` Statement +// `if` `(` Expression `)` Statement +export function VarDeclaredNames_IfStatement(IfStatement) { + if (IfStatement.alternate) { + return [ + ...VarDeclaredNames_Statement(IfStatement.consequent), + ...VarDeclaredNames_Statement(IfStatement.alternate), + ]; + } + return VarDeclaredNames_Statement(IfStatement.consequent); +} + +// 13.7.2.4 #sec-do-while-statement-static-semantics-vardeclarednames +// IterationStatement : `do` Statement `while` `(` Expression `)` `;` +// +// 13.7.3.4 #sec-while-statement-static-semantics-vardeclarednames +// IterationStatement : `while` `(` Expression `)` Statement +// +// 13.7.4.5 #sec-for-statement-static-semantics-vardeclarednames +// IterationStatement : +// `for` `(` Expression `;` Expression `;` Expression `)` Statement +// `for` `(` `var` VariableDeclarationList `;` Expression `;` Expression `)` Statement +// `for` `(` LexicalDeclaration Expression `;` Expression `)` Statement +// +// 13.7.5.7 #sec-for-in-and-for-of-statements-static-semantics-vardeclarednames +// IterationStatement : +// `for` `(` LeftHandSideExpression `in` Expression `)` Statement +// `for` `(` `var` ForBinding `in` Expression `)` Statement +// `for` `(` ForDeclaration `in` Expression `)` Statement +// `for` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `await` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `await` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `(` ForDeclaration `of` Expression `)` Statement +// `for` `await` `(` ForDeclaration `of` Expression `)` Statement +export function VarDeclaredNames_IterationStatement(IterationStatement) { + let namesFromBinding = []; + switch (IterationStatement.type) { + case 'DoWhileStatement': + case 'WhileStatement': + break; + case 'ForStatement': + if (IterationStatement.init && isVariableStatement(IterationStatement.init)) { + const VariableDeclarationList = IterationStatement.init.declarations; + namesFromBinding = BoundNames_VariableDeclarationList( + VariableDeclarationList, + ); + } + break; + case 'ForInStatement': + case 'ForOfStatement': + // https://github.com/tc39/ecma262/pull/1284 + if (isVariableStatement(IterationStatement.left)) { + const ForBinding = IterationStatement.left.declarations[0].id; + namesFromBinding = BoundNames_ForBinding(ForBinding); + } + break; + default: + throw new TypeError(`Invalid IterationStatement: ${IterationStatement.type}`); + } + return [ + ...namesFromBinding, + ...VarDeclaredNames_Statement(IterationStatement.body), + ]; +} + +// 13.11.6 #sec-with-statement-static-semantics-varscopeddeclarations +// WithStatement : `with` `(` Expression `)` Statement +export function VarDeclaredNames_WithStatement(WithStatement) { + return VarDeclaredNames_Statement(WithStatement.body); +} + +// 13.12.7 #sec-switch-statement-static-semantics-vardeclarednames +// SwitchStatement : `switch` `(` Expression `)` CaseBlock +export function VarDeclaredNames_SwitchStatement(SwitchStatement) { + return VarDeclaredNames_CaseBlock(SwitchStatement.cases); +} + +// 13.12.7 #sec-switch-statement-static-semantics-vardeclarednames +// CaseBlock : +// `{` `}` +// `{` CaseClauses_opt DefaultClause CaseClauses_opt `}` +// CaseClauses : CaseClauses CaseClause +// CaseClause : `case` Expression `:` StatementList_opt +// DefaultClause : `default` `:` StatementList_opt +// +// (implicit) +// CaseBlock : `{` CaseClauses `}` +// CaseClauses : CaseClause +export function VarDeclaredNames_CaseBlock(CaseBlock) { + const names = []; + for (const CaseClauseOrDefaultClause of CaseBlock) { + names.push(...VarDeclaredNames_StatementList(CaseClauseOrDefaultClause.consequent)); + } + return names; +} + +// 13.13.12 #sec-labelled-statements-static-semantics-vardeclarednames +// LabelledStatement : LabelIdentifier `:` LabelledItem +// LabelledItem : FunctionDeclaration +// +// (implicit) +// LabelledItem : Statement +export function VarDeclaredNames_LabelledStatement(LabelledStatement) { + const LabelledItem = LabelledStatement.body; + switch (true) { + case isFunctionDeclaration(LabelledItem): + return []; + case isStatement(LabelledItem): + return VarDeclaredNames_Statement(LabelledItem); + default: + throw new TypeError(`Invalid LabelledItem: ${LabelledItem.type}`); + } +} + +// 13.15.5 #sec-try-statement-static-semantics-vardeclarednames +// TryStatement : +// `try` Block Catch +// `try` Block Finally +// `try` Block Catch Finally +// Catch : `catch` `(` CatchParameter `)` Block +// +// (implicit) +// Catch : `catch` Block +// Finally : `finally` Block +export function VarDeclaredNames_TryStatement(TryStatement) { + const namesBlock = VarDeclaredNames_Block(TryStatement.block); + const namesCatch = TryStatement.handler !== null + ? VarDeclaredNames_Block(TryStatement.handler.body) : []; + const namesFinally = TryStatement.finalizer !== null + ? VarDeclaredNames_Block(TryStatement.finalizer) : []; + return [ + ...namesBlock, + ...namesCatch, + ...namesFinally, + ]; +} + +// 14.1.16 #sec-function-definitions-static-semantics-vardeclarednames +// FunctionStatementList : +// [empty] +// StatementList +export const VarDeclaredNames_FunctionStatementList = TopLevelVarDeclaredNames_StatementList; + +// (implicit) +// FunctionBody : FunctionStatementList +export const VarDeclaredNames_FunctionBody = VarDeclaredNames_FunctionStatementList; + +// (implicit) +// GeneratorBody : FunctionBody +export const VarDeclaredNames_GeneratorBody = VarDeclaredNames_FunctionBody; + +// (implicit) +// AsyncFunctionBody : FunctionBody +export const VarDeclaredNames_AsyncFunctionBody = VarDeclaredNames_FunctionBody; + +// 14.2.12 #sec-arrow-function-definitions-static-semantics-vardeclarednames +// ConciseBody : ExpressionBody +// +// (implicit) +// ConciseBody : `{` FunctionBody `}` +export function VarDeclaredNames_ConciseBody(ConciseBody) { + switch (true) { + case isExpressionBody(ConciseBody): + return []; + case isBlockStatement(ConciseBody): + return VarDeclaredNames_FunctionBody(ConciseBody.body); + default: + throw new TypeError(`Unexpected ConciseBody: ${ConciseBody.type}`); + } +} + +// 14.8.11 #sec-async-arrow-function-definitions-static-semantics-VarDeclaredNames +// AsyncConciseBody : [lookahead ≠ `{`] ExpressionBody +// +// (implicit) +// AsyncConciseBody : `{` AsyncFunctionBody `}` +// AsyncFunctionBody : FunctionBody +export const VarDeclaredNames_AsyncConciseBody = VarDeclaredNames_ConciseBody; + +// 15.1.5 #sec-scripts-static-semantics-vardeclarednames +// ScriptBody : StatementList +export const VarDeclaredNames_ScriptBody = TopLevelVarDeclaredNames_StatementList; + +// (implicit) +// Script : +// [empty] +// ScriptBody +export const VarDeclaredNames_Script = VarDeclaredNames_ScriptBody; + +// 15.2.1.13 #sec-module-semantics-static-semantics-vardeclarednames +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function VarDeclaredNames_ModuleItemList(ModuleItemList) { + const names = []; + for (const ModuleItem of ModuleItemList) { + names.push(...VarDeclaredNames_ModuleItem(ModuleItem)); + } + return names; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const VarDeclaredNames_ModuleBody = VarDeclaredNames_ModuleItemList; + +// 15.2.1.13 #sec-module-semantics-static-semantics-vardeclarednames +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const VarDeclaredNames_Module = VarDeclaredNames_ModuleBody; + +// 15.2.1.13 #sec-module-semantics-static-semantics-vardeclarednames +// ModuleItem : +// ImportDeclaration +// ExportDeclaration +// +// (implicit) +// ModuleItem : StatementListItem +export function VarDeclaredNames_ModuleItem(ModuleItem) { + switch (true) { + case isImportDeclaration(ModuleItem): + return []; + case isExportDeclaration(ModuleItem): + if (isExportDeclarationWithVariable(ModuleItem)) { + return BoundNames_VariableStatement(ModuleItem.declaration); + } + return []; + default: + return VarDeclaredNames_StatementListItem(ModuleItem); + } +} diff --git a/src/engine262/src/static-semantics/VarScopedDeclarations.mjs b/src/engine262/src/static-semantics/VarScopedDeclarations.mjs new file mode 100644 index 0000000..a220bb8 --- /dev/null +++ b/src/engine262/src/static-semantics/VarScopedDeclarations.mjs @@ -0,0 +1,375 @@ +import { + isBlockStatement, + isBreakStatement, + isContinueStatement, + isDebuggerStatement, + isDeclaration, + isEmptyStatement, + isExportDeclaration, + isExportDeclarationWithVariable, + isExpressionBody, + isExpressionStatement, + isFunctionDeclaration, + isIfStatement, + isImportDeclaration, + isIterationStatement, + isLabelledStatement, + isReturnStatement, + isStatement, + isSwitchStatement, + isThrowStatement, + isTryStatement, + isVariableStatement, + isWithStatement, +} from '../ast.mjs'; +import { + TopLevelVarScopedDeclarations_StatementList, +} from './TopLevelVarScopedDeclarations.mjs'; + +// 13.1.6 #sec-statement-semantics-static-semantics-varscopeddeclarations +// Statement : +// EmptyStatement +// ExpressionStatement +// ContinueStatement +// BreakStatement +// ReturnStatement +// ThrowStatement +// DebuggerStatement +// +// (implicit) +// Statement : +// BlockStatement +// VariableStatement +// IfStatement +// BreakableStatement +// WithStatement +// LabelledStatement +// TryStatement +// BreakableStatement : +// IterationStatement +// SwitchStatement +export function VarScopedDeclarations_Statement(Statement) { + switch (true) { + case isEmptyStatement(Statement): + case isExpressionStatement(Statement): + case isContinueStatement(Statement): + case isBreakStatement(Statement): + case isReturnStatement(Statement): + case isThrowStatement(Statement): + case isDebuggerStatement(Statement): + return []; + + case isBlockStatement(Statement): + return VarScopedDeclarations_BlockStatement(Statement); + case isVariableStatement(Statement): + return VarScopedDeclarations_VariableStatement(Statement); + case isIfStatement(Statement): + return VarScopedDeclarations_IfStatement(Statement); + case isWithStatement(Statement): + return VarScopedDeclarations_WithStatement(Statement); + case isLabelledStatement(Statement): + return VarScopedDeclarations_LabelledStatement(Statement); + case isTryStatement(Statement): + return VarScopedDeclarations_TryStatement(Statement); + case isIterationStatement(Statement): + return VarScopedDeclarations_IterationStatement(Statement); + case isSwitchStatement(Statement): + return VarScopedDeclarations_SwitchStatement(Statement); + + default: + throw new TypeError(`Invalid Statement: ${Statement.type}`); + } +} + +// 13.2.12 #sec-block-static-semantics-varscopeddeclarations +// StatementList : StatementList StatementListItem +// +// (implicit) +// StatementList : StatementListItem +export function VarScopedDeclarations_StatementList(StatementList) { + const declarations = []; + for (const StatementListItem of StatementList) { + declarations.push(...VarScopedDeclarations_StatementListItem(StatementListItem)); + } + return declarations; +} + +// 13.2.12 #sec-block-static-semantics-varscopeddeclarations +// Block : `{` `}` +// +// (implicit) +// Block : `{` StatementList `}` +export function VarScopedDeclarations_Block(Block) { + return VarScopedDeclarations_StatementList(Block.body); +} + +// (implicit) +// BlockStatement : Block +export const VarScopedDeclarations_BlockStatement = VarScopedDeclarations_Block; + +// 13.2.12 #sec-block-static-semantics-varscopeddeclarations +// StatementListItem : Declaration +// +// (implicit) +// StatementListItem : Statement +export function VarScopedDeclarations_StatementListItem(StatementListItem) { + switch (true) { + case isDeclaration(StatementListItem): + return []; + case isStatement(StatementListItem): + return VarScopedDeclarations_Statement(StatementListItem); + default: + throw new TypeError(`Unexpected StatementListItem: ${StatementListItem.type}`); + } +} + +// 13.3.2.3 #sec-variable-statement-static-semantics-varscopeddeclarations +// VariableDeclarationList : +// VariableDeclaration +// VariableDeclarationList `,` VariableDeclaration +export function VarScopedDeclarations_VariableDeclarationList(VariableDeclarationList) { + return VariableDeclarationList; +} + +// (implicit) +// VariableStatement : `var` VariableDeclarationList `;` +export function VarScopedDeclarations_VariableStatement(VariableStatement) { + return VarScopedDeclarations_VariableDeclarationList(VariableStatement.declarations); +} + +// 13.6.6 #sec-if-statement-static-semantics-varscopeddeclarations +// IfStatement : +// `if` `(` Expression `)` Statement `else` Statement +// `if` `(` Expression `)` Statement +export function VarScopedDeclarations_IfStatement(IfStatement) { + if (IfStatement.alternate) { + return [ + ...VarScopedDeclarations_Statement(IfStatement.consequent), + ...VarScopedDeclarations_Statement(IfStatement.alternate), + ]; + } + return VarScopedDeclarations_Statement(IfStatement.consequent); +} + +// 13.7.2.5 #sec-do-while-statement-static-semantics-varscopeddeclarations +// IterationStatement : `do` Statement `while` `(` Expression `)` `;` +// +// 13.7.3.5 #sec-while-statement-static-semantics-varscopeddeclarations +// IterationStatement : `while` `(` Expression `)` Statement +// +// 13.7.4.6 #sec-for-statement-static-semantics-varscopeddeclarations +// IterationStatement : +// `for` `(` Expression `;` Expression `;` Expression `)` Statement +// `for` `(` `var` VariableDeclarationList `;` Expression `;` Expression `)` Statement +// `for` `(` LexicalDeclaration Expression `;` Expression `)` Statement +// +// 13.7.5.8 #sec-for-in-and-for-of-statements-static-semantics-varscopeddeclarations +// IterationStatement : +// `for` `(` LeftHandSideExpression `in` Expression `)` Statement +// `for` `(` `var` ForBinding `in` Expression `)` Statement +// `for` `(` ForDeclaration `in` Expression `)` Statement +// `for` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `await` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement +// `for` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `await` `(` `var` ForBinding `of` AssignmentExpression `)` Statement +// `for` `(` ForDeclaration `of` Expression `)` Statement +// `for` `await` `(` ForDeclaration `of` Expression `)` Statement +export function VarScopedDeclarations_IterationStatement(IterationStatement) { + let declarationsFromBinding = []; + switch (IterationStatement.type) { + case 'DoWhileStatement': + case 'WhileStatement': + break; + case 'ForStatement': + if (IterationStatement.init && isVariableStatement(IterationStatement.init)) { + const VariableDeclarationList = IterationStatement.init.declarations; + declarationsFromBinding = VarScopedDeclarations_VariableDeclarationList( + VariableDeclarationList, + ); + } + break; + case 'ForInStatement': + case 'ForOfStatement': + if (isVariableStatement(IterationStatement.left)) { + const ForBinding = IterationStatement.left.declarations[0].id; + declarationsFromBinding = [ForBinding]; + } + break; + default: + throw new TypeError(`Invalid IterationStatement: ${IterationStatement.type}`); + } + return [ + ...declarationsFromBinding, + ...VarScopedDeclarations_Statement(IterationStatement.body), + ]; +} + +// 13.11.6 #sec-with-statement-static-semantics-varscopeddeclarations +// WithStatement : `with` `(` Expression `)` Statement +export function VarScopedDeclarations_WithStatement(WithStatement) { + return VarScopedDeclarations_Statement(WithStatement.body); +} + +// 13.12.8 #sec-switch-statement-static-semantics-varscopeddeclarations +// SwitchStatement : `switch` `(` Expression `)` CaseBlock +export function VarScopedDeclarations_SwitchStatement(SwitchStatement) { + return VarScopedDeclarations_CaseBlock(SwitchStatement.cases); +} + +// 13.12.8 #sec-switch-statement-static-semantics-varscopeddeclarations +// CaseBlock : +// `{` `}` +// `{` CaseClauses_opt DefaultClause CaseClauses_opt `}` +// CaseClauses : CaseClauses CaseClause +// CaseClause : `case` Expression `:` StatementList_opt +// DefaultClause : `default` `:` StatementList_opt +// +// (implicit) +// CaseBlock : `{` CaseClauses `}` +// CaseClauses : CaseClause +export function VarScopedDeclarations_CaseBlock(CaseBlock) { + const declarations = []; + for (const CaseClauseOrDefaultClause of CaseBlock) { + declarations.push(...VarScopedDeclarations_StatementList(CaseClauseOrDefaultClause.consequent)); + } + return declarations; +} + +// 13.13.13 #sec-labelled-statements-static-semantics-varscopeddeclarations +// LabelledStatement : LabelIdentifier `:` LabelledItem +// LabelledItem : FunctionDeclaration +// +// (implicit) +// LabelledItem : Statement +export function VarScopedDeclarations_LabelledStatement(LabelledStatement) { + const LabelledItem = LabelledStatement.body; + switch (true) { + case isFunctionDeclaration(LabelledItem): + return []; + case isStatement(LabelledItem): + return VarScopedDeclarations_Statement(LabelledItem); + default: + throw new TypeError(`Invalid LabelledItem: ${LabelledItem.type}`); + } +} + +// 13.15.6 #sec-try-statement-static-semantics-varscopeddeclarations +// TryStatement : +// `try` Block Catch +// `try` Block Finally +// `try` Block Catch Finally +// Catch : `catch` `(` CatchParameter `)` Block +// +// (implicit) +// Catch : `catch` Block +// Finally : `finally` Block +export function VarScopedDeclarations_TryStatement(TryStatement) { + const declarationsBlock = VarScopedDeclarations_Block(TryStatement.block); + const declarationsCatch = TryStatement.handler !== null + ? VarScopedDeclarations_Block(TryStatement.handler.body) : []; + const declarationsFinally = TryStatement.finalizer !== null + ? VarScopedDeclarations_Block(TryStatement.finalizer) : []; + return [ + ...declarationsBlock, + ...declarationsCatch, + ...declarationsFinally, + ]; +} + +// 14.1.17 #sec-function-definitions-static-semantics-varscopeddeclarations +// FunctionStatementList : +// [empty] +// StatementList +export const + VarScopedDeclarations_FunctionStatementList = TopLevelVarScopedDeclarations_StatementList; + +// (implicit) +// FunctionBody : FunctionStatementList +export const VarScopedDeclarations_FunctionBody = VarScopedDeclarations_FunctionStatementList; + +// (implicit) +// GeneratorBody : FunctionBody +export const VarScopedDeclarations_GeneratorBody = VarScopedDeclarations_FunctionBody; + +// (implicit) +// AsyncFunctionBody : FunctionBody +export const VarScopedDeclarations_AsyncFunctionBody = VarScopedDeclarations_FunctionBody; + +// 14.2.13 #sec-arrow-function-definitions-static-semantics-varscopeddeclarations +// ConciseBody : ExpressionBody +// +// (implicit) +// ConciseBody : `{` FunctionBody `}` +export function VarScopedDeclarations_ConciseBody(ConciseBody) { + switch (true) { + case isExpressionBody(ConciseBody): + return []; + case isBlockStatement(ConciseBody): + return VarScopedDeclarations_FunctionBody(ConciseBody.body); + default: + throw new TypeError(`Unexpected ConciseBody: ${ConciseBody.type}`); + } +} + +// 14.8.12 #sec-async-arrow-function-definitions-static-semantics-VarScopedDeclarations +// AsyncConciseBody : [lookahead ≠ `{`] ExpressionBody +// +// (implicit) +// AsyncConciseBody : `{` AsyncFunctionBody `}` +// AsyncFunctionBody : FunctionBody +export const VarScopedDeclarations_AsyncConciseBody = VarScopedDeclarations_ConciseBody; + +// 15.1.6 #sec-scripts-static-semantics-varscopeddeclarations +// ScriptBody : StatementList +export const VarScopedDeclarations_ScriptBody = TopLevelVarScopedDeclarations_StatementList; + +// (implicit) +// Script : +// [empty] +// ScriptBody +export const VarScopedDeclarations_Script = VarScopedDeclarations_ScriptBody; + +// 15.2.1.14 #sec-module-semantics-static-semantics-varscopeddeclarations +// ModuleItemList : ModuleItemList ModuleItem +// +// (implicit) +// ModuleItemList : ModuleItem +export function VarScopedDeclarations_ModuleItemList(ModuleItemList) { + const declarations = []; + for (const ModuleItem of ModuleItemList) { + declarations.push(...VarScopedDeclarations_ModuleItem(ModuleItem)); + } + return declarations; +} + +// (implicit) +// ModuleBody : ModuleItemList +export const VarScopedDeclarations_ModuleBody = VarScopedDeclarations_ModuleItemList; + +// 15.2.1.14 #sec-module-semantics-static-semantics-varscopeddeclarations +// Module : [empty] +// +// (implicit) +// Module : ModuleBody +export const VarScopedDeclarations_Module = VarScopedDeclarations_ModuleBody; + +// 15.2.1.14 #sec-module-semantics-static-semantics-varscopeddeclarations +// ModuleItem : +// ImportDeclaration +// ExportDeclaration +// +// (implicit) +// ModuleItem : StatementListItem +export function VarScopedDeclarations_ModuleItem(ModuleItem) { + switch (true) { + case isImportDeclaration(ModuleItem): + return []; + case isExportDeclaration(ModuleItem): + if (isExportDeclarationWithVariable(ModuleItem)) { + return VarScopedDeclarations_VariableStatement(ModuleItem.declaration); + } + return []; + default: + return VarScopedDeclarations_StatementListItem(ModuleItem); + } +} diff --git a/src/engine262/src/static-semantics/all.mjs b/src/engine262/src/static-semantics/all.mjs new file mode 100644 index 0000000..25ec4fb --- /dev/null +++ b/src/engine262/src/static-semantics/all.mjs @@ -0,0 +1,36 @@ +export * from './BoundNames.mjs'; +export * from './ConstructorMethod.mjs'; +export * from './ContainsExpression.mjs'; +export * from './ContainsUseStrict.mjs'; +export * from './DeclarationPart.mjs'; +export * from './ExpectedArgumentCount.mjs'; +export * from './ExportEntriesForModule.mjs'; +export * from './ExportEntries.mjs'; +export * from './HasInitializer.mjs'; +export * from './HasName.mjs'; +export * from './ImportEntries.mjs'; +export * from './ImportEntriesForModule.mjs'; +export * from './ImportedLocalNames.mjs'; +export * from './IsAnonymousFunctionDefinition.mjs'; +export * from './IsConstantDeclaration.mjs'; +export * from './IsDestructuring.mjs'; +export * from './IsFunctionDefinition.mjs'; +export * from './IsIdentifierRef.mjs'; +export * from './IsInTailPosition.mjs'; +export * from './IsSimpleParameterList.mjs'; +export * from './IsStatic.mjs'; +export * from './IsStrict.mjs'; +export * from './LexicallyDeclaredNames.mjs'; +export * from './LexicallyScopedDeclarations.mjs'; +export * from './MV.mjs'; +export * from './ModuleRequests.mjs'; +export * from './NonConstructorMethodDefinitions.mjs'; +export * from './TRV.mjs'; +export * from './TV.mjs'; +export * from './TemplateStrings.mjs'; +export * from './TopLevelLexicallyDeclaredNames.mjs'; +export * from './TopLevelLexicallyScopedDeclarations.mjs'; +export * from './TopLevelVarDeclaredNames.mjs'; +export * from './TopLevelVarScopedDeclarations.mjs'; +export * from './VarDeclaredNames.mjs'; +export * from './VarScopedDeclarations.mjs'; diff --git a/src/engine262/src/value.mjs b/src/engine262/src/value.mjs new file mode 100644 index 0000000..a6a852e --- /dev/null +++ b/src/engine262/src/value.mjs @@ -0,0 +1,851 @@ +import { surroundingAgent } from './engine.mjs'; +import { + Assert, + CreateBuiltinFunction, + OrdinaryDefineOwnProperty, + OrdinaryDelete, + OrdinaryGet, + OrdinaryGetOwnProperty, + OrdinaryGetPrototypeOf, + OrdinaryHasProperty, + OrdinaryIsExtensible, + OrdinaryOwnPropertyKeys, + OrdinaryPreventExtensions, + OrdinarySet, + OrdinarySetPrototypeOf, + ToInt32, + ToUint32, +} from './abstract-ops/all.mjs'; +import { EnvironmentRecord, LexicalEnvironment } from './environment.mjs'; +import { + Completion, + X, +} from './completion.mjs'; +import { ValueMap, OutOfRange } from './helpers.mjs'; + +export function Value(value) { + if (new.target !== undefined && new.target !== Value) { + return undefined; + } + + switch (typeof value) { + case 'string': + return new StringValue(value); + case 'number': + return new NumberValue(value); + case 'bigint': + return new BigIntValue(value); + case 'function': + return CreateBuiltinFunction(value, []); + default: + throw new OutOfRange('new Value', value); + } +} + +export class PrimitiveValue extends Value {} + +export class UndefinedValue extends PrimitiveValue {} + +export class NullValue extends PrimitiveValue {} + +export class BooleanValue extends PrimitiveValue { + constructor(v) { + super(); + this.boolean = v; + } + + booleanValue() { + return this.boolean; + } + + [Symbol.for('nodejs.util.inspect.custom')]() { + return `Boolean { ${this.boolean} }`; + } +} + +Object.defineProperties(Value, { + undefined: { value: new UndefinedValue(), configurable: false, writable: false }, + null: { value: new NullValue(), configurable: false, writable: false }, + true: { value: new BooleanValue(true), configurable: false, writable: false }, + false: { value: new BooleanValue(false), configurable: false, writable: false }, +}); + +export class NumberValue extends PrimitiveValue { + constructor(number) { + super(); + this.number = number; + } + + numberValue() { + return this.number; + } + + isNaN() { + return Number.isNaN(this.number); + } + + isInfinity() { + return !Number.isFinite(this.number) && !this.isNaN(); + } + + isFinite() { + return Number.isFinite(this.number); + } + + // #sec-numeric-types-number-unaryMinus + static unaryMinus(x) { + if (x.isNaN()) { + return new Value(NaN); + } + return new Value(-x.numberValue()); + } + + // #sec-numeric-types-number-bitwiseNOT + static bitwiseNOT(x) { + // 1. Let oldValue be ! ToInt32(x). + const oldValue = X(ToInt32(x)); + // 2. Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer. + return new Value(~oldValue.numberValue()); // eslint-disable-line no-bitwise + } + + // #sec-numeric-types-number-exponentiate + static exponentiate(base, exponent) { + return new Value(base.numberValue() ** exponent.numberValue()); + } + + // #sec-numeric-types-number-multiply + static multiply(x, y) { + return new Value(x.numberValue() * y.numberValue()); + } + + // #sec-numeric-types-number-divide + static divide(x, y) { + return new Value(x.numberValue() / y.numberValue()); + } + + // #sec-numeric-types-number-remainder + static remainder(n, d) { + return new Value(n.numberValue() % d.numberValue()); + } + + // #sec-numeric-types-number-add + static add(x, y) { + return new Value(x.numberValue() + y.numberValue()); + } + + // #sec-numeric-types-number-subtract + static subtract(x, y) { + // The result of - operator is x + (-y). + return NumberValue.add(x, new Value(-y.numberValue())); + } + + // #sec-numeric-types-number-leftShift + static leftShift(x, y) { + // 1. Let lnum be ! ToInt32(x). + const lnum = X(ToInt32(x)); + // 2. Let rnum be ! ToUint32(y). + const rnum = X(ToUint32(y)); + // 3. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F. + const shiftCount = rnum.numberValue() & 0x1F; // eslint-disable-line no-bitwise + // 4. Return the result of left shifting lnum by shiftCount bits. The result is a signed 32-bit integer. + return new Value(lnum.numberValue() << shiftCount); // eslint-disable-line no-bitwise + } + + // #sec-numeric-types-number-signedRightShift + static signedRightShift(x, y) { + // 1. Let lnum be ! ToInt32(x). + const lnum = X(ToInt32(x)); + // 2. Let rnum be ! ToUint32(y). + const rnum = X(ToUint32(y)); + // 3. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F. + const shiftCount = rnum.numberValue() & 0x1F; // eslint-disable-line no-bitwise + // 4. Return the result of performing a sign-extending right shift of lnum by shiftCount bits. + // The most significant bit is propagated. The result is a signed 32-bit integer. + return new Value(lnum.numberValue() >> shiftCount); // eslint-disable-line no-bitwise + } + + // #sec-numeric-types-number-unsignedRightShift + static unsignedRightShift(x, y) { + // 1. Let lnum be ! ToInt32(x). + const lnum = X(ToInt32(x)); + // 2. Let rnum be ! ToUint32(y). + const rnum = X(ToUint32(y)); + // 3. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F. + const shiftCount = rnum.numberValue() & 0x1F; // eslint-disable-line no-bitwise + // 4. Return the result of performing a zero-filling right shift of lnum by shiftCount bits. + // Vacated bits are filled with zero. The result is an unsigned 32-bit integer. + return new Value(lnum.numberValue() >>> shiftCount); // eslint-disable-line no-bitwise + } + + // #sec-numeric-types-number-lessThan + static lessThan(x, y) { + if (x.isNaN()) { + return Value.undefined; + } + if (y.isNaN()) { + return Value.undefined; + } + // If nx and ny are the same Number value, return false. + // If nx is +0 and ny is -0, return false. + // If nx is -0 and ny is +0, return false. + if (x.numberValue() === y.numberValue()) { + return Value.false; + } + if (x.numberValue() === +Infinity) { + return Value.false; + } + if (y.numberValue() === +Infinity) { + return Value.true; + } + if (y.numberValue() === -Infinity) { + return Value.false; + } + if (x.numberValue() === -Infinity) { + return Value.true; + } + return x.numberValue() < y.numberValue() ? Value.true : Value.false; + } + + // #sec-numeric-types-number-equal + static equal(x, y) { + if (x.isNaN()) { + return Value.false; + } + if (y.isNaN()) { + return Value.false; + } + const xVal = x.numberValue(); + const yVal = y.numberValue(); + if (xVal === yVal) { + return Value.true; + } + if (Object.is(xVal, 0) && Object.is(yVal, -0)) { + return Value.true; + } + if (Object.is(xVal, -0) && Object.is(yVal, 0)) { + return Value.true; + } + return Value.false; + } + + // #sec-numeric-types-number-sameValue + static sameValue(x, y) { + if (x.isNaN() && y.isNaN()) { + return Value.true; + } + const xVal = x.numberValue(); + const yVal = y.numberValue(); + if (Object.is(xVal, 0) && Object.is(yVal, -0)) { + return Value.false; + } + if (Object.is(xVal, -0) && Object.is(yVal, 0)) { + return Value.false; + } + if (xVal === yVal) { + return Value.true; + } + return Value.false; + } + + // #sec-numeric-types-number-sameValueZero + static sameValueZero(x, y) { + if (x.isNaN() && y.isNaN()) { + return Value.true; + } + const xVal = x.numberValue(); + const yVal = y.numberValue(); + if (Object.is(xVal, 0) && Object.is(yVal, -0)) { + return Value.true; + } + if (Object.is(xVal, -0) && Object.is(yVal, 0)) { + return Value.true; + } + if (xVal === yVal) { + return Value.true; + } + return Value.false; + } + + // #sec-numeric-types-number-bitwiseAND + static bitwiseAND(x, y) { + return NumberBitwiseOp('&', x, y); + } + + // #sec-numeric-types-number-bitwiseXOR + static bitwiseXOR(x, y) { + return NumberBitwiseOp('^', x, y); + } + + // #sec-numeric-types-number-bitwiseOR + static bitwiseOR(x, y) { + return NumberBitwiseOp('|', x, y); + } + + // #sec-numeric-types-number-tostring + static toString(x) { + if (x.isNaN()) { + return new Value('NaN'); + } + const xVal = x.numberValue(); + if (xVal === 0) { + return new Value('0'); + } + if (xVal < 0) { + const str = X(NumberValue.toString(new Value(-xVal))).stringValue(); + return new Value(`-${str}`); + } + if (x.isInfinity()) { + return new Value('Infinity'); + } + // TODO: implement properly + return new Value(`${xVal}`); + } +} + +NumberValue.unit = new NumberValue(1); + +// #sec-numberbitwiseop +function NumberBitwiseOp(op, x, y) { + // 1. Let lnum be ! ToInt32(x). + const lnum = X(ToInt32(x)); + // 2. Let rnum be ! ToUint32(y). + const rnum = X(ToUint32(y)); + // 3. Return the result of applying the bitwise operator op to lnum and rnum. The result is a signed 32-bit integer. + switch (op) { + case '&': + return new Value(lnum.numberValue() & rnum.numberValue()); // eslint-disable-line no-bitwise + case '|': + return new Value(lnum.numberValue() | rnum.numberValue()); // eslint-disable-line no-bitwise + case '^': + return new Value(lnum.numberValue() ^ rnum.numberValue()); // eslint-disable-line no-bitwise + default: + throw new OutOfRange('NumberBitwiseOp', op); + } +} + +export class BigIntValue extends PrimitiveValue { + constructor(bigint) { + super(); + this.bigint = bigint; + } + + bigintValue() { + return this.bigint; + } + + isNaN() { + return false; + } + + isFinite() { + return true; + } + + // #sec-numeric-types-bigint-unaryMinus + static unaryMinus(x) { + if (x.bigintValue() === 0n) { + return new Value(0n); + } + return new Value(-x.bigintValue()); + } + + // #sec-numeric-types-bigint-bitwiseNOT + static bitwiseNOT(x) { + return new Value(-x.bigintValue() - 1n); + } + + // #sec-numeric-types-bigint-exponentiate + static exponentiate(base, exponent) { + // 1. If exponent < 0n, throw a RangeError exception. + if (exponent.bigintValue() < 0n) { + return surroundingAgent.Throw('RangeError', 'BigIntNegativeExponent'); + } + // 2. If base is 0n and exponent is 0n, return 1n. + if (base.bigintValue() === 0n && exponent.bigintValue() === 0n) { + return new Value(1n); + } + // 3. Return the BigInt value that represents the mathematical value of base raised to the power exponent. + return new Value(base.bigintValue() ** exponent.bigintValue()); + } + + // #sec-numeric-types-bigint-multiply + static multiply(x, y) { + return new Value(x.bigintValue() * y.bigintValue()); + } + + // #sec-numeric-types-bigint-divide + static divide(x, y) { + // 1. If y is 0n, throw a RangeError exception. + if (y.bigintValue() === 0n) { + return surroundingAgent.Throw('RangeError', 'BigIntDivideByZero'); + } + // 2. Let quotient be the mathematical value of x divided by y. + const quotient = x.bigintValue() / y.bigintValue(); + // 3. Return the BigInt value that represents quotient rounded towards 0 to the next integral value. + return new Value(quotient); + } + + // #sec-numeric-types-bigint-remainder + static remainder(n, d) { + // 1. If d is 0n, throw a RangeError exception. + if (d.bigintValue() === 0n) { + return surroundingAgent.Throw('RangeError', 'BigIntDivideByZero'); + } + // 2. If n is 0n, return 0n. + if (n.bigintValue() === 0n) { + return new Value(0n); + } + // 3. Let r be the BigInt defined by the mathematical relation r = n - (d × q) + // where q is a BigInt that is negative only if n/d is negative and positive + // only if n/d is positive, and whose magnitude is as large as possible without + // exceeding the magnitude of the true mathematical quotient of n and d. + const r = new Value(n.bigintValue() % d.bigintValue()); + // 4. Return r. + return r; + } + + // #sec-numeric-types-bigint-add + static add(x, y) { + return new Value(x.bigintValue() + y.bigintValue()); + } + + // #sec-numeric-types-bigint-subtract + static subtract(x, y) { + return new Value(x.bigintValue() - y.bigintValue()); + } + + // #sec-numeric-types-bigint-leftShift + static leftShift(x, y) { + return new Value(x.bigintValue() << y.bigintValue()); // eslint-disable-line no-bitwise + } + + // #sec-numeric-types-bigint-signedRightShift + static signedRightShift(x, y) { + // 1. Return BigInt::leftShift(x, -y). + return BigIntValue.leftShift(x, new Value(-y.bigintValue())); + } + + // #sec-numeric-types-bigint-unsighedRightShift + static unsignedRightShift(_x, _y) { + return surroundingAgent.Throw('TypeError', 'BigIntUnsignedRightShift'); + } + + // #sec-numeric-types-bigint-lessThan + static lessThan(x, y) { + return x.bigintValue() < y.bigintValue() ? Value.true : Value.false; + } + + // #sec-numeric-types-bigint-equal + static equal(x, y) { + // Return true if x and y have the same mathematical integer value and false otherwise. + return x.bigintValue() === y.bigintValue() ? Value.true : Value.false; + } + + // #sec-numeric-types-bigint-sameValue + static sameValue(x, y) { + // 1. Return BigInt::equal(x, y). + return BigIntValue.equal(x, y); + } + + // #sec-numeric-types-bigint-sameValueZero + static sameValueZero(x, y) { + // 1. Return BigInt::equal(x, y). + return BigIntValue.equal(x, y); + } + + // #sec-numeric-types-bigint-bitwiseAND + static bitwiseAND(x, y) { + // 1. Return BigIntBitwiseOp("&", x, y). + return BigIntBitwiseOp('&', x.bigintValue(), y.bigintValue()); + } + + // #sec-numeric-types-bigint-bitwiseXOR + static bitwiseXOR(x, y) { + // 1. Return BigIntBitwiseOp("^", x, y). + return BigIntBitwiseOp('^', x.bigintValue(), y.bigintValue()); + } + + // #sec-numeric-types-bigint-bitwiseOR + static bitwiseOR(x, y) { + // 1. Return BigIntBitwiseOp("|", x, y); + return BigIntBitwiseOp('|', x.bigintValue(), y.bigintValue()); + } + + // #sec-numeric-types-bigint-tostring + static toString(x) { + // 1. If x is less than zero, return the string-concatenation of the String "-" and ! BigInt::toString(-x). + if (x.bigintValue() < 0n) { + const str = X(BigIntValue.toString(new Value(-x.bigintValue()))).stringValue(); + return new Value(`-${str}`); + } + // 2. Return the String value consisting of the code units of the digits of the decimal representation of x. + return new Value(`${x.bigintValue()}`); + } +} + +BigIntValue.unit = new BigIntValue(1n); + +/* +// #sec-binaryand +function BinaryAnd(x, y) { + // 1. Assert: x is 0 or 1. + Assert(x === 0n || x === 1n); + // 2. Assert: y is 0 or 1. + Assert(x === 0n || x === 1n); + // 3. If x is 1 and y is 1, return 1. + if (x === 1n && y === 1n) { + return 1n; + } else { + // 4. Else, return 0. + return 0n; + } +} + +// #sec-binaryor +function BinaryOr(x, y) { + // 1. Assert: x is 0 or 1. + Assert(x === 0n || x === 1n); + // 2. Assert: y is 0 or 1. + Assert(x === 0n || x === 1n); + // 3. If x is 1 or y is 1, return 1. + if (x === 1n || y === 1n) { + return 1n; + } else { + // 4. Else, return 0. + return 0n; + } +} + +// #sec-binaryxor +function BinaryXor(x, y) { + // 1. Assert: x is 0 or 1. + Assert(x === 0n || x === 1n); + // 2. Assert: y is 0 or 1. + Assert(x === 0n || x === 1n); + // 3. If x is 1 and y is 0, return 1. + if (x === 1n && y === 0n) { + return 1n; + } else if (x === 0n && y === 1n) { + // Else if x is 0 and y is 1, return 1. + return 1n; + } else { + // 4. Else, return 0. + return 0n; + } +} +*/ + +// #sec-bigintbitwiseop +function BigIntBitwiseOp(op, x, y) { + // TODO: figure out why this doesn't work, probably the modulo. + /* + // 1. Assert: op is "&", "|", or "^". + Assert(['&', '|', '^'].includes(op)); + // 2. Let result be 0n. + let result = 0n; + // 3. Let shift be 0. + let shift = 0n; + // 4. Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1), + while (!((x === 0n || x === -1n) && (y === 0n || y === -1n))) { + // a. Let xDigit be x modulo 2. + const xDigit = x % 2n; + // b. Let yDigit be y modulo 2. + const yDigit = y % 2n; + // c. If op is "&", set result to result + 2^shift × BinaryAnd(xDigit, yDigit). + if (op === '&') { + result += (2n ** shift) * BinaryAnd(xDigit, yDigit); + } else if (op === '|') { + // d. Else if op is "|", set result to result + 2shift × BinaryOr(xDigit, yDigit). + result += (2n ** shift) * BinaryXor(xDigit, yDigit); + } else { + // i. Assert: op is "^". + Assert(op === '^'); + // ii. Set result to result + 2^shift × BinaryXor(xDigit, yDigit). + result += (2n ** shift) * BinaryXor(xDigit, yDigit); + } + // f. Set shift to shift + 1. + shift += 1n; + // g. Set x to (x - xDigit) / 2. + x = (x - xDigit) / 2n; + // h. Set y to (y - yDigit) / 2. + y = (y - yDigit) / 2n; + } + let tmp; + // 5. If op is "&", let tmp be BinaryAnd(x modulo 2, y modulo 2). + if (op === '&') { + tmp = BinaryAnd(x % 2n, y % 2n); + } else if (op === '|') { + // 6. Else if op is "|", let tmp be BinaryOr(x modulo 2, y modulo 2). + tmp = BinaryOr(x % 2n, y % 2n); + } else { + // a. Assert: op is "^". + Assert(op === '^'); + // b. Let tmp be BinaryXor(x modulo 2, y modulo 2). + tmp = BinaryXor(x % 2n, y % 2n); + } + // 8. If tmp ≠ 0, then + if (tmp !== 0n) { + // a. Set result to result - 2^shift. NOTE: This extends the sign. + result -= 2n ** shift; + } + // 9. Return result. + return new Value(result); + */ + switch (op) { + case '&': + return new Value(x & y); // eslint-disable-line no-bitwise + case '|': + return new Value(x | y); // eslint-disable-line no-bitwise + case '^': + return new Value(x ^ y); // eslint-disable-line no-bitwise + default: + throw new OutOfRange('BigIntBitwiseOp', op); + } +} + +export class StringValue extends PrimitiveValue { + constructor(string) { + super(); + this.string = string; + } + + stringValue() { + return this.string; + } +} + +export class SymbolValue extends PrimitiveValue { + constructor(Description) { + super(); + this.Description = Description; + } +} + +export const wellKnownSymbols = Object.create(null); +for (const name of [ + 'asyncIterator', + 'hasInstance', + 'isConcatSpreadable', + 'iterator', + 'match', + 'matchAll', + 'replace', + 'search', + 'species', + 'split', + 'toPrimitive', + 'toStringTag', + 'unscopables', +]) { + const sym = new SymbolValue(new StringValue(`Symbol.${name}`)); + wellKnownSymbols[name] = sym; +} +Object.freeze(wellKnownSymbols); + +export class ObjectValue extends Value { + constructor(internalSlotsList) { + super(); + + this.properties = new ValueMap(); + this.internalSlotsList = internalSlotsList; + } + + GetPrototypeOf() { + return OrdinaryGetPrototypeOf(this); + } + + SetPrototypeOf(V) { + return OrdinarySetPrototypeOf(this, V); + } + + IsExtensible() { + return OrdinaryIsExtensible(this); + } + + PreventExtensions() { + return OrdinaryPreventExtensions(this); + } + + GetOwnProperty(P) { + return OrdinaryGetOwnProperty(this, P); + } + + DefineOwnProperty(P, Desc) { + return OrdinaryDefineOwnProperty(this, P, Desc); + } + + HasProperty(P) { + return OrdinaryHasProperty(this, P); + } + + Get(P, Receiver) { + return OrdinaryGet(this, P, Receiver); + } + + Set(P, V, Receiver) { + return OrdinarySet(this, P, V, Receiver); + } + + Delete(P) { + return OrdinaryDelete(this, P); + } + + OwnPropertyKeys() { + return OrdinaryOwnPropertyKeys(this); + } + + // NON-SPEC + mark(m) { + m(this.properties); + this.internalSlotsList.forEach((s) => { + m(this[s]); + }); + } +} + +export class Reference { + constructor({ BaseValue, ReferencedName, StrictReference }) { + this.BaseValue = BaseValue; + this.ReferencedName = ReferencedName; + Assert(Type(StrictReference) === 'Boolean'); + this.StrictReference = StrictReference; + } + + // NON-SPEC + mark(m) { + m(this.BaseValue); + m(this.ReferencedName); + } +} + +export class SuperReference extends Reference { + constructor({ + BaseValue, + ReferencedName, + thisValue, + StrictReference, + }) { + super({ BaseValue, ReferencedName, StrictReference }); + this.thisValue = thisValue; + } + + // NON-SPEC + mark(m) { + super.mark(m); + m(this.thisValue); + } +} + +export function Descriptor(O) { + if (new.target === Descriptor) { + this.Value = O.Value; + this.Get = O.Get; + this.Set = O.Set; + this.Writable = O.Writable; + this.Enumerable = O.Enumerable; + this.Configurable = O.Configurable; + } else { + return new Descriptor(O); + } +} + +Descriptor.prototype.everyFieldIsAbsent = function everyFieldIsAbsent() { + return this.Value === undefined + && this.Get === undefined + && this.Set === undefined + && this.Writable === undefined + && this.Enumerable === undefined + && this.Configurable === undefined; +}; + +// NON-SPEC +Descriptor.prototype.mark = function mark(m) { + m(this.Value); + m(this.Get); + m(this.Set); +}; + +export class DataBlock extends Uint8Array { + constructor(sizeOrBuffer, ...restArgs) { + if (sizeOrBuffer instanceof ArrayBuffer) { + // fine. + super(sizeOrBuffer, ...restArgs); + } else { + Assert(typeof sizeOrBuffer === 'number'); + super(sizeOrBuffer); + } + } +} + +export function Type(val) { + if (val instanceof UndefinedValue) { + return 'Undefined'; + } + + if (val instanceof NullValue) { + return 'Null'; + } + + if (val instanceof BooleanValue) { + return 'Boolean'; + } + + if (val instanceof StringValue) { + return 'String'; + } + + if (val instanceof NumberValue) { + return 'Number'; + } + + if (val instanceof BigIntValue) { + return 'BigInt'; + } + + if (val instanceof SymbolValue) { + return 'Symbol'; + } + + if (val instanceof ObjectValue) { + return 'Object'; + } + + if (val instanceof Reference) { + return 'Reference'; + } + + if (val instanceof Completion) { + return 'Completion'; + } + + if (val instanceof EnvironmentRecord) { + return 'EnvironmentRecord'; + } + + if (val instanceof LexicalEnvironment) { + return 'LexicalEnvironment'; + } + + if (val instanceof Descriptor) { + return 'Descriptor'; + } + + if (val instanceof DataBlock) { + return 'Data Block'; + } + + throw new OutOfRange('Type', val); +} + +// Used for Type(x)::y for numerics +export function TypeNumeric(val) { + if (val instanceof NumberValue) { + return NumberValue; + } + + if (val instanceof BigIntValue) { + return BigIntValue; + } + + throw new OutOfRange('TypeNumeric', val); +} diff --git a/src/engine262/test/base.js b/src/engine262/test/base.js new file mode 100644 index 0000000..b679a3b --- /dev/null +++ b/src/engine262/test/base.js @@ -0,0 +1,109 @@ +'use strict'; + +const readline = require('readline'); +const os = require('os'); + +process.on('unhandledRejection', (reason) => { + require('fs').writeSync(0, `\n${require('util').inspect(reason)}\n`); + process.exit(1); +}); + +const CI = !!process.env.CONTINUOUS_INTEGRATION; + +const ANSI = CI ? { + reset: '', + red: '', + green: '', + yellow: '', + blue: '', +} : { + reset: '\u001b[0m', + red: '\u001b[31m', + green: '\u001b[32m', + yellow: '\u001b[33m', + blue: '\u001b[34m', +}; + +const CPU_COUNT = os.cpus().length; + +let skipped = 0; +let passed = 0; +let failed = 0; +let total = 0; + +const start = Date.now(); + +const handledPerSecLast5 = []; +const pad = (n, l, c = '0') => n.toString().padStart(l, c); +const average = (array) => (array.reduce((a, b) => a + b, 0) / array.length) || 0; + +const printStatusLine = () => { + const elapsed = Math.floor((Date.now() - start) / 1000); + const min = Math.floor(elapsed / 60); + const sec = elapsed % 60; + + const time = `${pad(min, 2)}:${pad(sec, 2)}`; + const found = `${ANSI.blue}:${pad(total, 5, ' ')}${ANSI.reset}`; + const p = `${ANSI.green}+${pad(passed, 5, ' ')}${ANSI.reset}`; + const f = `${ANSI.red}-${pad(failed, 5, ' ')}${ANSI.reset}`; + const s = `${ANSI.yellow}»${pad(skipped, 5, ' ')}${ANSI.reset}`; + const testsPerSec = average(handledPerSecLast5); + + const line = `[${time}|${found}|${p}|${f}|${s}] (${testsPerSec.toFixed(2)}/s)`; + + if (!CI) { + readline.clearLine(process.stdout, 0); + readline.cursorTo(process.stdout, 0); + } + process.stdout.write(`${line}${CI ? '\n' : ''}`); +}; + +let handledPerSecCounter = 0; + +setInterval(() => { + handledPerSecLast5.unshift(handledPerSecCounter); + handledPerSecCounter = 0; + if (handledPerSecLast5.length > 5) { + handledPerSecLast5.length = 5; + } +}, 1000).unref(); + +module.exports = { + total() { + total += 1; + }, + pass() { + passed += 1; + handledPerSecCounter += 1; + }, + fail(name, error) { + failed += 1; + handledPerSecCounter += 1; + process.exitCode = 1; + process.stderr.write(`\nFAILURE! ${name}\n${error}\n`); + }, + skip() { + skipped += 1; + handledPerSecCounter += 1; + }, + CPU_COUNT, + CI, +}; + +process.stdout.write(` +####################### + engine262 Test Runner + Detected ${CPU_COUNT} CPUs + ${CI ? 'Running' : 'Not running'} on CI +####################### + +`); + +printStatusLine(); +setInterval(() => { + printStatusLine(); +}, CI ? 5000 : 500).unref(); + +process.on('exit', () => { + printStatusLine(); +}); diff --git a/src/engine262/test/coverage_root.sh b/src/engine262/test/coverage_root.sh new file mode 100644 index 0000000..af93e13 --- /dev/null +++ b/src/engine262/test/coverage_root.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +npm run test:test262 +npm run test:supplemental +npm run test:json diff --git a/src/engine262/test/eslint-plugin-engine262/index.js b/src/engine262/test/eslint-plugin-engine262/index.js new file mode 100644 index 0000000..fa16ea1 --- /dev/null +++ b/src/engine262/test/eslint-plugin-engine262/index.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + rules: { + 'no-use-in-def': require('./no-use-in-def'), + 'valid-throw': require('./valid-throw'), + }, +}; diff --git a/src/engine262/test/eslint-plugin-engine262/no-use-in-def.js b/src/engine262/test/eslint-plugin-engine262/no-use-in-def.js new file mode 100644 index 0000000..4233b32 --- /dev/null +++ b/src/engine262/test/eslint-plugin-engine262/no-use-in-def.js @@ -0,0 +1,68 @@ +'use strict'; + +// https://github.com/eslint/eslint/blob/master/lib/rules/no-use-before-define.js + +const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u; +const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u; + +function isInRange(node, location) { + return node && node.range[0] <= location && location <= node.range[1]; +} + +function isUsedInDef(reference) { + const variable = reference.resolved; + if (!variable || variable.scope !== reference.from) { + return false; + } + + let node = variable.identifiers[0].parent; + const location = reference.identifier.range[1]; + + while (node) { + if (node.type === 'VariableDeclarator') { + if (isInRange(node.init, location)) { + return true; + } + if (FOR_IN_OF_TYPE.test(node.parent.parent.type) && isInRange(node.parent.parent.right, location)) { + return true; + } + break; + } + + if (node.type === 'AssignmentPattern' && isInRange(node.right, location)) { + return true; + } + + if (SENTINEL_TYPE.test(node.type)) { + break; + } + + node = node.parent; + } + + return false; +} + +module.exports = { + create(context) { + function findVariablesInScope(scope) { + scope.references.forEach((reference) => { + if (isUsedInDef(reference)) { + context.report({ + node: reference.identifier, + message: '{{name}} was used in its own definition', + data: reference.identifier, + }); + } + }); + + scope.childScopes.forEach((s) => findVariablesInScope(s)); + } + + return { + Program() { + findVariablesInScope(context.getScope()); + }, + }; + }, +}; diff --git a/src/engine262/test/eslint-plugin-engine262/valid-throw.js b/src/engine262/test/eslint-plugin-engine262/valid-throw.js new file mode 100644 index 0000000..f2e7034 --- /dev/null +++ b/src/engine262/test/eslint-plugin-engine262/valid-throw.js @@ -0,0 +1,63 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const acorn = require('acorn'); + +function isThrowCall(node) { + return node.callee.type === 'MemberExpression' + && node.callee.computed === false + && node.callee.object.type === 'Identifier' + && node.callee.object.name === 'surroundingAgent' + && node.callee.property.type === 'Identifier' + && node.callee.property.name === 'Throw'; +} + +const templates = {}; + +{ + const source = fs.readFileSync(path.join(__dirname, '../../src/messages.mjs'), 'utf8'); + const ast = acorn.parse(source, { ecmaVersion: 2020, sourceType: 'module' }); + + ast.body.forEach((n) => { + if (n.type !== 'ExportNamedDeclaration') { + return; + } + const [v] = n.declaration.declarations; + const name = v.id.name; + const length = v.init.params.length; + templates[name] = length; + }); +} + +module.exports = { + create(context) { + return { + CallExpression(node) { + if (!isThrowCall(node)) { + return; + } + if (node.arguments.length === 1 && node.arguments[0].type !== 'Literal') { + return; + } + const [type, template, ...templateArgs] = node.arguments; + if (!type || type.type !== 'Literal') { + context.report(node, 'Throw must use a valid error constructor'); + return; + } + if (!template || template.type !== 'Literal') { + context.report(node, 'Throw must use a valid message template'); + return; + } + const tfn = templates[template.value]; + if (tfn === undefined) { + context.report(template, `'${template.value}' is not a valid message template`); + return; + } + if (tfn !== templateArgs.length) { + context.report(node, `Template expects ${tfn} args`); + } + }, + }; + }, +}; diff --git a/src/engine262/test/json/json.js b/src/engine262/test/json/json.js new file mode 100644 index 0000000..c9922e7 --- /dev/null +++ b/src/engine262/test/json/json.js @@ -0,0 +1,62 @@ +'use strict'; + +/* eslint-disable no-await-in-loop */ + +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const { + pass, fail, skip, total, +} = require('../base'); +const { + Agent, + Realm, + AbruptCompletion, + inspect, +} = require('../..'); + +const BASE_DIR = path.resolve(__dirname, 'JSONTestSuite'); + +const agent = new Agent(); +agent.enter(); + +function test(filename) { + const realm = new Realm(); + + const source = fs.readFileSync(filename, 'utf8'); + + let result; + try { + result = realm.evaluateScript(` + JSON.parse(${JSON.stringify(source)}); + `); + } catch { + // ... + } + + const testName = path.basename(filename); + + if (!result || result instanceof AbruptCompletion) { + if (testName.startsWith('n_')) { + pass(); + } else if (testName.startsWith('i_')) { + skip(); + } else { + fail(testName, inspect(realm, result)); + } + } else { + if (testName.startsWith('n_')) { + fail(testName, 'JSON parsed but should have failed!'); + } else { + pass(); + } + } +} + +const tests = glob.sync(`${path.resolve(BASE_DIR, 'test_parsing')}/**/*.json`) + .concat(glob.sync(`${path.resolve(BASE_DIR, 'test_transform')}/**/*.json`)); + +tests.forEach((t) => { + total(); + test(t); +}); diff --git a/src/engine262/test/stepped.js b/src/engine262/test/stepped.js new file mode 100644 index 0000000..d6c5fa1 --- /dev/null +++ b/src/engine262/test/stepped.js @@ -0,0 +1,73 @@ +'use strict'; + +require('@snek/source-map-support/register'); +const { + isMainThread, parentPort, workerData, Worker, +} = require('worker_threads'); +const fs = require('fs'); +// eslint-disable-next-line import/no-extraneous-dependencies +const { codeFrameColumns } = require('@babel/code-frame'); + +if (isMainThread) { + const shared = new SharedArrayBuffer(4); + const shared32 = new Int32Array(shared); + const source = fs.readFileSync(process.argv[2], 'utf8'); + const worker = new Worker(__filename, { + workerData: { shared, source }, + }); + process.stdin.on('data', () => { + const old = Atomics.compareExchange(shared32, 0, 0, 1); + if (old === 0) { + Atomics.notify(shared32, 0, 1); + } + }); + worker.on('message', (data) => { + const node = JSON.parse(data); + const frame = codeFrameColumns(source, { + start: { + line: node.loc.start.line, + column: node.loc.start.column + 1, + }, + end: { + line: node.loc.end.line, + column: node.loc.end.column + 1, + }, + }, { + highlightCode: true, + message: node.type, + }); + process.stdout.write(`${frame}\n\n\n`); + }); + worker.on('exit', () => { + process.exit(0); + }); +} else { + const { + Agent, + Realm, + AbruptCompletion, + inspect, + } = require('..'); + + const shared32 = new Int32Array(workerData.shared); + const agent = new Agent({ + onNodeEvaluation(node) { + if (node.type === 'ExpressionStatement') { + return; + } + parentPort.postMessage(JSON.stringify(node)); + Atomics.wait(shared32, 0, 0); + Atomics.store(shared32, 0, 0); + }, + }); + agent.enter(); + + const realm = new Realm(); + + const completion = realm.evaluateScript(workerData.source); + if (completion instanceof AbruptCompletion) { + process.stdout.write(`${inspect(completion, realm)}\n`); + } + + process.exit(0); +} diff --git a/src/engine262/test/supplemental.js b/src/engine262/test/supplemental.js new file mode 100644 index 0000000..6786601 --- /dev/null +++ b/src/engine262/test/supplemental.js @@ -0,0 +1,177 @@ +'use strict'; + +const assert = require('assert'); +const { + Abstract, + Agent, + Realm, + Value, +} = require('..'); +const { total, pass, fail } = require('./base'); + +// Features that cannot be tested by test262 should go here. + +[ + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript('debugger;'); + assert.strictEqual(result.Value, Value.undefined); + }, + () => { + const agent = new Agent({ + onDebugger() { + return new Value(realm, 42); + }, + }); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript('debugger;'); + assert.strictEqual(result.Value.numberValue(), 42); + }, + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + function x() { throw new Error('owo'); } + function y() { x(); } + try { + y(); + } catch (e) { + e.stack; + } + `); + assert.strictEqual(result.Value.stringValue(), ` +Error: owo + at x (:2:37) + at y (:3:21) + at :5:8`.trim()); + }, + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + async function x() { await 1; throw new Error('owo'); } + async function y() { await x(); } + y().catch((e) => e.stack); + `); + assert.strictEqual(result.Value.PromiseResult.stringValue(), ` +Error: owo + at async x (:2:52) + at async y (:3:33)`.trim()); + }, + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + function x() { Reflect.get(); } + try { + x(); + } catch (e) { + e.stack; + } + `); + assert.strictEqual(result.Value.stringValue(), ` +TypeError: undefined is not an object + at get (native) + at x (:2:21) + at :4:8`.trim()); + }, + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + function Y() { throw new Error('owo'); } + function x() { new Y(); } + try { + x(); + } catch (e) { + e.stack; + } + `); + assert.strictEqual(result.Value.stringValue(), ` +Error: owo + at new Y (:2:37) + at x (:3:25) + at :5:8`.trim()); + }, + () => { + const agent = new Agent(); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + let e; + new Promise(() => { + e = new Error('owo'); + }); + e.stack; + `); + assert.strictEqual(result.Value.stringValue(), ` +Error: owo + at (:4:22) + at new Promise (native) + at :3:18`.trim()); + }, + () => { + const agent = new Agent({ + features: ['WeakRefs'], + }); + agent.enter(); + const realm = new Realm(); + const result = realm.evaluateScript(` + const w = new WeakRef({}); + Promise.resolve() + .then(() => { + if (typeof w.deref() !== 'object') { + throw new Error(); + } + }) + .then(() => { + if (typeof w.deref() !== 'undefined') { + throw new Error(); + } + }) + .then(() => 'pass'); + `); + assert.strictEqual(result.Value.PromiseResult.stringValue(), 'pass'); + }, + () => { + const agent = new Agent({ + features: ['WeakRefs'], + }); + agent.enter(); + const realm = new Realm(); + const module = realm.createSourceTextModule('test.js', ` + const w = new WeakRef({}); + globalThis.result = Promise.resolve() + .then(() => { + if (typeof w.deref() !== 'object') { + throw new Error(); + } + }) + .then(() => { + if (typeof w.deref() !== 'undefined') { + throw new Error(); + } + }) + .then(() => 'pass'); + `); + module.Link(); + module.Evaluate(); + const result = Abstract.Get(realm.global, new Value(realm, 'result')); + assert.strictEqual(result.Value.PromiseResult.stringValue(), 'pass'); + }, +].forEach((test) => { + total(); + try { + test(); + pass(); + } catch (e) { + fail('', e.stack || e); + } +}); diff --git a/src/engine262/test/test262/features b/src/engine262/test/test262/features new file mode 100644 index 0000000..b814344 --- /dev/null +++ b/src/engine262/test/test262/features @@ -0,0 +1,25 @@ +// blacklist + +Atomics +Atomics.waitAsync + +caller + +SharedArrayBuffer + +tail-call-optimization + +u180e + +class-fields-public +class-fields-private +class-methods-private +class-static-fields-public +class-static-fields-private +class-static-methods-private + +export-star-as-namespace-from-module + +hashbang + +numeric-separator-literal diff --git a/src/engine262/test/test262/longlist b/src/engine262/test/test262/longlist new file mode 100644 index 0000000..85e4969 --- /dev/null +++ b/src/engine262/test/test262/longlist @@ -0,0 +1,13 @@ +# Tests that pass but take a really long time + +built-ins/parseFloat/S15.1.2.3_A6.js +built-ins/parseInt/S15.1.2.2_A8.js + +built-ins/RegExp/character-class-escape-non-whitespace.js +built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape* +built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape* +built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape* +built-ins/RegExp/property-escapes/generated/*.js + +language/comments/S7.4_A5.js +language/comments/S7.4_A6.js diff --git a/src/engine262/test/test262/skiplist b/src/engine262/test/test262/skiplist new file mode 100644 index 0000000..2742034 --- /dev/null +++ b/src/engine262/test/test262/skiplist @@ -0,0 +1,90 @@ +// random issues in test262 +built-ins/Proxy/get/trap-is-missing-target-is-proxy.js +built-ins/Proxy/set/trap-is-missing-target-is-proxy.js +built-ins/Proxy/has/trap-is-missing-target-is-proxy.js + +// https://github.com/tc39/ecma262/pull/1408 +language/statements/for-of/iterator-close-throw-get-method-abrupt.js +language/statements/for-of/iterator-close-throw-get-method-non-callable.js +language/statements/for-await-of/iterator-close-throw-get-method-abrupt.js +language/statements/for-await-of/iterator-close-throw-get-method-non-callable.js + +// https://github.com/tc39/ecma262/pull/1814 +built-ins/Proxy/create-target-is-revoked-proxy.js +built-ins/Proxy/create-handler-is-revoked-proxy.js +built-ins/Proxy/create-target-is-revoked-function-proxy.js + +// https://github.com/tc39/ecma262/pull/1776 +built-ins/AsyncFromSyncIteratorPrototype/return/absent-value-not-passed.js +built-ins/AsyncFromSyncIteratorPrototype/next/absent-value-not-passed.js +built-ins/AsyncFromSyncIteratorPrototype/throw/absent-value-not-passed.js + +// https://github.com/tc39/ecma262/issues/1426 +built-ins/String/prototype/replace/S15.5.4.11_A3_T*.js + +// https://github.com/acornjs/acorn/issues/934 +language/expressions/import.meta/syntax/escape-sequence-import.js +language/expressions/dynamic-import/escape-sequence-import.js + +// https://github.com/acornjs/acorn/issues/938 +language/identifiers/start-unicode-13.0.0.js +language/identifiers/part-unicode-13.0.0.js +language/identifiers/part-unicode-13.0.0-escaped.js +language/identifiers/start-unicode-13.0.0-escaped.js + +// TODO(7): Missing Date. +built-ins/Date/S15.9.3.1_A5_T*.js +built-ins/Date/parse/time-value-maximum-range.js +built-ins/Date/parse/without-utc-offset.js +built-ins/Date/prototype/*/this-value-invalid-date.js +built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js +built-ins/Date/prototype/toISOString/15.9.5.43-0-11.js +built-ins/Date/prototype/toISOString/15.9.5.43-0-12.js +built-ins/Date/prototype/toISOString/15.9.5.43-0-4.js +built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js +harness/assertRelativeDateMs.js + +// TODO: Missing {decode|encode}URI{Component} +built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-10.js +built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-11.js +built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-9.js +built-ins/Object/getOwnPropertyNames/15.2.3.4-4-1.js +built-ins/decodeURI/**/*.js +built-ins/decodeURIComponent/**/*.js +built-ins/encodeURI/**/*.js +built-ins/encodeURIComponent/**/*.js +built-ins/global/S10.2.3_A1.1_T2.js +built-ins/global/S10.2.3_A1.2_T2.js +built-ins/global/S10.2.3_A1.3_T2.js + +// TODO(46): Missing Number +built-ins/Number/prototype/*.js +built-ins/Number/prototype/toExponential/*.js +built-ins/Number/prototype/toFixed/*.js +built-ins/Number/prototype/toLocaleString/*.js +built-ins/Number/prototype/toPrecision/*.js +built-ins/Number/prototype/toString/*.js +built-ins/JSON/stringify/string-escape-ascii.js +built-ins/TypedArray/prototype/toLocaleString/get-length-uses-internal-arraylength.js +built-ins/TypedArray/prototype/toLocaleString/return-result.js +language/expressions/property-accessors/S11.2.1_A3_T2.js +language/literals/regexp/S7.8.5_A1.1_T2.js +language/literals/regexp/S7.8.5_A1.4_T2.js +language/literals/regexp/S7.8.5_A2.1_T2.js +language/literals/regexp/S7.8.5_A2.4_T2.js +language/statements/class/subclass/builtin-objects/Number/regular-subclassing.js + +// Failing tests in language +language/eval-code/direct/new.target-fn.js +language/eval-code/direct/super-prop-method.js +language/expressions/super/prop-dot-cls-val-from-eval.js +language/expressions/super/prop-dot-obj-val-from-eval.js +language/expressions/super/prop-expr-cls-val-from-eval.js +language/expressions/super/prop-expr-obj-val-from-eval.js +language/expressions/super/prop-expr-cls-val.js + +// https://github.com/tc39/test262/pull/2575 +built-ins/RegExp/lookBehind/sliced-strings.js + +built-ins/RegExp/named-groups/unicode-property-names-valid.js +built-ins/RegExp/named-groups/non-unicode-property-names-valid.js diff --git a/src/engine262/test/test262/test262.js b/src/engine262/test/test262/test262.js new file mode 100644 index 0000000..635c148 --- /dev/null +++ b/src/engine262/test/test262/test262.js @@ -0,0 +1,280 @@ +'use strict'; + +/* eslint-disable no-inner-declarations */ + +require('@snek/source-map-support/register'); +const path = require('path'); +const fs = require('fs'); + +if (!process.send) { + // supervisor + + const childProcess = require('child_process'); + const TestStream = require('test262-stream'); + const glob = require('glob'); + + const { + pass, + fail, + skip, + total, + CPU_COUNT, + } = require('../base'); + + const override = process.argv.find((e, i) => i > 1 && !e.startsWith('-')); + const NUM_WORKERS = process.env.NUM_WORKERS + ? Number.parseInt(process.env.NUM_WORKERS, 10) + : Math.round(CPU_COUNT * 0.75); + const RUN_LONG = process.argv.includes('--run-long'); + + const createWorker = () => { + const c = childProcess.fork(__filename); + c.on('message', ({ description, status, error }) => { + switch (status) { + case 'PASS': + pass(); + break; + case 'FAIL': + fail(description, error); + break; + case 'SKIP': + skip(); + break; + default: + break; + } + }); + c.on('exit', (code) => { + if (code !== 0) { + process.exit(1); + } + }); + return c; + }; + + const workers = Array.from({ length: NUM_WORKERS }, () => createWorker()); + let longRunningWorker; + if (RUN_LONG) { + longRunningWorker = createWorker(); + } + + const readList = (name) => { + const source = fs.readFileSync(path.resolve(__dirname, name), 'utf8'); + return source.split('\n').filter((l) => l && !l.startsWith('//')); + }; + const readListPaths = (name) => readList(name) + .flatMap((t) => glob.sync(path.resolve(__dirname, 'test262', 'test', t))) + .map((f) => path.relative(path.resolve(__dirname, 'test262'), f)); + + const features = readList('features'); + const longlist = readListPaths('longlist'); + const skiplist = readListPaths('skiplist'); + + const stream = new TestStream(path.resolve(__dirname, 'test262'), { + paths: [override || 'test'], + omitRuntime: true, + }); + + let workerIndex = 0; + stream.on('data', (test) => { + total(); + + if (/annexB|intl402/.test(test.file) + || (test.attrs.features && test.attrs.features.some((feature) => features.includes(feature))) + || test.attrs.includes.includes('nativeFunctionMatcher.js') + || skiplist.includes(test.file)) { + skip(); + return; + } + + if (longlist.includes(test.file)) { + if (RUN_LONG) { + longRunningWorker.send(test, () => 0); + } else { + skip(); + } + } else { + workers[workerIndex].send(test, () => 0); + workerIndex += 1; + if (workerIndex >= workers.length) { + workerIndex = 0; + } + } + }); + + stream.on('end', () => { + workers.forEach((w) => { + w.send('DONE'); + if (RUN_LONG) { + longRunningWorker.send('DONE'); + } + }); + }); +} else { + // worker + + const { + Agent, Value, + FEATURES, Abstract, + Throw, + AbruptCompletion, + inspect, + } = require('../..'); + const { createRealm } = require('../../bin/test262_realm'); + + const agent = new Agent({ + features: FEATURES.map((f) => f.name), + }); + agent.enter(); + + const isError = (realm, type, value) => { + if (Abstract.Type(value) !== 'Object') { + return false; + } + const proto = value.Prototype; + if (!proto || Abstract.Type(proto) !== 'Object') { + return false; + } + const ctorDesc = proto.properties.get(new Value(realm, 'constructor')); + if (!ctorDesc || !Abstract.IsDataDescriptor(ctorDesc)) { + return false; + } + const ctor = ctorDesc.Value; + if (Abstract.Type(ctor) !== 'Object' || Abstract.IsCallable(ctor) !== Value.true) { + return false; + } + const namePropDesc = ctor.properties.get(new Value(realm, 'name')); + if (!namePropDesc || !Abstract.IsDataDescriptor(namePropDesc)) { + return false; + } + const nameProp = namePropDesc.Value; + return Abstract.Type(nameProp) === 'String' && nameProp.stringValue() === type; + }; + + const includeCache = {}; + + const run = (test) => { + const { file, contents, attrs } = test; + const specifier = path.resolve(__dirname, 'test262', file); + const { + realm, trackedPromises, + resolverCache, setPrintHandle, + } = createRealm({ file }); + let asyncPromise; + let timeout; + if (attrs.flags.async) { + asyncPromise = new Promise((resolve) => { + timeout = setTimeout(() => { + const failure = [...trackedPromises][0]; + if (failure) { + resolve({ status: 'FAIL', error: inspect(failure.PromiseResult, realm) }); + } else { + resolve({ status: 'FAIL', error: 'test timed out' }); + } + }, 2500); + setPrintHandle((m) => { + if (m.stringValue && m.stringValue() === 'Test262:AsyncTestComplete') { + resolve({ status: 'PASS' }); + } else { + resolve({ status: 'FAIL', error: m.stringValue ? m.stringValue() : inspect(m, realm) }); + } + setPrintHandle(undefined); + }); + }); + } + + attrs.includes.unshift('assert.js', 'sta.js'); + if (attrs.flags.async) { + attrs.includes.unshift('doneprintHandle.js'); + } + attrs.includes.forEach((include) => { + if (includeCache[include] === undefined) { + const p = path.resolve(__dirname, `./test262/harness/${include}`); + includeCache[include] = { + source: fs.readFileSync(p, 'utf8'), + specifier: p, + }; + } + const entry = includeCache[include]; + realm.evaluateScript(entry.source, { specifier: entry.specifier }); + }); + + realm.evaluateScript(` +var Test262Error = class Test262Error extends Error {}; + +function $DONE(error) { + if (error) { + if (typeof error === 'object' && error !== null && 'stack' in error) { + __consolePrintHandle__('Test262:AsyncTestFailure:' + error.stack); + } else { + __consolePrintHandle__('Test262:AsyncTestFailure:Test262Error: ' + error); + } + } else { + __consolePrintHandle__('Test262:AsyncTestComplete'); + } +}`.trim()); + + let completion; + if (attrs.flags.module) { + completion = realm.createSourceTextModule(specifier, contents); + if (!(completion instanceof AbruptCompletion)) { + const module = completion; + resolverCache.set(specifier, module); + completion = module.Link(); + if (!(completion instanceof AbruptCompletion)) { + completion = module.Evaluate(); + if (!(completion instanceof AbruptCompletion)) { + if (completion.PromiseState === 'rejected') { + completion = Throw(realm, completion.PromiseResult); + } + } + } + } + } else { + completion = realm.evaluateScript(contents, { specifier }); + } + + if (completion instanceof AbruptCompletion) { + clearTimeout(timeout); + if (attrs.negative && isError(realm, attrs.negative.type, completion.Value)) { + return { status: 'PASS' }; + } else { + return { status: 'FAIL', error: inspect(completion, realm) }; + } + } + + if (asyncPromise !== undefined) { + return asyncPromise; + } + + clearTimeout(timeout); + + if (attrs.negative) { + return { status: 'FAIL', error: `Expected ${attrs.negative.type} during ${attrs.negative.phase}` }; + } else { + return { status: 'PASS' }; + } + }; + + let p = Promise.resolve(); + process.on('message', (test) => { + if (test === 'DONE') { + p = p.then(() => process.exit(0)); + } else { + const description = `${test.file}\n${test.attrs.description}`; + p = p + .then(() => run(test)) + .catch((e) => { + process.send({ description, status: 'FAIL', error: e.stack || e }); + process.exit(1); + }) + .then((r) => { + process.send({ description, ...r }, (e) => { + if (e) { + process.exit(1); + } + }); + }); + } + }); +} diff --git a/src/engine262/yarn.lock b/src/engine262/yarn.lock new file mode 100644 index 0000000..8cbb6e6 --- /dev/null +++ b/src/engine262/yarn.lock @@ -0,0 +1,2222 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.7.5", "@babel/core@^7.8.7": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.8.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.7.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5", "@babel/traverse@^7.7.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.7.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@snek/source-map-support@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@snek/source-map-support/-/source-map-support-1.0.4.tgz#50b979693d7b42ed4ccf82c0823e60292592e7e4" + integrity sha512-l9k8NRmgeqr5yQyCTgrgtR8Flv360gimepegout/MEb49nwT0d/4q4IROLG9Uqqx8Rgj/OiR/ISWOdNVWO1P5w== + dependencies: + source-map "^0.8.0-beta.0" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/estree@*": + version "0.0.45" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" + integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/node@*": + version "14.6.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" + integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== + +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + +acorn@^7.1.0, acorn@^7.1.1: + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.10.0, ajv@^6.10.2: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +builtin-modules@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" + integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== + +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^2.0.0, chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.19.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + dependencies: + strip-bom "^4.0.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-config-airbnb-base@^14.1.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4" + integrity sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q== + dependencies: + confusing-browser-globals "^1.0.9" + object.assign "^4.1.0" + object.entries "^1.1.2" + +eslint-import-resolver-node@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.20.2: + version "2.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" + integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== + dependencies: + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.3" + eslint-module-utils "^2.6.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.1" + read-pkg-up "^2.0.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + +eslint-scope@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" + integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +find-cache-dir@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +fromentries@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.2.1.tgz#64c31665630479bc993cd800d53387920dc61b4d" + integrity sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.9: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hasha@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.0.tgz#33094d1f69c40a4a6ac7be53d5fe3ff95a269e0c" + integrity sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-reference@^1.1.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1, js-yaml@^3.2.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +klaw@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.1.1.tgz#42b76894701169cc910fd0d19ce677b5fb378af1" + integrity sha1-QrdolHARacyRD9DRnOZ3tfs3ivE= + dependencies: + graceful-fs "^4.1.9" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash@^4.17.14, lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +magic-string@^0.25.2: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +moo@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" + integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nearley@2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7" + integrity sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg== + dependencies: + commander "^2.19.0" + moo "^0.4.3" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + semver "^5.4.1" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +nyc@^15.0.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.entries@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" + integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + has "^1.0.3" + +object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + dependencies: + es6-error "^4.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-babel@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb" + integrity sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + rollup-pluginutils "^2.8.1" + +rollup-plugin-commonjs@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb" + integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q== + dependencies: + estree-walker "^0.6.1" + is-reference "^1.1.2" + magic-string "^0.25.2" + resolve "^1.11.0" + rollup-pluginutils "^2.8.1" + +rollup-plugin-node-resolve@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" + integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== + dependencies: + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.11.1" + rollup-pluginutils "^2.8.1" + +rollup-pluginutils@^2.8.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^1.32.1: + version "1.32.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" + integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== + dependencies: + "@types/estree" "*" + "@types/node" "*" + acorn "^7.1.0" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-json-comments@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +test262-stream@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/test262-stream/-/test262-stream-1.3.0.tgz#98c4ab9f8e74c7f9478afcf52e071a89d319df61" + integrity sha512-TSv+Z4hftmRuUJVJk7kaguWLlVLRfwyWm1DOt4kvTXAanATxv6A1HhjRTrSBI00XXEWQ+cUdsDii8tn+fkjXyw== + dependencies: + js-yaml "^3.2.1" + klaw "^2.1.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.8.0, type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +unicode-13.0.0@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/unicode-13.0.0/-/unicode-13.0.0-0.8.0.tgz#4f30f98240e246d14e142c7a57febcdc197c3bca" + integrity sha512-Ekct2eo5hBIp/29ERCj1ABIBNnrFOAisNHFv8l2KksHJg4PurIN/nGPFItaIpBJHVzlBYuJaVx/bAvmJnFFL/w== + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^7.2.3: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2"