1// Copyright 2017 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_TORQUE_DECLARABLE_H_ 6#define V8_TORQUE_DECLARABLE_H_ 7 8#include <cassert> 9#include <string> 10#include <unordered_map> 11 12#include "src/base/functional.h" 13#include "src/base/logging.h" 14#include "src/torque/ast.h" 15#include "src/torque/types.h" 16#include "src/torque/utils.h" 17 18namespace v8 { 19namespace internal { 20namespace torque { 21 22class Scope; 23class Namespace; 24class TypeArgumentInference; 25 26DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*); 27 28struct QualifiedName { 29 std::vector<std::string> namespace_qualification; 30 std::string name; 31 32 QualifiedName(std::vector<std::string> namespace_qualification, 33 std::string name) 34 : namespace_qualification(std::move(namespace_qualification)), 35 name(std::move(name)) {} 36 explicit QualifiedName(std::string name) 37 : QualifiedName({}, std::move(name)) {} 38 39 static QualifiedName Parse(std::string qualified_name); 40 41 bool HasNamespaceQualification() const { 42 return !namespace_qualification.empty(); 43 } 44 45 QualifiedName DropFirstNamespaceQualification() const { 46 return QualifiedName{ 47 std::vector<std::string>(namespace_qualification.begin() + 1, 48 namespace_qualification.end()), 49 name}; 50 } 51 52 friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name); 53}; 54 55class Declarable { 56 public: 57 virtual ~Declarable() = default; 58 enum Kind { 59 kNamespace, 60 kTorqueMacro, 61 kExternMacro, 62 kMethod, 63 kBuiltin, 64 kRuntimeFunction, 65 kIntrinsic, 66 kGenericCallable, 67 kGenericType, 68 kTypeAlias, 69 kExternConstant, 70 kNamespaceConstant 71 }; 72 Kind kind() const { return kind_; } 73 bool IsNamespace() const { return kind() == kNamespace; } 74 bool IsMacro() const { return IsTorqueMacro() || IsExternMacro(); } 75 bool IsTorqueMacro() const { return kind() == kTorqueMacro || IsMethod(); } 76 bool IsMethod() const { return kind() == kMethod; } 77 bool IsExternMacro() const { return kind() == kExternMacro; } 78 bool IsIntrinsic() const { return kind() == kIntrinsic; } 79 bool IsBuiltin() const { return kind() == kBuiltin; } 80 bool IsRuntimeFunction() const { return kind() == kRuntimeFunction; } 81 bool IsGenericCallable() const { return kind() == kGenericCallable; } 82 bool IsGenericType() const { return kind() == kGenericType; } 83 bool IsTypeAlias() const { return kind() == kTypeAlias; } 84 bool IsExternConstant() const { return kind() == kExternConstant; } 85 bool IsNamespaceConstant() const { return kind() == kNamespaceConstant; } 86 bool IsValue() const { return IsExternConstant() || IsNamespaceConstant(); } 87 bool IsScope() const { return IsNamespace() || IsCallable(); } 88 bool IsCallable() const { 89 return IsMacro() || IsBuiltin() || IsRuntimeFunction() || IsIntrinsic() || 90 IsMethod(); 91 } 92 virtual const char* type_name() const { return "<<unknown>>"; } 93 Scope* ParentScope() const { return parent_scope_; } 94 95 // The SourcePosition of the whole declarable. For example, for a macro 96 // this will encompass not only the signature, but also the body. 97 SourcePosition Position() const { return position_; } 98 void SetPosition(const SourcePosition& position) { position_ = position; } 99 100 // The SourcePosition of the identifying name of the declarable. For example, 101 // for a macro this will be the SourcePosition of the name. 102 // Note that this SourcePosition might not make sense for all kinds of 103 // declarables, in that case, the default SourcePosition is returned. 104 SourcePosition IdentifierPosition() const { 105 return identifier_position_.source.IsValid() ? identifier_position_ 106 : position_; 107 } 108 void SetIdentifierPosition(const SourcePosition& position) { 109 identifier_position_ = position; 110 } 111 112 bool IsUserDefined() const { return is_user_defined_; } 113 void SetIsUserDefined(bool is_user_defined) { 114 is_user_defined_ = is_user_defined; 115 } 116 117 protected: 118 explicit Declarable(Kind kind) : kind_(kind) {} 119 120 private: 121 const Kind kind_; 122 Scope* const parent_scope_ = CurrentScope::Get(); 123 SourcePosition position_ = CurrentSourcePosition::Get(); 124 SourcePosition identifier_position_ = SourcePosition::Invalid(); 125 bool is_user_defined_ = true; 126}; 127 128#define DECLARE_DECLARABLE_BOILERPLATE(x, y) \ 129 static x* cast(Declarable* declarable) { \ 130 DCHECK(declarable->Is##x()); \ 131 return static_cast<x*>(declarable); \ 132 } \ 133 static const x* cast(const Declarable* declarable) { \ 134 DCHECK(declarable->Is##x()); \ 135 return static_cast<const x*>(declarable); \ 136 } \ 137 const char* type_name() const override { return #y; } \ 138 static x* DynamicCast(Declarable* declarable) { \ 139 if (!declarable) return nullptr; \ 140 if (!declarable->Is##x()) return nullptr; \ 141 return static_cast<x*>(declarable); \ 142 } \ 143 static const x* DynamicCast(const Declarable* declarable) { \ 144 if (!declarable) return nullptr; \ 145 if (!declarable->Is##x()) return nullptr; \ 146 return static_cast<const x*>(declarable); \ 147 } 148 149// Information about what code caused a specialization to exist. This is used 150// for error reporting. 151struct SpecializationRequester { 152 // The position of the expression that caused this specialization. 153 SourcePosition position; 154 // The Scope which contains the expression that caused this specialization. 155 // It may in turn also be within a specialization, which allows us to print 156 // the stack of requesters when an error occurs. 157 Scope* scope; 158 // The name of the specialization. 159 std::string name; 160 161 static SpecializationRequester None() { 162 return {SourcePosition::Invalid(), nullptr, ""}; 163 } 164 165 bool IsNone() const { 166 return position == SourcePosition::Invalid() && scope == nullptr && 167 name == ""; 168 } 169 SpecializationRequester(SourcePosition position, Scope* scope, 170 std::string name); 171}; 172 173class Scope : public Declarable { 174 public: 175 DECLARE_DECLARABLE_BOILERPLATE(Scope, scope) 176 explicit Scope(Declarable::Kind kind) : Declarable(kind) {} 177 178 std::vector<Declarable*> LookupShallow(const QualifiedName& name) { 179 if (!name.HasNamespaceQualification()) return declarations_[name.name]; 180 Scope* child = nullptr; 181 for (Declarable* declarable : 182 declarations_[name.namespace_qualification.front()]) { 183 if (Scope* scope = Scope::DynamicCast(declarable)) { 184 if (child != nullptr) { 185 ReportError("ambiguous reference to scope ", 186 name.namespace_qualification.front()); 187 } 188 child = scope; 189 } 190 } 191 if (child == nullptr) return {}; 192 return child->LookupShallow(name.DropFirstNamespaceQualification()); 193 } 194 195 std::vector<Declarable*> Lookup(const QualifiedName& name); 196 template <class T> 197 T* AddDeclarable(const std::string& name, T* declarable) { 198 declarations_[name].push_back(declarable); 199 return declarable; 200 } 201 202 const SpecializationRequester& GetSpecializationRequester() const { 203 return requester_; 204 } 205 void SetSpecializationRequester(const SpecializationRequester& requester) { 206 requester_ = requester; 207 } 208 209 private: 210 std::unordered_map<std::string, std::vector<Declarable*>> declarations_; 211 212 // If this Scope was created for specializing a generic type or callable, 213 // then {requester_} refers to the place that caused the specialization so we 214 // can construct useful error messages. 215 SpecializationRequester requester_ = SpecializationRequester::None(); 216}; 217 218class Namespace : public Scope { 219 public: 220 DECLARE_DECLARABLE_BOILERPLATE(Namespace, namespace) 221 explicit Namespace(const std::string& name) 222 : Scope(Declarable::kNamespace), name_(name) {} 223 const std::string& name() const { return name_; } 224 bool IsDefaultNamespace() const; 225 bool IsTestNamespace() const; 226 227 private: 228 std::string name_; 229}; 230 231inline Namespace* CurrentNamespace() { 232 Scope* scope = CurrentScope::Get(); 233 while (true) { 234 if (Namespace* n = Namespace::DynamicCast(scope)) { 235 return n; 236 } 237 scope = scope->ParentScope(); 238 } 239} 240 241class Value : public Declarable { 242 public: 243 DECLARE_DECLARABLE_BOILERPLATE(Value, value) 244 const Identifier* name() const { return name_; } 245 virtual bool IsConst() const { return true; } 246 VisitResult value() const { return *value_; } 247 const Type* type() const { return type_; } 248 249 void set_value(VisitResult value) { 250 DCHECK(!value_); 251 value_ = value; 252 } 253 254 protected: 255 Value(Kind kind, const Type* type, Identifier* name) 256 : Declarable(kind), type_(type), name_(name) {} 257 258 private: 259 const Type* type_; 260 Identifier* name_; 261 base::Optional<VisitResult> value_; 262}; 263 264class NamespaceConstant : public Value { 265 public: 266 DECLARE_DECLARABLE_BOILERPLATE(NamespaceConstant, constant) 267 268 const std::string& external_name() const { return external_name_; } 269 Expression* body() const { return body_; } 270 271 private: 272 friend class Declarations; 273 explicit NamespaceConstant(Identifier* constant_name, 274 std::string external_name, const Type* type, 275 Expression* body) 276 : Value(Declarable::kNamespaceConstant, type, constant_name), 277 external_name_(std::move(external_name)), 278 body_(body) {} 279 280 std::string external_name_; 281 Expression* body_; 282}; 283 284class ExternConstant : public Value { 285 public: 286 DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant) 287 288 private: 289 friend class Declarations; 290 explicit ExternConstant(Identifier* name, const Type* type, std::string value) 291 : Value(Declarable::kExternConstant, type, name) { 292 set_value(VisitResult(type, std::move(value))); 293 } 294}; 295 296enum class OutputType { 297 kCSA, 298 kCC, 299 kCCDebug, 300}; 301 302class Callable : public Scope { 303 public: 304 DECLARE_DECLARABLE_BOILERPLATE(Callable, callable) 305 const std::string& ExternalName() const { return external_name_; } 306 const std::string& ReadableName() const { return readable_name_; } 307 const Signature& signature() const { return signature_; } 308 bool IsTransitioning() const { return signature().transitioning; } 309 const NameVector& parameter_names() const { 310 return signature_.parameter_names; 311 } 312 bool HasReturnValue() const { 313 return !signature_.return_type->IsVoidOrNever(); 314 } 315 void IncrementReturns() { ++returns_; } 316 bool HasReturns() const { return returns_; } 317 base::Optional<Statement*> body() const { return body_; } 318 bool IsExternal() const { return !body_.has_value(); } 319 virtual bool ShouldBeInlined(OutputType output_type) const { 320 // C++ output doesn't support exiting to labels, so functions with labels in 321 // the signature must be inlined. 322 return output_type == OutputType::kCC && !signature().labels.empty(); 323 } 324 bool ShouldGenerateExternalCode(OutputType output_type) const { 325 return !ShouldBeInlined(output_type); 326 } 327 328 static std::string PrefixNameForCCOutput(const std::string& name) { 329 // If a Torque macro requires a C++ runtime function to be generated, then 330 // the generated function begins with this prefix to avoid any naming 331 // collisions with the generated CSA function for the same macro. 332 return "TqRuntime" + name; 333 } 334 335 static std::string PrefixNameForCCDebugOutput(const std::string& name) { 336 // If a Torque macro requires a C++ runtime function to be generated, then 337 // the generated function begins with this prefix to avoid any naming 338 // collisions with the generated CSA function for the same macro. 339 return "TqDebug" + name; 340 } 341 342 // Name to use in runtime C++ code. 343 virtual std::string CCName() const { 344 return PrefixNameForCCOutput(ExternalName()); 345 } 346 347 // Name to use in debug C++ code. 348 virtual std::string CCDebugName() const { 349 return PrefixNameForCCDebugOutput(ExternalName()); 350 } 351 352 protected: 353 Callable(Declarable::Kind kind, std::string external_name, 354 std::string readable_name, Signature signature, 355 base::Optional<Statement*> body) 356 : Scope(kind), 357 external_name_(std::move(external_name)), 358 359 readable_name_(std::move(readable_name)), 360 signature_(std::move(signature)), 361 returns_(0), 362 body_(body) { 363 DCHECK(!body || *body); 364 } 365 366 private: 367 std::string external_name_; 368 std::string readable_name_; 369 Signature signature_; 370 size_t returns_; 371 base::Optional<Statement*> body_; 372}; 373 374class Macro : public Callable { 375 public: 376 DECLARE_DECLARABLE_BOILERPLATE(Macro, macro) 377 bool ShouldBeInlined(OutputType output_type) const override { 378 for (const LabelDeclaration& label : signature().labels) { 379 for (const Type* type : label.types) { 380 if (type->StructSupertype()) return true; 381 } 382 } 383 // Intrinsics that are used internally in Torque and implemented as torque 384 // code should be inlined and not generate C++ definitions. 385 if (ReadableName()[0] == '%') return true; 386 return Callable::ShouldBeInlined(output_type); 387 } 388 389 void SetUsed() { used_ = true; } 390 bool IsUsed() const { return used_; } 391 392 protected: 393 Macro(Declarable::Kind kind, std::string external_name, 394 std::string readable_name, const Signature& signature, 395 base::Optional<Statement*> body) 396 : Callable(kind, std::move(external_name), std::move(readable_name), 397 signature, body), 398 used_(false) { 399 if (signature.parameter_types.var_args) { 400 ReportError("Varargs are not supported for macros."); 401 } 402 } 403 404 private: 405 bool used_; 406}; 407 408class ExternMacro : public Macro { 409 public: 410 DECLARE_DECLARABLE_BOILERPLATE(ExternMacro, ExternMacro) 411 412 const std::string& external_assembler_name() const { 413 return external_assembler_name_; 414 } 415 416 std::string CCName() const override { 417 return "TorqueRuntimeMacroShims::" + external_assembler_name() + 418 "::" + ExternalName(); 419 } 420 421 std::string CCDebugName() const override { 422 return "TorqueDebugMacroShims::" + external_assembler_name() + 423 "::" + ExternalName(); 424 } 425 426 private: 427 friend class Declarations; 428 ExternMacro(const std::string& name, std::string external_assembler_name, 429 Signature signature) 430 : Macro(Declarable::kExternMacro, name, name, std::move(signature), 431 base::nullopt), 432 external_assembler_name_(std::move(external_assembler_name)) {} 433 434 std::string external_assembler_name_; 435}; 436 437class TorqueMacro : public Macro { 438 public: 439 DECLARE_DECLARABLE_BOILERPLATE(TorqueMacro, TorqueMacro) 440 bool IsExportedToCSA() const { return exported_to_csa_; } 441 std::string CCName() const override { 442 // Exported functions must have unique and C++-friendly readable names, so 443 // prefer those wherever possible. 444 return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName() 445 : ExternalName()); 446 } 447 std::string CCDebugName() const override { 448 // Exported functions must have unique and C++-friendly readable names, so 449 // prefer those wherever possible. 450 return PrefixNameForCCDebugOutput(IsExportedToCSA() ? ReadableName() 451 : ExternalName()); 452 } 453 454 protected: 455 TorqueMacro(Declarable::Kind kind, std::string external_name, 456 std::string readable_name, const Signature& signature, 457 base::Optional<Statement*> body, bool is_user_defined, 458 bool exported_to_csa) 459 : Macro(kind, std::move(external_name), std::move(readable_name), 460 signature, body), 461 exported_to_csa_(exported_to_csa) { 462 SetIsUserDefined(is_user_defined); 463 } 464 465 private: 466 friend class Declarations; 467 TorqueMacro(std::string external_name, std::string readable_name, 468 const Signature& signature, base::Optional<Statement*> body, 469 bool is_user_defined, bool exported_to_csa) 470 : TorqueMacro(Declarable::kTorqueMacro, std::move(external_name), 471 std::move(readable_name), signature, body, is_user_defined, 472 exported_to_csa) {} 473 474 bool exported_to_csa_ = false; 475}; 476 477class Method : public TorqueMacro { 478 public: 479 DECLARE_DECLARABLE_BOILERPLATE(Method, Method) 480 bool ShouldBeInlined(OutputType output_type) const override { 481 return Macro::ShouldBeInlined(output_type) || 482 signature() 483 .parameter_types.types[signature().implicit_count] 484 ->IsStructType(); 485 } 486 AggregateType* aggregate_type() const { return aggregate_type_; } 487 488 private: 489 friend class Declarations; 490 Method(AggregateType* aggregate_type, std::string external_name, 491 std::string readable_name, const Signature& signature, Statement* body) 492 : TorqueMacro(Declarable::kMethod, std::move(external_name), 493 std::move(readable_name), signature, body, true, false), 494 aggregate_type_(aggregate_type) {} 495 AggregateType* aggregate_type_; 496}; 497 498class Builtin : public Callable { 499 public: 500 enum Kind { kStub, kFixedArgsJavaScript, kVarArgsJavaScript }; 501 DECLARE_DECLARABLE_BOILERPLATE(Builtin, builtin) 502 Kind kind() const { return kind_; } 503 bool IsStub() const { return kind_ == kStub; } 504 bool IsVarArgsJavaScript() const { return kind_ == kVarArgsJavaScript; } 505 bool IsFixedArgsJavaScript() const { return kind_ == kFixedArgsJavaScript; } 506 507 private: 508 friend class Declarations; 509 Builtin(std::string external_name, std::string readable_name, 510 Builtin::Kind kind, const Signature& signature, 511 base::Optional<Statement*> body) 512 : Callable(Declarable::kBuiltin, std::move(external_name), 513 std::move(readable_name), signature, body), 514 kind_(kind) {} 515 516 Kind kind_; 517}; 518 519class RuntimeFunction : public Callable { 520 public: 521 DECLARE_DECLARABLE_BOILERPLATE(RuntimeFunction, runtime) 522 523 private: 524 friend class Declarations; 525 RuntimeFunction(const std::string& name, const Signature& signature) 526 : Callable(Declarable::kRuntimeFunction, name, name, signature, 527 base::nullopt) {} 528}; 529 530class Intrinsic : public Callable { 531 public: 532 DECLARE_DECLARABLE_BOILERPLATE(Intrinsic, intrinsic) 533 534 private: 535 friend class Declarations; 536 Intrinsic(std::string name, const Signature& signature) 537 : Callable(Declarable::kIntrinsic, name, name, signature, base::nullopt) { 538 if (signature.parameter_types.var_args) { 539 ReportError("Varargs are not supported for intrinsics."); 540 } 541 } 542}; 543 544class TypeConstraint { 545 public: 546 base::Optional<std::string> IsViolated(const Type*) const; 547 548 static TypeConstraint Unconstrained() { return {}; } 549 static TypeConstraint SubtypeConstraint(const Type* upper_bound) { 550 TypeConstraint result; 551 result.upper_bound = {upper_bound}; 552 return result; 553 } 554 555 private: 556 base::Optional<const Type*> upper_bound; 557}; 558 559base::Optional<std::string> FindConstraintViolation( 560 const std::vector<const Type*>& types, 561 const std::vector<TypeConstraint>& constraints); 562 563std::vector<TypeConstraint> ComputeConstraints( 564 Scope* scope, const GenericParameters& parameters); 565 566template <class SpecializationType, class DeclarationType> 567class GenericDeclarable : public Declarable { 568 private: 569 using Map = std::unordered_map<TypeVector, SpecializationType, 570 base::hash<TypeVector>>; 571 572 public: 573 void AddSpecialization(const TypeVector& type_arguments, 574 SpecializationType specialization) { 575 DCHECK_EQ(0, specializations_.count(type_arguments)); 576 if (auto violation = 577 FindConstraintViolation(type_arguments, Constraints())) { 578 Error(*violation).Throw(); 579 } 580 specializations_[type_arguments] = specialization; 581 } 582 base::Optional<SpecializationType> GetSpecialization( 583 const TypeVector& type_arguments) const { 584 auto it = specializations_.find(type_arguments); 585 if (it != specializations_.end()) return it->second; 586 return base::nullopt; 587 } 588 589 using iterator = typename Map::const_iterator; 590 iterator begin() const { return specializations_.begin(); } 591 iterator end() const { return specializations_.end(); } 592 593 const std::string& name() const { return name_; } 594 auto declaration() const { return generic_declaration_->declaration; } 595 const GenericParameters& generic_parameters() const { 596 return generic_declaration_->generic_parameters; 597 } 598 599 const std::vector<TypeConstraint>& Constraints() { 600 if (!constraints_) 601 constraints_ = {ComputeConstraints(ParentScope(), generic_parameters())}; 602 return *constraints_; 603 } 604 605 protected: 606 GenericDeclarable(Declarable::Kind kind, const std::string& name, 607 DeclarationType generic_declaration) 608 : Declarable(kind), 609 name_(name), 610 generic_declaration_(generic_declaration) { 611 DCHECK(!generic_declaration->generic_parameters.empty()); 612 } 613 614 private: 615 std::string name_; 616 DeclarationType generic_declaration_; 617 Map specializations_; 618 base::Optional<std::vector<TypeConstraint>> constraints_; 619}; 620 621class GenericCallable 622 : public GenericDeclarable<Callable*, GenericCallableDeclaration*> { 623 public: 624 DECLARE_DECLARABLE_BOILERPLATE(GenericCallable, generic_callable) 625 626 base::Optional<Statement*> CallableBody(); 627 628 TypeArgumentInference InferSpecializationTypes( 629 const TypeVector& explicit_specialization_types, 630 const std::vector<base::Optional<const Type*>>& arguments); 631 632 private: 633 friend class Declarations; 634 GenericCallable(const std::string& name, 635 GenericCallableDeclaration* generic_declaration) 636 : GenericDeclarable<Callable*, GenericCallableDeclaration*>( 637 Declarable::kGenericCallable, name, generic_declaration) {} 638}; 639 640class GenericType 641 : public GenericDeclarable<const Type*, GenericTypeDeclaration*> { 642 public: 643 DECLARE_DECLARABLE_BOILERPLATE(GenericType, generic_type) 644 645 private: 646 friend class Declarations; 647 GenericType(const std::string& name, 648 GenericTypeDeclaration* generic_declaration) 649 : GenericDeclarable<const Type*, GenericTypeDeclaration*>( 650 Declarable::kGenericType, name, generic_declaration) {} 651}; 652 653class TypeAlias : public Declarable { 654 public: 655 DECLARE_DECLARABLE_BOILERPLATE(TypeAlias, type_alias) 656 657 const Type* type() const { 658 if (type_) return *type_; 659 return Resolve(); 660 } 661 const Type* Resolve() const; 662 bool IsRedeclaration() const { return redeclaration_; } 663 SourcePosition GetDeclarationPosition() const { 664 return declaration_position_; 665 } 666 667 private: 668 friend class Declarations; 669 friend class TypeVisitor; 670 671 explicit TypeAlias( 672 const Type* type, bool redeclaration, 673 SourcePosition declaration_position = SourcePosition::Invalid()) 674 : Declarable(Declarable::kTypeAlias), 675 type_(type), 676 redeclaration_(redeclaration), 677 declaration_position_(declaration_position) {} 678 explicit TypeAlias( 679 TypeDeclaration* type, bool redeclaration, 680 SourcePosition declaration_position = SourcePosition::Invalid()) 681 : Declarable(Declarable::kTypeAlias), 682 delayed_(type), 683 redeclaration_(redeclaration), 684 declaration_position_(declaration_position) {} 685 686 mutable bool being_resolved_ = false; 687 mutable base::Optional<TypeDeclaration*> delayed_; 688 mutable base::Optional<const Type*> type_; 689 bool redeclaration_; 690 const SourcePosition declaration_position_; 691}; 692 693std::ostream& operator<<(std::ostream& os, const Callable& m); 694std::ostream& operator<<(std::ostream& os, const Builtin& b); 695std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b); 696std::ostream& operator<<(std::ostream& os, const GenericCallable& g); 697 698#undef DECLARE_DECLARABLE_BOILERPLATE 699 700} // namespace torque 701} // namespace internal 702} // namespace v8 703 704#endif // V8_TORQUE_DECLARABLE_H_ 705