From a789519ca196601050c6a7e1ae75620e7ec0bbf2 Mon Sep 17 00:00:00 2001 From: ctw-ian Date: Wed, 28 Dec 2022 11:47:14 +0800 Subject: [PATCH] Implementation of TaskPool Issue:I67SPL Signed-off-by: ctw-ian Change-Id: I60a78633218759de6b4e0894166901e8f32e4fcf --- es2panda/BUILD.gn | 1 + es2panda/binder/binder.cpp | 3 + es2panda/binder/scope.cpp | 13 +- es2panda/binder/scope.h | 18 ++- es2panda/compiler/core/function.cpp | 5 +- es2panda/compiler/core/pandagen.cpp | 11 ++ es2panda/compiler/core/pandagen.h | 1 + es2panda/ir/astNode.h | 3 +- es2panda/ir/base/scriptFunction.h | 10 ++ ...lid-concurrent-arrow-function-expected.txt | 2 + .../invalid-concurrent-arrow-function.js | 20 +++ ...ncurrent-async-arrow-function-expected.txt | 2 + ...invalid-concurrent-async-arrow-function.js | 20 +++ ...lid-concurrent-async-function-expected.txt | 2 + .../invalid-concurrent-async-function.js | 20 +++ ...rent-async-generator-function-expected.txt | 2 + ...lid-concurrent-async-generator-function.js | 20 +++ ...concurrent-generator-function-expected.txt | 2 + .../invalid-concurrent-generator-function.js | 20 +++ ...oncurrent-only-in-top-scope-1-expected.txt | 2 + .../use-concurrent-only-in-top-scope-1.js | 21 +++ ...oncurrent-only-in-top-scope-2-expected.txt | 2 + .../use-concurrent-only-in-top-scope-2.js | 22 +++ ...ng-mutable-lexical-variable-1-expected.txt | 2 + .../using-mutable-lexical-variable-1.js | 22 +++ ...ng-mutable-lexical-variable-2-expected.txt | 2 + .../using-mutable-lexical-variable-2.js | 22 +++ ...ng-mutable-lexical-variable-3-expected.txt | 2 + .../using-mutable-lexical-variable-3.js | 22 +++ es2panda/test/runner.py | 1 + es2panda/util/concurrent.cpp | 142 ++++++++++++++++++ es2panda/util/concurrent.h | 63 ++++++++ 32 files changed, 492 insertions(+), 8 deletions(-) create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-arrow-function-expected.txt create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-arrow-function.js create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function-expected.txt create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function.js create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-function-expected.txt create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-function.js create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function-expected.txt create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function.js create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-generator-function-expected.txt create mode 100644 es2panda/test/parser/concurrent/invalid-concurrent-generator-function.js create mode 100644 es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1-expected.txt create mode 100644 es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1.js create mode 100644 es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2-expected.txt create mode 100644 es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2.js create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-1-expected.txt create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-1.js create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-2-expected.txt create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-2.js create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-3-expected.txt create mode 100644 es2panda/test/parser/concurrent/using-mutable-lexical-variable-3.js create mode 100644 es2panda/util/concurrent.cpp create mode 100644 es2panda/util/concurrent.h diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index 346e0446b80..2fcb985b6f7 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -247,6 +247,7 @@ es2panda_src = [ "typescript/types/voidType.cpp", "util/base64.cpp", "util/bitset.cpp", + "util/concurrent.cpp", "util/dumper.cpp", "util/helpers.cpp", "util/hotfix.cpp", diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp index 99155a70775..06e2f1ff8c9 100644 --- a/es2panda/binder/binder.cpp +++ b/es2panda/binder/binder.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include namespace panda::es2panda::binder { @@ -216,6 +217,7 @@ void Binder::LookupIdentReference(ir::Identifier *ident) if (res.level != 0) { ASSERT(res.variable); + util::Concurrent::CheckUsingMutableLexicalVar(Program()->GetLineIndex(), ident, res); res.variable->SetLexical(res.scope, program_->HotfixHelper()); } @@ -495,6 +497,7 @@ void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) } case ir::AstNodeType::SCRIPT_FUNCTION: { auto *scriptFunc = childNode->AsScriptFunction(); + util::Concurrent::SetConcurrent(const_cast(scriptFunc), Program()->GetLineIndex()); auto *funcScope = scriptFunc->Scope(); auto *outerScope = scope_; diff --git a/es2panda/binder/scope.cpp b/es2panda/binder/scope.cpp index 63e1295967f..b6a4162fdd7 100644 --- a/es2panda/binder/scope.cpp +++ b/es2panda/binder/scope.cpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #include @@ -91,12 +93,13 @@ ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions uint32_t level = 0; uint32_t lexLevel = 0; const auto *iter = this; + bool crossConcurrent = false; if (iter->IsFunctionParamScope()) { Variable *v = iter->FindLocal(name, options); if (v != nullptr) { - return {name, const_cast(iter), level, lexLevel, v}; + return {name, const_cast(iter), level, lexLevel, v, crossConcurrent}; } level++; @@ -114,12 +117,16 @@ ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions Variable *v = iter->FindLocal(name, options); if (v != nullptr) { - return {name, const_cast(iter), level, lexLevel, v}; + return {name, const_cast(iter), level, lexLevel, v, crossConcurrent}; } if (iter->IsVariableScope()) { level++; + if (iter->IsFunctionScope()) { + crossConcurrent = iter->Node()->AsScriptFunction()->IsConcurrent() ? true : false; + } + if (iter->AsVariableScope()->NeedLexEnv()) { lexLevel++; } @@ -128,7 +135,7 @@ ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions iter = iter->Parent(); } - return {name, nullptr, 0, 0, nullptr}; + return {name, nullptr, 0, 0, nullptr, crossConcurrent}; } Decl *Scope::FindDecl(const util::StringView &name) const diff --git a/es2panda/binder/scope.h b/es2panda/binder/scope.h index 29c03a3297c..19e31ec9a41 100644 --- a/es2panda/binder/scope.h +++ b/es2panda/binder/scope.h @@ -140,10 +140,10 @@ private: class ScopeFindResult { public: ScopeFindResult() = default; - ScopeFindResult(util::StringView n, Scope *s, uint32_t l, Variable *v) : ScopeFindResult(n, s, l, l, v) {} + ScopeFindResult(util::StringView n, Scope *s, uint32_t l, Variable *v) : ScopeFindResult(n, s, l, l, v, false) {} ScopeFindResult(Scope *s, uint32_t l, uint32_t ll, Variable *v) : scope(s), level(l), lexLevel(ll), variable(v) {} - ScopeFindResult(util::StringView n, Scope *s, uint32_t l, uint32_t ll, Variable *v) - : name(n), scope(s), level(l), lexLevel(ll), variable(v) + ScopeFindResult(util::StringView n, Scope *s, uint32_t l, uint32_t ll, Variable *v, bool c) + : name(n), scope(s), level(l), lexLevel(ll), variable(v), crossConcurrent(c) { } @@ -152,6 +152,7 @@ public: uint32_t level {}; uint32_t lexLevel {}; Variable *variable {}; + bool crossConcurrent {false}; }; class Scope { @@ -591,12 +592,23 @@ public: return internalName_; } + void UseConcurrent() + { + hasConcurrent_ = true; + } + + bool HasConcurrent() const + { + return hasConcurrent_; + } + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) override; private: util::StringView name_ {}; util::StringView internalName_ {}; + bool hasConcurrent_ {false}; }; class LocalScope : public Scope { diff --git a/es2panda/compiler/core/function.cpp b/es2panda/compiler/core/function.cpp index b7b8e9c3931..44274f48bf1 100644 --- a/es2panda/compiler/core/function.cpp +++ b/es2panda/compiler/core/function.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace panda::es2panda::compiler { @@ -171,8 +172,8 @@ static void CompileFunction(PandaGen *pg) pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION); pg->FunctionEnter(); pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION); - CompileFunctionParameterDeclaration(pg, decl); const ir::AstNode *body = decl->Body(); + CompileFunctionParameterDeclaration(pg, decl); if (body->IsExpression()) { body->Compile(pg); @@ -186,6 +187,8 @@ static void CompileFunction(PandaGen *pg) static void CompileFunctionOrProgram(PandaGen *pg) { + util::Concurrent::StoreEnvForConcurrent(pg, pg->RootNode()); + FunctionRegScope lrs(pg); const auto *topScope = pg->TopScope(); diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp index 4069401e8f7..9ab19aecbf3 100644 --- a/es2panda/compiler/core/pandagen.cpp +++ b/es2panda/compiler/core/pandagen.cpp @@ -16,6 +16,7 @@ #include "pandagen.h" #include +#include #include #include #include @@ -50,6 +51,11 @@ namespace panda::es2panda::compiler { void PandaGen::SetFunctionKind() { + // make sure concurrent info will not be overwritten + if (funcKind_ == panda::panda_file::FunctionKind::CONCURRENT_FUNCTION) { + return; + } + if (rootNode_->IsProgram()) { funcKind_ = panda::panda_file::FunctionKind::FUNCTION; return; @@ -1806,6 +1812,11 @@ void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t StoreLexicalVar(node, level, slot, value); } +void PandaGen::StoreLexicalEnv(const ir::AstNode *node) +{ + ra_.Emit(node); // modify later +} + void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num) { ra_.Emit(node, num); diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 4b17ee205ca..26b53b69f10 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -419,6 +419,7 @@ public: void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot); void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name); void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, VReg value); + void StoreLexicalEnv(const ir::AstNode *node); void ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num); void ThrowUndefinedIfHole(const ir::AstNode *node, const util::StringView &name); diff --git a/es2panda/ir/astNode.h b/es2panda/ir/astNode.h index 19fd7d0a72a..c45530efbe3 100644 --- a/es2panda/ir/astNode.h +++ b/es2panda/ir/astNode.h @@ -87,7 +87,8 @@ enum class ScriptFunctionFlags { EXPRESSION = 1 << 3, OVERLOAD = 1 << 4, CONSTRUCTOR = 1 << 5, - METHOD = 1 << 6 + METHOD = 1 << 6, + CONCURRENT = 1 << 7 }; DEFINE_BITOPS(ScriptFunctionFlags) diff --git a/es2panda/ir/base/scriptFunction.h b/es2panda/ir/base/scriptFunction.h index 9d73b8121ce..0096f653fee 100644 --- a/es2panda/ir/base/scriptFunction.h +++ b/es2panda/ir/base/scriptFunction.h @@ -172,6 +172,16 @@ public: return scope_; } + bool IsConcurrent() const + { + return (flags_ & ir::ScriptFunctionFlags::CONCURRENT) != 0; + } + + bool CanBeConcurrent() const + { + return !(IsGenerator() || IsAsync() || IsArrow() || IsConstructor() || IsMethod()); + } + void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function-expected.txt b/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function-expected.txt new file mode 100644 index 00000000000..cda2d57567f --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be function declaration [invalid-concurrent-arrow-function.js:18:4] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function.js b/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function.js new file mode 100644 index 00000000000..805b407964f --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-arrow-function.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +() => { + "use concurrent"; +} diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function-expected.txt b/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function-expected.txt new file mode 100644 index 00000000000..c38dc29bc62 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be function declaration [invalid-concurrent-async-arrow-function.js:18:4] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function.js b/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function.js new file mode 100644 index 00000000000..95a1914f2a5 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-arrow-function.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +async () => { + "use concurrent"; +} \ No newline at end of file diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-function-expected.txt b/es2panda/test/parser/concurrent/invalid-concurrent-async-function-expected.txt new file mode 100644 index 00000000000..a53c7f64929 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-function-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be function declaration [invalid-concurrent-async-function.js:18:4] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-function.js b/es2panda/test/parser/concurrent/invalid-concurrent-async-function.js new file mode 100644 index 00000000000..45d163165b2 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-function.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +async function a() { + "use concurrent"; +} \ No newline at end of file diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function-expected.txt b/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function-expected.txt new file mode 100644 index 00000000000..0f0859a4aa2 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be function declaration [invalid-concurrent-async-generator-function.js:18:4] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function.js b/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function.js new file mode 100644 index 00000000000..d440cac6935 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-async-generator-function.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +async function *a() { + "use concurrent"; +} diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-generator-function-expected.txt b/es2panda/test/parser/concurrent/invalid-concurrent-generator-function-expected.txt new file mode 100644 index 00000000000..453f7a994c4 --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-generator-function-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be function declaration [invalid-concurrent-generator-function.js:18:4] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/invalid-concurrent-generator-function.js b/es2panda/test/parser/concurrent/invalid-concurrent-generator-function.js new file mode 100644 index 00000000000..ebaa563fdbe --- /dev/null +++ b/es2panda/test/parser/concurrent/invalid-concurrent-generator-function.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function *a() { + "use concurrent"; +} \ No newline at end of file diff --git a/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1-expected.txt b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1-expected.txt new file mode 100644 index 00000000000..1add0331da1 --- /dev/null +++ b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be defined in top-level scope [use-concurrent-only-in-top-scope-1.js:18:8] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1.js b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1.js new file mode 100644 index 00000000000..793cb030eb0 --- /dev/null +++ b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-1.js @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; +{ + function a() { + "use concurrent"; + } +} diff --git a/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2-expected.txt b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2-expected.txt new file mode 100644 index 00000000000..3114f7b8cf3 --- /dev/null +++ b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only be defined in top-level scope [use-concurrent-only-in-top-scope-2.js:19:8] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2.js b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2.js new file mode 100644 index 00000000000..1543a1e2cc5 --- /dev/null +++ b/es2panda/test/parser/concurrent/use-concurrent-only-in-top-scope-2.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function a() { + function b() { + "use concurrent"; + } +} \ No newline at end of file diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1-expected.txt b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1-expected.txt new file mode 100644 index 00000000000..9f8e5184316 --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only use const lexical variable [using-mutable-lexical-variable-1.js:20:11] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1.js b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1.js new file mode 100644 index 00000000000..3714b5ddbf9 --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-1.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; +let a = 1; + +function b() { + "use concurrent"; + return a; +} diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2-expected.txt b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2-expected.txt new file mode 100644 index 00000000000..3b3081439e0 --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only use const lexical variable [using-mutable-lexical-variable-2.js:20:11] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2.js b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2.js new file mode 100644 index 00000000000..a61abb4c64c --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-2.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; +var a = 1; + +function b() { + "use concurrent"; + return a; +} diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3-expected.txt b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3-expected.txt new file mode 100644 index 00000000000..f3b03414065 --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3-expected.txt @@ -0,0 +1,2 @@ +Error: Concurrent function should only use const lexical variable [using-mutable-lexical-variable-3.js:20:11] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3.js b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3.js new file mode 100644 index 00000000000..637c27662b2 --- /dev/null +++ b/es2panda/test/parser/concurrent/using-mutable-lexical-variable-3.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; +class a {} + +function b() { + "use concurrent"; + return a; +} diff --git a/es2panda/test/runner.py b/es2panda/test/runner.py index 8515d2e4e4a..55406d46106 100755 --- a/es2panda/test/runner.py +++ b/es2panda/test/runner.py @@ -852,6 +852,7 @@ def main(): if args.regression: runner = RegressionRunner(args) + runner.add_directory("parser/concurrent", "js", ["--module"]) runner.add_directory("parser/js", "js", ["--parse-only"]) runner.add_directory("parser/ts", "ts", ["--parse-only", "--module", "--extension=ts"]) diff --git a/es2panda/util/concurrent.cpp b/es2panda/util/concurrent.cpp new file mode 100644 index 00000000000..34e9d2d19fe --- /dev/null +++ b/es2panda/util/concurrent.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "concurrent.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::util { + +void Concurrent::SetConcurrent(ir::ScriptFunction *func, const lexer::LineIndex &lineIndex) +{ + auto *body = func->Body(); + if (!body) { + return; + } + + if (body->IsExpression()) { + return; + } + + auto &statements = body->AsBlockStatement()->Statements(); + if (statements.empty()) { + return; + } + + /** + * verify the firstStmt + * if IsExpressionStatement(firstStmt) == false + * return; + * else + * if (firstStmt->IsStringLiteral() && strVal == "use concurrent") + * [fall through] + * else + * return + */ + const auto *stmt = statements.front(); + if (!stmt->IsExpressionStatement()) { + return; + } + + auto *expr = stmt->AsExpressionStatement()->GetExpression(); + if (!expr->IsStringLiteral()) { + return; + } + + if (!expr->AsStringLiteral()->Str().Is(USE_CONCURRENT)) { + return; + } + + // concurrent function should only be function declaration + if (!func->CanBeConcurrent()) { + ThrowIncorrectUsing(lineIndex, stmt, ConcurrentInvalidFlag::NOT_ORDINARY_FUNCTION); + } + + // concurrent function should be defined in top-level scope + if (!func->Parent()->Parent()->IsProgram()) { + ThrowIncorrectUsing(lineIndex, stmt, ConcurrentInvalidFlag::NOT_TOP_LEVEL); + } + + func->AddFlag(ir::ScriptFunctionFlags::CONCURRENT); + auto *funcScope = func->Scope()->Parent(); + while (funcScope) { + if (funcScope->IsGlobalScope() || funcScope->IsModuleScope()) { + (static_cast(funcScope))->UseConcurrent(); + break; + } + + funcScope = funcScope->Parent(); + } +} + +void Concurrent::ThrowIncorrectUsing(const lexer::LineIndex &lineIndex, const ir::AstNode *expr, + ConcurrentInvalidFlag errFlag) +{ + auto line = expr->Range().start.line; + auto column = (const_cast(lineIndex)).GetLocation(expr->Range().start).col - 1; + switch (errFlag) { + case ConcurrentInvalidFlag::NOT_TOP_LEVEL: { + throw Error {ErrorType::GENERIC, "Concurrent function should only be defined in top-level scope", line, + column}; + break; + } + case ConcurrentInvalidFlag::NOT_ORDINARY_FUNCTION: { + throw Error {ErrorType::GENERIC, "Concurrent function should only be function declaration", line, + column}; + break; + } + case ConcurrentInvalidFlag::USING_MUTABLE_VARIABLE: { + throw Error {ErrorType::GENERIC, "Concurrent function should only use const lexical variable", line, + column}; + break; + } + default: + break; + } +} + +void Concurrent::StoreEnvForConcurrent(compiler::PandaGen *pg, const ir::AstNode *node) +{ + // Store env for concurrent should only be implemented in Top-Level scope + if (!pg->TopScope()->IsGlobalScope() && !pg->TopScope()->IsModuleScope()) { + return; + } + + if (pg->TopScope()->HasConcurrent()) { + pg->StoreLexicalEnv(node); + } +} + +void Concurrent::CheckUsingMutableLexicalVar(const lexer::LineIndex &lineIndex, const ir::AstNode *node, + const binder::ScopeFindResult &result) +{ + if (!result.crossConcurrent) { + return; + } + + if (!result.variable->Declaration()->IsConstDecl()) { + ThrowIncorrectUsing(lineIndex, node, ConcurrentInvalidFlag::USING_MUTABLE_VARIABLE); + } +} + +} // namespace panda::es2panda::util \ No newline at end of file diff --git a/es2panda/util/concurrent.h b/es2panda/util/concurrent.h new file mode 100644 index 00000000000..18c84e219f1 --- /dev/null +++ b/es2panda/util/concurrent.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_UTIL_CONCURRENT_H +#define ES2PANDA_UTIL_CONCURRENT_H + +#include + +namespace panda::es2panda::compiler { +class PandaGen; +} // namespace panda::es2panda::compiler + +namespace panda::es2panda::ir { +class AstNode; +class BlockStatement; +class ScriptFunction; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { +class ScopeFindResult; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::lexer { +class LineIndex; +} + +namespace panda::es2panda::util { + +enum class ConcurrentInvalidFlag { + NOT_TOP_LEVEL = 1, + NOT_ORDINARY_FUNCTION = 2, + USING_MUTABLE_VARIABLE = 3 +}; + +class Concurrent { +public: + Concurrent() = delete; + + static void SetConcurrent(ir::ScriptFunction *func, const lexer::LineIndex &lineIndex); + static void ThrowIncorrectUsing(const lexer::LineIndex &lineIndex, const ir::AstNode *expr, + ConcurrentInvalidFlag errFlag); + static void StoreEnvForConcurrent(compiler::PandaGen *pg, const ir::AstNode *node); + static void CheckUsingMutableLexicalVar(const lexer::LineIndex &lineIndex, const ir::AstNode *node, + const binder::ScopeFindResult &result); + + static constexpr std::string_view USE_CONCURRENT = "use concurrent"; +}; + +} // namespace panda::es2panda::util + +#endif \ No newline at end of file -- Gitee