From 6aa825e480d48127b480b08d13adf70033237097 Mon Sep 17 00:00:00 2001 From: Facebook Community Bot Date: Tue, 27 Sep 2022 11:14:30 -0400 Subject: [PATCH] Re-sync with internal repository (#822) Co-authored-by: Facebook Community Bot <6422482+facebook-github-bot@users.noreply.github.com> --- .../BCGen/HBC/BytecodeInstructionGenerator.h | 21 +++++++---- .../hermes/CompilerDriver/CompilerDriver.h | 2 ++ include/hermes/VM/CodeBlock.h | 11 +++--- lib/BCGen/HBC/BytecodeGenerator.cpp | 2 ++ lib/BCGen/HBC/BytecodeProviderFromSrc.cpp | 7 ++-- lib/BCGen/HBC/HBC.cpp | 9 +++++ lib/BCGen/HBC/ISel.cpp | 4 +++ lib/CompilerDriver/CompilerDriver.cpp | 15 ++++++-- lib/VM/Callable.cpp | 6 +++- lib/VM/CodeBlock.cpp | 15 +++++++- lib/VM/Debugger/Debugger.cpp | 7 +++- lib/VM/Interpreter.cpp | 15 ++++++-- lib/VM/Operations.cpp | 4 ++- test/hermes/far-environment-access-eval.js | 33 +++++++++++++++++ test/hermes/far-environment-access.js | 36 +++++++++++++++++++ 15 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 test/hermes/far-environment-access-eval.js create mode 100644 test/hermes/far-environment-access.js diff --git a/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h b/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h index dd838aaa36c..a7c7d60e6fc 100644 --- a/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h +++ b/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h @@ -45,7 +45,16 @@ class BytecodeInstructionGenerator { /// A list of opcodes. std::vector opcodes_{}; + /// A flag indicating that an encoding error happened, in which case the + /// bytecode is wrong and shouldn't be executed. + bool encodingError_{}; + public: + /// Returns whether an encoding error happened during bytecode emission. + bool hasEncodingError() const { + return encodingError_; + } + /// Returns the current location of the bytecode stream. offset_t getCurrentLocation() { return opcodes_.size(); @@ -76,13 +85,11 @@ class BytecodeInstructionGenerator { // We also assert that the value can fit into ctype. // For integer values, ((param_t)(ctype)value) == value will do the job; // We also want doubles to pass the check unconditionally. -#define DEFINE_OPERAND_TYPE(name, ctype) \ - void emit##name(param_t value) { \ - assert( \ - (((param_t)(ctype)value) == value || \ - std::is_floating_point::value) && \ - "Value does not fit in " #ctype); \ - emitOperand(value, sizeof(ctype)); \ +#define DEFINE_OPERAND_TYPE(name, ctype) \ + void emit##name(param_t value) { \ + encodingError_ |= ((param_t)(ctype)value) != value && \ + !std::is_floating_point::value; \ + emitOperand(value, sizeof(ctype)); \ } #include "hermes/BCGen/HBC/BytecodeList.def" diff --git a/include/hermes/CompilerDriver/CompilerDriver.h b/include/hermes/CompilerDriver/CompilerDriver.h index 30d6c40cbf6..0e3719cc894 100644 --- a/include/hermes/CompilerDriver/CompilerDriver.h +++ b/include/hermes/CompilerDriver/CompilerDriver.h @@ -32,6 +32,8 @@ enum CompileStatus { OutputFileError, /// An error occured during optimization. OptimizationFailed, + /// An error occured in the backend during/after IR lowering. + BackendError, }; /// Information about a bytecode file that is loaded into a buffer. diff --git a/include/hermes/VM/CodeBlock.h b/include/hermes/VM/CodeBlock.h index a8d47903df4..01d4893835d 100644 --- a/include/hermes/VM/CodeBlock.h +++ b/include/hermes/VM/CodeBlock.h @@ -63,7 +63,7 @@ class CodeBlock final #ifndef HERMESVM_LEAN /// Compiles a lazy CodeBlock. Intended to be called from lazyCompile. - void lazyCompileImpl(Runtime &runtime); + ExecutionStatus lazyCompileImpl(Runtime &runtime); #endif /// Helper function for getting start and end locations. @@ -237,17 +237,20 @@ class CodeBlock final } /// Compiles this CodeBlock, if it's lazy and not already compiled. - void lazyCompile(Runtime &runtime) { + ExecutionStatus lazyCompile(Runtime &runtime) { if (LLVM_UNLIKELY(isLazy())) { - lazyCompileImpl(runtime); + return lazyCompileImpl(runtime); } + return ExecutionStatus::RETURNED; } #else /// Checks whether this function is lazily compiled. bool isLazy() const { return false; } - void lazyCompile(Runtime &) {} + ExecutionStatus lazyCompile(Runtime &) { + return ExecutionStatus::RETURNED; + } #endif /// Get the start location of this function, if it's lazy. diff --git a/lib/BCGen/HBC/BytecodeGenerator.cpp b/lib/BCGen/HBC/BytecodeGenerator.cpp index 58124b9052d..4d30144f809 100644 --- a/lib/BCGen/HBC/BytecodeGenerator.cpp +++ b/lib/BCGen/HBC/BytecodeGenerator.cpp @@ -217,6 +217,8 @@ void BytecodeModuleGenerator::setFunctionGenerator( assert( functionGenerators_.find(F) == functionGenerators_.end() && "Adding same function twice."); + assert( + !BFG->hasEncodingError() && "Error should have been reported already."); functionGenerators_[F] = std::move(BFG); } diff --git a/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp b/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp index 1262630baa6..8553eaad757 100644 --- a/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp +++ b/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp @@ -236,8 +236,11 @@ BCProviderFromSrc::createBCProviderFromSrcImpl( opts.staticBuiltinsEnabled = context->getOptimizationSettings().staticBuiltins; opts.verifyIR = compileFlags.verifyIR; - auto bytecode = createBCProviderFromSrc( - hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts)); + auto BM = hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts); + if (context->getSourceErrorManager().getErrorCount() > 0) { + return {nullptr, getErrorString()}; + } + auto bytecode = createBCProviderFromSrc(std::move(BM)); bytecode->singleFunction_ = isSingleFunctionExpression(parsed.getValue()); return {std::move(bytecode), std::string{}}; } diff --git a/lib/BCGen/HBC/HBC.cpp b/lib/BCGen/HBC/HBC.cpp index c8dc018a174..0c75f8c26d3 100644 --- a/lib/BCGen/HBC/HBC.cpp +++ b/lib/BCGen/HBC/HBC.cpp @@ -313,6 +313,11 @@ std::unique_ptr hbc::generateBytecodeModule( debugCache = hbciSel.getDebugCache(); } + if (funcGen->hasEncodingError()) { + M->getContext().getSourceErrorManager().error( + F.getSourceRange().Start, "Error encoding bytecode"); + return nullptr; + } BMGen.setFunctionGenerator(&F, std::move(funcGen)); } @@ -335,6 +340,10 @@ std::unique_ptr hbc::generateBytecode( sourceMapGen, std::move(baseBCProvider)); + if (!BM) { + return {}; + } + if (options.format == OutputFormatKind::EmitBundle) { assert(BM != nullptr); BytecodeSerializer BS{OS, options}; diff --git a/lib/BCGen/HBC/ISel.cpp b/lib/BCGen/HBC/ISel.cpp index 03968879cf9..e1343ad7ef2 100644 --- a/lib/BCGen/HBC/ISel.cpp +++ b/lib/BCGen/HBC/ISel.cpp @@ -1337,6 +1337,10 @@ void HBCISel::generateHBCResolveEnvironment( "Cannot access variables in inner scopes"); int32_t delta = curScopeDepth.getValue() - instScopeDepth.getValue(); assert(delta > 0 && "HBCResolveEnvironment for current scope"); + if (std::numeric_limits::max() < delta) { + F_->getContext().getSourceErrorManager().error( + Inst->getLocation(), "Variable environment is out-of-reach"); + } BCFGen_->emitGetEnvironment(encodeValue(Inst), delta - 1); } void HBCISel::generateHBCStoreToEnvironmentInst( diff --git a/lib/CompilerDriver/CompilerDriver.cpp b/lib/CompilerDriver/CompilerDriver.cpp index 5388be7b270..9cf24d6ff2d 100644 --- a/lib/CompilerDriver/CompilerDriver.cpp +++ b/lib/CompilerDriver/CompilerDriver.cpp @@ -1694,9 +1694,15 @@ CompileResult generateBytecodeForExecution( std::shared_ptr context = M.shareContext(); CompileResult result{Success}; if (cl::BytecodeFormat == cl::BytecodeFormatKind::HBC) { - result.bytecodeProvider = hbc::BCProviderFromSrc::createBCProviderFromSrc( - hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), genOptions)); + auto BM = + hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), genOptions); + if (auto N = context->getSourceErrorManager().getErrorCount()) { + llvh::errs() << "Emitted " << N << " errors in the backend. exiting.\n"; + return BackendError; + } + result.bytecodeProvider = + hbc::BCProviderFromSrc::createBCProviderFromSrc(std::move(BM)); } else { llvm_unreachable("Invalid bytecode kind for execution"); result = InvalidFlags; @@ -1735,6 +1741,11 @@ CompileResult generateBytecodeForSerialization( sourceMapGenOrNull, std::move(baseBCProvider)); + if (auto N = M.getContext().getSourceErrorManager().getErrorCount()) { + llvh::errs() << "Emitted " << N << " errors in the backend. exiting.\n"; + return BackendError; + } + if (cl::DumpTarget == DumpBytecode) { disassembleBytecode(hbc::BCProviderFromSrc::createBCProviderFromSrc( std::move(bytecodeModule))); diff --git a/lib/VM/Callable.cpp b/lib/VM/Callable.cpp index 25e1de85eaf..500d423eb45 100644 --- a/lib/VM/Callable.cpp +++ b/lib/VM/Callable.cpp @@ -1395,7 +1395,11 @@ CallResult> GeneratorInnerFunction::callInnerFunction( // Note that this will do nothing after the very first time a lazy function // is called, so we only resize before we save any registers at all. if (LLVM_UNLIKELY(selfHandle->getCodeBlock(runtime)->isLazy())) { - selfHandle->getCodeBlock(runtime)->lazyCompile(runtime); + if (LLVM_UNLIKELY( + selfHandle->getCodeBlock(runtime)->lazyCompile(runtime) == + ExecutionStatus::EXCEPTION)) { + return ExecutionStatus::EXCEPTION; + } if (LLVM_UNLIKELY( ArrayStorage::resize( ctx, diff --git a/lib/VM/CodeBlock.cpp b/lib/VM/CodeBlock.cpp index d175dc81b4a..b1204bf4fae 100644 --- a/lib/VM/CodeBlock.cpp +++ b/lib/VM/CodeBlock.cpp @@ -14,6 +14,7 @@ #include "hermes/IRGen/IRGen.h" #include "hermes/Support/Conversions.h" #include "hermes/Support/PerfSection.h" +#include "hermes/Support/SimpleDiagHandler.h" #include "hermes/VM/GCPointer-inline.h" #include "hermes/VM/Runtime.h" #include "hermes/VM/RuntimeModule.h" @@ -330,14 +331,24 @@ std::unique_ptr compileLazyFunction( } } // namespace -void CodeBlock::lazyCompileImpl(Runtime &runtime) { +ExecutionStatus CodeBlock::lazyCompileImpl(Runtime &runtime) { assert(isLazy() && "Laziness has not been checked"); PerfSection perf("Lazy function compilation"); auto *provider = (hbc::BCProviderLazy *)runtimeModule_->getBytecode(); auto *func = provider->getBytecodeFunction(); auto *lazyData = func->getLazyCompilationData(); + SourceErrorManager &manager = lazyData->context->getSourceErrorManager(); + SimpleDiagHandlerRAII outputManager{manager}; auto bcModule = compileLazyFunction(lazyData); + if (manager.getErrorCount()) { + // Raise a SyntaxError to be consistent with eval(). + return runtime.raiseSyntaxError( + llvh::StringRef{outputManager.getErrorString()}); + } + + assert(bcModule && "No errors, yet no bcModule"); + runtimeModule_->initializeLazyMayAllocate( hbc::BCProviderFromSrc::createBCProviderFromSrc(std::move(bcModule))); // Reset all meta lazyData of the CodeBlock to point to the newly @@ -346,6 +357,8 @@ void CodeBlock::lazyCompileImpl(Runtime &runtime) { functionHeader_ = runtimeModule_->getBytecode()->getFunctionHeader(functionID_); bytecode_ = runtimeModule_->getBytecode()->getBytecode(functionID_); + + return ExecutionStatus::RETURNED; } #endif // HERMESVM_LEAN diff --git a/lib/VM/Debugger/Debugger.cpp b/lib/VM/Debugger/Debugger.cpp index 869dbb76acb..3a37c336a0a 100644 --- a/lib/VM/Debugger/Debugger.cpp +++ b/lib/VM/Debugger/Debugger.cpp @@ -1147,7 +1147,12 @@ bool Debugger::resolveBreakpointLocation(Breakpoint &breakpoint) const { (start.col <= request.column && request.column <= end.col))) { // The code block probably contains the breakpoint we want to set. // First, we compile it. - codeBlock->lazyCompile(runtime_); + if (LLVM_UNLIKELY( + codeBlock->lazyCompile(runtime_) == + ExecutionStatus::EXCEPTION)) { + // TODO: how to better handle this? + runtime_.clearThrownValue(); + } // We've found the codeBlock at this level and expanded it, // so there's no point continuing the search. diff --git a/lib/VM/Interpreter.cpp b/lib/VM/Interpreter.cpp index 7f97b9e71cb..dc415482ae3 100644 --- a/lib/VM/Interpreter.cpp +++ b/lib/VM/Interpreter.cpp @@ -785,7 +785,10 @@ static inline const Inst *nextInstCall(const Inst *ip) { CallResult Runtime::interpretFunctionImpl( CodeBlock *newCodeBlock) { - newCodeBlock->lazyCompile(*this); + if (LLVM_UNLIKELY( + newCodeBlock->lazyCompile(*this) == ExecutionStatus::EXCEPTION)) { + return ExecutionStatus::EXCEPTION; + } #if defined(HERMES_MEMORY_INSTRUMENTATION) || !defined(NDEBUG) // We always call getCurrentIP() in a debug build as this has the effect @@ -1584,7 +1587,10 @@ CallResult Interpreter::interpretFunction( #endif CodeBlock *calleeBlock = func->getCodeBlock(runtime); - CAPTURE_IP(calleeBlock->lazyCompile(runtime)); + CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); + if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { + goto exception; + } curCodeBlock = calleeBlock; CAPTURE_IP_SET(); goto tailCall; @@ -1637,7 +1643,10 @@ CallResult Interpreter::interpretFunction( assert(!SingleStep && "can't single-step a call"); - CAPTURE_IP(calleeBlock->lazyCompile(runtime)); + CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime)); + if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { + goto exception; + } curCodeBlock = calleeBlock; CAPTURE_IP_SET(); goto tailCall; diff --git a/lib/VM/Operations.cpp b/lib/VM/Operations.cpp index 1d69bdca6f7..f677bfe1de3 100644 --- a/lib/VM/Operations.cpp +++ b/lib/VM/Operations.cpp @@ -1679,7 +1679,9 @@ CallResult isConstructor(Runtime &runtime, Callable *callable) { auto *cb = func->getCodeBlock(runtime); // Even though it doesn't make sense logically, we need to compile the // function in order to access it flags. - cb->lazyCompile(runtime); + if (LLVM_UNLIKELY(cb->lazyCompile(runtime) == ExecutionStatus::EXCEPTION)) { + return ExecutionStatus::EXCEPTION; + } return !func->getCodeBlock(runtime)->getHeaderFlags().isCallProhibited( true); } diff --git a/test/hermes/far-environment-access-eval.js b/test/hermes/far-environment-access-eval.js new file mode 100644 index 00000000000..1d72e84f3b4 --- /dev/null +++ b/test/hermes/far-environment-access-eval.js @@ -0,0 +1,33 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: %hermes -O -target=HBC %s 2>&1 | %FileCheck --match-full-lines %s +// RUN: %hermes -O -target=HBC %s -lazy 2>&1 | %FileCheck --match-full-lines %s +// XFAIL: windows +// UNSUPPORTED: ubsan + +// Hermes has a 256 limit on the number of scopes nesting. This test ensures +// that the compiler will handle applications that require more scopes +// gracefully -- i.e., it won't crash, nor emit bad bytecode. + +try { + eval('"use strict";\n' + + '\n' + + 'function sink(x) { return x; }\n' + + 'function foo(){\n' + + ' var var0 = sink("1");\n' + + ' var var1 = sink("2");\n' + + ' var var2 = sink("3");\n' + + ' return ()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=> [var0, var1, var2];\n' + + '}\n' + + '\n' + + 'print(foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()());\n') +} catch (e) { + print(e); +} + +// CHECK: SyntaxError: {{[0-9]+}}:{{[0-9]+}}:Variable environment is out-of-reach diff --git a/test/hermes/far-environment-access.js b/test/hermes/far-environment-access.js new file mode 100644 index 00000000000..da94f2eb3bd --- /dev/null +++ b/test/hermes/far-environment-access.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: (%hermesc -O -target=HBC %s --dump-bytecode 2>&1 || true) | %FileCheck --match-full-lines %s +// RUN: (%hermes -O -target=HBC %s 2>&1 || true) | %FileCheck --match-full-lines %s +// RUN: %hermes -O -target=HBC %s -lazy 2>&1 | %FileCheck --match-full-lines %s --check-prefix=LAZY +// XFAIL: windows +// UNSUPPORTED: ubsan + +// Hermes has a 256 limit on the number of scopes nesting. This test ensures +// that the compiler will handle applications that require more scopes +// gracefully -- i.e., it won't crash, nor emit bad bytecode. + +"use strict"; + +function sink(x) { return x; } +function foo(){ + var var0 = sink("1"); + var var1 = sink("2"); + var var2 = sink("3"); + return ()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=> [var0, var1, var2]; +} + +try { + print(foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()); +} catch (e) { + print(e); +} + + +// CHECK: {{.*}}far-environment-access.js:{{[0-9]+}}:{{[0-9]+}}: error: Variable environment is out-of-reach +// LAZY: SyntaxError: {{[0-9]+}}:{{[0-9]+}}:Variable environment is out-of-reach