.DS_Store
*~
+# === Rules for tools ===
+*.code-workspace
+
# === lwnode ===
.circleci/gbs.conf
kuep_net_signer.sh
--- /dev/null
+Copyright (c) 2022 Samsung Inner Source, Lightweight node.js. All Rights Reserved.
+
+# Code owners
+Code owners in alphabetical order.
+
+```
+Ryan Hyun Choi <ryan.choi@samsung.com>
+Daeyeon Jeong <daeyeon.jeong@samsung.com>
+Haesik Jun <haesik.jun@samsung.com>
+Ho Sung Kim <hs852.kim@samsung.com>
+```
+
--- /dev/null
+# Samsung Inner Source Governance
+
+The *Samsung Inner Source Governance*(the "Governance") sets forth the responsibilities and procedures for technical contribution to, and oversight of, the *Samsung Inner Source Community*(the "Community") within Samsung Electronics. Contributors to the Community **MUST** comply with the terms of the governance as well as any applicable policies of Samsung Electronics.
+
+All the Communities under the *Samsung Inner Source Program* **MUST** inherit this Governance and each Community is allowed to create its own Community Rule but only as a type of ADDON tailoring. Additionally which **SHOULD** be announced each project wiki page with a title named 'Community Rules' and should be described in [Project List] page of your project in this portal.
+
+## 1. Guideline for Operation
+Samsung Inner Source Program uses an **OPEN ENVIRONMENT** which the CODE(Github enterprise version) as a source forge.
+The baseline of Inner Source Program repository is **PUBLIC**. If you have any specific reason to make it PRIVATE, you SHOULD make a request via Inner Source Portal. However the request COULD not be accepted if it doesn't have obvious reasons.
+
+## 2. Roles and Responsibilities
+The Samsung Inner Source Community Recognizes the following formal roles: a Developer, a Committer and a Maintainer.
+Informally, the Community may organize itself and give rights and responsibilities to the necessary people to achieve its goals.
+
+1. A Developer
+
+ A Developer is anyone who wishes to contribute to the project, at any level. Developers are granted the following right to:
+ - Contribute code, documentation, translations, artwork, etc,.
+ - Report defects(bugs) and suggestions for enhancements;
+ - Participate in the process of reviewing contributions by others;
+ - Initiate and participate in discussions in communication channels.
+
+ Developers are required to:
+ - Abide by decisions, once made. They are welcome to provide new, relevant information to reopen decisions;
+ - Assume responsibility for issues and bugs introduced by one's own contributions;
+ - Respect the 'Rules of Community'(see below);
+ - Provide constructive advices whenever participating in discussions and in the review of contributions.
+
+2. A Committer
+
+ A Committer is a Developer who is also responsible for the maintenance of a particular area of code or of a source code repository. Committers have the following rights and responsibilities, in addition to those listed for Developers:
+ - Right to set goals for the short and medium terms for the repository being maintained;
+ - Responsibility to ensure all contributions to the repository have been reviewed within reasonable time;
+ - Responsibility to monitor discussions in the Community.
+
+3. A Maintainer
+
+ A Maintainer is a Developer who is also responsible for knowing, directing and anticipating the needs of a given repository. Maintainers have the following rights and responsibilities, in addition to those listed for Committers:
+ - Right to set the overall organization of the source code in the repository;
+ - Right and responsibility to lead the feature development discussion;
+ - Responsibility to ensure the quality of the code to expected levels;
+ - Responsibility to participate in the quality verification and release process, when those happen.
+ - Responsibility of a **representative of the Community**;
+ - Maximum 2 Maintainers are allowed per Community.
+
+ A Maintainer is not a mandatory role of Samsung Inner Source Governance. When the Community decide to go without the role title of a Maintainers, one of the Committer of the Community SHOULD take the written roles and responsibilities of a Maintainer in Samsung Inner Source Governance.
+
+4. A Benevolent-Leader
+
+ A Benevolent-Leader is a member of the Community who has the final saying in the case of disputes. This is a model followed by many successful Open Source projects, and they are sometimes called the "benevolent dictator". The Benevolent-Leader has the following responsibilities:
+ - Govern Samsung Inner Source Program;
+ - Make calm controversy in Communities and a Program itself.
+
+ A Benevolent-Leader refers to the role, not a person.
+
+5. An Administrator
+
+ An Administrator is a member who help operate and gear to healthy Samsung Inner Source Program. Samsung Inner Source Portal is operated by the Administrator.
+
+6. Selection of Committers and Maintainers
+
+ A candidate for the Committer role should be one of the Developers who has submitted at least 10 non-trivial patches in that repository and has shown characteristics consistent with the requirements of the Committer role. To be a candidate for the Committer or the Maintainer, a Developer can self-nominate with proper evidences.
+
+ The selection process SHOULD be archived by consensus of the Developers active, Committers and Maintainers in that repository. If consensus cannot be achieved, the Benevolent-Leader will make the decision and all decisions MUST be ratified by the Benevolent-Leader.
+
+7. Revocation of Committers/Maintainers status
+
+ A Maintainer or a Committer who intentionally abuses his review privilege may have it temporarily suspended on the request of other Committers or Maintainers. Committers and Maintainers not including the person under consideration should discuss on the revocation of the person. If consensus cannot be reached, the Benevolent-Leader will make the decision and all decisions MUST be ratified by the Benevolent-Leader.
+
+## 3. Rules of Community
+
+1. Samsung Inner Source Program aims for OPEN COLLABORATION.
+
+ 1) Egalitarian, Every contributors who is willing to help a project should be welcome.
+ 2) Contributions to projects should be judged meritocracy based on the value they bring to the project. For transparency, all the merits SHOULD be come with **OPEN COMMUNICATION** (decisions should be discussed publicly).
+ 3) A Community is an individual organizational unit.
+
+ All Community members must abide by rules of common sense, civility and good neighborliness. Frank discussion is welcomed and encouraged with the goal of arriving at the best technical solution possible. Discussion about the people participating in development is not welcomed and ad hominem attacks MUST not be tolerated.
+
+2. All Community members must adhere to these simple rules:
+
+ 1) Respect and acknowledge all contributions, suggestions and comments from the Community;
+ 2) Listen and be open to all opinions, which are subject to open discussion;
+ 3) Help each other and the other developers;
+ 4) Assume people mean well;
+ 5) **Be humble and bold**.
+
+3. Lazy Consensus and Silent Consent
+
+ 1) Lazy Consensus means that developers may proceed with work when they have a reason to believe that other Developers in the Community will agree with the direction, and need not stop to initiate unnecessary discussions about the work. In this case, they should publish their work rapidly (that is, merge proposals to version control) to allow others to raise objections when there are any. When the developer is not sure there will be consensus, they should raise a proposal on the appropriate communication channels. The Silent Consent principle will apply in this case.
+ 2) Silent Consent means that those who do not offer a reasoned alternative in course of the discussion implicitly agree with the proposal.
+ 3) These principles are valid for **OPEN DISCUSSIONS** in the Github Issues and/or the Primary Communication Channel of the Community. Consensus reached in other ways (such as face-to face discussions or communication on other channels) are to be considered unapproved proposals until submitted to the relevant Github Issues and/or the Primary Communication Channel of the Community. Thus giving dissenting options the opportunity to be voiced.
+
+4. Decision Making in the Samsung Inner Source Community
+
+ 1) Decisions are made always at the lowest level possible that is applicable for the decision in a question.
+ 2) Individual Developers are making decisions every time they submit changes.
+ 3) two or more Developers also make decisions when participating in an **OPEN DISCUSSION**. Their arguments in why a given decision should be made are part of the consensus that needs to be reached for the decision. Any of opinions **SHOULD NOT** be disregarded.
+ 4) If those Developers cannot agree and reach consensus on a decision, then the Benevolent-Leader WOULD mediates the situation, avoiding stalemates. Arbitration COULD made by voting of relevant Community members on the issue.
+ - Decisions affecting a project are made by community members of the project.
+ - Decisions that overlap more than one project are made in conjunction by community members of related projects.
+
a memory optimized JavaScript Engine developed by Samsung Research,
instead of the default V8 JS engine.
+Memory usage and binary footprint are reported [here](https://pages.github.sec.samsung.net/lws/lwnode-test-results).
+
## Supported Platforms
* Ubuntu 18.04, 16.04
* Tizen 4.0 and above
## Maintainers
A list of maintainers can be found in [MAINTAINERS.md](MAINTAINERS.md).
+
+## Governance
+See [GOVERNANCE.md](GOVERNANCE.md)
+
'-fno-omit-frame-pointer',
'-fsanitize=address',
'-fsanitize-address-use-after-scope',
+ '-Wno-maybe-uninitialized',
],
'defines': [ 'LEAK_SANITIZER', 'V8_USE_ADDRESS_SANITIZER' ],
'cflags!': [ '-fomit-frame-pointer' ],
dest='escargot_threading',
help='Enable Escargot threading')
+lwnode_optgroup.add_option('--escargot-debugger',
+ action='store_true',
+ dest='escargot_debugger',
+ help='Enable Escargot debugging')
+
parser.add_option_group(lwnode_optgroup)
def get_lwnode_gyp_options():
else:
args += (['-Descargot_threading=0'])
+ if options.escargot_debugger:
+ args += (['-Descargot_debugger=1'])
+ else:
+ args += (['-Descargot_debugger=0'])
args += ['-Dnode_core_target_name=lwnode']
args += ['-Dnode_lib_target_name=liblwnode']
+++ /dev/null
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-\f
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-\f
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-\f
- Appendix: How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19yy name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
+// @lwnode
+UV_EXTERN int uv_watcher_queue_empty(uv_loop_t* loop);
+
/* Don't export the private CPP symbols. */
#undef UV_HANDLE_TYPE_PRIVATE
#undef UV_REQ_TYPE_PRIVATE
buf[*buflen] = '\0';
return 0;
- }
+ }
/* Case iii). Search PATH environment variable */
cloned_path = NULL;
/* Out of tokens (path entries), and no match found */
return UV_EINVAL;
}
+
+// @lwnode
+int uv_watcher_queue_empty(uv_loop_t* loop) {
+ return QUEUE_EMPTY(&loop->watcher_queue);
+}
--- /dev/null
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = lwnode
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 0.1
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ./docs/api
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = YES
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = YES
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = lwnode/code/escargotshim/src lwnode/code/escargotshim/include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome-css/doxygen-awesome.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 350
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = YES
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = YES
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+# Introduction
+The development of node.js has created a platform-independent JavaScript runtime environment, where Web developers can run their JavaScript code outside a Web browser. Benefits of using node.js for a Web app developer when creating Web apps includes, but not limited to, reusing already known Web development knowledge, using only JavaScript on both server and client sides, and so on. While node.js provides the state-of-the-art performance and a wide range of supporting libraries, it is not suitable for memory-constrained devices such as TVs, and consumer electronics. This is because node.js is designed to provide better performance by efficiently using a large amount of memory. This goal of node.js conflicts with the goal of optimizing memory usage on these devices in memory-constrained environments. Adopting node.js to these memory-constrained devices is challenging, because the design and implementation of node.js conflict with the physically small amount of memory shipped with these devices.
+
+In this document, we introduce lightweight node.js (LWNode) whose aim is to provide a JavaScript runtime environment for memory-constrained devices. Some advantages of LWNode are as follows:
+
+* LWNode is a memory-optimized node.js for Tizen-based memory-constrained devices such as TVs. Compared to stock node.js, LWNode consumes 30% less amount of memory on TV.
+* Supports a wide range of node.js modules
+
+LWNode is already shipped to the following Samsung products.
+
+* Provides a runtime environment for Spotify on TV
+
+# Expected Values
+LWNode can easily provide a JavaScript runtime environment in a memory-efficient way. In particular, LWNode
+
+* Provides a Tizen-optimized, and memory-efficient JavaScript runtime environment
+* Provides a set of Tizen-specific Web Device APIs
+
+# Requirements
+LWNode is designed to meet the following requirements.
+
+* Functional requirements
+ - Supports Spotify on TV
+ - Supports a set of node.js modules defined in Appendix 1
+ - Supports a set of Tizen Web Device APIs
+* Non-functional requirements
+ - Customizable for various business requirements and target devices
+ - Low memory usage and small memory footprints
+
+# Target Applications
+LWNode supports the following devices.
+
+* Tizen-based devices such as TVs, speakers, etc.
+
+# Appendix 1: Supporting Modules
+A list of supporting modules are described in [spec.md](spec.md).
--- /dev/null
+# Doxyfile 1.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Doxygen Awesome"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Modern Doxygen theme"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO = ./logo.drawio.svg
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which efficively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = include \
+ README.md \
+ docs/page.dox
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
+# *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f18 \
+ *.f \
+ *.for \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf \
+ *.ice
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH = img
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see:
+# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
+# performance. This can be particularly helpful with template rich C++ code for
+# which doxygen's built-in parser lacks the necessary type information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
+# YES then doxygen will add the directory of each input to the include path.
+# The default value is: YES.
+
+CLANG_ADD_INC_PATHS = YES
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the directory containing a file called compile_commands.json. This
+# file is the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
+# options used when the source files were built. This is equivalent to
+# specifying the -p option to a clang tool, such as clang-check. These options
+# will then be passed to the parser. Any options specified with CLANG_OPTIONS
+# will be added as well.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER = docs/doxygen-custom/header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER = docs/doxygen-custom/footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = doxygen-awesome.css \
+ doxygen-awesome-sidebar-only.css \
+ doxygen-awesome-sidebar-only-darkmode-toggle.css \
+ docs/doxygen-custom/custom.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES = doxygen-awesome-darkmode-toggle.js
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 209
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 255
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 113
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see:
+# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 340
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME =
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to Sqlite3 output
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc and
+# plantuml temporary files.
+# The default value is: YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+MIT License
+
+Copyright (c) 2021 jothepro
+
+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.
--- /dev/null
+# Doxygen Awesome
+
+[](https://github.com/jothepro/doxygen-awesome-css/releases/latest)
+[](https://github.com/jothepro/doxygen-awesome-css/blob/main/LICENSE)
+
+<div style="filter: drop-shadow(0px 3px 15px rgba(0,0,0,0.25))">
+
+[](https://jothepro.github.io/doxygen-awesome-css/)
+
+</div>
+<br>
+
+**Doxygen Awesome** is a custom **CSS theme for doxygen** html-documentation with lots of customization parameters.
+
+## Motivation
+
+I really like how the doxygen html-documentation is structured! But IMHO it looks a bit outdated.
+
+This theme is an attemt to update the visuals of doxygen without changing it's overall layout too much.
+
+## Features
+
+- 🌈 Clean, modern design
+- 🚀 Heavily customizable by adjusting CSS-variables
+- 🧩 No changes to the HTML structure of Doxygen required
+- 📱 Improved mobile usability
+- 🌘 Dark mode support!
+- 🥇 Works best with **doxygen 1.9.1** or **1.9.2**
+
+## Installation
+
+Copy the `css` files from this repository into your project or add this repository as submodule and check out the latest release:
+
+```bash
+git submodule add https://github.com/jothepro/doxygen-awesome-css.git
+cd doxygen-awesome-css
+git checkout v1.5.0
+```
+
+Then make the option `HTML_EXTRA_STYLESHEET` in your Doxyfile point to the `css` files:
+
+```
+# Doxyfile
+# ...
+HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
+```
+
+### Variants
+
+There is two variants of the theme.
+
+
+
+1. **Base theme**:
+```
+# Doxyfile
+GENERATE_TREEVIEW = YES # optional. Also works without treeview
+HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
+```
+
+2. **Sidebar-only theme** (experimental):
+```
+# Doxyfile
+GENERATE_TREEVIEW = YES # required!
+HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css doxygen-awesome-css/doxygen-awesome-sidebar-only.css
+```
+
+### Dark Mode Toggle (Experimental)
+
+The theme comes with an experimental feature that adds a button to enable and disable the dark theme variant manually.
+
+It requires customizations in both the header & footer html template.
+
+1. Create default header & footer templates:
+ ```bash
+ doxygen -w html header.html footer.html delete_me.css
+ ```
+
+2. Reference the required resources in your `Doxyfile`:
+ ```
+ # Include the required Javascript
+ HTML_EXTRA_FILES = doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js
+
+ # Add the additional CSS. This is ONLY required for the sidebar-only theme variant!
+ HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \
+ doxygen-awesome-css/doxygen-awesome-sidebar-only.css \
+ doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css
+
+ # set custom header & footer files generated in previous step
+ HTML_HEADER = header.html
+ HTML_FOOTER = footer.html
+ ```
+
+3. In `header.html`, include `doxygen-awesome-darkmode-toggle.js` at the end of the `<head>`:
+ ```html
+ <html>
+ <head>
+ <!-- ... other metadata & script includes ... -->
+ <script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
+ </head>
+ <body>
+ ```
+4. In `footer.html`, initialize the `doxygen-awesome-dark-mode-toggle` element at the end of the `<body>`:
+ ```html
+ <!-- ... -->
+ <script type="text/javascript">
+ // script for doxygen 1.9.1
+ $(function() {
+ $(document).ready(function(){
+ toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
+ toggleButton.title = "Toggle Light/Dark Mode"
+ document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
+ })
+ })
+ </script>
+ </body>
+ </html>
+ ```
+ **Attention**: In Doxygen 1.9.2 the searchbox and it's siblings are deleted on every resize, which is why the toggle button has to be re-added every time:
+ ```js
+ $(function() {
+ toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
+ toggleButton.title = "Toggle Light/Dark Mode"
+
+ $(document).ready(function(){
+ document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
+ })
+ $(window).resize(function(){
+ document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
+ })
+ })
+ ```
+
+## Examples
+
+- Sidebar-Only theme: [Documentation of this repository](https://jothepro.github.io/doxygen-awesome-css/)
+- Base theme: [libsl3](https://a4z.github.io/libsl3/)
+
+
+## Configuration
+
+### CSS Variables
+
+This theme is highly customizable because a lot of things are parameterized with CSS variables. The following
+list of parameters is not complete! You can easily modify any variable with the developer tools of your browser to find
+out what it does.
+
+To customize the existing theme, add your own `custom.css` and overwrite the variables there:
+```
+HTML_EXTRA_STYLESHEET = doxygen-awesome-theme/doxygen-awesome.css custom.css
+```
+
+```css
+/* custom.css */
+html {
+ /* define light-mode variable overrides here */
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light-mode) {
+ /* define dark-mode variable overrides here if you DON'T use doxygen-awesome-darkmode-toggle.js */
+ }
+}
+
+html.dark-mode {
+ /* define dark-mode variable overrides here if you DO use doxygen-awesome-darkmode-toggle.js */
+}
+```
+
+| Parameter | Default (Light) | Default (Dark) |
+| :-------------------------------- | :---------------------------------------------------------- | ----------------------------------------------------------- |
+| **Color Scheme**:<br>primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... |||
+| `--primary-color` | <span style="background:#1779c4;color:white">#1779c4</span> | <span style="background:#1982d2;color:white">#1982d2</span> |
+| `--primary-dark-color` | <span style="background:#00559f;color:white">#00559f</span> | <span style="background:#5ca8e2;color:white">#5ca8e2</span> |
+| `--primary-light-color` | <span style="background:#7aabd6;color:black">#7aabd6</span> | <span style="background:#4779ac;color:white">#4779ac</span> |
+| `--primary-lighter-color` | <span style="background:#cae1f1;color:black">#cae1f1</span> | <span style="background:#191e21;color:white">#191e21</span> |
+| `--primary-lightest-color` | <span style="background:#e9f1f8;color:black">#e9f1f8</span> | <span style="background:#191a1c;color:white">#191a1c</span> |
+| **Spacing:**<br>default spacings. Most ui components reference these values for spacing, to provide uniform spacing on the page. |||
+| `--spacing-small` | `5px` | |
+| `--spacing-medium` | `10px` | |
+| `--spacing-large` | `16px` | |
+| **Border Radius**:<br>border radius for all rounded ui components. Will affect many components, like dropdowns, memitems, codeblocks, ... |||
+| `--border-radius-small` | `4px` | |
+| `--border-radius-medium` | `6px` | |
+| `--border-radius-large` | `8px` | |
+| **Content Width**:<br>The content is centered and constrained in its width. To make the content fill the whole page, set the following variable to `auto`. |||
+| `--content-maxwidth` | `1000px` | |
+| **Code Fragment Colors**:<br>Color-Scheme of multiline codeblocks |||
+| `--fragment-background` | <span style="background:#282c34;color:white">#282c34</span> | |
+| `--fragment-foreground` | <span style="background:#fff;wolor:black">#fff</span> | |
+| **Arrow Opacity**:<br>By default the arrows in the sidebar are only visible on hover. You can override this behaviour so they are visible all the time. |||
+| `--side-nav-arrow-opacity` | `0` | |
+| `--side-nav-arrow-hover-opacity` | `0.9` | |
+| **Darkmode Toggle Icon**:<br>If you have enabled the darkmode toggle button, you can define the icon that is shown for the current mode. |||
+| `--darkmode-toggle-button-icon` | ☀️ | 🌛 |
+| ...and many more |||
+
+If you miss a configuration option or find a bug, please consider [opening an issue](https://github.com/jothepro/doxygen-awesome-css/issues)!
+
+### Doxygen generator
+
+The theme overrides most colors with the `--primary-color-*` variables.
+
+But there is a few small images and graphics that the theme cannot adjust or replace. To make these blend in better with
+the rest, it is recommended to adjust the [doxygen color settings](https://www.doxygen.nl/manual/customize.html#minor_tweaks_colors)
+to something that matches the chosen color-scheme.
+
+For the default color-scheme, these values work out quite well:
+
+```
+# Doxyfile
+HTML_COLORSTYLE_HUE = 209
+HTML_COLORSTYLE_SAT = 255
+HTML_COLORSTYLE_GAMMA = 113
+```
+
+## Browser support
+
+Tested with
+
+- Chrome 91, Chrome 91 for Android, Chrome 87 for iOS
+- Safari 14, Safari for iOS 14
+- Firefox 89, Firefox Daylight 89 for Android, Firefox Daylight 33 for iOS
+
+## Tips & Tricks
+
+### Class Diagrams with Graphviz
+
+To get the best looking class diagrams for your documentation, generate them with Graphviz as vector graphics with transparent background:
+
+```
+# Doxyfile
+HAVE_DOT = YES
+DOT_IMAGE_FORMAT = svg
+DOT_TRANSPARENT = YES
+```
+
+### Share your own theme customizations
+
+If you customized the theme with custom colors, spacings, font-sizes, etc. and you want to share your creation with others, you can to this [here](https://github.com/jothepro/doxygen-awesome-css/discussions/13).
+
+I am always curious to learn about how you made the theme look even better!
+
+
+## Credits
+
+This theme is heavily inspired by the beautiful [vuepress](https://vuepress.vuejs.org/) static site generator default theme!
--- /dev/null
+.github-corner svg {
+ fill: var(--primary-light-color);
+ color: var(--page-background-color);
+ width: 72px;
+ height: 72px;
+}
+
+@media screen and (max-width: 767px) {
+ .github-corner svg {
+ width: 55px;
+ height: 55px;
+ }
+ #projectnumber {
+ margin-right: 22px;
+ }
+}
--- /dev/null
+<!-- HTML footer for doxygen 1.9.1-->
+<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ $navpath
+ <li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion </li>
+ </ul>
+</div>
+<!--END GENERATE_TREEVIEW-->
+<!--BEGIN !GENERATE_TREEVIEW-->
+<hr class="footer"/><address class="footer"><small>
+$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion
+</small></address>
+<!--END !GENERATE_TREEVIEW-->
+<script type="text/javascript">
+ $(function() {
+ toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
+ toggleButton.title = "Toggle Light/Dark Mode"
+
+ $(document).ready(function(){
+ document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
+ })
+ $(window).resize(function(){
+ document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
+ })
+ })
+</script>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen $doxygenversion"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+
+<!-- BEGIN opengraph metadata -->
+<meta property="og:title" content="Doxygen Awesome" />
+<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
+<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
+<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
+<!-- END opengraph metadata -->
+
+<!-- BEGIN twitter metadata -->
+<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
+<meta name="twitter:title" content="Doxygen Awesome" />
+<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
+<!-- END twitter metadata -->
+
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
+<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
+<script type="text/javascript" src="$relpath^jquery.js"></script>
+<script type="text/javascript" src="$relpath^dynsections.js"></script>
+<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+
+<!-- https://tholman.com/github-corners/ -->
+<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub">
+ <svg viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
+ <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
+
+
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <!--BEGIN PROJECT_LOGO-->
+ <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
+ <!--END PROJECT_LOGO-->
+ <!--BEGIN PROJECT_NAME-->
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">$projectname
+ <!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
+ </div>
+ <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
+ </td>
+ <!--END PROJECT_NAME-->
+ <!--BEGIN !PROJECT_NAME-->
+ <!--BEGIN PROJECT_BRIEF-->
+ <td style="padding-left: 0.5em;">
+ <div id="projectbrief">$projectbrief</div>
+ </td>
+ <!--END PROJECT_BRIEF-->
+ <!--END !PROJECT_NAME-->
+ <!--BEGIN DISABLE_INDEX-->
+ <!--BEGIN SEARCHENGINE-->
+ <td>$searchbox</td>
+ <!--END SEARCHENGINE-->
+ <!--END DISABLE_INDEX-->
+ </tr>
+ </tbody>
+</table>
+</div>
+<!--END TITLEAREA-->
+<!-- end header part -->
--- /dev/null
+/*!
+
+\page page1 Example Page
+\tableofcontents
+Leading text.
+\section sec An example section
+This page contains the subsections \ref subsection1 and \ref subsection2.
+\subsection subsection1 The first subsection
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+\subsection subsection2 The second subsection
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+\note Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+*/
\ No newline at end of file
--- /dev/null
+/**
+
+Doxygen Awesome
+https://github.com/jothepro/doxygen-awesome-css
+
+MIT License
+
+Copyright (c) 2021 jothepro
+
+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.
+
+*/
+
+class DoxygenAwesomeDarkModeToggle extends HTMLElement {
+ static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
+ static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
+
+ static _staticConstructor = function() {
+ DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
+ DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
+ // Update the color scheme when the browsers preference changes
+ // without user interaction on the website.
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
+ DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
+ })
+ // Update the color scheme when the tab is made visible again.
+ // It is possible that the appearance was changed in another tab
+ // while this tab was in the background.
+ document.addEventListener("visibilitychange", visibilityState => {
+ if (document.visibilityState === 'visible') {
+ DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
+ }
+ });
+ }()
+
+ constructor() {
+ super();
+ this.onclick=this.toggleDarkMode
+ }
+
+ /**
+ * @returns `true` for dark-mode, `false` for light-mode system preference
+ */
+ static get systemPreference() {
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
+ }
+
+ /**
+ * @returns `true` for dark-mode, `false` for light-mode user preference
+ */
+ static get userPreference() {
+ return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
+ (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
+ }
+
+ static set userPreference(userPreference) {
+ DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
+ if(!userPreference) {
+ if(DoxygenAwesomeDarkModeToggle.systemPreference) {
+ localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
+ } else {
+ localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
+ }
+ } else {
+ if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
+ localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
+ } else {
+ localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
+ }
+ }
+ DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
+ }
+
+ static enableDarkMode(enable) {
+ let head = document.getElementsByTagName('head')[0]
+ if(enable) {
+ document.documentElement.classList.add("dark-mode")
+ document.documentElement.classList.remove("light-mode")
+ } else {
+ document.documentElement.classList.remove("dark-mode")
+ document.documentElement.classList.add("light-mode")
+ }
+ }
+
+ static onSystemPreferenceChanged() {
+ DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
+ DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
+ }
+
+ static onUserPreferenceChanged() {
+ DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
+ }
+
+ toggleDarkMode() {
+ DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
+ }
+}
+
+customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);
--- /dev/null
+
+/**
+
+Doxygen Awesome
+https://github.com/jothepro/doxygen-awesome-css
+
+MIT License
+
+Copyright (c) 2021 jothepro
+
+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.
+
+*/
+
+@media screen and (min-width: 768px) {
+
+ #MSearchBox {
+ width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
+ }
+
+ #MSearchField {
+ width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
+ }
+}
--- /dev/null
+/**
+
+Doxygen Awesome
+https://github.com/jothepro/doxygen-awesome-css
+
+MIT License
+
+Copyright (c) 2021 jothepro
+
+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.
+
+ */
+
+html {
+ /* side nav width. MUST be = `TREEVIEW_WIDTH`.
+ * Make sure it is wide enought to contain the page title (logo + title + version)
+ */
+ --side-nav-fixed-width: 340px;
+ --menu-display: none;
+
+ --top-height: 120px;
+}
+
+
+@media screen and (min-width: 768px) {
+ html {
+ --searchbar-background: var(--page-background-color);
+ }
+
+ #side-nav {
+ min-width: var(--side-nav-fixed-width);
+ max-width: var(--side-nav-fixed-width);
+ top: var(--top-height);
+ overflow: visible;
+ }
+
+ #nav-tree, #side-nav {
+ height: calc(100vh - var(--top-height)) !important;
+ }
+
+ #nav-tree {
+ padding: 0;
+ }
+
+ #top {
+ display: block;
+ border-bottom: none;
+ height: var(--top-height);
+ margin-bottom: calc(0px - var(--top-height));
+ max-width: var(--side-nav-fixed-width);
+ background: var(--side-nav-background);
+ }
+ #main-nav {
+ float: left;
+ padding-right: 0;
+ }
+
+ .ui-resizable-handle {
+ cursor: default;
+ width: 1px !important;
+ box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
+ }
+
+ #nav-path {
+ position: fixed;
+ right: 0;
+ left: var(--side-nav-fixed-width);
+ bottom: 0;
+ width: auto;
+ }
+
+ #doc-content {
+ height: calc(100vh - 31px) !important;
+ padding-bottom: calc(3 * var(--spacing-large));
+ padding-top: calc(var(--top-height) - 80px);
+ box-sizing: border-box;
+ margin-left: var(--side-nav-fixed-width) !important;
+ }
+
+ #MSearchBox {
+ width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
+ }
+
+ #MSearchField {
+ width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
+ }
+
+ #MSearchResultsWindow {
+ left: var(--spacing-medium) !important;
+ right: auto;
+ }
+}
--- /dev/null
+/**
+
+Doxygen Awesome
+https://github.com/jothepro/doxygen-awesome-css
+
+MIT License
+
+Copyright (c) 2021 jothepro
+
+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.
+
+*/
+
+html {
+ /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
+ --primary-color: #1779c4;
+ --primary-dark-color: #00559f;
+ --primary-light-color: #7aabd6;
+ --primary-lighter-color: #cae1f1;
+ --primary-lightest-color: #e9f1f8;
+
+ /* page base colors */
+ --page-background-color: white;
+ --page-foreground-color: #2c3e50;
+ --page-secondary-foreground-color: #67727e;
+
+ /* color for all separators on the website: hr, borders, ... */
+ --separator-color: #dedede;
+
+ /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */
+ --border-radius-large: 8px;
+ --border-radius-small: 4px;
+ --border-radius-medium: 6px;
+
+ /* default spacings. Most compontest reference these values for spacing, to provide uniform spacing on the page. */
+ --spacing-small: 5px;
+ --spacing-medium: 10px;
+ --spacing-large: 16px;
+
+ /* default box shadow used for raising an element above the normal content. Used in dropdowns, Searchresult, ... */
+ --box-shadow: 0 2px 10px 0 rgba(0,0,0,.1);
+
+ --odd-color: rgba(0,0,0,.03);
+
+ /* font-families. will affect all text on the website
+ * font-family: the normal font for text, headlines, menus
+ * font-family-monospace: used for preformatted text in memtitle, code, fragments
+ */
+ --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
+ --font-family-monospace: source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace;
+
+ /* font sizes */
+ --page-font-size: 15.6px;
+ --navigation-font-size: 14.4px;
+ --code-font-size: 14.4px; /* affects code, fragment */
+ --title-font-size: 22px;
+
+ /* content text properties. These only affect the page content, not the navigation or any other ui elements */
+ --content-line-height: 27px;
+ /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/
+ --content-maxwidth: 1000px;
+
+ /* colors for various content boxes: @warning, @note, @deprecated @bug */
+ --warning-color: #fca49b;
+ --warning-color-dark: #b61825;
+ --warning-color-darker: #75070f;
+ --note-color: rgba(255,229,100,.3);
+ --note-color-dark: #c39900;
+ --note-color-darker: #8d7400;
+ --deprecated-color: rgb(214, 216, 224);
+ --deprecated-color-dark: #5b6269;
+ --deprecated-color-darker: #43454a;
+ --bug-color: rgb(246, 208, 178);
+ --bug-color-dark: #a53a00;
+ --bug-color-darker: #5b1d00;
+ --invariant-color: #b7f8d0;
+ --invariant-color-dark: #00ba44;
+ --invariant-color-darker: #008622;
+
+ /* blockquote colors */
+ --blockquote-background: #f5f5f5;
+ --blockquote-foreground: #727272;
+
+ /* table colors */
+ --tablehead-background: #f1f1f1;
+ --tablehead-foreground: var(--page-foreground-color);
+
+ /* menu-display: block | none
+ * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible.
+ * `GENERATE_TREEVIEW` MUST be enabled!
+ */
+ --menu-display: block;
+
+ --menu-focus-foreground: var(--page-background-color);
+ --menu-focus-background: var(--primary-color);
+ --menu-selected-background: rgba(0,0,0,.05);
+
+
+ --header-background: var(--page-background-color);
+ --header-foreground: var(--page-foreground-color);
+
+ /* searchbar colors */
+ --searchbar-background: var(--side-nav-background);
+ --searchbar-foreground: var(--page-foreground-color);
+
+ /* searchbar size
+ * (`searchbar-width` is only applied on screens >= 768px.
+ * on smaller screens the searchbar will always fill the entire screen width) */
+ --searchbar-height: 33px;
+ --searchbar-width: 210px;
+
+ /* code block colors */
+ --code-background: #f5f5f5;
+ --code-foreground: var(--page-foreground-color);
+
+ /* fragment colors */
+ --fragment-background: #282c34;
+ --fragment-foreground: #ffffff;
+ --fragment-keyword: #cc99cd;
+ --fragment-keywordtype: #ab99cd;
+ --fragment-keywordflow: #e08000;
+ --fragment-token: #7ec699;
+ --fragment-comment: #999999;
+ --fragment-link: #98c0e3;
+ --fragment-preprocessor: #65cabe;
+ --fragment-linenumber-color: #cccccc;
+ --fragment-linenumber-background: #35393c;
+ --fragment-linenumber-border: #1f1f1f;
+ --fragment-lineheight: 20px;
+
+ /* sidebar navigation (treeview) colors */
+ --side-nav-background: #fbfbfb;
+ --side-nav-foreground: var(--page-foreground-color);
+ --side-nav-arrow-opacity: 0;
+ --side-nav-arrow-hover-opacity: 0.9;
+
+ /* height of an item in any tree / collapsable table */
+ --tree-item-height: 30px;
+
+ --darkmode-toggle-button-icon: '☀️'
+}
+
+@media screen and (max-width: 767px) {
+ html {
+ --page-font-size: 16px;
+ --navigation-font-size: 16px;
+ --code-font-size: 15px; /* affects code, fragment */
+ --title-font-size: 22px;
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light-mode) {
+ color-scheme: dark;
+
+ --primary-color: #1982d2;
+ --primary-dark-color: #5ca8e2;
+ --primary-light-color: #4779ac;
+ --primary-lighter-color: #191e21;
+ --primary-lightest-color: #191a1c;
+
+ --box-shadow: 0 2px 10px 0 rgba(0,0,0,.35);
+
+ --odd-color: rgba(0,0,0,.1);
+
+ --menu-selected-background: rgba(0,0,0,.4);
+
+ --page-background-color: #1C1D1F;
+ --page-foreground-color: #d2dbde;
+ --page-secondary-foreground-color: #859399;
+ --separator-color: #000000;
+ --side-nav-background: #252628;
+
+ --code-background: #2a2c2f;
+
+ --tablehead-background: #2a2c2f;
+
+ --blockquote-background: #1f2022;
+ --blockquote-foreground: #77848a;
+
+ --warning-color: #b61825;
+ --warning-color-dark: #510a02;
+ --warning-color-darker: #f5b1aa;
+ --note-color: rgb(255, 183, 0);
+ --note-color-dark: #9f7300;
+ --note-color-darker: #645b39;
+ --deprecated-color: rgb(88, 90, 96);
+ --deprecated-color-dark: #262e37;
+ --deprecated-color-darker: #a0a5b0;
+ --bug-color: rgb(248, 113, 0);
+ --bug-color-dark: #812a00;
+ --bug-color-darker: #ffd3be;
+
+ --darkmode-toggle-button-icon: '🌛';
+ }
+}
+
+/* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */
+html.dark-mode {
+ color-scheme: dark;
+
+ --primary-color: #1982d2;
+ --primary-dark-color: #5ca8e2;
+ --primary-light-color: #4779ac;
+ --primary-lighter-color: #191e21;
+ --primary-lightest-color: #191a1c;
+
+ --box-shadow: 0 2px 10px 0 rgba(0,0,0,.35);
+
+ --odd-color: rgba(0,0,0,.1);
+
+ --menu-selected-background: rgba(0,0,0,.4);
+
+ --page-background-color: #1C1D1F;
+ --page-foreground-color: #d2dbde;
+ --page-secondary-foreground-color: #859399;
+ --separator-color: #000000;
+ --side-nav-background: #252628;
+
+ --code-background: #2a2c2f;
+
+ --tablehead-background: #2a2c2f;
+
+ --blockquote-background: #1f2022;
+ --blockquote-foreground: #77848a;
+
+ --warning-color: #b61825;
+ --warning-color-dark: #510a02;
+ --warning-color-darker: #f5b1aa;
+ --note-color: rgb(255, 183, 0);
+ --note-color-dark: #9f7300;
+ --note-color-darker: #645b39;
+ --deprecated-color: rgb(88, 90, 96);
+ --deprecated-color-dark: #262e37;
+ --deprecated-color-darker: #a0a5b0;
+ --bug-color: rgb(248, 113, 0);
+ --bug-color-dark: #812a00;
+ --bug-color-darker: #ffd3be;
+
+ --darkmode-toggle-button-icon: '🌛';
+}
+
+body {
+ color: var(--page-foreground-color);
+ background-color: var(--page-background-color);
+ font-size: var(--page-font-size);
+}
+
+body, table, div, p, dl, #nav-tree .label, .title, .sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname, .SelectItem, #MSearchField, .navpath li.navelem a, .navpath li.navelem a:hover {
+ font-family: var(--font-family);
+}
+
+h1, h2, h3, h4, h5 {
+ margin-top: .9em;
+ font-weight: 600;
+ line-height: initial;
+}
+
+p, div, table, dl {
+ font-size: var(--page-font-size);
+}
+
+a:link, a:visited, a:hover, a:focus, a:active {
+ color: var(--primary-color) !important;
+ font-weight: 500;
+}
+
+/*
+ Title and top navigation
+ */
+
+#top {
+ background: var(--header-background);
+ border-bottom: 1px solid var(--separator-color);
+}
+
+@media screen and (min-width: 768px) {
+ #top {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
+ }
+}
+
+#main-nav {
+ flex-grow: 5;
+ padding: var(--spacing-small) var(--spacing-medium);
+}
+
+#titlearea {
+ width: auto;
+ padding: var(--spacing-medium) var(--spacing-large);
+ background: none;
+ color: var(--header-foreground);
+ border-bottom: none;
+}
+
+@media screen and (max-width: 767px) {
+ #titlearea {
+ padding-bottom: var(--spacing-small);
+ }
+}
+
+#titlearea table tbody tr {
+ height: auto !important;
+}
+
+#projectname {
+ font-size: var(--title-font-size);
+ font-weight: 600;
+}
+
+#projectnumber {
+ font-family: inherit;
+ font-size: 60%;
+}
+
+#projectbrief {
+ font-family: inherit;
+ font-size: 80%;
+}
+
+#projectlogo {
+ vertical-align: middle;
+}
+
+#projectlogo img {
+ max-height: calc(var(--title-font-size) * 2);
+ margin-right: var(--spacing-small);
+}
+
+.sm-dox, .tabs, .tabs2, .tabs3 {
+ background: none;
+ padding: 0;
+}
+
+.tabs, .tabs2, .tabs3 {
+ border-bottom: 1px solid var(--separator-color);
+ margin-bottom: -1px;
+}
+
+@media screen and (max-width: 767px) {
+ .sm-dox a span.sub-arrow {
+ background: var(--code-background);
+ }
+}
+
+@media screen and (min-width: 768px) {
+ .sm-dox li, .tablist li {
+ display: var(--menu-display);
+ }
+
+ .sm-dox a span.sub-arrow {
+ border-color: var(--header-foreground) transparent transparent transparent;
+ }
+
+ .sm-dox a:hover span.sub-arrow {
+ border-color: var(--menu-focus-foreground) transparent transparent transparent;
+ }
+
+ .sm-dox ul a span.sub-arrow {
+ border-color: transparent transparent transparent var(--page-foreground-color);
+ }
+
+ .sm-dox ul a:hover span.sub-arrow {
+ border-color: transparent transparent transparent var(--menu-focus-foreground);
+ }
+}
+
+.sm-dox ul {
+ background: var(--page-background-color);
+ box-shadow: var(--box-shadow);
+ border: 1px solid var(--separator-color);
+ border-radius: var(--border-radius-medium) !important;
+ padding: var(--spacing-small);
+ animation: ease-out 150ms slideInMenu;
+}
+
+@keyframes slideInMenu {
+ from {
+ opacity: 0;
+ transform: translate(0px, -2px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate(0px, 0px);
+ }
+}
+
+.sm-dox ul a {
+ color: var(--page-foreground-color) !important;
+ background: var(--page-background-color);
+ font-size: var(--navigation-font-size);
+}
+
+.sm-dox>li>ul:after {
+ border-bottom-color: var(--page-background-color) !important;
+}
+
+.sm-dox>li>ul:before {
+ border-bottom-color: var(--separator-color) !important;
+}
+
+.sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus {
+ font-size: var(--navigation-font-size) !important;
+ color: var(--menu-focus-foreground) !important;
+ text-shadow: none;
+ background-color: var(--menu-focus-background);
+ border-radius: var(--border-radius-small) !important;
+}
+
+.sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a {
+ text-shadow: none;
+ background: transparent;
+ background-image: none !important;
+ color: var(--header-foreground) !important;
+ font-weight: normal;
+ font-size: var(--navigation-font-size);
+}
+
+.sm-dox a:focus {
+ outline: auto;
+}
+
+.sm-dox a:hover, .sm-dox a:active, .tablist li a:hover {
+ text-shadow: none;
+ font-weight: normal;
+ background: var(--menu-focus-background);
+ color: var(--menu-focus-foreground) !important;
+ border-radius: var(--border-radius-small) !important;
+ font-size: var(--navigation-font-size);
+}
+
+.tablist li.current {
+ border-radius: var(--border-radius-small);
+ background: var(--menu-selected-background);
+}
+
+.tablist li {
+ margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small);
+}
+
+.tablist a {
+ padding: 0 var(--spacing-large);
+}
+
+
+/*
+ Search box
+ */
+
+#MSearchBox {
+ height: var(--searchbar-height);
+ background: var(--searchbar-background);
+ border-radius: var(--searchbar-height);
+ border: 1px solid var(--separator-color);
+ overflow: hidden;
+ width: var(--searchbar-width);
+ position: relative;
+ box-shadow: none;
+ display: block;
+ margin-top: 0;
+}
+
+.left #MSearchSelect {
+ left: 0;
+}
+
+.tabs .left #MSearchSelect {
+ padding-left: 0;
+}
+
+.tabs #MSearchBox {
+ position: absolute;
+ right: var(--spacing-medium);
+}
+
+@media screen and (max-width: 767px) {
+ .tabs #MSearchBox {
+ position: relative;
+ right: 0;
+ margin-left: var(--spacing-medium);
+ margin-top: 0;
+ }
+}
+
+#MSearchSelectWindow, #MSearchResultsWindow {
+ z-index: 9999;
+}
+
+#MSearchBox.MSearchBoxActive {
+ border-color: var(--primary-color);
+ box-shadow: inset 0 0 0 1px var(--primary-color);
+}
+
+#main-menu > li:last-child {
+ margin-right: 0;
+}
+
+@media screen and (max-width: 767px) {
+ #main-menu > li:last-child {
+ height: 50px;
+ }
+}
+
+#MSearchField {
+ font-size: var(--navigation-font-size);
+ height: calc(var(--searchbar-height) - 2px);
+ background: transparent;
+ width: calc(var(--searchbar-width) - 64px);
+}
+
+.MSearchBoxActive #MSearchField {
+ color: var(--searchbar-foreground);
+}
+
+#MSearchSelect {
+ top: calc(calc(var(--searchbar-height) / 2) - 11px);
+}
+
+.left #MSearchSelect {
+ padding-left: 8px;
+}
+
+#MSearchBox span.left, #MSearchBox span.right {
+ background: none;
+}
+
+#MSearchBox span.right {
+ padding-top: calc(calc(var(--searchbar-height) / 2) - 12px);
+ position: absolute;
+ right: var(--spacing-small);
+}
+
+.tabs #MSearchBox span.right {
+ top: calc(calc(var(--searchbar-height) / 2) - 12px);
+}
+
+@keyframes slideInSearchResults {
+ from {
+ opacity: 0;
+ transform: translate(0, 15px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate(0, 20px);
+ }
+}
+
+#MSearchResultsWindow {
+ left: auto !important;
+ right: var(--spacing-medium);
+ border-radius: var(--border-radius-large);
+ border: 1px solid var(--separator-color);
+ transform: translate(0, 20px);
+ box-shadow: var(--box-shadow);
+ animation: ease-out 280ms slideInSearchResults;
+ background: var(--page-background-color);
+}
+
+iframe#MSearchResults {
+ margin: 4px;
+}
+
+iframe {
+ color-scheme: normal;
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light-mode) iframe#MSearchResults {
+ filter: invert() hue-rotate(180deg);
+ }
+}
+
+html.dark-mode iframe#MSearchResults {
+ filter: invert() hue-rotate(180deg);
+}
+
+#MSearchSelectWindow {
+ border: 1px solid var(--separator-color);
+ border-radius: var(--border-radius-medium);
+ box-shadow: var(--box-shadow);
+ background: var(--page-background-color);
+}
+
+#MSearchSelectWindow a.SelectItem {
+ font-size: var(--navigation-font-size);
+ line-height: var(--content-line-height);
+ margin: 0 var(--spacing-small);
+ border-radius: var(--border-radius-small);
+ color: var(--page-foreground-color) !important;
+ font-weight: normal;
+}
+
+#MSearchSelectWindow a.SelectItem:hover {
+ background: var(--menu-focus-background);
+ color: var(--menu-focus-foreground) !important;
+}
+
+@media screen and (max-width: 767px) {
+ #MSearchBox {
+ margin-top: var(--spacing-medium);
+ margin-bottom: var(--spacing-medium);
+ width: calc(100vw - 30px);
+ }
+
+ #main-menu > li:last-child {
+ float: none !important;
+ }
+
+ #MSearchField {
+ width: calc(100vw - 110px);
+ }
+
+ @keyframes slideInSearchResultsMobile {
+ from {
+ opacity: 0;
+ transform: translate(0, 15px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate(0, 20px);
+ }
+ }
+
+ #MSearchResultsWindow {
+ left: var(--spacing-medium) !important;
+ right: var(--spacing-medium);
+ overflow: auto;
+ transform: translate(0, 20px);
+ animation: ease-out 280ms slideInSearchResultsMobile;
+ }
+
+ /*
+ * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2
+ */
+ label.main-menu-btn ~ #searchBoxPos1 {
+ top: 3px !important;
+ right: 6px !important;
+ left: 45px;
+ display: flex;
+ }
+
+ label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox {
+ margin-top: 0;
+ margin-bottom: 0;
+ flex-grow: 2;
+ float: left;
+ }
+}
+
+/*
+ Tree view
+ */
+
+#side-nav {
+ padding: 0 !important;
+ background: var(--side-nav-background);
+}
+
+@media screen and (max-width: 767px) {
+ #side-nav {
+ display: none;
+ }
+
+ #doc-content {
+ margin-left: 0 !important;
+ height: auto !important;
+ padding-bottom: calc(2 * var(--spacing-large));
+ }
+}
+
+#nav-tree {
+ background: transparent;
+}
+
+#nav-tree .label {
+ font-size: var(--navigation-font-size);
+}
+
+#nav-tree .item {
+ height: var(--tree-item-height);
+ line-height: var(--tree-item-height);
+}
+
+#nav-sync {
+ top: 12px !important;
+ right: 12px;
+}
+
+#nav-tree .selected {
+ text-shadow: none;
+ background-image: none;
+ background-color: transparent;
+ box-shadow: inset 4px 0 0 0 var(--primary-color);
+}
+
+#nav-tree a {
+ color: var(--side-nav-foreground) !important;
+ font-weight: normal;
+}
+
+#nav-tree a:focus {
+ outline-style: auto;
+}
+
+#nav-tree .arrow {
+ opacity: var(--side-nav-arrow-opacity);
+}
+
+.arrow {
+ color: inherit;
+ cursor: pointer;
+ font-size: 45%;
+ vertical-align: middle;
+ margin-right: 2px;
+ font-family: serif;
+ height: auto;
+ text-align: right;
+}
+
+#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow {
+ opacity: var(--side-nav-arrow-hover-opacity);
+}
+
+#nav-tree .selected a {
+ color: var(--primary-color) !important;
+ font-weight: bolder;
+ font-weight: 600;
+}
+
+.ui-resizable-e {
+ background: var(--separator-color);
+ width: 1px;
+}
+
+/*
+ Contents
+ */
+
+div.header {
+ border-bottom: 1px solid var(--separator-color);
+ background-color: var(--page-background-color);
+ background-image: none;
+}
+
+div.contents, div.header .title, div.header .summary {
+ max-width: var(--content-maxwidth);
+}
+
+div.contents, div.header .title {
+ line-height: initial;
+ margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto;
+}
+
+div.header .summary {
+ margin: var(--spacing-medium) auto 0 auto;
+}
+
+div.headertitle {
+ padding: 0;
+}
+
+div.header .title {
+ font-weight: 600;
+ font-size: 210%;
+ padding: var(--spacing-medium) var(--spacing-large);
+ word-break: break-word;
+}
+
+div.header .summary {
+ width: auto;
+ display: block;
+ float: none;
+ padding: 0 var(--spacing-large);
+}
+
+td.memSeparator {
+ border-color: var(--separator-color);
+}
+
+.mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+ background: var(--code-background);
+}
+
+.mdescRight {
+ color: var(--page-secondary-foreground-color);
+}
+
+span.mlabel {
+ background: var(--primary-color);
+ border: none;
+ padding: 4px 9px;
+ border-radius: 12px;
+ margin-right: var(--spacing-medium);
+}
+
+span.mlabel:last-of-type {
+ margin-right: 2px;
+}
+
+div.contents {
+ padding: 0 var(--spacing-large);
+}
+
+div.contents p, div.contents li {
+ line-height: var(--content-line-height);
+}
+
+div.contents div.dyncontent {
+ margin: var(--spacing-medium) 0;
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light-mode) div.contents div.dyncontent img,
+ html:not(.light-mode) div.contents center img,
+ html:not(.light-mode) div.contents table img,
+ html:not(.light-mode) div.contents div.dyncontent iframe,
+ html:not(.light-mode) div.contents center iframe,
+ html:not(.light-mode) div.contents table iframe {
+ filter: hue-rotate(180deg) invert();
+ }
+}
+
+html.dark-mode div.contents div.dyncontent img,
+html.dark-mode div.contents center img,
+html.dark-mode div.contents table img,
+html.dark-mode div.contents div.dyncontent iframe,
+html.dark-mode div.contents center iframe,
+html.dark-mode div.contents table iframe {
+ filter: hue-rotate(180deg) invert();
+}
+
+h2.groupheader {
+ border-bottom: 1px solid var(--separator-color);
+ color: var(--page-foreground-color);
+}
+
+blockquote {
+ padding: var(--spacing-small) var(--spacing-medium);
+ background: var(--blockquote-background);
+ color: var(--blockquote-foreground);
+ border-left: 2px solid var(--blockquote-foreground);
+ margin: 0;
+}
+
+blockquote p {
+ margin: var(--spacing-small) 0 var(--spacing-medium) 0;
+}
+.paramname {
+ font-weight: 600;
+ color: var(--primary-dark-color);
+}
+
+.glow {
+ text-shadow: 0 0 15px var(--primary-light-color) !important;
+}
+
+.alphachar a {
+ color: var(--page-foreground-color);
+}
+
+/*
+ Table of Contents
+ */
+
+div.toc {
+ background-color: var(--side-nav-background);
+ border: 1px solid var(--separator-color);
+ border-radius: var(--border-radius-medium);
+ box-shadow: var(--box-shadow);
+ padding: 0 var(--spacing-large);
+ margin: 0 0 var(--spacing-medium) var(--spacing-medium);
+}
+
+div.toc h3 {
+ color: var(--side-nav-foreground);
+ font-size: var(--navigation-font-size);
+ margin: var(--spacing-large) 0;
+}
+
+div.toc li {
+ font-size: var(--navigation-font-size);
+ padding: 0;
+ background: none;
+}
+
+div.toc li:before {
+ content: '↓';
+ font-weight: 800;
+ font-family: var(--font-family);
+ margin-right: var(--spacing-small);
+ color: var(--side-nav-foreground);
+ opacity: .4;
+}
+
+div.toc ul li.level1 {
+ margin: 0;
+}
+
+div.toc ul li.level2, div.toc ul li.level3 {
+ margin-top: 0;
+}
+
+
+@media screen and (max-width: 767px) {
+ div.toc {
+ float: none;
+ width: auto;
+ margin: 0 0 var(--spacing-medium) 0;
+ }
+}
+
+/*
+ Code & Fragments
+ */
+
+code, div.fragment, pre.fragment {
+ border-radius: var(--border-radius-small);
+ border: none;
+ overflow: hidden;
+}
+
+code {
+ display: inline;
+ background: var(--code-background);
+ color: var(--code-foreground);
+ padding: 2px 6px;
+ word-break: break-word;
+}
+
+div.fragment, pre.fragment {
+ margin: var(--spacing-medium) 0;
+ padding: 14px 16px;
+ background: var(--fragment-background);
+ color: var(--fragment-foreground);
+ overflow-x: auto;
+}
+
+@media screen and (max-width: 767px) {
+ div.fragment, pre.fragment {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .contents > div.fragment, .textblock > div.fragment, .textblock > pre.fragment {
+ margin: var(--spacing-medium) calc(0px - var(--spacing-large));
+ border-radius: 0;
+ }
+
+ .textblock li > .fragment {
+ margin: var(--spacing-medium) calc(0px - var(--spacing-large));
+ }
+
+ .memdoc li > .fragment {
+ margin: var(--spacing-medium) calc(0px - var(--spacing-medium));
+ }
+
+ .memdoc > div.fragment, .memdoc > pre.fragment, dl dd > div.fragment, dl dd pre.fragment {
+ margin: var(--spacing-medium) calc(0px - var(--spacing-medium));
+ border-radius: 0;
+ }
+}
+
+code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span {
+ font-family: var(--font-family-monospace);
+ font-size: var(--code-font-size) !important;
+}
+
+div.line:after {
+ margin-right: var(--spacing-medium);
+}
+
+div.fragment .line, pre.fragment {
+ white-space: pre;
+ word-wrap: initial;
+ line-height: var(--fragment-lineheight);
+}
+
+div.fragment span.keyword {
+ color: var(--fragment-keyword);
+}
+
+div.fragment span.keywordtype {
+ color: var(--fragment-keywordtype);
+}
+
+div.fragment span.keywordflow {
+ color: var(--fragment-keywordflow);
+}
+
+div.fragment span.stringliteral {
+ color: var(--fragment-token)
+}
+
+div.fragment span.comment {
+ color: var(--fragment-comment);
+}
+
+div.fragment a.code {
+ color: var(--fragment-link) !important;
+}
+
+div.fragment span.preprocessor {
+ color: var(--fragment-preprocessor);
+}
+
+div.fragment span.lineno {
+ display: inline-block;
+ width: 27px;
+ border-right: none;
+ background: var(--fragment-linenumber-background);
+ color: var(--fragment-linenumber-color);
+}
+
+div.fragment span.lineno a {
+ background: none;
+ color: var(--fragment-link) !important;
+}
+
+div.fragment .line:first-child .lineno {
+ box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border);
+}
+
+/*
+ dl warning, attention, note, deprecated, bug, ...
+ */
+
+dl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre {
+ padding: var(--spacing-medium);
+ margin: var(--spacing-medium) 0;
+ color: var(--page-background-color);
+ overflow: hidden;
+ margin-left: 0;
+ border-radius: var(--border-radius-small);
+}
+
+dl.section dd {
+ margin-bottom: 2px;
+}
+
+dl.warning, dl.attention {
+ background: var(--warning-color);
+ border-left: 8px solid var(--warning-color-dark);
+ color: var(--warning-color-darker);
+}
+
+dl.warning dt, dl.attention dt {
+ color: var(--warning-color-dark);
+}
+
+dl.note {
+ background: var(--note-color);
+ border-left: 8px solid var(--note-color-dark);
+ color: var(--note-color-darker);
+}
+
+dl.note dt {
+ color: var(--note-color-dark);
+}
+
+dl.bug {
+ background: var(--bug-color);
+ border-left: 8px solid var(--bug-color-dark);
+ color: var(--bug-color-darker);
+}
+
+dl.bug dt a {
+ color: var(--bug-color-dark) !important;
+}
+
+dl.deprecated {
+ background: var(--deprecated-color);
+ border-left: 8px solid var(--deprecated-color-dark);
+ color: var(--deprecated-color-darker);
+}
+
+dl.deprecated dt a {
+ color: var(--deprecated-color-dark) !important;
+}
+
+dl.section dd, dl.bug dd, dl.deprecated dd {
+ margin-inline-start: 0px;
+}
+
+dl.invariant, dl.pre {
+ background: var(--invariant-color);
+ border-left: 8px solid var(--invariant-color-dark);
+ color: var(--invariant-color-darker);
+}
+
+/*
+ memitem
+ */
+
+div.memdoc, div.memproto, h2.memtitle {
+ box-shadow: none;
+ background-image: none;
+ border: none;
+}
+
+div.memdoc {
+ padding: 0 var(--spacing-medium);
+ background: var(--page-background-color);
+}
+
+h2.memtitle, div.memitem {
+ border: 1px solid var(--separator-color);
+}
+
+div.memproto, h2.memtitle {
+ background: var(--code-background);
+ text-shadow: none;
+}
+
+h2.memtitle {
+ font-weight: 500;
+ font-family: monospace, fixed;
+ border-bottom: none;
+ border-top-left-radius: var(--border-radius-medium);
+ border-top-right-radius: var(--border-radius-medium);
+ word-break: break-all;
+}
+
+a:target + h2.memtitle, a:target + h2.memtitle + div.memitem {
+ border-color: var(--primary-light-color);
+}
+
+a:target + h2.memtitle {
+ box-shadow: -3px -3px 3px 0 var(--primary-lightest-color), 3px -3px 3px 0 var(--primary-lightest-color);
+}
+
+a:target + h2.memtitle + div.memitem {
+ box-shadow: 0 0 10px 0 var(--primary-lighter-color);
+}
+
+div.memitem {
+ border-top-right-radius: var(--border-radius-medium);
+ border-bottom-right-radius: var(--border-radius-medium);
+ border-bottom-left-radius: var(--border-radius-medium);
+ overflow: hidden;
+ display: block !important;
+}
+
+div.memdoc {
+ border-radius: 0;
+}
+
+div.memproto {
+ border-radius: 0 var(--border-radius-small) 0 0;
+ overflow: auto;
+ border-bottom: 1px solid var(--separator-color);
+ padding: var(--spacing-medium);
+ margin-bottom: -1px;
+}
+
+div.memtitle {
+ border-top-right-radius: var(--border-radius-medium);
+ border-top-left-radius: var(--border-radius-medium);
+}
+
+div.memproto table.memname {
+ font-family: monospace, fixed;
+ color: var(--page-foreground-color);
+}
+
+table.mlabels, table.mlabels > tbody {
+ display: block;
+}
+
+td.mlabels-left {
+ width: auto;
+}
+
+table.mlabels > tbody > tr:first-child {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.memname, .memitem span.mlabels {
+ margin: 0
+}
+
+/*
+ reflist
+ */
+
+dl.reflist {
+ box-shadow: var(--box-shadow);
+ border-radius: var(--border-radius-medium);
+ border: 1px solid var(--separator-color);
+ overflow: hidden;
+ padding: 0;
+}
+
+
+dl.reflist dt, dl.reflist dd {
+ box-shadow: none;
+ text-shadow: none;
+ background-image: none;
+ border: none;
+ padding: 12px;
+}
+
+
+dl.reflist dt {
+ font-weight: 500;
+ border-radius: 0;
+ background: var(--code-background);
+ border-bottom: 1px solid var(--separator-color);
+ color: var(--page-foreground-color)
+}
+
+
+dl.reflist dd {
+ background: none;
+}
+
+/*
+ Table
+ */
+
+table.markdownTable, table.fieldtable {
+ width: 100%;
+ border: 1px solid var(--separator-color);
+ margin: var(--spacing-medium) 0;
+}
+
+table.fieldtable {
+ box-shadow: none;
+ border-radius: var(--border-radius-small);
+}
+
+th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone {
+ background: var(--tablehead-background);
+ color: var(--tablehead-foreground);
+ font-weight: 600;
+ font-size: var(--page-font-size);
+}
+
+table.markdownTable td, table.markdownTable th, table.fieldtable dt {
+ border: 1px solid var(--separator-color);
+ padding: var(--spacing-small) var(--spacing-medium);
+}
+
+table.fieldtable th {
+ font-size: var(--page-font-size);
+ font-weight: 600;
+ background-image: none;
+ background-color: var(--tablehead-background);
+ color: var(--tablehead-foreground);
+ border-bottom: 1px solid var(--separator-color);
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+ border-bottom: 1px solid var(--separator-color);
+ border-right: 1px solid var(--separator-color);
+}
+
+.fieldtable td.fielddoc {
+ border-bottom: 1px solid var(--separator-color);
+}
+
+.memberdecls td.glow, .fieldtable tr.glow {
+ background-color: var(--primary-light-color);
+ box-shadow: 0 0 15px var(--primary-lighter-color);
+}
+
+table.memberdecls {
+ display: block;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+
+/*
+ Horizontal Rule
+ */
+
+hr {
+ margin-top: var(--spacing-large);
+ margin-bottom: var(--spacing-large);
+ border-top:1px solid var(--separator-color);
+}
+
+.contents hr {
+ box-shadow: var(--content-maxwidth) 0 0 0 var(--separator-color), calc(0px - var(--content-maxwidth)) 0 0 0 var(--separator-color);
+}
+
+.contents img {
+ max-width: 100%;
+}
+
+/*
+ Directories
+ */
+div.directory {
+ border-top: 1px solid var(--separator-color);
+ border-bottom: 1px solid var(--separator-color);
+ width: auto;
+}
+
+table.directory {
+ font-family: var(--font-family);
+ font-size: var(--page-font-size);
+ font-weight: normal;
+}
+
+.directory td.entry {
+ padding: var(--spacing-small);
+ display: flex;
+ align-items: center;
+}
+
+.directory tr.even {
+ background-color: var(--odd-color);
+}
+
+.icona {
+ width: auto;
+ height: auto;
+ margin: 0 var(--spacing-small);
+}
+
+.icon {
+ background: var(--primary-color);
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+}
+
+.iconfopen, .icondoc, .iconfclosed {
+ background-position: center;
+ margin-bottom: 0;
+}
+
+.icondoc {
+ filter: saturate(0.2);
+}
+
+@media screen and (max-width: 767px) {
+ div.directory {
+ margin-left: calc(0px - var(--spacing-medium));
+ margin-right: calc(0px - var(--spacing-medium));
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed {
+ filter: hue-rotate(180deg) invert();
+ }
+}
+
+html.dark-mode .iconfopen, html.dark-mode .iconfclosed {
+ filter: hue-rotate(180deg) invert();
+}
+
+/*
+ Class list
+ */
+
+.classindex dl.odd {
+ background: var(--odd-color);
+ border-radius: var(--border-radius-small);
+}
+
+@media screen and (max-width: 767px) {
+ .classindex {
+ margin: 0 calc(0px - var(--spacing-small));
+ }
+}
+
+/*
+ Footer and nav-path
+ */
+
+#nav-path {
+ margin-bottom: -1px;
+ width: 100%;
+}
+
+#nav-path ul {
+ background-image: none;
+ background: var(--page-background-color);
+ border: none;
+ border-top: 1px solid var(--separator-color);
+ border-bottom: 1px solid var(--separator-color);
+ font-size: var(--navigation-font-size);
+}
+
+img.footer {
+ width: 60px;
+}
+
+.navpath li.footer {
+ color: var(--page-secondary-foreground-color);
+}
+
+address.footer {
+ margin-bottom: var(--spacing-large);
+}
+
+#nav-path li.navelem {
+ background-image: none;
+ display: flex;
+ align-items: center;
+}
+
+.navpath li.navelem a {
+ text-shadow: none;
+ display: inline-block;
+ color: var(--primary-color) !important;
+}
+
+.navpath li.navelem b {
+ color: var(--primary-dark-color);
+ font-weight: 500;
+}
+
+li.navelem {
+ padding: 0;
+ margin-left: -8px;
+}
+
+li.navelem:first-child {
+ margin-left: var(--spacing-large);
+}
+
+li.navelem:first-child:before {
+ display: none;
+}
+
+#nav-path li.navelem:after {
+ content: '';
+ border: 5px solid var(--page-background-color);
+ border-bottom-color: transparent;
+ border-right-color: transparent;
+ border-top-color: transparent;
+ transform: scaleY(4.2);
+ z-index: 10;
+ margin-left: 6px;
+}
+
+#nav-path li.navelem:before {
+ content: '';
+ border: 5px solid var(--separator-color);
+ border-bottom-color: transparent;
+ border-right-color: transparent;
+ border-top-color: transparent;
+ transform: scaleY(3.2);
+ margin-right: var(--spacing-small);
+}
+
+.navpath li.navelem a:hover {
+ color: var(--primary-color);
+}
+
+/*
+ Optional Dark mode toggle button
+*/
+
+doxygen-awesome-dark-mode-toggle {
+ display: inline-block;
+ margin: 0 0 0 var(--spacing-small);
+ padding: 0;
+ width: var(--searchbar-height);
+ height: var(--searchbar-height);
+ background: none;
+ border: none;
+ font-size: 23px;
+ border-radius: var(--border-radius-medium);
+ vertical-align: middle;
+ text-align: center;
+ line-height: var(--searchbar-height);
+}
+
+doxygen-awesome-dark-mode-toggle:hover {
+ background: var(--separator-color);
+}
+
+doxygen-awesome-dark-mode-toggle:after {
+ content: var(--darkmode-toggle-button-icon)
+}
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1001px" height="401px" viewBox="-0.5 -0.5 1001 401" content="<mxfile host="drawio-plugin" modified="2021-03-17T15:33:31.636Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36" etag="RyKBBCdKCVcXAhIW2RaO" version="13.7.9" type="embed"><diagram id="6E4AiNPWWr3a8GvC3Ypl" name="Page-1">1VlNc5swEP01nkkPZRACAcfYsdNL24Mz07NiFGAqkEeWY6e/vgIkAxbUJJh4Chkinj7Qvt2VXpQZXGTHR463yXcWETpz7Og4gw8zxwlCWz4L4K0CPM+tgJinUQWBGlinf4gCVb94n0Zk12ooGKMi3bbBDctzshEtDHPODu1mL4y2v7rFMTGA9QZTE/2VRiJRZnl2jX8jaZzoLwNb1WRYN1bALsEROzQguJzBBWdMVKXsuCC04E7zUvVb9dSeJsZJLgZ18Koer5jutXGWfJ/jHZG/nhKSETVT8abNF+QoB58nIqMSALKIaRrnsryRnyVcAq+Ei1QSdq8qsjSKiu7zQ5IKst7iTTHWQUaHxDjb5xGJ1FgvLBfK5XJ26l19vKxPKV0wyng5Gbi6L27VroHb5SVxkxLFUjFHcmxAiqJHwjIi+Jtsomt931JEqYiFgXLgofY/gKpN0vC9djRWIRefBq+9IgvKMT1OQoaTDJ/EksTtcFNPuYGf9Qh2NwUNA12dt00DoXcFCwcYWAeJ3RNGjXjsIAL1MHEji13D4oWM32K6owzfCc5+k0YaLMt7LCW6NgwsDwJ0uvxWUriuFfjAD139NFJEjmAhD4HT1ZEwPrD80HehF5TPcDzXjsH1UyooecZconc/8GsaY5GyfFbkgCTKXhPMN8mXca44W6celsuH1Wrq2OxxwRj2oMHeWu6+BnnXZWvlF/dYtgbHJXCQ5csNHOgf+CmBaW6+K7nzk5LZOSc42vB9tt2NpPYaFMqktxAyEv/iUgkt2LrQeNJ8Mx7LdP0HSWBiklQtLCLEcQH0Qwhs6LYlAziPQscMQxtYurMbOL6OsSalZ45AKBhPadC9QBqMmsKvvd/kLCdnuawgLRApeREfl4d2Wx46PfLu/fsasoIQeR7Q3nPe6zkZ6E2/BahDCk7hOWBmg+G1yaSh6uUBUwtPJ5zCywaPXR39HmZuJBU1vTfVisM5aWlFnQ6OfaYV+5LBEIt1UgK3g2D7GgQDg+Ba4swcRIsl61m+oLgo3anFUYnFW0igD0VoJXLq7cnt2F2uE7Cm4P4kXfP+KO3hZLDsgQhaOsLLPaSD0wlEEOhQ5dOooA9QaqEWpUGLUhe0E79DjN9IBQHzb/L/WgZd23MAXFyybyaDTAXrFMeYahn/+jOnhZ1DjzM73fmpZ5w9Z5nnW0R5XeeMUzrCcgYcctZHoSOPOeVrfc5d1jX+WQCXfwE=</diagram></mxfile>"><defs/><g><rect x="177.5" y="380" width="135" height="20" rx="3" ry="3" fill="#fafafa" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 390px; margin-left: 179px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; ">1. Base Theme</div></div></div></foreignObject><text x="245" y="395" fill="#000000" font-family="Helvetica" font-size="15px" text-anchor="middle" font-weight="bold">1. Base Theme</text></switch></g><rect x="0" y="0" width="490" height="350" fill="#ffffff" stroke="#000000" pointer-events="all"/><rect x="198.53" y="44.87" width="219.66" height="271.8" fill="#ffffff" stroke="#e3e3e3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 181px; margin-left: 200px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Content</div></div></div></foreignObject><text x="308" y="184" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Content</text></switch></g><rect x="0" y="0" width="490" height="44.87" fill="#deedff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 22px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Titlebar (Navigation + Search)</div></div></div></foreignObject><text x="245" y="26" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Titlebar (Navigation + Search)</text></switch></g><rect x="0" y="44.87" width="126.73" height="271.8" fill="#f7f7f7" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 181px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Sidebar (Navigation)</div></div></div></foreignObject><text x="63" y="184" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Sidebar (Navigation)</text></switch></g><rect x="0" y="316.67" width="490" height="33.33" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 333px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Footer (Breadcrumbs)</div></div></div></foreignObject><text x="245" y="337" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Footer (Breadcrumbs)</text></switch></g><rect x="371.72" y="14.87" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 23px; margin-left: 373px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Search</div></div></div></foreignObject><text x="422" y="27" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Search</text></switch></g><rect x="16.9" y="14.87" width="33.79" height="16.67" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 23px; margin-left: 19px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Title</div></div></div></foreignObject><text x="19" y="29" fill="#000000" font-family="Helvetica" font-size="20px">Tit...</text></switch></g><rect x="510" y="0" width="490" height="350" fill="#ffffff" stroke="#000000" pointer-events="all"/><rect x="708.53" y="16.67" width="219.66" height="300" fill="#ffffff" stroke="#e3e3e3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 167px; margin-left: 710px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Content</div></div></div></foreignObject><text x="818" y="170" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Content</text></switch></g><rect x="510" y="0" width="126.72" height="350" fill="#f7f7f7" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 175px; margin-left: 511px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Sidebar <br />(Title + Navigation)</div></div></div></foreignObject><text x="573" y="179" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Sidebar...</text></switch></g><rect x="636.72" y="316.67" width="363.28" height="33.33" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 361px; height: 1px; padding-top: 333px; margin-left: 638px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Footer (Breadcrumbs)</div></div></div></foreignObject><text x="818" y="337" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Footer (Breadcrumbs)</text></switch></g><rect x="522.67" y="41.67" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 50px; margin-left: 524px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Search</div></div></div></foreignObject><text x="573" y="54" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Search</text></switch></g><rect x="522.67" y="11.67" width="33.79" height="16.67" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 20px; margin-left: 525px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Title</div></div></div></foreignObject><text x="525" y="26" fill="#000000" font-family="Helvetica" font-size="20px">Tit...</text></switch></g><rect x="666.25" y="380" width="177.5" height="20" rx="3" ry="3" fill="#ffffff" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 176px; height: 1px; padding-top: 390px; margin-left: 667px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; ">2. Sidebar-Only Theme</div></div></div></foreignObject><text x="755" y="395" fill="#000000" font-family="Helvetica" font-size="15px" text-anchor="middle" font-weight="bold">2. Sidebar-Only Theme</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>\r
--- /dev/null
+#pragma once
+#include <string>
+
+namespace MyLibrary {
+
+enum Color { red, green, blue };
+
+/**
+ * Example class to demonstrate the features of the custom CSS.
+ *
+ * @author jothepro
+ *
+ */
+class Example {
+public:
+ /**
+ * @brief brief summary
+ *
+ * doxygen test documentation
+ *
+ * @param test this is the only parameter of this test function. It does nothing!
+ *
+ * # Supported elements
+ *
+ * These elements have been tested with the custom CSS.
+ *
+ *
+ * ## Lists
+ *
+ * - element 1
+ * - element 2
+ *
+ * 1. element 1
+ * ```
+ * code in lists
+ * ```
+ * 2. element 2
+ *
+ * ## Quotes
+ *
+ * > This is an **awesome** design...
+ * >
+ * > ...do you agree?
+ * *- jothepro*
+ *
+ * ## Code block
+ *
+ * ```
+ * code within md fences (```)
+ * ```
+ *
+ * @code{.cpp}
+ * // code within @code block
+ * if(true) {
+ * auto example = std::make_shared<Example>(5);
+ * example->test("test");
+ * }
+ *
+ * @endcode
+ *
+ * // code within indented code block
+ * auto test = std::shared_ptr<Example(5);
+ *
+ *
+ * Inline `code` elements in a text. Lorem Ipsum set dolor. This also works within multiline text and does not break the `layout`.
+ *
+ *
+ * ## special hints
+ *
+ * @warning this is a warning only for demonstration purposes
+ *
+ * @note this is a note to show that notes work. They can also include `code`:
+ * @code{.c}
+ * void this_looks_awesome();
+ * @endcode
+ *
+ * @bug this css has no bugs, it is perfect... NOT!
+ *
+ * @deprecated None of this will be deprecated, because it's beautiful!
+ *
+ * @invariant This is an invariant
+ *
+ * @pre This is a precondition
+ *
+ *
+ *
+ */
+ std::string test(const std::string& test);
+
+ virtual int virtualfunc() = 0;
+
+ static bool staticfunc();
+
+
+};
+
+}
+
--- /dev/null
+#pragma once
+#include <string>
+#include "example.hpp"
+#include <iostream>
+
+namespace MyLibrary {
+
+ class SubclassExample : public Example {
+ public:
+
+ /**
+ * @bug second bug
+ * @return
+ */
+ int virtualfunc() override;
+
+ std::shared_ptr<std::string> super_long_function_with_lots_of_parameters(std::shared_ptr<std::string>& text, std::shared_ptr<std::string>& text2) {
+ if(true) {
+ std::cout << "this even has some code." << std::endl;
+ }
+ }
+
+ };
+
+}
+
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="61px" height="74px" viewBox="-0.5 -0.5 61 74" content="<mxfile host="drawio-plugin" modified="2021-03-16T23:58:23.462Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36" version="13.7.9" etag="JoeaGLJ54FcERO7YrWLQ" type="embed"><diagram id="JMB9aH8b_oZ7EWDuqJgx" name="Page-1">7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=</diagram></mxfile>"><defs/><g><path d="M 13 57 L 13.01 57.01 L 15.87 50.14 L 18.37 43.14 L 20.91 36.15 L 23.67 29.25 L 26.4 22.33 Q 30 13 33.71 22.28 L 33.55 22.22 L 35.48 26.91 L 37.49 31.64 L 39.48 36.36 L 41.2 40.97 L 43.05 45.63" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 47.51 56.77 L 47.65 56.93 L 45.43 54.91 L 43.41 53.11 L 41.43 51.35 L 39.63 49.8 L 37.48 47.86 L 37.39 47.64 L 39.79 47.17 L 41.9 45.98 L 44.24 45.37 L 46.48 44.52 L 48.62 43.4 L 48.54 43.39 L 48.58 46.09 L 48.04 48.74 L 48.04 51.43 L 47.8 54.1 L 47.51 56.77 Z Z" fill-opacity="0.1" fill="#010508" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 43 L 9.94 42.88 L 12.16 41.98 L 14.31 40.96 L 16.51 40.01 L 18.62 38.89 L 20.88 38.1 Q 30 34 40 34 L 40 33.75 L 42 33.83 L 44 33.8 L 46 33.79 L 48 34.05 L 50 34" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="7" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 54 L 9.97 53.99 L 12.69 47.07 L 15.43 40.16 L 18.07 33.21 L 20.65 26.24 L 23.4 19.33 Q 27 10 30.71 19.28 L 30.66 19.26 L 32.46 23.91 L 34.55 28.66 L 36.26 33.27 L 38.35 38.03 L 40.05 42.63" fill="none" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 44.51 53.77 L 44.56 53.83 L 42.48 51.97 L 40.5 50.21 L 38.48 48.41 L 36.41 46.56 L 34.48 44.86 L 34.55 45.02 L 36.72 44 L 39 43.24 L 41.21 42.28 L 43.48 41.51 L 45.62 40.4 L 45.78 40.42 L 45.51 43.09 L 45.01 45.74 L 44.87 48.42 L 44.94 51.12 L 44.51 53.77 Z Z" fill="#1982d2" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 7 40 L 7.02 40.05 L 9.28 39.25 L 11.33 38 L 13.48 36.96 L 15.73 36.14 L 17.88 35.1 Q 27 31 37 31 L 37 30.79 L 39 31.11 L 41 30.85 L 43 30.78 L 45 30.89 L 47 31" fill="none" stroke="#1982d2" stroke-width="8" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/></g></svg>
\ No newline at end of file
--- /dev/null
+# Internal documentation of LWNode.js
+
+These flags and variables are for LWNode.js core development usage only. Do not use them in your own applications.
+
+## CLI
+
+### Flags
+
+#### `--internal-log`
+
+If the `--internal-log` flag is specified or the `LWNODE_INTERNAL_LOG` environment variable is set to 1, LWNode will print internal logs in release build.
+
+#### `--trace-call[=[-]trace id[,<trace id>]]`
+
+If the `--trace-call` flag is specified or the `LWNODE_TRACE_CALL` environment variable is set to trace ids, LWNode will print trace call logs in debug build. Setting trace id with `-` will negate printing trace logs for the specific id.
+
+```shell
+--trace-call=COMMON,ISOLATE
+
+# or
+
+export LWNODE_TRACE_CALL=COMMON,ISOLATE
+```
+
+### Environment variables
+
+#### `LWNODE_INTERNAL_LOG`
+
+Refer to the description above.
+
+#### `LWNODE_TRACE_CALL`
+
+Refer to the description above.
+
+#### `LWNODE_RUNNING_ON_TESTS`
+
+If the `LWNODE_RUNNING_ON_TESTS` environment variable is set to 1, LWNode will ignore comparing error messages in detail while using `assert.throw` and similars. This is used as default when using `tools/test.py`. Please refer to https://github.sec.samsung.net/lws/node-escargot/issues/1002 for more information.
--- /dev/null
+# Lightweight Node.js Specification
+
+* Forked version: 14.14
+
+* Node.js APIs
+ - Supported Features
+ - [Assertion](https://nodejs.org/dist/latest-v14.x/docs/api/assert.html)
+ - [Buffer](https://nodejs.org/dist/latest-v14.x/docs/api/buffer.html)
+ - [C/C++ addons with Node-API](https://nodejs.org/dist/latest-v14.x/docs/api/n-api.html)
+ - [Console](https://nodejs.org/dist/latest-v14.x/docs/api/console.html)
+ - [Crypto](https://nodejs.org/dist/latest-v14.x/docs/api/crypto.html)
+ - [DNS](https://nodejs.org/dist/latest-v14.x/docs/api/dns.html)
+ - [Errors](https://nodejs.org/dist/latest-v14.x/docs/api/errors.html)
+ - [Events](https://nodejs.org/dist/latest-v14.x/docs/api/events.html)
+ - [File system](https://nodejs.org/dist/latest-v14.x/docs/api/fs.html)
+ - [Globals](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html)
+ - [HTTP](https://nodejs.org/dist/latest-v14.x/docs/api/http.html)
+ - [HTTPS](https://nodejs.org/dist/latest-v14.x/docs/api/https.html)
+ - [Modules: CommonJS modules](https://nodejs.org/dist/latest-v14.x/docs/api/modules.html)
+ - [Net](https://nodejs.org/dist/latest-v14.x/docs/api/net.html)
+ - [OS](https://nodejs.org/dist/latest-v14.x/docs/api/os.html)
+ - [Path](https://nodejs.org/dist/latest-v14.x/docs/api/path.html)
+ - [Process](https://nodejs.org/dist/latest-v14.x/docs/api/process.html)
+ - [Query strings](https://nodejs.org/dist/latest-v14.x/docs/api/querystring.html)
+ - [Stream](https://nodejs.org/dist/latest-v14.x/docs/api/stream.html)
+ - [String decoder](https://nodejs.org/dist/latest-v14.x/docs/api/string_decoder.html)
+ - [Timers](https://nodejs.org/dist/latest-v14.x/docs/api/timers.html)
+ - [TLS/SSL](https://nodejs.org/dist/latest-v14.x/docs/api/tls.html)
+ - [UDP/datagram](https://nodejs.org/dist/latest-v14.x/docs/api/dgram.html)
+ - [URL](https://nodejs.org/dist/latest-v14.x/docs/api/url.html)
+ - [Utilities](https://nodejs.org/dist/latest-v14.x/docs/api/util.html)
+ - [Zlib](https://nodejs.org/dist/latest-v14.x/docs/api/zlib.html)
+ - Supported (Possibly) but Not Opened Features
+ - Experimental
+ - [Async hooks](https://nodejs.org/dist/latest-v14.x/docs/api/async_hooks.html)
+ - [Modules: ECMAScript modules](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html)
+ - [Modules: `module` API](https://nodejs.org/dist/latest-v14.x/docs/api/module.html)
+ - [Modules: Packages](https://nodejs.org/dist/latest-v14.x/docs/api/packages.html)
+ - Deprecated
+ - [Domain](https://nodejs.org/dist/latest-v14.x/docs/api/domain.html)
+ - [Punycode](https://nodejs.org/dist/latest-v14.x/docs/api/punycode.html)
+ - [Command-line options](https://nodejs.org/dist/latest-v14.x/docs/api/cli.html)
+ - [Child processes](https://nodejs.org/dist/latest-v14.x/docs/api/child_process.html)
+ - [Cluster](https://nodejs.org/dist/latest-v14.x/docs/api/cluster.html)
+ - [HTTP/2](https://nodejs.org/dist/latest-v14.x/docs/api/http2.html)
+ - [Internationalization](https://nodejs.org/dist/latest-v14.x/docs/api/intl.html)
+ - [Readline](https://nodejs.org/dist/latest-v14.x/docs/api/readline.html)
+ - [REPL](https://nodejs.org/dist/latest-v14.x/docs/api/repl.html)
+ - [TTY](https://nodejs.org/dist/latest-v14.x/docs/api/tty.html)
+ - Unsupported Features
+ - Experimental
+ - [WASI](https://nodejs.org/dist/latest-v14.x/docs/api/wasi.html)
+ - [Policies](https://nodejs.org/dist/latest-v14.x/docs/api/policy.html)
+ - [Trace events](https://nodejs.org/dist/latest-v14.x/docs/api/tracing.html)
+ - [C++ addons](https://nodejs.org/dist/latest-v14.x/docs/api/addons.html)
+ - [C++ embedder API](https://nodejs.org/dist/latest-v14.x/docs/api/embedding.html)
+ - [Debugger](https://nodejs.org/dist/latest-v14.x/docs/api/debugger.html)
+ - [Deprecated APIs](https://nodejs.org/dist/latest-v14.x/docs/api/deprecations.html)
+ - [Worker threads](https://nodejs.org/dist/latest-v14.x/docs/api/worker_threads.html)
+ - [Diagnostics Channel](https://nodejs.org/dist/latest-v14.x/docs/api/diagnostics_channel.html)
+ - [Inspector](https://nodejs.org/dist/latest-v14.x/docs/api/inspector.html)
+ - [Report](https://nodejs.org/dist/latest-v14.x/docs/api/report.html)
+ - [V8](https://nodejs.org/dist/latest-v14.x/docs/api/v8.html)
+ - [VM](https://nodejs.org/dist/latest-v14.x/docs/api/vm.html)
+ - [Performance hooks](https://nodejs.org/dist/latest-v14.x/docs/api/perf_hooks.html)
+
+* ECMAScript
+ * [node.green](https://node.green/) provides an overview over supported ECMAScript features in our target version of Node.js, `v14.14`.
}
}
+// @lwnode
+function canIgnoreErrorMessage() {
+ const useEscargot = process.config.variables.javascript_engine == "escargot";
+ const isRunningOnTests = !!process.env["LWNODE_RUNNING_ON_TESTS"];
+ return useEscargot && isRunningOnTests;
+}
+
function expectedException(actual, expected, message, fn) {
let generatedMessage = false;
let throwError = false;
RegExpPrototypeTest(expected[key], actual[key])) {
continue;
}
+ if (key == 'message' && canIgnoreErrorMessage()) {
+ continue;
+ }
compareExceptionKey(actual, expected, key, message, keys, fn);
}
return;
if (string.length === 0)
return new FastBuffer();
}
+
+ // @lwnode
+ // if the given string with latin1 or ascii encoding is not
+ // being handled as one byte string in JS engine, we newly create it.
+ if (ops == encodingOps.latin1 || ops == encodingOps.ascii) {
+ if (process.lwnode.checkIfHandledAsOneByteString(string) == false) {
+ return fromStringFast(`${string}`, ops);
+ }
+ }
+
return fromStringFast(string, ops);
}
'zlib'
]);
+// @lwnode
+const userModuleBlacklist = [
+ 'v8',
+];
+
// Set up process.binding() and process._linkedBinding().
{
const bindingObj = ObjectCreate(null);
constructor(id) {
this.filename = `${id}.js`;
this.id = id;
- this.canBeRequiredByUsers = !id.startsWith('internal/');
+ // this.canBeRequiredByUsers = !id.startsWith('internal/');
+ // @lwnode
+ this.canBeRequiredByUsers =
+ !id.startsWith("internal/") && !userModuleBlacklist.includes(id);
// The CJS exports object of the module.
this.exports = {};
return binding.MemSnapshot.apply(null, args);
}
},
+ checkIfHandledAsOneByteString: (...args) => {
+ if (binding.checkIfHandledAsOneByteString) {
+ return binding.checkIfHandledAsOneByteString.apply(null, args);
+ }
+ },
isReloadScriptEnabled: () => {
return !!binding.CreateReloadableSourceFromFile;
},
ROOT_PATH=out/cctest
./tools/gyp/gyp ./lwnode/code/escargotshim/test/cctest.gyp --depth=. -f ninja \
- --generator-output=$ROOT_PATH -Dbuild_asan=1 -Dbuild_mode=debug \
+ --generator-output=$ROOT_PATH -Dasan=1 -Dbuild_mode=debug \
-Descargot_lib_type=static_lib -Dtarget_arch=x64 -Dtarget_os=linux \
- -Denable_experimental=true -Descargot_threading=1
+ -Denable_experimental=true -Descargot_threading=1 -Dinclude_node_bindings=false \
+ -Descargot_debugger=false
ninja -v -C $ROOT_PATH/out/Debug cctest
--- /dev/null
+#!/bin/bash
+# Copyright (c) 2022-present Samsung Electronics 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.
+
+TARGET_OS="linux"
+
+PROJECT_ROOT_PATH=$PWD
+MODULES_ROOT_PATH=$PROJECT_ROOT_PATH/lwnode/modules
+BUILD_OUT_ROOT_PATH=$PROJECT_ROOT_PATH/out/modules
+LWNODE_INCLUDES_PATH="$PROJECT_ROOT_PATH/src;$PROJECT_ROOT_PATH/lwnode/code/escargotshim/include"
+
+fancy_echo() {
+ local BOLD="\033[1m"
+ local GREEN="\033[1;32m"
+ local CLEAR="\033[0m"
+ echo -e ""
+ echo -e ${GREEN}$1$2${CLEAR}
+}
+
+error_echo() {
+ local BOLD="\033[1m"
+ local RED="\033[1;31m"
+ local CLEAR="\033[0m"
+ echo -e ""
+ echo -e ${RED}$1$2${CLEAR}
+}
+
+usage() {
+ echo "Usage: build-modules.sh <modules-list> [options]"
+ echo "
+ options:
+ --os) target os (linux/tizen)
+ "
+ echo "example) build-modules.sh hello-world,device-api --os=tizen
+ "
+}
+
+find_and_build_modules() {
+ local module_list=($(echo $1 | tr "," "\n"))
+
+ for module in "${module_list[@]}"; do
+ if [ -d "$MODULES_ROOT_PATH/$module" ]; then
+ build_module $module
+ else
+ error_echo "Cannot find module: $module"
+ fi
+ done
+}
+
+build_module() {
+ fancy_echo "build [$1]"
+
+ local out_path=$BUILD_OUT_ROOT_PATH/$TARGET_OS/$1
+ local module_path=$MODULES_ROOT_PATH/$1
+ mkdir -p $out_path
+
+ cmake $module_path -B$out_path -H$module_path -DLWNODE_INCLUDES=$LWNODE_INCLUDES_PATH \
+ -G Ninja
+ ninja -C $out_path
+}
+
+if [[ -z $1 ]] || [[ $1 == -* ]]; then
+ usage
+ exit 1
+fi
+
+MODULES_LIST=$1
+shift
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --os=*)
+ TARGET_OS="${1#*=}"
+ ;;
+ *)
+ echo "Unknown option $1"
+ exit -1
+ ;;
+ esac
+
+ shift
+done
+
+fancy_echo "target os: $TARGET_OS"
+find_and_build_modules $MODULES_LIST
ROOT_PATH=./out/sample
gyp ./lwnode/code/escargotshim/sample/sample.gyp --depth=. -f ninja \
- --generator-output=$ROOT_PATH -Dbuild_asan=1 -Dbuild_mode=debug \
- -Descargot_lib_type=static_lib
+ --generator-output=$ROOT_PATH \
+ -Descargot_lib_type=static_lib -Dtarget_arch=x64 -Dtarget_os=linux \
+ -Dbuild_mode=debug \
+ -Descargot_threading=1 \
+ -Dinclude_node_bindings=false
ninja -v -C $ROOT_PATH/out/Debug sample
'target_os%': 'none', # configure with --tizen
'build_mode%': 'release',
'build_host%': '<(OS)',
- 'build_asan%': '0',
+ 'asan%': '0',
},
'target_defaults': {
'defines': [ 'LWNODE=1' ],
],
},
}],
- ['build_asan==1', {
+ ['asan==1', {
'cflags+': [ '-fsanitize=address', '-fno-omit-frame-pointer', '-fno-common', '-D_FORTIFY_SOURCE=2' ],
'cflags_cc+': [ '-fsanitize=address', '-fno-omit-frame-pointer', '-fno-common', '-D_FORTIFY_SOURCE=2' ],
'cflags!': [ '-fomit-frame-pointer' ],
ENDIF()
IF (ESCARGOT_USE_CUSTOM_LOGGING)
- SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DESCARGOT_USE_CUSTOM_LOGGING)
+ SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_CUSTOM_LOGGING)
ELSEIF (${ESCARGOT_HOST} STREQUAL "tizen_obs")
PKG_CHECK_MODULES (DLOG REQUIRED dlog)
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} ${DLOG_LIBRARIES})
IF (${ESCARGOT_OUTPUT} MATCHES "shell")
ADD_EXECUTABLE (${ESCARGOT_TARGET} ${ESCARGOT_SRC_LIST})
- TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
+ TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_INCDIRS})
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_DEFINITIONS})
TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${CXXFLAGS_FROM_ENV} ${PROFILER_FLAGS} ${DEBUGGER_FLAGS})
ELSEIF (${ESCARGOT_OUTPUT} STREQUAL "shared_lib")
ADD_LIBRARY (${ESCARGOT_TARGET} SHARED ${ESCARGOT_SRC_LIST})
- TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
+ TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} PUBLIC ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_INCDIRS})
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_DEFINITIONS})
- TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${CXXFLAGS_FROM_ENV})
+ TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${DEBUGGER_FLAGS} ${CXXFLAGS_FROM_ENV})
ELSEIF (${ESCARGOT_OUTPUT} STREQUAL "static_lib")
ADD_LIBRARY (${ESCARGOT_TARGET} STATIC ${ESCARGOT_SRC_LIST})
- TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
+ TARGET_LINK_LIBRARIES (${ESCARGOT_TARGET} PUBLIC ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV})
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_INCDIRS})
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_DEFINITIONS})
- TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${CXXFLAGS_FROM_ENV})
+ TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${DEBUGGER_FLAGS} ${CXXFLAGS_FROM_ENV})
ELSEIF (${ESCARGOT_OUTPUT} STREQUAL "cctest")
ADD_EXECUTABLE (${ESCARGOT_CCTEST_TARGET} ${ESCARGOT_SRC_LIST})
- TARGET_LINK_LIBRARIES (${ESCARGOT_CCTEST_TARGET} ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV} gtest)
+ TARGET_LINK_LIBRARIES (${ESCARGOT_CCTEST_TARGET} PRIVATE ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV} gtest)
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_CCTEST_TARGET} PRIVATE ${ESCARGOT_INCDIRS})
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_CCTEST_TARGET} PRIVATE ${ESCARGOT_DEFINITIONS})
- TARGET_COMPILE_OPTIONS (${ESCARGOT_CCTEST_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} -I${ESCARGOT_ROOT}/third_party/googletest/googletest/include/ ${CXXFLAGS_FROM_ENV})
+ TARGET_COMPILE_OPTIONS (${ESCARGOT_CCTEST_TARGET} PRIVATE ${ESCARGOT_CXXFLAGS} ${DEBUGGER_FLAGS} -I${ESCARGOT_ROOT}/third_party/googletest/googletest/include/ ${CXXFLAGS_FROM_ENV})
ENDIF()
#include <unordered_set>
#include <vector>
#include <random>
+#include <queue>
#if defined(COMPILER_MSVC)
#include <stddef.h>
#include "RuntimeICUBinder.h"
#include "ICUPolyfill.h"
#else
+
+#if defined(OS_WINDOWS_UWP)
+#include <icu.h>
+#else
#include <unicode/locid.h>
#include <unicode/uchar.h>
#include <unicode/ustring.h>
int32_t& dstOffset, UErrorCode& ec);
int32_t vzone_getRawOffset(VZone* zone);
}
+#endif // !defined(OS_WINDOWS_UWP)
+
#endif
#else
#define UCHAR_MAX_VALUE 0x10ffff
#endif
+#if defined(ENABLE_INTL) && !defined(OS_WINDOWS_UWP)
+#define ENABLE_INTL_DISPLAYNAMES
+#define ENABLE_INTL_NUMBERFORMAT
+#define ENABLE_INTL_PLURALRULES
+#define ENABLE_INTL_RELATIVETIMEFORMAT
+#define ENABLE_INTL_LISTFORMAT
+#endif
+
#ifndef TRUE
#define TRUE 1
#endif
#define FALSE 0
#endif
-#ifdef ESCARGOT_USE_CUSTOM_LOGGING
+#ifdef ENABLE_CUSTOM_LOGGING
// use customized logging
#include <stdarg.h>
namespace Escargot {
#define REGEXP_CACHE_SIZE_MAX 64
#endif
-#ifndef ROPE_STRING_MIN_LENGTH
-#define ROPE_STRING_MIN_LENGTH 24
-#endif
-
#include "EscargotInfo.h"
#include "heap/Heap.h"
#include "util/Util.h"
m_platform->deallocateThreadLocalCustomData();
}
-#ifdef ESCARGOT_USE_CUSTOM_LOGGING
+#ifdef ENABLE_CUSTOM_LOGGING
virtual void customInfoLogger(const char* format, va_list arg) override
{
m_platform->customInfoLogger(format, arg);
StringRef* StringRef::createFromASCII(const char* s, size_t len)
{
- return toRef(new ASCIIString(s, len));
+ return toRef(String::fromASCII(s, len));
}
StringRef* StringRef::createFromUTF8(const char* s, size_t len, bool maybeASCII)
StringRef* StringRef::createFromLatin1(const unsigned char* s, size_t len)
{
- return toRef(new Latin1String(s, len));
+ return toRef(String::fromLatin1(s, len));
}
StringRef* StringRef::createExternalFromASCII(const char* s, size_t len)
}
Evaluator::StackTraceData::StackTraceData()
- : src(toRef(String::emptyString))
+ : srcName(toRef(String::emptyString))
, sourceCode(toRef(String::emptyString))
, loc(SIZE_MAX, SIZE_MAX, SIZE_MAX)
, functionName(toRef(String::emptyString))
+ , callee(nullptr)
, isFunction(false)
, isConstructor(false)
, isAssociatedWithJavaScriptCode(false)
Evaluator::EvaluatorResult::EvaluatorResult(const EvaluatorResult& src)
: result(src.result)
, error(src.error)
- , stackTraceData(src.stackTraceData)
+ , stackTrace(src.stackTrace)
{
}
{
result = src.result;
error = src.error;
- stackTraceData = src.stackTraceData;
+ stackTrace = src.stackTrace;
return *this;
}
Evaluator::EvaluatorResult::EvaluatorResult(EvaluatorResult&& src)
: result(src.result)
, error(src.error)
- , stackTraceData(std::move(src.stackTraceData))
+ , stackTrace(std::move(src.stackTrace))
{
src.result = ValueRef::createUndefined();
src.error = nullptr;
r.result = toRef(result.result);
if (!result.error.isEmpty()) {
- new (&r.stackTraceData) GCManagedVector<Evaluator::StackTraceData>(result.stackTraceData.size());
- for (size_t i = 0; i < result.stackTraceData.size(); i++) {
+ new (&r.stackTrace) GCManagedVector<Evaluator::StackTraceData>(result.stackTrace.size());
+ for (size_t i = 0; i < result.stackTrace.size(); i++) {
Evaluator::StackTraceData t;
- t.src = toRef(result.stackTraceData[i].src);
- t.sourceCode = toRef(result.stackTraceData[i].sourceCode);
- t.loc.index = result.stackTraceData[i].loc.index;
- t.loc.line = result.stackTraceData[i].loc.line;
- t.loc.column = result.stackTraceData[i].loc.column;
- t.functionName = toRef(result.stackTraceData[i].functionName);
- t.isFunction = result.stackTraceData[i].isFunction;
- t.isConstructor = result.stackTraceData[i].isConstructor;
- t.isAssociatedWithJavaScriptCode = result.stackTraceData[i].isAssociatedWithJavaScriptCode;
- t.isEval = result.stackTraceData[i].isEval;
- r.stackTraceData[i] = t;
+ t.srcName = toRef(result.stackTrace[i].srcName);
+ t.sourceCode = toRef(result.stackTrace[i].sourceCode);
+ t.loc.index = result.stackTrace[i].loc.index;
+ t.loc.line = result.stackTrace[i].loc.line;
+ t.loc.column = result.stackTrace[i].loc.column;
+ t.functionName = toRef(result.stackTrace[i].functionName);
+ if (result.stackTrace[i].callee) {
+ t.callee = toRef(result.stackTrace[i].callee.value());
+ }
+ t.isFunction = result.stackTrace[i].isFunction;
+ t.isConstructor = result.stackTrace[i].isConstructor;
+ t.isAssociatedWithJavaScriptCode = result.stackTrace[i].isAssociatedWithJavaScriptCode;
+ t.isEval = result.stackTrace[i].isEval;
+ r.stackTrace[i] = t;
}
}
COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::Before == (int)VMInstance::PromiseHookType::Before, "");
COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::After == (int)VMInstance::PromiseHookType::After, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseRejectEvent::PromiseRejectWithNoHandler == (int)VMInstance::PromiseRejectEvent::PromiseRejectWithNoHandler, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseRejectEvent::PromiseHandlerAddedAfterReject == (int)VMInstance::PromiseRejectEvent::PromiseHandlerAddedAfterReject, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseRejectEvent::PromiseRejectAfterResolved == (int)VMInstance::PromiseRejectEvent::PromiseRejectAfterResolved, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseRejectEvent::PromiseResolveAfterResolved == (int)VMInstance::PromiseRejectEvent::PromiseResolveAfterResolved, "");
+
PersistentRefHolder<VMInstanceRef> VMInstanceRef::create(const char* locale, const char* timezone, const char* baseCacheDir)
{
return PersistentRefHolder<VMInstanceRef>(toRef(new VMInstance(locale, timezone, baseCacheDir)));
toImpl(this)->unregisterPromiseHook();
}
+void VMInstanceRef::registerPromiseRejectCallback(PromiseRejectCallback rejectCallback)
+{
+ toImpl(this)->registerPromiseRejectCallback([](ExecutionState& state, PromiseObject* promise, const Value& value, VMInstance::PromiseRejectEvent event, void* callback) -> void {
+ ASSERT(!!callback);
+ (reinterpret_cast<PromiseRejectCallback>(callback))(toRef(&state), toRef(promise), toRef(value), (PromiseRejectEvent)(event));
+ },
+ (void*)rejectCallback);
+}
+
+void VMInstanceRef::unregisterPromiseRejectCallback()
+{
+ toImpl(this)->unregisterPromiseRejectCallback();
+}
+
void VMInstanceRef::enterIdleMode()
{
toImpl(this)->enterIdleMode();
return toEvaluatorResultRef(result);
}
+#ifdef ESCARGOT_DEBUGGER
+
+StringRef* DebuggerOperationsRef::BreakpointOperations::eval(StringRef* sourceCode, bool& isError)
+{
+ ExecutionState* state = toImpl(m_executionState);
+ Debugger* debugger = state->context()->debugger();
+
+ if (debugger == nullptr) {
+ isError = true;
+
+ return StringRef::createFromASCII("Debugger is not available");
+ }
+
+ isError = false;
+
+ String* result;
+
+ debugger->setStopState(ESCARGOT_DEBUGGER_IN_EVAL_MODE);
+
+ try {
+ Value asValue(toImpl(sourceCode));
+ Value evalResult(Value::ForceUninitialized);
+ evalResult = state->context()->globalObject()->evalLocal(*state, asValue, state->thisValue(), reinterpret_cast<ByteCodeBlock*>(weakCodeRef())->m_codeBlock, true);
+ result = evalResult.toStringWithoutException(*state);
+ } catch (const Value& val) {
+ result = val.toStringWithoutException(*state);
+
+ isError = true;
+ }
+
+ debugger->setStopState(ESCARGOT_DEBUGGER_IN_WAIT_MODE);
+ return toRef(result);
+}
+
+void DebuggerOperationsRef::BreakpointOperations::getStackTrace(DebuggerOperationsRef::DebuggerStackTraceDataVector& outStackTrace)
+{
+ ExecutionState* state = toImpl(m_executionState);
+ SandBox::StackTraceDataVector stackTraceDataVector;
+
+ bool hasSavedStackTrace = SandBox::createStackTrace(stackTraceDataVector, *state, true);
+ ByteCodeLOCDataMap locMap;
+ size_t size = stackTraceDataVector.size();
+
+ outStackTrace.clear();
+
+ for (uint32_t i = 0; i < size; i++) {
+ if (reinterpret_cast<size_t>(stackTraceDataVector[i].loc.actualCodeBlock) != SIZE_MAX) {
+ ByteCodeBlock* byteCodeBlock = stackTraceDataVector[i].loc.actualCodeBlock;
+ size_t line, column;
+
+ if (stackTraceDataVector[i].loc.index == SIZE_MAX) {
+ size_t byteCodePosition = stackTraceDataVector[i].loc.byteCodePosition;
+
+ ByteCodeLOCData* locData;
+ auto iterMap = locMap.find(byteCodeBlock);
+ if (iterMap == locMap.end()) {
+ locData = new ByteCodeLOCData();
+ locMap.insert(std::make_pair(byteCodeBlock, locData));
+ } else {
+ locData = iterMap->second;
+ }
+
+ ExtendedNodeLOC loc = byteCodeBlock->computeNodeLOCFromByteCode(state->context(), byteCodePosition, byteCodeBlock->m_codeBlock, locData);
+ line = (uint32_t)loc.line;
+ column = (uint32_t)loc.column;
+ } else {
+ line = (uint32_t)stackTraceDataVector[i].loc.line;
+ column = (uint32_t)stackTraceDataVector[i].loc.column;
+ }
+
+ outStackTrace.push_back(DebuggerStackTraceData(reinterpret_cast<WeakCodeRef*>(byteCodeBlock), line, column, stackTraceDataVector[i].executionStateDepth));
+ }
+ }
+
+ for (auto iter = locMap.begin(); iter != locMap.end(); iter++) {
+ delete iter->second;
+ }
+
+ if (hasSavedStackTrace) {
+ Debugger* debugger = state->context()->debugger();
+
+ for (auto iter = debugger->activeSavedStackTrace()->begin(); iter != debugger->activeSavedStackTrace()->end(); iter++) {
+ outStackTrace.push_back(DebuggerStackTraceData(reinterpret_cast<WeakCodeRef*>(iter->byteCodeBlock), iter->line, iter->column, SIZE_MAX));
+ }
+ }
+}
+
+void DebuggerOperationsRef::BreakpointOperations::getLexicalScopeChain(uint32_t stateIndex, DebuggerOperationsRef::LexicalScopeChainVector& outLexicalScopeChain)
+{
+ outLexicalScopeChain.clear();
+
+ ExecutionState* state = toImpl(m_executionState);
+
+ while (stateIndex > 0) {
+ state = state->parent();
+ stateIndex--;
+
+ if (state == nullptr) {
+ return;
+ }
+ }
+
+ LexicalEnvironment* lexEnv = state->lexicalEnvironment();
+
+ while (lexEnv) {
+ EnvironmentRecord* record = lexEnv->record();
+ ScopeType type;
+
+ if (record->isGlobalEnvironmentRecord()) {
+ type = GLOBAL_ENVIRONMENT;
+ } else if (record->isDeclarativeEnvironmentRecord()) {
+ DeclarativeEnvironmentRecord* declarativeRecord = record->asDeclarativeEnvironmentRecord();
+ if (declarativeRecord->isFunctionEnvironmentRecord()) {
+ type = FUNCTION_ENVIRONMENT;
+ } else if (record->isModuleEnvironmentRecord()) {
+ type = MODULE_ENVIRONMENT;
+ } else {
+ type = DECLARATIVE_ENVIRONMENT;
+ }
+ } else if (record->isObjectEnvironmentRecord()) {
+ type = OBJECT_ENVIRONMENT;
+ } else {
+ type = UNKNOWN_ENVIRONMENT;
+ }
+
+ outLexicalScopeChain.push_back(type);
+ lexEnv = lexEnv->outerEnvironment();
+ }
+}
+
+StringRef* DebuggerOperationsRef::getFunctionName(WeakCodeRef* weakCodeRef)
+{
+ ByteCodeBlock* byteCode = reinterpret_cast<ByteCodeBlock*>(weakCodeRef);
+
+ return toRef(byteCode->codeBlock()->functionName().string());
+}
+
+bool DebuggerOperationsRef::updateBreakpoint(WeakCodeRef* weakCodeRef, uint32_t offset, bool enable)
+{
+ ByteCodeBlock* byteCode = reinterpret_cast<ByteCodeBlock*>(weakCodeRef);
+
+ ByteCode* breakpoint = (ByteCode*)(byteCode->m_code.data() + offset);
+
+#if defined(COMPILER_GCC) || defined(COMPILER_CLANG)
+ if (enable) {
+ if (breakpoint->m_opcodeInAddress != g_opcodeTable.m_addressTable[BreakpointDisabledOpcode]) {
+ return false;
+ }
+ breakpoint->m_opcodeInAddress = g_opcodeTable.m_addressTable[BreakpointEnabledOpcode];
+ } else {
+ if (breakpoint->m_opcodeInAddress != g_opcodeTable.m_addressTable[BreakpointEnabledOpcode]) {
+ return false;
+ }
+ breakpoint->m_opcodeInAddress = g_opcodeTable.m_addressTable[BreakpointDisabledOpcode];
+ }
+#else
+ if (enable) {
+ if (breakpoint->m_opcode != BreakpointDisabledOpcode) {
+ return false;
+ }
+ breakpoint->m_opcode = BreakpointEnabledOpcode;
+ } else {
+ if (breakpoint->m_opcode != BreakpointEnabledOpcode) {
+ return false;
+ }
+ breakpoint->m_opcode = BreakpointDisabledOpcode;
+ }
+#endif
+
+ return true;
+}
+
+class DebuggerC : public Debugger {
+public:
+ virtual void parseCompleted(String* source, String* srcName, String* error = nullptr) override;
+ virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
+ virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
+ virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
+ virtual void consoleOut(String* output) override;
+ virtual String* getClientSource(String** sourceName) override;
+ virtual bool getWaitBeforeExitClient() override;
+
+ DebuggerC(DebuggerOperationsRef::DebuggerClient* debuggerClient, Context* context)
+ : m_debuggerClient(debuggerClient)
+ {
+ enable(context);
+ }
+
+protected:
+ virtual bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
+
+private:
+ DebuggerOperationsRef::DebuggerClient* m_debuggerClient;
+};
+
+void DebuggerC::parseCompleted(String* source, String* srcName, String* error)
+{
+ if (error != nullptr) {
+ m_debuggerClient->parseError(toRef(source), toRef(srcName), toRef(error));
+ return;
+ }
+
+ size_t breakpointLocationsSize = m_breakpointLocationsVector.size();
+
+ for (size_t i = 0; i < breakpointLocationsSize; i++) {
+ InterpretedCodeBlock* codeBlock = reinterpret_cast<InterpretedCodeBlock*>(m_breakpointLocationsVector[i]->weakCodeRef);
+
+ m_breakpointLocationsVector[i]->weakCodeRef = reinterpret_cast<Debugger::WeakCodeRef*>(codeBlock->byteCodeBlock());
+ }
+
+ // Same structure, but the definition is duplicated.
+ std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>* info = reinterpret_cast<std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>*>(&m_breakpointLocationsVector);
+
+ m_debuggerClient->parseCompleted(toRef(source), toRef(srcName), *info);
+}
+
+static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
+{
+ LexicalEnvironment* lexEnv = state->lexicalEnvironment();
+
+ while (lexEnv) {
+ EnvironmentRecord* record = lexEnv->record();
+
+ if (record->isDeclarativeEnvironmentRecord()
+ && record->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
+ return lexEnv;
+ }
+
+ lexEnv = lexEnv->outerEnvironment();
+ }
+ return nullptr;
+}
+
+void DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
+{
+ DebuggerOperationsRef::BreakpointOperations operations(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock), toRef(state), offset);
+
+ switch (m_debuggerClient->stopAtBreakpoint(operations)) {
+ case DebuggerOperationsRef::Continue: {
+ m_stopState = nullptr;
+ break;
+ }
+ case DebuggerOperationsRef::Step: {
+ m_stopState = ESCARGOT_DEBUGGER_ALWAYS_STOP;
+ break;
+ }
+ case DebuggerOperationsRef::Next: {
+ m_stopState = state;
+ break;
+ }
+ case DebuggerOperationsRef::Finish: {
+ LexicalEnvironment* lexEnv = getFunctionLexEnv(state);
+
+ if (!lexEnv) {
+ m_stopState = nullptr;
+ break;
+ }
+
+ ExecutionState* stopState = state->parent();
+
+ while (stopState && getFunctionLexEnv(stopState) == lexEnv) {
+ stopState = stopState->parent();
+ }
+
+ m_stopState = stopState;
+ break;
+ }
+ }
+}
+
+void DebuggerC::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock)
+{
+ m_debuggerClient->codeReleased(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock));
+}
+
+void DebuggerC::exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace)
+{
+ UNUSED_PARAMETER(message);
+ UNUSED_PARAMETER(exceptionTrace);
+}
+
+void DebuggerC::consoleOut(String* output)
+{
+ UNUSED_PARAMETER(output);
+}
+
+String* DebuggerC::getClientSource(String** sourceName)
+{
+ UNUSED_PARAMETER(sourceName);
+
+ return nullptr;
+}
+
+bool DebuggerC::getWaitBeforeExitClient()
+{
+ return false;
+}
+
+bool DebuggerC::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
+{
+ UNUSED_PARAMETER(state);
+ UNUSED_PARAMETER(byteCodeBlock);
+ UNUSED_PARAMETER(isBlockingRequest);
+
+ return false;
+}
+
+#endif /* ESCARGOT_DEBUGGER */
+
PersistentRefHolder<ContextRef> ContextRef::create(VMInstanceRef* vminstanceref)
{
VMInstance* vminstance = toImpl(vminstanceref);
virtual ObjectGetResult getOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- Value PV = P.toPlainValue(state);
+ Value PV = P.toPlainValue();
if (!PV.isSymbol()) {
auto result = m_getOwnPropetyCallback(toRef(&state), toRef(this), toRef(PV));
if (result.m_value.hasValue()) {
{
// Only value type supported
if (desc.isValuePresent()) {
- Value PV = P.toPlainValue(state);
+ Value PV = P.toPlainValue();
if (!PV.isSymbol() && m_defineOwnPropertyCallback(toRef(&state), toRef(this), toRef(PV), toRef(desc.value()))) {
return true;
}
}
virtual bool deleteOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- Value PV = P.toPlainValue(state);
+ Value PV = P.toPlainValue();
if (!PV.isSymbol()) {
- auto result = m_getOwnPropetyCallback(toRef(&state), toRef(this), toRef(P.toPlainValue(state)));
+ auto result = m_getOwnPropetyCallback(toRef(&state), toRef(this), toRef(P.toPlainValue()));
if (result.m_value.hasValue()) {
- return m_deleteOwnPropertyCallback(toRef(&state), toRef(this), toRef(P.toPlainValue(state)));
+ return m_deleteOwnPropertyCallback(toRef(&state), toRef(this), toRef(P.toPlainValue()));
}
}
return Object::deleteOwnProperty(state, P);
toImpl(this)->enumeration(*toImpl(state), [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
const std::function<bool(ExecutionStateRef * state, ValueRef * propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)>* cb
= (const std::function<bool(ExecutionStateRef * state, ValueRef * propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)>*)data;
- return (*cb)(toRef(&state), toRef(name.toPlainValue(state)), desc.isWritable(), desc.isEnumerable(), desc.isConfigurable());
+ return (*cb)(toRef(&state), toRef(name.toPlainValue()), desc.isWritable(), desc.isEnumerable(), desc.isConfigurable());
},
(void*)&cb, shouldSkipSymbolKey);
}
newArgv[i] = toImpl(argumentNameArray[i]);
}
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, toImpl(functionName), argumentCount, newArgv, toImpl(body), false, false, false, false);
+ auto functionSource = FunctionObject::createFunctionScript(state, toImpl(functionName), argumentCount, newArgv, toImpl(body), false, false, false, false);
+
+ Object* proto = state.context()->globalObject()->functionPrototype();
+ ScriptFunctionObject* result = new ScriptFunctionObject(state, proto, functionSource.codeBlock, functionSource.outerEnvironment, true, false);
+
+ return toRef(result);
+}
+
+FunctionObjectRef* FunctionObjectRef::create(ExecutionStateRef* stateRef, StringRef* sourceName, AtomicStringRef* functionName, size_t argumentCount, ValueRef** argumentNameArray, ValueRef* body)
+{
+ ASSERT(toImpl(sourceName));
+
+ ExecutionState& state = *toImpl(stateRef);
+ Value* newArgv = ALLOCA(sizeof(Value) * argumentCount, Value, state);
+ for (size_t i = 0; i < argumentCount; i++) {
+ newArgv[i] = toImpl(argumentNameArray[i]);
+ }
+
+ auto functionSource = FunctionObject::createFunctionScript(state, toImpl(functionName), argumentCount, newArgv, toImpl(body), false, false, false, false, false, toImpl(sourceName));
Object* proto = state.context()->globalObject()->functionPrototype();
- ScriptFunctionObject* result = new ScriptFunctionObject(state, proto, functionSource.codeBlock, functionSource.outerEnvironment, true, false, false);
+ ScriptFunctionObject* result = new ScriptFunctionObject(state, proto, functionSource.codeBlock, functionSource.outerEnvironment, true, false);
return toRef(result);
}
}
}
+bool FunctionObjectRef::setName(AtomicStringRef* name)
+{
+ return toImpl(this)->setName(toImpl(name));
+}
+
GlobalObjectRef* ContextRef::globalObject()
{
Context* ctx = toImpl(this);
toImpl(this)->throwException(s, toImpl(exceptionValue));
}
-bool ContextRef::initDebugger(const char* options)
+bool ContextRef::initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient)
+{
+#ifdef ESCARGOT_DEBUGGER
+ Context* context = toImpl(this);
+
+ if (debuggerClient == nullptr || context->debuggerEnabled()) {
+ return false;
+ }
+
+ new DebuggerC(debuggerClient, context);
+ return true;
+#else /* !ESCARGOT_DEBUGGER */
+ return false;
+#endif /* ESCARGOT_DEBUGGER */
+}
+
+bool ContextRef::initDebuggerRemote(const char* options)
{
#ifdef ESCARGOT_DEBUGGER
- return toImpl(this)->initDebugger(options);
+ return toImpl(this)->initDebuggerRemote(options);
#else /* !ESCARGOT_DEBUGGER */
return false;
#endif /* ESCARGOT_DEBUGGER */
bool ContextRef::isDebuggerRunning()
{
#ifdef ESCARGOT_DEBUGGER
- return !!toImpl(this)->debugger() && toImpl(this)->debugger()->enabled();
+ return toImpl(this)->debuggerEnabled();
+#else /* !ESCARGOT_DEBUGGER */
+ return false;
+#endif /* ESCARGOT_DEBUGGER */
+}
+
+bool ContextRef::isWaitBeforeExit()
+{
+#ifdef ESCARGOT_DEBUGGER
+ return isDebuggerRunning() && toImpl(this)->debugger()->getWaitBeforeExitClient();
#else /* !ESCARGOT_DEBUGGER */
return false;
#endif /* ESCARGOT_DEBUGGER */
#endif /* ESCARGOT_DEBUGGER */
}
+void ContextRef::pumpDebuggerEvents()
+{
+#ifdef ESCARGOT_DEBUGGER
+ toImpl(this)->pumpDebuggerEvents();
+#endif /* ESCARGOT_DEBUGGER */
+}
+
+void ContextRef::setAsAlwaysStopState()
+{
+#ifdef ESCARGOT_DEBUGGER
+ toImpl(this)->setAsAlwaysStopState();
+#endif /* ESCARGOT_DEBUGGER */
+}
+
StringRef* ContextRef::getClientSource(StringRef** sourceName)
{
#ifdef ESCARGOT_DEBUGGER
imp->throwException(toImpl(value));
}
-GCManagedVector<Evaluator::StackTraceData> ExecutionStateRef::computeStackTraceData()
+GCManagedVector<Evaluator::StackTraceData> ExecutionStateRef::computeStackTrace()
{
ExecutionState* state = toImpl(this);
- SandBox::StackTraceDataVector stackTraceData;
- SandBox::createStackTraceData(stackTraceData, *state);
+ SandBox::StackTraceDataVector stackTraceDataVector;
+ SandBox::createStackTrace(stackTraceDataVector, *state);
- GCManagedVector<Evaluator::StackTraceData> result(stackTraceData.size());
+ GCManagedVector<Evaluator::StackTraceData> result(stackTraceDataVector.size());
ByteCodeLOCDataMap locMap;
- for (size_t i = 0; i < stackTraceData.size(); i++) {
- if ((size_t)stackTraceData[i].second.loc.index == SIZE_MAX && (size_t)stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
- ByteCodeBlock* byteCodeBlock = stackTraceData[i].second.loc.actualCodeBlock;
+ for (size_t i = 0; i < stackTraceDataVector.size(); i++) {
+ if (stackTraceDataVector[i].loc.index == SIZE_MAX && reinterpret_cast<size_t>(stackTraceDataVector[i].loc.actualCodeBlock) != SIZE_MAX) {
+ ByteCodeBlock* byteCodeBlock = stackTraceDataVector[i].loc.actualCodeBlock;
ByteCodeLOCData* locData;
auto iterMap = locMap.find(byteCodeBlock);
}
InterpretedCodeBlock* codeBlock = byteCodeBlock->codeBlock();
- size_t byteCodePosition = stackTraceData[i].second.loc.byteCodePosition;
- stackTraceData[i].second.loc = byteCodeBlock->computeNodeLOCFromByteCode(state->context(), byteCodePosition, byteCodeBlock->m_codeBlock, locData);
- stackTraceData[i].second.src = codeBlock->script()->srcName();
- stackTraceData[i].second.sourceCode = codeBlock->script()->sourceCode();
+ size_t byteCodePosition = stackTraceDataVector[i].loc.byteCodePosition;
+ stackTraceDataVector[i].loc = byteCodeBlock->computeNodeLOCFromByteCode(state->context(), byteCodePosition, byteCodeBlock->m_codeBlock, locData);
+
+ stackTraceDataVector[i].srcName = codeBlock->script()->srcName();
+ stackTraceDataVector[i].sourceCode = codeBlock->script()->sourceCode();
}
Evaluator::StackTraceData t;
- t.src = toRef(stackTraceData[i].second.src);
- t.sourceCode = toRef(stackTraceData[i].second.sourceCode);
- t.loc.index = stackTraceData[i].second.loc.index;
- t.loc.line = stackTraceData[i].second.loc.line;
- t.loc.column = stackTraceData[i].second.loc.column;
- t.functionName = toRef(stackTraceData[i].second.functionName);
- t.isFunction = stackTraceData[i].second.isFunction;
- t.isConstructor = stackTraceData[i].second.isConstructor;
- t.isAssociatedWithJavaScriptCode = stackTraceData[i].second.isAssociatedWithJavaScriptCode;
- t.isEval = stackTraceData[i].second.isEval;
+ t.srcName = toRef(stackTraceDataVector[i].srcName);
+ t.sourceCode = toRef(stackTraceDataVector[i].sourceCode);
+ t.loc.index = stackTraceDataVector[i].loc.index;
+ t.loc.line = stackTraceDataVector[i].loc.line;
+ t.loc.column = stackTraceDataVector[i].loc.column;
+ t.functionName = toRef(stackTraceDataVector[i].functionName);
+ if (stackTraceDataVector[i].callee) {
+ t.callee = toRef(stackTraceDataVector[i].callee.value());
+ }
+ t.isFunction = stackTraceDataVector[i].isFunction;
+ t.isConstructor = stackTraceDataVector[i].isConstructor;
+ t.isAssociatedWithJavaScriptCode = stackTraceDataVector[i].isAssociatedWithJavaScriptCode;
+ t.isEval = stackTraceDataVector[i].isEval;
result[i] = t;
}
for (auto iter = locMap.begin(); iter != locMap.end(); iter++) {
toImpl(this)->reject(*toImpl(state), toImpl(reason));
}
+bool PromiseObjectRef::hasResolveHandlers()
+{
+ return toImpl(this)->hasResolveHandlers();
+}
+
+bool PromiseObjectRef::hasRejectHandlers()
+{
+ return toImpl(this)->hasRejectHandlers();
+}
+
ProxyObjectRef* ProxyObjectRef::create(ExecutionStateRef* state, ObjectRef* target, ObjectRef* handler)
{
return toRef(ProxyObject::createProxy(*toImpl(state), toImpl(target), toImpl(handler)));
return toRef(new ObjectTemplate());
}
-void ObjectTemplateRef::setNamedPropertyHandler(const ObjectTemplateNamedPropertyHandlerData& data)
+void ObjectTemplateRef::setNamedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data)
{
toImpl(this)->setNamedPropertyHandler(data);
}
+void ObjectTemplateRef::setIndexedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data)
+{
+ toImpl(this)->setIndexedPropertyHandler(data);
+}
+
void ObjectTemplateRef::removeNamedPropertyHandler()
{
toImpl(this)->removeNamedPropertyHandler();
}
+void ObjectTemplateRef::removeIndexedPropertyHandler()
+{
+ toImpl(this)->removeIndexedPropertyHandler();
+}
+
OptionalRef<FunctionTemplateRef> ObjectTemplateRef::constructor()
{
if (toImpl(this)->constructor()) {
return toRef(toImpl(this)->moduleEvaluationError());
}
+ScriptRef::ModuleStatus ScriptRef::moduleStatus()
+{
+ auto md = toImpl(this)->moduleData();
+ if (md->m_evaluationError) {
+ return ModuleStatus::Errored;
+ }
+ switch (md->m_status) {
+ case Script::ModuleData::Unlinked:
+ return ModuleStatus::Uninstantiated;
+ case Script::ModuleData::Linking:
+ return ModuleStatus::Instantiating;
+ case Script::ModuleData::Linked:
+ return ModuleStatus::Instantiated;
+ case Script::ModuleData::Evaluating:
+ return ModuleStatus::Evaluating;
+ case Script::ModuleData::Evaluated:
+ return ModuleStatus::Evaluated;
+ }
+
+ ASSERT_NOT_REACHED();
+ return ModuleStatus::Errored;
+}
+
+ValueRef* ScriptRef::moduleInstantiate(ExecutionStateRef* state)
+{
+ return toRef(toImpl(this)->moduleInstantiate(*toImpl(state)));
+}
+
+ValueRef* ScriptRef::moduleEvaluate(ExecutionStateRef* state)
+{
+ return toRef(toImpl(this)->moduleEvaluate(*toImpl(state)));
+}
+
PlatformRef::LoadModuleResult::LoadModuleResult(ScriptRef* result)
: script(result)
, errorMessage(StringRef::emptyString())
{
}
+bool PlatformRef::isCustomLoggingEnabled()
+{
+#if defined(ENABLE_CUSTOM_LOGGING)
+ return true;
+#else
+ return false;
+#endif
+}
+
bool SerializerRef::serializeInto(ValueRef* value, std::ostringstream& output)
{
return Serializer::serializeInto(toImpl(value), output);
return toRef(result.result);
}
+bool WASMOperationsRef::isWASMOperationsEnabled()
+{
+#if defined(ENABLE_WASM)
+ return true;
+#else
+ return false;
+#endif
+}
+
#if defined(ENABLE_WASM)
ValueRef* WASMOperationsRef::copyStableBufferBytes(ExecutionStateRef* state, ValueRef* source)
{
{
WASMOperations::collectHeap();
}
+#else
+ValueRef* WASMOperationsRef::copyStableBufferBytes(ExecutionStateRef* state, ValueRef* source)
+{
+ ESCARGOT_LOG_ERROR("If you want to use this function, you should enable WASM");
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+ObjectRef* WASMOperationsRef::asyncCompileModule(ExecutionStateRef* state, ValueRef* source)
+{
+ ESCARGOT_LOG_ERROR("If you want to use this function, you should enable WASM");
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+ObjectRef* WASMOperationsRef::instantiatePromiseOfModuleWithImportObject(ExecutionStateRef* state, PromiseObjectRef* promiseOfModule, ValueRef* importObj)
+{
+ ESCARGOT_LOG_ERROR("If you want to use this function, you should enable WASM");
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+void WASMOperationsRef::collectHeap()
+{
+ ESCARGOT_LOG_ERROR("If you want to use this function, you should enable WASM");
+ RELEASE_ASSERT_NOT_REACHED();
+}
#endif
} // namespace Escargot
};
struct ESCARGOT_EXPORT StackTraceData {
- StringRef* src;
+ StringRef* srcName;
StringRef* sourceCode;
LOC loc;
StringRef* functionName;
+ OptionalRef<FunctionObjectRef> callee;
bool isFunction;
bool isConstructor;
bool isAssociatedWithJavaScriptCode;
ValueRef* result;
OptionalRef<ValueRef> error;
- GCManagedVector<StackTraceData> stackTraceData;
+ GCManagedVector<StackTraceData> stackTrace;
};
template <typename... Args, typename F>
void throwException(ValueRef* value);
- GCManagedVector<Evaluator::StackTraceData> computeStackTraceData();
+ GCManagedVector<Evaluator::StackTraceData> computeStackTrace();
ContextRef* context();
After
};
+ enum PromiseRejectEvent {
+ PromiseRejectWithNoHandler = 0,
+ PromiseHandlerAddedAfterReject = 1,
+ PromiseRejectAfterResolved = 2,
+ PromiseResolveAfterResolved = 3,
+ };
+
typedef void (*PromiseHook)(ExecutionStateRef* state, PromiseHookType type, PromiseObjectRef* promise, ValueRef* parent);
+ typedef void (*PromiseRejectCallback)(ExecutionStateRef* state, PromiseObjectRef* promise, ValueRef* value, PromiseRejectEvent event);
// Register PromiseHook (PromiseHook is used by third party app)
void registerPromiseHook(PromiseHook promiseHook);
void unregisterPromiseHook();
+ // Register a callback to call if this promise is rejected, but it does not have a reject handler
+ void registerPromiseRejectCallback(PromiseRejectCallback);
+ void unregisterPromiseRejectCallback();
// this function enforce do gc,
// remove every compiled bytecodes,
Evaluator::EvaluatorResult executePendingJob();
};
+class ESCARGOT_EXPORT DebuggerOperationsRef {
+public:
+ class WeakCodeRef;
+
+ struct BreakpointLocation {
+ BreakpointLocation(uint32_t line, uint32_t offset)
+ : line(line)
+ , offset(offset)
+ {
+ }
+
+ uint32_t line;
+ uint32_t offset;
+ };
+
+ typedef std::vector<BreakpointLocation> BreakpointLocationVector;
+
+ struct BreakpointLocationsInfo {
+ BreakpointLocationsInfo(WeakCodeRef* weakCodeRef)
+ : weakCodeRef(weakCodeRef)
+ {
+ }
+
+ // The codeRef is a unique id which is not garbage collected
+ // to avoid keeping script / function code in the memory.
+ WeakCodeRef* weakCodeRef;
+ BreakpointLocationVector breakpointLocations;
+ };
+
+ struct DebuggerStackTraceData {
+ DebuggerStackTraceData(WeakCodeRef* weakCodeRef, size_t line, size_t column, size_t depth)
+ : weakCodeRef(weakCodeRef)
+ , line(line)
+ , column(column)
+ , depth(depth)
+ {
+ }
+
+ WeakCodeRef* weakCodeRef;
+ size_t line;
+ size_t column;
+ size_t depth;
+ };
+
+ typedef std::vector<DebuggerStackTraceData> DebuggerStackTraceDataVector;
+
+ enum ScopeType {
+ UNKNOWN_ENVIRONMENT,
+ GLOBAL_ENVIRONMENT,
+ FUNCTION_ENVIRONMENT,
+ DECLARATIVE_ENVIRONMENT,
+ OBJECT_ENVIRONMENT,
+ MODULE_ENVIRONMENT,
+ };
+
+ typedef std::vector<ScopeType> LexicalScopeChainVector;
+
+ class ESCARGOT_EXPORT BreakpointOperations {
+ friend class DebuggerC;
+
+ public:
+ WeakCodeRef* weakCodeRef()
+ {
+ return m_weakCodeRef;
+ }
+
+ ExecutionStateRef* executionState()
+ {
+ return m_executionState;
+ }
+
+ uint32_t offset()
+ {
+ return m_offset;
+ }
+
+ StringRef* eval(StringRef* sourceCode, bool& isError);
+ void getStackTrace(DebuggerStackTraceDataVector& outStackTrace);
+ void getLexicalScopeChain(uint32_t stateIndex, LexicalScopeChainVector& outLexicalScopeChain);
+
+ private:
+ BreakpointOperations(WeakCodeRef* weakCodeRef, ExecutionStateRef* executionState, uint32_t offset)
+ : m_weakCodeRef(weakCodeRef)
+ , m_executionState(executionState)
+ , m_offset(offset)
+ {
+ }
+
+ WeakCodeRef* m_weakCodeRef;
+ ExecutionStateRef* m_executionState;
+ uint32_t m_offset;
+ };
+
+ enum ResumeBreakpointOperation {
+ Continue,
+ Step,
+ Next,
+ Finish,
+ };
+
+ // Base class for debugger callbacks
+ class DebuggerClient {
+ public:
+ virtual void parseCompleted(StringRef* source, StringRef* srcName, std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>& breakpointLocationsVector) = 0;
+ virtual void parseError(StringRef* source, StringRef* srcName, StringRef* error) = 0;
+ virtual void codeReleased(WeakCodeRef* weakCodeRef) = 0;
+
+ virtual ResumeBreakpointOperation stopAtBreakpoint(BreakpointOperations& operations) = 0;
+ };
+
+ static StringRef* getFunctionName(WeakCodeRef* weakCodeRef);
+ // Returns true, if the breakpoint status is changed from enabled to disabled or vica versa
+ static bool updateBreakpoint(WeakCodeRef* weakCodeRef, uint32_t offset, bool enable);
+};
+
class ESCARGOT_EXPORT ContextRef {
public:
static PersistentRefHolder<ContextRef> create(VMInstanceRef* vmInstance);
void throwException(ValueRef* exceptionValue); // if you use this function without Evaluator, your program will crash :(
- bool initDebugger(const char* options);
+ bool initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient);
+ // available options(separator is ';')
+ // "--port=6501", default for TCP debugger
+ bool initDebuggerRemote(const char* options);
bool isDebuggerRunning();
+ bool isWaitBeforeExit();
void printDebugger(StringRef* output);
+ void pumpDebuggerEvents();
+ void setAsAlwaysStopState();
StringRef* getClientSource(StringRef** sourceName);
typedef OptionalRef<ValueRef> (*VirtualIdentifierCallback)(ExecutionStateRef* state, ValueRef* name);
void operator delete[](void* obj) = delete;
private:
- friend class ObjectWithNamedPropertyHandler;
+ friend class ObjectWithPropertyHandler;
ObjectPropertyDescriptorRef(void* src);
void* m_privateData;
static FunctionObjectRef* create(ExecutionStateRef* state, NativeFunctionInfo info);
static FunctionObjectRef* createBuiltinFunction(ExecutionStateRef* state, NativeFunctionInfo info); // protoype of builtin function is non-writable
+
+ // dynamically create a function from source string
static FunctionObjectRef* create(ExecutionStateRef* state, AtomicStringRef* functionName, size_t argumentCount, ValueRef** argumentNameArray, ValueRef* body);
+ static FunctionObjectRef* create(ExecutionStateRef* state, StringRef* sourceName, AtomicStringRef* functionName, size_t argumentCount, ValueRef** argumentNameArray, ValueRef* body);
// get prototype property of constructible function(not [[prototype]])
// this property is used for new object construction. see https://www.ecma-international.org/ecma-262/6.0/#sec-ordinarycreatefromconstructor
bool isConstructor();
void markFunctionNeedsSlowVirtualIdentifierOperation();
+
+ // set function name is allowed only for native function or dynamically created function except class constructor
+ bool setName(AtomicStringRef* name);
};
class ESCARGOT_EXPORT IteratorObjectRef : public ObjectRef {
ObjectRef* then(ExecutionStateRef* state, ValueRef* onFulfilled, ValueRef* onRejected);
void fulfill(ExecutionStateRef* state, ValueRef* value);
void reject(ExecutionStateRef* state, ValueRef* reason);
+
+ bool hasResolveHandlers();
+ bool hasRejectHandlers();
};
class ESCARGOT_EXPORT ProxyObjectRef : public ObjectRef {
void* instanceExtraData();
};
-typedef OptionalRef<ValueRef> (*TemplateNamedPropertyHandlerGetterCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
+typedef OptionalRef<ValueRef> (*PropertyHandlerGetterCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
// if intercepted you may returns non-empty value.
// the returned value will be use futuer operation(you can return true, or false)
-typedef OptionalRef<ValueRef> (*TemplateNamedPropertyHandlerSetterCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName, ValueRef* value);
-enum TemplatePropertyAttribute {
- TemplatePropertyAttributeNotExist = 1 << 0,
- TemplatePropertyAttributeExist = 1 << 1,
- TemplatePropertyAttributeWritable = 1 << 2,
- TemplatePropertyAttributeEnumerable = 1 << 3,
- TemplatePropertyAttributeConfigurable = 1 << 4,
-};
-typedef TemplatePropertyAttribute (*TemplateNamedPropertyHandlerQueryCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
+typedef OptionalRef<ValueRef> (*PropertyHandlerSetterCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName, ValueRef* value);
+enum class ObjectTemplatePropertyAttribute : uint8_t {
+ PropertyAttributeNotExist = 1 << 0,
+ PropertyAttributeExist = 1 << 1,
+ PropertyAttributeWritable = 1 << 2,
+ PropertyAttributeEnumerable = 1 << 3,
+ PropertyAttributeConfigurable = 1 << 4,
+};
+inline bool operator&(ObjectTemplatePropertyAttribute a, ObjectTemplatePropertyAttribute b)
+{
+ return static_cast<uint8_t>(a) & static_cast<uint8_t>(b);
+}
+inline ObjectTemplatePropertyAttribute operator|(ObjectTemplatePropertyAttribute a, ObjectTemplatePropertyAttribute b)
+{
+ return static_cast<ObjectTemplatePropertyAttribute>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
+}
+typedef ObjectTemplatePropertyAttribute (*PropertyHandlerQueryCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
// if intercepted you may returns non-empty value.
// the returned value will be use futuer operation(you can return true, or false)
-typedef OptionalRef<ValueRef> (*TemplateNamedPropertyHandlerDeleteCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
-typedef ValueVectorRef* (*TemplateNamedPropertyHandlerEnumerationCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data);
+typedef OptionalRef<ValueRef> (*PropertyHandlerDeleteCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
+typedef ValueVectorRef* (*PropertyHandlerEnumerationCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data);
// if intercepted you may returns non-empty value.
// the returned value will be use futuer operation(you can return true, or false)
-typedef OptionalRef<ValueRef> (*TemplateNamedPropertyHandlerDefineOwnPropertyCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName, const ObjectPropertyDescriptorRef& desc);
-typedef OptionalRef<ValueRef> (*TemplateNamedPropertyHandlerGetPropertyDescriptorCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
-
-struct ESCARGOT_EXPORT ObjectTemplateNamedPropertyHandlerData {
- TemplateNamedPropertyHandlerGetterCallback getter;
- TemplateNamedPropertyHandlerSetterCallback setter;
- TemplateNamedPropertyHandlerQueryCallback query;
- TemplateNamedPropertyHandlerDeleteCallback deleter;
- TemplateNamedPropertyHandlerEnumerationCallback enumerator;
- TemplateNamedPropertyHandlerDefineOwnPropertyCallback definer;
- TemplateNamedPropertyHandlerGetPropertyDescriptorCallback descriptor;
- void* data;
-
- ObjectTemplateNamedPropertyHandlerData(
- TemplateNamedPropertyHandlerGetterCallback getter = nullptr,
- TemplateNamedPropertyHandlerSetterCallback setter = nullptr,
- TemplateNamedPropertyHandlerQueryCallback query = nullptr,
- TemplateNamedPropertyHandlerDeleteCallback deleter = nullptr,
- TemplateNamedPropertyHandlerEnumerationCallback enumerator = nullptr,
- TemplateNamedPropertyHandlerDefineOwnPropertyCallback definer = nullptr,
- TemplateNamedPropertyHandlerGetPropertyDescriptorCallback descriptor = nullptr,
+typedef OptionalRef<ValueRef> (*PropertyHandlerDefineOwnPropertyCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName, const ObjectPropertyDescriptorRef& desc);
+typedef OptionalRef<ValueRef> (*PropertyHandlerGetPropertyDescriptorCallback)(ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, ValueRef* propertyName);
+
+struct ESCARGOT_EXPORT ObjectTemplatePropertyHandlerConfiguration {
+ PropertyHandlerGetterCallback getter;
+ PropertyHandlerSetterCallback setter;
+ PropertyHandlerQueryCallback query;
+ PropertyHandlerDeleteCallback deleter;
+ PropertyHandlerEnumerationCallback enumerator;
+ PropertyHandlerDefineOwnPropertyCallback definer;
+ PropertyHandlerGetPropertyDescriptorCallback descriptor;
+ void* data; // this data member is guaranteed to be kept in GC heap
+
+ ObjectTemplatePropertyHandlerConfiguration(
+ PropertyHandlerGetterCallback getter = nullptr,
+ PropertyHandlerSetterCallback setter = nullptr,
+ PropertyHandlerQueryCallback query = nullptr,
+ PropertyHandlerDeleteCallback deleter = nullptr,
+ PropertyHandlerEnumerationCallback enumerator = nullptr,
+ PropertyHandlerDefineOwnPropertyCallback definer = nullptr,
+ PropertyHandlerGetPropertyDescriptorCallback descriptor = nullptr,
void* data = nullptr)
: getter(getter)
, setter(setter)
class ESCARGOT_EXPORT ObjectTemplateRef : public TemplateRef {
public:
static ObjectTemplateRef* create();
- void setNamedPropertyHandler(const ObjectTemplateNamedPropertyHandlerData& data);
+ void setNamedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data);
+ void setIndexedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data);
void removeNamedPropertyHandler();
+ void removeIndexedPropertyHandler();
// returns function template if object template is instance template of function template
OptionalRef<FunctionTemplateRef> constructor();
ObjectRef* moduleNamespace(ExecutionStateRef* state);
bool wasThereErrorOnModuleEvaluation();
ValueRef* moduleEvaluationError();
+ enum ModuleStatus {
+ Uninstantiated,
+ Instantiating,
+ Instantiated,
+ Evaluating,
+ Evaluated,
+ Errored
+ };
+ ModuleStatus moduleStatus();
+ ValueRef* moduleInstantiate(ExecutionStateRef* state);
+ ValueRef* moduleEvaluate(ExecutionStateRef* state);
};
class ESCARGOT_EXPORT PlatformRef {
// do nothing
}
-#ifdef ESCARGOT_USE_CUSTOM_LOGGING
+ // you can use these functions only if you enabled custom logging
+ static bool isCustomLoggingEnabled();
// default custom logger
virtual void customInfoLogger(const char* format, va_list arg)
{
{
vfprintf(stderr, format, arg);
}
-#endif
void* threadLocalCustomData();
};
-#if defined(ENABLE_WASM)
class ESCARGOT_EXPORT WASMOperationsRef {
public:
+ // you can use these functions only if you enabled WASM
+ static bool isWASMOperationsEnabled();
static ValueRef* copyStableBufferBytes(ExecutionStateRef* state, ValueRef* source);
static ObjectRef* asyncCompileModule(ExecutionStateRef* state, ValueRef* source);
static ObjectRef* instantiatePromiseOfModuleWithImportObject(ExecutionStateRef* state, PromiseObjectRef* promiseOfModule, ValueRef* importObj);
static void collectHeap();
};
-#endif
} // namespace Escargot
int64_t index;
Data* e = (Data*)data;
int64_t* ret = &e->ret;
- Value key = name.toPlainValue(state);
+ Value key = name.toPlainValue();
index = key.toNumber(state);
if ((uint64_t)index != Value::InvalidIndexValue) {
if (self->get(state, name).value(state, self).isUndefined()) {
{
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, true, false);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, true, false);
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
if (!newTarget.hasValue()) {
{
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, true, false);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, true, false);
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
if (!newTarget.hasValue()) {
return (static_cast<size_t>(accessIndex) * elementSize) + offset;
}
-template <typename T, typename ArgType = int64_t>
-static T atomicOperation(volatile uint8_t* rawStart, ArgType value, AtomicBinaryOps op)
+template <typename T>
+static T atomicOperation(volatile uint8_t* rawStart, int64_t v, AtomicBinaryOps op)
{
- T returnValue;
+ T returnValue = 0;
+ T value = static_cast<T>(v);
#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
volatile T* ptr = reinterpret_cast<volatile T*>(rawStart);
switch (op) {
returnValue = __atomic_fetch_and(ptr, value, __ATOMIC_SEQ_CST);
break;
case AtomicBinaryOps::EXCH: {
- T v = value;
- __atomic_exchange(ptr, &v, &returnValue, __ATOMIC_SEQ_CST);
+ __atomic_exchange(ptr, &value, &returnValue, __ATOMIC_SEQ_CST);
} break;
case AtomicBinaryOps::OR:
returnValue = __atomic_fetch_or(ptr, value, __ATOMIC_SEQ_CST);
ASSERT(indexedPosition + elemSize <= buffer->byteLength());
uint8_t* rawStart = const_cast<uint8_t*>(buffer->data()) + indexedPosition;
- if (v2.isInt32()) {
- switch (type) {
- case TypedArrayType::Int8:
- return Value(atomicOperation<int8_t>(rawStart, v2.asInt32(), op));
- case TypedArrayType::Int16:
- return Value(atomicOperation<int16_t>(rawStart, v2.asInt32(), op));
- case TypedArrayType::Int32:
- return Value(atomicOperation<int32_t>(rawStart, v2.asInt32(), op));
- case TypedArrayType::Uint8:
- return Value(atomicOperation<uint8_t>(rawStart, v2.asInt32(), op));
- case TypedArrayType::Uint16:
- return Value(atomicOperation<uint16_t>(rawStart, v2.asInt32(), op));
- case TypedArrayType::Uint32:
- return Value(atomicOperation<uint32_t>(rawStart, v2.asInt32(), op));
- default:
- ASSERT(TypedArrayType::Uint8Clamped == type);
- return Value(atomicOperation<uint8_t>(rawStart, v2.asInt32(), op));
- }
- }
-
switch (type) {
case TypedArrayType::Int8:
- return Value(atomicOperation<int8_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<int8_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Int16:
- return Value(atomicOperation<int16_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<int16_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Int32:
- return Value(atomicOperation<int32_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<int32_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Uint8:
- return Value(atomicOperation<uint8_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<uint8_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Uint16:
- return Value(atomicOperation<uint16_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<uint16_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Uint32:
- return Value(atomicOperation<uint32_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<uint32_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::Uint8Clamped:
- return Value(atomicOperation<uint8_t, int64_t>(rawStart, v2.asNumber(), op));
+ return Value(atomicOperation<uint8_t>(rawStart, v2.asNumber(), op));
case TypedArrayType::BigInt64:
- return new BigInt(atomicOperation<int64_t, int64_t>(rawStart, v2.asBigInt()->toInt64(), op));
+ return new BigInt(atomicOperation<int64_t>(rawStart, v2.asBigInt()->toInt64(), op));
default:
ASSERT(TypedArrayType::BigUint64 == type);
- return new BigInt(atomicOperation<uint64_t, uint64_t>(rawStart, v2.asBigInt()->toUint64(), op));
+ return new BigInt(atomicOperation<uint64_t>(rawStart, v2.asBigInt()->toUint64(), op));
}
}
return atomicReadModifyWrite(state, argv[0], argv[1], argv[2], AtomicBinaryOps::EXCH);
}
-#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
-template <typename T>
-void atomicLoad(uint8_t* rawStart, uint8_t* rawBytes)
-{
- __atomic_load(reinterpret_cast<T*>(rawStart), reinterpret_cast<T*>(rawBytes), __ATOMIC_SEQ_CST);
-}
-#endif
-
static Value builtinAtomicsLoad(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
ArrayBuffer* buffer = validateIntegerTypedArray(state, argv[0]);
size_t indexedPosition = validateAtomicAccess(state, TA, argv[1]);
TypedArrayType type = TA->typedArrayType();
-#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
- uint8_t* rawStart = buffer->data() + indexedPosition;
- uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
- switch (type) {
- case TypedArrayType::Int8:
- atomicLoad<int8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Int16:
- atomicLoad<int16_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Int32:
- atomicLoad<int32_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint8:
- atomicLoad<uint8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint16:
- atomicLoad<uint16_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint32:
- atomicLoad<uint32_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint8Clamped:
- atomicLoad<uint8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::BigInt64:
- atomicLoad<int64_t>(rawStart, rawBytes);
- break;
- default:
- ASSERT(TypedArrayType::BigUint64 == type);
- atomicLoad<uint64_t>(rawStart, rawBytes);
- break;
- }
- return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes);
-#else
- std::lock_guard<SpinLock> guard(Global::atomicsLock());
return buffer->getValueFromBuffer(state, indexedPosition, type);
-#endif
}
static Value builtinAtomicsOr(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
return atomicReadModifyWrite(state, argv[0], argv[1], argv[2], AtomicBinaryOps::OR);
}
-#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
-template <typename T>
-void atomicStore(uint8_t* rawStart, uint8_t* rawBytes)
-{
- __atomic_store(reinterpret_cast<T*>(rawStart), reinterpret_cast<T*>(rawBytes), __ATOMIC_SEQ_CST);
-}
-#endif
-
static Value builtinAtomicsStore(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
ArrayBuffer* buffer = validateIntegerTypedArray(state, argv[0]);
v = Value(value.toInteger(state));
}
-#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
- uint8_t* rawStart = buffer->data() + indexedPosition;
- uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
- TypedArrayHelper::numberToRawBytes(state, type, v, rawBytes);
-
- switch (type) {
- case TypedArrayType::Int8:
- atomicStore<int8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Int16:
- atomicStore<int16_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Int32:
- atomicStore<int32_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint8:
- atomicStore<uint8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint16:
- atomicStore<uint16_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint32:
- atomicStore<uint32_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::Uint8Clamped:
- atomicStore<uint8_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::BigInt64:
- atomicStore<int64_t>(rawStart, rawBytes);
- break;
- default:
- ASSERT(TypedArrayType::BigUint64 == type);
- atomicStore<uint64_t>(rawStart, rawBytes);
- break;
- }
- return v;
-#else
- std::lock_guard<SpinLock> guard(Global::atomicsLock());
buffer->setValueInBuffer(state, indexedPosition, type, v);
return v;
-#endif
}
static Value builtinAtomicsSub(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
WL->m_mutex.lock();
// 14. Let elementType be the Element Type value in Table 63 for arrayTypeName.
// 15. Let w be ! GetValueFromBuffer(buffer, indexedPosition, elementType, true, SeqCst).
+ ASSERT(arrayTypeName == TypedArrayType::Int32 || arrayTypeName == TypedArrayType::BigInt64);
Value w(Value::ForceUninitialized);
-#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
- uint8_t* rawStart = buffer->data() + indexedPosition;
- uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
- switch (arrayTypeName) {
- case TypedArrayType::Int32:
- atomicLoad<int32_t>(rawStart, rawBytes);
- break;
- case TypedArrayType::BigInt64:
- atomicLoad<int64_t>(rawStart, rawBytes);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- w = TypedArrayHelper::rawBytesToNumber(state, arrayTypeName, rawBytes);
-#else
- {
- std::lock_guard<SpinLock> guard(Global::atomicsLock());
- w = buffer->getValueFromBuffer(state, indexedPosition, arrayTypeName);
- }
-#endif
+ w = buffer->getValueFromBuffer(state, indexedPosition, arrayTypeName);
// 16. If v ≠ w, then
if (!v.equalsTo(state, w)) {
// a. Perform LeaveCriticalSection(WL).
static Value builtinAtomicsIsLockFree(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
- auto size = argv[0].toInteger(state);
+ const auto size = argv[0].toInteger(state);
#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
if (size == 1 || size == 2 || size == 4 || size == 8) {
return Value(true);
{
RESOLVE_THIS_BINDING_TO_BIGINT(thisObject, BigInt, toLocaleString);
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL_NUMBERFORMAT)
Value locales = argc > 0 ? argv[0] : Value();
Value options = argc > 1 ? argv[1] : Value();
Object* numberFormat = IntlNumberFormat::create(state, state.context(), locales, options);
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, false, false);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, false, false);
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
if (!newTarget.hasValue()) {
return constructorRealm->globalObject()->functionPrototype();
});
- ScriptFunctionObject* result = new ScriptFunctionObject(state, proto, functionSource.codeBlock, functionSource.outerEnvironment, true, false, false);
+ ScriptFunctionObject* result = new ScriptFunctionObject(state, proto, functionSource.codeBlock, functionSource.outerEnvironment, true, false);
return result;
}
{
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, false, false);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, false, false);
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
if (!newTarget.hasValue()) {
return Intl::supportedLocales(state, availableLocales, requestedLocales, options);
}
+#if defined(ENABLE_INTL_NUMBERFORMAT)
static Value builtinIntlNumberFormatFormat(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
FunctionObject* callee = state.resolveCallee();
// Return the result of calling the SupportedLocales abstract operation (defined in 9.2.8) with arguments availableLocales, requestedLocales, and options.
return Intl::supportedLocales(state, availableLocales, requestedLocales, options);
}
+#endif
+#if defined(ENABLE_INTL_PLURALRULES)
static Value builtinIntlPluralRulesConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// If NewTarget is undefined, throw a TypeError exception.
// Return the result of calling the SupportedLocales abstract operation (defined in 9.2.8) with arguments availableLocales, requestedLocales, and options.
return Intl::supportedLocales(state, availableLocales, requestedLocales, options);
}
+#endif
static Value builtinIntlLocaleConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
return thisValue.asObject()->asIntlLocaleObject()->timeZones(state);
}
+#if defined(ENABLE_INTL_RELATIVETIMEFORMAT)
static Value builtinIntlRelativeTimeFormatConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// If NewTarget is undefined, throw a TypeError exception.
// Return ? FormatRelativeTimeToParts(relativeTimeFormat, value, unit).
return thisValue.asObject()->asIntlRelativeTimeFormatObject()->formatToParts(state, value, unit);
}
+#endif
+#if defined(ENABLE_INTL_DISPLAYNAMES)
static Value builtinIntlDisplayNamesConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// https://402.ecma-international.org/8.0/#sec-Intl.DisplayNames
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(staticStrings.lazyLanguageDisplay()), ObjectPropertyDescriptor(r->languageDisplay(), ObjectPropertyDescriptor::AllPresent));
return options;
}
+#endif
+#if defined(ENABLE_INTL_LISTFORMAT)
static Value builtinIntlListFormatConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// If NewTarget is undefined, throw a TypeError exception.
IntlListFormatObject* r = thisValue.asObject()->asIntlListFormatObject();
return r->formatToParts(state, argv[0]);
}
+#endif
static Value builtinIntlGetCanonicalLocales(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
m_intlDateTimeFormat->defineOwnProperty(state, state.context()->staticStrings().supportedLocalesOf,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->supportedLocalesOf, builtinIntlDateTimeFormatSupportedLocalesOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
+#if defined(ENABLE_INTL_NUMBERFORMAT)
m_intlNumberFormat = new NativeFunctionObject(state, NativeFunctionInfo(strings->NumberFormat, builtinIntlNumberFormatConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlNumberFormat->setGlobalIntrinsicObject(state);
m_intlNumberFormatPrototype = m_intlNumberFormat->getFunctionPrototype(state).asObject();
m_intlNumberFormat->defineOwnProperty(state, state.context()->staticStrings().supportedLocalesOf,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->supportedLocalesOf, builtinIntlNumberFormatSupportedLocalesOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
+#endif
+#if defined(ENABLE_INTL_PLURALRULES)
m_intlPluralRules = new NativeFunctionObject(state, NativeFunctionInfo(strings->PluralRules, builtinIntlPluralRulesConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlPluralRules->setGlobalIntrinsicObject(state);
m_intlPluralRules->defineOwnProperty(state, state.context()->staticStrings().supportedLocalesOf,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->supportedLocalesOf, builtinIntlPluralRulesSupportedLocalesOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
-
+#endif
m_intlLocale = new NativeFunctionObject(state, NativeFunctionInfo(strings->Locale, builtinIntlLocaleConstructor, 1), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlLocale->setGlobalIntrinsicObject(state);
m_intlLocalePrototype->defineOwnProperty(state, ObjectPropertyName(state, strings->lazyTimeZones()), desc);
}
+#if defined(ENABLE_INTL_RELATIVETIMEFORMAT)
m_intlRelativeTimeFormat = new NativeFunctionObject(state, NativeFunctionInfo(strings->RelativeTimeFormat, builtinIntlRelativeTimeFormatConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlRelativeTimeFormat->setGlobalIntrinsicObject(state);
m_intlRelativeTimeFormat->defineOwnProperty(state, state.context()->staticStrings().supportedLocalesOf,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->supportedLocalesOf, builtinIntlRelativeTimeFormatSupportedLocalesOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
+#endif
+#if defined(ENABLE_INTL_DISPLAYNAMES)
m_intlDisplayNames = new NativeFunctionObject(state, NativeFunctionInfo(strings->DisplayNames, builtinIntlDisplayNamesConstructor, 2), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlDisplayNames->setGlobalIntrinsicObject(state);
m_intlDisplayNamesPrototype->defineOwnProperty(state, state.context()->staticStrings().resolvedOptions,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->resolvedOptions, builtinIntlDisplayNamesResolvedOptions, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
+#endif
+#if defined(ENABLE_INTL_LISTFORMAT)
m_intlListFormat = new NativeFunctionObject(state, NativeFunctionInfo(strings->ListFormat, builtinIntlListFormatConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__);
m_intlListFormat->setGlobalIntrinsicObject(state);
m_intl->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag),
ObjectPropertyDescriptor(Value(state.context()->staticStrings().Intl.string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
+#endif
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->Collator),
ObjectPropertyDescriptor(m_intlCollator, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->DateTimeFormat),
ObjectPropertyDescriptor(m_intlDateTimeFormat, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#if defined(ENABLE_INTL_NUMBERFORMAT)
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->NumberFormat),
ObjectPropertyDescriptor(m_intlNumberFormat, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#endif
+#if defined(ENABLE_INTL_PLURALRULES)
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->PluralRules),
ObjectPropertyDescriptor(m_intlPluralRules, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#endif
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->Locale),
ObjectPropertyDescriptor(m_intlLocale, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#if defined(ENABLE_INTL_RELATIVETIMEFORMAT)
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->RelativeTimeFormat),
ObjectPropertyDescriptor(m_intlRelativeTimeFormat, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#endif
+#if defined(ENABLE_INTL_DISPLAYNAMES)
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->DisplayNames),
ObjectPropertyDescriptor(m_intlDisplayNames, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#endif
+#if defined(ENABLE_INTL_LISTFORMAT)
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->ListFormat),
ObjectPropertyDescriptor(m_intlListFormat, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
-
+#endif
FunctionObject* getCanonicalLocales = new NativeFunctionObject(state, NativeFunctionInfo(strings->getCanonicalLocales, builtinIntlGetCanonicalLocales, 1, NativeFunctionInfo::Strict));
m_intl->defineOwnProperty(state, ObjectPropertyName(strings->getCanonicalLocales),
ObjectPropertyDescriptor(getCanonicalLocales, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
const char16_t* chars = (const char16_t*)value.GetString();
unsigned len = value.GetStringLength();
if (isAllLatin1(chars, len)) {
- return new Latin1String(chars, len);
+ return String::fromLatin1(chars, len);
} else {
return new UTF16String(chars, len);
}
} else {
const char* valueAsString = (const char*)value.GetString();
- if (isAllASCII(valueAsString, strlen(valueAsString))) {
- return new ASCIIString(valueAsString);
+ size_t len = strlen(valueAsString);
+ if (isAllASCII(valueAsString, len)) {
+ return String::fromASCII(valueAsString, len);
} else {
- return new UTF16String(utf8StringToUTF16String(valueAsString, strlen(valueAsString)));
+ return new UTF16String(utf8StringToUTF16String(valueAsString, len));
}
}
} else if (value.IsArray()) {
}
}
}
- Value arguments[] = { name.toPlainValue(state), val };
+ Value arguments[] = { name.toPlainValue(), val };
return Object::call(state, reviver, holder, 2, arguments);
};
return Walk(root, ObjectPropertyName(state, String::emptyString));
std::vector<Value::ValueIndex> indexes;
arrObject->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& P, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
- Value::ValueIndex idx = P.toPlainValue(state).toNumber(state);
+ Value::ValueIndex idx = P.toPlainValue().toNumber(state);
if (idx != Value::InvalidIndexValue) {
std::vector<Value::ValueIndex>* indexes = (std::vector<Value::ValueIndex>*)data;
indexes->push_back(idx);
char buffer[NUMBER_TO_STRING_BUFFER_LENGTH];
double_conversion::StringBuilder builder(buffer, NUMBER_TO_STRING_BUFFER_LENGTH);
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToFixed(number, digit, &builder);
- return Value(new ASCIIString(builder.Finalize()));
+ auto len = builder.position();
+ return Value(String::fromASCII(builder.Finalize(), len));
}
static Value builtinNumberToExponential(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
} else {
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(number, digit, &builder);
}
- return Value(new ASCIIString(builder.Finalize()));
+ auto len = builder.position();
+ return Value(String::fromASCII(builder.Finalize(), len));
}
static Value builtinNumberToPrecision(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
char buffer[NUMBER_TO_STRING_BUFFER_LENGTH];
double_conversion::StringBuilder builder(buffer, NUMBER_TO_STRING_BUFFER_LENGTH);
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(number, p, &builder);
- return Value(new ASCIIString(builder.Finalize()));
+ auto len = builder.position();
+ return Value(String::fromASCII(builder.Finalize(), len));
}
static Value builtinNumberToString(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
} else {
itoa(static_cast<int64_t>(number), buffer, radix);
}
- return new ASCIIString(buffer);
+ return String::fromASCII(buffer, strlen(buffer));
} else {
ASSERT(Value(number).isDouble());
NumberObject::RadixBuffer s;
const char* str = NumberObject::toStringWithRadix(state, s, number, radix);
- return new ASCIIString(str);
+ return String::fromASCII(str, strlen(str));
}
}
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::GlobalObject_ThisNotNumber);
}
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL_NUMBERFORMAT)
Value locales = argc > 0 ? argv[0] : Value();
Value options = argc > 1 ? argv[1] : Value();
Object* numberFormat = IntlNumberFormat::create(state, state.context(), locales, options);
int e = result.m_matchResults[0][0].m_end;
if (option & RegExpObject::Option::Unicode) {
char16_t utfRes = str->charAt(e);
- size_t eUTF = str->find(new ASCIIString((const char*)&utfRes), 0);
+ const char* buf = reinterpret_cast<const char*>(&utfRes);
+ size_t len = strlen(buf);
+ size_t eUTF = str->find(buf, len, 0);
if (eUTF >= str->length()) {
e = str->length();
} else if ((int)eUTF > e || e == (int)str->length()) {
return Value(obj->isResizableArrayBuffer());
}
+static Value builtinSharedArrayBufferGrow(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
+{
+ RESOLVE_THIS_BINDING_TO_SHAREDARRAYBUFFER(O, SharedArrayBuffer, grow);
+
+ if (!O->isResizableArrayBuffer()) {
+ ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, state.context()->staticStrings().SharedArrayBuffer.string(), true, state.context()->staticStrings().grow.string(), "SharedArrayBuffer is not a growable buffer");
+ }
+
+ // Let newByteLength to ? ToIntegerOrInfinity(newLength).
+ auto newByteLength = argv[0].toInteger(state);
+
+ if ((newByteLength < O->byteLength()) || (newByteLength > O->maxByteLength())) {
+ ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), true, state.context()->staticStrings().grow.string(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
+ }
+
+ O->backingStore()->resize(static_cast<size_t>(newByteLength));
+ return Value();
+}
+
// https://262.ecma-international.org/#sec-sharedarraybuffer.prototype.slice
static Value builtinSharedArrayBufferSlice(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
m_sharedArrayBufferPrototype->defineOwnProperty(state, ObjectPropertyName(strings->growable), growableDesc);
}
+ m_sharedArrayBufferPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->grow),
+ ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->grow, builtinSharedArrayBufferGrow, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_sharedArrayBufferPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->slice),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->slice, builtinSharedArrayBufferSlice, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
ASSERT(from <= to);
if (to - from == 1) {
char16_t c = str->charAt(from);
- if (c < ESCARGOT_ASCII_TABLE_MAX) {
- return state.context()->staticStrings().asciiTable[c].string();
- }
+ return state.context()->staticStrings().charCodeToString(c);
}
return str->substring(from, to);
}
RESOLVE_THIS_BINDING_TO_STRING(str, String, charCodeAt);
int position = argv[0].toInteger(state);
Value ret;
- const auto& data = str->bufferAccessData();
- if (position < 0 || position >= (int)data.length)
+ size_t length = str->length();
+ if (position < 0 || position >= (int)length)
ret = Value(std::numeric_limits<double>::quiet_NaN());
else {
- char16_t c;
- if (data.has8BitContent) {
- c = ((LChar*)data.buffer)[position];
- } else {
- c = ((char16_t*)data.buffer)[position];
- }
- ret = Value(c);
+ ret = Value(str->charAt(position));
}
return ret;
}
RESOLVE_THIS_BINDING_TO_STRING(str, String, codePointAt);
int position = argv[0].toInteger(state);
Value ret;
- const auto& data = str->bufferAccessData();
- const int size = (int)data.length;
+ size_t length = str->length();
+ const int size = (int)length;
if (position < 0 || position >= size)
return Value();
- char16_t first;
- if (data.has8BitContent) {
- first = ((LChar*)data.buffer)[position];
- } else {
- first = ((char16_t*)data.buffer)[position];
- }
-
+ char16_t first = str->charAt(position);
if (first < 0xD800 || first > 0xDBFF || (position + 1) == size) {
return Value(first);
}
- char16_t second;
- if (data.has8BitContent) {
- second = ((LChar*)data.buffer)[position + 1];
- } else {
- second = ((char16_t*)data.buffer)[position + 1];
- }
-
+ char16_t second = str->charAt(position + 1);
if (second < 0xDC00 || second > 0xDFFF) {
return Value(first);
}
position = argv[0].toInteger(state);
}
- const auto& accessData = str->bufferAccessData();
-
- if (LIKELY(0 <= position && position < (int64_t)accessData.length)) {
- char16_t c;
- if (accessData.has8BitContent) {
- c = ((LChar*)accessData.buffer)[position];
- } else {
- c = ((char16_t*)accessData.buffer)[position];
- }
- if (LIKELY(c < ESCARGOT_ASCII_TABLE_MAX)) {
- return state.context()->staticStrings().asciiTable[c].string();
- } else {
- return String::fromCharCode(c);
- }
+ const auto length = str->length();
+ if (LIKELY(0 <= position && position < (int64_t)length)) {
+ char16_t c = str->charAt(position);
+ return state.context()->staticStrings().charCodeToString(c);
} else {
return String::emptyString;
}
{
if (argc == 1) {
char16_t c = argv[0].toUint32(state) & 0xFFFF;
- if (c < ESCARGOT_ASCII_TABLE_MAX)
- return state.context()->staticStrings().asciiTable[c].string();
- return String::fromCharCode(c);
+ return state.context()->staticStrings().charCodeToString(c);
}
StringBuilder builder;
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, toLowerCase);
if (str->has8BitContent()) {
- Latin1StringData newStr;
size_t len = str->length();
- newStr.resizeWithUninitializedValues(len);
- const LChar* buf = str->characters8();
+ const LChar* from = str->characters8();
+ LChar* dest;
+ Latin1StringData newStr;
+ if (len <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
+ dest = static_cast<LChar*>(alloca(len));
+ } else {
+ newStr.resizeWithUninitializedValues(len);
+ dest = newStr.data();
+ }
+
for (size_t i = 0; i < len; i++) {
#if defined(ENABLE_ICU)
- char32_t u2 = u_tolower(buf[i]);
+ char32_t u2 = u_tolower(from[i]);
#else
- char32_t u2 = tolower(buf[i]);
+ char32_t u2 = tolower(from[i]);
#endif
ASSERT(u2 < 256);
- newStr[i] = u2;
+ dest[i] = u2;
+ }
+
+ if (len <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
+ return String::fromLatin1(dest, len);
+ } else {
+ return new Latin1String(std::move(newStr));
}
- return new Latin1String(std::move(newStr));
}
#if defined(ENABLE_ICU)
if (relativeStart < 0 || relativeStart >= len) {
return Value();
}
- return String::fromCharCode(str->charAt(relativeStart));
+ return state.context()->staticStrings().charCodeToString(str->charAt(relativeStart));
}
#define DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fnName, P0, P1, P2) \
#ifdef ESCARGOT_DEBUGGER
namespace Escargot {
-void Debugger::sendType(uint8_t type)
+void Debugger::enable(Context* context)
+{
+ ASSERT(m_context == nullptr);
+ m_context = context;
+ m_context->initDebugger(this);
+}
+
+void Debugger::disable()
+{
+ ASSERT(m_context != nullptr);
+ m_context->removeDebugger();
+ m_context = nullptr;
+}
+
+void DebuggerRemote::sendType(uint8_t type)
{
send(type, nullptr, 0);
}
-void Debugger::sendSubtype(uint8_t type, uint8_t subType)
+void DebuggerRemote::sendSubtype(uint8_t type, uint8_t subType)
{
send(type, &subType, 1);
}
-void Debugger::sendString(uint8_t type, String* string)
+void DebuggerRemote::sendString(uint8_t type, String* string)
{
size_t length = string->length();
send(type + 2 + 1, chars, length * 2);
}
-void Debugger::sendPointer(uint8_t type, const void* ptr)
+void DebuggerRemote::sendPointer(uint8_t type, const void* ptr)
{
// The pointer itself is sent, not the data pointed by it
send(type, (void*)&ptr, sizeof(void*));
}
-void Debugger::sendFunctionInfo(InterpretedCodeBlock* codeBlock)
+void DebuggerRemote::parseCompleted(String* source, String* srcName, String* error)
{
- char* byteCodeStart = codeBlock->byteCodeBlock()->m_code.data();
- uint32_t startLine = (uint32_t)codeBlock->functionStart().line;
- uint32_t startColumn = (uint32_t)(codeBlock->functionStart().column + 1);
- FunctionInfo functionInfo;
+ if (!enabled()) {
+ return;
+ }
- memcpy(&functionInfo.byteCodeStart, (void*)&byteCodeStart, sizeof(char*));
- memcpy(&functionInfo.startLine, &startLine, sizeof(uint32_t));
- memcpy(&functionInfo.startColumn, &startColumn, sizeof(uint32_t));
+ sendString(ESCARGOT_MESSAGE_SOURCE_8BIT, source);
- send(ESCARGOT_MESSAGE_FUNCTION_PTR, (void*)&functionInfo, sizeof(FunctionInfo));
-}
+ if (!enabled()) {
+ return;
+ }
-void Debugger::sendBreakpointLocations(std::vector<Debugger::BreakpointLocation>& locations)
-{
- const size_t maxPacketLength = (ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH - 1) / sizeof(BreakpointLocation);
- BreakpointLocation* ptr = locations.data();
- size_t length = locations.size();
+ sendString(ESCARGOT_MESSAGE_FILE_NAME_8BIT, srcName);
- while (length > maxPacketLength) {
- if (!send(ESCARGOT_MESSAGE_BREAKPOINT_LOCATION, ptr, maxPacketLength * sizeof(BreakpointLocation))) {
+ if (!enabled()) {
+ return;
+ }
+
+ if (error != nullptr) {
+ sendType(ESCARGOT_MESSAGE_PARSE_ERROR);
+
+ if (enabled()) {
+ sendString(ESCARGOT_MESSAGE_STRING_8BIT, error);
+ }
+ return;
+ }
+
+ size_t breakpointLocationsSize = m_breakpointLocationsVector.size();
+
+ for (size_t i = 0; i < breakpointLocationsSize; i++) {
+ /* Send breakpoint locations. */
+ const size_t maxPacketLength = (ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH - 1) / sizeof(BreakpointLocation);
+ BreakpointLocation* ptr = m_breakpointLocationsVector[i]->breakpointLocations.data();
+ size_t length = m_breakpointLocationsVector[i]->breakpointLocations.size();
+
+ while (length > maxPacketLength) {
+ if (!send(ESCARGOT_MESSAGE_BREAKPOINT_LOCATION, ptr, maxPacketLength * sizeof(BreakpointLocation))) {
+ return;
+ }
+ ptr += maxPacketLength;
+ length -= maxPacketLength;
+ }
+
+ if (!send(ESCARGOT_MESSAGE_BREAKPOINT_LOCATION, ptr, length * sizeof(BreakpointLocation))) {
+ return;
+ }
+
+ InterpretedCodeBlock* codeBlock = reinterpret_cast<InterpretedCodeBlock*>(m_breakpointLocationsVector[i]->weakCodeRef);
+ String* functionName = codeBlock->functionName().string();
+
+ /* Send function name. */
+ if (functionName->length() > 0) {
+ StringView* functionNameView = new StringView(functionName);
+ sendString(ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT, functionNameView);
+
+ if (!enabled()) {
+ return;
+ }
+ }
+
+ /* Send function info. */
+ char* byteCodeStart = codeBlock->byteCodeBlock()->m_code.data();
+ uint32_t startLine = (uint32_t)codeBlock->functionStart().line;
+ uint32_t startColumn = (uint32_t)(codeBlock->functionStart().column + 1);
+ FunctionInfo functionInfo;
+
+ memcpy(&functionInfo.byteCodeStart, (void*)&byteCodeStart, sizeof(char*));
+ memcpy(&functionInfo.startLine, &startLine, sizeof(uint32_t));
+ memcpy(&functionInfo.startColumn, &startColumn, sizeof(uint32_t));
+
+ if (!send(ESCARGOT_MESSAGE_FUNCTION_PTR, (void*)&functionInfo, sizeof(FunctionInfo))) {
return;
}
- ptr += maxPacketLength;
- length -= maxPacketLength;
}
- send(ESCARGOT_MESSAGE_BREAKPOINT_LOCATION, ptr, length * sizeof(BreakpointLocation));
+ sendType(ESCARGOT_MESSAGE_PARSE_DONE);
+
+ if (enabled() && pendingWait()) {
+ waitForResolvingPendingBreakpoints();
+ }
}
-void Debugger::sendBacktraceInfo(uint8_t type, ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column, uint32_t executionStateDepth)
+void DebuggerRemote::sendBacktraceInfo(uint8_t type, ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column, uint32_t executionStateDepth)
{
BacktraceInfo backtraceInfo;
send(type, &backtraceInfo, sizeof(BacktraceInfo));
}
-void Debugger::sendVariableObjectInfo(uint8_t subType, Object* object)
+void DebuggerRemote::sendVariableObjectInfo(uint8_t subType, Object* object)
{
/* Maximum UINT32_MAX number of objects are stored. */
uint32_t size = (uint32_t)m_activeObjects.size();
m_activeObjects.pushBack(object);
}
- Debugger::VariableObjectInfo variableObjectInfo;
+ VariableObjectInfo variableObjectInfo;
variableObjectInfo.subType = subType;
memcpy(&variableObjectInfo.index, &index, sizeof(uint32_t));
- send(Debugger::ESCARGOT_MESSAGE_VARIABLE, &variableObjectInfo, sizeof(VariableObjectInfo));
+ send(ESCARGOT_MESSAGE_VARIABLE, &variableObjectInfo, sizeof(VariableObjectInfo));
}
-void Debugger::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
+void DebuggerRemote::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
{
if (m_stopState == ESCARGOT_DEBUGGER_IN_EVAL_MODE) {
- m_delay = (uint8_t)(m_delay - 1);
+ m_delay--;
if (m_delay == 0) {
- processIncomingMessages(state, byteCodeBlock);
+ processEvents(state, byteCodeBlock);
}
return;
}
ASSERT(m_activeObjects.size() == 0);
m_stopState = ESCARGOT_DEBUGGER_IN_WAIT_MODE;
- while (processIncomingMessages(state, byteCodeBlock))
+ while (processEvents(state, byteCodeBlock))
;
m_activeObjects.clear();
m_delay = ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY;
}
-void Debugger::releaseFunction(const void* ptr)
+void DebuggerRemote::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock)
{
// All messages which involves this pointer should be ignored until the confirmation arrives.
- m_releasedFunctions.push_back(reinterpret_cast<uintptr_t>(ptr));
- sendPointer(ESCARGOT_MESSAGE_RELEASE_FUNCTION, ptr);
+ if (enabled()) {
+ m_releasedFunctions.push_back(reinterpret_cast<uintptr_t>(byteCodeBlock));
+ sendPointer(ESCARGOT_MESSAGE_RELEASE_FUNCTION, byteCodeBlock);
+ }
+}
+
+void DebuggerRemote::exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace)
+{
+ if (!enabled()) {
+ return;
+ }
+
+ sendType(ESCARGOT_MESSAGE_EXCEPTION);
+
+ if (!enabled()) {
+ return;
+ }
+
+ sendString(ESCARGOT_MESSAGE_STRING_8BIT, message);
+
+ size_t size = exceptionTrace.size();
+ for (size_t i = 0; i < size && enabled(); i++) {
+ sendBacktraceInfo(ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE, exceptionTrace[i].byteCodeBlock, exceptionTrace[i].line, exceptionTrace[i].column, UINT32_MAX);
+ }
}
-bool Debugger::doEval(ExecutionState* state, ByteCodeBlock* byteCodeBlock, uint8_t* buffer, size_t length)
+void DebuggerRemote::consoleOut(String* output)
+{
+ if (enabled()) {
+ sendType(ESCARGOT_MESSAGE_PRINT);
+
+ if (enabled()) {
+ sendString(ESCARGOT_MESSAGE_STRING_8BIT, output);
+ }
+ }
+}
+
+String* DebuggerRemote::getClientSource(String** sourceName)
+{
+ if (!enabled()) {
+ return nullptr;
+ }
+
+ sendType(ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE);
+ while (processEvents(nullptr, nullptr))
+ ;
+
+ if (sourceName) {
+ *sourceName = m_clientSourceName;
+ }
+ String* sourceData = m_clientSourceData;
+ m_clientSourceName = nullptr;
+ m_clientSourceData = nullptr;
+ return sourceData;
+}
+
+bool DebuggerRemote::getWaitBeforeExitClient()
+{
+ sendType(ESCARGOT_DEBUGGER_WAIT_FOR_WAIT_EXIT);
+ while (processEvents(nullptr, nullptr))
+ ;
+ return this->m_exitClient;
+}
+
+bool DebuggerRemote::doEval(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, uint8_t* buffer, size_t length)
{
uint8_t type = (uint8_t)(buffer[0] + 1);
uint32_t size;
}
String* str;
- if (type == ESCARGOT_MESSAGE_EVAL_8BIT) {
+ if (type == ESCARGOT_MESSAGE_EVAL_8BIT || type == ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT) {
str = new Latin1String(data, size);
- } else if (type == ESCARGOT_MESSAGE_EVAL_16BIT) {
+ } else if (type == ESCARGOT_MESSAGE_EVAL_16BIT || type == ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT) {
str = new UTF16String((char16_t*)data, size / 2);
} else if (type == ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT) {
char* sourceNameSrc = (char*)memchr(data, '\0', size);
return false;
}
- type = ESCARGOT_MESSAGE_EVAL_RESULT_8BIT;
m_stopState = ESCARGOT_DEBUGGER_IN_EVAL_MODE;
try {
Value asValue(str);
- Value result(state->context()->globalObject()->evalLocal(*state, asValue, Value(Value::Undefined), byteCodeBlock->m_codeBlock, true));
+ Value result(Value::ForceUninitialized);
+ if (type == ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT || type == ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT) {
+ result = state->context()->globalObject()->eval(*state, asValue);
+ } else {
+ result = state->context()->globalObject()->evalLocal(*state, asValue, state->thisValue(), byteCodeBlock->m_codeBlock, true);
+ }
+ type = ESCARGOT_MESSAGE_EVAL_RESULT_8BIT;
str = result.toStringWithoutException(*state);
} catch (const Value& val) {
type = ESCARGOT_MESSAGE_EVAL_FAILED_8BIT;
return false;
}
-void Debugger::getBacktrace(ExecutionState* state, uint32_t minDepth, uint32_t maxDepth, bool getTotal)
+void DebuggerRemote::getBacktrace(ExecutionState* state, uint32_t minDepth, uint32_t maxDepth, bool getTotal)
{
SandBox::StackTraceDataVector stackTraceData;
- bool hasSavedStackTrace = SandBox::createStackTraceData(stackTraceData, *state, true);
+ bool hasSavedStackTrace = SandBox::createStackTrace(stackTraceData, *state, true);
uint32_t size = (uint32_t)stackTraceData.size();
uint32_t total = 0;
for (uint32_t i = 0; i < size; i++) {
- if ((size_t)stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
+ if ((size_t)stackTraceData[i].loc.actualCodeBlock != SIZE_MAX) {
total++;
}
}
if (hasSavedStackTrace) {
- total += (uint32_t)m_activeSavedStackTrace->size();
+ total += (uint32_t)activeSavedStackTrace()->size();
}
if (getTotal && !send(ESCARGOT_MESSAGE_BACKTRACE_TOTAL, &total, sizeof(uint32_t))) {
uint32_t counter = 0;
for (uint32_t i = 0; i < size && counter < maxDepth; i++) {
- if ((size_t)stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
+ if ((size_t)stackTraceData[i].loc.actualCodeBlock != SIZE_MAX) {
if (++counter <= minDepth) {
continue;
}
- ByteCodeBlock* byteCodeBlock = stackTraceData[i].second.loc.actualCodeBlock;
+ ByteCodeBlock* byteCodeBlock = stackTraceData[i].loc.actualCodeBlock;
uint32_t line, column;
- if ((size_t)stackTraceData[i].second.loc.index == SIZE_MAX) {
- size_t byteCodePosition = stackTraceData[i].second.loc.byteCodePosition;
+ if ((size_t)stackTraceData[i].loc.index == SIZE_MAX) {
+ size_t byteCodePosition = stackTraceData[i].loc.byteCodePosition;
ByteCodeLOCData* locData;
auto iterMap = locMap.find(byteCodeBlock);
line = (uint32_t)loc.line;
column = (uint32_t)loc.column;
} else {
- line = (uint32_t)stackTraceData[i].second.loc.line;
- column = (uint32_t)stackTraceData[i].second.loc.column;
+ line = (uint32_t)stackTraceData[i].loc.line;
+ column = (uint32_t)stackTraceData[i].loc.column;
}
- sendBacktraceInfo(ESCARGOT_MESSAGE_BACKTRACE, byteCodeBlock, line, column, (uint32_t)stackTraceData[i].second.executionStateDepth);
+ sendBacktraceInfo(ESCARGOT_MESSAGE_BACKTRACE, byteCodeBlock, line, column, (uint32_t)stackTraceData[i].executionStateDepth);
if (!enabled()) {
return;
}
if (hasSavedStackTrace) {
- SavedStackTraceData* savedStackTracePtr = m_activeSavedStackTrace->begin();
- SavedStackTraceData* savedStackTraceEnd = m_activeSavedStackTrace->end();
+ SavedStackTraceData* savedStackTracePtr = activeSavedStackTrace()->begin();
+ SavedStackTraceData* savedStackTraceEnd = activeSavedStackTrace()->end();
while (counter < maxDepth && savedStackTracePtr < savedStackTraceEnd) {
if (++counter <= minDepth) {
sendType(ESCARGOT_MESSAGE_BACKTRACE_END);
}
-void Debugger::getScopeChain(ExecutionState* state, uint32_t stateIndex)
+void DebuggerRemote::getScopeChain(ExecutionState* state, uint32_t stateIndex)
{
const size_t maxMessageLength = ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH - 1;
uint8_t buffer[maxMessageLength];
send(ESCARGOT_MESSAGE_SCOPE_CHAIN_END, buffer, nextScope);
}
-static void sendProperty(Debugger* debugger, ExecutionState* state, AtomicString name, Value value)
+static void sendProperty(DebuggerRemote* debugger, ExecutionState* state, AtomicString name, Value value)
{
- uint8_t type = Debugger::ESCARGOT_VARIABLE_UNDEFINED;
+ uint8_t type = DebuggerRemote::ESCARGOT_VARIABLE_UNDEFINED;
StringView* valueView = nullptr;
if (value.isNull()) {
- type = Debugger::ESCARGOT_VARIABLE_NULL;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_NULL;
} else if (value.isTrue()) {
- type = Debugger::ESCARGOT_VARIABLE_TRUE;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_TRUE;
} else if (value.isFalse()) {
- type = Debugger::ESCARGOT_VARIABLE_FALSE;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_FALSE;
} else if (value.isNumber()) {
- type = Debugger::ESCARGOT_VARIABLE_NUMBER;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_NUMBER;
String* valueString = value.toString(*state);
valueView = new StringView(valueString);
} else if (value.isString() || value.isSymbol() || value.isBigInt()) {
String* valueString;
if (value.isString()) {
- type = Debugger::ESCARGOT_VARIABLE_STRING;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_STRING;
valueString = value.asString();
} else if (value.isBigInt()) {
- type = Debugger::ESCARGOT_VARIABLE_BIGINT;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_BIGINT;
valueString = value.asBigInt()->toString();
} else {
- type = Debugger::ESCARGOT_VARIABLE_SYMBOL;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_SYMBOL;
Symbol* symbol = value.asSymbol();
valueString = String::emptyString;
size_t valueLength = valueString->length();
if (valueLength >= ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH) {
- type |= Debugger::ESCARGOT_VARIABLE_LONG_VALUE;
+ type |= DebuggerRemote::ESCARGOT_VARIABLE_LONG_VALUE;
valueLength = ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH;
}
valueView = new StringView(valueString, 0, valueLength);
} else if (value.isFunction()) {
- type = Debugger::ESCARGOT_VARIABLE_FUNCTION;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_FUNCTION;
} else if (value.isObject()) {
- type = Debugger::ESCARGOT_VARIABLE_OBJECT;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_OBJECT;
Object* valueObject = value.asObject();
if (valueObject->isArrayObject()) {
- type = Debugger::ESCARGOT_VARIABLE_ARRAY;
+ type = DebuggerRemote::ESCARGOT_VARIABLE_ARRAY;
}
}
size_t nameLength = name.string()->length();
if (nameLength > ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH) {
- type |= Debugger::ESCARGOT_VARIABLE_LONG_NAME;
+ type |= DebuggerRemote::ESCARGOT_VARIABLE_LONG_NAME;
nameLength = ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH;
}
- if ((type & Debugger::ESCARGOT_VARIABLE_TYPE_MASK) < Debugger::ESCARGOT_VARIABLE_OBJECT) {
- debugger->sendSubtype(Debugger::ESCARGOT_MESSAGE_VARIABLE, type);
+ if ((type & DebuggerRemote::ESCARGOT_VARIABLE_TYPE_MASK) < DebuggerRemote::ESCARGOT_VARIABLE_OBJECT) {
+ debugger->sendSubtype(DebuggerRemote::ESCARGOT_MESSAGE_VARIABLE, type);
} else {
debugger->sendVariableObjectInfo(type, value.asObject());
}
- if (debugger->enabled()) {
+ if (debugger->connected()) {
StringView* nameView = new StringView(name.string(), 0, nameLength);
- debugger->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, nameView);
+ debugger->sendString(DebuggerRemote::ESCARGOT_MESSAGE_STRING_8BIT, nameView);
}
- if (valueView && debugger->enabled()) {
- debugger->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, valueView);
+ if (valueView && debugger->connected()) {
+ debugger->sendString(DebuggerRemote::ESCARGOT_MESSAGE_STRING_8BIT, valueView);
}
}
-static void sendUnaccessibleProperty(Debugger* debugger, AtomicString name)
+static void sendUnaccessibleProperty(DebuggerRemote* debugger, AtomicString name)
{
- uint8_t type = Debugger::ESCARGOT_VARIABLE_UNACCESSIBLE;
+ uint8_t type = DebuggerRemote::ESCARGOT_VARIABLE_UNACCESSIBLE;
size_t nameLength = name.string()->length();
if (nameLength > ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH) {
- type |= Debugger::ESCARGOT_VARIABLE_LONG_NAME;
+ type |= DebuggerRemote::ESCARGOT_VARIABLE_LONG_NAME;
nameLength = ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH;
}
- debugger->sendSubtype(Debugger::ESCARGOT_MESSAGE_VARIABLE, type);
+ debugger->sendSubtype(DebuggerRemote::ESCARGOT_MESSAGE_VARIABLE, type);
- if (debugger->enabled()) {
+ if (debugger->connected()) {
StringView* nameView = new StringView(name.string(), 0, nameLength);
- debugger->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, nameView);
+ debugger->sendString(DebuggerRemote::ESCARGOT_MESSAGE_STRING_8BIT, nameView);
}
}
-static void sendObjectProperties(Debugger* debugger, ExecutionState* state, Object* object)
+static void sendObjectProperties(DebuggerRemote* debugger, ExecutionState* state, Object* object)
{
Object::OwnPropertyKeyVector keys = object->ownPropertyKeys(*state);
size_t size = keys.size();
}
}
-static void sendRecordProperties(Debugger* debugger, ExecutionState* state, IdentifierRecordVector& identifiers, EnvironmentRecord* record)
+static void sendRecordProperties(DebuggerRemote* debugger, ExecutionState* state, IdentifierRecordVector& identifiers, EnvironmentRecord* record)
{
size_t size = identifiers.size();
}
}
-static void sendRecordProperties(Debugger* debugger, ExecutionState* state, const ModuleEnvironmentRecord::ModuleBindingRecordVector& bindings, ModuleEnvironmentRecord* record)
+static void sendRecordProperties(DebuggerRemote* debugger, ExecutionState* state, const ModuleEnvironmentRecord::ModuleBindingRecordVector& bindings, ModuleEnvironmentRecord* record)
{
size_t size = bindings.size();
}
}
-void Debugger::getScopeVariables(ExecutionState* state, uint32_t stateIndex, uint32_t index)
+void DebuggerRemote::getScopeVariables(ExecutionState* state, uint32_t stateIndex, uint32_t index)
{
while (stateIndex > 0) {
state = state->parent();
return nullptr;
}
-bool Debugger::processIncomingMessages(ExecutionState* state, ByteCodeBlock* byteCodeBlock)
+bool DebuggerRemote::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
{
uint8_t buffer[ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH];
size_t length;
- while (receive(buffer, length)) {
+ while (true) {
+ if (isBlockingRequest) {
+ if (!receive(buffer, length)) {
+ break;
+ }
+ } else {
+ if (isThereAnyEvent()) {
+ if (!receive(buffer, length)) {
+ break;
+ }
+ } else {
+ return false;
+ }
+ }
switch (buffer[0]) {
case ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START:
case ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START: {
- if ((length <= 1 + sizeof(uint32_t) || state != nullptr || byteCodeBlock != nullptr)) {
+ if ((length <= 1 + sizeof(uint32_t) || state != nullptr || byteCodeBlock)) {
break;
}
return doEval(state, byteCodeBlock, buffer, length);
}
case ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE: {
- if (length != 1 || state != nullptr || byteCodeBlock != nullptr) {
+ if (length != 1 || state != nullptr || byteCodeBlock) {
break;
}
return false;
}
+ case ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT: {
+ m_exitClient = buffer[1];
+ return false;
+ }
+
case ESCARGOT_MESSAGE_FUNCTION_RELEASED: {
if (length != 1 + sizeof(uintptr_t)) {
break;
if ((length <= 1 + sizeof(uint32_t)) || m_stopState != ESCARGOT_DEBUGGER_IN_WAIT_MODE) {
break;
}
-
+ ASSERT(byteCodeBlock.hasValue());
return doEval(state, byteCodeBlock, buffer, length);
}
+ case ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START:
+ case ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START: {
+ if ((length <= 1 + sizeof(uint32_t)) || (m_stopState != ESCARGOT_DEBUGGER_IN_WAIT_MODE && m_stopState != ESCARGOT_DEBUGGER_ALWAYS_STOP)) {
+ break;
+ }
+ ASSERT(!byteCodeBlock.hasValue());
+ return doEval(state, nullptr, buffer, length);
+ }
case ESCARGOT_MESSAGE_GET_BACKTRACE: {
if ((length != 1 + sizeof(uint32_t) + sizeof(uint32_t) + 1) || m_stopState != ESCARGOT_DEBUGGER_IN_WAIT_MODE) {
break;
return enabled();
}
-void Debugger::waitForResolvingPendingBreakpoints()
+void DebuggerRemote::waitForResolvingPendingBreakpoints()
{
m_waitForResume = true;
- sendType(Debugger::ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING);
+ sendType(ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING);
while (m_waitForResume) {
- processIncomingMessages(nullptr, nullptr);
+ processEvents(nullptr, nullptr);
if (!enabled()) {
break;
}
}
-Debugger::SavedStackTraceDataVector* Debugger::saveStackTrace(ExecutionState& state)
+DebuggerRemote::SavedStackTraceDataVector* Debugger::saveStackTrace(ExecutionState& state)
{
SavedStackTraceDataVector* savedStackTrace = new SavedStackTraceDataVector();
SandBox::StackTraceDataVector stackTraceData;
ByteCodeLOCDataMap locMap;
uint32_t counter = 0;
- bool hasSavedStackTrace = SandBox::createStackTraceData(stackTraceData, state, true);
+ bool hasSavedStackTrace = SandBox::createStackTrace(stackTraceData, state, true);
uint32_t total = (uint32_t)stackTraceData.size();
for (uint32_t i = 0; i < total && counter < ESCARGOT_DEBUGGER_MAX_STACK_TRACE_LENGTH; i++) {
- if ((size_t)stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
- ByteCodeBlock* byteCodeBlock = stackTraceData[i].second.loc.actualCodeBlock;
+ if ((size_t)stackTraceData[i].loc.actualCodeBlock != SIZE_MAX) {
+ ByteCodeBlock* byteCodeBlock = stackTraceData[i].loc.actualCodeBlock;
uint32_t line, column;
counter++;
- if ((size_t)stackTraceData[i].second.loc.index == SIZE_MAX) {
- size_t byteCodePosition = stackTraceData[i].second.loc.byteCodePosition;
+ if ((size_t)stackTraceData[i].loc.index == SIZE_MAX) {
+ size_t byteCodePosition = stackTraceData[i].loc.byteCodePosition;
ByteCodeLOCData* locData;
auto iterMap = locMap.find(byteCodeBlock);
line = (uint32_t)loc.line;
column = (uint32_t)loc.column;
} else {
- line = (uint32_t)stackTraceData[i].second.loc.line;
- column = (uint32_t)stackTraceData[i].second.loc.column;
+ line = (uint32_t)stackTraceData[i].loc.line;
+ column = (uint32_t)stackTraceData[i].loc.column;
}
savedStackTrace->push_back(SavedStackTraceData(byteCodeBlock, line, column));
return savedStackTrace;
}
-Debugger* createDebugger(const char* options, bool* debuggerEnabled)
+void Debugger::pumpDebuggerEvents(ExecutionState* state)
{
- Debugger* debugger = new DebuggerTcp();
+ while (processEvents(state, nullptr, false))
+ ;
+}
- if (debugger->init(options)) {
- union {
- uint16_t u16Value;
- uint8_t u8Value;
- } endian;
+void DebuggerRemote::init(const char*, Context*)
+{
+ union {
+ uint16_t u16Value;
+ uint8_t u8Value;
+ } endian;
- endian.u16Value = 1;
+ endian.u16Value = 1;
- Debugger::MessageVersion version;
- version.littleEndian = (endian.u8Value == 1);
+ MessageVersion version;
+ version.littleEndian = (endian.u8Value == 1);
- uint32_t debuggerVersion = ESCARGOT_DEBUGGER_VERSION;
- memcpy(version.version, &debuggerVersion, sizeof(uint32_t));
+ uint32_t debuggerVersion = ESCARGOT_DEBUGGER_VERSION;
+ memcpy(version.version, &debuggerVersion, sizeof(uint32_t));
- if (debugger->send(Debugger::ESCARGOT_MESSAGE_VERSION, &version, sizeof(version))) {
- Debugger::MessageConfiguration configuration;
+ if (!send(ESCARGOT_MESSAGE_VERSION, &version, sizeof(version))) {
+ return;
+ }
- configuration.maxMessageSize = ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH;
- configuration.pointerSize = (uint8_t)sizeof(void*);
+ MessageConfiguration configuration;
- debugger->send(Debugger::ESCARGOT_MESSAGE_CONFIGURATION, &configuration, sizeof(configuration));
- }
+ configuration.maxMessageSize = ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH;
+ configuration.pointerSize = (uint8_t)sizeof(void*);
- if (debugger->enabled()) {
- *debuggerEnabled = true;
- debugger->m_debuggerEnabled = debuggerEnabled;
- }
- }
- return debugger;
+ send(ESCARGOT_MESSAGE_CONFIGURATION, &configuration, sizeof(configuration));
}
-String* Debugger::getClientSource(String** sourceName)
+void Debugger::createDebuggerRemote(const char* options, Context* context)
{
- sendType(Debugger::ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE);
- while (processIncomingMessages(nullptr, nullptr))
- ;
- if (sourceName) {
- *sourceName = m_clientSourceName;
- }
- String* sourceData = m_clientSourceData;
- m_clientSourceName = nullptr;
- m_clientSourceData = nullptr;
- return sourceData;
+ DebuggerRemote* debugger = new DebuggerTcp();
+
+ debugger->init(options, context);
}
+
} // namespace Escargot
#endif /* ESCARGOT_DEBUGGER */
#define ESCARGOT_DEBUGGER_NO_STACK_TRACE_RESTORE (reinterpret_cast<ExecutionState*>(0x1))
#define ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH 128
+class Context;
class Object;
class String;
class ExecutionState;
class InterpretedCodeBlock;
class Debugger : public gc {
- friend Debugger* createDebugger(const char* options, bool* debuggerEnabled);
+public:
+ // The following code is the sam as in EscargotPublic.h
+ class WeakCodeRef;
+
+ struct BreakpointLocation {
+ BreakpointLocation(uint32_t line, uint32_t offset)
+ : line(line)
+ , offset(offset)
+ {
+ }
+
+ uint32_t line;
+ uint32_t offset;
+ };
+
+ typedef std::vector<BreakpointLocation> BreakpointLocationVector;
+
+ struct BreakpointLocationsInfo {
+ BreakpointLocationsInfo(WeakCodeRef* weakCodeRef)
+ : weakCodeRef(weakCodeRef)
+ {
+ }
+
+ // The codeRef is a unique id which is not garbage collected
+ // to avoid keeping script / function code in the memory.
+ WeakCodeRef* weakCodeRef;
+ BreakpointLocationVector breakpointLocations;
+ };
+
+ // End of the code from EscargotPublic.h
+
+ struct SavedStackTraceData : public gc {
+ ByteCodeBlock* byteCodeBlock;
+ uint32_t line;
+ uint32_t column;
+
+ SavedStackTraceData(ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column)
+ : byteCodeBlock(byteCodeBlock)
+ , line(line)
+ , column(column)
+ {
+ }
+ };
+ typedef Vector<SavedStackTraceData, GCUtil::gc_malloc_allocator<SavedStackTraceData>> SavedStackTraceDataVector;
+
+ bool inDebuggingCodeMode() const
+ {
+ return m_inDebuggingCodeMode;
+ }
+
+ void setInDebuggingCodeMode(bool mode)
+ {
+ m_inDebuggingCodeMode = mode;
+ }
+
+ void appendBreakpointLocations(BreakpointLocationsInfo* breakpointLocations)
+ {
+ m_breakpointLocationsVector.push_back(breakpointLocations);
+ }
+
+ void clearParsingData()
+ {
+ m_breakpointLocationsVector.clear();
+ }
+
+ void setActiveSavedStackTrace(ExecutionState* state, SavedStackTraceDataVector* trace)
+ {
+ m_activeSavedStackTraceExecutionState = state;
+ m_activeSavedStackTrace = trace;
+ }
+
+ ExecutionState* activeSavedStackTraceExecutionState()
+ {
+ return m_activeSavedStackTraceExecutionState;
+ }
+
+ SavedStackTraceDataVector* activeSavedStackTrace()
+ {
+ return m_activeSavedStackTrace;
+ }
+
+ inline void processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
+ {
+ if (m_stopState != ESCARGOT_DEBUGGER_ALWAYS_STOP && m_stopState != state) {
+ m_delay--;
+ if (m_delay == 0) {
+ processEvents(state, byteCodeBlock);
+ }
+ }
+
+ if (m_stopState == ESCARGOT_DEBUGGER_ALWAYS_STOP || m_stopState == state) {
+ stopAtBreakpoint(byteCodeBlock, offset, state);
+ }
+ }
+
+ static inline void updateStopState(Debugger* debugger, ExecutionState* state, ExecutionState* newState)
+ {
+ if (debugger != nullptr && debugger->m_stopState == state) {
+ // Stop at the next breakpoint if a "next" operation targets the current function
+ debugger->m_stopState = newState;
+ }
+ }
+
+ void setStopState(ExecutionState* stopState)
+ {
+ m_stopState = stopState;
+ }
+
+ static void createDebuggerRemote(const char* options, Context* context);
+
+ virtual void parseCompleted(String* source, String* srcName, String* error = nullptr) = 0;
+ virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) = 0;
+ virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) = 0;
+ virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) = 0;
+ virtual void consoleOut(String* output) = 0;
+ virtual String* getClientSource(String** sourceName) = 0;
+ virtual bool getWaitBeforeExitClient() = 0;
+ virtual bool skipSourceCode(String* srcName) const
+ {
+ return false;
+ }
+
+ static SavedStackTraceDataVector* saveStackTrace(ExecutionState& state);
+
+ void pumpDebuggerEvents(ExecutionState* state);
+
+protected:
+ Debugger()
+ : m_delay(ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY)
+ , m_stopState(ESCARGOT_DEBUGGER_ALWAYS_STOP)
+ , m_context(nullptr)
+ , m_activeSavedStackTraceExecutionState(nullptr)
+ , m_activeSavedStackTrace(nullptr)
+ , m_inDebuggingCodeMode(false)
+ {
+ }
+
+ bool enabled()
+ {
+ return m_context != nullptr;
+ }
+
+ void enable(Context* context);
+ void disable();
+
+ virtual bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) = 0;
+
+ uint32_t m_delay;
+ ExecutionState* m_stopState;
+ std::vector<BreakpointLocationsInfo*> m_breakpointLocationsVector;
+
+private:
+ Context* m_context;
+ ExecutionState* m_activeSavedStackTraceExecutionState;
+ SavedStackTraceDataVector* m_activeSavedStackTrace;
+
+ // represent that every created InterpretedCodeBlock and its ByteCode should be marked with debugging feature
+ // ByteCode should contain debugging code (breakpoint)
+ bool m_inDebuggingCodeMode;
+};
+
+class DebuggerRemote : public Debugger {
public:
// Messages sent by Escargot to the debugger client
enum {
ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE = 42,
ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE = 43,
ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING = 44,
+ ESCARGOT_DEBUGGER_WAIT_FOR_WAIT_EXIT = 45,
};
// Messages sent by the debugger client to Escargot
ESCARGOT_MESSAGE_EVAL_8BIT = 7,
ESCARGOT_MESSAGE_EVAL_16BIT_START = 8,
ESCARGOT_MESSAGE_EVAL_16BIT = 9,
- ESCARGOT_MESSAGE_GET_BACKTRACE = 10,
- ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 11,
- ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 12,
- ESCARGOT_MESSAGE_GET_OBJECT = 13,
// These four must be in the same order.
- ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 14,
- ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 15,
- ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 16,
- ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 17,
- ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 18,
- ESCARGOT_DEBUGGER_PENDING_CONFIG = 19,
- ESCARGOT_DEBUGGER_PENDING_RESUME = 20,
+ ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START = 10,
+ ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT = 11,
+ ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START = 12,
+ ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT = 13,
+ ESCARGOT_MESSAGE_GET_BACKTRACE = 14,
+ ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 15,
+ ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 16,
+ ESCARGOT_MESSAGE_GET_OBJECT = 17,
+ // These four must be in the same order.
+ ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 18,
+ ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 19,
+ ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 20,
+ ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 21,
+ ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 22,
+ ESCARGOT_DEBUGGER_PENDING_CONFIG = 23,
+ ESCARGOT_DEBUGGER_PENDING_RESUME = 24,
+ ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT = 25,
};
// Environment record types
ESCARGOT_VARIABLE_LONG_VALUE = 0x80,
};
- struct BreakpointLocation {
- BreakpointLocation(uint32_t line, uint32_t offset)
- : line(line)
- , offset(offset)
- {
- }
-
- uint32_t line;
- uint32_t offset;
- };
-
- struct SavedStackTraceData : public gc {
- ByteCodeBlock* byteCodeBlock;
- uint32_t line;
- uint32_t column;
-
- SavedStackTraceData(ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column)
- : byteCodeBlock(byteCodeBlock)
- , line(line)
- , column(column)
- {
- }
- };
-
- typedef Vector<SavedStackTraceData, GCUtil::gc_malloc_allocator<SavedStackTraceData>> SavedStackTraceDataVector;
-
- bool enabled()
- {
- return m_enabled;
- }
-
- bool parsingEnabled()
- {
- return m_parsingEnabled;
- }
-
- void setParsingEnabled(bool value)
- {
- m_parsingEnabled = value;
- }
-
- bool pendingWait(void)
+ inline bool pendingWait(void)
{
return m_pendingWait;
}
- inline void processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
+ inline bool connected(void)
{
- if (m_stopState != ESCARGOT_DEBUGGER_ALWAYS_STOP && m_stopState != state) {
- m_delay = (uint8_t)(m_delay - 1);
- if (m_delay == 0) {
- processIncomingMessages(state, byteCodeBlock);
- }
- }
-
- if (m_stopState == ESCARGOT_DEBUGGER_ALWAYS_STOP || m_stopState == state) {
- stopAtBreakpoint(byteCodeBlock, offset, state);
- }
- }
-
- void setActiveSavedStackTrace(ExecutionState* state, SavedStackTraceDataVector* trace)
- {
- m_activeSavedStackTraceExecutionState = state;
- m_activeSavedStackTrace = trace;
- }
-
- ExecutionState* activeSavedStackTraceExecutionState()
- {
- return m_activeSavedStackTraceExecutionState;
- }
-
- SavedStackTraceDataVector* activeSavedStackTrace()
- {
- return m_activeSavedStackTrace;
- }
-
- static inline void updateStopState(Debugger* debugger, ExecutionState* state, ExecutionState* newState)
- {
- if (debugger != nullptr && debugger->m_stopState == state) {
- // Stop at the next breakpoint if a "next" operation targets the current function
- debugger->m_stopState = newState;
- }
+ return enabled();
}
void sendType(uint8_t type);
void sendSubtype(uint8_t type, uint8_t subType);
void sendString(uint8_t type, String* string);
void sendPointer(uint8_t type, const void* ptr);
- void sendFunctionInfo(InterpretedCodeBlock* codeBlock);
- void sendBreakpointLocations(std::vector<Debugger::BreakpointLocation>& locations);
+
+ virtual void init(const char* options, Context* context) = 0;
+ virtual void parseCompleted(String* source, String* srcName, String* error = nullptr) override;
+ virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
+ virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
+ virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
+ virtual void consoleOut(String* output) override;
+ virtual String* getClientSource(String** sourceName) override;
+ virtual bool getWaitBeforeExitClient() override;
+
void sendBacktraceInfo(uint8_t type, ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column, uint32_t executionStateDepth);
void sendVariableObjectInfo(uint8_t subType, Object* object);
- void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state);
- void releaseFunction(const void* ptr);
- String* getClientSource(String** sourceName);
void waitForResolvingPendingBreakpoints();
- static SavedStackTraceDataVector* saveStackTrace(ExecutionState& state);
protected:
- Debugger()
- : m_enabled(false)
- , m_parsingEnabled(false)
- , m_debuggerEnabled(nullptr)
- , m_delay(ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY)
+ DebuggerRemote()
+ : m_exitClient(false)
, m_pendingWait(false)
, m_waitForResume(false)
- , m_stopState(ESCARGOT_DEBUGGER_ALWAYS_STOP)
, m_clientSourceData(nullptr)
, m_clientSourceName(nullptr)
- , m_activeSavedStackTraceExecutionState(nullptr)
- , m_activeSavedStackTrace(nullptr)
{
}
- virtual bool init(const char* options) = 0;
+ virtual bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
+
virtual bool send(uint8_t type, const void* buffer, size_t length) = 0;
virtual bool receive(uint8_t* buffer, size_t& length) = 0;
+ virtual bool isThereAnyEvent() = 0;
virtual void close(void) = 0;
- bool m_enabled;
- bool m_parsingEnabled;
- bool* m_debuggerEnabled;
-
private:
// Packed structure definitions to reduce network traffic
};
uint32_t appendToActiveObjects(Object* object);
- bool doEval(ExecutionState* state, ByteCodeBlock* byteCodeBlock, uint8_t* buffer, size_t length);
+ bool doEval(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, uint8_t* buffer, size_t length);
void getBacktrace(ExecutionState* state, uint32_t minDepth, uint32_t maxDepth, bool getTotal);
void getScopeChain(ExecutionState* state, uint32_t stateIndex);
void getScopeVariables(ExecutionState* state, uint32_t stateIndex, uint32_t index);
- bool processIncomingMessages(ExecutionState* state, ByteCodeBlock* byteCodeBlock);
- uint8_t m_delay;
+ bool m_exitClient : 1;
bool m_pendingWait : 1;
bool m_waitForResume : 1;
- ExecutionState* m_stopState;
String* m_clientSourceData;
String* m_clientSourceName;
+
Vector<uintptr_t, GCUtil::gc_malloc_atomic_allocator<uintptr_t>> m_releasedFunctions;
Vector<Object*, GCUtil::gc_malloc_allocator<Object*>> m_activeObjects;
- ExecutionState* m_activeSavedStackTraceExecutionState;
- SavedStackTraceDataVector* m_activeSavedStackTrace;
};
-Debugger* createDebugger(const char* options, bool* debuggerEnabled);
} // namespace Escargot
#endif /* ESCARGOT_DEBUGGER */
#include "Escargot.h"
#include "DebuggerTcp.h"
+#include "runtime/String.h" // for split function
#ifdef ESCARGOT_DEBUGGER
namespace Escargot {
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
+#include <sys/poll.h>
#include <unistd.h>
/* On *nix the EWOULDBLOCK errno value can be returned for non-blocking operations */
return tcpSend(socket, responseSuffix, sizeof(responseSuffix) - 1);
}
-bool DebuggerTcp::init(const char*)
+void DebuggerTcp::init(const char* options, Context* context)
{
uint16_t port = 6501;
+ int timeout = -1;
+
+ if (options) {
+ auto v = split(options, ';');
+ const char portOption[] = "--port=";
+ const char acceptTimeoutOption[] = "--accept-timeout=";
+ const char skipOption[] = "--skip=";
+ for (size_t i = 0; i < v.size(); i++) {
+ const std::string& s = v[i];
+ if (s.find(portOption) == 0) {
+ int i = std::atoi(s.data() + sizeof(portOption) - 1);
+ if (i > 0 && i <= 65535) {
+ port = i;
+ }
+ } else if (s.find(acceptTimeoutOption) == 0) {
+ timeout = std::atoi(s.data() + sizeof(acceptTimeoutOption) - 1);
+ } else if (s.find(skipOption) == 0) {
+ const char* skipStr = const_cast<const char*>(s.data() + sizeof(skipOption) - 1);
+ size_t skipLen = strlen(skipStr);
+ m_skipSourceName = String::fromASCII(skipStr, skipLen);
+ }
+ }
+ }
ASSERT(enabled() == false);
WSADATA wsaData;
int wsa_init_status = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (wsa_init_status != NO_ERROR) {
- return false;
+ return;
}
#endif /* WIN32*/
EscargotSocket serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (m_socket == ESCARGOT_INVALID_SOCKET) {
- return false;
+ return;
}
if (!tcpConfigureSocket(serverSocket, port)) {
int error = tcpGetErrno();
tcpCloseSocket(serverSocket);
tcpLogError(error);
- return false;
+ return;
}
- printf("Waiting for client connection\n");
+ ESCARGOT_LOG_INFO("Waiting for client connection 0.0.0.0:%hd\n", port);
+
+ struct pollfd fd[1];
+ fd[0].fd = serverSocket;
+ fd[0].events = POLLIN;
+ while (true) {
+ int rc = poll(fd, 1, 0);
+
+ if (rc != 0) {
+ break;
+ }
+
+ usleep(10 * 1000); // 10ms
+ if (timeout == -1) {
+ continue;
+ }
+ timeout -= 10;
+ if (timeout < 0) {
+ ESCARGOT_LOG_ERROR("Waiting for client connection error: timeout reached\n");
+ tcpCloseSocket(serverSocket);
+ return;
+ }
+ }
sockaddr_in addr;
socklen_t sinSize = sizeof(sockaddr_in);
if (m_socket == ESCARGOT_INVALID_SOCKET) {
tcpLogError(tcpGetErrno());
- return false;
+ return;
}
#ifdef WIN32
/* Set non-blocking mode. */
if (ioctlsocket(m_socket, FIONBIO, &nonblockingEnabled) != NO_ERROR) {
tcpCloseSocket(m_socket);
- return false;
+ return;
}
#else /* !WIN32 */
int socketFlags = fcntl(m_socket, F_GETFL, 0);
if (socketFlags < 0) {
tcpCloseSocket(m_socket);
- return false;
+ return;
}
/* Set non-blocking mode. */
if (fcntl(m_socket, F_SETFL, socketFlags | O_NONBLOCK) == -1) {
tcpCloseSocket(m_socket);
- return false;
+ return;
}
#endif /* WIN32 */
- printf("Connected from: %s\n", inet_ntoa(addr.sin_addr));
+ ESCARGOT_LOG_INFO("Connected from: %s\n", inet_ntoa(addr.sin_addr));
if (!webSocketHandshake(m_socket)) {
tcpCloseSocket(m_socket);
- return false;
+ return;
}
- m_enabled = true;
- m_parsingEnabled = true;
m_receiveBufferFill = 0;
m_messageLength = 0;
- return true;
+
+ enable(context);
+
+ return DebuggerRemote::init(nullptr, context);
+}
+
+bool DebuggerTcp::skipSourceCode(String* srcName) const
+{
+ ASSERT(!!srcName);
+ if (!m_skipSourceName || m_skipSourceName->length() == 0 || srcName->length() == 0) {
+ return false;
+ }
+
+ return srcName->contains(m_skipSourceName);
}
#define ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT 0x80
return false;
}
+bool DebuggerTcp::isThereAnyEvent()
+{
+ // if there is remained receive buffer data,
+ // user should call receive function again
+ if (m_receiveBufferFill) {
+ return true;
+ }
+
+ struct pollfd fd[1];
+ fd[0].fd = m_socket;
+ fd[0].events = POLLIN;
+ int rc = poll(fd, 1, 0);
+
+ if (rc == 0) {
+ return false;
+ }
+
+ return true;
+}
+
bool DebuggerTcp::receive(uint8_t* buffer, size_t& length)
{
size_t receivedLength;
{
if (enabled()) {
tcpCloseSocket(m_socket);
- m_enabled = false;
- *m_debuggerEnabled = false;
+ disable();
}
}
} // namespace Escargot
typedef int EscargotSocket;
#endif /* WIN32 */
-class DebuggerTcp : public Debugger {
+class DebuggerTcp : public DebuggerRemote {
public:
DebuggerTcp()
: m_socket(0)
, m_receiveBuffer{}
, m_receiveBufferFill(0)
, m_messageLength(0)
+ , m_skipSourceName(nullptr)
{
}
+ virtual void init(const char* options, Context* context) override;
+
+ virtual bool skipSourceCode(String* srcName) const override;
+
static void computeSha1(const uint8_t* source1, size_t source1Length,
const uint8_t* source2, size_t source2Length,
uint8_t destination[20]);
protected:
- virtual bool init(const char* options) override;
virtual bool send(uint8_t type, const void* buffer, size_t length) override;
virtual bool receive(uint8_t* buffer, size_t& length) override;
+ virtual bool isThereAnyEvent() override;
virtual void close(void) override;
private:
uint8_t m_receiveBuffer[2 + sizeof(uint32_t) + ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH];
uint8_t m_receiveBufferFill;
uint8_t m_messageLength;
+
+ // skip generating debugging bytecode for source code whose name contains m_skipSourceName
+ String* m_skipSourceName;
};
} // namespace Escargot
#endif /* ESCARGOT_DEBUGGER */
{
// Un-comment this to use default allocator
// return (Value*)GC_MALLOC(sizeof(GetObjectInlineCacheData) * GC_n);
+ // typed calloc test
+ /*
+ static MAY_THREAD_LOCAL bool typeInited = false;
+ static MAY_THREAD_LOCAL GC_descr descr;
+ if (!typeInited) {
+ GC_word obj_bitmap[GC_BITMAP_SIZE(GetObjectInlineCacheData)] = { 0 };
+ GC_set_bit(obj_bitmap, GC_WORD_OFFSET(GetObjectInlineCacheData, m_cachedhiddenClassChain));
+ descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(GetObjectInlineCacheData));
+ typeInited = true;
+ }
+ return (GetObjectInlineCacheData*)GC_CALLOC_EXPLICITLY_TYPED(GC_n, sizeof(GetObjectInlineCacheData), descr);
+ */
int kind = s_gcKinds[HeapObjectKind::GetObjectInlineCacheDataVectorKind];
size_t size = sizeof(GetObjectInlineCacheData) * GC_n;
{
#ifdef ESCARGOT_DEBUGGER
Debugger* debugger = self->m_codeBlock->context()->debugger();
- if (debugger && debugger->enabled()) {
- debugger->releaseFunction(self->m_code.data());
+ if (debugger != nullptr) {
+ debugger->byteCodeReleaseNotification(self);
}
#endif
self->m_code.clear();
}
}
+ ASSERT(index >= cb->functionStart().index);
size_t indexRelatedWithScript = index;
index -= cb->functionStart().index;
, m_loadRegisterIndex(loadRegisterIndex)
, m_propertyName(propertyName)
, m_presentAttribute(presentAttribute)
+ , m_missCount(0)
+ , m_inlineCachedStructureBefore(nullptr)
+ , m_inlineCachedStructureAfter(nullptr)
{
}
ByteCodeRegisterIndex m_loadRegisterIndex : REGISTER_INDEX_IN_BIT;
AtomicString m_propertyName;
ObjectPropertyDescriptor::PresentAttribute m_presentAttribute : 8;
-#ifndef NDEBUG
+ size_t m_missCount : 4;
+ ObjectStructure* m_inlineCachedStructureBefore;
+ ObjectStructure* m_inlineCachedStructureAfter;
+#ifndef NDEBUG
void dump()
{
printf("object define own property with name r%u.%s <- r%u", m_objectRegisterIndex, m_propertyName.string()->toUTF8StringData().data(), m_loadRegisterIndex);
#endif
};
-BYTECODE_SIZE_CHECK_IN_32BIT(ObjectDefineOwnPropertyWithNameOperation, sizeof(size_t) * 4);
+BYTECODE_SIZE_CHECK_IN_32BIT(ObjectDefineOwnPropertyWithNameOperation, sizeof(size_t) * 6);
#define ARRAY_DEFINE_OPERATION_MERGE_COUNT 8
{
m_cachedhiddenClassChain = nullptr;
m_cachedhiddenClassChainLength = 0;
+ m_isPlainDataProperty = false;
m_cachedIndex = 0;
}
ObjectStructure** m_cachedhiddenClassChain;
ObjectStructure* m_cachedhiddenClass;
};
- size_t m_cachedhiddenClassChainLength;
- size_t m_cachedIndex;
+ bool m_isPlainDataProperty : 1;
+ // 15bits of storage is enough
+ // inlineCacheProtoTraverseMaxCount is so small
+ uint16_t m_cachedhiddenClassChainLength : 15;
+ static constexpr size_t inlineCacheCachedIndexMax = std::numeric_limits<uint16_t>::max();
+ uint16_t m_cachedIndex : 16;
};
typedef Vector<GetObjectInlineCacheData, CustomAllocator<GetObjectInlineCacheData>, ComputeReservedCapacityFunctionWithLog2<>> GetObjectInlineCacheDataVector;
GetObjectPreComputedCase(const ByteCodeLOC& loc, const size_t objectRegisterIndex, const size_t storeRegisterIndex, ObjectStructurePropertyName propertyName)
: ByteCode(Opcode::GetObjectPreComputedCaseOpcode, loc)
, m_isLength(propertyName.plainString()->equals("length"))
+ , m_inlineCacheProtoTraverseMaxIndex(0)
, m_cacheMissCount(0)
, m_inlineCache(nullptr)
, m_objectRegisterIndex(objectRegisterIndex)
{
}
+ static constexpr size_t inlineCacheProtoTraverseMaxCount = 12;
+
bool m_isLength : 1;
- uint16_t m_cacheMissCount : 16;
+ unsigned char m_inlineCacheProtoTraverseMaxIndex : 8;
+ size_t m_cacheMissCount : 16;
GetObjectInlineCache* m_inlineCache;
ByteCodeRegisterIndex m_objectRegisterIndex;
class ObjectDefineGetterSetter : public ByteCode {
public:
- ObjectDefineGetterSetter(const ByteCodeLOC& loc, size_t objectRegisterIndex, size_t objectPropertyNameRegisterIndex, size_t objectPropertyValueRegisterIndex, ObjectPropertyDescriptor::PresentAttribute presentAttribute, bool isGetter)
+ ObjectDefineGetterSetter(const ByteCodeLOC& loc, size_t objectRegisterIndex, size_t objectPropertyNameRegisterIndex, size_t objectPropertyValueRegisterIndex, ObjectPropertyDescriptor::PresentAttribute presentAttribute, bool isGetter, bool isPrecomputed)
: ByteCode(Opcode::ObjectDefineGetterSetterOpcode, loc)
, m_objectRegisterIndex(objectRegisterIndex)
, m_objectPropertyNameRegisterIndex(objectPropertyNameRegisterIndex)
, m_objectPropertyValueRegisterIndex(objectPropertyValueRegisterIndex)
, m_presentAttribute(presentAttribute)
, m_isGetter(isGetter)
+ , m_isPrecomputed(isPrecomputed)
+ , m_missCount(0)
+ , m_inlineCachedStructureBefore(nullptr)
+ , m_inlineCachedStructureAfter(nullptr)
{
}
ByteCodeRegisterIndex m_objectPropertyValueRegisterIndex : REGISTER_INDEX_IN_BIT;
ObjectPropertyDescriptor::PresentAttribute m_presentAttribute : 8;
bool m_isGetter : 1; // other case, this is setter
+ bool m_isPrecomputed : 1;
+ size_t m_missCount : 4;
+ ObjectStructure* m_inlineCachedStructureBefore;
+ ObjectStructure* m_inlineCachedStructureAfter;
#ifndef NDEBUG
void dump()
#endif
};
-BYTECODE_SIZE_CHECK_IN_32BIT(ObjectDefineGetterSetter, sizeof(size_t) * 3);
+BYTECODE_SIZE_CHECK_IN_32BIT(ObjectDefineGetterSetter, sizeof(size_t) * 5);
class BindingRestElement : public ByteCode {
public:
}
#ifdef ESCARGOT_DEBUGGER
-size_t ByteCodeGenerateContext::calculateBreakpointLineOffset(size_t index, ExtendedNodeLOC sourceElementStart)
+void ByteCodeGenerateContext::calculateBreakpointLocation(size_t index, ExtendedNodeLOC sourceElementStart)
{
- StringView src = m_codeBlock->src();
- size_t lastLineOffset = m_breakpointContext->m_lastBreakpointLineOffset;
- index -= sourceElementStart.index;
-
- // if cache is invalid, we should recalulate {index, lineOffset} from begin
- if (UNLIKELY(index < m_breakpointContext->m_lastBreakpointIndexOffset)) {
- m_breakpointContext->m_lastBreakpointLineOffset = m_breakpointContext->m_lastBreakpointIndexOffset = 0;
- lastLineOffset = 0;
- }
+ ASSERT(index >= sourceElementStart.index);
+ const size_t indexOffset = index - sourceElementStart.index;
+ size_t lineOffset = m_breakpointContext->m_lastBreakpointLineOffset;
+ ASSERT(indexOffset >= m_breakpointContext->m_lastBreakpointIndexOffset);
- for (size_t i = m_breakpointContext->m_lastBreakpointIndexOffset; i < index; i++) {
+ // calculate the current breakpoint's location based on the last breakpoint's location
+ StringView src = m_codeBlock->src();
+ for (size_t i = m_breakpointContext->m_lastBreakpointIndexOffset; i < indexOffset; i++) {
char16_t c = src.charAt(i);
if (EscargotLexer::isLineTerminator(c)) {
// skip \r\n
- if (UNLIKELY(c == 13 && (i + 1 < index) && src.charAt(i + 1) == 10)) {
+ if (UNLIKELY(c == 13 && (i + 1 < indexOffset) && src.charAt(i + 1) == 10)) {
i++;
}
- lastLineOffset++;
+ lineOffset++;
}
}
- m_breakpointContext->m_lastBreakpointIndexOffset = index;
-
- return lastLineOffset;
+ // cache the current breakpoint's calculated location (offset)
+ m_breakpointContext->m_lastBreakpointIndexOffset = indexOffset;
+ m_breakpointContext->m_lastBreakpointLineOffset = lineOffset;
}
void ByteCodeGenerateContext::insertBreakpoint(size_t index, Node* node)
ASSERT(m_breakpointContext != nullptr);
ASSERT(index != SIZE_MAX);
+ // do not insert any breakpoint for unmarked code
+ if (UNLIKELY(!m_codeBlock->markDebugging())) {
+ return;
+ }
+
+ // dynamically generated code should not insert any debugging code
+ ASSERT(!m_codeBlock->hasDynamicSourceCode());
+
+ // handle eval code
+ // insert one break point only at the start with line 1
+ if (UNLIKELY(m_isEvalCode)) {
+ if (m_breakpointContext->m_breakpointLocations->breakpointLocations.size() == 0) {
+ insertBreakpointAt(1, node);
+ }
+ return;
+ }
+
+ // previous breakpoint's line offset
+ size_t lastLineOffset = m_breakpointContext->m_lastBreakpointLineOffset;
ExtendedNodeLOC sourceElementStart = m_codeBlock->functionStart();
- size_t lastLineOffset = calculateBreakpointLineOffset(index, sourceElementStart);
+ calculateBreakpointLocation(index, sourceElementStart);
- if (lastLineOffset != m_breakpointContext->m_lastBreakpointLineOffset) {
- ASSERT(lastLineOffset > m_breakpointContext->m_lastBreakpointLineOffset);
- m_breakpointContext->m_lastBreakpointLineOffset = lastLineOffset;
- insertBreakpointAt(lastLineOffset + sourceElementStart.line, node);
+ // insert a breakpoint if its the first breakpoint insertion or
+ // there was no breakpoint insertion with the same lineoffset
+ if ((m_breakpointContext->m_breakpointLocations->breakpointLocations.size() == 0) || (lastLineOffset != m_breakpointContext->m_lastBreakpointLineOffset)) {
+ ASSERT(lastLineOffset <= m_breakpointContext->m_lastBreakpointLineOffset);
+ insertBreakpointAt(m_breakpointContext->m_lastBreakpointLineOffset + sourceElementStart.line, node);
}
}
void ByteCodeGenerateContext::insertBreakpointAt(size_t line, Node* node)
{
- if (m_breakpointContext->m_parsingEnabled) {
- m_breakpointContext->m_breakpointLocations.push_back(Debugger::BreakpointLocation(line, (uint32_t)m_byteCodeBlock->currentCodeSize()));
- m_byteCodeBlock->pushCode(BreakpointDisabled(ByteCodeLOC(node->loc().index)), this, node);
- }
+ m_breakpointContext->m_breakpointLocations->breakpointLocations.push_back(Debugger::BreakpointLocation(line, (uint32_t)m_byteCodeBlock->currentCodeSize()));
+ m_byteCodeBlock->pushCode(BreakpointDisabled(ByteCodeLOC(node->loc().index)), this, node);
}
+ByteCodeBreakpointContext::ByteCodeBreakpointContext(Debugger* debugger, InterpretedCodeBlock* codeBlock)
+ : m_lastBreakpointLineOffset(0)
+ , m_lastBreakpointIndexOffset(0)
+ , m_breakpointLocations()
+{
+ m_breakpointLocations = new Debugger::BreakpointLocationsInfo(reinterpret_cast<Debugger::WeakCodeRef*>(codeBlock));
+ if (debugger && codeBlock->markDebugging()) {
+ debugger->appendBreakpointLocations(m_breakpointLocations);
+ }
+}
#endif /* ESCARGOT_DEBUGGER */
#define ASSIGN_STACKINDEX_IF_NEEDED(registerIndex, stackBase, stackBaseWillBe, stackVariableSize) \
ByteCodeGenerateContext ctx(codeBlock, block, codeBlock->isGlobalScope(), codeBlock->isEvalCode(), inWithFromRuntime || codeBlock->inWith(), nData);
#ifdef ESCARGOT_DEBUGGER
- ByteCodeBreakpointContext breakpointContext(context->debugger() && context->debugger()->parsingEnabled());
+ ByteCodeBreakpointContext breakpointContext(context->debugger(), codeBlock);
ctx.m_breakpointContext = &breakpointContext;
#endif /* ESCARGOT_DEBUGGER */
// generate bytecode
ast->generateStatementByteCode(block, &ctx);
-#ifdef ESCARGOT_DEBUGGER
- if (context->debugger() && context->debugger()->enabled() && breakpointContext.m_parsingEnabled) {
- context->debugger()->sendBreakpointLocations(breakpointContext.m_breakpointLocations);
- }
-#endif /* ESCARGOT_DEBUGGER */
-
if (ctx.m_keepNumberalLiteralsInRegisterFile) {
block->m_numeralLiteralData.resizeWithUninitializedValues(nData->size());
memcpy(block->m_numeralLiteralData.data(), nData->data(), sizeof(Value) * nData->size());
ctx.m_locData = locData;
#ifdef ESCARGOT_DEBUGGER
- ByteCodeBreakpointContext breakpointContext(context->debugger() && context->debugger()->parsingEnabled());
+ ByteCodeBreakpointContext breakpointContext(context->debugger(), codeBlock);
ctx.m_breakpointContext = &breakpointContext;
#endif /* ESCARGOT_DEBUGGER */
b++;
}
- for (size_t i = 0; i < codeBlock->identifierInfos().size(); i++) {
+ size_t localStart = 0;
+ if (codeBlock->isFunctionExpression() && !codeBlock->isFunctionNameSaveOnHeap()) {
+ localStart = 1;
+ }
+ for (size_t i = localStart; i < codeBlock->identifierInfos().size(); i++) {
if (codeBlock->identifierInfos()[i].m_needToAllocateOnStack) {
auto name = codeBlock->identifierInfos()[i].m_name.string()->toNonGCUTF8StringData();
if (i == 0 && codeBlock->isFunctionExpression()) {
struct ClassContextInformation {
ClassContextInformation()
- : m_thisExpressionIndex(REGULAR_REGISTER_LIMIT)
- , m_constructorIndex(SIZE_MAX)
+ : m_constructorIndex(SIZE_MAX)
, m_prototypeIndex(SIZE_MAX)
, m_superIndex(SIZE_MAX)
, m_name()
{
}
- size_t m_thisExpressionIndex;
size_t m_constructorIndex;
size_t m_prototypeIndex;
size_t m_superIndex;
#ifdef ESCARGOT_DEBUGGER
struct ByteCodeBreakpointContext {
+ size_t m_lastBreakpointLineOffset; // cache breakpoint's calculated line offset
+ size_t m_lastBreakpointIndexOffset; // cache breakpoint's calculated index offset
+ Debugger::BreakpointLocationsInfo* m_breakpointLocations;
bool m_parsingEnabled;
- size_t m_lastBreakpointLineOffset;
- size_t m_lastBreakpointIndexOffset;
- std::vector<Debugger::BreakpointLocation> m_breakpointLocations;
-
- ByteCodeBreakpointContext(bool parsingEnabled)
- : m_parsingEnabled(parsingEnabled)
- , m_lastBreakpointLineOffset(0)
- , m_lastBreakpointIndexOffset(0)
- {
- }
+
+ ByteCodeBreakpointContext(Debugger* debugger, InterpretedCodeBlock* codeBlock);
};
#endif /* ESCARGOT_DEBUGGER */
, m_positionToContinue(0)
, m_complexJumpBreakIgnoreCount(0)
, m_complexJumpContinueIgnoreCount(0)
- , m_complexJumpLabelledBreakIgnoreCount(0)
- , m_complexJumpLabelledContinueIgnoreCount(0)
, m_lexicalBlockIndex(0)
, m_openedNonBlockEnvCount(0)
, m_classInfo()
, m_lexicallyDeclaredNames(contextBefore.m_lexicallyDeclaredNames)
, m_positionToContinue(contextBefore.m_positionToContinue)
, m_recursiveStatementStack(contextBefore.m_recursiveStatementStack)
- , m_complexJumpBreakIgnoreCount(contextBefore.m_complexJumpBreakIgnoreCount)
- , m_complexJumpContinueIgnoreCount(contextBefore.m_complexJumpContinueIgnoreCount)
- , m_complexJumpLabelledBreakIgnoreCount(contextBefore.m_complexJumpLabelledBreakIgnoreCount)
- , m_complexJumpLabelledContinueIgnoreCount(contextBefore.m_complexJumpLabelledContinueIgnoreCount)
+ , m_complexJumpBreakIgnoreCount(contextBefore.m_recursiveStatementStack.size())
+ , m_complexJumpContinueIgnoreCount(m_complexJumpBreakIgnoreCount)
, m_lexicalBlockIndex(contextBefore.m_lexicalBlockIndex)
, m_openedNonBlockEnvCount(contextBefore.m_openedNonBlockEnvCount)
, m_classInfo(contextBefore.m_classInfo)
, m_breakpointContext(contextBefore.m_breakpointContext)
#endif /* ESCARGOT_DEBUGGER */
{
+ ASSERT(m_complexJumpBreakIgnoreCount == m_complexJumpContinueIgnoreCount);
}
~ByteCodeGenerateContext()
for (unsigned i = 0; i < m_labelledBreakStatmentPositions.size(); i++) {
if (m_labelledBreakStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledBreakStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
- if (tryCatchWithBlockStatementCount() - m_complexJumpLabelledBreakIgnoreCount > 0) {
- m_complexCaseStatementPositions.insert(std::make_pair(m_labelledBreakStatmentPositions[i].second, tryCatchWithBlockStatementCount() - m_complexJumpLabelledBreakIgnoreCount));
+ if (tryCatchWithBlockStatementCount() > 0) {
+ m_complexCaseStatementPositions.insert(std::make_pair(m_labelledBreakStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
}
}
}
for (unsigned i = 0; i < m_labelledContinueStatmentPositions.size(); i++) {
if (m_labelledContinueStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledContinueStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
- if (tryCatchWithBlockStatementCount() - m_complexJumpLabelledContinueIgnoreCount > 0) {
- m_complexCaseStatementPositions.insert(std::make_pair(m_labelledContinueStatmentPositions[i].second, tryCatchWithBlockStatementCount() - m_complexJumpLabelledContinueIgnoreCount));
+ if (tryCatchWithBlockStatementCount() > 0) {
+ m_complexCaseStatementPositions.insert(std::make_pair(m_labelledContinueStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
}
}
}
}
#ifdef ESCARGOT_DEBUGGER
- size_t calculateBreakpointLineOffset(size_t index, ExtendedNodeLOC sourceElementStart);
+ void calculateBreakpointLocation(size_t index, ExtendedNodeLOC sourceElementStart);
void insertBreakpoint(size_t index, Node* node);
void insertBreakpointAt(size_t line, Node* node);
#endif /* ESCARGOT_DEBUGGER */
std::vector<std::pair<RecursiveStatementKind, size_t>> m_recursiveStatementStack;
int m_complexJumpBreakIgnoreCount;
int m_complexJumpContinueIgnoreCount;
- int m_complexJumpLabelledBreakIgnoreCount;
- int m_complexJumpLabelledContinueIgnoreCount;
size_t m_lexicalBlockIndex;
size_t m_openedNonBlockEnvCount;
ClassContextInformation m_classInfo;
const Value& willBeObject = registerFile[code->m_objectRegisterIndex];
const Value& property = registerFile[code->m_propertyRegisterIndex];
PointerValue* v;
- if (LIKELY(willBeObject.isObject() && (v = willBeObject.asPointerValue())->isArrayObject())) {
+ if (LIKELY(willBeObject.isObject() && (v = willBeObject.asPointerValue())->hasArrayObjectTag())) {
ArrayObject* arr = (ArrayObject*)v;
if (LIKELY(arr->isFastModeArray())) {
uint32_t idx = property.tryToUseAsIndexProperty(*state);
SetObjectOperation* code = (SetObjectOperation*)programCounter;
const Value& willBeObject = registerFile[code->m_objectRegisterIndex];
const Value& property = registerFile[code->m_propertyRegisterIndex];
- if (LIKELY(willBeObject.isObject() && (willBeObject.asPointerValue())->isArrayObject())) {
+ if (LIKELY(willBeObject.isObject() && (willBeObject.asPointerValue())->hasArrayObjectTag())) {
ArrayObject* arr = willBeObject.asObject()->asArrayObject();
uint32_t idx = property.tryToUseAsIndexProperty(*state);
if (LIKELY(arr->isFastModeArray())) {
:
{
GetObjectPreComputedCase* code = (GetObjectPreComputedCase*)programCounter;
- const Value& willBeObject = registerFile[code->m_objectRegisterIndex];
- Object* obj;
- if (LIKELY(willBeObject.isObject())) {
- obj = willBeObject.asObject();
- } else {
- obj = fastToObject(*state, willBeObject);
- }
- registerFile[code->m_storeRegisterIndex] = getObjectPrecomputedCaseOperation(*state, obj, willBeObject, code, byteCodeBlock);
+ getObjectPrecomputedCaseOperation(*state, code, registerFile, byteCodeBlock);
ADD_PROGRAM_COUNTER(GetObjectPreComputedCase);
NEXT_INSTRUCTION();
}
:
{
ObjectDefineOwnPropertyWithNameOperation* code = (ObjectDefineOwnPropertyWithNameOperation*)programCounter;
- objectDefineOwnPropertyWithNameOperation(*state, code, registerFile);
+ objectDefineOwnPropertyWithNameOperation(*state, code, byteCodeBlock, registerFile);
ADD_PROGRAM_COUNTER(ObjectDefineOwnPropertyWithNameOperation);
NEXT_INSTRUCTION();
}
:
{
CreateEnumerateObject* code = (CreateEnumerateObject*)programCounter;
- auto data = createEnumerateObject(*state, registerFile[code->m_objectRegisterIndex].toObject(*state), code->m_isDestruction);
- registerFile[code->m_dataRegisterIndex] = Value((PointerValue*)data);
+ createEnumerateObject(*state, code, registerFile);
ADD_PROGRAM_COUNTER(CreateEnumerateObject);
NEXT_INSTRUCTION();
}
:
{
ObjectDefineGetterSetter* code = (ObjectDefineGetterSetter*)programCounter;
- defineObjectGetterSetter(*state, code, registerFile);
+ defineObjectGetterSetter(*state, code, byteCodeBlock, registerFile);
ADD_PROGRAM_COUNTER(ObjectDefineGetterSetter);
NEXT_INSTRUCTION();
}
{
#ifdef ESCARGOT_DEBUGGER
Debugger* debugger = state->context()->debugger();
- if (debugger && debugger->enabled()) {
+ if (debugger != nullptr) {
debugger->processDisabledBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)codeBuffer), state);
}
#endif /* ESCARGOT_DEBUGGER */
{
#ifdef ESCARGOT_DEBUGGER
Debugger* debugger = state->context()->debugger();
- if (debugger && debugger->enabled()) {
+ if (debugger != nullptr) {
debugger->stopAtBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)codeBuffer), state);
}
#endif /* ESCARGOT_DEBUGGER */
}
}
-ALWAYS_INLINE Value ByteCodeInterpreter::getObjectPrecomputedCaseOperation(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, ByteCodeBlock* block)
+ALWAYS_INLINE void ByteCodeInterpreter::getObjectPrecomputedCaseOperation(ExecutionState& state, GetObjectPreComputedCase* code, Value* registerFile, ByteCodeBlock* block)
{
- Object* orgObj = obj;
+ const Value& receiver = registerFile[code->m_objectRegisterIndex];
+ Object* obj;
+ if (LIKELY(receiver.isObject())) {
+ obj = receiver.asObject();
+ } else {
+ obj = fastToObject(state, receiver);
+ }
+
if (LIKELY(code->m_inlineCache != nullptr)) {
- auto inlineCache = code->m_inlineCache;
+ GetObjectInlineCache* const inlineCache = code->m_inlineCache;
const size_t cacheFillCount = inlineCache->m_cache.size();
- GetObjectInlineCacheData* cacheData = inlineCache->m_cache.data();
- unsigned currentCacheIndex = 0;
- TestCache:
- for (; currentCacheIndex < cacheFillCount; currentCacheIndex++) {
- const GetObjectInlineCacheData& data = cacheData[currentCacheIndex];
- const size_t cSiz = data.m_cachedhiddenClassChainLength - 1;
- if (cSiz) {
- obj = orgObj;
- ObjectStructure** cachedHiddenClassChain = data.m_cachedhiddenClassChain;
- size_t cachedIndex = data.m_cachedIndex;
- for (size_t i = 0; i < cSiz; i++) {
- if (cachedHiddenClassChain[i] != obj->structure()) {
- currentCacheIndex++;
- goto TestCache;
- }
- Object* protoObject = obj->Object::getPrototypeObject(state);
- if (protoObject != nullptr) {
- obj = protoObject;
+ GetObjectInlineCacheData* const cacheData = inlineCache->m_cache.data();
+
+ // simple case
+ if (!code->m_inlineCacheProtoTraverseMaxIndex) {
+ ObjectStructure* const objStructure = obj->structure();
+ for (unsigned currentCacheIndex = 0; currentCacheIndex < cacheFillCount; currentCacheIndex++) {
+ const GetObjectInlineCacheData& data = cacheData[currentCacheIndex];
+ if (data.m_cachedhiddenClass == objStructure) {
+ const auto& cachedIndex = data.m_cachedIndex;
+ // this is possible. but super rare
+ // so not cached for performance
+ ASSERT(cachedIndex != GetObjectInlineCacheData::inlineCacheCachedIndexMax);
+ if (LIKELY(data.m_isPlainDataProperty)) {
+ registerFile[code->m_storeRegisterIndex] = obj->m_values[cachedIndex];
} else {
- currentCacheIndex++;
- goto TestCache;
+ registerFile[code->m_storeRegisterIndex] = obj->getOwnNonPlainDataPropertyUtilForObject(state, cachedIndex, receiver);
}
+ return;
}
+ }
+ } else {
+ if (getObjectPrecomputedCaseOperationSlowCase(state, obj, receiver, code, cacheData, cacheFillCount, registerFile, block)) {
+ return;
+ }
+ }
+ }
- if (LIKELY(cachedHiddenClassChain[cSiz] == obj->structure())) {
- if (LIKELY(data.m_cachedIndex != SIZE_MAX)) {
- return obj->getOwnPropertyUtilForObject(state, data.m_cachedIndex, receiver);
- } else {
- return Value();
- }
+ getObjectPrecomputedCaseOperationCacheMiss(state, obj, receiver, code, registerFile, block);
+}
+
+NEVER_INLINE bool ByteCodeInterpreter::getObjectPrecomputedCaseOperationSlowCase(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code,
+ GetObjectInlineCacheData* cacheData,
+ const size_t& cacheFillCount,
+ Value* registerFile,
+ ByteCodeBlock* block)
+{
+ Object* objChain[GetObjectPreComputedCase::inlineCacheProtoTraverseMaxCount];
+ ObjectStructure* objStructures[GetObjectPreComputedCase::inlineCacheProtoTraverseMaxCount];
+ const auto& maxIndex = code->m_inlineCacheProtoTraverseMaxIndex;
+ size_t fillCount;
+ for (fillCount = 0; fillCount <= maxIndex && obj; fillCount++) {
+ objChain[fillCount] = obj;
+ objStructures[fillCount] = obj->structure();
+ obj = obj->Object::getPrototypeObject(state);
+ }
+
+ for (unsigned currentCacheIndex = 0; currentCacheIndex < cacheFillCount; currentCacheIndex++) {
+ const GetObjectInlineCacheData& data = cacheData[currentCacheIndex];
+ const size_t& cSiz = data.m_cachedhiddenClassChainLength;
+ if (cSiz <= fillCount) {
+ bool ok = true;
+ for (size_t i = 0; i < cSiz; i++) {
+ if (objStructures[i] != data.m_cachedhiddenClassChain[i]) {
+ ok = false;
+ break;
}
- } else {
- if (LIKELY(data.m_cachedhiddenClass == orgObj->structure())) {
- if (LIKELY(data.m_cachedIndex != SIZE_MAX)) {
- return orgObj->getOwnPropertyUtilForObject(state, data.m_cachedIndex, receiver);
+ }
+ if (ok) {
+ const auto& cachedIndex = data.m_cachedIndex;
+ if (LIKELY(cachedIndex != GetObjectInlineCacheData::inlineCacheCachedIndexMax)) {
+ if (LIKELY(data.m_isPlainDataProperty)) {
+ registerFile[code->m_storeRegisterIndex] = objChain[cSiz - 1]->m_values[cachedIndex];
} else {
- return Value();
+ registerFile[code->m_storeRegisterIndex] = objChain[cSiz - 1]->getOwnNonPlainDataPropertyUtilForObject(state, cachedIndex, receiver);
}
+ } else {
+ registerFile[code->m_storeRegisterIndex] = Value();
}
+ return true;
}
}
}
-
- return getObjectPrecomputedCaseOperationCacheMiss(state, orgObj, receiver, code, block);
+ return false;
}
-NEVER_INLINE Value ByteCodeInterpreter::getObjectPrecomputedCaseOperationCacheMiss(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, ByteCodeBlock* block)
+NEVER_INLINE void ByteCodeInterpreter::getObjectPrecomputedCaseOperationCacheMiss(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, Value* registerFile, ByteCodeBlock* block)
{
if (code->m_isLength && obj->isArrayObject()) {
- return Value(obj->asArrayObject()->arrayLength(state));
+ registerFile[code->m_storeRegisterIndex] = Value(obj->asArrayObject()->arrayLength(state));
+ return;
}
#if defined(ESCARGOT_SMALL_CONFIG)
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ registerFile[code->m_storeRegisterIndex] = obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ return;
#endif
- const int maxCacheMissCount = 16;
- const int minCacheFillCount = 3;
- const size_t maxCacheCount = 6;
+ const size_t maxCacheMissCount = 32;
+ const size_t minCacheFillCount = 4;
+ const size_t maxCacheCount = 24;
// cache miss.
if (code->m_cacheMissCount > maxCacheMissCount) {
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ registerFile[code->m_storeRegisterIndex] = obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ return;
}
code->m_cacheMissCount++;
if (code->m_cacheMissCount <= minCacheFillCount) {
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ registerFile[code->m_storeRegisterIndex] = obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ return;
}
if (UNLIKELY(!obj->isInlineCacheable())) {
code->m_cacheMissCount = maxCacheMissCount + 1;
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ registerFile[code->m_storeRegisterIndex] = obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ return;
}
if (UNLIKELY(code->m_cacheMissCount == maxCacheMissCount)) {
- if (code->m_inlineCache) {
- code->m_inlineCache->m_cache.clear();
- }
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ registerFile[code->m_storeRegisterIndex] = obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ return;
}
auto& currentCodeSizeTotal = state.context()->vmInstance()->compiledByteCodeSize();
}
auto inlineCache = code->m_inlineCache;
+ Object* orgObj = obj;
if (inlineCache->m_cache.size() > maxCacheCount) {
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ for (size_t i = inlineCache->m_cache.size() - 1; i > 0; i--) {
+ inlineCache->m_cache[i] = inlineCache->m_cache[i - 1];
+ }
+ } else {
+ inlineCache->m_cache.insert(0, GetObjectInlineCacheData());
+ block->m_inlineCacheDataSize += sizeof(GetObjectInlineCacheData);
+ currentCodeSizeTotal += sizeof(GetObjectInlineCacheData);
}
- Object* orgObj = obj;
- inlineCache->m_cache.insert(0, GetObjectInlineCacheData());
- block->m_inlineCacheDataSize += sizeof(GetObjectInlineCacheData);
- currentCodeSizeTotal += sizeof(GetObjectInlineCacheData);
-
auto& newItem = inlineCache->m_cache[0];
- VectorWithInlineStorage<24, ObjectStructure*, std::allocator<ObjectStructure*>> cachedhiddenClassChain;
+ VectorWithInlineStorage<GetObjectPreComputedCase::inlineCacheProtoTraverseMaxCount, ObjectStructure*, std::allocator<ObjectStructure*>> cachedhiddenClassChain;
while (true) {
auto s = obj->structure();
auto result = s->findProperty(code->m_propertyName);
if (result.first != SIZE_MAX) {
+ if (UNLIKELY(result.first > GetObjectInlineCacheData::inlineCacheCachedIndexMax)) {
+ goto GiveUp;
+ }
newItem.m_cachedIndex = result.first;
+ newItem.m_isPlainDataProperty = result.second->m_descriptor.isPlainDataProperty();
break;
}
obj = obj->Object::getPrototypeObject(state);
if (!obj) {
- newItem.m_cachedIndex = SIZE_MAX;
+ newItem.m_cachedIndex = GetObjectInlineCacheData::inlineCacheCachedIndexMax;
break;
}
if (UNLIKELY(!obj->isInlineCacheable())) {
- inlineCache->m_cache.clear();
- code->m_cacheMissCount = maxCacheMissCount + 1;
- return obj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
+ goto GiveUp;
}
}
+ if (UNLIKELY(cachedhiddenClassChain.size() > GetObjectPreComputedCase::inlineCacheProtoTraverseMaxCount)) {
+ goto GiveUp;
+ }
+
+ // this is possible. but super rare
+ // so this case is not cached for performance
+ if (UNLIKELY(cachedhiddenClassChain.size() == 1 && newItem.m_cachedIndex == GetObjectInlineCacheData::inlineCacheCachedIndexMax)) {
+ goto GiveUp;
+ }
+
+ code->m_inlineCacheProtoTraverseMaxIndex = std::max(cachedhiddenClassChain.size() - 1, (size_t)code->m_inlineCacheProtoTraverseMaxIndex);
+
newItem.m_cachedhiddenClassChainLength = cachedhiddenClassChain.size();
if (newItem.m_cachedhiddenClassChainLength == 1) {
newItem.m_cachedhiddenClass = cachedhiddenClassChain[0];
memcpy(newItem.m_cachedhiddenClassChain, cachedhiddenClassChain.data(), sizeof(ObjectStructure*) * cachedhiddenClassChain.size());
}
- if (newItem.m_cachedIndex != SIZE_MAX) {
- return obj->getOwnPropertyUtilForObject(state, newItem.m_cachedIndex, receiver);
+ if (newItem.m_cachedIndex != GetObjectInlineCacheData::inlineCacheCachedIndexMax) {
+ if (newItem.m_isPlainDataProperty) {
+ registerFile[code->m_storeRegisterIndex] = obj->m_values[newItem.m_cachedIndex];
+ } else {
+ registerFile[code->m_storeRegisterIndex] = obj->getOwnNonPlainDataPropertyUtilForObject(state, newItem.m_cachedIndex, receiver);
+ }
+ return;
} else {
- return Value();
+ registerFile[code->m_storeRegisterIndex] = Value();
+ return;
}
+
+GiveUp:
+ inlineCache->m_cache.clear();
+ code->m_inlineCache = nullptr;
+ code->m_cacheMissCount = maxCacheMissCount + 1;
+ registerFile[code->m_storeRegisterIndex] = orgObj->get(state, ObjectPropertyName(state, code->m_propertyName)).value(state, receiver);
}
ALWAYS_INLINE void ByteCodeInterpreter::setObjectPreComputedCaseOperation(ExecutionState& state, const Value& willBeObject, const Value& value, SetObjectPreComputedCase* code, ByteCodeBlock* block)
NEVER_INLINE void ByteCodeInterpreter::setObjectPreComputedCaseOperationCacheMiss(ExecutionState& state, Object* originalObject, const Value& willBeObject, const Value& value, SetObjectPreComputedCase* code, ByteCodeBlock* block)
{
- if (code->m_isLength && originalObject->isArrayObject() && originalObject->asArrayObject()->isFastModeArray()) {
- if (!originalObject->asArrayObject()->setArrayLength(state, value) && state.inStrictMode()) {
- ErrorObject::throwBuiltinError(state, ErrorObject::Code::TypeError, code->m_propertyName.toExceptionString(), false, String::emptyString, ErrorObject::Messages::DefineProperty_NotWritable);
+ if (code->m_isLength && originalObject->isArrayObject()) {
+ if (LIKELY(originalObject->asArrayObject()->isFastModeArray())) {
+ if (!originalObject->asArrayObject()->setArrayLength(state, value) && state.inStrictMode()) {
+ ErrorObject::throwBuiltinError(state, ErrorObject::Code::TypeError, code->m_propertyName.toExceptionString(), false, String::emptyString, ErrorObject::Messages::DefineProperty_NotWritable);
+ }
+ } else {
+ originalObject->setThrowsExceptionWhenStrictMode(state, ObjectPropertyName(state, code->m_propertyName), value, willBeObject);
}
return;
}
} else if (cb->isObjectMethod() || cb->isClassMethod() || cb->isClassStaticMethod()) {
registerFile[code->m_registerIndex] = new ScriptClassMethodFunctionObject(state, proto, cb, outerLexicalEnvironment, registerFile[code->m_homeObjectRegisterIndex].asObject());
} else {
- registerFile[code->m_registerIndex] = new ScriptFunctionObject(state, proto, cb, outerLexicalEnvironment, true, false, false);
+ registerFile[code->m_registerIndex] = new ScriptFunctionObject(state, proto, cb, outerLexicalEnvironment, true, false);
}
}
newState->ensureRareData()->m_controlFlowRecord = state->rareData()->m_controlFlowRecord;
}
- SandBox::StackTraceDataVector stackTraceData;
+ SandBox::StackTraceDataVector stackTraceDataVector;
if (LIKELY(!code->m_isCatchResumeProcess && !code->m_isFinallyResumeProcess)) {
try {
ESCARGOT_LOG_ERROR("%s\n", builder.finalize()->toUTF8StringData().data());
}
#endif
- stackTraceData = std::move(newState->context()->vmInstance()->currentSandBox()->stackTraceData());
+ stackTraceDataVector = std::move(newState->context()->vmInstance()->currentSandBox()->stackTraceDataVector());
if (!code->m_hasCatch) {
newState->rareData()->m_controlFlowRecord->back() = new ControlFlowRecord(ControlFlowRecord::NeedsThrow, val);
} else {
- stackTraceData.clear();
+ stackTraceDataVector.clear();
registerFile[code->m_catchedValueRegisterIndex] = val;
try {
ExecutionStateVariableChanger<void (*)(ExecutionState&, bool)> changer(*state, [](ExecutionState& state, bool in) {
return Value();
}
} catch (const Value& val) {
- stackTraceData = newState->context()->vmInstance()->currentSandBox()->stackTraceData();
+ stackTraceDataVector = newState->context()->vmInstance()->currentSandBox()->stackTraceDataVector();
newState->rareData()->m_controlFlowRecord->back() = new ControlFlowRecord(ControlFlowRecord::NeedsThrow, val);
}
}
return Value(Value::EmptyValue);
}
} else if (record->reason() == ControlFlowRecord::NeedsThrow) {
- state->context()->vmInstance()->currentSandBox()->rethrowPreviouslyCaughtException(*state, record->value(), stackTraceData);
+ state->context()->vmInstance()->currentSandBox()->rethrowPreviouslyCaughtException(*state, record->value(), stackTraceDataVector);
ASSERT_NOT_REACHED();
// never get here. but I add return statement for removing compile warning
return Value(Value::EmptyValue);
} else {
if (!heritagePresent) {
Value argv[] = { String::emptyString, String::emptyString };
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().constructor, 1, &argv[0], argv[1], true, false, false, false, true);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().constructor, 1, &argv[0], argv[1], true, false, false, false, true);
functionSource.codeBlock->setAsClassConstructor();
constructor = new ScriptClassConstructorFunctionObject(state, constructorParent.asObject(),
functionSource.codeBlock, functionSource.outerEnvironment, proto,
} else {
Value argv[] = { state.context()->staticStrings().lazyDotDotDotArgs().string(),
state.context()->staticStrings().lazySuperDotDotDotArgs().string() };
- auto functionSource = FunctionObject::createFunctionSourceFromScriptSource(state, state.context()->staticStrings().constructor, 1, &argv[0], argv[1], true, false, false, true, true);
+ auto functionSource = FunctionObject::createFunctionScript(state, state.context()->staticStrings().constructor, 1, &argv[0], argv[1], true, false, false, true, true);
functionSource.codeBlock->setAsClassConstructor();
functionSource.codeBlock->setAsDerivedClassConstructor();
constructor = new ScriptClassConstructorFunctionObject(state, constructorParent.asObject(),
}
}
-NEVER_INLINE EnumerateObject* ByteCodeInterpreter::createEnumerateObject(ExecutionState& state, Object* obj, bool isDestruction)
+NEVER_INLINE void ByteCodeInterpreter::createEnumerateObject(ExecutionState& state, CreateEnumerateObject* code, Value* registerFile)
{
+ Object* obj = registerFile[code->m_objectRegisterIndex].toObject(state);
+ bool isDestruction = code->m_isDestruction;
+
EnumerateObject* enumObj;
if (isDestruction) {
enumObj = new EnumerateObjectWithDestruction(state, obj);
} else {
enumObj = new EnumerateObjectWithIteration(state, obj);
}
-
- return enumObj;
+ registerFile[code->m_dataRegisterIndex] = Value((PointerValue*)enumObj);
}
NEVER_INLINE void ByteCodeInterpreter::checkLastEnumerateKey(ExecutionState& state, CheckLastEnumerateKey* code, char* codeBuffer, size_t& programCounter, Value* registerFile)
{
EnumerateObject* data = (EnumerateObject*)registerFile[code->m_registerIndex].asPointerValue();
if (data->checkLastEnumerateKey(state)) {
+ delete data;
programCounter = jumpTo(codeBuffer, code->m_exitPosition);
} else {
ADD_PROGRAM_COUNTER(CheckLastEnumerateKey);
willBeObject.asObject()->defineOwnProperty(state, ObjectPropertyName(state, propertyStringOrSymbol), ObjectPropertyDescriptor(value, code->m_presentAttribute));
}
-NEVER_INLINE void ByteCodeInterpreter::objectDefineOwnPropertyWithNameOperation(ExecutionState& state, ObjectDefineOwnPropertyWithNameOperation* code, Value* registerFile)
+NEVER_INLINE void ByteCodeInterpreter::objectDefineOwnPropertyWithNameOperation(ExecutionState& state, ObjectDefineOwnPropertyWithNameOperation* code, ByteCodeBlock* byteCodeBlock, Value* registerFile)
{
- const Value& willBeObject = registerFile[code->m_objectRegisterIndex];
+ Object* object = registerFile[code->m_objectRegisterIndex].asObject();
+ const Value& v = registerFile[code->m_loadRegisterIndex];
// http://www.ecma-international.org/ecma-262/6.0/#sec-__proto__-property-names-in-object-initializers
- if (!willBeObject.asObject()->isScriptClassConstructorPrototypeObject() && (code->m_propertyName == state.context()->staticStrings().__proto__)) {
- const Value& v = registerFile[code->m_loadRegisterIndex];
+ if (!object->isScriptClassConstructorPrototypeObject() && (code->m_propertyName == state.context()->staticStrings().__proto__)) {
if (v.isObject() || v.isNull()) {
- willBeObject.asObject()->setPrototype(state, v);
+ object->setPrototype(state, v);
}
} else {
- willBeObject.asObject()->defineOwnProperty(state, ObjectPropertyName(code->m_propertyName), ObjectPropertyDescriptor(registerFile[code->m_loadRegisterIndex], code->m_presentAttribute));
+ const size_t minCacheFillCount = 2;
+ if (object->structure() == code->m_inlineCachedStructureBefore) {
+ object->m_values.push_back(v, code->m_inlineCachedStructureAfter->propertyCount());
+ object->m_structure = code->m_inlineCachedStructureAfter;
+ } else if (code->m_missCount > minCacheFillCount) {
+ // cache miss
+ object->defineOwnProperty(state, ObjectPropertyName(code->m_propertyName), ObjectPropertyDescriptor(v, code->m_presentAttribute));
+ } else {
+ code->m_missCount++;
+ // should we fill cache?
+ if (minCacheFillCount == code->m_missCount) {
+ // try to fill cache
+ auto oldStructure = object->structure();
+ object->defineOwnProperty(state, ObjectPropertyName(code->m_propertyName), ObjectPropertyDescriptor(v, code->m_presentAttribute));
+ auto newStructure = object->structure();
+ if (object->isInlineCacheable() && oldStructure != newStructure) {
+ byteCodeBlock->m_otherLiteralData.push_back(oldStructure);
+ byteCodeBlock->m_otherLiteralData.push_back(newStructure);
+ code->m_inlineCachedStructureBefore = oldStructure;
+ code->m_inlineCachedStructureAfter = newStructure;
+ } else {
+ // failed to cache
+ }
+ } else {
+ object->defineOwnProperty(state, ObjectPropertyName(code->m_propertyName), ObjectPropertyDescriptor(v, code->m_presentAttribute));
+ }
+ }
}
}
registerFile[code->m_registerIndex] = spreadArray;
}
-NEVER_INLINE void ByteCodeInterpreter::defineObjectGetterSetter(ExecutionState& state, ObjectDefineGetterSetter* code, Value* registerFile)
+static void defineObjectGetterSetterOperation(ExecutionState& state, ObjectDefineGetterSetter* code, ByteCodeBlock* byteCodeBlock, Value* registerFile, Object* object)
{
FunctionObject* fn = registerFile[code->m_objectPropertyValueRegisterIndex].asFunction();
Value pName = code->m_objectPropertyNameRegisterIndex == REGISTER_LIMIT ? fn->codeBlock()->functionName().string() : registerFile[code->m_objectPropertyNameRegisterIndex];
gs = new (alloca(sizeof(JSGetterSetter))) JSGetterSetter(Value(Value::EmptyValue), registerFile[code->m_objectPropertyValueRegisterIndex].asFunction());
}
ObjectPropertyDescriptor desc(*gs, code->m_presentAttribute);
- Object* object = registerFile[code->m_objectRegisterIndex].toObject(state);
object->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, pName), desc);
}
+NEVER_INLINE void ByteCodeInterpreter::defineObjectGetterSetter(ExecutionState& state, ObjectDefineGetterSetter* code, ByteCodeBlock* byteCodeBlock, Value* registerFile)
+{
+ Object* object = registerFile[code->m_objectRegisterIndex].toObject(state);
+ const size_t minCacheFillCount = 2;
+ if (object->structure() == code->m_inlineCachedStructureBefore) {
+ JSGetterSetter* gs;
+ if (code->m_isGetter) {
+ gs = new JSGetterSetter(registerFile[code->m_objectPropertyValueRegisterIndex].asFunction(), Value(Value::EmptyValue));
+ } else {
+ gs = new JSGetterSetter(Value(Value::EmptyValue), registerFile[code->m_objectPropertyValueRegisterIndex].asFunction());
+ }
+ object->m_values.push_back(Value(gs), code->m_inlineCachedStructureAfter->propertyCount());
+ object->m_structure = code->m_inlineCachedStructureAfter;
+ } else if (code->m_missCount > minCacheFillCount) {
+ // cache miss
+ defineObjectGetterSetterOperation(state, code, byteCodeBlock, registerFile, object);
+ } else {
+ code->m_missCount++;
+ // should we fill cache?
+ if (minCacheFillCount == code->m_missCount && code->m_isPrecomputed) {
+ // try to fill cache
+ auto oldStructure = object->structure();
+ defineObjectGetterSetterOperation(state, code, byteCodeBlock, registerFile, object);
+ auto newStructure = object->structure();
+ if (object->isInlineCacheable() && oldStructure != newStructure) {
+ byteCodeBlock->m_otherLiteralData.push_back(oldStructure);
+ byteCodeBlock->m_otherLiteralData.push_back(newStructure);
+ code->m_inlineCachedStructureBefore = oldStructure;
+ code->m_inlineCachedStructureAfter = newStructure;
+ } else {
+ // failed to cache
+ }
+ } else {
+ defineObjectGetterSetterOperation(state, code, byteCodeBlock, registerFile, object);
+ }
+ }
+}
+
ALWAYS_INLINE Value ByteCodeInterpreter::incrementOperation(ExecutionState& state, const Value& value)
{
if (LIKELY(value.isInt32())) {
class GetObjectPreComputedCase;
class SetObjectPreComputedCase;
struct GetObjectInlineCache;
+struct GetObjectInlineCacheData;
struct SetObjectInlineCache;
struct GlobalVariableAccessCacheItem;
class InitializeGlobalVariable;
class CheckLastEnumerateKey;
class TaggedTemplateOperation;
class MarkEnumerateKey;
+class CreateEnumerateObject;
class ByteCodeInterpreter {
public:
static bool abstractLeftIsLessThanEqualRightSlowCase(ExecutionState& state, const Value& left, const Value& right, bool switched);
static bool abstractLeftIsLessThanEqualRight(ExecutionState& state, const Value& left, const Value& right, bool switched);
- static Value getObjectPrecomputedCaseOperation(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, ByteCodeBlock* block);
- static Value getObjectPrecomputedCaseOperationCacheMiss(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, ByteCodeBlock* block);
+ static void getObjectPrecomputedCaseOperation(ExecutionState& state, GetObjectPreComputedCase* code, Value* registerFile, ByteCodeBlock* block);
+ static bool getObjectPrecomputedCaseOperationSlowCase(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, GetObjectInlineCacheData* cacheData,
+ const size_t& cacheFillCount, Value* registerFile, ByteCodeBlock* block);
+ static void getObjectPrecomputedCaseOperationCacheMiss(ExecutionState& state, Object* obj, const Value& receiver, GetObjectPreComputedCase* code, Value* registerFile, ByteCodeBlock* block);
static void setObjectPreComputedCaseOperation(ExecutionState& state, const Value& willBeObject, const Value& value, SetObjectPreComputedCase* code, ByteCodeBlock* block);
static void setObjectPreComputedCaseOperationCacheMiss(ExecutionState& state, Object* obj, const Value& willBeObject, const Value& value, SetObjectPreComputedCase* code, ByteCodeBlock* block);
static void callFunctionComplexCase(ExecutionState& state, CallFunctionComplexCase* code, Value* registerFile, ByteCodeBlock* byteCodeBlock);
static void spreadFunctionArguments(ExecutionState& state, const Value* argv, const size_t argc, ValueVector& argVector);
- static EnumerateObject* createEnumerateObject(ExecutionState& state, Object* obj, bool isDestruction);
+ static void createEnumerateObject(ExecutionState& state, CreateEnumerateObject* code, Value* registerFile);
static void checkLastEnumerateKey(ExecutionState& state, CheckLastEnumerateKey* code, char* codeBuffer, size_t& programCounter, Value* registerFile);
static void markEnumerateKey(ExecutionState& state, MarkEnumerateKey* code, Value* registerFile);
static void metaPropertyOperation(ExecutionState& state, MetaPropertyOperation* code, ByteCodeBlock* byteCodeBlock, Value* registerFile);
static void objectDefineOwnPropertyOperation(ExecutionState& state, ObjectDefineOwnPropertyOperation* code, Value* registerFile);
- static void objectDefineOwnPropertyWithNameOperation(ExecutionState& state, ObjectDefineOwnPropertyWithNameOperation* code, Value* registerFile);
+ static void objectDefineOwnPropertyWithNameOperation(ExecutionState& state, ObjectDefineOwnPropertyWithNameOperation* code, ByteCodeBlock* byteCodeBlock, Value* registerFile);
static void arrayDefineOwnPropertyOperation(ExecutionState& state, ArrayDefineOwnPropertyOperation* code, Value* registerFile);
static void arrayDefineOwnPropertyBySpreadElementOperation(ExecutionState& state, ArrayDefineOwnPropertyBySpreadElementOperation* code, Value* registerFile);
static void createSpreadArrayObject(ExecutionState& state, CreateSpreadArrayObject* code, Value* registerFile);
- static void defineObjectGetterSetter(ExecutionState& state, ObjectDefineGetterSetter* code, Value* registerFile);
+ static void defineObjectGetterSetter(ExecutionState& state, ObjectDefineGetterSetter* code, ByteCodeBlock* byteCodeBlock, Value* registerFile);
static Value incrementOperation(ExecutionState& state, const Value& value);
static Value incrementOperationSlowCase(ExecutionState& state, const Value& value);
static Value decrementOperation(ExecutionState& state, const Value& value);
return state.context()->staticStrings().lazyExponentMinusSign().string();
case UNUM_EXPONENT_FIELD:
return state.context()->staticStrings().lazyExponentInteger().string();
+#ifndef UNUM_MEASURE_UNIT_FIELD
+#define UNUM_MEASURE_UNIT_FIELD (UNUM_SIGN_FIELD + 1)
+#endif
+#ifndef UNUM_COMPACT_FIELD
+#define UNUM_COMPACT_FIELD (UNUM_SIGN_FIELD + 2)
+#endif
case UNUM_MEASURE_UNIT_FIELD:
return state.context()->staticStrings().lazyUnit().string();
case UNUM_COMPACT_FIELD:
static String* defaultTimeZone(ExecutionState& state)
{
- // ensure timezone
- state.context()->vmInstance()->timezone();
+#if !defined(OS_WINDOWS_UWP)
+ state.context()->vmInstance()->ensureTimezone();
+#endif
return String::fromUTF8(state.context()->vmInstance()->timezoneID().data(), state.context()->vmInstance()->timezoneID().length());
}
#include "Intl.h"
#include "IntlDisplayNames.h"
+#if defined(ENABLE_INTL_DISPLAYNAMES)
+
namespace Escargot {
IntlDisplayNamesObject::IntlDisplayNamesObject(ExecutionState& state, Object* proto, Value locales, Value options)
} // namespace Escargot
#endif
+#endif
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_DISPLAYNAMES)
/*
* Copyright (c) 2021-present Samsung Electronics Co., Ltd
*
#include "Intl.h"
#include "IntlListFormat.h"
+#if defined(ENABLE_INTL_LISTFORMAT)
+
namespace Escargot {
IntlListFormatObject::IntlListFormatObject(ExecutionState& state, Object* proto, Value locales, Value options)
} // namespace Escargot
#endif
+#endif
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_LISTFORMAT)
/*
* Copyright (c) 2021-present Samsung Electronics Co., Ltd
*
#include "Intl.h"
#include "IntlNumberFormat.h"
+#if defined(ENABLE_INTL_NUMBERFORMAT)
+
namespace Escargot {
static const char* const intlNumberFormatRelevantExtensionKeys[1] = { "nu" };
} // namespace Escargot
#endif
+#endif
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_NUMBERFORMAT)
/*
* Copyright (c) 2019-present Samsung Electronics Co., Ltd
*
#include "Intl.h"
#include "IntlPluralRules.h"
+#if defined(ENABLE_INTL_PLURALRULES)
+
namespace Escargot {
static double getNumberOption(ExecutionState& state, Optional<Object*> options, String* property, double minimum, double maximum, double fallback)
} // namespace Escargot
#endif
+#endif
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_PLURALRULES)
/*
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
*
#include "Intl.h"
#include "IntlRelativeTimeFormat.h"
+#if defined(ENABLE_INTL_RELATIVETIMEFORMAT)
+
namespace Escargot {
static const char* const intlRelativeTimeFormatRelevantExtensionKeys[1] = { "nu" };
} // namespace Escargot
#endif
+#endif
-#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
+#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_RELATIVETIMEFORMAT)
/*
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
*
, m_allowSuperCall(false)
, m_allowSuperProperty(false)
, m_allowArguments(false)
+ , m_hasDynamicSourceCode(false)
+#ifdef ESCARGOT_DEBUGGER
+ , m_markDebugging(ctx->inDebuggingCodeMode())
+#endif
#ifndef NDEBUG
, m_scopeContext(scopeCtx)
#endif
, m_allowSuperCall(false)
, m_allowSuperProperty(false)
, m_allowArguments(false)
+ , m_hasDynamicSourceCode(false)
+#ifdef ESCARGOT_DEBUGGER
+ , m_markDebugging(ctx->inDebuggingCodeMode())
+#endif
#ifndef NDEBUG
, m_scopeContext(scopeCtx)
#endif
, m_allowSuperCall(false)
, m_allowSuperProperty(false)
, m_allowArguments(false)
+ , m_hasDynamicSourceCode(false)
+#ifdef ESCARGOT_DEBUGGER
+ , m_markDebugging(ctx->inDebuggingCodeMode())
+#endif
#ifndef NDEBUG
, m_scopeContext(nullptr)
#endif
virtual uint16_t functionLength() const = 0;
virtual AtomicString functionName() const = 0;
+ virtual void setFunctionName(AtomicString name) = 0;
virtual bool isNativeCodeBlock() const
{
return m_functionName;
}
+ virtual void setFunctionName(AtomicString name) override
+ {
+ m_functionName = name;
+ }
+
bool isNativeConstructor() const
{
return m_isNativeConstructor;
typedef TightVector<IdentifierInfo, GCUtil::gc_malloc_atomic_allocator<IdentifierInfo>> IdentifierInfoVector;
- static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, StringView src, ASTScopeContext* scopeCtx, bool isEvalCode, bool isEvalCodeInFunction);
- static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, StringView src, ASTScopeContext* scopeCtx, InterpretedCodeBlock* parentBlock, bool isEvalCode, bool isEvalCodeInFunction);
- static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, bool needRareData = false);
-
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
return m_functionName;
}
+ virtual void setFunctionName(AtomicString name) override
+ {
+ // set function name is allowed only for dynamically created function except class constructor
+ ASSERT(m_hasDynamicSourceCode && !m_isClassConstructor);
+ m_functionName = name;
+ }
+
virtual bool hasRareData() const
{
return false;
return m_allowArguments;
}
+ bool hasDynamicSourceCode() const
+ {
+ return m_hasDynamicSourceCode;
+ }
+
+ void setDynamicSourceCode()
+ {
+ m_hasDynamicSourceCode = true;
+ }
+
+#ifdef ESCARGOT_DEBUGGER
+ bool markDebugging() const
+ {
+ return m_markDebugging;
+ }
+#endif
+
bool isFunctionNameSaveOnHeap() const
{
return m_isFunctionNameSaveOnHeap;
bool m_allowSuperCall : 1;
bool m_allowSuperProperty : 1;
bool m_allowArguments : 1;
+ // represent if its source code is created dynamically by createFunctionScript
+ bool m_hasDynamicSourceCode : 1;
+#ifdef ESCARGOT_DEBUGGER
+ // mark that this InterpretedCodeBlock should generate debugging bytecode (breakpoint)
+ bool m_markDebugging : 1;
+#endif
#ifndef NDEBUG
ASTScopeContext* m_scopeContext;
// empty CodeBlock (for CodeCache loading)
InterpretedCodeBlock(Context* ctx, Script* script);
+ static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, StringView src, ASTScopeContext* scopeCtx, bool isEvalCode, bool isEvalCodeInFunction);
+ static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, StringView src, ASTScopeContext* scopeCtx, InterpretedCodeBlock* parentBlock, bool isEvalCode, bool isEvalCodeInFunction);
+ static InterpretedCodeBlock* createInterpretedCodeBlock(Context* ctx, Script* script, bool needRareData = false);
+
void recordGlobalParsingInfo(ASTScopeContext* scopeCtx, bool isEvalCode, bool isEvalCodeInFunction);
void recordFunctionParsingInfo(ASTScopeContext* scopeCtxm, bool isEvalCode, bool isEvalCodeInFunction);
Value Scanner::ScannerResult::valueStringLiteralToValue(Scanner* scannerInstance)
{
- if (this->type == Token::KeywordToken) {
- return keywordToString(scannerInstance->escargotContext, this->valueKeywordKind).string();
- }
+ ASSERT(this->type == Token::StringLiteralToken);
- if (this->hasAllocatedString) {
+ if (UNLIKELY(this->hasAllocatedString)) {
if (!this->valueStringLiteralData.m_stringIfNewlyAllocated) {
constructStringLiteral(scannerInstance);
}
return this->valueStringLiteralData.m_stringIfNewlyAllocated;
}
- return new StringView(scannerInstance->sourceAsNormalView, this->valueStringLiteralData.m_start, this->valueStringLiteralData.m_end);
+ // check if string is one of typeof strings
+ // we only consider the most common cases which are undefined, object, function
+ size_t start = this->valueStringLiteralData.m_start;
+ size_t end = this->valueStringLiteralData.m_end;
+ size_t length = end - start;
+ if (length > 5 && length < 10) {
+ ParserStringView str(scannerInstance->source, start, end);
+ switch (str.bufferedCharAt(0)) {
+ case 'o': {
+ if (length == 6 && str.equalsSameLength("object", 1)) {
+ return scannerInstance->escargotContext->staticStrings().object.string();
+ }
+ break;
+ }
+ case 'f': {
+ if (length == 8 && str.equalsSameLength("function", 1)) {
+ return scannerInstance->escargotContext->staticStrings().function.string();
+ }
+ break;
+ }
+ case 'u': {
+ if (length == 9 && str.equalsSameLength("undefined", 1)) {
+ return scannerInstance->escargotContext->staticStrings().undefined.string();
+ }
+ break;
+ }
+ default: {
+ return new StringView(scannerInstance->sourceAsNormalView, start, end);
+ }
+ }
+ }
+
+ return new StringView(scannerInstance->sourceAsNormalView, start, end);
}
ParserStringView Scanner::ScannerResult::valueStringLiteral(Scanner* scannerInstance)
String* newStr;
if (isEveryCharLatin1) {
- newStr = new Latin1String(stringUTF16.data(), stringUTF16.length());
+ newStr = String::fromLatin1(stringUTF16.data(), stringUTF16.length());
} else {
newStr = new UTF16String(stringUTF16.data(), stringUTF16.length());
}
}
if (isAllASCII(flags.data(), flags.length())) {
- return new ASCIIString(flags.data(), flags.length());
+ return String::fromLatin1(flags.data(), flags.length());
}
return new UTF16String(flags.data(), flags.length());
, lineStart(0)
, start(0)
, end(0)
+ , valueKeywordKind(NotKeyword)
{
}
}
const auto& data = m_bufferData;
- if (data.has8BitContent) {
+ if (LIKELY(data.has8BitContent)) {
for (size_t i = 0; i < srcLen; i++) {
- if (src[i] != ((const LChar*)data.buffer)[i]) {
+ if (src[i] != data.bufferAs8Bit[i]) {
return false;
}
}
} else {
for (size_t i = 0; i < srcLen; i++) {
- if (src[i] != ((const char16_t*)data.buffer)[i]) {
+ if (src[i] != data.bufferAs16Bit[i]) {
return false;
}
}
return true;
}
+ ALWAYS_INLINE bool equalsSameLength(const char* str, size_t compareStartAt = 0) const
+ {
+ ASSERT(strlen(str) == length());
+
+ const auto& data = m_bufferData;
+ if (LIKELY(data.has8BitContent)) {
+ return memcmp(data.bufferAs8Bit + compareStartAt, str + compareStartAt, data.length - compareStartAt) == 0;
+ } else {
+ return equals16Bit(data.bufferAs16Bit + compareStartAt, str + compareStartAt, data.length - compareStartAt);
+ }
+ }
+
template <const size_t srcLen>
bool operator!=(const char (&src)[srcLen]) const
{
ret.resizeWithUninitializedValues(len);
for (size_t i = 0; i < len; i++) {
- ret[i] = charAt(i);
+ ret[i] = bufferedCharAt(i);
}
return ret;
virtual const LChar* characters8() const override
{
ASSERT(has8BitContent());
- return (LChar*)m_bufferData.buffer;
+ return reinterpret_cast<const LChar*>(m_bufferData.bufferAs8Bit);
}
virtual const char16_t* characters16() const override
{
ASSERT(!has8BitContent());
- return (const char16_t*)m_bufferData.buffer;
+ return reinterpret_cast<const char16_t*>(m_bufferData.bufferAs16Bit);
}
virtual bool isStringView() override
char16_t bufferedCharAt(const size_t idx) const
{
- if (m_bufferData.has8BitContent) {
- return ((const LChar*)m_bufferData.buffer)[idx];
+ if (LIKELY(m_bufferData.has8BitContent)) {
+ return m_bufferData.bufferAs8Bit[idx];
} else {
- return ((const char16_t*)m_bufferData.buffer)[idx];
+ return m_bufferData.bufferAs16Bit[idx];
}
}
m_bufferData.has8BitContent = srcData.has8BitContent;
m_bufferData.length = end - start;
if (srcData.has8BitContent) {
- m_bufferData.buffer = ((LChar*)srcData.buffer) + start;
+ m_bufferData.bufferAs8Bit = srcData.bufferAs8Bit + start;
} else {
- m_bufferData.buffer = ((char16_t*)srcData.buffer) + start;
+ m_bufferData.bufferAs16Bit = srcData.bufferAs16Bit + start;
}
}
+
+ bool equals16Bit(const char16_t* c1, const char* c2, size_t len) const
+ {
+ while (len > 0) {
+ if (*c1++ != *c2++) {
+ return false;
+ }
+ len--;
+ }
+
+ return true;
+ }
};
} // namespace Escargot
bool Script::isExecuted()
{
if (isModule()) {
- return m_moduleData->m_status != ModuleData::ModuleStatus::Unlinked;
+ return m_moduleData->m_status >= ModuleData::ModuleStatus::Evaluating;
}
return m_topCodeBlock->byteCodeBlock() == nullptr;
}
return m_moduleData->m_requestedModules[i];
}
+Value Script::moduleInstantiate(ExecutionState& state)
+{
+ ASSERT(isModule());
+ if (!moduleData()->m_didCallLoadedCallback) {
+ Global::platform()->didLoadModule(context(), nullptr, this);
+ moduleData()->m_didCallLoadedCallback = true;
+ }
+
+ auto result = moduleLinking(state);
+ if (result.gotException) {
+ throw result.value;
+ }
+ return result.value;
+}
+
+Value Script::moduleEvaluate(ExecutionState& state)
+{
+ ASSERT(isModule());
+
+ auto result = moduleEvaluation(state);
+ if (result.gotException) {
+ throw result.value;
+ }
+ return result.value;
+}
+
AtomicStringVector Script::exportedNames(ExecutionState& state, std::vector<Script*>& exportStarSet)
{
// Let module be this Source Text Module Record.
if (result.gotException) {
throw result.value;
}
- result = moduleEvaluate(state);
+ result = moduleEvaluation(state);
if (result.gotException) {
throw result.value;
}
// Assert: requiredModule.[[Status]] is either "linking", "linked", or "evaluated".
ASSERT(requiredModule->moduleData()->m_status == ModuleData::Linking || requiredModule->moduleData()->m_status == ModuleData::Linked || requiredModule->moduleData()->m_status == ModuleData::Evaluated);
// Assert: requiredModule.[[Status]] is "linking" if and only if requiredModule is in stack.
- if (requiredModule->moduleData()->m_status == ModuleData::Linking) {
- bool onStack = false;
- for (size_t j = 0; j < stack.size(); j++) {
- if (stack[j] == requiredModule) {
- onStack = true;
- break;
- }
- }
- ASSERT(onStack);
- }
+ // this assert is removed. because some users want to instantiate their module on onLoadModule
// If requiredModule.[[Status]] is "linking", then
if (requiredModule->moduleData()->m_status == ModuleData::Linking) {
return Script::ModuleExecutionResult(false, Value(index));
}
-Script::ModuleExecutionResult Script::moduleEvaluate(ExecutionState& state)
+Script::ModuleExecutionResult Script::moduleEvaluation(ExecutionState& state)
{
// + https://tc39.es/proposal-top-level-await/#sec-moduleevaluation
, m_topCodeBlock(nullptr)
, m_moduleData(moduleData)
{
+ // srcName and sourceCode should have valid string (empty string for no name)
+ ASSERT(!!srcName && !!sourceCode);
}
void* operator new(size_t size);
size_t moduleRequestsLength();
String* moduleRequest(size_t i);
+ Value moduleInstantiate(ExecutionState& state);
+ Value moduleEvaluate(ExecutionState& state);
+
bool isExecuted();
bool wasThereErrorOnModuleEvaluation();
Value moduleEvaluationError();
ModuleExecutionResult innerModuleLinking(ExecutionState& state, std::vector<Script*>& stack, uint32_t index);
// https://tc39.es/ecma262/#sec-moduleevaluation
- ModuleExecutionResult moduleEvaluate(ExecutionState& state);
+ ModuleExecutionResult moduleEvaluation(ExecutionState& state);
// https://tc39.es/ecma262/#sec-innermoduleevaluation
ModuleExecutionResult innerModuleEvaluation(ExecutionState& state, std::vector<Script*>& stack, uint32_t index);
{
ASSERT(m_context->astAllocator().isInitialized());
#ifdef ESCARGOT_DEBUGGER
- if (LIKELY(needByteCodeGeneration) && m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
+ if (LIKELY(needByteCodeGeneration) && m_context->debuggerEnabled() && !m_context->debugger()->skipSourceCode(srcName)) {
return initializeScriptWithDebugger(source, srcName, parentCodeBlock, isModule, isEvalMode, isEvalCodeInFunction, inWithOperation, strictFromOutside, allowSuperCall, allowSuperProperty, allowNewTarget);
}
#endif /* ESCARGOT_DEBUGGER */
{
#ifdef ESCARGOT_DEBUGGER
// When the debugger is enabled, lazy compilation is disabled, so the functions are compiled
- // during parsing, and this function is never called. However, implicit class constructors
- // has no source code, and still compiled later. These functions are ignored by the debugger.
- if (m_context->debugger() != nullptr) {
- m_context->debugger()->setParsingEnabled(false);
- }
+ // during parsing, and this function is never called. However, implicit class constructors and dynamically generated functions
+ // are still compiled later. These functions are ignored by the debugger.
+ ASSERT(!m_context->debuggerEnabled() || !m_context->inDebuggingCodeMode());
#endif /* ESCARGOT_DEBUGGER */
GC_disable();
// reset ASTAllocator
m_context->astAllocator().reset();
GC_enable();
-
-#ifdef ESCARGOT_DEBUGGER
- if (m_context->debugger() != nullptr) {
- m_context->debugger()->setParsingEnabled(true);
- }
-#endif /* ESCARGOT_DEBUGGER */
}
#ifdef ESCARGOT_DEBUGGER
FunctionNode* functionNode = esprima::parseSingleFunction(m_context, codeBlock, SIZE_MAX);
codeBlock->m_byteCodeBlock = ByteCodeGenerator::generateByteCode(m_context, codeBlock, functionNode);
- if (m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
- String* functionName = codeBlock->functionName().string();
- if (functionName->length() > 0) {
- StringView* functionNameView = new StringView(functionName);
- m_context->debugger()->sendString(Debugger::ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT, functionNameView);
- }
-
- if (m_context->debugger()->enabled()) {
- m_context->debugger()->sendFunctionInfo(codeBlock);
- }
- }
-
m_context->astAllocator().reset();
}
ScriptParser::InitializeScriptResult ScriptParser::initializeScriptWithDebugger(String* source, String* srcName, InterpretedCodeBlock* parentCodeBlock, bool isModule, bool isEvalMode, bool isEvalCodeInFunction, bool inWithOperation, bool strictFromOutside, bool allowSuperCall, bool allowSuperProperty, bool allowNewTarget)
{
GC_disable();
+ if (m_context->debuggerEnabled()) {
+ m_context->debugger()->setInDebuggingCodeMode(true);
+ }
bool inWith = (parentCodeBlock ? parentCodeBlock->inWith() : false) || inWithOperation;
bool allowSC = (parentCodeBlock ? parentCodeBlock->allowSuperCall() : false) || allowSuperCall;
programNode = esprima::parseProgram(m_context, sourceView, outerClassInfo, isModule, strictFromOutside, inWith, SIZE_MAX, allowSC, allowSP, allowNewTarget, allowArguments);
- if (m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
- m_context->debugger()->sendString(Debugger::ESCARGOT_MESSAGE_SOURCE_8BIT, source);
-
- if (m_context->debugger()->enabled()) {
- m_context->debugger()->sendString(Debugger::ESCARGOT_MESSAGE_FILE_NAME_8BIT, srcName);
- }
- }
-
script = new Script(srcName, source, programNode->moduleData(), !parentCodeBlock);
if (parentCodeBlock) {
programNode->scopeContext()->m_hasEval = parentCodeBlock->hasEval();
// reset ASTAllocator
m_context->astAllocator().reset();
- if (m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
- m_context->debugger()->sendType(Debugger::ESCARGOT_MESSAGE_PARSE_ERROR);
- StringView* errorView = new StringView(orgError->message, 0, orgError->message->length());
- m_context->debugger()->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, errorView);
+ if (m_context->debuggerEnabled()) {
+ m_context->debugger()->parseCompleted(source, srcName, orgError->message);
+ m_context->debugger()->clearParsingData();
+ m_context->debugger()->setInDebuggingCodeMode(false);
}
GC_enable();
// Generate ByteCode
topCodeBlock->m_byteCodeBlock = ByteCodeGenerator::generateByteCode(m_context, topCodeBlock, programNode, inWith);
- if (m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
- m_context->debugger()->sendFunctionInfo(topCodeBlock);
- }
-
// reset ASTAllocator
m_context->astAllocator().reset();
- if (m_context->debugger() != nullptr && m_context->debugger()->enabled()) {
+ Debugger* debugger = m_context->debugger();
+ if (debugger != nullptr) {
recursivelyGenerateChildrenByteCode(topCodeBlock);
- m_context->debugger()->sendType(Debugger::ESCARGOT_MESSAGE_PARSE_DONE);
- if (m_context->debugger()->pendingWait()) {
- m_context->debugger()->waitForResolvingPendingBreakpoints();
- }
+
+ debugger->parseCompleted(source, srcName);
+ debugger->clearParsingData();
+ debugger->setInDebuggingCodeMode(false);
}
GC_enable();
if (blk->usesArgumentsObject() && !codeBlock->m_codeBlock->isArrowFunctionExpression()) {
codeBlock->pushCode(EnsureArgumentsObject(ByteCodeLOC(m_loc.index)), context, this);
}
- codeBlock->pushCode(CreateFunction(ByteCodeLOC(m_loc.index), dstIndex, context->m_classInfo.m_thisExpressionIndex, blk), context, this);
+ codeBlock->pushCode(CreateFunction(ByteCodeLOC(m_loc.index), dstIndex, REGULAR_REGISTER_LIMIT, blk), context, this);
}
private:
ByteCodeRegisterIndex startIndex = args.first;
context->giveUpRegister();
codeBlock->pushCode(CallFunctionComplexCase(ByteCodeLOC(m_loc.index), CallFunctionComplexCase::MayBuiltinEval, context->m_isWithScope, args.second,
- isOptional, context->m_classInfo.m_thisExpressionIndex, evalIndex, startIndex, dstRegister, m_arguments.size()),
+ isOptional, REGULAR_REGISTER_LIMIT, evalIndex, startIndex, dstRegister, m_arguments.size()),
context, this);
return;
}
codeBlock->pushCode(ObjectDefineOwnPropertyOperation(ByteCodeLOC(m_loc.index), destIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent), true), context, this);
}
} else if (p->kind() == ClassElementNode::Kind::Get) {
- codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), destIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::NonEnumerablePresent), true), context, this);
+ codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), destIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::NonEnumerablePresent), true, hasKeyName), context, this);
} else {
ASSERT(p->kind() == ClassElementNode::Kind::Set);
- codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), destIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::NonEnumerablePresent), false), context, this);
+ codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), destIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::NonEnumerablePresent), false, hasKeyName), context, this);
}
if (propertyIndex != SIZE_MAX) {
newContext.giveUpRegister();
}
+ // we should increase this count here.
+ // because the block was created after newContext creation
if (bi->m_shouldAllocateEnvironment) {
newContext.m_complexJumpContinueIgnoreCount++;
+ newContext.m_complexJumpBreakIgnoreCount++;
}
}
size_t homeObjectIndex = blk->isClassStaticMethod() ? context->m_classInfo.m_constructorIndex : context->m_classInfo.m_prototypeIndex;
codeBlock->pushCode(CreateFunction(ByteCodeLOC(m_loc.index), dstIndex, homeObjectIndex, blk), context, this);
} else {
- codeBlock->pushCode(CreateFunction(ByteCodeLOC(m_loc.index), dstIndex, context->m_classInfo.m_thisExpressionIndex, blk), context, this);
+ codeBlock->pushCode(CreateFunction(ByteCodeLOC(m_loc.index), dstIndex, REGULAR_REGISTER_LIMIT, blk), context, this);
}
codeBlock->m_shouldClearStack = true;
codeBlock->pushCode(ObjectDefineOwnPropertyOperation(ByteCodeLOC(m_loc.index), objIndex, propertyIndex, valueIndex, ObjectPropertyDescriptor::AllPresent, hasFunctionOnRightSide | hasClassOnRightSide), context, this);
}
} else if (p->kind() == PropertyNode::Kind::Get) {
- codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), objIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::EnumerablePresent), true), context, this);
+ codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), objIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::EnumerablePresent), true, hasKeyName), context, this);
} else {
ASSERT(p->kind() == PropertyNode::Kind::Set);
- codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), objIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::EnumerablePresent), false), context, this);
+ codeBlock->pushCode(ObjectDefineGetterSetter(ByteCodeLOC(m_loc.index), objIndex, propertyIndex, valueIndex, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::EnumerablePresent), false, hasKeyName), context, this);
}
if (!hasKeyName) {
codeBlock->finalizeLexicalBlock(context, blockContext);
#ifdef ESCARGOT_DEBUGGER
- if (context->m_breakpointContext->m_breakpointLocations.size() == 0) {
- if (context->m_isEvalCode) {
- context->insertBreakpointAt(1, this);
- } else {
- insertBreakpoint(context);
- }
+ if (context->m_breakpointContext->m_breakpointLocations->breakpointLocations.size() == 0) {
+ // add a break point for empty global code (including eval code)
+ insertBreakpoint(context);
}
#endif /* ESCARGOT_DEBUGGER */
codeBlock->pushCode(End(ByteCodeLOC(SIZE_MAX), 0), context, this);
virtual void generateStatementByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context) override
{
#ifdef ESCARGOT_DEBUGGER
- if (context->m_breakpointContext->m_breakpointLocations.size() == 0 && context->m_breakpointContext->m_parsingEnabled) {
- ASSERT(context->m_breakpointContext->m_lastBreakpointLineOffset == 0);
- ASSERT(context->m_breakpointContext->m_breakpointLocations.size() == 0);
-
+ if (codeBlock->codeBlock()->markDebugging()) {
InterpretedCodeBlock* interpretedCodeBlock = context->m_codeBlock;
-
if (interpretedCodeBlock->hasRareData() && interpretedCodeBlock->rareData()->m_debuggerLineStart != SIZE_MAX) {
ASSERT(interpretedCodeBlock->isOneExpressionOnlyVirtualArrowFunctionExpression());
context->insertBreakpointAt(interpretedCodeBlock->rareData()->m_debuggerLineStart, this);
} else {
- ExtendedNodeLOC sourceElementStart = context->m_codeBlock->functionStart();
- size_t lastLineOffset = context->calculateBreakpointLineOffset(m_loc.index, sourceElementStart);
- context->insertBreakpointAt(lastLineOffset + sourceElementStart.line, this);
+ insertBreakpoint(context);
}
- } else {
- insertBreakpoint(context);
}
#endif /* ESCARGOT_DEBUGGER */
virtual ASTNodeType type() override { return ASTNodeType::ThisExpression; }
virtual ByteCodeRegisterIndex getRegister(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context) override
{
- if (UNLIKELY(context->m_classInfo.m_thisExpressionIndex != REGULAR_REGISTER_LIMIT || codeBlock->m_codeBlock->isClassConstructor())) {
+ if (UNLIKELY(codeBlock->m_codeBlock->isClassConstructor())) {
return context->getRegister();
}
context->pushRegister(REGULAR_REGISTER_LIMIT);
virtual void generateExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister) override
{
- if (UNLIKELY(context->m_classInfo.m_thisExpressionIndex != REGULAR_REGISTER_LIMIT)) {
- codeBlock->pushCode(Move(ByteCodeLOC(m_loc.index), context->m_classInfo.m_thisExpressionIndex, dstRegister), context, this);
- return;
- }
if (UNLIKELY(codeBlock->m_codeBlock->needsToLoadThisBindingFromEnvironment())) {
codeBlock->pushCode(LoadThisBinding(ByteCodeLOC(m_loc.index), dstRegister), context, this);
return;
this->currentScopeContext = ctx;
this->lastUsingName = AtomicString();
#ifdef ESCARGOT_DEBUGGER
- if (this->escargotContext->debugger() && this->escargotContext->debugger()->enabled()) {
+ if (this->escargotContext->debuggerEnabled()) {
ctx->m_hasEval = true;
}
#endif /* ESCARGOT_DEBUGGER */
targetRecord->setHeapValueByIndex(state, info.m_indexForIndexedStorage, setterInputData);
}
-void* ArgumentsObject::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(ArgumentsObject)] = { 0 };
- Object::fillGCDescriptor(obj_bitmap);
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ArgumentsObject, m_targetRecord));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ArgumentsObject, m_sourceFunctionObject));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ArgumentsObject, m_parameterMap));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ArgumentsObject, m_modifiedArguments));
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(ArgumentsObject));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-
-
ArgumentsObject::ArgumentsObject(ExecutionState& state, Object* proto, ScriptFunctionObject* sourceFunctionObject, size_t argc, Value* argv, FunctionEnvironmentRecord* environmentRecordWillArgumentsObjectBeLocatedIn, bool isMapped)
: Object(state, proto, ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3)
, m_targetRecord(environmentRecordWillArgumentsObjectBeLocatedIn->isFunctionEnvironmentRecordOnStack() ? nullptr : environmentRecordWillArgumentsObjectBeLocatedIn)
virtual bool set(ExecutionState& state, const ObjectPropertyName& propertyName, const Value& v, const Value& receiver) override;
virtual bool setIndexedProperty(ExecutionState& state, const Value& property, const Value& value, const Value& receiver) override;
- virtual bool isInlineCacheable() override
+ virtual bool isArgumentsObject() const override
{
- return false;
+ return true;
}
- virtual bool isArgumentsObject() const override
+ virtual bool hasOwnEnumeration() const override
{
return true;
}
return m_sourceFunctionObject;
}
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
-
private:
FunctionEnvironmentRecord* m_targetRecord;
ScriptFunctionObject* m_sourceFunctionObject;
+++ /dev/null
-/*
- * Copyright (c) 2021-present Samsung Electronics Co., Ltd
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- * USA
- */
-
-#include "Escargot.h"
-#include "runtime/VMInstance.h"
-#include "runtime/BackingStore.h"
-#include "runtime/ArrayBuffer.h"
-#include "runtime/TypedArrayInlines.h"
-#include "runtime/ArrayBufferObject.h"
-#include "runtime/SharedArrayBufferObject.h"
-
-namespace Escargot {
-
-unsigned TypedArrayHelper::elementSizeTable[11] = { 1, 2, 4, 1, 2, 4, 1, 4, 8, 8, 8 };
-
-ArrayBuffer::ArrayBuffer(ExecutionState& state, Object* proto)
- : Object(state, proto, ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER)
-{
-}
-
-// $24.1.1.6
-Value ArrayBuffer::getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian)
-{
- // If isLittleEndian is not present, set isLittleEndian to either true or false.
- ASSERT(byteLength());
- size_t elemSize = TypedArrayHelper::elementSize(type);
- ASSERT(byteindex + elemSize <= byteLength());
- uint8_t* rawStart = data() + byteindex;
- if (LIKELY(isLittleEndian)) {
- return TypedArrayHelper::rawBytesToNumber(state, type, rawStart);
- } else {
- uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
- for (size_t i = 0; i < elemSize; i++) {
- rawBytes[elemSize - i - 1] = rawStart[i];
- }
- return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes);
- }
-}
-
-// $24.1.1.8
-void ArrayBuffer::setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian)
-{
- // If isLittleEndian is not present, set isLittleEndian to either true or false.
- ASSERT(byteLength());
- size_t elemSize = TypedArrayHelper::elementSize(type);
- ASSERT(byteindex + elemSize <= byteLength());
- uint8_t* rawStart = data() + byteindex;
- uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
- TypedArrayHelper::numberToRawBytes(state, type, val, rawBytes);
- if (LIKELY(isLittleEndian)) {
- memcpy(rawStart, rawBytes, elemSize);
- } else {
- for (size_t i = 0; i < elemSize; i++) {
- rawStart[i] = rawBytes[elemSize - i - 1];
- }
- }
-}
-} // namespace Escargot
};
class ArrayBuffer : public Object {
- friend void initializeCustomAllocators();
-
public:
static const uint64_t maxArrayBufferSize = 210000000;
- explicit ArrayBuffer(ExecutionState& state, Object* proto);
+ explicit ArrayBuffer(ExecutionState& state, Object* proto)
+ : Object(state, proto, ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER)
+ {
+ }
virtual bool isArrayBuffer() const override
{
return true;
}
+ // pure virtual function to ensure that ArrayBuffer instance never created
+ // all these pure virtual functions guarantee thread-safe operations
+ virtual void fillData(const uint8_t* newData, size_t length) = 0;
+ virtual Value getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian = true) = 0;
+ virtual void setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian = true) = 0;
+
Optional<BackingStore*> backingStore()
{
return m_backingStore;
return m_backingStore->maxByteLength();
}
- // $24.1.1.6
- Value getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian = true);
-
- // $24.1.1.8
- void setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian = true);
-
ALWAYS_INLINE bool isDetachedBuffer()
{
return (data() == nullptr);
}
}
- void fillData(const uint8_t* newData, size_t length)
- {
- ASSERT(!isDetachedBuffer());
- memcpy(data(), newData, length);
- }
-
void* operator new(size_t size) = delete;
void* operator new[](size_t size) = delete;
namespace Escargot {
+unsigned TypedArrayHelper::elementSizeTable[11] = { 1, 2, 4, 1, 2, 4, 1, 4, 8, 8, 8 };
+
ArrayBufferObject* ArrayBufferObject::allocateArrayBuffer(ExecutionState& state, Object* constructor, uint64_t byteLength, Optional<uint64_t> maxByteLength)
{
// https://www.ecma-international.org/ecma-262/10.0/#sec-allocatearraybuffer
return CustomAllocator<ArrayBufferObject>().allocate(1);
#endif
}
+
+Value ArrayBufferObject::getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian)
+{
+ // If isLittleEndian is not present, set isLittleEndian to either true or false.
+ ASSERT(byteLength());
+ size_t elemSize = TypedArrayHelper::elementSize(type);
+ ASSERT(byteindex + elemSize <= byteLength());
+ uint8_t* rawStart = data() + byteindex;
+ if (LIKELY(isLittleEndian)) {
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawStart);
+ } else {
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
+ for (size_t i = 0; i < elemSize; i++) {
+ rawBytes[elemSize - i - 1] = rawStart[i];
+ }
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes);
+ }
+}
+
+void ArrayBufferObject::setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian)
+{
+ // If isLittleEndian is not present, set isLittleEndian to either true or false.
+ ASSERT(byteLength());
+ size_t elemSize = TypedArrayHelper::elementSize(type);
+ ASSERT(byteindex + elemSize <= byteLength());
+ uint8_t* rawStart = data() + byteindex;
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
+ TypedArrayHelper::numberToRawBytes(state, type, val, rawBytes);
+ if (LIKELY(isLittleEndian)) {
+ memcpy(rawStart, rawBytes, elemSize);
+ } else {
+ for (size_t i = 0; i < elemSize; i++) {
+ rawStart[i] = rawBytes[elemSize - i - 1];
+ }
+ }
+}
} // namespace Escargot
void attachBuffer(BackingStore* backingStore);
void detachArrayBuffer();
- virtual bool isArrayBufferObject() const
+ virtual bool isArrayBufferObject() const override
{
return true;
}
+ virtual void fillData(const uint8_t* newData, size_t length) override
+ {
+ ASSERT(!isDetachedBuffer());
+ memcpy(data(), newData, length);
+ }
+
+ virtual Value getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian = true) override;
+ virtual void setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian = true) override;
+
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
};
bool hasRD = hasRareData();
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
m_fastModeData.resizeWithUninitializedValues(oldLength, newLength);
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedSmallValue(EncodedSmallValue::EmptyValue);
+
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData.data() + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
#else
size_t oldCapacity = hasRD ? (size_t)rareData()->m_arrayObjectFastModeBufferCapacity : 0;
if (newLength > oldCapacity) {
m_fastModeData = (EncodedValue*)GC_REALLOC(m_fastModeData, sizeof(EncodedValue) * newLength);
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedValue(EncodedValue::EmptyValue);
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
} else {
} else {
m_fastModeData = (EncodedValue*)GC_REALLOC(m_fastModeData, sizeof(EncodedValue) * newLength);
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedValue(EncodedValue::EmptyValue);
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
}
#endif
}
m_fastModeData.resizeWithUninitializedValues(oldLength, newCapacity);
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedSmallValue(EncodedSmallValue::EmptyValue);
+
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData.data() + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
rd->m_arrayObjectFastModeBufferCapacity = newCapacity;
rd->m_arrayObjectFastModeBufferExpandCount++;
}
} else {
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedSmallValue(EncodedSmallValue::EmptyValue);
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData.data() + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
+
rd->m_arrayObjectFastModeBufferCapacity = oldCapacity;
}
#else
GC_FREE(m_fastModeData);
m_fastModeData = newFastModeData;
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedValue(EncodedValue::EmptyValue);
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
rd->m_arrayObjectFastModeBufferCapacity = newCapacity;
rd->m_arrayObjectFastModeBufferExpandCount++;
}
} else {
- for (size_t i = oldLength; i < newLength; i++) {
- m_fastModeData[i] = EncodedValue(EncodedValue::EmptyValue);
+ if (oldLength < newLength) {
+ memset(static_cast<void*>(m_fastModeData + oldLength), 0, sizeof(ObjectPropertyValue) * (newLength - oldLength));
}
rd->m_arrayObjectFastModeBufferCapacity = oldCapacity;
}
return Object::preventExtensions(state);
}
+uint64_t ArrayObject::length(ExecutionState& state)
+{
+ return arrayLength(state);
+}
+
ArrayIteratorObject::ArrayIteratorObject(ExecutionState& state, Object* a, Type type)
: IteratorObject(state, state.context()->globalObject()->arrayIteratorPrototype())
, m_array(a)
static ArrayObject* createSpreadArray(ExecutionState& state);
- virtual bool isInlineCacheable() override
+ virtual bool hasOwnEnumeration() const override
{
- return false;
+ return true;
}
virtual ObjectHasPropertyResult hasProperty(ExecutionState& state, const ObjectPropertyName& P) override;
virtual ObjectHasPropertyResult hasIndexedProperty(ExecutionState& state, const Value& propertyName) override;
virtual bool setIndexedProperty(ExecutionState& state, const Value& property, const Value& value, const Value& receiver) override;
virtual bool preventExtensions(ExecutionState&) override;
+ virtual uint64_t length(ExecutionState& state) override;
// Use custom allocator for Array object (for Badtime)
void* operator new(size_t size);
init(map, src, len);
}
-AtomicString::AtomicString(ExecutionState& ec, String* name)
-{
- init(ec.context()->m_atomicStringMap, name);
-}
-
class ASCIIStringOnStack : public String {
public:
ASCIIStringOnStack(const char* str, size_t len)
auto iter = map->find(&stringForSearch);
if (map->end() == iter) {
- ASCIIString* newStr;
+ String* newStr;
if (fromExternalMemory) {
newStr = new ASCIIStringFromExternalMemory(src, len);
} else {
- newStr = new ASCIIString(src, len);
+ newStr = String::fromLatin1(reinterpret_cast<const LChar*>(src), len);
}
map->insert(newStr);
m_string = newStr;
if (map->end() == iter) {
String* newStr;
if (isAllASCII(src, len)) {
- newStr = new ASCIIString(src, len);
+ newStr = String::fromLatin1(src, len);
} else {
newStr = new UTF16String(src, len);
}
String* newString;
auto buffer = sv.bufferAccessData();
if (buffer.has8BitContent) {
- newString = new Latin1String((const char*)buffer.buffer, buffer.length);
+ newString = String::fromLatin1(reinterpret_cast<const LChar*>(buffer.buffer), buffer.length);
} else {
newString = new UTF16String((const char16_t*)buffer.buffer, buffer.length);
}
}
}
-AtomicString::AtomicString(Context* c, String* name)
-{
- init(c->m_atomicStringMap, name);
-}
-
void AtomicString::initStaticString(AtomicStringMap* ec, String* name)
{
ASSERT(ec->find(name) == ec->end());
name->m_tag = (size_t)POINTER_VALUE_STRING_TAG_IN_DATA | (size_t)m_string;
}
-void AtomicString::init(AtomicStringMap* ec, String* name)
+void AtomicString::init(Context* c, String* name)
{
- size_t v = name->getTagInFirstDataArea();
- if (v > POINTER_VALUE_STRING_TAG_IN_DATA) {
- m_string = (String*)(v & ~POINTER_VALUE_STRING_TAG_IN_DATA);
- return;
- }
-
+ AtomicStringMap* ec = c->atomicStringMap();
auto iter = ec->find(name);
if (ec->end() == iter) {
if (name->isStringView()) {
auto buffer = name->bufferAccessData();
if (buffer.has8BitContent) {
- name = new Latin1String((const char*)buffer.buffer, buffer.length);
+ name = String::fromLatin1(reinterpret_cast<const LChar*>(buffer.buffer), buffer.length);
} else {
name = new UTF16String((const char16_t*)buffer.buffer, buffer.length);
}
AtomicString(AtomicStringMap* map, const char* src, size_t len, FromExternalMemoryTag);
AtomicString(AtomicStringMap* map, const LChar* src, size_t len);
AtomicString(AtomicStringMap* map, const char16_t* src, size_t len);
- AtomicString(ExecutionState& ec, String* name);
+ AtomicString(ExecutionState& ec, String* name)
+ : AtomicString(ec.context(), name)
+ {
+ }
AtomicString(ExecutionState& ec, const char16_t* src, size_t len);
AtomicString(ExecutionState& ec, const char* src, size_t len);
template <const size_t srcLen>
{
}
AtomicString(Context* c, const StringView& sv);
- AtomicString(Context* c, String* name);
+ ALWAYS_INLINE AtomicString(Context* c, String* name)
+ {
+ // fast path
+ size_t v = name->getTagInFirstDataArea();
+ if (v > POINTER_VALUE_STRING_TAG_IN_DATA) {
+ m_string = (String*)(v & ~POINTER_VALUE_STRING_TAG_IN_DATA);
+ return;
+ }
+ init(c, name);
+ }
inline String* string() const
{
void init(AtomicStringMap* ec, const char* src, size_t len, bool fromExternalMemory = false);
void init(AtomicStringMap* ec, const LChar* str, size_t len);
void init(AtomicStringMap* ec, const char16_t* src, size_t len);
- void init(AtomicStringMap* ec, String* name);
+ void init(Context* c, String* name);
void initStaticString(AtomicStringMap* ec, String* name);
String* m_string;
};
nullptr, nullptr, nullptr);
}
+void SharedBackingStore::resize(size_t newByteLength)
+{
+ ASSERT(m_sharedDataBlockInfo->hasValidReference());
+ ASSERT(isResizable() && newByteLength <= maxByteLength());
+
+ m_sharedDataBlockInfo->grow(newByteLength);
+}
+
void* SharedBackingStore::operator new(size_t size)
{
// SharedBackingStore does not have any GC member
return 0;
}
+ virtual void grow(size_t newByteLength)
+ {
+ UNUSED_PARAMETER(newByteLength);
+ ASSERT_NOT_REACHED();
+ }
+
void* data() const
{
ASSERT(hasValidReference());
size_t byteLength() const
{
ASSERT(hasValidReference());
- return m_byteLength;
+ return m_byteLength.load();
}
void ref()
protected:
void* m_data;
- size_t m_byteLength;
+ // defined as atomic value to not to use a lock
+ std::atomic<size_t> m_byteLength;
std::atomic<size_t> m_refCount;
};
return m_maxByteLength;
}
+ virtual void grow(size_t newByteLength) override
+ {
+ ASSERT(newByteLength <= m_maxByteLength);
+ m_byteLength.store(newByteLength);
+ }
+
private:
- size_t m_maxByteLength;
+ // defined once and never change
+ const size_t m_maxByteLength;
};
class SharedBackingStore : public BackingStore {
return m_sharedDataBlockInfo->isGrowable();
}
+ virtual void resize(size_t newByteLength) override;
+
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
{
}
-void* BigIntObject::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(BigIntObject)] = { 0 };
- Object::fillGCDescriptor(obj_bitmap);
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(BigIntObject, m_primitiveValue));
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(BigIntObject));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
} // namespace Escargot
m_primitiveValue = data;
}
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
-
private:
BigInt* m_primitiveValue;
};
{
}
-void* BooleanObject::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(BooleanObject)] = { 0 };
- Object::fillGCDescriptor(obj_bitmap);
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(BooleanObject));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
} // namespace Escargot
return true;
}
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
-
private:
bool m_primitiveValue : 1;
};
: String()
, m_isOwnerMayFreed(false)
, m_isCompressed(false)
+ , m_refCount(0)
, m_vmInstance(instance)
, m_lastUsedTickcount(fastTickCount())
{
v.push_back(this);
GC_REGISTER_FINALIZER_NO_ORDER(this, [](void* obj, void*) {
CompressibleString* self = (CompressibleString*)obj;
+ ASSERT(self->refCount() == 0);
+
if (self->isCompressed()) {
self->m_compressedData.~CompressedDataVector();
} else {
bool CompressibleString::compress()
{
ASSERT(!m_isCompressed);
- if (UNLIKELY(!m_bufferData.length)) {
+ if (UNLIKELY(!m_bufferData.length || m_refCount > 0)) {
return false;
}
bool has8Bit = m_bufferData.has8BitContent;
if (has8Bit) {
- return compressWorker<LChar>(currentStackPointer());
+ return compressWorker<LChar>();
} else {
- return compressWorker<char16_t>(currentStackPointer());
+ return compressWorker<char16_t>();
}
}
constexpr static const size_t g_compressChunkSize = 1044465;
static_assert(LZ4_COMPRESSBOUND(g_compressChunkSize) == 1024 * 1024, "");
-static ATTRIBUTE_NO_SANITIZE_ADDRESS bool testPointerExistsOnStack(size_t* start, size_t* end, const void* ptr)
-{
- while (start != end) {
- if (UNLIKELY(*start == (size_t)ptr)) {
- // if there is reference on stack, we cannot compress string.
- return true;
- }
- start++;
- }
-
- return false;
-}
-
template <typename StringType>
-bool CompressibleString::compressWorker(void* callerSP)
+bool CompressibleString::compressWorker()
{
- ASSERT(!m_isCompressed);
+ ASSERT(!m_isCompressed && !m_refCount);
ASSERT(m_bufferData.length > 0);
-#if defined(STACK_GROWS_DOWN)
- size_t* start = (size_t*)((size_t)callerSP & ~(sizeof(size_t) - 1));
- size_t* end = (size_t*)m_vmInstance->stackStartAddress();
-#else
- size_t* start = (size_t*)m_vmInstance->stackStartAddress();
- size_t* end = (size_t*)((size_t)callerSP & ~(sizeof(size_t) - 1));
-#endif
-
- if (testPointerExistsOnStack(start, end, m_bufferData.buffer)) {
- return false;
- }
-
size_t originByteLength = m_bufferData.length * sizeof(StringType);
int lastBoundLength = 0;
std::unique_ptr<char[]> compBuffer;
if (isCompressed()) {
decompress();
}
- return StringBufferAccessData(m_bufferData.has8BitContent, m_bufferData.length, const_cast<void*>(m_bufferData.buffer));
+
+ // add refCount pointer to count its usage in StringBufferAccessData
+ return StringBufferAccessData(m_bufferData.has8BitContent, m_bufferData.length, const_cast<void*>(m_bufferData.buffer), &m_refCount);
}
bool isCompressed()
return m_isCompressed;
}
+ size_t refCount() const
+ {
+ return m_refCount;
+ }
+
void* operator new(size_t);
void* operator new[](size_t) = delete;
void operator delete[](void*) = delete;
}
template <typename StringType>
- NEVER_INLINE bool compressWorker(void* callerSP);
+ NEVER_INLINE bool compressWorker();
template <typename StringType>
NEVER_INLINE void decompressWorker();
bool m_isOwnerMayFreed;
bool m_isCompressed;
+ size_t m_refCount; // reference count representing the usage of this CompressibleString
VMInstance* m_vmInstance;
uint64_t m_lastUsedTickcount;
typedef std::vector<std::vector<char>> CompressedDataVector;
#ifdef ESCARGOT_DEBUGGER
-bool Context::initDebugger(const char* options)
+bool Context::initDebuggerRemote(const char* options)
{
- if (m_debugger != nullptr) {
+ if (debuggerEnabled()) {
// debugger cannot be re-initialized
return false;
}
- m_debugger = createDebugger(options, &m_instance->m_debuggerEnabled);
- return m_debugger->enabled();
+ Debugger::createDebuggerRemote(options, this);
+ return m_debugger != nullptr;
+}
+
+void Context::initDebugger(Debugger* debugger)
+{
+ ASSERT(m_debugger == nullptr);
+ m_debugger = debugger;
+}
+
+void Context::removeDebugger()
+{
+ ASSERT(m_debugger != nullptr);
+ m_debugger = nullptr;
+}
+
+bool Context::debuggerEnabled() const
+{
+ return m_debugger != nullptr;
}
void Context::printDebugger(StringView* output)
{
- if (!m_debugger || !m_debugger->enabled()) {
+ if (debuggerEnabled()) {
+ m_debugger->consoleOut(output);
+ }
+}
+
+String* Context::getClientSource(String** sourceName)
+{
+ if (debuggerEnabled()) {
+ return m_debugger->getClientSource(sourceName);
+ }
+ return nullptr;
+}
+
+void Context::setAsAlwaysStopState()
+{
+ if (!debuggerEnabled()) {
return;
}
- m_debugger->sendType(Debugger::ESCARGOT_MESSAGE_PRINT);
- if (m_debugger->enabled()) {
- m_debugger->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, output);
+
+ m_debugger->setStopState(ESCARGOT_DEBUGGER_ALWAYS_STOP);
+}
+
+bool Context::inDebuggingCodeMode() const
+{
+ if (!debuggerEnabled()) {
+ return false;
}
+
+ return m_debugger->inDebuggingCodeMode();
}
-String* Context::getClientSource(String** sourceName)
+void Context::pumpDebuggerEvents()
{
- if (!m_debugger || !m_debugger->enabled()) {
- return nullptr;
+ if (!debuggerEnabled()) {
+ return;
}
- return m_debugger->getClientSource(sourceName);
+
+ SandBox sb(this);
+ sb.run([](ExecutionState& state, void* d) -> Value {
+ Debugger* debugger = reinterpret_cast<Debugger*>(d);
+ debugger->pumpDebuggerEvents(&state);
+ return Value();
+ },
+ m_debugger);
}
#endif /* ESCARGOT_DEBUGGER */
return m_debugger;
}
- bool initDebugger(const char* options);
+ bool initDebuggerRemote(const char* options);
+ void initDebugger(Debugger* debugger);
+ void removeDebugger();
+ bool debuggerEnabled() const;
void printDebugger(StringView* output);
+ void pumpDebuggerEvents();
+ void setAsAlwaysStopState();
+ bool inDebuggingCodeMode() const;
String* getClientSource(String** sourceName);
#endif /* ESCARGOT_DEBUGGER */
int32_t stdOffset = 0, dstOffset = 0;
// roughly check range before calling yearFromTime function
-#if defined(ENABLE_ICU)
+#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
stdOffset = vzone_getRawOffset(state.context()->vmInstance()->timezone());
#else
stdOffset = 0;
time64_t msBetweenYears = (realYear != equivalentYear) ? (timeFromYear(equivalentYear) - timeFromYear(realYear)) : 0;
t += msBetweenYears;
-#if defined(ENABLE_ICU)
+#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
vzone_getOffset3(state.context()->vmInstance()->timezone(), t, true, stdOffset, dstOffset, succ);
#else
dstOffset = 0;
t += msBetweenYears;
int32_t stdOffset = 0, dstOffset = 0;
-#if defined(ENABLE_ICU)
+#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
UErrorCode succ = U_ZERO_ERROR;
vzone_getOffset3(state.context()->vmInstance()->timezone(), t, true, stdOffset, dstOffset, succ);
#endif
public:
enum ForceUninitializedTag { ForceUninitialized };
enum EmptyValueInitTag { EmptyValue };
+ COMPILE_ASSERT(EncodedValue::EmptyValue == 0, "");
EncodedValue(ForceUninitializedTag)
{
class EncodedSmallValue {
public:
enum EmptyValueInitTag { EmptyValue };
+ COMPILE_ASSERT(EncodedSmallValue::EmptyValue == 0, "");
EncodedSmallValue(EmptyValueInitTag)
{
void EnumerateObject::update(ExecutionState& state)
{
- EncodedValueVector newKeys;
+ EncodedValueTightVector newKeys;
executeEnumeration(state, newKeys);
- Vector<Value, GCUtil::gc_malloc_allocator<Value>> differenceKeys;
+ VectorWithInlineStorage<32, Value, GCUtil::gc_malloc_allocator<Value>> differenceKeys;
for (size_t i = 0; i < newKeys.size(); i++) {
const auto& key = newKeys[i];
// If a property that has not yet been visited during enumeration is deleted, then it will not be visited.
}
}
-void EnumerateObjectWithDestruction::executeEnumeration(ExecutionState& state, EncodedValueVector& keys)
+void EnumerateObjectWithDestruction::executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys)
{
ASSERT(!!m_object);
m_hiddenClass = m_object->structure();
struct Properties {
- std::vector<Value::ValueIndex> indexes;
+ std::multiset<Value::ValueIndex, std::less<Value::ValueIndex>> indexes;
VectorWithInlineStorage<32, EncodedValue, GCUtil::gc_malloc_allocator<EncodedValue>> strings;
VectorWithInlineStorage<4, Value, GCUtil::gc_malloc_allocator<Value>> symbols;
} properties;
m_object->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
auto properties = (Properties*)data;
- auto value = name.toPlainValue(state);
+ auto value = name.toPlainValue();
if (desc.isEnumerable()) {
Value::ValueIndex nameAsIndexValue;
if (value.isSymbol()) {
properties->symbols.push_back(value);
} else if (name.isIndexString() && (nameAsIndexValue = value.toIndex(state)) != Value::InvalidIndexValue) {
- properties->indexes.push_back(nameAsIndexValue);
+ properties->indexes.insert(nameAsIndexValue);
} else {
properties->strings.push_back(value);
}
},
&properties, false);
- std::sort(properties.indexes.begin(), properties.indexes.end(), std::less<Value::ValueIndex>());
-
keys.resizeWithUninitializedValues(properties.indexes.size() + properties.strings.size() + properties.symbols.size());
size_t idx = 0;
return false;
}
-void EnumerateObjectWithIteration::executeEnumeration(ExecutionState& state, EncodedValueVector& keys)
+void EnumerateObjectWithIteration::executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys)
{
ASSERT(!!m_object);
m_hiddenClassChain.clear();
}
bool shouldSearchProto = false;
-
m_hiddenClassChain.push_back(m_object->structure());
std::unordered_set<String*, std::hash<String*>, std::equal_to<String*>, GCUtil::gc_malloc_allocator<String*>> keyStringSet;
- Value proto = m_object->getPrototype(state);
- while (proto.isObject()) {
+ Object* proto = m_object->getPrototypeObject(state);
+ while (proto) {
if (!shouldSearchProto) {
- proto.asObject()->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
- if (desc.isEnumerable()) {
- bool* shouldSearchProto = (bool*)data;
- *shouldSearchProto = true;
- return false;
- }
- return true;
- },
- &shouldSearchProto);
+ if (proto->hasOwnEnumeration()) {
+ proto->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
+ if (desc.isEnumerable()) {
+ bool* shouldSearchProto = (bool*)data;
+ *shouldSearchProto = true;
+ return false;
+ }
+ return true;
+ },
+ &shouldSearchProto);
+
+ } else {
+ shouldSearchProto |= proto->structure()->hasEnumerableProperty();
+ }
}
- ASSERT(!!proto.asObject()->structure());
- m_hiddenClassChain.push_back(proto.asObject()->structure());
- proto = proto.asObject()->getPrototype(state);
+ m_hiddenClassChain.push_back(proto->structure());
+ proto = proto->getPrototypeObject(state);
}
if (shouldSearchProto) {
// TODO sorting properties
struct EData {
std::unordered_set<String*, std::hash<String*>, std::equal_to<String*>, GCUtil::gc_malloc_allocator<String*>>* keyStringSet;
- EncodedValueVector* keys;
+ VectorWithInlineStorage<32, EncodedValue, GCUtil::gc_malloc_allocator<EncodedValue>> keys;
Object* obj;
} eData;
eData.keyStringSet = &keyStringSet;
- eData.keys = &keys;
eData.obj = m_object;
- Value target = m_object;
- while (target.isObject()) {
- target.asObject()->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
+ Object* target = m_object;
+ while (target) {
+ target->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
EData* eData = (EData*)data;
if (desc.isEnumerable()) {
- String* key = name.toPlainValue(state).toString(state);
+ String* key = name.toPlainValue().toString(state);
auto iter = eData->keyStringSet->find(key);
if (iter == eData->keyStringSet->end()) {
eData->keyStringSet->insert(key);
- eData->keys->pushBack(Value(key));
+ eData->keys.pushBack(Value(key));
}
} else if (self == eData->obj) {
// 12.6.4 The values of [[Enumerable]] attributes are not considered
// when determining if a property of a prototype object is shadowed by a previous object on the prototype chain.
- String* key = name.toPlainValue(state).toString(state);
+ String* key = name.toPlainValue().toString(state);
ASSERT(eData->keyStringSet->find(key) == eData->keyStringSet->end());
eData->keyStringSet->insert(key);
}
return true;
},
- &eData);
- target = target.asObject()->getPrototype(state);
+ &eData);
+ target = target->getPrototypeObject(state);
}
+
+ keys.resizeWithUninitializedValues(eData.keys.size());
+ for (size_t i = 0; i < eData.keys.size(); i++) {
+ keys[i] = eData.keys[i];
+ }
+
} else {
- struct Properties {
- std::vector<Value::ValueIndex> indexes;
- VectorWithInlineStorage<32, EncodedValue, GCUtil::gc_malloc_allocator<EncodedValue>> strings;
- } properties;
-
- m_object->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
- auto properties = (Properties*)data;
- auto value = name.toPlainValue(state);
- if (desc.isEnumerable()) {
- Value::ValueIndex nameAsIndexValue;
- if (name.isIndexString() && (nameAsIndexValue = value.toIndex(state)) != Value::InvalidIndexValue) {
- properties->indexes.push_back(nameAsIndexValue);
- } else {
- properties->strings.push_back(value);
+ if (m_object->hasOwnEnumeration() || m_object->structure()->hasIndexPropertyName()) {
+ struct Properties {
+ std::multiset<Value::ValueIndex, std::less<Value::ValueIndex>> indexes;
+ VectorWithInlineStorage<32, EncodedValue, GCUtil::gc_malloc_allocator<EncodedValue>> strings;
+ } properties;
+
+ m_object->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
+ auto properties = (Properties*)data;
+ auto value = name.toPlainValue();
+ if (desc.isEnumerable()) {
+ Value::ValueIndex nameAsIndexValue;
+ if (name.isIndexString() && (nameAsIndexValue = value.toIndex(state)) != Value::InvalidIndexValue) {
+ properties->indexes.insert(nameAsIndexValue);
+ } else {
+ properties->strings.push_back(value);
+ }
}
- }
- return true;
- },
- &properties);
-
- std::sort(properties.indexes.begin(), properties.indexes.end(), std::less<Value::ValueIndex>());
+ return true;
+ },
+ &properties);
- keys.resizeWithUninitializedValues(properties.indexes.size() + properties.strings.size());
- size_t idx = 0;
- for (auto& v : properties.indexes) {
- keys[idx++] = Value(v).toString(state);
- }
- for (auto& v : properties.strings) {
- keys[idx++] = v;
+ keys.resizeWithUninitializedValues(properties.indexes.size() + properties.strings.size());
+ size_t idx = 0;
+ for (auto& v : properties.indexes) {
+ keys[idx++] = Value(v).toString(state);
+ }
+ for (auto& v : properties.strings) {
+ keys[idx++] = v;
+ }
+ } else {
+ m_object->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
+ EncodedValueTightVector* keys = reinterpret_cast<EncodedValueTightVector*>(data);
+ auto value = name.toPlainValue();
+ if (desc.isEnumerable()) {
+ ASSERT(!name.isIndexString() || value.toIndex(state) == Value::InvalidIndexValue);
+ keys->pushBack(value);
+ }
+ return true;
+ },
+ &keys);
}
}
if (UNLIKELY(hc != structure)) {
return true;
}
- Value val = obj->getPrototype(state);
- if (val.isObject()) {
- obj = val.asObject();
+ Object* val = obj->getPrototypeObject(state);
+ if (val) {
+ obj = val;
} else {
break;
}
return false;
}
} // namespace Escargot
+;
class EnumerateObject : public PointerValue {
public:
- virtual bool isEnumerateObject() const
+ virtual bool isEnumerateObject() const override
{
return true;
}
}
size_t m_index;
- EncodedValueVector m_keys;
+ EncodedValueTightVector m_keys;
protected:
EnumerateObject(Object* obj)
void update(ExecutionState& state);
- virtual void executeEnumeration(ExecutionState& state, EncodedValueVector& keys) = 0;
+ virtual void executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys) = 0;
virtual bool checkIfModified(ExecutionState& state) = 0;
Object* m_object;
- uint64_t m_arrayLength;
+ uint32_t m_arrayLength;
};
// enumerate object for destruction operation e.g. var obj = { a, ...b };
virtual void fillRestElement(ExecutionState& state, Object* result) override;
+ inline void* operator new(size_t size, void* p)
+ {
+ return p;
+ }
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
protected:
- virtual void executeEnumeration(ExecutionState& state, EncodedValueVector& keys) override;
+ virtual void executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys) override;
virtual bool checkIfModified(ExecutionState& state) override;
ObjectStructure* m_hiddenClass;
};
// enumerate object for iteration operation (for-in)
-// exclude symbol, include prototype chain and check modification during enumetation
+// exclude symbol, include prototype chain and check modification during enumeration
class EnumerateObjectWithIteration : public EnumerateObject {
public:
EnumerateObjectWithIteration(ExecutionState& state, Object* obj)
executeEnumeration(state, m_keys);
}
+ inline void* operator new(size_t size, void* p)
+ {
+ return p;
+ }
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
protected:
- virtual void executeEnumeration(ExecutionState& state, EncodedValueVector& keys) override;
+ virtual void executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys) override;
virtual bool checkIfModified(ExecutionState& state) override;
Vector<ObjectStructure*, GCUtil::gc_malloc_allocator<ObjectStructure*>> m_hiddenClassChain;
m_heapStorage[slot.m_index] = v;
}
+template <bool canBindThisValue, bool hasNewTarget, size_t inlineStorageSize>
+FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValue, hasNewTarget, inlineStorageSize>::FunctionEnvironmentRecordOnHeapWithInlineStorage(ScriptFunctionObject* function)
+ : FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>(function)
+{
+}
+
+template <bool canBindThisValue, bool hasNewTarget, size_t inlineStorageSize>
+void FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValue, hasNewTarget, inlineStorageSize>::setMutableBindingByBindingSlot(ExecutionState& state, const EnvironmentRecord::BindingSlot& slot, const AtomicString& name, const Value& v)
+{
+ // Storing to const variable check only (TDZ check is already done by bytecode generation)
+ const auto& recordInfo = FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>::functionObject()->interpretedCodeBlock()->identifierInfos();
+ if (UNLIKELY(!recordInfo[slot.m_index].m_isMutable)) {
+ if (state.inStrictMode()) {
+ ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::AssignmentToConstantVariable, name);
+ }
+ return;
+ }
+ m_inlineStorage[slot.m_index] = v;
+}
+
template <bool canBindThisValue, bool hasNewTarget>
FunctionEnvironmentRecordNotIndexed<canBindThisValue, hasNewTarget>::FunctionEnvironmentRecordNotIndexed(ScriptFunctionObject* function)
: FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>(function)
template class FunctionEnvironmentRecordOnHeap<true, true>;
template class FunctionEnvironmentRecordOnHeap<false, true>;
+#define DEFINE_FE_WITH_INLINE_STORAGE(num) \
+ template class FunctionEnvironmentRecordOnHeapWithInlineStorage<false, false, num>; \
+ template class FunctionEnvironmentRecordOnHeapWithInlineStorage<true, true, num>; \
+ template class FunctionEnvironmentRecordOnHeapWithInlineStorage<false, true, num>;
+
+DEFINE_FE_WITH_INLINE_STORAGE(1)
+DEFINE_FE_WITH_INLINE_STORAGE(2)
+DEFINE_FE_WITH_INLINE_STORAGE(3)
+DEFINE_FE_WITH_INLINE_STORAGE(4)
+DEFINE_FE_WITH_INLINE_STORAGE(5)
+
template class FunctionEnvironmentRecordNotIndexed<false, false>;
template class FunctionEnvironmentRecordNotIndexed<true, true>;
template class FunctionEnvironmentRecordNotIndexed<false, true>;
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-global-environment-records
class GlobalEnvironmentRecord : public EnvironmentRecord {
#ifdef ESCARGOT_DEBUGGER
- friend class Debugger;
+ friend class DebuggerRemote;
#endif /* ESCARGOT_DEBUGGER */
public:
GlobalEnvironmentRecord(ExecutionState& state, InterpretedCodeBlock* codeBlock, GlobalObject* global, IdentifierRecordVector* globalDeclarativeRecord, EncodedValueVector* globalDeclarativeStorage);
return m_globalCodeBlock;
}
+ GlobalObject* globalObject()
+ {
+ return m_globalObject;
+ }
+
private:
InterpretedCodeBlock* m_globalCodeBlock;
GlobalObject* m_globalObject;
// DeclarativeEnvironmentRecordNotIndexed record does not create binding self likes FunctionEnvironmentRecord
class DeclarativeEnvironmentRecordNotIndexed : public DeclarativeEnvironmentRecord {
#ifdef ESCARGOT_DEBUGGER
- friend class Debugger;
+ friend class DebuggerRemote;
#endif /* ESCARGOT_DEBUGGER */
public:
DeclarativeEnvironmentRecordNotIndexed(ExecutionState& state, bool isVarDeclarationTarget = false, bool isCatchClause = false)
return false;
}
- virtual EncodedValueTightVector& heapStorage()
- {
- RELEASE_ASSERT_NOT_REACHED();
- }
-
Object* homeObject()
{
return functionObject()->homeObject();
RELEASE_ASSERT_NOT_REACHED();
}
- EncodedValueTightVector& heapStorage() override
+private:
+ EncodedValueTightVector m_heapStorage;
+};
+
+template <bool canBindThisValue, bool hasNewTarget, size_t inlineStorageSize>
+class FunctionEnvironmentRecordOnHeapWithInlineStorage : public FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget> {
+ friend class LexicalEnvironment;
+ friend class ByteCodeInterpreter;
+ friend class ScriptFunctionObject;
+
+public:
+ FunctionEnvironmentRecordOnHeapWithInlineStorage(ScriptFunctionObject* function);
+
+ virtual void setHeapValueByIndex(ExecutionState& state, const size_t idx, const Value& v) override
+ {
+ m_inlineStorage[idx] = v;
+ }
+
+ virtual Value getHeapValueByIndex(ExecutionState& state, const size_t idx) override
+ {
+ return m_inlineStorage[idx];
+ }
+
+ virtual EnvironmentRecord::GetBindingValueResult getBindingValue(ExecutionState& state, const AtomicString& name) override
+ {
+ const auto& v = FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>::functionObject()->interpretedCodeBlock()->identifierInfos();
+
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i].m_name == name) {
+ return EnvironmentRecord::GetBindingValueResult(m_inlineStorage[v[i].m_indexForIndexedStorage]);
+ }
+ }
+ return EnvironmentRecord::GetBindingValueResult();
+ }
+
+ virtual EnvironmentRecord::BindingSlot hasBinding(ExecutionState& state, const AtomicString& name) override
+ {
+ const auto& v = FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>::functionObject()->interpretedCodeBlock()->identifierInfos();
+
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i].m_name == name) {
+ return EnvironmentRecord::BindingSlot(this, v[i].m_indexForIndexedStorage, false);
+ }
+ }
+ return EnvironmentRecord::BindingSlot(this, SIZE_MAX, false);
+ }
+
+ virtual bool deleteBinding(ExecutionState& state, const AtomicString& name) override
+ {
+ return false;
+ }
+
+ virtual void setMutableBindingByBindingSlot(ExecutionState& state, const EnvironmentRecord::BindingSlot& slot, const AtomicString& name, const Value& v) override;
+ virtual void setMutableBindingByIndex(ExecutionState& state, const size_t idx, const Value& v) override
+ {
+ m_inlineStorage[idx] = v;
+ }
+
+ virtual void initializeBindingByIndex(ExecutionState& state, const size_t idx, const Value& v) override
+ {
+ m_inlineStorage[idx] = v;
+ }
+
+ virtual void setMutableBinding(ExecutionState& state, const AtomicString& name, const Value& V) override
{
- return m_heapStorage;
+ const auto& v = FunctionEnvironmentRecordWithExtraData<canBindThisValue, hasNewTarget>::functionObject()->interpretedCodeBlock()->identifierInfos();
+
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i].m_name == name) {
+ m_inlineStorage[v[i].m_indexForIndexedStorage] = V;
+ return;
+ }
+ }
+ RELEASE_ASSERT_NOT_REACHED();
}
private:
- EncodedValueTightVector m_heapStorage;
+ EncodedValue m_inlineStorage[inlineStorageSize];
};
template <bool canBindThisValue, bool hasNewTarget>
class ModuleEnvironmentRecord : public DeclarativeEnvironmentRecord {
#ifdef ESCARGOT_DEBUGGER
- friend class Debugger;
+ friend class DebuggerRemote;
#endif /* ESCARGOT_DEBUGGER */
public:
struct ModuleBindingRecord {
WASMRuntimeError,
#endif
};
+
+ struct StackTraceGCData {
+ union {
+ ByteCodeBlock* byteCodeBlock;
+ String* infoString;
+ };
+ };
+
+ struct StackTraceNonGCData {
+ size_t byteCodePosition;
+ };
+
+ struct StackTraceData : public gc {
+ TightVector<StackTraceGCData, GCUtil::gc_malloc_allocator<StackTraceGCData>> gcValues;
+ TightVector<StackTraceNonGCData, GCUtil::gc_malloc_atomic_allocator<StackTraceNonGCData>> nonGCValues;
+ Value exception;
+
+ void buildStackTrace(Context* context, StringBuilder& builder);
+ static StackTraceData* create(SandBox* sandBox);
+
+ private:
+ StackTraceData() {}
+ };
+
static void throwBuiltinError(ExecutionState& state, Code code, const char* templateString)
{
throwBuiltinError(state, code, String::emptyString, false, String::emptyString, templateString);
}
+
static void throwBuiltinError(ExecutionState& state, Code code, const char* templateString, AtomicString templateDataString)
{
throwBuiltinError(state, code, templateDataString.string(), false, String::emptyString, templateString);
}
+
static void throwBuiltinError(ExecutionState& state, Code code, const char* templateString, String* templateDataString)
{
throwBuiltinError(state, code, templateDataString, false, String::emptyString, templateString);
}
+
static ErrorObject* createBuiltinError(ExecutionState& state, Code code, const char* templateString)
{
return createBuiltinError(state, code, String::emptyString, false, String::emptyString, templateString);
}
+
static ErrorObject* createError(ExecutionState& state, ErrorObject::Code code, String* errorMessage);
static ErrorObject* createBuiltinError(ExecutionState& state, Code code, String* objectName, bool prototype, String* functionName, const char* templateString);
static void throwBuiltinError(ExecutionState& state, Code code, String* objectName, bool prototype, String* functionName, const char* templateString);
return true;
}
- struct StackTraceGCData {
- union {
- ByteCodeBlock* byteCodeBlock;
- String* infoString;
- };
- };
- struct StackTraceNonGCData {
- size_t byteCodePosition;
- };
- struct StackTraceData : public gc {
- TightVector<StackTraceGCData, GCUtil::gc_malloc_allocator<StackTraceGCData>> gcValues;
- TightVector<StackTraceNonGCData, GCUtil::gc_malloc_atomic_allocator<StackTraceNonGCData>> nonGCValues;
- Value exception;
-
- void buildStackTrace(Context* context, StringBuilder& builder);
- static StackTraceData* create(SandBox* sandBox);
-
- private:
- StackTraceData() {}
- };
-
StackTraceData* stackTraceData()
{
return m_stackTraceData;
debugger->setActiveSavedStackTrace(activeSavedStackTraceExecutionState, activeSavedStackTrace);
}
- if (from != Generator && self->m_savedStackTrace == nullptr && debugger != nullptr && debugger->enabled()) {
+ debugger = state.context()->debugger();
+ if (from != Generator && self->m_savedStackTrace == nullptr && debugger != nullptr) {
self->m_savedStackTrace = Debugger::saveStackTrace(state);
}
#endif /* ESCARGOT_DEBUGGER */
if (record->isDeclarativeEnvironmentRecord() && record->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
return record->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject();
}
- } else if (es->m_isNativeFunctionObjectExecutionContext) {
+ } else if (es->isNativeFunctionObjectExecutionContext()) {
return es->m_calledNativeFunctionObject;
}
}
}
+Value ExecutionState::thisValue()
+{
+ LexicalEnvironment* lex = m_lexicalEnvironment;
+ while (lex) {
+ EnvironmentRecord* envRec = lex->record();
+ if (envRec->hasThisBinding()) {
+ if (envRec->isDeclarativeEnvironmentRecord() && envRec->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
+ auto fnRecord = envRec->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord();
+ ScriptFunctionObject* f = fnRecord->functionObject();
+ if (f->codeBlock()->isInterpretedCodeBlock() && f->codeBlock()->asInterpretedCodeBlock()->needsToLoadThisBindingFromEnvironment()) {
+ return fnRecord->getThisBinding(*this);
+ } else {
+ return f;
+ }
+ } else if (envRec->isGlobalEnvironmentRecord()) {
+ if (envRec->asGlobalEnvironmentRecord()->globalCodeBlock()->isStrict()) {
+ return Value();
+ } else {
+ return envRec->asGlobalEnvironmentRecord()->globalObject();
+ }
+ } else if (envRec->isModuleEnvironmentRecord()) {
+ return Value();
+ }
+ }
+
+ lex = lex->outerEnvironment();
+ }
+ return m_context->globalObject();
+}
+
Value ExecutionState::makeSuperPropertyReference()
{
// Let env be GetThisEnvironment( ).
return m_onFinally;
}
+ bool isNativeFunctionObjectExecutionContext() const
+ {
+ return m_isNativeFunctionObjectExecutionContext;
+ }
+
// callee is pauser && isNotInEvalCode
bool inPauserScope();
Object* getNewTarget();
// http://www.ecma-international.org/ecma-262/6.0/#sec-getthisenvironment
EnvironmentRecord* getThisEnvironment();
+ Value thisValue();
Value makeSuperPropertyReference();
Value getSuperConstructor();
namespace Escargot {
-void FunctionObject::initStructureAndValues(ExecutionState& state, bool isConstructor, bool isGenerator, bool isAsync)
+void FunctionObject::initStructureAndValues(ExecutionState& state, bool isConstructor, bool isGenerator)
{
if (isGenerator) {
// Unlike function instances, the object that is the value of the GeneratorFunction’s of AsyncGeneratorFunction prototype property
{
}
-FunctionObject::FunctionSource FunctionObject::createFunctionSourceFromScriptSource(ExecutionState& state, AtomicString functionName, size_t argCount, Value* argArray, Value bodyValue, bool useStrict, bool isGenerator, bool isAsync, bool allowSuperCall, bool isInternalSource)
+static String* createFunctionSource(ExecutionState& state, AtomicString functionName, size_t argCount, Value* argArray, Value bodyValue, bool useStrict, bool isGenerator, bool isAsync, bool isInternalSource)
{
StringBuilder src, parameters;
if (useStrict) {
char* dest = reinterpret_cast<char*>(malloc((data->m_dest->length()) * (is8Bit ? 1 : 2)));
+ {
auto headAccessData = data->m_head->bufferAccessData();
auto bodyAccessData = data->m_body->bufferAccessData();
if (is8Bit) {
- ASSERT(headAccessData.has8BitContent && bodyAccessData.has8BitContent);
- char* ptr = dest;
- memcpy(ptr, headAccessData.bufferAs8Bit, headAccessData.length);
- ptr += headAccessData.length;
- memcpy(ptr, bodyAccessData.bufferAs8Bit, bodyAccessData.length);
- ptr += bodyAccessData.length;
- ptr[0] = '\n';
- ptr[1] = '}';
+ ASSERT(headAccessData.has8BitContent && bodyAccessData.has8BitContent);
+ char* ptr = dest;
+ memcpy(ptr, headAccessData.bufferAs8Bit, headAccessData.length);
+ ptr += headAccessData.length;
+ memcpy(ptr, bodyAccessData.bufferAs8Bit, bodyAccessData.length);
+ ptr += bodyAccessData.length;
+ ptr[0] = '\n';
+ ptr[1] = '}';
} else {
char16_t* ptr = reinterpret_cast<char16_t*>(dest);
if (headAccessData.has8BitContent) {
ptr[0] = '\n';
ptr[1] = '}';
}
+ }
// unload original body source immediately
- // set nullptr to unload body string
- bodyAccessData.buffer = nullptr;
data->m_body->unload();
return dest; }, [](void* memoryPtr, void* callbackData) { free(memoryPtr); });
scriptSource = src.finalize(&state);
}
+ return scriptSource;
+}
+
+FunctionObject::FunctionSource FunctionObject::createFunctionScript(ExecutionState& state, AtomicString functionName, size_t argCount, Value* argArray, Value bodyValue, bool useStrict, bool isGenerator, bool isAsync, bool allowSuperCall, bool isInternalSource, String* sourceName)
+{
+ String* scriptSource = createFunctionSource(state, functionName, argCount, argArray, bodyValue, useStrict, isGenerator, isAsync, isInternalSource);
+
ScriptParser parser(state.context());
- String* srcName = String::emptyString;
+ String* srcName = sourceName;
+
// find srcName through outer script except internal source
- if (!isInternalSource) {
+ if (!isInternalSource && !srcName->length()) {
auto script = state.resolveOuterScript();
if (script) {
srcName = script->srcName();
Script* script = parser.initializeScript(scriptSource, srcName, nullptr, false, false, false, false, false, allowSuperCall, false, true, false).scriptThrowsExceptionIfParseError(state);
InterpretedCodeBlock* cb = script->topCodeBlock()->childBlockAt(0);
+ // mark it as dynamic code
+ cb->setDynamicSourceCode();
LexicalEnvironment* globalEnvironment = new LexicalEnvironment(new GlobalEnvironmentRecord(state, script->topCodeBlock(), state.context()->globalObject(), state.context()->globalDeclarativeRecord(), state.context()->globalDeclarativeStorage()), nullptr);
FunctionObject::FunctionSource fs;
return fs;
}
+
+bool FunctionObject::setName(AtomicString name)
+{
+ // re-set function name is allowed only for native function or dynamically created function except class constructor
+ ASSERT(!!m_codeBlock);
+ if (m_codeBlock->isInterpretedCodeBlock()) {
+ InterpretedCodeBlock* cb = m_codeBlock->asInterpretedCodeBlock();
+ if (!cb->hasDynamicSourceCode() || cb->isClassConstructor()) {
+ return false;
+ }
+ }
+
+ // set function name property
+ size_t nameIndex = functionNameIndex();
+ m_values[nameIndex] = Value(name.string());
+
+ // set function name in CodeBlock
+ m_codeBlock->setFunctionName(name);
+
+ return true;
+}
} // namespace Escargot
InterpretedCodeBlock* codeBlock;
LexicalEnvironment* outerEnvironment;
};
- static FunctionSource createFunctionSourceFromScriptSource(ExecutionState& state, AtomicString functionName, size_t argCount, Value* argArray, Value bodyString, bool useStrict, bool isGenerator, bool isAsync, bool allowSuperCall, bool isInternalSource = false);
+ static FunctionSource createFunctionScript(ExecutionState& state, AtomicString functionName, size_t argCount, Value* argArray, Value bodyString, bool useStrict, bool isGenerator, bool isAsync, bool allowSuperCall, bool isInternalSource = false, String* sourceName = String::emptyString);
+
+ bool setName(AtomicString name);
protected:
FunctionObject(ExecutionState& state, Object* proto, size_t defaultSpace); // function for derived classes. derived class MUST initlize member variable of FunctionObject.
FunctionObject(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto); // ctor for FunctionTemplate
+ FunctionObject() // ctor for reading tag
+ : Object()
+ , m_codeBlock(nullptr)
+ {
+ }
- void initStructureAndValues(ExecutionState& state, bool isConstructor, bool isGenerator, bool isAsync);
+ void initStructureAndValues(ExecutionState& state, bool isConstructor, bool isGenerator);
virtual size_t functionPrototypeIndex()
{
ASSERT(isConstructor() || isGenerator());
return ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER;
}
+ size_t functionNameIndex()
+ {
+ // getting a function name index of ScriptClassConstructorFunctionObject is not supported now
+ ASSERT(!isScriptClassConstructorFunctionObject());
+ if (isConstructor() || isGenerator()) {
+ return ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2;
+ } else {
+ return ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 1;
+ }
+ }
+
void ensureFunctionPrototype(ExecutionState& state, const size_t& prototypeIndex)
{
if (m_values[prototypeIndex].isEmpty()) {
template <typename FunctionObjectType, bool isConstructCall, bool hasNewTargetOnEnvironment, bool canBindThisValueOnEnvironment, typename ThisValueBinder, typename NewTargetBinder, typename ReturnValueBinder>
static ALWAYS_INLINE Value processCall(ExecutionState& state, FunctionObjectType* self, const Value& thisArgument, const size_t argc, Value* argv, Object* newTarget) // newTarget is null on [[call]]
{
- volatile int sp;
- size_t currentStackBase = (size_t)&sp;
#ifdef STACK_GROWS_DOWN
- if (UNLIKELY(state.stackLimit() > currentStackBase)) {
+ if (UNLIKELY(state.stackLimit() > (size_t)currentStackPointer())) {
#else
- if (UNLIKELY(state.stackLimit() < currentStackBase)) {
+ if (UNLIKELY(state.stackLimit() < (size_t)currentStackPointer())) {
#endif
ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Maximum call stack size exceeded");
}
#endif
);
} else {
- if (LIKELY(codeBlock->canUseIndexedVariableStorage())) {
- record = new FunctionEnvironmentRecordOnHeap<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment>(self);
- } else {
- if (LIKELY(!codeBlock->needsVirtualIDOperation())) {
- record = new FunctionEnvironmentRecordNotIndexed<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment>(self);
- } else {
- record = new FunctionEnvironmentRecordNotIndexedWithVirtualID(self);
- }
- }
+ record = createFunctionEnvironmentRecord<FunctionObjectType, hasNewTargetOnEnvironment, canBindThisValueOnEnvironment>(state, self, codeBlock);
lexEnv = new LexicalEnvironment(record, self->outerEnvironment());
}
return returnValue;
}
+
+private:
+ template <typename FunctionObjectType, bool hasNewTargetOnEnvironment, bool canBindThisValueOnEnvironment>
+ static NEVER_INLINE FunctionEnvironmentRecord* createFunctionEnvironmentRecord(ExecutionState& state, FunctionObjectType* self, InterpretedCodeBlock* codeBlock)
+ {
+ if (LIKELY(codeBlock->canUseIndexedVariableStorage())) {
+ switch (codeBlock->identifierOnHeapCount()) {
+ case 1:
+ return new FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment, 1>(self);
+ case 2:
+ return new FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment, 2>(self);
+ case 3:
+ return new FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment, 3>(self);
+ case 4:
+ return new FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment, 4>(self);
+ case 5:
+ return new FunctionEnvironmentRecordOnHeapWithInlineStorage<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment, 5>(self);
+ default:
+ return new FunctionEnvironmentRecordOnHeap<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment>(self);
+ }
+ } else {
+ if (LIKELY(!codeBlock->needsVirtualIDOperation())) {
+ return new FunctionEnvironmentRecordNotIndexed<canBindThisValueOnEnvironment, hasNewTargetOnEnvironment>(self);
+ } else {
+ return new FunctionEnvironmentRecordNotIndexedWithVirtualID(self);
+ }
+ }
+ }
};
template <bool isConstruct, bool shouldReturnsObjectOnConstructCall>
const size_t functionDefaultPropertyCount = m_isConstructor ? ctx->defaultStructureForFunctionObject()->propertyCount() : ctx->defaultStructureForNotConstructorFunctionObject()->propertyCount();
if (!m_cachedObjectStructure.m_objectStructure) {
if (m_isConstructor) {
- // [prototype, name, length]
+ // [prototype, length, name]
ASSERT(functionDefaultPropertyCount == 3);
ObjectStructureItem structureItemVector[3] = {
ObjectStructureItem(ctx->staticStrings().prototype,
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::WritablePresent)),
- ObjectStructureItem(ctx->staticStrings().name,
- ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
ObjectStructureItem(ctx->staticStrings().length,
+ ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
+ ObjectStructureItem(ctx->staticStrings().name,
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent))
};
m_cachedObjectStructure = constructObjectStructure(ctx, structureItemVector, 3);
} else {
- // [name, length]
+ // [length, name]
ASSERT(functionDefaultPropertyCount == 2);
ObjectStructureItem structureItemVector[2] = {
- ObjectStructureItem(ctx->staticStrings().name,
- ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
ObjectStructureItem(ctx->staticStrings().length,
+ ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
+ ObjectStructureItem(ctx->staticStrings().name,
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent))
};
ObjectPropertyValueVector objectPropertyValues;
Object* functionPrototype = nullptr;
if (m_isConstructor) {
- // [prototype, name, length]
+ // [prototype, length, name]
if (!m_prototypeTemplate->has(ctx->staticStrings().constructor.string())) {
m_prototypeTemplate->set(ctx->staticStrings().constructor.string(), Value(), true, false, true);
}
ObjectPropertyValue baseValues[3];
baseValues[0] = functionPrototype = m_prototypeTemplate->instantiate(ctx);
- baseValues[1] = m_name.string();
- baseValues[2] = Value(m_argumentCount);
+ baseValues[1] = Value(m_argumentCount);
+ baseValues[2] = m_name.string();
constructObjectPropertyValues(ctx, baseValues, 3, objectPropertyValues);
} else {
- // [name, length]
+ // [length, name]
ObjectPropertyValue baseValues[2];
- baseValues[0] = m_name.string();
- baseValues[1] = Value(m_argumentCount);
+ baseValues[0] = Value(m_argumentCount);
+ baseValues[1] = m_name.string();
constructObjectPropertyValues(ctx, baseValues, 2, objectPropertyValues);
}
#include "runtime/Platform.h"
#include "runtime/PointerValue.h"
#include "runtime/ArrayObject.h"
+#include "runtime/ScriptFunctionObject.h"
namespace Escargot {
// tag values should be initialized once and not changed
PointerValue::g_arrayObjectTag = ArrayObject().getTag();
PointerValue::g_arrayPrototypeObjectTag = ArrayPrototypeObject().getTag();
+ PointerValue::g_scriptFunctionObjectTag = ScriptFunctionObject().getTag();
PointerValue::g_objectRareDataTag = ObjectRareData(nullptr).getTag();
PointerValue::g_doubleInEncodedValueTag = DoubleInEncodedValue(0).getTag();
}
#endif
-#ifdef ESCARGOT_USE_CUSTOM_LOGGING
+#ifdef ENABLE_CUSTOM_LOGGING
void customEscargotInfoLogger(const char* format, ...)
{
va_list arg;
}
target = target->getPrototypeObject(state);
}
- Value virtialIdResult = state.context()->virtualIdentifierCallback()(state, P.toPlainValue(state));
+ Value virtialIdResult = state.context()->virtualIdentifierCallback()(state, P.toPlainValue());
if (!virtialIdResult.isEmpty()) {
return ObjectHasPropertyResult(ObjectGetResult(virtialIdResult, true, true, true));
}
}
target = target->getPrototypeObject(state);
}
- Value virtialIdResult = state.context()->virtualIdentifierCallback()(state, P.toPlainValue(state));
+ Value virtialIdResult = state.context()->virtualIdentifierCallback()(state, P.toPlainValue());
if (!virtialIdResult.isEmpty())
return ObjectGetResult(virtialIdResult, true, true, true);
}
F(generatorPrototype, Object, objName)
//INTL
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
-#define GLOBALOBJECT_BUILTIN_INTL(F, objName) \
- F(intl, Object, objName) \
- F(intlCollator, FunctionObject, objName) \
- F(intlDateTimeFormat, FunctionObject, objName) \
- F(intlDateTimeFormatPrototype, Object, objName) \
- F(intlNumberFormat, FunctionObject, objName) \
- F(intlNumberFormatPrototype, Object, objName) \
- F(intlRelativeTimeFormat, FunctionObject, objName) \
- F(intlRelativeTimeFormatPrototype, Object, objName) \
- F(intlDisplayNames, FunctionObject, objName) \
- F(intlDisplayNamesPrototype, Object, objName) \
- F(intlLocale, FunctionObject, objName) \
- F(intlLocalePrototype, Object, objName) \
- F(intlListFormat, FunctionObject, objName) \
- F(intlListFormatPrototype, Object, objName) \
- F(intlPluralRules, FunctionObject, objName) \
+#if defined(ENABLE_INTL_DISPLAYNAMES)
+#define GLOBALOBJECT_BUILTIN_INTL_DISPLAYNAMES(F, objName) \
+ F(intlDisplayNames, FunctionObject, objName) \
+ F(intlDisplayNamesPrototype, Object, objName)
+#else
+#define GLOBALOBJECT_BUILTIN_INTL_DISPLAYNAMES(F, objName)
+#endif
+#if defined(ENABLE_INTL_NUMBERFORMAT)
+#define GLOBALOBJECT_BUILTIN_INTL_NUMBERFORMAT(F, objName) \
+ F(intlNumberFormat, FunctionObject, objName) \
+ F(intlNumberFormatPrototype, Object, objName)
+#else
+#define GLOBALOBJECT_BUILTIN_INTL_NUMBERFORMAT(F, objName)
+#endif
+#if defined(ENABLE_INTL_PLURALRULES)
+#define GLOBALOBJECT_BUILTIN_INTL_PLURALRULES(F, objName) \
+ F(intlPluralRules, FunctionObject, objName) \
F(intlPluralRulesPrototype, Object, objName)
#else
+#define GLOBALOBJECT_BUILTIN_INTL_PLURALRULES(F, objName)
+#endif
+#if defined(ENABLE_INTL_RELATIVETIMEFORMAT)
+#define GLOBALOBJECT_BUILTIN_INTL_RELATIVETIMEFORMAT(F, objName) \
+ F(intlRelativeTimeFormat, FunctionObject, objName) \
+ F(intlRelativeTimeFormatPrototype, Object, objName)
+#else
+#define GLOBALOBJECT_BUILTIN_INTL_RELATIVETIMEFORMAT(F, objName)
+#endif
+#if defined(ENABLE_INTL_LISTFORMAT)
+#define GLOBALOBJECT_BUILTIN_INTL_LISTFORMAT(F, objName) \
+ F(intlListFormat, FunctionObject, objName) \
+ F(intlListFormatPrototype, Object, objName)
+#else
+#define GLOBALOBJECT_BUILTIN_INTL_LISTFORMAT(F, objName)
+#endif
+
+#define GLOBALOBJECT_BUILTIN_INTL(F, objName) \
+ F(intl, Object, objName) \
+ F(intlCollator, FunctionObject, objName) \
+ F(intlDateTimeFormat, FunctionObject, objName) \
+ F(intlDateTimeFormatPrototype, Object, objName) \
+ F(intlLocale, FunctionObject, objName) \
+ F(intlLocalePrototype, Object, objName) \
+ GLOBALOBJECT_BUILTIN_INTL_DISPLAYNAMES(F, objName) \
+ GLOBALOBJECT_BUILTIN_INTL_NUMBERFORMAT(F, objName) \
+ GLOBALOBJECT_BUILTIN_INTL_PLURALRULES(F, objName) \
+ GLOBALOBJECT_BUILTIN_INTL_RELATIVETIMEFORMAT(F, objName) \
+ GLOBALOBJECT_BUILTIN_INTL_LISTFORMAT(F, objName)
+#else
#define GLOBALOBJECT_BUILTIN_INTL(F, objName)
#endif
#define GLOBALOBJECT_BUILTIN_JSON(F, objName) \
virtual bool isExtensible(ExecutionState&) override;
virtual bool preventExtensions(ExecutionState&) override;
+ virtual bool hasOwnEnumeration() const override
+ {
+ return true;
+ }
+
virtual bool canUseOwnPropertyKeysFastPath() override
{
return false;
return false;
}
+ virtual bool hasOwnEnumeration() const override
+ {
+ return true;
+ }
+
// http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
virtual Object* getPrototypeObject(ExecutionState&) override
{
: FunctionObject(state, state.context()->globalObject()->functionPrototype(), info.m_isConstructor ? (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3) : (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2))
{
m_codeBlock = new NativeCodeBlock(state.context(), info);
- initStructureAndValues(state, info.m_isConstructor, false, false);
+ initStructureAndValues(state, info.m_isConstructor, false);
}
NativeFunctionObject::NativeFunctionObject(ExecutionState& state, const NativeFunctionInfo& info, ForGlobalBuiltin)
: FunctionObject(state, state.context()->globalObject()->objectPrototype(), ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2)
{
m_codeBlock = new NativeCodeBlock(state.context(), info);
- initStructureAndValues(state, info.m_isConstructor, false, false);
+ initStructureAndValues(state, info.m_isConstructor, false);
ASSERT(!NativeFunctionObject::isConstructor());
}
: FunctionObject(state, state.context()->globalObject()->functionPrototype(), info.m_isConstructor ? (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3) : (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2))
{
m_codeBlock = new NativeCodeBlock(state.context(), info);
- initStructureAndValues(state, info.m_isConstructor, false, false);
+ initStructureAndValues(state, info.m_isConstructor, false);
if (info.m_isConstructor) {
m_structure = state.context()->defaultStructureForBuiltinFunctionObject();
}
m_data = (size_t)value.asSymbol();
return;
}
+
String* string = value.toString(state);
+ size_t v = string->getTagInFirstDataArea();
+ if (v > POINTER_VALUE_STRING_TAG_IN_DATA) {
+ ASSERT(v == ((v & ~POINTER_VALUE_STRING_TAG_IN_DATA) | OBJECT_PROPERTY_NAME_ATOMIC_STRING_VIAS));
+ m_data = v;
+ return;
+ }
+
const auto& data = string->bufferAccessData();
if (data.length == 0) {
m_data = ((size_t)AtomicString().string()) | OBJECT_PROPERTY_NAME_ATOMIC_STRING_VIAS;
return;
}
bool needsRemainNormalString = false;
- char16_t c = data.has8BitContent ? ((LChar*)data.buffer)[0] : ((char16_t*)data.buffer)[0];
+ char16_t c = data.charAt(0);
if ((c == '.' || (c >= '0' && c <= '9')) && data.length > 16) {
needsRemainNormalString = true;
}
return hasProperty(state, ObjectPropertyName(state, propertyName));
}
+typedef std::pair<Value::ValueIndex, ObjectStructurePropertyDescriptor> IndexItem;
+struct CompareIndexItem {
+ bool operator()(const IndexItem& a, const IndexItem& b) const
+ {
+ return a.first < b.first;
+ }
+};
+
template <typename ResultType, typename ResultBinder>
static ResultType objectOwnPropertyKeys(ExecutionState& state, Object* self)
{
// https://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
struct Properties {
- VectorWithInlineStorage<32, std::pair<Value::ValueIndex, ObjectStructurePropertyDescriptor>, GCUtil::gc_malloc_allocator<std::pair<Value::ValueIndex, ObjectStructurePropertyDescriptor>>> indexes;
+ std::multiset<IndexItem, CompareIndexItem, GCUtil::gc_malloc_allocator<IndexItem>> indexes;
VectorWithInlineStorage<32, std::pair<String*, ObjectStructurePropertyDescriptor>, GCUtil::gc_malloc_allocator<std::pair<String*, ObjectStructurePropertyDescriptor>>> strings;
VectorWithInlineStorage<4, std::pair<Symbol*, ObjectStructurePropertyDescriptor>, GCUtil::gc_malloc_allocator<std::pair<Symbol*, ObjectStructurePropertyDescriptor>>> symbols;
} properties;
- self->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
- auto properties = (Properties*)data;
-
- auto indexProperty = name.tryToUseAsIndexProperty();
- if (indexProperty != Value::InvalidIndexPropertyValue) {
- properties->indexes.push_back(std::make_pair(indexProperty, desc));
- } else {
- const ObjectStructurePropertyName& propertyName = name.objectStructurePropertyName();
+ self->enumeration(
+ state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
+ auto properties = (Properties*)data;
- if (propertyName.isSymbol()) {
- properties->symbols.push_back(std::make_pair(propertyName.symbol(), desc));
+ auto indexProperty = name.tryToUseAsIndexProperty();
+ if (indexProperty != Value::InvalidIndexPropertyValue) {
+ properties->indexes.insert(std::make_pair(indexProperty, desc));
} else {
- ASSERT(propertyName.isPlainString());
- String* name = propertyName.plainString();
- properties->strings.push_back(std::make_pair(name, desc));
- }
- }
- return true;
- },
- &properties, false);
-
- std::sort(properties.indexes.begin(), properties.indexes.end(),
- [](const std::pair<Value::ValueIndex, ObjectStructurePropertyDescriptor>& a, const std::pair<Value::ValueIndex, ObjectStructurePropertyDescriptor>& b) -> bool {
- return a.first < b.first;
- });
+ const ObjectStructurePropertyName& propertyName = name.objectStructurePropertyName();
+ if (propertyName.isSymbol()) {
+ properties->symbols.push_back(std::make_pair(propertyName.symbol(), desc));
+ } else {
+ ASSERT(propertyName.isPlainString());
+ String* name = propertyName.plainString();
+ properties->strings.push_back(std::make_pair(name, desc));
+ }
+ }
+ return true;
+ },
+ &properties, false);
ResultType result(properties.indexes.size() + properties.strings.size() + properties.symbols.size());
// 2. Let ownDesc be O.[[GetOwnProperty]](P).
auto ownDesc = this->getOwnProperty(state, propertyName);
// 4. If ownDesc is undefined, then
- if (!ownDesc.hasValue()) {
+ bool hasValue = ownDesc.hasValue();
+ if (!hasValue) {
// FIXME need to optimize prototype iteration
// 4.a. Let parent be O.[[GetPrototypeOf]]().
Value parent = this->getPrototype(state);
// 5.c. Let existingDescriptor be Receiver.[[GetOwnProperty]](P).
Object* receiverObj = receiver.asObject();
- auto existingDesc = receiverObj->getOwnProperty(state, propertyName);
+
+ ObjectGetResult existingDesc;
+ if (hasValue && LIKELY(this == receiverObj)) {
+ existingDesc = ownDesc;
+ ObjectPropertyDescriptor propertyDesc(v);
+ // 5.e.iv. Return Receiver.[[DefineOwnProperty]](P, valueDesc).
+ return receiverObj->defineOwnProperty(state, propertyName, propertyDesc);
+ }
+
+ existingDesc = receiverObj->getOwnProperty(state, propertyName);
+
// 5.e. If existingDescriptor is not undefined, then
if (existingDesc.hasValue()) {
// 5.e.i. If IsAccessorDescriptor(existingDescriptor) is true, return false.
// Assert: IsCallable(constructor) is true.
ASSERT(constructor->isCallable());
- // Let proto be ? Get(constructor, "prototype").
- Value proto = constructor->get(state, ObjectPropertyName(state.context()->staticStrings().prototype)).value(state, constructor);
+ Value proto;
+ if (LIKELY(constructor->isFunctionObject())) {
+ proto = constructor->asFunctionObject()->getFunctionPrototype(state);
+ } else {
+ // Let proto be ? Get(constructor, "prototype").
+ proto = constructor->get(state, ObjectPropertyName(state.context()->staticStrings().prototype)).value(state, constructor);
+ }
// If Type(proto) is not Object, then
if (!proto.isObject()) {
return;
}
- ptr.asObject()->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) {
- int64_t index;
- Data* e = (Data*)data;
- int64_t* ret = e->ret;
- Value key = name.toPlainValue(state);
- index = key.toNumber(state);
- if ((uint64_t)index != Value::InvalidIndexValue) {
- if (index > *e->cur && *ret > index) {
- *ret = std::min(index, *ret);
+ ptr.asObject()->enumeration(
+ state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) {
+ int64_t index;
+ Data* e = (Data*)data;
+ int64_t* ret = e->ret;
+ Value key = name.toPlainValue();
+ index = key.toNumber(state);
+ if ((uint64_t)index != Value::InvalidIndexValue) {
+ if (index > *e->cur && *ret > index) {
+ *ret = std::min(index, *ret);
+ }
}
- }
- return true;
- },
- &data);
+ return true;
+ },
+ &data);
ptr = ptr.asObject()->getPrototype(state);
}
nextIndex = std::max(ret, cur + 1);
nextIndex = std::max(end, cur - 1);
return;
}
- ptr.asObject()->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) {
- int64_t index;
- Data* e = (Data*)data;
- int64_t* ret = e->ret;
- Value key = name.toPlainValue(state);
- index = key.toNumber(state);
- if ((uint64_t)index != Value::InvalidIndexValue) {
- if (index < *e->cur) {
- *ret = std::max(index, *ret);
+ ptr.asObject()->enumeration(
+ state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) {
+ int64_t index;
+ Data* e = (Data*)data;
+ int64_t* ret = e->ret;
+ Value key = name.toPlainValue();
+ index = key.toNumber(state);
+ if ((uint64_t)index != Value::InvalidIndexValue) {
+ if (index < *e->cur) {
+ *ret = std::max(index, *ret);
+ }
}
- }
- return true;
- },
- &data);
+ return true;
+ },
+ &data);
ptr = ptr.asObject()->getPrototype(state);
}
nextIndex = std::min(ret, cur - 1);
rareData()->m_isFinalizerRegistered = true;
#define FINALIZER_CALLBACK() \
+ GC_disable(); \
Object* self = (Object*)obj; \
auto r = self->ensureObjectExtendedExtraData(); \
for (size_t i = 0; i < r->m_finalizer.size(); i++) { \
r->m_finalizer[i].first(self, r->m_finalizer[i].second); \
- }
+ } \
+ GC_enable();
#ifndef NDEBUG
GC_finalization_proc of = nullptr;
return false;
}
+Object::FastLookupSymbolResult Object::fastLookupForSymbol(ExecutionState& state, Symbol* s, Optional<Object*> protochainSearchStopAt)
+{
+ ObjectStructurePropertyName name(s);
+ Object* obj = this;
+ while (true) {
+ if (protochainSearchStopAt.unwrap() == obj) {
+ return FastLookupSymbolResult(true, obj, SIZE_MAX);
+ }
+
+ if (!obj->isInlineCacheable()) {
+ break;
+ }
+
+ auto structure = obj->structure();
+ if (structure->hasSymbolPropertyName()) {
+ auto ret = structure->findProperty(name).first;
+ if (ret != SIZE_MAX) {
+ return FastLookupSymbolResult(true, obj, ret);
+ }
+ }
+ obj = obj->Object::getPrototypeObject(state);
+ if (!obj) {
+ return FastLookupSymbolResult(true, nullptr, SIZE_MAX);
+ }
+ }
+
+ // fast path was failed
+ return FastLookupSymbolResult();
+}
+
} // namespace Escargot
return objectStructurePropertyName().tryToUseAsIndexProperty();
}
- Value toPlainValue(ExecutionState&) const
+ Value toPlainValue() const
{
if (isUIntType()) {
return Value(uintValue());
return hasRareData() ? rareData()->m_isExtensible : true;
}
+ // represent that this Object has its own enumeration function other than Object::enumeration
+ virtual bool hasOwnEnumeration() const
+ {
+ return false;
+ }
+
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
virtual bool preventExtensions(ExecutionState&)
{
// callback function should skip un-Enumerable property if needs
virtual void enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey = true);
// ToLength(Get(obj, "length"))
- uint64_t length(ExecutionState& state);
+ virtual uint64_t length(ExecutionState& state);
// https://www.ecma-international.org/ecma-262/10.0/#sec-isarray
bool isArray(ExecutionState& state);
void addFinalizer(ObjectFinalizer fn, void* data);
bool removeFinalizer(ObjectFinalizer fn, void* data);
+ struct FastLookupSymbolResult {
+ FastLookupSymbolResult()
+ : m_wasSucessful(false)
+ , m_propertyIndexInStructure(0)
+ {
+ }
+
+ FastLookupSymbolResult(bool wasSucessful, Object* o, size_t idx)
+ : m_wasSucessful(wasSucessful)
+ , m_matchedObject(o)
+ , m_propertyIndexInStructure(idx)
+ {
+ }
+
+ bool wasSucessful() const
+ {
+ return m_wasSucessful;
+ }
+
+ Optional<Object*> matchedObject() const
+ {
+ return m_matchedObject;
+ }
+
+ size_t propertyIndexInStructure() const
+ {
+ return m_propertyIndexInStructure;
+ }
+
+ Value value(ExecutionState& state, Object* receiver)
+ {
+ ASSERT(wasSucessful());
+ if (m_matchedObject) {
+ return m_matchedObject->getOwnPropertyUtilForObject(state, m_propertyIndexInStructure, Value(receiver));
+ } else {
+ return Value();
+ }
+ }
+
+ private:
+ bool m_wasSucessful;
+ Optional<Object*> m_matchedObject;
+ size_t m_propertyIndexInStructure;
+ };
+ FastLookupSymbolResult fastLookupForSymbol(ExecutionState& state, Symbol* s, Optional<Object*> protochainSearchStopAt = nullptr);
+
protected:
static inline void fillGCDescriptor(GC_word* desc)
{
}
Value getOwnPropertyUtilForObjectAccCase(ExecutionState& state, size_t idx, const Value& receiver);
- ALWAYS_INLINE Value getOwnPropertyUtilForObject(ExecutionState& state, size_t idx, const Value& receiver)
+ Value getOwnPropertyUtilForObject(ExecutionState& state, size_t idx, const Value& receiver)
{
const ObjectStructureItem& item = m_structure->readProperty(idx);
if (LIKELY(item.m_descriptor.isDataProperty())) {
}
}
+ NEVER_INLINE Value getOwnNonPlainDataPropertyUtilForObject(ExecutionState& state, size_t idx, const Value& receiver)
+ {
+ const ObjectStructureItem& item = m_structure->readProperty(idx);
+ if (LIKELY(item.m_descriptor.isDataProperty())) {
+ ASSERT(!item.m_descriptor.isPlainDataProperty());
+ return item.m_descriptor.nativeGetterSetterData()->m_getter(state, this, receiver, m_values[idx]);
+ } else {
+ return getOwnPropertyUtilForObjectAccCase(state, idx, receiver);
+ }
+ }
+
bool setOwnPropertyUtilForObjectAccCase(ExecutionState& state, size_t idx, const Value& newValue, const Value& receiver);
ALWAYS_INLINE bool setOwnPropertyUtilForObject(ExecutionState& state, size_t idx, const Value& newValue, const Value& receiver)
{
{
size_t size = m_properties->size();
- if (LIKELY(s.hasAtomicString() && !m_hasNonAtomicPropertyName)) {
- for (size_t i = 0; i < size; i++) {
- if ((*m_properties)[i].m_propertyName.rawValue() == s.rawValue()) {
- return std::make_pair(i, &(*m_properties)[i]);
+ if (LIKELY(s.hasAtomicString())) {
+ if (LIKELY(!m_hasNonAtomicPropertyName)) {
+ for (size_t i = 0; i < size; i++) {
+ if ((*m_properties)[i].m_propertyName.rawValue() == s.rawValue()) {
+ return std::make_pair(i, &(*m_properties)[i]);
+ }
+ }
+ } else {
+ AtomicString as = s.asAtomicString();
+ for (size_t i = 0; i < size; i++) {
+ if ((*m_properties)[i].m_propertyName == as) {
+ return std::make_pair(i, &(*m_properties)[i]);
+ }
}
}
- } else if (LIKELY(s.hasAtomicString())) {
- AtomicString as = s.asAtomicString();
- for (size_t i = 0; i < size; i++) {
- if ((*m_properties)[i].m_propertyName == as) {
- return std::make_pair(i, &(*m_properties)[i]);
+ } else if (s.isSymbol()) {
+ if (m_hasSymbolPropertyName) {
+ for (size_t i = 0; i < size; i++) {
+ if ((*m_properties)[i].m_propertyName == s) {
+ return std::make_pair(i, &(*m_properties)[i]);
+ }
}
}
} else {
{
ObjectStructureItem newItem(name, desc);
bool nameIsIndexString = m_hasIndexPropertyName ? true : name.isIndexString();
+ bool nameIsSymbol = m_hasSymbolPropertyName ? true : name.isSymbol();
bool hasNonAtomicName = m_hasNonAtomicPropertyName ? true : !name.hasAtomicString();
+ bool hasEnumerableProperty = m_hasEnumerableProperty ? true : desc.isEnumerable();
ObjectStructure* newStructure;
m_properties->push_back(newItem);
if (m_properties->size() + 1 > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) {
- newStructure = new ObjectStructureWithMap(m_properties, ObjectStructureWithMap::createPropertyNameMap(m_properties), m_hasIndexPropertyName || nameIsIndexString);
+ newStructure = new ObjectStructureWithMap(m_properties, ObjectStructureWithMap::createPropertyNameMap(m_properties), nameIsIndexString, nameIsSymbol, hasEnumerableProperty);
} else {
- newStructure = new ObjectStructureWithoutTransition(m_properties, nameIsIndexString, hasNonAtomicName);
+ newStructure = new ObjectStructureWithoutTransition(m_properties, nameIsIndexString, nameIsSymbol, hasNonAtomicName, hasEnumerableProperty);
}
m_properties = nullptr;
size_t newIdx = 0;
bool hasIndexString = false;
+ bool hasSymbol = false;
bool hasNonAtomicName = false;
+ bool hasEnumerableProperty = false;
for (size_t i = 0; i < ps; i++) {
if (i == pIndex)
continue;
hasIndexString = hasIndexString | (*m_properties)[i].m_propertyName.isIndexString();
+ hasSymbol = hasSymbol | (*m_properties)[i].m_propertyName.isSymbol();
hasNonAtomicName = hasNonAtomicName | !(*m_properties)[i].m_propertyName.hasAtomicString();
+ hasEnumerableProperty = hasEnumerableProperty | (*m_properties)[i].m_descriptor.isEnumerable();
(*newProperties)[newIdx].m_propertyName = (*m_properties)[i].m_propertyName;
(*newProperties)[newIdx].m_descriptor = (*m_properties)[i].m_descriptor;
newIdx++;
}
- auto newStructure = new ObjectStructureWithoutTransition(newProperties, hasIndexString, hasNonAtomicName);
+ auto newStructure = new ObjectStructureWithoutTransition(newProperties, hasIndexString, hasSymbol, hasNonAtomicName, hasEnumerableProperty);
m_properties = nullptr;
return newStructure;
}
ObjectStructure* ObjectStructureWithoutTransition::replacePropertyDescriptor(size_t idx, const ObjectStructurePropertyDescriptor& newDesc)
{
m_properties->at(idx).m_descriptor = newDesc;
- auto newStructure = new ObjectStructureWithoutTransition(m_properties, m_hasIndexPropertyName, m_hasNonAtomicPropertyName);
+ auto newStructure = new ObjectStructureWithoutTransition(m_properties, m_hasIndexPropertyName, m_hasSymbolPropertyName, m_hasNonAtomicPropertyName, m_hasEnumerableProperty);
m_properties = nullptr;
return newStructure;
}
{
size_t size = m_properties.size();
- if (LIKELY(s.hasAtomicString() && !m_hasNonAtomicPropertyName)) {
- for (size_t i = 0; i < size; i++) {
- if (m_properties[i].m_propertyName.rawValue() == s.rawValue()) {
- return std::make_pair(i, &m_properties[i]);
+ if (LIKELY(s.hasAtomicString())) {
+ if (LIKELY(!m_hasNonAtomicPropertyName)) {
+ for (size_t i = 0; i < size; i++) {
+ if (m_properties[i].m_propertyName.rawValue() == s.rawValue()) {
+ return std::make_pair(i, &m_properties[i]);
+ }
+ }
+ } else {
+ AtomicString as = s.asAtomicString();
+ for (size_t i = 0; i < size; i++) {
+ if (m_properties[i].m_propertyName == as) {
+ return std::make_pair(i, &m_properties[i]);
+ }
}
}
- } else if (LIKELY(s.hasAtomicString())) {
- AtomicString as = s.asAtomicString();
- for (size_t i = 0; i < size; i++) {
- if (m_properties[i].m_propertyName == as) {
- return std::make_pair(i, &m_properties[i]);
+ } else if (s.isSymbol()) {
+ if (m_hasSymbolPropertyName) {
+ for (size_t i = 0; i < size; i++) {
+ if (m_properties[i].m_propertyName == s) {
+ return std::make_pair(i, &m_properties[i]);
+ }
}
}
} else {
ObjectStructureItem newItem(name, desc);
bool nameIsIndexString = m_hasIndexPropertyName ? true : name.isIndexString();
+ bool hasSymbol = m_hasSymbolPropertyName ? true : name.isSymbol();
bool hasNonAtomicName = m_hasNonAtomicPropertyName ? true : !name.hasAtomicString();
+ bool hasEnumerableProperty = m_hasEnumerableProperty ? true : desc.isEnumerable();
ObjectStructure* newObjectStructure;
size_t nextSize = m_properties.size() + 1;
if (nextSize > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) {
- newObjectStructure = new ObjectStructureWithMap(nameIsIndexString, m_properties, newItem);
+ newObjectStructure = new ObjectStructureWithMap(nameIsIndexString, hasSymbol, hasEnumerableProperty, m_properties, newItem);
} else if (nextSize > ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE) {
ObjectStructureItemVector* newProperties = new ObjectStructureItemVector(m_properties, newItem);
- newObjectStructure = new ObjectStructureWithoutTransition(newProperties, nameIsIndexString, hasNonAtomicName);
+ newObjectStructure = new ObjectStructureWithoutTransition(newProperties, nameIsIndexString, hasSymbol, hasNonAtomicName, hasEnumerableProperty);
} else {
ObjectStructureItemTightVector newProperties(m_properties, newItem);
- newObjectStructure = new ObjectStructureWithTransition(std::move(newProperties), nameIsIndexString, hasNonAtomicName);
+ newObjectStructure = new ObjectStructureWithTransition(std::move(newProperties), nameIsIndexString, hasSymbol, hasNonAtomicName, hasEnumerableProperty);
ObjectStructureTransitionVectorItem newTransitionItem(name, desc, newObjectStructure);
if (m_doesTransitionTableUseMap) {
size_t newIdx = 0;
bool hasIndexString = false;
+ bool hasSymbol = false;
bool hasNonAtomicName = false;
+ bool hasEnumerableProperty = false;
for (size_t i = 0; i < pc; i++) {
if (i == pIndex)
continue;
hasIndexString = hasIndexString | m_properties[i].m_propertyName.isIndexString();
+ hasSymbol = hasSymbol | m_properties[i].m_propertyName.isSymbol();
hasNonAtomicName = hasNonAtomicName | !m_properties[i].m_propertyName.hasAtomicString();
+ hasEnumerableProperty = hasEnumerableProperty | m_properties[i].m_descriptor.isEnumerable();
(*newProperties)[newIdx].m_propertyName = m_properties[i].m_propertyName;
(*newProperties)[newIdx].m_descriptor = m_properties[i].m_descriptor;
newIdx++;
}
- return new ObjectStructureWithoutTransition(newProperties, hasIndexString, hasNonAtomicName);
+ return new ObjectStructureWithoutTransition(newProperties, hasIndexString, hasSymbol, hasNonAtomicName, hasEnumerableProperty);
}
ObjectStructure* ObjectStructureWithTransition::replacePropertyDescriptor(size_t idx, const ObjectStructurePropertyDescriptor& newDesc)
{
ObjectStructureItemVector* newProperties = new ObjectStructureItemVector(m_properties);
newProperties->at(idx).m_descriptor = newDesc;
- return new ObjectStructureWithoutTransition(newProperties, m_hasIndexPropertyName, m_hasNonAtomicPropertyName);
+ return new ObjectStructureWithoutTransition(newProperties, m_hasIndexPropertyName, m_hasSymbolPropertyName, m_hasNonAtomicPropertyName, m_hasEnumerableProperty);
}
ObjectStructure* ObjectStructureWithTransition::convertToNonTransitionStructure()
{
ObjectStructureItemVector* newProperties = new ObjectStructureItemVector(m_properties);
- return new ObjectStructureWithoutTransition(newProperties, m_hasIndexPropertyName, m_hasNonAtomicPropertyName);
+ return new ObjectStructureWithoutTransition(newProperties, m_hasIndexPropertyName, m_hasSymbolPropertyName, m_hasNonAtomicPropertyName, m_hasEnumerableProperty);
}
void* ObjectStructureWithMap::operator new(size_t size)
{
ObjectStructureItem newItem(name, desc);
bool nameIsIndexString = m_hasIndexPropertyName ? true : name.isIndexString();
+ bool hasSymbol = m_hasSymbolPropertyName ? true : name.isSymbol();
+ bool hasEnumerableProperty = m_hasEnumerableProperty ? true : desc.isEnumerable();
m_propertyNameMap->insert(std::make_pair(name, m_properties->size()));
m_properties->push_back(newItem);
- ObjectStructure* newStructure = new ObjectStructureWithMap(m_properties, m_propertyNameMap, nameIsIndexString);
+ ObjectStructure* newStructure = new ObjectStructureWithMap(m_properties, m_propertyNameMap, nameIsIndexString, hasSymbol, hasEnumerableProperty);
m_properties = nullptr;
m_propertyNameMap = nullptr;
return newStructure;
size_t newIdx = 0;
bool hasIndexString = false;
+ bool hasSymbol = false;
+ bool hasEnumerableProperty = false;
for (size_t i = 0; i < ps; i++) {
if (i == pIndex)
continue;
hasIndexString = hasIndexString | (*m_properties)[i].m_propertyName.isIndexString();
+ hasSymbol = hasSymbol | (*m_properties)[i].m_propertyName.isSymbol();
+ hasEnumerableProperty = hasEnumerableProperty | (*m_properties)[i].m_descriptor.isEnumerable();
(*newProperties)[newIdx].m_propertyName = (*m_properties)[i].m_propertyName;
(*newProperties)[newIdx].m_descriptor = (*m_properties)[i].m_descriptor;
newIdx++;
}
- ObjectStructure* newStructure = new ObjectStructureWithMap(newProperties, ObjectStructureWithMap::createPropertyNameMap(newProperties), hasIndexString);
+ ObjectStructure* newStructure = new ObjectStructureWithMap(newProperties, ObjectStructureWithMap::createPropertyNameMap(newProperties), hasIndexString, hasSymbol, hasEnumerableProperty);
m_properties = nullptr;
m_propertyNameMap = nullptr;
return newStructure;
ObjectStructure* ObjectStructureWithMap::replacePropertyDescriptor(size_t idx, const ObjectStructurePropertyDescriptor& newDesc)
{
m_properties->at(idx).m_descriptor = newDesc;
- ObjectStructure* newStructure = new ObjectStructureWithMap(m_properties, m_propertyNameMap, m_hasIndexPropertyName);
+ ObjectStructure* newStructure = new ObjectStructureWithMap(m_properties, m_propertyNameMap, m_hasIndexPropertyName, m_hasSymbolPropertyName, m_hasEnumerableProperty);
m_properties = nullptr;
m_propertyNameMap = nullptr;
return newStructure;
};
#if defined(ESCARGOT_SMALL_CONFIG)
+#ifndef ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE 256
+#endif
+#ifndef ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE 12
+#endif
+#ifndef ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MAP_MIN_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MAP_MIN_SIZE 32
+#endif
#else
+#ifndef ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE 96
+#endif
+#ifndef ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE 48
+#endif
+#ifndef ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MAP_MIN_SIZE
#define ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MAP_MIN_SIZE 32
#endif
+#endif
class ObjectStructure : public gc {
public:
virtual ObjectStructure* replacePropertyDescriptor(size_t idx, const ObjectStructurePropertyDescriptor& newDesc) = 0;
virtual ObjectStructure* convertToNonTransitionStructure() = 0;
-
virtual bool inTransitionMode() = 0;
- virtual bool hasIndexPropertyName() = 0;
+
+ bool hasIndexPropertyName() const
+ {
+ return m_hasIndexPropertyName;
+ }
+
+ bool hasSymbolPropertyName() const
+ {
+ return m_hasSymbolPropertyName;
+ }
+
+ bool hasEnumerableProperty() const
+ {
+ return m_hasEnumerableProperty;
+ }
+
+protected:
+ ObjectStructure(bool hasIndexPropertyName,
+ bool hasSymbolPropertyName, bool hasEnumerableProperty)
+ : m_doesTransitionTableUseMap(false)
+ , m_hasIndexPropertyName(hasIndexPropertyName)
+ , m_hasSymbolPropertyName(hasSymbolPropertyName)
+ , m_hasNonAtomicPropertyName(false)
+ , m_hasEnumerableProperty(hasEnumerableProperty)
+ , m_transitionTableVectorBufferSize(0)
+ , m_transitionTableVectorBufferCapacity(0)
+ {
+ }
+
+ ObjectStructure(bool hasIndexPropertyName,
+ bool hasSymbolPropertyName, bool hasNonAtomicPropertyName, bool hasEnumerableProperty)
+ : m_doesTransitionTableUseMap(false)
+ , m_hasIndexPropertyName(hasIndexPropertyName)
+ , m_hasSymbolPropertyName(hasSymbolPropertyName)
+ , m_hasNonAtomicPropertyName(hasNonAtomicPropertyName)
+ , m_hasEnumerableProperty(hasEnumerableProperty)
+ , m_transitionTableVectorBufferSize(0)
+ , m_transitionTableVectorBufferCapacity(0)
+ {
+ }
+
+ // every class should share flag members
+ // this way can reduce size of ObjectStructureWithTransition
+ bool m_doesTransitionTableUseMap : 1;
+ bool m_hasIndexPropertyName : 1;
+ bool m_hasSymbolPropertyName : 1;
+ bool m_hasNonAtomicPropertyName : 1;
+ bool m_hasEnumerableProperty : 1;
+ uint8_t m_transitionTableVectorBufferSize : 8;
+ uint8_t m_transitionTableVectorBufferCapacity : 8;
};
class ObjectStructureWithoutTransition : public ObjectStructure {
public:
- ObjectStructureWithoutTransition(ObjectStructureItemVector* properties, bool hasIndexPropertyName, bool hasNonAtomicPropertyName)
- : m_hasIndexPropertyName(hasIndexPropertyName)
- , m_hasNonAtomicPropertyName(hasNonAtomicPropertyName)
+ ObjectStructureWithoutTransition(ObjectStructureItemVector* properties, bool hasIndexPropertyName,
+ bool hasSymbolPropertyName, bool hasNonAtomicPropertyName, bool hasEnumerableProperty)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasNonAtomicPropertyName, hasEnumerableProperty)
, m_properties(properties)
{
}
return false;
}
- virtual bool hasIndexPropertyName() override
- {
- return m_hasIndexPropertyName;
- }
-
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
private:
- bool m_hasIndexPropertyName;
- bool m_hasNonAtomicPropertyName;
ObjectStructureItemVector* m_properties;
};
class ObjectStructureWithTransition : public ObjectStructure {
public:
- ObjectStructureWithTransition(ObjectStructureItemTightVector&& properties, bool hasIndexPropertyName, bool hasNonAtomicPropertyName)
- : m_properties(std::move(properties))
- , m_doesTransitionTableUseMap(false)
- , m_hasIndexPropertyName(hasIndexPropertyName)
- , m_hasNonAtomicPropertyName(hasNonAtomicPropertyName)
- , m_transitionTableVectorBufferSize(0)
- , m_transitionTableVectorBufferCapacity(0)
+ ObjectStructureWithTransition(ObjectStructureItemTightVector&& properties, bool hasIndexPropertyName, bool hasSymbolPropertyName, bool hasNonAtomicPropertyName, bool hasEnumerableProperty)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasNonAtomicPropertyName, hasEnumerableProperty)
+ , m_properties(std::move(properties))
, m_transitionTableVectorBuffer(nullptr)
{
}
return true;
}
- virtual bool hasIndexPropertyName() override
- {
- return m_hasIndexPropertyName;
- }
-
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
}
ObjectStructureItemTightVector m_properties;
-
- bool m_doesTransitionTableUseMap : 1;
- bool m_hasIndexPropertyName : 1;
- bool m_hasNonAtomicPropertyName : 1;
- uint8_t m_transitionTableVectorBufferSize;
- uint8_t m_transitionTableVectorBufferCapacity;
-
union {
ObjectStructureTransitionVectorItem* m_transitionTableVectorBuffer;
ObjectStructureTransitionTableMap* m_transitionTableMap;
class ObjectStructureWithMap : public ObjectStructure {
public:
- ObjectStructureWithMap(ObjectStructureItemVector* properties, PropertyNameMap* map, bool hasIndexPropertyName)
- : m_hasIndexPropertyName(hasIndexPropertyName)
+ ObjectStructureWithMap(ObjectStructureItemVector* properties, PropertyNameMap* map, bool hasIndexPropertyName, bool hasSymbolPropertyName, bool hasEnumerableProperty)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasEnumerableProperty)
, m_properties(properties)
, m_propertyNameMap(map)
{
}
template <typename SourceProperties>
- ObjectStructureWithMap(bool hasIndexPropertyName, const SourceProperties& properties, const ObjectStructureItem& newItem)
- : m_hasIndexPropertyName(hasIndexPropertyName)
+ ObjectStructureWithMap(bool hasIndexPropertyName, bool hasSymbolPropertyName, bool hasEnumerableProperty, const SourceProperties& properties, const ObjectStructureItem& newItem)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasEnumerableProperty)
+
{
ObjectStructureItemVector* newProperties = new ObjectStructureItemVector();
newProperties->resizeWithUninitializedValues(properties.size() + 1);
m_propertyNameMap = ObjectStructureWithMap::createPropertyNameMap(newProperties);
}
- ObjectStructureWithMap(bool hasIndexPropertyName, const ObjectStructureItemTightVector& properties)
- : m_hasIndexPropertyName(hasIndexPropertyName)
+ ObjectStructureWithMap(bool hasIndexPropertyName, bool hasSymbolPropertyName, bool hasEnumerableProperty, const ObjectStructureItemTightVector& properties)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasEnumerableProperty)
+
{
ObjectStructureItemVector* newProperties = new ObjectStructureItemVector();
newProperties->resizeWithUninitializedValues(properties.size());
m_propertyNameMap = ObjectStructureWithMap::createPropertyNameMap(newProperties);
}
- ObjectStructureWithMap(bool hasIndexPropertyName, ObjectStructureItemTightVector&& properties)
- : m_hasIndexPropertyName(hasIndexPropertyName)
+ ObjectStructureWithMap(bool hasIndexPropertyName, bool hasSymbolPropertyName, bool hasEnumerableProperty, ObjectStructureItemTightVector&& properties)
+ : ObjectStructure(hasIndexPropertyName,
+ hasSymbolPropertyName, hasEnumerableProperty)
{
m_properties = new ObjectStructureItemVector(std::move(properties));
m_propertyNameMap = ObjectStructureWithMap::createPropertyNameMap(m_properties);
{
return false;
}
- virtual bool hasIndexPropertyName() override
- {
- return m_hasIndexPropertyName;
- }
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
}
private:
- bool m_hasIndexPropertyName;
ObjectStructureItemVector* m_properties;
PropertyNameMap* m_propertyNameMap;
};
namespace Escargot {
-void* ObjectTemplate::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word objBitmap[GC_BITMAP_SIZE(ObjectTemplate)] = { 0 };
- Template::fillGCDescriptor(objBitmap);
- GC_set_bit(objBitmap, GC_WORD_OFFSET(ObjectTemplate, m_constructor));
- GC_set_bit(objBitmap, GC_WORD_OFFSET(ObjectTemplate, m_namedPropertyHandler));
- descr = GC_make_descriptor(objBitmap, GC_WORD_LEN(ObjectTemplate));
- typeInited = true;
+class ObjectTemplatePropertyHandlerData : public gc {
+ friend class ObjectWithPropertyHandler;
+
+public:
+ explicit ObjectTemplatePropertyHandlerData(const ObjectTemplatePropertyHandlerConfiguration& data)
+ : m_getter(data.getter)
+ , m_setter(data.setter)
+ , m_query(data.query)
+ , m_deleter(data.deleter)
+ , m_enumerator(data.enumerator)
+ , m_definer(data.definer)
+ , m_descriptor(data.descriptor)
+ , m_data(data.data)
+ {
}
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-ObjectTemplate::ObjectTemplate(Optional<FunctionTemplate*> constructor)
- : m_constructor(constructor)
- , m_namedPropertyHandler(nullptr)
-{
-}
+ void* operator new(size_t size)
+ {
+ static MAY_THREAD_LOCAL bool typeInited = false;
+ static MAY_THREAD_LOCAL GC_descr descr;
+ if (!typeInited) {
+ // only mark m_data
+ GC_word obj_bitmap[GC_BITMAP_SIZE(ObjectTemplatePropertyHandlerData)] = { 0 };
+ GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ObjectTemplatePropertyHandlerData, m_data));
+ descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(ObjectTemplatePropertyHandlerData));
+ typeInited = true;
+ }
+ return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
+ }
+
+private:
+ PropertyHandlerGetterCallback m_getter;
+ PropertyHandlerSetterCallback m_setter;
+ PropertyHandlerQueryCallback m_query;
+ PropertyHandlerDeleteCallback m_deleter;
+ PropertyHandlerEnumerationCallback m_enumerator;
+ PropertyHandlerDefineOwnPropertyCallback m_definer;
+ PropertyHandlerGetPropertyDescriptorCallback m_descriptor;
+ void* m_data;
+};
-class ObjectWithNamedPropertyHandler : public Object {
+class ObjectWithPropertyHandler : public Object {
public:
- ObjectWithNamedPropertyHandler(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto, ObjectTemplateNamedPropertyHandlerData* data)
+ ObjectWithPropertyHandler(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto, ObjectTemplatePropertyHandlerData* namedData, ObjectTemplatePropertyHandlerData* indexedData)
: Object(structure, std::move(values), proto)
- , m_data(data)
+ , m_namedPropertyHandler(namedData)
+ , m_indexedPropertyHandler(indexedData)
{
+ ASSERT(namedData || indexedData);
}
virtual ObjectGetResult getOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- if (!P.isIndexString() && m_data->getter) {
- OptionalRef<ValueRef> ret(m_data->getter(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue())));
-
- if (ret) {
- if (m_data->query) {
- auto attr = m_data->query(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue()));
- return ObjectGetResult(toImpl(ret.value()), attr & TemplatePropertyAttribute::TemplatePropertyAttributeWritable, attr & TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable, attr & TemplatePropertyAttribute::TemplatePropertyAttributeConfigurable);
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_getter) {
+ auto ret = propertyHandler->m_getter(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+
+ if (ret.hasValue()) {
+ if (propertyHandler->m_query) {
+ auto attr = propertyHandler->m_query(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ return ObjectGetResult(toImpl(ret.value()), attr & ObjectTemplatePropertyAttribute::PropertyAttributeWritable, attr & ObjectTemplatePropertyAttribute::PropertyAttributeEnumerable, attr & ObjectTemplatePropertyAttribute::PropertyAttributeConfigurable);
} else {
return ObjectGetResult(toImpl(ret.value()), true, true, true);
}
}
}
+
return Object::getOwnProperty(state, P);
}
virtual Value getOwnPropertyDescriptor(ExecutionState& state, const ObjectPropertyName& P) override
{
- if (m_data->descriptor) {
- auto ret = m_data->descriptor(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue()));
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_descriptor) {
+ auto ret = propertyHandler->m_descriptor(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
if (ret.hasValue()) {
return toImpl(ret.value());
- } else {
- return Object::getOwnPropertyDescriptor(state, P);
}
- } else {
- return Object::getOwnPropertyDescriptor(state, P);
}
+
+ return Object::getOwnPropertyDescriptor(state, P);
}
virtual bool defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc) override
{
- if (!P.isIndexString()) {
- if (m_data->definer) {
- auto ret = m_data->definer(toRef(&state), toRef(this), toRef(this), m_data->data, toRef(P.toPropertyKeyValue()), ObjectPropertyDescriptorRef((void*)&desc));
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler) {
+ if (propertyHandler->m_definer) {
+ auto ret = propertyHandler->m_definer(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data, toRef(P.toPlainValue()), ObjectPropertyDescriptorRef((void*)&desc));
if (ret.hasValue()) {
return ret.value()->toBoolean(toRef(&state));
}
- } else if (m_data->setter && desc.isValuePresent() && desc.isWritable() && desc.isEnumerable() && desc.isConfigurable()) {
- auto ret = m_data->setter(toRef(&state), toRef(this), toRef(this), m_data->data, toRef(P.toPropertyKeyValue()), toRef(desc.value()));
+ } else if (propertyHandler->m_setter && desc.isValuePresent() && desc.isWritable() && desc.isEnumerable() && desc.isConfigurable()) {
+ auto ret = propertyHandler->m_setter(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data, toRef(P.toPlainValue()), toRef(desc.value()));
if (ret.hasValue()) {
return ret.value()->toBoolean(toRef(&state));
}
}
}
+
return Object::defineOwnProperty(state, P, desc);
}
- virtual bool hasOwnProperty(ExecutionState& state, const ObjectPropertyName& propertyName) override
+ virtual bool hasOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- if (m_data->query) {
- auto attr = m_data->query(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(propertyName.toPropertyKeyValue()));
- return attr & TemplatePropertyAttributeExist;
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_query) {
+ auto attr = propertyHandler->m_query(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ return attr & ObjectTemplatePropertyAttribute::PropertyAttributeExist;
}
- return Object::hasOwnProperty(state, propertyName);
+
+ return Object::hasOwnProperty(state, P);
}
virtual ObjectHasPropertyResult hasProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- if (m_data->query) {
- auto attr = m_data->query(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue()));
- if (attr & TemplatePropertyAttributeExist) {
- bool isWritable = attr & TemplatePropertyAttribute::TemplatePropertyAttributeWritable;
- bool isEnumerable = attr & TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable;
- bool isConfigurable = attr & TemplatePropertyAttribute::TemplatePropertyAttributeConfigurable;
-
- Value v;
- if (m_data->getter) {
- OptionalRef<ValueRef> ret = (m_data->getter(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue())));
- if (ret) {
- v = toImpl(ret.value());
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler) {
+ if (propertyHandler->m_query) {
+ auto attr = propertyHandler->m_query(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ if (attr & ObjectTemplatePropertyAttribute::PropertyAttributeExist) {
+ bool isWritable = attr & ObjectTemplatePropertyAttribute::PropertyAttributeWritable;
+ bool isEnumerable = attr & ObjectTemplatePropertyAttribute::PropertyAttributeEnumerable;
+ bool isConfigurable = attr & ObjectTemplatePropertyAttribute::PropertyAttributeConfigurable;
+
+ Value v;
+ if (propertyHandler->m_getter) {
+ OptionalRef<ValueRef> ret = propertyHandler->m_getter(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ if (ret.hasValue()) {
+ v = toImpl(ret.value());
+ }
}
- }
- ObjectHasPropertyResult result(ObjectGetResult(v, isWritable, isEnumerable, isConfigurable));
- return result;
- }
- } else if (m_data->getter) {
- OptionalRef<ValueRef> ret = (m_data->getter(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue())));
- if (ret) {
- return ObjectGetResult(toImpl(ret.value()), true, true, true);
+ ObjectHasPropertyResult result(ObjectGetResult(v, isWritable, isEnumerable, isConfigurable));
+ return result;
+ }
+ } else if (propertyHandler->m_getter) {
+ OptionalRef<ValueRef> ret = propertyHandler->m_getter(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ if (ret.hasValue()) {
+ return ObjectGetResult(toImpl(ret.value()), true, true, true);
+ }
}
}
virtual bool deleteOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override
{
- if (m_data->deleter) {
- auto ret = m_data->deleter(toRef(&state), toRef(this), toRef(this), m_data->data,
- toRef(P.toPropertyKeyValue()));
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_deleter) {
+ auto ret = propertyHandler->m_deleter(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
if (ret.hasValue()) {
return ret.value()->toBoolean(toRef(&state));
}
}
+
return Object::deleteOwnProperty(state, P);
}
virtual void enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey = true) override
{
- if (m_data->enumerator) {
- auto ret = m_data->enumerator(toRef(&state), toRef(this), toRef(this), m_data->data);
- if (m_data->query) {
- for (size_t i = 0; i < ret->size(); i++) {
- if (shouldSkipSymbolKey && ret->at(i)->isSymbol()) {
- continue;
+ ObjectTemplatePropertyHandlerData* propertyHandler = m_namedPropertyHandler;
+ for (size_t i = 0; i < 2; i++) {
+ if (propertyHandler && propertyHandler->m_enumerator) {
+ auto ret = propertyHandler->m_enumerator(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data);
+ if (propertyHandler->m_query) {
+ for (size_t i = 0; i < ret->size(); i++) {
+ if (shouldSkipSymbolKey && ret->at(i)->isSymbol()) {
+ continue;
+ }
+
+ auto attr = propertyHandler->m_query(toRef(&state), toRef(this), toRef(this), propertyHandler->m_data,
+ ret->at(i));
+
+ ObjectStructurePropertyDescriptor desc = ObjectStructurePropertyDescriptor::createDataDescriptor(
+ (ObjectStructurePropertyDescriptor::PresentAttribute)(
+ ((attr & ObjectTemplatePropertyAttribute::PropertyAttributeWritable) ? ObjectStructurePropertyDescriptor::WritablePresent : 0) | ((attr & ObjectTemplatePropertyAttribute::PropertyAttributeEnumerable) ? ObjectStructurePropertyDescriptor::EnumerablePresent : 0) | ((attr & ObjectTemplatePropertyAttribute::PropertyAttributeConfigurable) ? ObjectStructurePropertyDescriptor::ConfigurablePresent : 0)));
+ if (!callback(state, this, ObjectPropertyName(state, toImpl(ret->at(i))), desc, data)) {
+ return;
+ }
}
-
- auto attr = m_data->query(toRef(&state), toRef(this), toRef(this), m_data->data,
- ret->at(i));
-
- ObjectStructurePropertyDescriptor desc = ObjectStructurePropertyDescriptor::createDataDescriptor(
- (ObjectStructurePropertyDescriptor::PresentAttribute)(
- ((attr & TemplatePropertyAttribute::TemplatePropertyAttributeWritable) ? ObjectStructurePropertyDescriptor::WritablePresent : 0) | ((attr & TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable) ? ObjectStructurePropertyDescriptor::EnumerablePresent : 0) | ((attr & TemplatePropertyAttribute::TemplatePropertyAttributeConfigurable) ? ObjectStructurePropertyDescriptor::ConfigurablePresent : 0)));
- callback(state, this, ObjectPropertyName(state, toImpl(ret->at(i))), desc, data);
- }
- } else {
- for (size_t i = 0; i < ret->size(); i++) {
- if (shouldSkipSymbolKey && ret->at(i)->isSymbol()) {
- continue;
+ } else {
+ for (size_t i = 0; i < ret->size(); i++) {
+ if (shouldSkipSymbolKey && ret->at(i)->isSymbol()) {
+ continue;
+ }
+ ObjectStructurePropertyDescriptor desc = ObjectStructurePropertyDescriptor::createDataDescriptor();
+ if (!callback(state, this, ObjectPropertyName(state, toImpl(ret->at(i))), desc, data)) {
+ return;
+ }
}
- ObjectStructurePropertyDescriptor desc = ObjectStructurePropertyDescriptor::createDataDescriptor();
- callback(state, this, ObjectPropertyName(state, toImpl(ret->at(i))), desc, data);
}
}
+
+ propertyHandler = m_indexedPropertyHandler;
}
+
Object::enumeration(state, callback, data, shouldSkipSymbolKey);
}
virtual ObjectGetResult get(ExecutionState& state, const ObjectPropertyName& P, const Value& receiver) override
{
- if (!P.isIndexString()) {
- OptionalRef<ValueRef> ret(m_data->getter(toRef(&state), toRef(this), toRef(receiver), m_data->data,
- toRef(P.toPropertyKeyValue())));
-
- if (ret) {
- if (m_data->query) {
- auto attr = m_data->query(toRef(&state), toRef(this), toRef(receiver), m_data->data,
- toRef(P.toPropertyKeyValue()));
- return ObjectGetResult(toImpl(ret.value()), attr & TemplatePropertyAttribute::TemplatePropertyAttributeWritable, attr & TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable, attr & TemplatePropertyAttribute::TemplatePropertyAttributeConfigurable);
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_getter) {
+ auto ret = propertyHandler->m_getter(toRef(&state), toRef(this), toRef(receiver), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+
+ if (ret.hasValue()) {
+ if (propertyHandler->m_query) {
+ auto attr = propertyHandler->m_query(toRef(&state), toRef(this), toRef(receiver), propertyHandler->m_data,
+ toRef(P.toPlainValue()));
+ return ObjectGetResult(toImpl(ret.value()), attr & ObjectTemplatePropertyAttribute::PropertyAttributeWritable, attr & ObjectTemplatePropertyAttribute::PropertyAttributeEnumerable, attr & ObjectTemplatePropertyAttribute::PropertyAttributeConfigurable);
} else {
return ObjectGetResult(toImpl(ret.value()), true, true, true);
}
}
}
+
return Object::get(state, P, receiver);
}
virtual bool set(ExecutionState& state, const ObjectPropertyName& P, const Value& v, const Value& receiver) override
{
- if (m_data->setter && !P.isIndexString()) {
- auto ret = m_data->setter(toRef(&state), toRef(this), toRef(receiver), m_data->data, toRef(P.toPropertyKeyValue()), toRef(v));
+ ObjectTemplatePropertyHandlerData* propertyHandler = P.isIndexString() ? m_indexedPropertyHandler : m_namedPropertyHandler;
+ if (propertyHandler && propertyHandler->m_setter) {
+ auto ret = propertyHandler->m_setter(toRef(&state), toRef(this), toRef(receiver), propertyHandler->m_data, toRef(P.toPlainValue()), toRef(v));
if (ret.hasValue()) {
return ret.value()->toBoolean(toRef(&state));
}
}
+
return Object::set(state, P, v, receiver);
}
}
private:
- ObjectTemplateNamedPropertyHandlerData* m_data;
+ ObjectTemplatePropertyHandlerData* m_namedPropertyHandler;
+ ObjectTemplatePropertyHandlerData* m_indexedPropertyHandler;
};
+void* ObjectTemplate::operator new(size_t size)
+{
+ static MAY_THREAD_LOCAL bool typeInited = false;
+ static MAY_THREAD_LOCAL GC_descr descr;
+ if (!typeInited) {
+ GC_word objBitmap[GC_BITMAP_SIZE(ObjectTemplate)] = { 0 };
+ Template::fillGCDescriptor(objBitmap);
+ GC_set_bit(objBitmap, GC_WORD_OFFSET(ObjectTemplate, m_constructor));
+ GC_set_bit(objBitmap, GC_WORD_OFFSET(ObjectTemplate, m_namedPropertyHandler));
+ GC_set_bit(objBitmap, GC_WORD_OFFSET(ObjectTemplate, m_indexedPropertyHandler));
+ descr = GC_make_descriptor(objBitmap, GC_WORD_LEN(ObjectTemplate));
+ typeInited = true;
+ }
+ return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
+}
+
+ObjectTemplate::ObjectTemplate(Optional<FunctionTemplate*> constructor)
+ : m_constructor(constructor)
+ , m_namedPropertyHandler(nullptr)
+ , m_indexedPropertyHandler(nullptr)
+{
+}
+
Object* ObjectTemplate::instantiate(Context* ctx)
{
if (!m_cachedObjectStructure.m_objectStructure) {
proto = ctx->globalObject()->objectPrototype();
}
- if (m_namedPropertyHandler) {
- result = new ObjectWithNamedPropertyHandler(m_cachedObjectStructure.m_objectStructure, std::move(objectPropertyValues), proto, m_namedPropertyHandler);
+ if (m_namedPropertyHandler || m_indexedPropertyHandler) {
+ result = new ObjectWithPropertyHandler(m_cachedObjectStructure.m_objectStructure, std::move(objectPropertyValues), proto, m_namedPropertyHandler, m_indexedPropertyHandler);
} else {
result = new Object(m_cachedObjectStructure.m_objectStructure, std::move(objectPropertyValues), proto);
}
return result.error.isEmpty();
}
-void ObjectTemplate::setNamedPropertyHandler(const ObjectTemplateNamedPropertyHandlerData& data)
+void ObjectTemplate::setNamedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data)
{
- m_namedPropertyHandler = new (GC) ObjectTemplateNamedPropertyHandlerData(data);
+ ASSERT(!m_namedPropertyHandler);
+ m_namedPropertyHandler = new ObjectTemplatePropertyHandlerData(data);
+}
+
+void ObjectTemplate::setIndexedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data)
+{
+ ASSERT(!m_indexedPropertyHandler);
+ m_indexedPropertyHandler = new ObjectTemplatePropertyHandlerData(data);
}
void ObjectTemplate::removeNamedPropertyHandler()
{
+ // just set nullptr here because property handler might be used in instantiated ObjectWithPropertyHandler
+ ASSERT(!m_namedPropertyHandler);
m_namedPropertyHandler = nullptr;
}
+
+void ObjectTemplate::removeIndexedPropertyHandler()
+{
+ // just set nullptr here because property handler might be used in instantiated ObjectWithPropertyHandler
+ ASSERT(!m_indexedPropertyHandler);
+ m_indexedPropertyHandler = nullptr;
+}
} // namespace Escargot
namespace Escargot {
class FunctionTemplate;
-struct ObjectTemplateNamedPropertyHandlerData;
+class ObjectTemplatePropertyHandlerData;
+struct ObjectTemplatePropertyHandlerConfiguration;
class ObjectTemplate : public Template {
public:
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
- void setNamedPropertyHandler(const ObjectTemplateNamedPropertyHandlerData& data);
+ void setNamedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data);
+ void setIndexedPropertyHandler(const ObjectTemplatePropertyHandlerConfiguration& data);
void removeNamedPropertyHandler();
+ void removeIndexedPropertyHandler();
-protected:
+private:
Optional<FunctionTemplate*> m_constructor;
- ObjectTemplateNamedPropertyHandlerData* m_namedPropertyHandler;
+ ObjectTemplatePropertyHandlerData* m_namedPropertyHandler;
+ ObjectTemplatePropertyHandlerData* m_indexedPropertyHandler;
};
} // namespace Escargot
virtual void* allocateThreadLocalCustomData() = 0;
virtual void deallocateThreadLocalCustomData() = 0;
-#ifdef ESCARGOT_USE_CUSTOM_LOGGING
+#ifdef ENABLE_CUSTOM_LOGGING
// customized logging
virtual void customInfoLogger(const char* format, va_list arg) = 0;
virtual void customErrorLogger(const char* format, va_list arg) = 0;
size_t PointerValue::g_arrayObjectTag;
size_t PointerValue::g_arrayPrototypeObjectTag;
+size_t PointerValue::g_scriptFunctionObjectTag;
size_t PointerValue::g_objectRareDataTag;
size_t PointerValue::g_doubleInEncodedValueTag;
friend class Global;
friend class ByteCodeInterpreter;
- // tag values for fast type check
- // these values actually have unique virtual table address of each object class
- static size_t g_arrayObjectTag;
- static size_t g_arrayPrototypeObjectTag;
- static size_t g_objectRareDataTag;
- static size_t g_doubleInEncodedValueTag;
-
public:
virtual ~PointerValue() {}
// fast type check with tag comparison
return hasTag(g_doubleInEncodedValueTag);
}
+ inline bool hasArrayObjectTag() const
+ {
+ return hasTag(g_arrayObjectTag);
+ }
+
// type check by virtual function call
virtual bool isFunctionObject() const
{
return *((size_t*)(this) + 1);
}
-private:
+protected:
inline bool hasTag(const size_t tag) const
{
ASSERT(!!tag);
return *((size_t*)(this));
}
+ inline void writeTag(const size_t tag)
+ {
+ *((size_t*)(this)) = tag;
+ }
+
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv);
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget);
+
+ // tag values for fast type check
+ // these values actually have unique virtual table address of each object class
+ static size_t g_arrayObjectTag;
+ static size_t g_arrayPrototypeObjectTag;
+ static size_t g_scriptFunctionObjectTag;
+ static size_t g_objectRareDataTag;
+ static size_t g_doubleInEncodedValueTag;
};
} // namespace Escargot
{
m_state = PromiseState::Rejected;
m_promiseResult = reason;
- triggerPromiseReactions(state, m_rejectReactions);
+ if (LIKELY(hasRejectHandlers() > 0)) {
+ triggerPromiseReactions(state, m_rejectReactions);
+ } else if (state.context()->vmInstance()->isPromiseRejectCallbackRegistered()) {
+ state.context()->vmInstance()->triggerPromiseRejectCallback(state, this, reason, VMInstance::PromiseRejectEvent::PromiseRejectWithNoHandler);
+ }
m_fulfillReactions.clear();
m_rejectReactions.clear();
PromiseReaction::Capability capability = resultCapability.hasValue() ? resultCapability.value() : PromiseReaction::Capability(nullptr, nullptr, nullptr);
#ifdef ESCARGOT_DEBUGGER
- if (state.context()->debugger() != nullptr && state.context()->debugger()->enabled()) {
+ if (state.context()->debuggerEnabled()) {
capability.m_savedStackTrace = Debugger::saveStackTrace(state);
}
#endif /* ESCARGOT_DEBUGGER */
case PromiseObject::PromiseState::Rejected: {
Job* job = new PromiseReactionJob(state.context(), PromiseReaction(onRejected, capability), promiseResult());
state.context()->vmInstance()->enqueueJob(job);
+
+ if (UNLIKELY(state.context()->vmInstance()->isPromiseRejectCallbackRegistered())) {
+ state.context()->vmInstance()->triggerPromiseRejectCallback(state, this, promiseResult(), VMInstance::PromiseRejectEvent::PromiseHandlerAddedAfterReject);
+ }
break;
}
default:
// https://tc39.es/ecma262/#sec-promise.any-reject-element-functions
static Value promiseAnyRejectElementFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget);
+ bool hasResolveHandlers() const { return m_fulfillReactions.size() > 0; }
+ bool hasRejectHandlers() const { return m_rejectReactions.size() > 0; }
+
protected:
static inline void fillGCDescriptor(GC_word* desc)
{
return m_isConstructible;
}
+ virtual bool hasOwnEnumeration() const override
+ {
+ return true;
+ }
+
virtual Context* getFunctionRealm(ExecutionState& state) override;
static ProxyObject* createProxy(ExecutionState& state, const Value& target, const Value& handler);
: Object(state, proto, ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + (hasLastIndex ? 5 : 4))
, m_source(NULL)
, m_optionString(NULL)
- , m_option(None)
+ , m_legacyFeaturesEnabled(true)
, m_yarrPattern(NULL)
, m_bytecodePattern(NULL)
, m_lastIndex(Value(0))
, m_lastExecutedString(NULL)
- , m_legacyFeaturesEnabled(true)
{
+ setOptionValueForGC(None);
initRegExpObject(state, hasLastIndex);
}
}
}
-void* RegExpObject::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(RegExpObject)] = { 0 };
- Object::fillGCDescriptor(obj_bitmap);
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_source));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_optionString));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_yarrPattern));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_bytecodePattern));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_lastIndex));
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(RegExpObject, m_lastExecutedString));
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(RegExpObject));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-
static String* escapeSlashInPattern(String* patternStr)
{
if (patternStr->length() == 0) {
String* defaultRegExpString = state.context()->staticStrings().defaultRegExpString.string();
String* previousSource = m_source;
- RegExpObject::Option previousOptions = m_option;
+ RegExpObject::Option previousOptions = this->option();
- m_option = option;
+ setOptionValueForGC(option);
m_source = source->length() ? source : defaultRegExpString;
m_source = escapeSlashInPattern(m_source);
- auto entry = getCacheEntryAndCompileIfNeeded(state, m_source, m_option);
+ auto entry = getCacheEntryAndCompileIfNeeded(state, m_source, this->option());
if (entry.m_yarrError) {
m_source = previousSource;
- m_option = previousOptions;
+ setOptionValueForGC(previousOptions);
ErrorObject::throwBuiltinError(state, ErrorObject::SyntaxError, entry.m_yarrError);
}
void RegExpObject::setLastIndex(ExecutionState& state, const Value& v)
{
- if (UNLIKELY(hasRareData() && rareData()->m_hasNonWritableLastIndexRegExpObject && (m_option & (Option::Sticky | Option::Global)))) {
+ if (UNLIKELY(hasRareData() && rareData()->m_hasNonWritableLastIndexRegExpObject && (option() & (Option::Sticky | Option::Global)))) {
Object::throwCannotWriteError(state, ObjectStructurePropertyName(state.context()->staticStrings().lastIndex));
}
m_lastIndex = v;
void RegExpObject::setOption(const Option& option)
{
- if (((m_option & Option::MultiLine) != (option & Option::MultiLine))
- || ((m_option & Option::IgnoreCase) != (option & Option::IgnoreCase))) {
+ Option currentOption = this->option();
+ if (((currentOption & Option::MultiLine) != (option & Option::MultiLine))
+ || ((currentOption & Option::IgnoreCase) != (option & Option::IgnoreCase))) {
ASSERT(!m_yarrPattern);
m_bytecodePattern = NULL;
}
- m_option = option;
+ setOptionValueForGC(option);
}
RegExpObject::RegExpCacheEntry& RegExpObject::getCacheEntryAndCompileIfNeeded(ExecutionState& state, String* source, const Option& option)
m_lastExecutedString = str;
if (!m_bytecodePattern) {
- RegExpCacheEntry& entry = getCacheEntryAndCompileIfNeeded(state, m_source, m_option);
+ RegExpCacheEntry& entry = getCacheEntryAndCompileIfNeeded(state, m_source, option());
if (entry.m_yarrError) {
matchResult.m_subPatternNum = 0;
return false;
Option option()
{
- return m_option;
+ return static_cast<Option>(m_option >> 1);
}
JSC::Yarr::YarrPattern* yarrPatern()
ArrayObject* createRegExpMatchedArray(ExecutionState& state, const RegexMatchResult& result, String* input);
void pushBackToRegExpMatchedArray(ExecutionState& state, ArrayObject* array, size_t& index, const size_t limit, const RegexMatchResult& result, String* str);
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
-
protected:
explicit RegExpObject(ExecutionState& state, Object* proto, bool hasLastIndex = true);
private:
+ void setOptionValueForGC(const Option& option)
+ {
+ m_option = static_cast<Option>(option << 1 | 1);
+ }
void setOption(const Option& option);
void internalInit(ExecutionState& state, String* source, Option option = None);
String* m_source;
String* m_optionString;
- Option m_option;
+ Option m_option : 16;
+ bool m_legacyFeaturesEnabled : 1;
JSC::Yarr::YarrPattern* m_yarrPattern;
JSC::Yarr::BytecodePattern* m_bytecodePattern;
EncodedValue m_lastIndex;
const String* m_lastExecutedString;
- bool m_legacyFeaturesEnabled;
};
class RegExpStringIteratorObject : public IteratorObject {
: String()
, m_isOwnerMayFreed(false)
, m_isUnloaded(true)
+ , m_refCount(0)
, m_vmInstance(instance)
, m_callbackData(callbackData)
, m_stringLoadCallback(loadCallback)
v.push_back(this);
GC_REGISTER_FINALIZER_NO_ORDER(this, [](void* obj, void*) {
ReloadableString* self = (ReloadableString*)obj;
+ ASSERT(self->refCount() == 0);
+
if (!self->m_isUnloaded) {
self->m_stringUnloadCallback(const_cast<void*>(self->m_bufferData.buffer), self->m_callbackData);
}
bool ReloadableString::unload()
{
ASSERT(!m_isUnloaded);
- if (UNLIKELY(!m_bufferData.length)) {
+ if (UNLIKELY(!m_bufferData.length || m_refCount > 0)) {
return false;
}
- return unloadWorker(currentStackPointer());
+ return unloadWorker();
}
-bool ReloadableString::unloadWorker(void* callerSP)
+bool ReloadableString::unloadWorker()
{
-#if defined(STACK_GROWS_DOWN)
- size_t* start = (size_t*)((size_t)callerSP & ~(sizeof(size_t) - 1));
- size_t* end = (size_t*)m_vmInstance->stackStartAddress();
-#else
- size_t* start = (size_t*)m_vmInstance->stackStartAddress();
- size_t* end = (size_t*)((size_t)callerSP & ~(sizeof(size_t) - 1));
-#endif
-
- while (start != end) {
- if (UNLIKELY(*start == (size_t)m_bufferData.buffer)) {
- // if there is reference on stack, we cannot unload string.
- return false;
- }
- start++;
- }
+ ASSERT(!m_isUnloaded && !m_refCount);
m_stringUnloadCallback(const_cast<void*>(m_bufferData.buffer), m_callbackData);
m_bufferData.buffer = nullptr;
if (isUnloaded()) {
load();
}
- return StringBufferAccessData(m_bufferData.has8BitContent, m_bufferData.length, const_cast<void*>(m_bufferData.buffer));
+
+ // add refCount pointer to count its usage in StringBufferAccessData
+ return StringBufferAccessData(m_bufferData.has8BitContent, m_bufferData.length, const_cast<void*>(m_bufferData.buffer), &m_refCount);
}
bool isUnloaded()
return m_isUnloaded;
}
+ size_t refCount() const
+ {
+ return m_refCount;
+ }
+
void* operator new(size_t);
void* operator new[](size_t) = delete;
void operator delete[](void*) = delete;
private:
void initBufferAccessData(void* data, size_t len, bool is8bit);
- ATTRIBUTE_NO_SANITIZE_ADDRESS bool unloadWorker(void* callerSP);
+ ATTRIBUTE_NO_SANITIZE_ADDRESS bool unloadWorker();
size_t unloadedBufferSize()
{
bool m_isOwnerMayFreed;
bool m_isUnloaded;
+ size_t m_refCount; // reference count representing the usage of this ReloadableString
VMInstance* m_vmInstance;
void* m_callbackData;
void* (*m_stringLoadCallback)(void* callbackData);
namespace Escargot {
-void* RopeString::operator new(size_t size)
+void* RopeString::operator new(size_t size, bool is8Bit)
{
+ if (is8Bit) {
+ // if 8-bit string, we don't needs typed malloc
+ return GC_MALLOC(size);
+ }
static MAY_THREAD_LOCAL bool typeInited = false;
static MAY_THREAD_LOCAL GC_descr descr;
if (!typeInited) {
return lstr;
}
- if (llen + rlen < ROPE_STRING_MIN_LENGTH) {
+ if (llen + rlen <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
const auto& lData = lstr->bufferAccessData();
const auto& rData = rstr->bufferAccessData();
if (LIKELY(lData.has8BitContent && rData.has8BitContent)) {
- Latin1StringData ret;
size_t len = lData.length + rData.length;
- ret.resizeWithUninitializedValues(len);
-
- LChar* result = ret.data();
- const LChar* buffer = (const LChar*)lData.buffer;
- for (size_t i = 0; i < lData.length; i++) {
- result[i] = buffer[i];
- }
-
- result += lData.length;
- buffer = (const LChar*)rData.buffer;
- for (size_t i = 0; i < rData.length; i++) {
- result[i] = buffer[i];
- }
- return new Latin1String(std::move(ret));
+ LChar* result = reinterpret_cast<LChar*>(alloca(len));
+ memcpy(result, lData.buffer, lData.length);
+ memcpy(result + lData.length, rData.buffer, rData.length);
+ return String::fromLatin1(result, len);
} else {
StringBuilder builder;
builder.appendString(lstr);
ErrorObject::throwBuiltinError(*state, ErrorObject::RangeError, ErrorObject::Messages::String_InvalidStringLength);
}
- RopeString* rope = new RopeString();
+ bool l8bit = lstr->has8BitContent();
+ bool r8bit = rstr->has8BitContent();
+ bool result8Bit = l8bit & r8bit;
+ RopeString* rope = new (result8Bit) RopeString();
rope->m_bufferData.length = llen + rlen;
rope->m_left = lstr;
rope->m_bufferData.buffer = rstr;
-
- bool l8bit;
- if (lstr->isRopeString()) {
- l8bit = ((RopeString*)lstr)->m_bufferData.has8BitContent;
- } else {
- l8bit = lstr->has8BitContent();
- }
-
- bool r8bit;
- if (rstr->isRopeString()) {
- r8bit = ((RopeString*)rstr)->m_bufferData.has8BitContent;
- } else {
- r8bit = rstr->has8BitContent();
- }
-
- rope->m_bufferData.has8BitContent = l8bit & r8bit;
+ rope->m_bufferData.has8BitContent = result8Bit;
return rope;
}
}
}
+static MAY_THREAD_LOCAL const RopeString* g_lastUsedString;
+static MAY_THREAD_LOCAL bool g_headOfCharAt = true;
+class RopeStringUsageChecker {
+public:
+ RopeStringUsageChecker()
+ {
+ m_headOfCharAt = g_headOfCharAt;
+ g_headOfCharAt = false;
+ }
+
+ ~RopeStringUsageChecker()
+ {
+ g_headOfCharAt = m_headOfCharAt;
+ }
+
+ bool isOftenUsed(const RopeString* rs)
+ {
+ auto old = g_lastUsedString;
+ if (m_headOfCharAt) {
+ g_lastUsedString = rs;
+ }
+ return rs == old;
+ }
+
+private:
+ bool m_headOfCharAt;
+};
+
+// worker for preventing too many recursive calls with RopeString
+static char16_t charAtWorker(size_t idx, const RopeString* self)
+{
+ while (true) {
+ size_t leftLength = self->left()->length();
+ if (idx < leftLength) {
+ if (self->left()->isRopeString() && !self->left()->asRopeString()->wasFlattened()) {
+ self = self->left()->asRopeString();
+ continue;
+ }
+ return self->left()->charAt(idx);
+ } else {
+ if (self->right()->isRopeString() && !self->right()->asRopeString()->wasFlattened()) {
+ self = self->right()->asRopeString();
+ idx = idx - leftLength;
+ continue;
+ }
+ return self->right()->charAt(idx - leftLength);
+ }
+ }
+}
+
+char16_t RopeString::charAt(const size_t idx) const
+{
+ if (wasFlattened()) {
+ return bufferAccessData().charAt(idx);
+ }
+
+ RopeStringUsageChecker checker;
+
+ if (checker.isOftenUsed(this)) {
+ return bufferAccessData().charAt(idx);
+ }
+
+ return charAtWorker(idx, this);
+}
+
UTF8StringDataNonGCStd RopeString::toNonGCUTF8StringData(int options) const
{
return bufferAccessData().toUTF8String<UTF8StringDataNonGCStd>();
return (String*)m_bufferData.buffer;
}
- void* operator new(size_t size);
+ virtual char16_t charAt(const size_t idx) const override;
+
+ void* operator new(size_t size, bool is8Bit);
void* operator new[](size_t size) = delete;
protected:
#ifdef ESCARGOT_DEBUGGER
Debugger* debugger = m_context->debugger();
- if (debugger && debugger->enabled()) {
- ExecutionState state(m_context);
- String* message = error.toStringWithoutException(state);
-
- debugger->sendType(Debugger::ESCARGOT_MESSAGE_EXCEPTION);
- if (debugger->enabled()) {
- StringView* messageView = new StringView(message);
- debugger->sendString(Debugger::ESCARGOT_MESSAGE_STRING_8BIT, messageView);
- }
- }
+ Debugger::SavedStackTraceDataVector exceptionTrace;
#endif /* ESCARGOT_DEBUGGER */
ByteCodeLOCDataMap locMap;
- for (size_t i = 0; i < m_stackTraceData.size(); i++) {
- if ((size_t)m_stackTraceData[i].second.loc.index == SIZE_MAX && (size_t)m_stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
+ for (size_t i = 0; i < m_stackTraceDataVector.size(); i++) {
+ if ((size_t)m_stackTraceDataVector[i].loc.index == SIZE_MAX && (size_t)m_stackTraceDataVector[i].loc.actualCodeBlock != SIZE_MAX) {
// this means loc not computed yet.
- ByteCodeBlock* block = m_stackTraceData[i].second.loc.actualCodeBlock;
+ StackTraceData traceData = m_stackTraceDataVector[i];
+ ByteCodeBlock* block = traceData.loc.actualCodeBlock;
ByteCodeLOCData* locData;
auto iterMap = locMap.find(block);
}
ExtendedNodeLOC loc = block->computeNodeLOCFromByteCode(m_context,
- m_stackTraceData[i].second.loc.byteCodePosition, block->m_codeBlock, locData);
+ traceData.loc.byteCodePosition, block->m_codeBlock, locData);
- StackTraceData traceData;
traceData.loc = loc;
- InterpretedCodeBlock* cb = block->m_codeBlock;
- traceData.src = cb->script()->srcName();
- traceData.sourceCode = cb->script()->sourceCode();
- traceData.functionName = m_stackTraceData[i].second.functionName;
- traceData.isFunction = m_stackTraceData[i].second.isFunction;
- traceData.isConstructor = m_stackTraceData[i].second.isConstructor;
- traceData.isAssociatedWithJavaScriptCode = m_stackTraceData[i].second.isAssociatedWithJavaScriptCode;
- traceData.isEval = m_stackTraceData[i].second.isEval;
-
- result.stackTraceData.pushBack(traceData);
+ result.stackTrace.pushBack(traceData);
#ifdef ESCARGOT_DEBUGGER
- if (i < 8 && debugger && debugger->enabled()) {
- debugger->sendBacktraceInfo(Debugger::ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE,
- block, (uint32_t)loc.line, (uint32_t)loc.column, m_stackTraceData[i].second.executionStateDepth);
+ if (i < 8 && debugger != nullptr) {
+ exceptionTrace.pushBack(Debugger::SavedStackTraceData(block, (uint32_t)loc.line, (uint32_t)loc.column));
}
#endif /* ESCARGOT_DEBUGGER */
} else {
- result.stackTraceData.pushBack(m_stackTraceData[i].second);
+ result.stackTrace.pushBack(m_stackTraceDataVector[i]);
}
}
for (auto iter = locMap.begin(); iter != locMap.end(); iter++) {
delete iter->second;
}
+
+#ifdef ESCARGOT_DEBUGGER
+ if (debugger != nullptr) {
+ ExecutionState state(m_context);
+ String* message = error.toStringWithoutException(state);
+
+ debugger->exceptionCaught(message, exceptionTrace);
+ }
+#endif /* ESCARGOT_DEBUGGER */
}
SandBox::SandBoxResult SandBox::run(Value (*scriptRunner)(ExecutionState&, void*), void* data)
return result;
}
-bool SandBox::createStackTraceData(StackTraceDataVector& stackTraceData, ExecutionState& state, bool stopAtPause)
+bool SandBox::createStackTrace(StackTraceDataVector& stackTraceDataVector, ExecutionState& state, bool stopAtPause)
{
UNUSED_VARIABLE(stopAtPause);
uint32_t executionStateDepthIndex = 0;
ExecutionState* activeSavedStackTraceExecutionState = nullptr;
- if (stopAtPause && state.context()->debugger() != nullptr) {
+ if (stopAtPause && state.context()->debuggerEnabled()) {
activeSavedStackTraceExecutionState = state.context()->debugger()->activeSavedStackTraceExecutionState();
}
#endif /* ESCARGOT_DEBUGGER */
+ std::vector<ExecutionState*> stateStack;
+
while (pstate) {
FunctionObject* callee = pstate->resolveCallee();
ExecutionState* es = pstate;
bool alreadyExists = false;
- for (size_t i = 0; i < stackTraceData.size(); i++) {
- if (stackTraceData[i].first == es || stackTraceData[i].first->lexicalEnvironment() == es->lexicalEnvironment()) {
+ for (size_t i = 0; i < stateStack.size(); i++) {
+ if (stateStack[i] == es || stateStack[i]->lexicalEnvironment() == es->lexicalEnvironment()) {
alreadyExists = true;
break;
}
if (cb) {
ByteCodeBlock* b = cb->byteCodeBlock();
ExtendedNodeLOC loc(SIZE_MAX, SIZE_MAX, SIZE_MAX);
- ASSERT(!pstate->m_isNativeFunctionObjectExecutionContext);
+ ASSERT(!pstate->isNativeFunctionObjectExecutionContext());
if (pstate->m_programCounter != nullptr) {
loc.byteCodePosition = *pstate->m_programCounter - (size_t)b->m_code.data();
loc.actualCodeBlock = b;
}
SandBox::StackTraceData data;
data.loc = loc;
- data.src = cb->script()->srcName();
+ data.srcName = cb->script()->srcName();
data.sourceCode = cb->script()->sourceCode();
data.isEval = true;
data.isFunction = false;
data.executionStateDepth = executionStateDepthIndex;
#endif /* ESCARGOT_DEBUGGER */
- stackTraceData.pushBack(std::make_pair(es, data));
+ stateStack.push_back(es);
+ stackTraceDataVector.pushBack(data);
}
} else if (pstate->codeBlock() && pstate->codeBlock()->isInterpretedCodeBlock() && pstate->codeBlock()->asInterpretedCodeBlock()->isEvalCodeInFunction()) {
CodeBlock* cb = pstate->codeBlock();
ExtendedNodeLOC loc(SIZE_MAX, SIZE_MAX, SIZE_MAX);
SandBox::StackTraceData data;
data.loc = loc;
- data.src = cb->asInterpretedCodeBlock()->script()->srcName();
+ data.srcName = cb->asInterpretedCodeBlock()->script()->srcName();
data.sourceCode = String::emptyString;
data.isEval = true;
data.isFunction = false;
data.executionStateDepth = executionStateDepthIndex;
#endif /* ESCARGOT_DEBUGGER */
- stackTraceData.pushBack(std::make_pair(es, data));
+ stateStack.push_back(es);
+ stackTraceDataVector.pushBack(data);
} else if (callee) {
CodeBlock* cb = callee->codeBlock();
ExtendedNodeLOC loc(SIZE_MAX, SIZE_MAX, SIZE_MAX);
if (cb->isInterpretedCodeBlock()) {
ByteCodeBlock* b = cb->asInterpretedCodeBlock()->byteCodeBlock();
- ASSERT(!pstate->m_isNativeFunctionObjectExecutionContext);
+ ASSERT(!pstate->isNativeFunctionObjectExecutionContext());
if (pstate->m_programCounter != nullptr) {
loc.byteCodePosition = *pstate->m_programCounter - (size_t)b->m_code.data();
loc.actualCodeBlock = b;
}
}
+
SandBox::StackTraceData data;
data.loc = loc;
+
if (cb->isInterpretedCodeBlock() && cb->asInterpretedCodeBlock()->script()) {
- data.src = cb->asInterpretedCodeBlock()->script()->srcName();
-#ifdef ESCARGOT_DEBUGGER
- data.executionStateDepth = executionStateDepthIndex;
-#endif /* ESCARGOT_DEBUGGER */
+ data.srcName = cb->asInterpretedCodeBlock()->script()->srcName();
+ data.sourceCode = cb->asInterpretedCodeBlock()->script()->sourceCode();
} else {
StringBuilder builder;
builder.appendString("function ");
builder.appendString("() { ");
builder.appendString("[native function]");
builder.appendString(" } ");
- data.src = builder.finalize();
-#ifdef ESCARGOT_DEBUGGER
- data.executionStateDepth = executionStateDepthIndex;
-#endif /* ESCARGOT_DEBUGGER */
+ data.srcName = builder.finalize();
+ data.sourceCode = String::emptyString;
}
+
data.functionName = cb->functionName().string();
data.isEval = false;
data.isFunction = true;
+ data.callee = callee;
data.isAssociatedWithJavaScriptCode = cb->isInterpretedCodeBlock();
data.isConstructor = callee->isConstructor();
- data.sourceCode = String::emptyString;
- stackTraceData.pushBack(std::make_pair(es, data));
+#ifdef ESCARGOT_DEBUGGER
+ data.executionStateDepth = executionStateDepthIndex;
+#endif /* ESCARGOT_DEBUGGER */
+
+ stateStack.push_back(es);
+ stackTraceDataVector.pushBack(data);
}
}
void SandBox::throwException(ExecutionState& state, Value exception)
{
- m_stackTraceData.clear();
- createStackTraceData(m_stackTraceData, state);
+ m_stackTraceDataVector.clear();
+ createStackTrace(m_stackTraceDataVector, state);
// We MUST save thrown exception Value.
// because bdwgc cannot track `thrown value`(may turned off by GC_DONT_REGISTER_MAIN_STATIC_DATA)
throw exception;
}
-void SandBox::rethrowPreviouslyCaughtException(ExecutionState& state, Value exception, const StackTraceDataVector& stackTraceData)
+void SandBox::rethrowPreviouslyCaughtException(ExecutionState& state, Value exception, const StackTraceDataVector& stackTraceDataVector)
{
- m_stackTraceData = stackTraceData;
+ m_stackTraceDataVector = stackTraceDataVector;
// update stack trace data if needs
- createStackTraceData(m_stackTraceData, state);
+ createStackTrace(m_stackTraceDataVector, state);
// We MUST save thrown exception Value.
// because bdwgc cannot track `thrown value`(may turned off by GC_DONT_REGISTER_MAIN_STATIC_DATA)
ErrorObject::StackTraceData* ErrorObject::StackTraceData::create(SandBox* sandBox)
{
ErrorObject::StackTraceData* data = new ErrorObject::StackTraceData();
- data->gcValues.resizeWithUninitializedValues(sandBox->m_stackTraceData.size());
- data->nonGCValues.resizeWithUninitializedValues(sandBox->m_stackTraceData.size());
+ data->gcValues.resizeWithUninitializedValues(sandBox->m_stackTraceDataVector.size());
+ data->nonGCValues.resizeWithUninitializedValues(sandBox->m_stackTraceDataVector.size());
data->exception = sandBox->m_exception;
- for (size_t i = 0; i < sandBox->m_stackTraceData.size(); i++) {
- if ((size_t)sandBox->m_stackTraceData[i].second.loc.index == SIZE_MAX && (size_t)sandBox->m_stackTraceData[i].second.loc.actualCodeBlock != SIZE_MAX) {
- data->gcValues[i].byteCodeBlock = sandBox->m_stackTraceData[i].second.loc.actualCodeBlock;
- data->nonGCValues[i].byteCodePosition = sandBox->m_stackTraceData[i].second.loc.byteCodePosition;
+ for (size_t i = 0; i < sandBox->m_stackTraceDataVector.size(); i++) {
+ if ((size_t)sandBox->m_stackTraceDataVector[i].loc.index == SIZE_MAX && (size_t)sandBox->m_stackTraceDataVector[i].loc.actualCodeBlock != SIZE_MAX) {
+ data->gcValues[i].byteCodeBlock = sandBox->m_stackTraceDataVector[i].loc.actualCodeBlock;
+ data->nonGCValues[i].byteCodePosition = sandBox->m_stackTraceDataVector[i].loc.byteCodePosition;
} else {
- data->gcValues[i].infoString = sandBox->m_stackTraceData[i].second.src;
+ data->gcValues[i].infoString = sandBox->m_stackTraceDataVector[i].srcName;
data->nonGCValues[i].byteCodePosition = SIZE_MAX;
}
}
{
if (e.isObject() && e.asObject()->isErrorObject()) {
ErrorObject* obj = e.asObject()->asErrorObject();
+ ExecutionState state(m_context);
+
+ if (UNLIKELY(m_context->vmInstance()->isErrorCreationCallbackRegistered() && obj->hasOwnProperty(state, ObjectPropertyName(m_context->staticStrings().stack)))) {
+ // if ErrorCreationCallback is registered and this callback already inserts `stack` property for evert created ErrorObject,
+ // we just ignore adding `stack` data here
+ return;
+ }
+
ErrorObject::StackTraceData* data = ErrorObject::StackTraceData::create(this);
obj->setStackTraceData(data);
- ExecutionState state(m_context);
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(m_context->staticStrings().stack, builtinErrorObjectStackInfo, 0, NativeFunctionInfo::Strict)),
Value(Value::EmptyValue));
~SandBox();
struct StackTraceData : public gc {
- String* src;
+ String* srcName;
String* sourceCode;
ExtendedNodeLOC loc;
String* functionName;
+
#ifdef ESCARGOT_DEBUGGER
uint32_t executionStateDepth;
#endif /* ESCARGOT_DEBUGGER */
+ Optional<FunctionObject*> callee;
bool isFunction;
bool isConstructor;
bool isAssociatedWithJavaScriptCode;
bool isEval;
+
StackTraceData()
- : src(String::emptyString)
+ : srcName(String::emptyString)
, sourceCode(String::emptyString)
, loc(SIZE_MAX, SIZE_MAX, SIZE_MAX)
, functionName(String::emptyString)
#ifdef ESCARGOT_DEBUGGER
, executionStateDepth(0)
#endif /* ESCARGOT_DEBUGGER */
+ , callee(nullptr)
, isFunction(false)
, isConstructor(false)
, isAssociatedWithJavaScriptCode(false)
}
};
- typedef Vector<std::pair<ExecutionState*, StackTraceData>, GCUtil::gc_malloc_allocator<std::pair<ExecutionState*, StackTraceData>>> StackTraceDataVector;
+ typedef Vector<StackTraceData, GCUtil::gc_malloc_allocator<StackTraceData>> StackTraceDataVector;
struct SandBoxResult {
Value result;
Value error;
- Vector<StackTraceData, GCUtil::gc_malloc_allocator<StackTraceData>> stackTraceData;
+ StackTraceDataVector stackTrace;
+
SandBoxResult()
: result(Value::EmptyValue)
, error(Value::EmptyValue)
SandBoxResult run(const std::function<Value()>& scriptRunner); // for capsule script executing with try-catch
SandBoxResult run(Value (*runner)(ExecutionState&, void*), void* data);
- static bool createStackTraceData(StackTraceDataVector& stackTraceData, ExecutionState& state, bool stopAtPause = false);
+
+ static bool createStackTrace(StackTraceDataVector& stackTraceDataVector, ExecutionState& state, bool stopAtPause = false);
+
void throwException(ExecutionState& state, Value exception);
- void rethrowPreviouslyCaughtException(ExecutionState& state, Value exception, const StackTraceDataVector& stackTraceData);
+ void rethrowPreviouslyCaughtException(ExecutionState& state, Value exception, const StackTraceDataVector& stackTraceDataVector);
- StackTraceDataVector& stackTraceData()
+ StackTraceDataVector& stackTraceDataVector()
{
- return m_stackTraceData;
+ return m_stackTraceDataVector;
}
Context* context()
private:
Context* m_context;
SandBox* m_oldSandBox;
- StackTraceDataVector m_stackTraceData;
+ StackTraceDataVector m_stackTraceDataVector;
Value m_exception; // To avoid accidential GC of exception value
};
} // namespace Escargot
class ScriptArrowFunctionObject : public ScriptFunctionObject {
public:
ScriptArrowFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, EncodedValue thisValue)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator(), codeBlock->isAsync())
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator())
, m_thisValue(thisValue)
{
}
namespace Escargot {
ScriptAsyncFunctionObject::ScriptAsyncFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, EncodedValue thisValue, Object* homeObject)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, false, true)
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, false)
, m_thisValue(thisValue)
, m_homeObject(homeObject)
{
class ScriptAsyncGeneratorFunctionObject : public ScriptFunctionObject {
public:
ScriptAsyncGeneratorFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, EncodedValue thisValue = EncodedValue(EncodedValue::EmptyValue), Object* homeObject = nullptr)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, true, true)
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, true)
, m_thisValue(thisValue)
, m_homeObject(homeObject)
{
class ScriptClassMethodFunctionObject : public ScriptFunctionObject {
public:
ScriptClassMethodFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, Object* homeObject)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator(), codeBlock->isAsync())
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator())
, m_homeObject(homeObject)
{
}
#include "runtime/EnvironmentRecord.h"
#include "runtime/ErrorObject.h"
#include "runtime/VMInstance.h"
+#include "runtime/ScriptSimpleFunctionObject.h"
#include "parser/ScriptParser.h"
#include "parser/ast/AST.h"
namespace Escargot {
-ScriptFunctionObject::ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnv, bool isConstructor, bool isGenerator, bool isAsync)
+ScriptFunctionObject::ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnv, bool isConstructor, bool isGenerator)
: ScriptFunctionObject(state, proto, codeBlock, outerEnv,
((isConstructor || isGenerator) ? (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 3) : (ESCARGOT_OBJECT_BUILTIN_PROPERTY_NUMBER + 2)) + (codeBlock->isStrict() ? 2 : 0))
{
- initStructureAndValues(state, isConstructor, isGenerator, isAsync);
+ initStructureAndValues(state, isConstructor, isGenerator);
}
ScriptFunctionObject::ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, size_t defaultPropertyCount)
auto& currentCodeSizeTotal = state.context()->vmInstance()->compiledByteCodeSize();
ASSERT(currentCodeSizeTotal < std::numeric_limits<size_t>::max());
currentCodeSizeTotal += interpretedCodeBlock()->byteCodeBlock()->memoryAllocatedSize();
+
+
+ if (hasTag(g_scriptFunctionObjectTag)) {
+ auto cb = m_codeBlock->asInterpretedCodeBlock();
+ auto byteCb = cb->byteCodeBlock();
+ size_t registerSize = byteCb->m_requiredRegisterFileSizeInValueSize;
+ size_t identifierOnStackCount = cb->identifierOnStackCount();
+ size_t stackStorageSize = cb->totalStackAllocatedVariableSize();
+ size_t literalStorageSize = byteCb->m_numeralLiteralData.size();
+ size_t registerFileSize = registerSize + stackStorageSize + literalStorageSize;
+
+ bool isStrict = cb->isStrict();
+ bool shouldClearStack = byteCb->m_shouldClearStack;
+
+#define DEFINE_SCRIPTSIMPLEFUNCTION_BRANCH(opt1, opt2) \
+ if (registerFileSize <= 4) { \
+ writeTag(ScriptSimpleFunctionObject<opt1, opt2, 4>().getTag()); \
+ } else if (registerFileSize <= 8) { \
+ writeTag(ScriptSimpleFunctionObject<opt1, opt2, 8>().getTag()); \
+ } else if (registerFileSize <= 16) { \
+ writeTag(ScriptSimpleFunctionObject<opt1, opt2, 16>().getTag()); \
+ } else { \
+ writeTag(ScriptSimpleFunctionObject<opt1, opt2, 24>().getTag()); \
+ }
+
+ if (cb->canAllocateEnvironmentOnStack() && registerFileSize <= 24) {
+ if (isStrict) {
+ if (shouldClearStack) {
+ DEFINE_SCRIPTSIMPLEFUNCTION_BRANCH(true, true);
+ } else {
+ DEFINE_SCRIPTSIMPLEFUNCTION_BRANCH(true, false);
+ }
+ } else {
+ if (shouldClearStack) {
+ DEFINE_SCRIPTSIMPLEFUNCTION_BRANCH(false, true);
+ } else {
+ DEFINE_SCRIPTSIMPLEFUNCTION_BRANCH(false, false);
+ }
+ }
+ }
+ }
}
Value ScriptFunctionObject::call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv)
if (v[i].m_needToAllocateOnStack) {
stackStorage[v[i].m_indexForIndexedStorage] = newArgumentsObject;
} else {
- environmentRecordWillArgumentsObjectBeLocatedIn->heapStorage()[v[i].m_indexForIndexedStorage] = newArgumentsObject;
+ environmentRecordWillArgumentsObjectBeLocatedIn->setHeapValueByIndex(state, v[i].m_indexForIndexedStorage, newArgumentsObject);
}
break;
}
friend class Script;
friend class ByteCodeInterpreter;
friend class FunctionObjectProcessCallGenerator;
+ friend class Global;
public:
enum ConstructorKind {
Global,
};
- ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, bool isConstructor, bool isGenerator, bool isAsync);
+ ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, bool isConstructor, bool isGenerator);
virtual bool isGenerator() const override
{
}
protected:
+ ScriptFunctionObject()
+ : FunctionObject()
+ , m_outerEnvironment(nullptr)
+ {
+ // dummy default constructor
+ // only called by Global::initialize to set tag value
+ }
+
ScriptFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, size_t defaultPropertyCount);
// https://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
public:
// both thisValue, homeObject are optional
ScriptGeneratorFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment, EncodedValue thisValue = EncodedValue(EncodedValue::EmptyValue), Object* homeObject = nullptr)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, true, false)
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, true)
, m_thisValue(thisValue)
, m_homeObject(homeObject)
{
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics Co., Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef __EscargotScriptSimpleFunctionObject__
+#define __EscargotScriptSimpleFunctionObject__
+
+#include "runtime/ScriptFunctionObject.h"
+#include "runtime/Environment.h"
+#include "runtime/EnvironmentRecord.h"
+#include "runtime/ErrorObject.h"
+#include "runtime/VMInstance.h"
+#include "interpreter/ByteCode.h"
+#include "interpreter/ByteCodeGenerator.h"
+#include "interpreter/ByteCodeInterpreter.h"
+
+namespace Escargot {
+
+template <bool isStrict = false, bool shouldClearStack = false, unsigned registerFileSize = 12>
+class ScriptSimpleFunctionObject : public ScriptFunctionObject {
+ friend class ScriptFunctionObject;
+
+protected:
+ ScriptSimpleFunctionObject() // ctor for reading tag
+ : ScriptFunctionObject()
+ {
+ }
+
+ virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv) override
+ {
+#ifdef STACK_GROWS_DOWN
+ if (UNLIKELY(state.stackLimit() > (size_t)currentStackPointer())) {
+#else
+ if (UNLIKELY(state.stackLimit() < (size_t)currentStackPointer())) {
+#endif
+ ErrorObject::throwBuiltinError(state, ErrorObject::RangeError, "Maximum call stack size exceeded");
+ }
+
+ ASSERT(codeBlock()->isInterpretedCodeBlock());
+ InterpretedCodeBlock* codeBlock = interpretedCodeBlock();
+
+ // prepare ByteCodeBlock if needed
+ if (UNLIKELY(codeBlock->byteCodeBlock() == nullptr)) {
+ generateByteCodeBlock(state);
+ }
+
+ ByteCodeBlock* blk = codeBlock->byteCodeBlock();
+ Context* ctx = codeBlock->context();
+ const size_t stackStorageSize = codeBlock->totalStackAllocatedVariableSize();
+ const size_t identifierOnStackCount = codeBlock->identifierOnStackCount();
+ const size_t registerSize = blk->m_requiredRegisterFileSizeInValueSize;
+ const size_t literalStorageSize = blk->m_numeralLiteralData.size();
+ Value* literalStorageSrc = blk->m_numeralLiteralData.data();
+
+#if !defined(NDEBUG)
+ ASSERT(codeBlock->isStrict() == isStrict);
+ ASSERT(blk->m_requiredRegisterFileSizeInValueSize + stackStorageSize + literalStorageSize <= registerFileSize);
+#endif
+
+ // prepare env, ec
+ ASSERT(codeBlock->canAllocateEnvironmentOnStack());
+ FunctionEnvironmentRecordOnStack<false, false> record(this);
+ LexicalEnvironment lexEnv(&record, outerEnvironment()
+#ifndef NDEBUG
+ ,
+ false
+#endif
+ );
+
+ char* registerFileBuffer[sizeof(Value) * registerFileSize];
+ Value* registerFile = reinterpret_cast<Value*>(registerFileBuffer);
+ Value* stackStorage = registerFile + registerSize;
+
+ {
+ Value* literalStorage = stackStorage + stackStorageSize;
+ for (size_t i = 0; i < literalStorageSize; i++) {
+ literalStorage[i] = literalStorageSrc[i];
+ }
+ }
+
+ // binding function name
+ stackStorage[1] = this;
+
+ // initialize identifiers by undefined value
+ for (size_t i = 2; i < identifierOnStackCount; i++) {
+ stackStorage[i] = Value();
+ }
+
+ ExecutionState newState(ctx, &state, &lexEnv, argc, argv, isStrict);
+ if (isStrict) {
+ stackStorage[0] = thisValue;
+ } else {
+ if (thisValue.isUndefinedOrNull()) {
+ stackStorage[0] = newState.context()->globalObjectProxy();
+ } else {
+ stackStorage[0] = thisValue.toObject(newState);
+ }
+ }
+
+ if (shouldClearStack) {
+ const Value returnValue = ByteCodeInterpreter::interpret(&newState, blk, 0, registerFile);
+ clearStack<512>();
+ return returnValue;
+ } else {
+ return ByteCodeInterpreter::interpret(&newState, blk, 0, registerFile);
+ }
+ }
+};
+
+COMPILE_ASSERT(sizeof(ScriptSimpleFunctionObject<>) == sizeof(ScriptFunctionObject), "");
+
+} // namespace Escargot
+
+#endif
public:
ScriptVirtualArrowFunctionObject(ExecutionState& state, Object* proto, InterpretedCodeBlock* codeBlock, LexicalEnvironment* outerEnvironment)
- : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator(), codeBlock->isAsync())
+ : ScriptFunctionObject(state, proto, codeBlock, outerEnvironment, false, codeBlock->isGenerator())
{
m_prototype = nullptr;
}
#include "runtime/Global.h"
#include "runtime/Platform.h"
#include "runtime/SharedArrayBufferObject.h"
+#include "runtime/TypedArrayInlines.h"
namespace Escargot {
return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
}
+void SharedArrayBufferObject::fillData(const uint8_t* newData, size_t length)
+{
+#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, nullptr);
+ uint8_t* rawTarget = data();
+ for (size_t i = 0; i < length; i++) {
+ __atomic_load(newData + i, rawBytes, __ATOMIC_SEQ_CST);
+ __atomic_store(rawTarget + i, rawBytes, __ATOMIC_SEQ_CST);
+ }
+#else
+ std::lock_guard<SpinLock> guard(Global::atomicsLock());
+ memcpy(data(), newData, length);
+#endif
+}
+
+Value SharedArrayBufferObject::getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian)
+{
+ ASSERT(byteLength());
+ size_t elemSize = TypedArrayHelper::elementSize(type);
+ ASSERT(byteindex + elemSize <= byteLength());
+
+ uint8_t* rawStart = data() + byteindex;
+#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
+
+ switch (type) {
+ case TypedArrayType::Int8:
+ __atomic_load(reinterpret_cast<int8_t*>(rawStart), reinterpret_cast<int8_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Int16:
+ __atomic_load(reinterpret_cast<int16_t*>(rawStart), reinterpret_cast<int16_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Int32:
+ __atomic_load(reinterpret_cast<int32_t*>(rawStart), reinterpret_cast<int32_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint8:
+ __atomic_load(reinterpret_cast<uint8_t*>(rawStart), reinterpret_cast<uint8_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint16:
+ __atomic_load(reinterpret_cast<uint16_t*>(rawStart), reinterpret_cast<uint16_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint32:
+ __atomic_load(reinterpret_cast<uint32_t*>(rawStart), reinterpret_cast<uint32_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint8Clamped:
+ __atomic_load(reinterpret_cast<uint8_t*>(rawStart), reinterpret_cast<uint8_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Float32:
+ __atomic_load(reinterpret_cast<float*>(rawStart), reinterpret_cast<float*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Float64:
+ __atomic_load(reinterpret_cast<double*>(rawStart), reinterpret_cast<double*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::BigInt64:
+ __atomic_load(reinterpret_cast<int64_t*>(rawStart), reinterpret_cast<int64_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ default:
+ ASSERT(TypedArrayType::BigUint64 == type);
+ __atomic_load(reinterpret_cast<uint64_t*>(rawStart), reinterpret_cast<uint64_t*>(rawBytes), __ATOMIC_SEQ_CST);
+ break;
+ }
+
+ if (LIKELY(isLittleEndian)) {
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes);
+ } else {
+ uint8_t* rawBytes2 = ALLOCA(8, uint8_t, state);
+ for (size_t i = 0; i < elemSize; i++) {
+ rawBytes2[elemSize - i - 1] = rawBytes[i];
+ }
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes2);
+ }
+#else
+ std::lock_guard<SpinLock> guard(Global::atomicsLock());
+ if (LIKELY(isLittleEndian)) {
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawStart);
+ } else {
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
+ for (size_t i = 0; i < elemSize; i++) {
+ rawBytes[elemSize - i - 1] = rawStart[i];
+ }
+ return TypedArrayHelper::rawBytesToNumber(state, type, rawBytes);
+ }
+#endif
+}
+
+void SharedArrayBufferObject::setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian)
+{
+ ASSERT(byteLength());
+ size_t elemSize = TypedArrayHelper::elementSize(type);
+ ASSERT(byteindex + elemSize <= byteLength());
+
+ uint8_t* rawStart = data() + byteindex;
+ uint8_t* rawBytes = ALLOCA(8, uint8_t, state);
+ TypedArrayHelper::numberToRawBytes(state, type, val, rawBytes);
+#if defined(HAVE_BUILTIN_ATOMIC_FUNCTIONS)
+ uint8_t* rawBytes2 = ALLOCA(8, uint8_t, state);
+ if (LIKELY(isLittleEndian)) {
+ rawBytes2 = rawBytes;
+ } else {
+ for (size_t i = 0; i < elemSize; i++) {
+ rawBytes2[i] = rawBytes[elemSize - i - 1];
+ }
+ }
+
+ switch (type) {
+ case TypedArrayType::Int8:
+ __atomic_store(reinterpret_cast<int8_t*>(rawStart), reinterpret_cast<int8_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Int16:
+ __atomic_store(reinterpret_cast<int16_t*>(rawStart), reinterpret_cast<int16_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Int32:
+ __atomic_store(reinterpret_cast<int32_t*>(rawStart), reinterpret_cast<int32_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint8:
+ __atomic_store(reinterpret_cast<uint8_t*>(rawStart), reinterpret_cast<uint8_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint16:
+ __atomic_store(reinterpret_cast<uint16_t*>(rawStart), reinterpret_cast<uint16_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint32:
+ __atomic_store(reinterpret_cast<uint32_t*>(rawStart), reinterpret_cast<uint32_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Uint8Clamped:
+ __atomic_store(reinterpret_cast<uint8_t*>(rawStart), reinterpret_cast<uint8_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Float32:
+ __atomic_store(reinterpret_cast<float*>(rawStart), reinterpret_cast<float*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::Float64:
+ __atomic_store(reinterpret_cast<double*>(rawStart), reinterpret_cast<double*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ case TypedArrayType::BigInt64:
+ __atomic_store(reinterpret_cast<int64_t*>(rawStart), reinterpret_cast<int64_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ default:
+ ASSERT(TypedArrayType::BigUint64 == type);
+ __atomic_store(reinterpret_cast<uint64_t*>(rawStart), reinterpret_cast<uint64_t*>(rawBytes2), __ATOMIC_SEQ_CST);
+ break;
+ }
+#else
+ std::lock_guard<SpinLock> guard(Global::atomicsLock());
+ if (LIKELY(isLittleEndian)) {
+ memcpy(rawStart, rawBytes, elemSize);
+ } else {
+ for (size_t i = 0; i < elemSize; i++) {
+ rawStart[i] = rawBytes[elemSize - i - 1];
+ }
+ }
+#endif
+}
} // namespace Escargot
#endif
return true;
}
+ // thread-safe functions
+ virtual void fillData(const uint8_t* newData, size_t length) override;
+ virtual Value getValueFromBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, bool isLittleEndian = true) override;
+ virtual void setValueInBuffer(ExecutionState& state, size_t byteindex, TypedArrayType type, const Value& val, bool isLittleEndian = true) override;
+
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
};
F(global) \
F(globalThis) \
F(groups) \
+ F(grow) \
F(growable) \
F(has) \
F(hasInstance) \
F(exports) \
F(f32) \
F(f64) \
- F(grow) \
F(i32) \
F(i64) \
F(imports) \
free(numbers);
}
+ Escargot::String* charCodeToString(char32_t ch)
+ {
+ if (LIKELY(ch < ESCARGOT_ASCII_TABLE_MAX)) {
+ return asciiTable[ch].string();
+ }
+ return String::fromCharCode(ch);
+ }
+
// keyword string
AtomicString stringBreak;
AtomicString stringCase;
kMaxExponentLength - first_char_pos);
}
-ASCIIStringData dtoa(double number)
+ASCIIStringDataNonGCStd dtoa(double number)
{
if (number == 0) {
- return ASCIIStringData("0", 1);
+ return "0";
}
const int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN;
bool sign = false;
str += *buf;
buf++;
}
- return ASCIIStringData(str.data(), str.length());
+ return str;
+}
+
+void String::initEmptyString()
+{
+ ASSERT(!String::emptyString);
+ String* emptyStr = new (NoGC) ASCIIString("");
+ // mark empty string as AtomicString source
+ // because empty string is the default string value of empty AtomicString
+ emptyStr->m_tag = (size_t)POINTER_VALUE_STRING_TAG_IN_DATA | (size_t)emptyStr;
+
+ ASSERT(emptyStr->isAtomicStringSource());
+ String::emptyString = emptyStr;
}
+#define LATIN1_LARGE_INLINE_BUFFER(F) \
+ F(1) \
+ F(2) \
+ F(3) \
+ F(4) \
+ F(5) \
+ F(6) \
+ F(7) \
+ F(8) \
+ F(9) \
+ F(10) \
+ F(11) \
+ F(12) \
+ F(13) \
+ F(14) \
+ F(15) \
+ F(16) \
+ F(17) \
+ F(18) \
+ F(19) \
+ F(20) \
+ F(21) \
+ F(22) \
+ F(23) \
+ F(24)
+
+#define LATIN1_LARGE_INLINE_BUFFER_DEFINE(N) \
+ template class Latin1StringWithLargeInlineBuffer<N>;
+
+LATIN1_LARGE_INLINE_BUFFER(LATIN1_LARGE_INLINE_BUFFER_DEFINE)
+
String* String::fromASCII(const char* src, size_t len)
{
- return new ASCIIString(src, len);
+ if (len <= String::StringBufferData::bufferPointerAsArraySize) {
+ return new ASCIIStringWithInlineBuffer(src, len);
+ } else {
+ switch (len) {
+#define LATIN1_LARGE_INLINE_BUFFER_SWITCH_ASCII(N) \
+ case N: \
+ return new Latin1StringWithLargeInlineBuffer<N>(reinterpret_cast<const LChar*>(src), len);
+ LATIN1_LARGE_INLINE_BUFFER(LATIN1_LARGE_INLINE_BUFFER_SWITCH_ASCII)
+ default:
+ return new ASCIIString(src, len);
+ }
+ }
+}
+
+String* String::fromLatin1(const LChar* src, size_t len)
+{
+ if (len <= String::StringBufferData::bufferPointerAsArraySize) {
+ return new Latin1StringWithInlineBuffer(src, len);
+ } else {
+ switch (len) {
+#define LATIN1_LARGE_INLINE_BUFFER_SWITCH(N) \
+ case N: \
+ return new Latin1StringWithLargeInlineBuffer<N>(src, len);
+ LATIN1_LARGE_INLINE_BUFFER(LATIN1_LARGE_INLINE_BUFFER_SWITCH)
+ default:
+ return new Latin1String(src, len);
+ }
+ }
+}
+
+String* String::fromLatin1(const char16_t* src, size_t len)
+{
+ if (len <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
+ LChar* dest = static_cast<LChar*>(alloca(len));
+ for (size_t i = 0; i < len; i++) {
+ ASSERT(src[i] < 256);
+ dest[i] = src[i];
+ }
+ return String::fromLatin1(dest, len);
+ } else {
+ return new Latin1String(src, len);
+ }
}
String* String::fromDouble(double v)
{
auto s = dtoa(v);
- return new ASCIIString(std::move(s));
+ return String::fromASCII(s.data(), s.length());
}
String* String::fromUTF8(const char* src, size_t len, bool maybeASCII)
{
if (maybeASCII && isAllASCII(src, len)) {
- return new ASCIIString(src, len);
+ return String::fromASCII(src, len);
} else {
auto s = utf8StringToUTF16String(src, len);
return new UTF16String(std::move(s));
}
#endif
+String* String::fromCharCode(char32_t code)
+{
+ if (code < 128) {
+ char c = (char)code;
+ return new ASCIIStringWithInlineBuffer(&c, 1);
+ } else if (code < 0x10000) {
+ char16_t buf = code;
+ return new UTF16StringWithInlineBuffer(&buf, 1);
+ } else {
+ char16_t buf[2];
+ buf[0] = (char16_t)(0xD800 + ((code - 0x10000) >> 10));
+ buf[1] = (char16_t)(0xDC00 + ((code - 0x10000) & 1023));
+ return new UTF16StringWithInlineBuffer(buf, 2);
+ }
+}
+
int String::stringCompare(size_t l1, size_t l2, const String* c1, const String* c2)
{
size_t s = 0;
return tryToUseAsIndex32();
}
-size_t String::find(String* str, size_t pos)
+size_t String::find(String* str, size_t pos) const
{
const size_t srcStrLen = str->length();
const size_t size = length();
return SIZE_MAX;
}
+size_t String::find(const char* str, size_t srcStrLen, size_t pos) const
+{
+ const size_t size = length();
+
+ if (srcStrLen == 0)
+ return pos <= size ? pos : SIZE_MAX;
+
+ if (srcStrLen <= size) {
+ char32_t src0 = str[0];
+ const auto& data = bufferAccessData();
+ if (data.has8BitContent) {
+ for (; pos <= size - srcStrLen; ++pos) {
+ if (((const LChar*)data.buffer)[pos] == src0) {
+ bool same = true;
+ for (size_t k = 1; k < srcStrLen; k++) {
+ if (((const LChar*)data.buffer)[pos + k] != str[k]) {
+ same = false;
+ break;
+ }
+ }
+ if (same)
+ return pos;
+ }
+ }
+ } else {
+ for (; pos <= size - srcStrLen; ++pos) {
+ if (((const char16_t*)data.buffer)[pos] == src0) {
+ bool same = true;
+ for (size_t k = 1; k < srcStrLen; k++) {
+ if (((const char16_t*)data.buffer)[pos + k] != str[k]) {
+ same = false;
+ break;
+ }
+ }
+ if (same)
+ return pos;
+ }
+ }
+ }
+ }
+ return SIZE_MAX;
+}
+
size_t String::rfind(String* str, size_t pos)
{
const size_t srcStrLen = str->length();
String* String::trim(String::StringTrimWhere where)
{
- const auto& bad = bufferAccessData();
- int64_t stringLength = (int64_t)bad.length;
+ int64_t stringLength = (int64_t)length();
int64_t s = 0;
int64_t e = stringLength - 1;
if (where == TrimStart || where == TrimBoth) {
for (s = 0; s < stringLength; s++) {
- if (!EscargotLexer::isWhiteSpaceOrLineTerminator(bad.charAt(s)))
+ if (!EscargotLexer::isWhiteSpaceOrLineTerminator(charAt(s)))
break;
}
}
if (where == TrimEnd || where == TrimBoth) {
for (e = stringLength - 1; e >= s; e--) {
- if (!EscargotLexer::isWhiteSpaceOrLineTerminator(bad.charAt(e)))
+ if (!EscargotLexer::isWhiteSpaceOrLineTerminator(charAt(e)))
break;
}
}
}
-void* ASCIIString::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(ASCIIString)] = { 0 };
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ASCIIString, m_bufferData.buffer));
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(ASCIIString));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-
-void* Latin1String::operator new(size_t size)
-{
- static MAY_THREAD_LOCAL bool typeInited = false;
- static MAY_THREAD_LOCAL GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(Latin1String)] = { 0 };
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(Latin1String, m_bufferData.buffer));
- descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(Latin1String));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-
void* UTF16String::operator new(size_t size)
{
static MAY_THREAD_LOCAL bool typeInited = false;
UTF16StringData utf8StringToUTF16String(const char* buf, const size_t len);
UTF8StringData utf16StringToUTF8String(const char16_t* buf, const size_t len);
ASCIIStringData utf16StringToASCIIString(const char16_t* buf, const size_t len);
-ASCIIStringData dtoa(double number);
+ASCIIStringDataNonGCStd dtoa(double number);
size_t utf32ToUtf8(char32_t uc, char* UTF8);
size_t utf32ToUtf16(char32_t i, char16_t* u);
};
void* extraData;
- StringBufferAccessData(bool has8Bit, size_t len, void* buffer, void* extraDataKeepInStack = nullptr)
+ StringBufferAccessData(bool has8Bit, size_t len, void* buffer, void* extraDataToKeep = nullptr)
: has8BitContent(has8Bit)
, length(len)
, buffer(buffer)
- , extraData(extraDataKeepInStack)
+ , extraData(extraDataToKeep)
{
+#if defined(ENABLE_COMPRESSIBLE_STRING) || defined(ENABLE_RELOADABLE_STRING)
+ if (extraData) {
+ // increase refCount in CompressibleString or ReloadableString
+ (*reinterpret_cast<size_t*>(extraData))++;
+ }
+#endif
+ }
+
+ ~StringBufferAccessData()
+ {
+#if defined(ENABLE_COMPRESSIBLE_STRING) || defined(ENABLE_RELOADABLE_STRING)
+ if (extraData) {
+ // decrease refCount in CompressibleString or ReloadableString
+ size_t& count = *reinterpret_cast<size_t*>(extraData);
+ ASSERT(count > 0);
+ count--;
+ }
+#endif
}
char16_t uncheckedCharAtFor8Bit(size_t idx) const
{
}
- bool has8BitContent : 1;
- bool hasSpecialImpl : 1;
+ union {
+ struct {
+ bool has8BitContent : 1;
+ bool hasSpecialImpl : 1;
#if defined(ESCARGOT_32)
- size_t length : 30;
+ size_t length : 30;
#else
- size_t length : 62;
+ size_t length : 62;
#endif
+ };
+ size_t valueShouldBeOddForFewTypes;
+ };
+
+ static constexpr size_t bufferPointerAsArraySize = sizeof(size_t);
union {
const void* buffer;
const char* bufferAs8Bit;
const char16_t* bufferAs16Bit;
String* bufferAsString;
+ LChar bufferPointerAsArray[bufferPointerAsArraySize];
+ char16_t bufferPointerAs16BitArray[bufferPointerAsArraySize / 2];
};
COMPILE_ASSERT(STRING_MAXIMUM_LENGTH < (std::numeric_limits<size_t>::max() >> 2), "");
return false;
}
+ // initialize String::emptyString value
+ // its called only once by VMInstance constructor
+ static void initEmptyString();
+
template <const size_t srcLen>
static String* fromASCII(const char (&src)[srcLen])
{
}
static String* fromASCII(const char* s, size_t len);
+
+ // if you want to change this value, you should change LATIN1_LARGE_INLINE_BUFFER macro in String.cpp
+#define LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE 24
+ static String* fromLatin1(const LChar* s, size_t len);
+ static String* fromLatin1(const char16_t* s, size_t len);
+
static String* fromCharCode(char32_t code);
static String* fromDouble(double v);
static String* fromInt32(int32_t v)
return true;
}
- size_t find(String* str, size_t pos = 0);
+ size_t find(String* str, size_t pos = 0) const;
+ size_t find(const char* str, size_t len, size_t pos = 0) const;
template <size_t N>
- size_t find(const char (&str)[N], size_t pos = 0) const
- {
- const size_t srcStrLen = N - 1;
- const size_t size = length();
-
- if (srcStrLen == 0)
- return pos <= size ? pos : SIZE_MAX;
-
- if (srcStrLen <= size) {
- char32_t src0 = str[0];
- const auto& data = bufferAccessData();
- if (data.has8BitContent) {
- for (; pos <= size - srcStrLen; ++pos) {
- if (((const LChar*)data.buffer)[pos] == src0) {
- bool same = true;
- for (size_t k = 1; k < srcStrLen; k++) {
- if (((const LChar*)data.buffer)[pos + k] != str[k]) {
- same = false;
- break;
- }
- }
- if (same)
- return pos;
- }
- }
- } else {
- for (; pos <= size - srcStrLen; ++pos) {
- if (((const char16_t*)data.buffer)[pos] == src0) {
- bool same = true;
- for (size_t k = 1; k < srcStrLen; k++) {
- if (((const char16_t*)data.buffer)[pos + k] != str[k]) {
- same = false;
- break;
- }
- }
- if (same)
- return pos;
- }
- }
- }
- }
- return SIZE_MAX;
+ size_t find(const char (&src)[N], size_t pos = 0) const
+ {
+ ASSERT(N - 1 == strlen(src));
+ return find(src, N - 1, 0);
}
template <size_t N>
return find(str) != SIZE_MAX;
}
+ bool contains(String* str) const
+ {
+ return find(str) != SIZE_MAX;
+ }
+
size_t rfind(String* str, size_t pos);
String* substring(size_t from, size_t to);
friend int stringCompare(const String& a, const String& b);
// NOTE these function generates new copy of string data
- virtual UTF16StringData toUTF16StringData() const = 0;
- virtual UTF8StringData toUTF8StringData() const = 0;
- virtual UTF8StringDataNonGCStd toNonGCUTF8StringData(int options = StringWriteOption::NoOptions) const = 0;
+ virtual UTF16StringData toUTF16StringData() const
+ {
+ UTF16StringData ret;
+ size_t len = length();
+ ret.resizeWithUninitializedValues(len);
+
+ auto bad = bufferAccessData();
+ for (size_t i = 0; i < len; i++) {
+ ret[i] = bad.charAt(i);
+ }
+
+ return ret;
+ }
+
+ virtual UTF8StringData toUTF8StringData() const
+ {
+ return bufferAccessData().toUTF8String<UTF8StringData, UTF8StringDataNonGCStd>();
+ }
+
+ virtual UTF8StringDataNonGCStd toNonGCUTF8StringData(int options = StringWriteOption::NoOptions) const
+ {
+ return bufferAccessData().toUTF8String<UTF8StringDataNonGCStd>();
+ }
+
static MAY_THREAD_LOCAL String* emptyString;
uint64_t tryToUseAsIndex() const;
}
};
+#if defined(NDEBUG) && defined(ESCARGOT_32) && !defined(COMPILER_MSVC)
+COMPILE_ASSERT(sizeof(String) == sizeof(size_t) * 4, "");
+#endif
+
inline bool operator<(const String& a, const String& b)
{
return String::stringCompare(a.length(), b.length(), &a, &b) < 0;
m_bufferData.has8BitContent = true;
m_bufferData.length = stringData.length();
m_bufferData.buffer = stringData.takeBuffer();
+
+ ASSERT(m_bufferData.valueShouldBeOddForFewTypes & 1);
}
virtual UTF16StringData toUTF16StringData() const override;
virtual UTF8StringData toUTF8StringData() const override;
virtual UTF8StringDataNonGCStd toNonGCUTF8StringData(int options = StringWriteOption::NoOptions) const override;
- void* operator new(size_t size);
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC(size);
+ }
void* operator new(size_t size, GCPlacement p)
{
return gc::operator new(size, p);
}
};
+class ASCIIStringWithInlineBuffer : public String {
+public:
+ ASCIIStringWithInlineBuffer(const char* str, size_t len)
+ : String()
+ {
+ ASSERT(len <= m_bufferData.bufferPointerAsArraySize);
+ memcpy(m_bufferData.bufferPointerAsArray, str, len);
+ m_bufferData.length = len;
+ m_bufferData.hasSpecialImpl = true;
+ m_bufferData.has8BitContent = true;
+ }
+
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC_ATOMIC(size);
+ }
+
+ virtual StringBufferAccessData bufferAccessDataSpecialImpl() override
+ {
+ return StringBufferAccessData(true, m_bufferData.length, &m_bufferData.bufferPointerAsArray);
+ }
+
+ virtual const LChar* characters8() const override
+ {
+ return (LChar*)&m_bufferData.bufferPointerAsArray;
+ }
+};
+
class Latin1String : public String {
public:
explicit Latin1String()
m_bufferData.has8BitContent = true;
m_bufferData.length = stringData.length();
m_bufferData.buffer = stringData.takeBuffer();
+
+ ASSERT(m_bufferData.valueShouldBeOddForFewTypes & 1);
}
virtual char16_t charAt(const size_t idx) const override
virtual UTF8StringData toUTF8StringData() const override;
virtual UTF8StringDataNonGCStd toNonGCUTF8StringData(int options = StringWriteOption::NoOptions) const override;
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC(size);
+ }
};
class Latin1StringFromExternalMemory : public Latin1String {
}
};
+class Latin1StringWithInlineBuffer : public String {
+public:
+ Latin1StringWithInlineBuffer(const LChar* str, size_t len)
+ : String()
+ {
+ ASSERT(len <= m_bufferData.bufferPointerAsArraySize);
+ memcpy(m_bufferData.bufferPointerAsArray, str, len);
+ m_bufferData.length = len;
+ m_bufferData.hasSpecialImpl = true;
+ m_bufferData.has8BitContent = true;
+ }
+
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC_ATOMIC(size);
+ }
+
+ virtual StringBufferAccessData bufferAccessDataSpecialImpl() override
+ {
+ return StringBufferAccessData(true, m_bufferData.length, &m_bufferData.bufferPointerAsArray);
+ }
+
+ virtual const LChar* characters8() const override
+ {
+ return (LChar*)&m_bufferData.bufferPointerAsArray;
+ }
+};
+
+template <const int bufferSize>
+class Latin1StringWithLargeInlineBuffer : public String {
+public:
+ Latin1StringWithLargeInlineBuffer(const LChar* str, size_t len)
+ : String()
+ {
+ ASSERT(len <= bufferSize);
+ m_bufferData.buffer = m_buffer;
+ m_bufferData.length = len;
+ m_bufferData.hasSpecialImpl = false;
+ m_bufferData.has8BitContent = true;
+ memcpy(m_buffer, str, len);
+ }
+
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC_ATOMIC(size);
+ }
+
+ virtual const LChar* characters8() const override
+ {
+ return m_buffer;
+ }
+
+private:
+ LChar m_buffer[bufferSize];
+};
+
class UTF16String : public String {
public:
explicit UTF16String()
}
};
+class UTF16StringWithInlineBuffer : public String {
+public:
+ UTF16StringWithInlineBuffer(const char16_t* str, size_t len)
+ : String()
+ {
+ ASSERT(len <= m_bufferData.bufferPointerAsArraySize / 2);
+ memcpy(m_bufferData.bufferPointerAs16BitArray, str, len * 2);
+ m_bufferData.length = len;
+ m_bufferData.hasSpecialImpl = true;
+ m_bufferData.has8BitContent = false;
+ }
-inline String* String::fromCharCode(char32_t code)
-{
- if (code < 128) {
- char c = (char)code;
- ASCIIStringData s(&c, 1);
- return new ASCIIString(std::move(s));
- } else if (code <= 0x10000) {
- char16_t c = (char16_t)code;
- UTF16StringData s(&c, 1);
- return new UTF16String(std::move(s));
- } else {
- char16_t buf[3];
- buf[0] = (char16_t)(0xD800 + ((code - 0x10000) >> 10));
- buf[1] = (char16_t)(0xDC00 + ((code - 0x10000) & 1023));
- buf[2] = 0;
- return new UTF16String(buf, 2);
+ void* operator new(size_t size)
+ {
+ return GC_MALLOC_ATOMIC(size);
}
-}
+
+ virtual StringBufferAccessData bufferAccessDataSpecialImpl() override
+ {
+ return StringBufferAccessData(false, m_bufferData.length, &m_bufferData.bufferPointerAs16BitArray);
+ }
+
+ virtual UTF16StringData toUTF16StringData() const override
+ {
+ UTF16StringData ret;
+ size_t len = length();
+ ret.resizeWithUninitializedValues(len);
+
+ auto bad = bufferAccessData();
+ for (size_t i = 0; i < len; i++) {
+ ret[i] = bad.charAt(i);
+ }
+
+ return ret;
+ }
+
+ virtual UTF8StringData toUTF8StringData() const override
+ {
+ return bufferAccessData().toUTF8String<UTF8StringData, UTF8StringDataNonGCStd>();
+ }
+
+ virtual UTF8StringDataNonGCStd toNonGCUTF8StringData(int options = StringWriteOption::NoOptions) const override
+ {
+ return bufferAccessData().toUTF8String<UTF8StringDataNonGCStd>();
+ }
+
+ virtual const char16_t* characters16() const override
+ {
+ return (char16_t*)&m_bufferData.bufferPointerAs16BitArray;
+ }
+};
+
} // namespace Escargot
namespace std {
}
if (m_has8BitContent) {
- Latin1StringData ret;
- ret.resizeWithUninitializedValues(m_contentLength);
+ Latin1StringData retString;
+ LChar* retArray;
+ LChar* ret;
+ if (m_contentLength <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
+ retArray = static_cast<LChar*>(alloca(m_contentLength));
+ ret = retArray;
+ } else {
+ retString.resizeWithUninitializedValues(m_contentLength);
+ ret = retString.data();
+ }
size_t currentLength = 0;
for (size_t i = 0; i < m_piecesInlineStorageUsage; i++) {
}
clear();
- return new Latin1String(std::move(ret));
+ if (currentLength <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
+ return String::fromLatin1(retArray, currentLength);
+ } else {
+ return new Latin1String(std::move(retString));
+ }
} else {
UTF16StringData ret;
ret.resizeWithUninitializedValues(m_contentLength);
if (idx != Value::InvalidIndexPropertyValue) {
size_t strLen = m_primitiveValue->length();
if (LIKELY(idx < strLen)) {
- return ObjectHasPropertyResult(ObjectGetResult(Value(String::fromCharCode(m_primitiveValue->charAt(idx))), false, true, false));
+ return ObjectHasPropertyResult(ObjectGetResult(state.context()->staticStrings().charCodeToString(m_primitiveValue->charAt(idx)), false, true, false));
}
}
return Object::hasProperty(state, P);
if (idx != Value::InvalidIndexPropertyValue) {
size_t strLen = m_primitiveValue->length();
if (LIKELY(idx < strLen)) {
- return ObjectGetResult(Value(String::fromCharCode(m_primitiveValue->charAt(idx))), false, true, false);
+ return ObjectGetResult(state.context()->staticStrings().charCodeToString(m_primitiveValue->charAt(idx)), false, true, false);
}
}
return Object::getOwnProperty(state, P);
if (idx != Value::InvalidIndexPropertyValue) {
size_t strLen = m_primitiveValue->length();
if (LIKELY(idx < strLen)) {
- return ObjectGetResult(Value(String::fromCharCode(m_primitiveValue->charAt(idx))), false, true, false);
+ return ObjectGetResult(state.context()->staticStrings().charCodeToString(m_primitiveValue->charAt(idx)), false, true, false);
}
}
return get(state, ObjectPropertyName(state, property), receiver);
if (idx != Value::InvalidIndexPropertyValue) {
size_t strLen = m_primitiveValue->length();
if (LIKELY(idx < strLen)) {
- return ObjectHasPropertyResult(ObjectGetResult(Value(String::fromCharCode(m_primitiveValue->charAt(idx))), false, true, false));
+ return ObjectHasPropertyResult(ObjectGetResult(state.context()->staticStrings().charCodeToString(m_primitiveValue->charAt(idx)), false, true, false));
}
}
return hasProperty(state, ObjectPropertyName(state, propertyName));
// If first < 0xD800 or first > 0xDBFF or position+1 = len, let resultString be the string consisting of the single code unit first.
String* resultString;
if (first < 0xD800 || first > 0xDBFF || (position + 1 == len)) {
- resultString = String::fromCharCode(first);
+ resultString = state.context()->staticStrings().charCodeToString(first);
} else {
// Let second be the code unit value at index position+1 in the String S.
auto second = s->charAt(position + 1);
// If second < 0xDC00 or second > 0xDFFF, let resultString be the string consisting of the single code unit first.
if (second < 0xDC00 || second > 0xDFFF) {
- resultString = String::fromCharCode(first);
+ resultString = state.context()->staticStrings().charCodeToString(first);
} else {
// Else, let resultString be the string consisting of the code unit first followed by the code unit second.
char16_t s[2] = { first, second };
return true;
}
+ virtual bool hasOwnEnumeration() const override
+ {
+ return true;
+ }
+
void setPrimitiveValue(ExecutionState& state, String* data)
{
m_primitiveValue = data;
ASSERT(m_bufferData.hasSpecialImpl);
StringBufferAccessData r = m_bufferData.bufferAsString->bufferAccessData();
- // keep original buffer pointer in stack
- // without this, compressible string can free this pointer
- r.extraData = const_cast<void*>(r.buffer);
r.length = m_bufferData.length;
if (r.has8BitContent) {
r.bufferAs8Bit += m_start;
}
bool hasIndexStringAsPropertyName = false;
+ bool hasSymbol = false;
bool hasNonAtomicPropertyName = false;
+ bool hasEnumerableProperty = false;
bool isInlineNonCacheable = false;
for (size_t i = baseItemCount; i < propertyCount; i++) {
auto propertyIndex = i - baseItemCount;
} else {
ASSERT(propertyNameValue.isSymbol());
propertyName = ObjectStructurePropertyName(propertyNameValue.asSymbol());
+ hasSymbol = true;
}
if (!hasIndexStringAsPropertyName) {
ASSERT(type == Template::TemplatePropertyData::PropertyType::PropertyAccessorData);
desc = ObjectStructurePropertyDescriptor::createAccessorDescriptor(m_properties[propertyIndex].second.presentAttributes());
}
+
+ hasEnumerableProperty |= desc.isEnumerable();
+
structureItemVector[i] = ObjectStructureItem(propertyName, desc);
}
ObjectStructure* newObjectStructure;
if (propertyCount > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) {
- newObjectStructure = new ObjectStructureWithMap(hasIndexStringAsPropertyName, std::move(structureItemVector));
+ newObjectStructure = new ObjectStructureWithMap(hasIndexStringAsPropertyName, hasSymbol, hasEnumerableProperty, std::move(structureItemVector));
} else {
- newObjectStructure = new ObjectStructureWithTransition(std::move(structureItemVector), hasIndexStringAsPropertyName, hasNonAtomicPropertyName);
+ newObjectStructure = new ObjectStructureWithTransition(std::move(structureItemVector), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty);
}
CachedObjectStructure s;
}
private:
- Vector<Object*, GCUtil::gc_malloc_allocator<Object*>> m_registeredItems;
+ VectorWithInlineStorage<8, Object*, GCUtil::gc_malloc_allocator<Object*>> m_registeredItems;
};
class ToStringRecursionPreventerItemAutoHolder {
RELEASE_ASSERT_NOT_REACHED();
}
+ virtual bool hasOwnEnumeration() const override
+ {
+ return true;
+ }
+
virtual ObjectHasPropertyResult hasProperty(ExecutionState& state, const ObjectPropertyName& P) override;
virtual ObjectGetResult getOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override;
virtual bool defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc) override;
#if defined(ENABLE_WASM)
#ifndef ESCARGOT_WASM_GC_CHECK_INTERVAL
-#define ESCARGOT_WASM_GC_CHECK_INTERVAL 5000
+#define ESCARGOT_WASM_GC_CHECK_INTERVAL 10000
#endif
#endif
{
VMInstance* self = (VMInstance*)data;
-#ifdef ESCARGOT_DEBUGGER
- if (!self->m_debuggerEnabled) {
-#endif
- if (self->m_regexpCache->size() > REGEXP_CACHE_SIZE_MAX || UNLIKELY(self->inIdleMode())) {
- self->m_regexpCache->clear();
- }
+#if !defined(ESCARGOT_DEBUGGER)
+ // in debugger mode, do not remove ByteCodeBlock
+ if (self->m_regexpCache->size() > REGEXP_CACHE_SIZE_MAX || UNLIKELY(self->inIdleMode())) {
+ self->m_regexpCache->clear();
+ }
- auto& currentCodeSizeTotal = self->compiledByteCodeSize();
- if (currentCodeSizeTotal > SCRIPT_FUNCTION_OBJECT_BYTECODE_SIZE_MAX || UNLIKELY(self->inIdleMode())) {
- currentCodeSizeTotal = std::numeric_limits<size_t>::max();
+ auto& currentCodeSizeTotal = self->compiledByteCodeSize();
+ if (currentCodeSizeTotal > SCRIPT_FUNCTION_OBJECT_BYTECODE_SIZE_MAX || UNLIKELY(self->inIdleMode())) {
+ currentCodeSizeTotal = std::numeric_limits<size_t>::max();
- auto& v = self->compiledByteCodeBlocks();
- for (size_t i = 0; i < v.size(); i++) {
- auto cb = v[i]->m_codeBlock;
- if (LIKELY(!cb->isAsync() && !cb->isGenerator())) {
- v[i]->m_codeBlock->setByteCodeBlock(nullptr);
- }
+ auto& v = self->compiledByteCodeBlocks();
+ for (size_t i = 0; i < v.size(); i++) {
+ auto cb = v[i]->m_codeBlock;
+ if (LIKELY(!cb->isAsync() && !cb->isGenerator())) {
+ v[i]->m_codeBlock->setByteCodeBlock(nullptr);
}
}
-#ifdef ESCARGOT_DEBUGGER
}
#endif
}
currentCodeSizeTotal = 0;
auto& v = self->compiledByteCodeBlocks();
for (size_t i = 0; i < v.size(); i++) {
- v[i]->m_codeBlock->setByteCodeBlock(v[i]);
+ auto cb = v[i]->m_codeBlock;
+ if (UNLIKELY(!cb->isAsync() && !cb->isGenerator())) {
+ v[i]->m_codeBlock->setByteCodeBlock(v[i]);
+ }
+ ASSERT(v[i]->m_codeBlock->byteCodeBlock() == v[i]);
+
currentCodeSizeTotal += v[i]->memoryAllocatedSize();
}
}
}
clearCachesRelatedWithContext();
-#ifdef ENABLE_ICU
+#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
vzone_close(m_timezone);
#endif
, m_isFinalized(false)
, m_inIdleMode(false)
, m_didSomePrototypeObjectDefineIndexedProperty(false)
-#ifdef ESCARGOT_DEBUGGER
- , m_debuggerEnabled(false)
-#endif /* ESCARGOT_DEBUGGER */
, m_compiledByteCodeSize(0)
#if defined(ENABLE_COMPRESSIBLE_STRING)
, m_lastCompressibleStringsTestTime(0)
, m_errorCreationCallbackPublic(nullptr)
, m_promiseHook(nullptr)
, m_promiseHookPublic(nullptr)
+ , m_promiseRejectCallback(nullptr)
+ , m_promiseRejectCallbackPublic(nullptr)
, m_cachedUTC(nullptr)
{
GC_REGISTER_FINALIZER_NO_ORDER(this, [](void* obj, void*) {
RELEASE_ASSERT(((size_t)m_stackStartAddress) % sizeof(size_t) == 0);
if (!String::emptyString) {
- String::emptyString = new (NoGC) ASCIIString("");
+ String::initEmptyString();
+ ASSERT(!!String::emptyString && String::emptyString->isAtomicStringSource());
}
m_staticStrings.initStaticStrings();
m_regexpOptionStringCache = (ASCIIString**)GC_MALLOC(64 * sizeof(ASCIIString*));
memset(m_regexpOptionStringCache, 0, 64 * sizeof(ASCIIString*));
-#ifdef ENABLE_ICU
+#if defined(ENABLE_ICU)
+#if !defined(OS_WINDOWS_UWP)
m_timezone = nullptr;
+#endif
if (timezone) {
m_timezoneID = timezone;
} else if (getenv("TZ")) {
ExecutionState stateForInit((Context*)nullptr);
- m_defaultStructureForObject = new ObjectStructureWithTransition(ObjectStructureItemTightVector(), false, false);
+ m_defaultStructureForObject = new ObjectStructureWithTransition(ObjectStructureItemTightVector(), false, false, false, false);
m_defaultStructureForFunctionObject = m_defaultStructureForObject->addProperty(m_staticStrings.prototype,
ObjectStructurePropertyDescriptor::createDataButHasNativeGetterSetterDescriptor(&functionPrototypeNativeGetterSetterData));
{
return RuntimeICUBinder::ICU::findSystemTimezoneName();
}
+#elif defined(OS_WINDOWS_UWP)
+static std::string findTimezone()
+{
+ DYNAMIC_TIME_ZONE_INFORMATION tz;
+ DWORD ret = GetDynamicTimeZoneInformation(&tz);
+ UErrorCode status = U_ZERO_ERROR;
+ UChar result[256];
+ int32_t len = ucal_getTimeZoneIDForWindowsID(
+ (const UChar*)tz.TimeZoneKeyName, -1,
+ "en_US",
+ result,
+ sizeof(result) / sizeof(UChar),
+ &status);
+
+ RELEASE_ASSERT(status == U_ZERO_ERROR);
+ return (new UTF16String(result, len))->toNonGCUTF8StringData();
+}
#else
static std::string findTimezone()
{
if (m_timezoneID == "") {
m_timezoneID = findTimezone();
} else {
+#if !defined(OS_WINDOWS_UWP)
tzset();
+#endif
}
+#if !defined(OS_WINDOWS_UWP)
auto u16 = utf8StringToUTF16String(m_timezoneID.data(), m_timezoneID.size());
m_timezone = vzone_openID(u16.data(), u16.size());
+#endif
}
#endif
After
};
+ enum PromiseRejectEvent {
+ PromiseRejectWithNoHandler = 0,
+ PromiseHandlerAddedAfterReject = 1,
+ PromiseRejectAfterResolved = 2,
+ PromiseResolveAfterResolved = 3,
+ };
+
typedef void (*PromiseHook)(ExecutionState& state, PromiseHookType type, PromiseObject* promise, const Value& parent, void* hook);
+ typedef void (*PromiseRejectCallback)(ExecutionState& state, PromiseObject* promise, const Value& value, PromiseRejectEvent event, void* callback);
VMInstance(const char* locale = nullptr, const char* timezone = nullptr, const char* baseCacheDir = nullptr);
~VMInstance();
return m_timezoneID;
}
+ void ensureTimezone();
+
+#if !defined(OS_WINDOWS_UWP)
VZone* timezone()
{
if (m_timezone == nullptr) {
}
return m_timezone;
}
+#endif
- void ensureTimezone();
#endif
DateObject* cachedUTC(ExecutionState& state);
}
}
+ // trigger PromiseRejectCallback
+ // Third party app registers PromiseRejectCallback when it is necessary
+ bool isPromiseRejectCallbackRegistered()
+ {
+ return !!m_promiseRejectCallback;
+ }
+
+ void registerPromiseRejectCallback(PromiseRejectCallback promiseRejectCallback, void* promiseRejectCallbackPublic)
+ {
+ m_promiseRejectCallback = promiseRejectCallback;
+ m_promiseRejectCallbackPublic = promiseRejectCallbackPublic;
+ }
+
+ void unregisterPromiseRejectCallback()
+ {
+ m_promiseRejectCallback = nullptr;
+ m_promiseRejectCallbackPublic = nullptr;
+ }
+
+ void triggerPromiseRejectCallback(ExecutionState& state, PromiseObject* promise, const Value& value, PromiseRejectEvent event)
+ {
+ ASSERT(!!m_promiseRejectCallback);
+ if (m_promiseRejectCallbackPublic) {
+ m_promiseRejectCallback(state, promise, value, event, m_promiseRejectCallbackPublic);
+ }
+ }
+
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& intlCollatorAvailableLocales();
const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& intlDateTimeFormatAvailableLocales();
bool m_inIdleMode;
// this flag should affect VM-wide array object
bool m_didSomePrototypeObjectDefineIndexedProperty;
-#ifdef ESCARGOT_DEBUGGER
- bool m_debuggerEnabled;
-#endif /* ESCARGOT_DEBUGGER */
ObjectStructure* m_defaultStructureForObject;
ObjectStructure* m_defaultStructureForFunctionObject;
PromiseHook m_promiseHook;
void* m_promiseHookPublic;
+ // Third party app registers PromiseRejectCallback when it is necessary
+ PromiseRejectCallback m_promiseRejectCallback;
+ void* m_promiseRejectCallbackPublic;
+
ToStringRecursionPreventer m_toStringRecursionPreventer;
void* m_stackStartAddress;
// date object data
#ifdef ENABLE_ICU
std::string m_locale;
+#if !defined(OS_WINDOWS_UWP)
VZone* m_timezone;
+#endif
std::string m_timezoneID;
#endif
DateObject* m_cachedUTC;
const StaticStrings& strings = state.context()->staticStrings();
Object* input = asObject();
+ // fast path
+ auto ret = input->fastLookupForSymbol(state, state.context()->vmInstance()->globalSymbols().toPrimitive);
+ if (ret.wasSucessful() && !ret.matchedObject().hasValue()) {
+ // If hint is "default", let hint be "number".
+ if (preferredType == PreferDefault) {
+ preferredType = PreferNumber;
+ }
+ return ordinaryToPrimitive(state, preferredType);
+ }
+
// Let exoticToPrim be GetMethod(input, @@toPrimitive).
Value exoticToPrim = Object::getMethod(state, input, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toPrimitive));
// If exoticToPrim is not undefined, then
return ordinaryToPrimitive(state, preferredType);
}
+bool Value::toBooleanSlowCase(ExecutionState& ec) const
+{
+ if (isDouble()) {
+ double d = asDouble();
+ if (std::isnan(d))
+ return false;
+ if (d == 0.0)
+ return false;
+ return true;
+ }
+
+ if (isUndefinedOrNull())
+ return false;
+
+ ASSERT(isPointerValue());
+
+ if (UNLIKELY(asPointerValue()->isString()))
+ return asString()->length();
+
+ if (UNLIKELY(isBigInt())) {
+ return !asBigInt()->isZero();
+ }
+
+#if defined(ESCARGOT_ENABLE_TEST)
+ if (UNLIKELY(checkIfObjectWithIsHTMLDDA())) {
+ return false;
+ }
+#endif
+ // Symbol, Objects..
+ return true;
+}
+
bool Value::abstractEqualsToSlowCase(ExecutionState& state, const Value& val) const
{
bool selfIsNumber = isNumber();
double a = asNumber();
double b = val.asNumber();
- if (std::isnan(a) || std::isnan(b))
+ if (UNLIKELY(std::isnan(a) || std::isnan(b)))
return false;
- else if (a == b)
- return true;
- return false;
+ return a == b;
} else {
+ if (u.asInt64 == val.u.asInt64) {
+ return true;
+ }
+
bool selfIsUndefinedOrNull = isUndefinedOrNull();
bool valIsUndefinedOrNull = val.isUndefinedOrNull();
if (selfIsUndefinedOrNull && valIsUndefinedOrNull)
bool Value::equalsToSlowCase(ExecutionState& state, const Value& val) const
{
- if (isUndefined())
- return val.isUndefined();
-
- if (isNull())
- return val.isNull();
+ if (!val.isPointerValue()) {
+ if (isNumber() && val.isNumber()) {
+ double a = asNumber();
+ double b = val.asNumber();
+ if (UNLIKELY(std::isnan(a) || std::isnan(b))) {
+ return false;
+ }
+ // we can pass [If x is +0 and y is −0, return true. If x is −0 and y is +0, return true.]
+ // because
+ // double a = -0.0;
+ // double b = 0.0;
+ // a == b; is true
+ return a == b;
+ }
- if (isBoolean())
- return val.isBoolean() && asBoolean() == val.asBoolean();
+ return u.asInt64 == val.u.asInt64;
+ } else {
+ if (u.asInt64 == val.u.asInt64) {
+ return true;
+ }
- if (isNumber()) {
- if (!val.isNumber())
+ if (!isPointerValue())
return false;
- double a = asNumber();
- double b = val.asNumber();
- if (std::isnan(a) || std::isnan(b))
- return false;
- // we can pass [If x is +0 and y is −0, return true. If x is −0 and y is +0, return true.]
- // because
- // double a = -0.0;
- // double b = 0.0;
- // a == b; is true
- return a == b;
- }
- if (isPointerValue()) {
PointerValue* o = asPointerValue();
- if (!val.isPointerValue())
- return false;
PointerValue* o2 = val.asPointerValue();
if (o->isString()) {
if (!o2->isString())
}
return o->asBigInt()->equals(o2->asBigInt());
}
- return o == o2;
}
+
return false;
}
bool Value::equalsToByTheSameValueAlgorithm(ExecutionState& ec, const Value& val) const
{
- if (isUndefined()) {
- return val.isUndefined();
- }
-
- if (isNull()) {
- return val.isNull();
- }
+ if (!val.isPointerValue()) {
+ if (isNumber() && val.isNumber()) {
+ double a = asNumber();
+ double b = val.asNumber();
- if (isBoolean()) {
- return val.isBoolean() && asBoolean() == val.asBoolean();
- }
+ if (UNLIKELY(std::isnan(a) || std::isnan(b))) {
+ return std::isnan(a) && std::isnan(b);
+ }
- if (isNumber()) {
- if (!val.isNumber()) {
- return false;
+ // we can pass [If x is +0 and y is −0, return true. If x is −0 and y is +0, return true.]
+ // because
+ // double a = -0.0;
+ // double b = 0.0;
+ // a == b; is true
+ return a == b && std::signbit(a) == std::signbit(b);
}
- double a = asNumber();
- double b = val.asNumber();
- if (std::isnan(a) && std::isnan(b)) {
+
+ return u.asInt64 == val.u.asInt64;
+ } else {
+ if (u.asInt64 == val.u.asInt64) {
return true;
}
- if (std::isnan(a) || std::isnan(b)) {
+
+ if (!isPointerValue()) {
return false;
}
- // we can pass [If x is +0 and y is −0, return true. If x is −0 and y is +0, return true.]
- // because
- // double a = -0.0;
- // double b = 0.0;
- // a == b; is true
- return a == b && std::signbit(a) == std::signbit(b);
- }
- if (isPointerValue()) {
PointerValue* o = asPointerValue();
- if (!val.isPointerValue()) {
- return false;
- }
PointerValue* o2 = val.asPointerValue();
if (o->isString()) {
if (!o2->isString()) {
}
return *o->asString() == *o2->asString();
}
- if (o->isSymbol()) {
- if (!o2->isSymbol()) {
- return false;
- }
- }
if (UNLIKELY(o->isBigInt())) {
if (!o2->isBigInt()) {
return false;
}
return o->asBigInt()->equals(o2->asBigInt());
}
- return o == o2;
}
+
return false;
}
bool Value::equalsToByTheSameValueZeroAlgorithm(ExecutionState& ec, const Value& val) const
{
- if (isUndefined()) {
- return val.isUndefined();
- }
+ if (LIKELY(!val.isPointerValue())) {
+ if (isNumber() && val.isNumber()) {
+ double a = asNumber();
+ double b = val.asNumber();
- if (isNull()) {
- return val.isNull();
- }
-
- if (isBoolean()) {
- return val.isBoolean() && asBoolean() == val.asBoolean();
- }
+ if (UNLIKELY(std::isnan(a) || std::isnan(b))) {
+ return std::isnan(a) && std::isnan(b);
+ }
- if (isNumber()) {
- if (!val.isNumber()) {
- return false;
+ return a == b;
}
- double a = asNumber();
- double b = val.asNumber();
- if (std::isnan(a) && std::isnan(b)) {
+
+ return u.asInt64 == val.u.asInt64;
+ } else {
+ if (u.asInt64 == val.u.asInt64) {
return true;
}
- if (std::isnan(a) || std::isnan(b)) {
+
+ if (!isPointerValue()) {
return false;
}
- return a == b;
- }
- if (isPointerValue()) {
PointerValue* o = asPointerValue();
- if (!val.isPointerValue()) {
- return false;
- }
PointerValue* o2 = val.asPointerValue();
if (o->isString()) {
if (!o2->isString()) {
}
return *o->asString() == *o2->asString();
}
- if (o->isSymbol()) {
- if (!o2->isSymbol()) {
- return false;
- }
- }
if (UNLIKELY(o->isBigInt())) {
if (!o2->isBigInt()) {
return false;
}
return o->asBigInt()->equals(o2->asBigInt());
}
- return o == o2;
}
+
return false;
}
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, ErrorObject::Messages::InstanceOf_NotFunction);
}
Object* C = other.asObject();
+
+ // fast path
+ auto globalFunctionPrototype = state.context()->globalObject()->functionPrototype();
+ auto fastPathResult = C->fastLookupForSymbol(state, state.context()->vmInstance()->globalSymbols().hasInstance, globalFunctionPrototype);
+ if (fastPathResult.wasSucessful() && fastPathResult.matchedObject().unwrap() == globalFunctionPrototype) {
+ return C->hasInstance(state, *this);
+ }
+
// Let instOfHandler be GetMethod(C,@@hasInstance).
Value instOfHandler = Object::getMethod(state, other, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().hasInstance));
// If instOfHandler is not undefined, then
String* toStringSlowCase(ExecutionState& ec) const; // $7.1.12 ToString
Object* toObjectSlowCase(ExecutionState& ec) const; // $7.1.13 ToObject
Value toPrimitiveSlowCase(ExecutionState& ec, PrimitiveTypeHint) const; // $7.1.1 ToPrimitive
+ bool toBooleanSlowCase(ExecutionState& ec) const; // $7.1.2 ToBoolean
int32_t toInt32SlowCase(ExecutionState& ec) const; // $7.1.5 ToInt32
ValueIndex tryToUseAsIndexSlowCase(ExecutionState& ec) const;
uint32_t tryToUseAsIndex32SlowCase(ExecutionState& ec) const;
{
if (isInt32() && val.isInt32()) {
#ifdef ESCARGOT_64
- if (u.asInt64 == val.u.asInt64)
+ return u.asInt64 == val.u.asInt64;
#else
- if (u.asBits.payload == val.u.asBits.payload)
+ return u.asBits.payload == val.u.asBits.payload;
#endif
- return true;
- return false;
} else {
return abstractEqualsToSlowCase(state, val);
}
{
if (isInt32() && val.isInt32()) {
#ifdef ESCARGOT_64
- if (u.asInt64 == val.u.asInt64)
+ return u.asInt64 == val.u.asInt64;
#else
- if (u.asBits.payload == val.u.asBits.payload)
+ return u.asBits.payload == val.u.asBits.payload;
#endif
- return true;
- return false;
} else {
return equalsToSlowCase(state, val);
}
if (isInt32())
return asInt32();
- if (isDouble()) {
- double d = asDouble();
- if (std::isnan(d))
- return false;
- if (d == 0.0)
- return false;
- return true;
- }
-
- if (isUndefinedOrNull())
- return false;
-
- ASSERT(isPointerValue());
-
- if (UNLIKELY(asPointerValue()->isString()))
- return asString()->length();
-
- if (UNLIKELY(isBigInt())) {
- return !asBigInt()->isZero();
- }
-
-#if defined(ESCARGOT_ENABLE_TEST)
- if (UNLIKELY(checkIfObjectWithIsHTMLDDA())) {
- return false;
- }
-#endif
- // Symbol, Objects..
- return true;
+ return toBooleanSlowCase(ec);
}
inline int32_t Value::toInt32(ExecutionState& state) const // $7.1.5 ToInt3
if (!evalResult.isSuccessful()) {
fprintf(stderr, "Uncaught %s:\n", evalResult.resultOrErrorToString(context)->toStdUTF8String().data());
- for (size_t i = 0; i < evalResult.stackTraceData.size(); i++) {
- fprintf(stderr, "%s (%d:%d)\n", evalResult.stackTraceData[i].src->toStdUTF8String().data(), (int)evalResult.stackTraceData[i].loc.line, (int)evalResult.stackTraceData[i].loc.column);
+ for (size_t i = 0; i < evalResult.stackTrace.size(); i++) {
+ fprintf(stderr, "%s (%d:%d)\n", evalResult.stackTrace[i].srcName->toStdUTF8String().data(), (int)evalResult.stackTrace[i].loc.line, (int)evalResult.stackTrace[i].loc.column);
}
return false;
}
mallopt(M_MMAP_MAX, 1024 * 1024);
#endif
+ bool wait_before_exit = false;
+
ShellPlatform* platform = new ShellPlatform();
Globals::initialize(platform);
continue;
}
if (strcmp(argv[i], "--start-debug-server") == 0) {
- context->initDebugger(nullptr);
+ context->initDebuggerRemote(nullptr);
continue;
}
if (strcmp(argv[i], "--debugger-wait-source") == 0) {
}
continue;
}
+ if (strcmp(argv[i], "--wait-before-exit") == 0) {
+ wait_before_exit = true;
+ continue;
+ }
} else { // `-option` case
if (strcmp(argv[i], "-e") == 0) {
runShell = false;
printf("escargot version:%s, %s%s\n", Globals::version(), Globals::buildDate(), Globals::supportsThreading() ? "(supports threading)" : "");
}
+ if (wait_before_exit || context->isWaitBeforeExit()) {
+ context->setAsAlwaysStopState();
+ auto evalResult = Evaluator::execute(context, [](ExecutionStateRef* state, ScriptRef* script) -> ValueRef* {
+ return script->execute(state);
+ },
+ context->scriptParser()->initializeScript(StringRef::createFromASCII(""), StringRef::createFromASCII("<ScriptEnd>"), seenModule).script.get());
+ }
+
while (runShell) {
static char buf[2048];
printf("escargot> ");
return 3;
}
StringRef* str = Escargot::StringRef::createFromUTF8(buf, strlen(buf));
- evalScript(context, str, StringRef::createFromASCII("from shell input"), true, false);
+ evalScript(context, str, StringRef::emptyString(), true, false);
}
#if defined(ESCARGOT_ENABLE_TEST)
public:
SpinLock()
- : m_locked(0)
+ : m_locked(ATOMIC_FLAG_INIT)
{
}
}
}
+ using iterator = T*;
+ constexpr iterator begin() const { return m_buffer; }
+ constexpr iterator end() const { return m_buffer + m_size; }
+
void* operator new(size_t size)
{
static MAY_THREAD_LOCAL bool typeInited = false;
int tz_dsttime; /* type of dst correction */
};
+#if !defined(_WINSOCKAPI_)
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#endif
+
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
if (NULL != tz) {
if (!tzflag) {
+#if !defined(OS_WINDOWS_UWP)
_tzset();
+#endif
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
// Let (store, ret) be the result of func_invoke(store, funcaddr, args).
own wasm_trap_t* trap = wasm_func_call(funcaddr, args.data, ret.data);
- // FIXME wabt allocates a Thread object for each function call
- // Thread object is never explicitly reclaimed
- // invoke collectHeap right after wasm_func_call
- WASMOperations::collectHeap();
// If ret is error, throw an exception. This exception should be a WebAssembly RuntimeError exception, unless otherwise indicated by the WebAssembly error mapping.
if (trap) {
+++ /dev/null
-/* include/config.h.in. Generated from configure.ac by autoheader. */
-
-/* See doc/README.macros. */
-/* #undef DARWIN_DONT_PARSE_STACK */
-
-/* Define to force debug headers on all objects. */
-/* #define DBG_HDRS_ALL 1 */
-
-/* Define to enable support for DB/UX threads. */
-/* #undef DGUX_THREADS */
-
-/* Define to enable eCos target support. */
-/* #undef ECOS */
-
-/* Wine getenv may not return NULL for missing entry. */
-/* #undef EMPTY_GETENV_RESULTS */
-
-/* Define to enable alternative finalization interface. */
-#define ENABLE_DISCLAIM 1
-
-/* Define to support IBM AIX threads. */
-/* #undef GC_AIX_THREADS */
-
-/* Define to enable internal debug assertions. */
-/* #define GC_ASSERTIONS 1 */
-
-/* Define to enable atomic uncollectible allocation. */
-#define GC_ATOMIC_UNCOLLECTABLE 1
-
-/* Define to support Darwin pthreads. */
-/* #undef GC_DARWIN_THREADS */
-
-/* Define to enable support for DB/UX threads on i386. */
-/* #undef GC_DGUX386_THREADS */
-
-/* Define to build dynamic libraries with only API symbols exposed. */
-#define GC_DLL 1
-
-/* Define to support FreeBSD pthreads. */
-/* #undef GC_FREEBSD_THREADS */
-
-/* Define to include support for gcj. */
-/* #define GC_GCJ_SUPPORT 1 */
-
-/* Define to support GNU pthreads. */
-/* #undef GC_GNU_THREADS */
-
-/* Define if backtrace information is supported. */
-/* #undef GC_HAVE_BUILTIN_BACKTRACE */
-
-/* Define to support HP/UX 11 pthreads. */
-/* #undef GC_HPUX_THREADS */
-
-/* Enable Win32 DllMain-based approach of threads registering. */
-/* #undef GC_INSIDE_DLL */
-
-/* Define to support Irix pthreads. */
-/* #undef GC_IRIX_THREADS */
-
-/* Define to support pthreads on Linux. */
-/* #undef GC_LINUX_THREADS */
-
-/* Define to support NetBSD pthreads. */
-/* #undef GC_NETBSD_THREADS */
-
-/* Define to support OpenBSD pthreads. */
-/* #undef GC_OPENBSD_THREADS */
-
-/* Define to support Tru64 pthreads. */
-/* #undef GC_OSF1_THREADS */
-
-/* Read environment variables from the GC 'env' file. */
-/* #undef GC_READ_ENV_FILE */
-
-/* Define to support rtems-pthreads. */
-/* #undef GC_RTEMS_PTHREADS */
-
-/* Define to support Solaris pthreads. */
-/* #undef GC_SOLARIS_THREADS */
-
-/* Define to support platform-specific threads. */
-/* #undef GC_THREADS */
-
-/* Explicitly prefix exported/imported WINAPI symbols with '_'. */
-/* #undef GC_UNDERSCORE_STDCALL */
-
-/* Force the GC to use signals based on SIGRTMIN+k. */
-/* #undef GC_USESIGRT_SIGNALS */
-
-/* See doc/README.macros. */
-/* #undef GC_USE_DLOPEN_WRAP */
-
-/* The major version number of this GC release. */
-#define GC_VERSION_MAJOR 8
-
-/* The micro version number of this GC release. */
-#define GC_VERSION_MICRO 4
-
-/* The minor version number of this GC release. */
-#define GC_VERSION_MINOR 0
-
-/* Define to support pthreads-win32 or winpthreads. */
-/* #undef GC_WIN32_PTHREADS */
-
-/* Define to support Win32 threads. */
-/* #undef GC_WIN32_THREADS */
-
-/* Define to install pthread_atfork() handlers by default. */
-/* #undef HANDLE_FORK */
-
-/* Define to use 'dladdr' function. */
-/* #undef HAVE_DLADDR */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-/* #undef HAVE_DLFCN_H */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-/* #undef HAVE_INTTYPES_H */
-
-/* Define to 1 if you have the <memory.h> header file. */
-/* #undef HAVE_MEMORY_H */
-
-/* Define to 1 if you have the <stdint.h> header file. */
-/* #undef HAVE_STDINT_H */
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-/* #undef HAVE_SYS_STAT_H */
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-/* #undef HAVE_SYS_TYPES_H */
-
-/* Define to 1 if you have the <unistd.h> header file. */
-/* #undef HAVE_UNISTD_H */
-
-/* See doc/README.macros. */
-/* #undef JAVA_FINALIZATION */
-
-/* Define to save back-pointers in debugging headers. */
-/* #define KEEP_BACK_PTRS */
-
-/* Define to optimize for large heaps or root sets. */
-#define LARGE_CONFIG
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
-/* #undef LT_OBJDIR */
-
-/* See doc/README.macros. */
-/* #undef MAKE_BACK_GRAPH */
-
-/* Number of GC cycles to wait before unmapping an unused block. */
-/* #undef MUNMAP_THRESHOLD */
-
-/* Define to not use system clock (cross compiling). */
-/* #undef NO_CLOCK */
-
-/* Disable debugging, like GC_dump and its callees. */
-#ifdef NDEBUG
-#define NO_DEBUGGING 1
-#else
-#define DBG_HDRS_ALL 1
-#define KEEP_BACK_PTRS 1
-#endif
-
-/* Define to make the collector not allocate executable memory by default. */
-/* #undef NO_EXECUTE_PERMISSION */
-
-/* Prohibit installation of pthread_atfork() handlers. */
-/* #undef NO_HANDLE_FORK */
-
-/* Name of package */
-/* #undef PACKAGE */
-
-/* Define to the address where bug reports for this package should be sent. */
-/* #undef PACKAGE_BUGREPORT */
-
-/* Define to the full name of this package. */
-/* #undef PACKAGE_NAME */
-
-/* Define to the full name and version of this package. */
-/* #undef PACKAGE_STRING */
-
-/* Define to the one symbol short name of this package. */
-/* #undef PACKAGE_TARNAME */
-
-/* Define to the home page for this package. */
-/* #undef PACKAGE_URL */
-
-/* Define to the version of this package. */
-/* #undef PACKAGE_VERSION */
-
-/* Define to enable parallel marking. */
-/* #undef PARALLEL_MARK */
-
-/* If defined, redirect free to this function. */
-/* #undef REDIRECT_FREE */
-
-/* If defined, redirect malloc to this function. */
-/* #undef REDIRECT_MALLOC */
-
-/* If defined, redirect GC_realloc to this function. */
-/* #undef REDIRECT_REALLOC */
-
-/* The number of caller frames saved when allocating with the debugging API.
- */
-/* #define SAVE_CALL_COUNT 8 */
-
-/* Shorten the headers to minimize object size at the expense of checking for
- writes past the end (see doc/README.macros). */
-/* #undef SHORT_DBG_HDRS */
-
-/* Define to tune the collector for small heap sizes. */
-/* #undef SMALL_CONFIG */
-
-/* See the comment in gcconfig.h. */
-/* #undef SOLARIS25_PROC_VDB_BUG_FIXED */
-
-/* Define to 1 if you have the ANSI C header files. */
-/* #undef STDC_HEADERS */
-
-/* Define to work around a Solaris 5.3 bug (see dyn_load.c). */
-/* #undef SUNOS53_SHARED_LIB */
-
-/* Define to enable thread-local allocation optimization. */
-/* #undef THREAD_LOCAL_ALLOC */
-
-/* Use Unicode (W) variant of Win32 API instead of ASCII (A) one. */
-/* #undef UNICODE */
-
-/* Define to use of compiler-support for thread-local variables. */
-/* #undef USE_COMPILER_TLS */
-
-/* Define to use mmap instead of sbrk to expand the heap. */
-/* #undef USE_MMAP */
-
-/* Define to return memory to OS with munmap calls (see doc/README.macros). */
-/* #undef USE_MUNMAP */
-
-/* Define to use Win32 VirtualAlloc (instead of sbrk or mmap) to expand the
- heap. */
-#define USE_WINALLOC 1
-
-/* Version number of package */
-#define VERSION "8.0.4"
-
-/* The POSIX feature macro. */
-/* #undef _POSIX_C_SOURCE */
-
-/* Indicates the use of pthreads (NetBSD). */
-/* #undef _PTHREADS */
-
-/* Required define if using POSIX threads. */
-/* #undef _REENTRANT */
-
-#define ALL_INTERIOR_POINTERS 0
-#define GC_DONT_REGISTER_MAIN_STATIC_DATA 1
-#define GC_ENABLE_SUSPEND_THREAD 1
-#define GC_NO_THREADS_DISCOVERY 1
-#define IGNORE_DYNAMIC_LOADING 1
-#define JAVA_FINALIZATION 1
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
- calls it, or to nothing if 'inline' is not supported under any name. */
-#ifndef __cplusplus
-#define inline
-#endif
\ No newline at end of file
# disable -Wpointer-arith: this is a GCC extension, and doesn't work in MSVC.
set(WASM_CXX_FLAGS_INTERNAL
-Wall -Wextra -Werror -Wno-unused-parameter -Wpointer-arith
- -Wuninitialized -fno-exceptions -fPIC -fdata-sections -ffunction-sections
+ -Wuninitialized -fPIC -fdata-sections -ffunction-sections
)
# set c++ flags
#
cmake_minimum_required(VERSION 3.0.0)
-project(WABT VERSION 1.0.24)
+project(WABT VERSION 1.0.26)
include(GNUInstallDirs)
if (POLICY CMP0077)
| [mutable globals][] | `--disable-mutable-globals` | ✓ | ✓ | ✓ | ✓ | ✓ |
| [nontrapping float-to-int conversions][] | `--disable-saturating-float-to-int` | ✓ | ✓ | ✓ | ✓ | ✓ |
| [sign extension][] | `--disable-sign-extension` | ✓ | ✓ | ✓ | ✓ | ✓ |
-| [simd][] | `--enable-simd` | | ✓ | ✓ | ✓ | ✓ |
+| [simd][] | `--disable-simd` | ✓ | ✓ | ✓ | ✓ | ✓ |
| [threads][] | `--enable-threads` | | ✓ | ✓ | ✓ | ✓ |
| [multi-value][] | `--disable-multi-value` | ✓ | ✓ | ✓ | ✓ | ✓ |
| [tail-call][] | `--enable-tail-call` | | ✓ | ✓ | ✓ | ✓ |
-| [bulk memory][] | `--enable-bulk-memory` | | ✓ | ✓ | ✓ | ✓ |
-| [reference types][] | `--enable-reference-types` | | ✓ | ✓ | ✓ | ✓ |
+| [bulk memory][] | `--disable-bulk-memory` | ✓ | ✓ | ✓ | ✓ | ✓ |
+| [reference types][] | `--disable-reference-types` | ✓ | ✓ | ✓ | ✓ | ✓ |
| [annotations][] | `--enable-annotations` | | | ✓ | | |
| [memory64][] | `--enable-memory64` | | | | | |
+| [multi-memory][] | `--enable-multi-memory` | | ✓ | ✓ | ✓ | ✓ |
[exception handling]: https://github.com/WebAssembly/exception-handling
[mutable globals]: https://github.com/WebAssembly/mutable-global
[reference types]: https://github.com/WebAssembly/reference-types
[annotations]: https://github.com/WebAssembly/annotations
[memory64]: https://github.com/WebAssembly/memory64
+[multi-memory]: https://github.com/WebAssembly/multi-memory
## Cloning
#include <cstdio>
#include <vector>
+#include "src/cast.h"
#include "src/expr-visitor.h"
#include "src/ir.h"
#include "src/string-view.h"
return Result::Ok;
}
-Result NameApplier::OnMemoryInitExpr(MemoryInitExpr* expr) {
+Result NameApplier::OnMemoryInitExpr(MemoryInitExpr* expr) {
CHECK_RESULT(UseNameForDataSegmentVar(&expr->var));
return Result::Ok;
}
-Result NameApplier::OnElemDropExpr(ElemDropExpr* expr) {
+Result NameApplier::OnElemDropExpr(ElemDropExpr* expr) {
CHECK_RESULT(UseNameForElemSegmentVar(&expr->var));
return Result::Ok;
}
return Result::Ok;
}
-Result NameApplier::OnTableInitExpr(TableInitExpr* expr) {
+Result NameApplier::OnTableInitExpr(TableInitExpr* expr) {
CHECK_RESULT(UseNameForElemSegmentVar(&expr->segment_index));
CHECK_RESULT(UseNameForTableVar(&expr->table_index));
return Result::Ok;
}
-Result NameApplier::OnTableGetExpr(TableGetExpr* expr) {
+Result NameApplier::OnTableGetExpr(TableGetExpr* expr) {
CHECK_RESULT(UseNameForTableVar(&expr->var));
return Result::Ok;
}
-Result NameApplier::OnTableSetExpr(TableSetExpr* expr) {
+Result NameApplier::OnTableSetExpr(TableSetExpr* expr) {
CHECK_RESULT(UseNameForTableVar(&expr->var));
return Result::Ok;
}
-Result NameApplier::OnTableGrowExpr(TableGrowExpr* expr) {
+Result NameApplier::OnTableGrowExpr(TableGrowExpr* expr) {
CHECK_RESULT(UseNameForTableVar(&expr->var));
return Result::Ok;
}
-Result NameApplier::OnTableSizeExpr(TableSizeExpr* expr) {
+Result NameApplier::OnTableSizeExpr(TableSizeExpr* expr) {
CHECK_RESULT(UseNameForTableVar(&expr->var));
return Result::Ok;
}
-Result NameApplier::OnTableFillExpr(TableFillExpr* expr) {
+Result NameApplier::OnTableFillExpr(TableFillExpr* expr) {
CHECK_RESULT(UseNameForTableVar(&expr->var));
return Result::Ok;
}
ElemSegment* segment) {
CHECK_RESULT(UseNameForTableVar(&segment->table_var));
CHECK_RESULT(visitor_.VisitExprList(segment->offset));
- for (ElemExpr& elem_expr : segment->elem_exprs) {
- if (elem_expr.kind == ElemExprKind::RefFunc) {
- CHECK_RESULT(UseNameForFuncVar(&elem_expr.var));
+ for (ExprList& elem_expr : segment->elem_exprs) {
+ Expr* expr = &elem_expr.front();
+ if (expr->type() == ExprType::RefFunc) {
+ CHECK_RESULT(UseNameForFuncVar(&cast<RefFuncExpr>(expr)->var));
}
}
return Result::Ok;
--- /dev/null
+/*
+ * Copyright 2021 WebAssembly Community Group participants
+ *
+ * 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 WABT_BASE_TYPES_H_
+#define WABT_BASE_TYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace wabt {
+
+typedef uint32_t Index; // An index into one of the many index spaces.
+typedef uint64_t Address; // An address or size in linear memory.
+typedef size_t Offset; // An offset into a host's file or memory buffer.
+
+static const Address kInvalidAddress = ~0;
+static const Index kInvalidIndex = ~0;
+static const Offset kInvalidOffset = ~0;
+
+} // namespace wabt
+
+#endif // WABT_BASE_TYPES_H_
class BinaryReaderIR : public BinaryReaderNop {
public:
- BinaryReaderIR(Module* out_module,
- const char* filename,
- Errors* errors);
+ BinaryReaderIR(Module* out_module, const char* filename, Errors* errors);
bool OnError(const Error&) override;
Result OnI64ConstExpr(uint64_t value) override;
Result OnIfExpr(Type sig_type) override;
Result OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnLocalGetExpr(Index local_index) override;
Result OnLocalSetExpr(Index local_index) override;
Result OnLocalTeeExpr(Index local_index) override;
Result OnLoopExpr(Type sig_type) override;
- Result OnMemoryCopyExpr() override;
+ Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override;
Result OnDataDropExpr(Index segment_index) override;
- Result OnMemoryFillExpr() override;
- Result OnMemoryGrowExpr() override;
- Result OnMemoryInitExpr(Index segment_index) override;
- Result OnMemorySizeExpr() override;
+ Result OnMemoryFillExpr(Index memidx) override;
+ Result OnMemoryGrowExpr(Index memidx) override;
+ Result OnMemoryInitExpr(Index segment_index, Index memidx) override;
+ Result OnMemorySizeExpr(Index memidx) override;
Result OnTableCopyExpr(Index dst_index, Index src_index) override;
Result OnElemDropExpr(Index segment_index) override;
Result OnTableInitExpr(Index segment_index, Index table_index) override;
Result OnReturnExpr() override;
Result OnSelectExpr(Index result_count, Type* result_types) override;
Result OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnThrowExpr(Index tag_index) override;
Result EndFunctionBody(Index index) override;
Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
Result OnTagType(Index index, Index sig_index) override;
Result EndTagSection() override { return Result::Ok; }
- Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprV128ConstExpr(Index index, v128 value) override;
- Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
- Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprRefNull(Index index, Type type) override;
- Result OnInitExprRefFunc(Index index, Index func_index) override;
-
- Result OnDataSymbol(Index index, uint32_t flags, string_view name,
- Index segment, uint32_t offset, uint32_t size) override;
- Result OnFunctionSymbol(Index index, uint32_t flags, string_view name,
- Index func_index) override;
- Result OnGlobalSymbol(Index index, uint32_t flags, string_view name,
- Index global_index) override;
- Result OnSectionSymbol(Index index, uint32_t flags,
- Index section_index) override;
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override;
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index global_index) override;
+ Result OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) override;
Result OnTagSymbol(Index index,
uint32_t flags,
string_view name,
Index tag_index) override;
- Result OnTableSymbol(Index index, uint32_t flags, string_view name,
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
Index table_index) override;
private:
void PushLabel(LabelType label_type,
ExprList* first,
Expr* context = nullptr);
+ Result BeginInitExpr(ExprList* init_expr);
+ Result EndInitExpr();
Result PopLabel();
Result GetLabelAt(LabelNode** label, Index depth);
Result TopLabel(LabelNode** label);
Result SetGlobalName(Index index, string_view name);
Result SetDataSegmentName(Index index, string_view name);
Result SetElemSegmentName(Index index, string_view name);
+ Result SetTagName(Index index, string_view name);
std::string GetUniqueName(BindingHash* bindings,
const std::string& original_name);
Func* current_func_ = nullptr;
std::vector<LabelNode> label_stack_;
- ExprList* current_init_expr_ = nullptr;
const char* filename_;
};
Result BinaryReaderIR::BeginGlobalInitExpr(Index index) {
assert(index == module_->globals.size() - 1);
Global* global = module_->globals[index];
- current_init_expr_ = &global->init_expr;
- return Result::Ok;
+ return BeginInitExpr(&global->init_expr);
}
Result BinaryReaderIR::EndGlobalInitExpr(Index index) {
- current_init_expr_ = nullptr;
- return Result::Ok;
+ return EndInitExpr();
}
Result BinaryReaderIR::OnExportCount(Index count) {
}
Result BinaryReaderIR::OnFunctionBodyCount(Index count) {
- assert(module_->num_func_imports + count == module_->funcs.size());
+ // Can hit this case on a malformed module if we don't stop on first error.
+ if (module_->num_func_imports + count != module_->funcs.size()) {
+ PrintError(
+ "number of imported func + func count in code section does not match "
+ "actual number of funcs in module");
+ return Result::Error;
+ }
return Result::Ok;
}
return AppendExpr(MakeUnique<ReturnCallExpr>(Var(func_index)));
}
-Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index, Index table_index) {
+Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index,
+ Index table_index) {
auto expr = MakeUnique<ReturnCallIndirectExpr>();
SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation()));
expr->table = Var(table_index);
}
Result BinaryReaderIR::OnEndExpr() {
- LabelNode* label;
- Expr* expr;
- CHECK_RESULT(TopLabelExpr(&label, &expr));
- switch (label->label_type) {
- case LabelType::Block:
- cast<BlockExpr>(expr)->block.end_loc = GetLocation();
- break;
- case LabelType::Loop:
- cast<LoopExpr>(expr)->block.end_loc = GetLocation();
- break;
- case LabelType::If:
- cast<IfExpr>(expr)->true_.end_loc = GetLocation();
- break;
- case LabelType::Else:
- cast<IfExpr>(expr)->false_end_loc = GetLocation();
- break;
- case LabelType::Try:
- cast<TryExpr>(expr)->block.end_loc = GetLocation();
- break;
-
- case LabelType::Func:
- case LabelType::Catch:
- break;
+ if (label_stack_.size() > 1) {
+ LabelNode* label;
+ Expr* expr;
+ CHECK_RESULT(TopLabelExpr(&label, &expr));
+ switch (label->label_type) {
+ case LabelType::Block:
+ cast<BlockExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::Loop:
+ cast<LoopExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::If:
+ cast<IfExpr>(expr)->true_.end_loc = GetLocation();
+ break;
+ case LabelType::Else:
+ cast<IfExpr>(expr)->false_end_loc = GetLocation();
+ break;
+ case LabelType::Try:
+ cast<TryExpr>(expr)->block.end_loc = GetLocation();
+ break;
+
+ case LabelType::InitExpr:
+ case LabelType::Func:
+ case LabelType::Catch:
+ break;
+ }
}
return PopLabel();
}
Result BinaryReaderIR::OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) {
- return AppendExpr(MakeUnique<LoadExpr>(opcode, 1 << alignment_log2, offset));
+ return AppendExpr(
+ MakeUnique<LoadExpr>(opcode, Var(memidx), 1 << alignment_log2, offset));
}
Result BinaryReaderIR::OnLoopExpr(Type sig_type) {
return Result::Ok;
}
-Result BinaryReaderIR::OnMemoryCopyExpr() {
- return AppendExpr(MakeUnique<MemoryCopyExpr>());
+Result BinaryReaderIR::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) {
+ return AppendExpr(
+ MakeUnique<MemoryCopyExpr>(Var(srcmemidx), Var(destmemidx)));
}
Result BinaryReaderIR::OnDataDropExpr(Index segment) {
return AppendExpr(MakeUnique<DataDropExpr>(Var(segment)));
}
-Result BinaryReaderIR::OnMemoryFillExpr() {
- return AppendExpr(MakeUnique<MemoryFillExpr>());
+Result BinaryReaderIR::OnMemoryFillExpr(Index memidx) {
+ return AppendExpr(MakeUnique<MemoryFillExpr>(Var(memidx)));
}
-Result BinaryReaderIR::OnMemoryGrowExpr() {
- return AppendExpr(MakeUnique<MemoryGrowExpr>());
+Result BinaryReaderIR::OnMemoryGrowExpr(Index memidx) {
+ return AppendExpr(MakeUnique<MemoryGrowExpr>(Var(memidx)));
}
-Result BinaryReaderIR::OnMemoryInitExpr(Index segment) {
- return AppendExpr(MakeUnique<MemoryInitExpr>(Var(segment)));
+Result BinaryReaderIR::OnMemoryInitExpr(Index segment, Index memidx) {
+ return AppendExpr(MakeUnique<MemoryInitExpr>(Var(segment), Var(memidx)));
}
-Result BinaryReaderIR::OnMemorySizeExpr() {
- return AppendExpr(MakeUnique<MemorySizeExpr>());
+Result BinaryReaderIR::OnMemorySizeExpr(Index memidx) {
+ return AppendExpr(MakeUnique<MemorySizeExpr>(Var(memidx)));
}
Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) {
}
Result BinaryReaderIR::OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) {
- return AppendExpr(MakeUnique<StoreExpr>(opcode, 1 << alignment_log2, offset));
+ return AppendExpr(
+ MakeUnique<StoreExpr>(opcode, Var(memidx), 1 << alignment_log2, offset));
}
Result BinaryReaderIR::OnThrowExpr(Index tag_index) {
auto* try_ = cast<TryExpr>(label->context);
- if (catch_.IsCatchAll() && !try_->catches.empty() && try_->catches.back().IsCatchAll()) {
+ if (catch_.IsCatchAll() && !try_->catches.empty() &&
+ try_->catches.back().IsCatchAll()) {
PrintError("only one catch_all allowed in try block");
return Result::Error;
}
}
Result BinaryReaderIR::EndFunctionBody(Index index) {
- CHECK_RESULT(PopLabel());
current_func_ = nullptr;
return Result::Ok;
}
}
Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) {
- return AppendExpr(
- MakeUnique<SimdLoadLaneExpr>(opcode, 1 << alignment_log2, offset, value));
+ return AppendExpr(MakeUnique<SimdLoadLaneExpr>(
+ opcode, Var(memidx), 1 << alignment_log2, offset, value));
}
Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode,
- Address alignment_log2,
- Address offset,
- uint64_t value) {
- return AppendExpr(
- MakeUnique<SimdStoreLaneExpr>(opcode, 1 << alignment_log2, offset, value));
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ return AppendExpr(MakeUnique<SimdStoreLaneExpr>(
+ opcode, Var(memidx), 1 << alignment_log2, offset, value));
}
Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) {
return Result::Ok;
}
+Result BinaryReaderIR::BeginInitExpr(ExprList* expr) {
+ PushLabel(LabelType::InitExpr, expr);
+ return Result::Ok;
+}
+
Result BinaryReaderIR::BeginElemSegmentInitExpr(Index index) {
assert(index == module_->elem_segments.size() - 1);
ElemSegment* segment = module_->elem_segments[index];
- current_init_expr_ = &segment->offset;
+ return BeginInitExpr(&segment->offset);
+}
+
+Result BinaryReaderIR::EndInitExpr() {
return Result::Ok;
}
Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) {
- current_init_expr_ = nullptr;
- return Result::Ok;
+ return EndInitExpr();
}
Result BinaryReaderIR::OnElemSegmentElemType(Index index, Type elem_type) {
Type type) {
assert(segment_index == module_->elem_segments.size() - 1);
ElemSegment* segment = module_->elem_segments[segment_index];
- segment->elem_exprs.emplace_back(type);
+ Location loc = GetLocation();
+ ExprList init_expr;
+ init_expr.push_back(MakeUnique<RefNullExpr>(type, loc));
+ segment->elem_exprs.push_back(std::move(init_expr));
return Result::Ok;
}
Index func_index) {
assert(segment_index == module_->elem_segments.size() - 1);
ElemSegment* segment = module_->elem_segments[segment_index];
- segment->elem_exprs.emplace_back(Var(func_index, GetLocation()));
+ Location loc = GetLocation();
+ ExprList init_expr;
+ init_expr.push_back(MakeUnique<RefFuncExpr>(Var(func_index, loc), loc));
+ segment->elem_exprs.push_back(std::move(init_expr));
return Result::Ok;
}
Result BinaryReaderIR::BeginDataSegmentInitExpr(Index index) {
assert(index == module_->data_segments.size() - 1);
DataSegment* segment = module_->data_segments[index];
- current_init_expr_ = &segment->offset;
- return Result::Ok;
+ return BeginInitExpr(&segment->offset);
}
Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) {
- current_init_expr_ = nullptr;
- return Result::Ok;
+ return EndInitExpr();
}
Result BinaryReaderIR::OnDataSegmentData(Index index,
return Result::Ok;
}
+Result BinaryReaderIR::SetTagName(Index index, string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->tags.size()) {
+ PrintError("invalid tag index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Tag* tag = module_->tags[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->tag_bindings, MakeDollarName(name));
+ tag->name = dollar_name;
+ module_->global_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
Result BinaryReaderIR::OnFunctionName(Index index, string_view name) {
return SetFunctionName(index, name);
}
case NameSectionSubsection::Label:
case NameSectionSubsection::Type:
break;
+ case NameSectionSubsection::Tag:
+ SetTagName(index, name);
+ break;
case NameSectionSubsection::Global:
SetGlobalName(index, name);
break;
return Result::Ok;
}
-Result BinaryReaderIR::OnInitExprF32ConstExpr(Index index, uint32_t value) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<ConstExpr>(Const::F32(value, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprF64ConstExpr(Index index, uint64_t value) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<ConstExpr>(Const::F64(value, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprV128ConstExpr(Index index, v128 value) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<ConstExpr>(Const::V128(value, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprGlobalGetExpr(Index index,
- Index global_index) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<GlobalGetExpr>(Var(global_index, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprI32ConstExpr(Index index, uint32_t value) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<ConstExpr>(Const::I32(value, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprI64ConstExpr(Index index, uint64_t value) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<ConstExpr>(Const::I64(value, loc), loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprRefNull(Index index, Type type) {
- Location loc = GetLocation();
- current_init_expr_->push_back(MakeUnique<RefNullExpr>(type, loc));
- return Result::Ok;
-}
-
-Result BinaryReaderIR::OnInitExprRefFunc(Index index, Index func_index) {
- Location loc = GetLocation();
- current_init_expr_->push_back(
- MakeUnique<RefFuncExpr>(Var(func_index, loc), loc));
- return Result::Ok;
-}
-
Result BinaryReaderIR::OnLocalName(Index func_index,
Index local_index,
string_view name) {
return Result::Ok;
}
-Result BinaryReaderIR::OnDataSymbol(Index index, uint32_t flags,
- string_view name, Index segment,
- uint32_t offset, uint32_t size) {
+Result BinaryReaderIR::OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) {
if (name.empty()) {
return Result::Ok;
}
return Result::Ok;
}
-Result BinaryReaderIR::OnFunctionSymbol(Index index, uint32_t flags,
- string_view name, Index func_index) {
+Result BinaryReaderIR::OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) {
if (name.empty()) {
return Result::Ok;
}
return Result::Ok;
}
-Result BinaryReaderIR::OnGlobalSymbol(Index index, uint32_t flags,
- string_view name, Index global_index) {
+Result BinaryReaderIR::OnGlobalSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index global_index) {
return SetGlobalName(global_index, name);
}
-Result BinaryReaderIR::OnSectionSymbol(Index index, uint32_t flags,
+Result BinaryReaderIR::OnSectionSymbol(Index index,
+ uint32_t flags,
Index section_index) {
return Result::Ok;
}
return Result::Ok;
}
-Result BinaryReaderIR::OnTableSymbol(Index index, uint32_t flags,
- string_view name, Index table_index) {
+Result BinaryReaderIR::OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index table_index) {
return SetTableName(index, name);
}
if (type.IsIndex()) {
LOGF_NOINDENT("typeidx[%d]", type.GetIndex());
} else {
- LOGF_NOINDENT("%s", type.GetName());
+ LOGF_NOINDENT("%s", type.GetName().c_str());
}
}
SPrintLimits(buf, sizeof(buf), elem_limits);
LOGF("OnImportTable(import_index: %" PRIindex ", table_index: %" PRIindex
", elem_type: %s, %s)\n",
- import_index, table_index, elem_type.GetName(), buf);
+ import_index, table_index, elem_type.GetName().c_str(), buf);
return reader_->OnImportTable(import_index, module_name, field_name,
table_index, elem_type, elem_limits);
}
LOGF("OnImportGlobal(import_index: %" PRIindex ", global_index: %" PRIindex
", type: %s, mutable: "
"%s)\n",
- import_index, global_index, type.GetName(), mutable_ ? "true" : "false");
+ import_index, global_index, type.GetName().c_str(),
+ mutable_ ? "true" : "false");
return reader_->OnImportGlobal(import_index, module_name, field_name,
global_index, type, mutable_);
}
char buf[100];
SPrintLimits(buf, sizeof(buf), elem_limits);
LOGF("OnTable(index: %" PRIindex ", elem_type: %s, %s)\n", index,
- elem_type.GetName(), buf);
+ elem_type.GetName().c_str(), buf);
return reader_->OnTable(index, elem_type, elem_limits);
}
Result BinaryReaderLogging::BeginGlobal(Index index, Type type, bool mutable_) {
LOGF("BeginGlobal(index: %" PRIindex ", type: %s, mutable: %s)\n", index,
- type.GetName(), mutable_ ? "true" : "false");
+ type.GetName().c_str(), mutable_ ? "true" : "false");
return reader_->BeginGlobal(index, type, mutable_);
}
Index count,
Type type) {
LOGF("OnLocalDecl(index: %" PRIindex ", count: %" PRIindex ", type: %s)\n",
- decl_index, count, type.GetName());
+ decl_index, count, type.GetName().c_str());
return reader_->OnLocalDecl(decl_index, count, type);
}
Result BinaryReaderLogging::OnElemSegmentElemType(Index index, Type elem_type) {
LOGF("OnElemSegmentElemType(index: %" PRIindex ", type: %s)\n", index,
- elem_type.GetName());
+ elem_type.GetName().c_str());
return reader_->OnElemSegmentElemType(index, elem_type);
}
return reader_->OnNameEntry(type, index, name);
}
-Result BinaryReaderLogging::OnInitExprF32ConstExpr(Index index,
- uint32_t value_bits) {
- float value;
- memcpy(&value, &value_bits, sizeof(value));
- LOGF("OnInitExprF32ConstExpr(index: %" PRIindex ", value: %g (0x04%x))\n",
- index, value, value_bits);
- return reader_->OnInitExprF32ConstExpr(index, value_bits);
-}
-
-Result BinaryReaderLogging::OnInitExprF64ConstExpr(Index index,
- uint64_t value_bits) {
- double value;
- memcpy(&value, &value_bits, sizeof(value));
- LOGF("OnInitExprF64ConstExpr(index: %" PRIindex " value: %g (0x08%" PRIx64
- "))\n",
- index, value, value_bits);
- return reader_->OnInitExprF64ConstExpr(index, value_bits);
-}
-
-Result BinaryReaderLogging::OnInitExprV128ConstExpr(Index index,
- v128 value_bits) {
- LOGF("OnInitExprV128ConstExpr(index: %" PRIindex
- " value: ( 0x%08x 0x%08x 0x%08x 0x%08x))\n",
- index, value_bits.u32(0), value_bits.u32(1), value_bits.u32(2),
- value_bits.u32(3));
- return reader_->OnInitExprV128ConstExpr(index, value_bits);
-}
-
-Result BinaryReaderLogging::OnInitExprI32ConstExpr(Index index,
- uint32_t value) {
- LOGF("OnInitExprI32ConstExpr(index: %" PRIindex ", value: %u)\n", index,
- value);
- return reader_->OnInitExprI32ConstExpr(index, value);
-}
-
-Result BinaryReaderLogging::OnInitExprI64ConstExpr(Index index,
- uint64_t value) {
- LOGF("OnInitExprI64ConstExpr(index: %" PRIindex ", value: %" PRIu64 ")\n",
- index, value);
- return reader_->OnInitExprI64ConstExpr(index, value);
-}
-
Result BinaryReaderLogging::OnDylinkInfo(uint32_t mem_size,
uint32_t mem_align,
uint32_t table_size,
return reader_->OnDylinkNeeded(so_name);
}
-Result BinaryReaderLogging::OnRelocCount(Index count,
- Index section_index) {
+Result BinaryReaderLogging::OnDylinkExport(string_view name, uint32_t flags) {
+ LOGF("OnDylinkExport(name: " PRIstringview ", flags: 0x%x)\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags);
+ return reader_->OnDylinkExport(name, flags);
+}
+
+Result BinaryReaderLogging::OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) {
+ LOGF("OnDylinkImport(module: " PRIstringview ", name: " PRIstringview
+ ", flags: 0x%x)\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module), WABT_PRINTF_STRING_VIEW_ARG(name),
+ flags);
+ return reader_->OnDylinkImport(module, name, flags);
+}
+
+Result BinaryReaderLogging::OnRelocCount(Index count, Index section_index) {
LOGF("OnRelocCount(count: %" PRIindex ", section: %" PRIindex ")\n", count,
section_index);
return reader_->OnRelocCount(count, section_index);
return reader_->OnReloc(type, offset, index, addend);
}
-Result BinaryReaderLogging::OnSymbol(Index symbol_index,
- SymbolType type,
- uint32_t flags) {
- LOGF("OnSymbol(type: %s flags: 0x%x)\n", GetSymbolTypeName(type), flags);
- return reader_->OnSymbol(symbol_index, type, flags);
+Result BinaryReaderLogging::OnFeature(uint8_t prefix, string_view name) {
+ LOGF("OnFeature(prefix: '%c', name: '" PRIstringview "')\n", prefix,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnFeature(prefix, name);
}
Result BinaryReaderLogging::OnDataSymbol(Index index,
string_view name,
Index table_index) {
LOGF("OnTableSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex
- ")\n",
+ ")\n",
WABT_PRINTF_STRING_VIEW_ARG(name), flags, table_index);
return reader_->OnTableSymbol(index, flags, name, table_index);
}
return reader_->name(value); \
}
-#define DEFINE_TYPE(name) \
- Result BinaryReaderLogging::name(Type type) { \
- LOGF(#name "(%s)\n", type.GetName()); \
- return reader_->name(type); \
+#define DEFINE_TYPE(name) \
+ Result BinaryReaderLogging::name(Type type) { \
+ LOGF(#name "(%s)\n", type.GetName().c_str()); \
+ return reader_->name(type); \
}
#define DEFINE_INDEX_DESC(name, desc) \
return reader_->name(value); \
}
-#define DEFINE_INDEX_TYPE(name) \
- Result BinaryReaderLogging::name(Index value, Type type) { \
- LOGF(#name "(index: %" PRIindex ", type: %s)\n", value, type.GetName()); \
- return reader_->name(value, type); \
+#define DEFINE_INDEX_TYPE(name) \
+ Result BinaryReaderLogging::name(Index value, Type type) { \
+ LOGF(#name "(index: %" PRIindex ", type: %s)\n", value, \
+ type.GetName().c_str()); \
+ return reader_->name(value, type); \
}
#define DEFINE_INDEX_INDEX(name, desc0, desc1) \
return reader_->name(opcode, alignment_log2, offset); \
}
+#define DEFINE_MEMORY_LOAD_STORE_OPCODE(name) \
+ Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \
+ Address alignment_log2, Address offset) { \
+ LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \
+ ", align log2: %" PRIaddress ", offset: %" PRIaddress ")\n", \
+ opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset); \
+ return reader_->name(opcode, memidx, alignment_log2, offset); \
+ }
+
#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \
- Result BinaryReaderLogging::name(Opcode opcode, Address alignment_log2, \
- Address offset, uint64_t value) { \
- LOGF(#name "(opcode: \"%s\" (%u), align log2: %" PRIaddress \
- ", offset: %" PRIaddress ", lane: %" PRIu64 ")\n", \
- opcode.GetName(), opcode.GetCode(), alignment_log2, offset, value); \
- return reader_->name(opcode, alignment_log2, offset, value); \
+ Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \
+ Address alignment_log2, Address offset, \
+ uint64_t value) { \
+ LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \
+ ", align log2: %" PRIaddress ", offset: %" PRIaddress \
+ ", lane: %" PRIu64 ")\n", \
+ opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset, \
+ value); \
+ return reader_->name(opcode, memidx, alignment_log2, offset, value); \
}
#define DEFINE0(name) \
DEFINE0(OnEndExpr)
DEFINE_INDEX_DESC(OnGlobalGetExpr, "index")
DEFINE_INDEX_DESC(OnGlobalSetExpr, "index")
-DEFINE_LOAD_STORE_OPCODE(OnLoadExpr);
+DEFINE_MEMORY_LOAD_STORE_OPCODE(OnLoadExpr);
DEFINE_INDEX_DESC(OnLocalGetExpr, "index")
DEFINE_INDEX_DESC(OnLocalSetExpr, "index")
DEFINE_INDEX_DESC(OnLocalTeeExpr, "index")
-DEFINE0(OnMemoryCopyExpr)
+DEFINE_INDEX_INDEX(OnMemoryCopyExpr, "src_memory_index", "dest_memory_index")
DEFINE_INDEX(OnDataDropExpr)
-DEFINE0(OnMemoryFillExpr)
-DEFINE0(OnMemoryGrowExpr)
-DEFINE_INDEX(OnMemoryInitExpr)
-DEFINE0(OnMemorySizeExpr)
+DEFINE_INDEX(OnMemoryFillExpr)
+DEFINE_INDEX(OnMemoryGrowExpr)
+DEFINE_INDEX_INDEX(OnMemoryInitExpr, "segment_index", "memory_index")
+DEFINE_INDEX(OnMemorySizeExpr)
DEFINE_INDEX_INDEX(OnTableCopyExpr, "dst_index", "src_index")
DEFINE_INDEX(OnElemDropExpr)
DEFINE_INDEX_INDEX(OnTableInitExpr, "segment_index", "table_index")
DEFINE0(OnReturnExpr)
DEFINE_LOAD_STORE_OPCODE(OnLoadSplatExpr);
DEFINE_LOAD_STORE_OPCODE(OnLoadZeroExpr);
-DEFINE_LOAD_STORE_OPCODE(OnStoreExpr);
+DEFINE_MEMORY_LOAD_STORE_OPCODE(OnStoreExpr);
DEFINE_INDEX_DESC(OnThrowExpr, "tag_index")
DEFINE0(OnUnreachableExpr)
DEFINE_OPCODE(OnUnaryExpr)
DEFINE_BEGIN(BeginRelocSection)
DEFINE_END(EndRelocSection)
-DEFINE_INDEX_INDEX(OnInitExprGlobalGetExpr, "index", "global_index")
-DEFINE_INDEX_TYPE(OnInitExprRefNull)
-DEFINE_INDEX_INDEX(OnInitExprRefFunc, "index", "func_index")
-
DEFINE_BEGIN(BeginDylinkSection)
DEFINE_INDEX(OnDylinkNeededCount)
+DEFINE_INDEX(OnDylinkExportCount)
+DEFINE_INDEX(OnDylinkImportCount)
DEFINE_END(EndDylinkSection)
+DEFINE_BEGIN(BeginTargetFeaturesSection)
+DEFINE_INDEX(OnFeatureCount)
+DEFINE_END(EndTargetFeaturesSection)
+
DEFINE_BEGIN(BeginLinkingSection)
DEFINE_INDEX(OnSymbolCount)
DEFINE_INDEX(OnSegmentInfoCount)
return reader_->OnOpcodeType(type);
}
-Result BinaryReaderLogging::OnEndFunc() {
- return reader_->OnEndFunc();
-}
-
} // namespace wabt
Result OnDropExpr() override;
Result OnElseExpr() override;
Result OnEndExpr() override;
- Result OnEndFunc() override;
Result OnF32ConstExpr(uint32_t value_bits) override;
Result OnF64ConstExpr(uint64_t value_bits) override;
Result OnV128ConstExpr(v128 value_bits) override;
Result OnI64ConstExpr(uint64_t value) override;
Result OnIfExpr(Type sig_type) override;
Result OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnLocalGetExpr(Index local_index) override;
Result OnLocalSetExpr(Index local_index) override;
Result OnLocalTeeExpr(Index local_index) override;
Result OnLoopExpr(Type sig_type) override;
- Result OnMemoryCopyExpr() override;
+ Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override;
Result OnDataDropExpr(Index segment_index) override;
- Result OnMemoryFillExpr() override;
- Result OnMemoryGrowExpr() override;
- Result OnMemoryInitExpr(Index segment_index) override;
- Result OnMemorySizeExpr() override;
+ Result OnMemoryFillExpr(Index memidx) override;
+ Result OnMemoryGrowExpr(Index memidx) override;
+ Result OnMemoryInitExpr(Index segment_index, Index memidx) override;
+ Result OnMemorySizeExpr(Index memidx) override;
Result OnTableCopyExpr(Index dst_index, Index src_index) override;
Result OnElemDropExpr(Index segment_index) override;
Result OnTableInitExpr(Index segment_index, Index table_index) override;
Result OnReturnExpr() override;
Result OnSelectExpr(Index result_count, Type* result_types) override;
Result OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnThrowExpr(Index tag_index) override;
Result EndCodeSection() override;
Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
uint32_t table_align) override;
Result OnDylinkNeededCount(Index count) override;
Result OnDylinkNeeded(string_view needed) override;
+ Result OnDylinkImportCount(Index count) override;
+ Result OnDylinkExportCount(Index count) override;
+ Result OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) override;
+ Result OnDylinkExport(string_view name, uint32_t flags) override;
Result EndDylinkSection() override;
+ Result BeginTargetFeaturesSection(Offset size) override;
+ Result OnFeatureCount(Index count) override;
+ Result OnFeature(uint8_t prefix, string_view name) override;
+ Result EndTargetFeaturesSection() override;
+
Result BeginLinkingSection(Offset size) override;
Result OnSymbolCount(Index count) override;
- Result OnSymbol(Index sybmol_index, SymbolType type, uint32_t flags) override;
Result OnDataSymbol(Index index,
uint32_t flags,
string_view name,
Result OnTagType(Index index, Index sig_index) override;
Result EndTagSection() override;
- Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprV128ConstExpr(Index index, v128 value) override;
- Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
- Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprRefNull(Index index, Type type) override;
- Result OnInitExprRefFunc(Index index, Index func_index) override;
-
private:
void Indent();
void Dedent();
TypeMut* fields) override {
return Result::Ok;
}
- Result OnArrayType(Index index, TypeMut field) override {
- return Result::Ok;
- }
+ Result OnArrayType(Index index, TypeMut field) override { return Result::Ok; }
Result EndTypeSection() override { return Result::Ok; }
/* Import section */
Result OnAtomicWaitExpr(Opcode, Address, Address) override {
return Result::Ok;
}
- Result OnAtomicFenceExpr(uint32_t) override {
- return Result::Ok;
- }
+ Result OnAtomicFenceExpr(uint32_t) override { return Result::Ok; }
Result OnAtomicNotifyExpr(Opcode, Address, Address) override {
return Result::Ok;
}
return Result::Ok;
}
Result OnCallExpr(Index func_index) override { return Result::Ok; }
- Result OnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; }
+ Result OnCallIndirectExpr(Index sig_index, Index table_index) override {
+ return Result::Ok;
+ }
Result OnCallRefExpr() override { return Result::Ok; }
Result OnCatchExpr(Index tag_index) override { return Result::Ok; }
Result OnCatchAllExpr() override { return Result::Ok; }
Result OnDropExpr() override { return Result::Ok; }
Result OnElseExpr() override { return Result::Ok; }
Result OnEndExpr() override { return Result::Ok; }
- Result OnEndFunc() override { return Result::Ok; }
Result OnF32ConstExpr(uint32_t value_bits) override { return Result::Ok; }
Result OnF64ConstExpr(uint64_t value_bits) override { return Result::Ok; }
Result OnV128ConstExpr(v128 value_bits) override { return Result::Ok; }
Result OnI64ConstExpr(uint64_t value) override { return Result::Ok; }
Result OnIfExpr(Type sig_type) override { return Result::Ok; }
Result OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override {
return Result::Ok;
Result OnLocalSetExpr(Index local_index) override { return Result::Ok; }
Result OnLocalTeeExpr(Index local_index) override { return Result::Ok; }
Result OnLoopExpr(Type sig_type) override { return Result::Ok; }
- Result OnMemoryCopyExpr() override { return Result::Ok; }
+ Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override {
+ return Result::Ok;
+ }
Result OnDataDropExpr(Index segment_index) override { return Result::Ok; }
- Result OnMemoryFillExpr() override { return Result::Ok; }
- Result OnMemoryGrowExpr() override { return Result::Ok; }
- Result OnMemoryInitExpr(Index segment_index) override { return Result::Ok; }
- Result OnMemorySizeExpr() override { return Result::Ok; }
+ Result OnMemoryFillExpr(Index memidx) override { return Result::Ok; }
+ Result OnMemoryGrowExpr(Index memidx) override { return Result::Ok; }
+ Result OnMemoryInitExpr(Index segment_index, Index memidx) override {
+ return Result::Ok;
+ }
+ Result OnMemorySizeExpr(Index memidx) override { return Result::Ok; }
Result OnTableCopyExpr(Index dst_index, Index src_index) override {
return Result::Ok;
}
Result OnNopExpr() override { return Result::Ok; }
Result OnRethrowExpr(Index depth) override { return Result::Ok; }
Result OnReturnCallExpr(Index sig_index) override { return Result::Ok; }
- Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; }
+ Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override {
+ return Result::Ok;
+ }
Result OnReturnExpr() override { return Result::Ok; }
Result OnSelectExpr(Index result_count, Type* result_types) override {
return Result::Ok;
}
Result OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override {
return Result::Ok;
return Result::Ok;
}
Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override {
return Result::Ok;
}
Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override {
}
Result OnDylinkNeededCount(Index count) override { return Result::Ok; }
Result OnDylinkNeeded(string_view so_name) override { return Result::Ok; }
+ Result OnDylinkImportCount(Index count) override { return Result::Ok; }
+ Result OnDylinkExportCount(Index count) override { return Result::Ok; }
+ Result OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) override {
+ return Result::Ok;
+ }
+ Result OnDylinkExport(string_view name, uint32_t flags) override {
+ return Result::Ok;
+ }
Result EndDylinkSection() override { return Result::Ok; }
+ /* target_features section */
+ Result BeginTargetFeaturesSection(Offset size) override { return Result::Ok; }
+ Result OnFeatureCount(Index count) override { return Result::Ok; }
+ Result OnFeature(uint8_t prefix, string_view name) override {
+ return Result::Ok;
+ }
+ Result EndTargetFeaturesSection() override { return Result::Ok; }
+
/* Linking section */
Result BeginLinkingSection(Offset size) override { return Result::Ok; }
Result OnSymbolCount(Index count) override { return Result::Ok; }
- Result OnSymbol(Index sybmol_index,
- SymbolType type,
- uint32_t flags) override {
- return Result::Ok;
- }
Result OnDataSymbol(Index index,
uint32_t flags,
string_view name,
return Result::Ok;
}
Result EndLinkingSection() override { return Result::Ok; }
-
- /* InitExpr - used by elem, data and global sections; these functions are
- * only called between calls to Begin*InitExpr and End*InitExpr */
- Result OnInitExprF32ConstExpr(Index index, uint32_t value) override {
- return Result::Ok;
- }
- Result OnInitExprF64ConstExpr(Index index, uint64_t value) override {
- return Result::Ok;
- }
- Result OnInitExprV128ConstExpr(Index index, v128 value) override {
- return Result::Ok;
- }
- Result OnInitExprGlobalGetExpr(Index index, Index global_index) override {
- return Result::Ok;
- }
- Result OnInitExprI32ConstExpr(Index index, uint32_t value) override {
- return Result::Ok;
- }
- Result OnInitExprI64ConstExpr(Index index, uint64_t value) override {
- return Result::Ok;
- }
- Result OnInitExprRefNull(Index index, Type type) override {
- return Result::Ok;
- }
- Result OnInitExprRefFunc(Index index, Index func_index) override {
- return Result::Ok;
- }
};
} // namespace wabt
protected:
string_view GetFunctionName(Index index) const;
string_view GetGlobalName(Index index) const;
+ string_view GetLocalName(Index function_index, Index local_index) const;
string_view GetSectionName(Index index) const;
string_view GetTagName(Index index) const;
string_view GetSymbolName(Index index) const;
return objdump_state_->global_names.Get(index);
}
+string_view BinaryReaderObjdumpBase::GetLocalName(Index function_index,
+ Index local_index) const {
+ return objdump_state_->local_names.Get(function_index, local_index);
+}
+
string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const {
return objdump_state_->section_names.Get(index);
}
return Result::Ok;
}
+ Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) override {
+ objdump_state_->function_param_counts[index] = param_count;
+ return Result::Ok;
+ }
+
Result OnNameEntry(NameSectionSubsection type,
Index index,
string_view name) override {
case NameSectionSubsection::DataSegment:
SetSegmentName(index, name);
break;
+ case NameSectionSubsection::Tag:
+ SetTagName(index, name);
+ break;
default:
break;
}
return Result::Ok;
}
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) override {
+ SetLocalName(function_index, local_index, local_name);
+ return Result::Ok;
+ }
+
Result OnSymbolCount(Index count) override {
objdump_state_->symtab.resize(count);
return Result::Ok;
protected:
void SetFunctionName(Index index, string_view name);
void SetGlobalName(Index index, string_view name);
+ void SetLocalName(Index function_index, Index local_index, string_view name);
void SetTagName(Index index, string_view name);
void SetTableName(Index index, string_view name);
void SetSegmentName(Index index, string_view name);
objdump_state_->global_names.Set(index, name);
}
+void BinaryReaderObjdumpPrepass::SetLocalName(Index function_index,
+ Index local_index,
+ string_view name) {
+ objdump_state_->local_names.Set(function_index, local_index, name);
+}
+
void BinaryReaderObjdumpPrepass::SetTagName(Index index, string_view name) {
objdump_state_->tag_names.Set(index, name);
}
std::string BlockSigToString(Type type) const;
Result BeginFunctionBody(Index index, Offset size) override;
+ Result EndFunctionBody(Index index) override;
Result OnLocalDeclCount(Index count) override;
Result OnLocalDecl(Index decl_index, Index count, Type type) override;
Result OnOpcodeIndexIndex(Index value, Index value2) override;
Result OnOpcodeUint32(uint32_t value) override;
Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override;
- Result OnOpcodeUint32Uint32Uint32(uint32_t value, uint32_t value2, uint32_t value3) override;
+ Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) override;
Result OnOpcodeUint64(uint64_t value) override;
Result OnOpcodeF32(uint32_t value) override;
Result OnOpcodeF64(uint64_t value) override;
Index default_target_depth) override;
Result OnDelegateExpr(Index) override;
Result OnEndExpr() override;
- Result OnEndFunc() override;
private:
- void LogOpcode(size_t data_size, const char* fmt, ...);
+ void LogOpcode(const char* fmt, ...);
Opcode current_opcode = Opcode::Unreachable;
Offset current_opcode_offset = 0;
Offset last_opcode_end = 0;
int indent_level = 0;
Index next_reloc = 0;
+ Index current_function_index = 0;
Index local_index_ = 0;
+ bool in_function_body = false;
};
std::string BinaryReaderObjdumpDisassemble::BlockSigToString(Type type) const {
}
Result BinaryReaderObjdumpDisassemble::OnOpcode(Opcode opcode) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
if (options_->debug) {
const char* opcode_name = opcode.GetName();
err_stream_->Writef("on_opcode: %#" PRIzx ": %s\n", state->offset,
Opcode missing_opcode = Opcode::FromCode(data_[last_opcode_end]);
const char* opcode_name = missing_opcode.GetName();
fprintf(stderr,
- "warning: %#" PRIzx " missing opcode callback at %#" PRIzx
+ "error: %#" PRIzx " missing opcode callback at %#" PRIzx
" (%#02x=%s)\n",
state->offset, last_opcode_end + 1, data_[last_opcode_end],
opcode_name);
#define IMMEDIATE_OCTET_COUNT 9
Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) {
- local_index_ = 0;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
current_opcode_offset = state->offset;
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index,
Index count,
Type type) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
Offset offset = current_opcode_offset;
size_t data_size = state->offset - offset;
}
local_index_ += count;
- printf("] type=%s\n", type.GetName());
+ printf("] type=%s\n", type.GetName().c_str());
last_opcode_end = current_opcode_offset + data_size;
current_opcode_offset = last_opcode_end;
return Result::Ok;
}
-void BinaryReaderObjdumpDisassemble::LogOpcode(size_t data_size,
- const char* fmt,
- ...) {
+void BinaryReaderObjdumpDisassemble::LogOpcode(const char* fmt, ...) {
+ // BinaryReaderObjdumpDisassemble is only used to disassembly function bodies
+ // so this should never be called for instructions outside of function bodies
+ // (i.e. init expresions).
+ assert(in_function_body);
+ const Offset immediate_len = state->offset - current_opcode_offset;
const Offset opcode_size = current_opcode.GetLength();
- const Offset total_size = opcode_size + data_size;
+ const Offset total_size = opcode_size + immediate_len;
// current_opcode_offset has already read past this opcode; rewind it by the
// size of this opcode, which may be more than one byte.
Offset offset = current_opcode_offset - opcode_size;
printf("\n");
}
- last_opcode_end = offset_end;
+ last_opcode_end = state->offset;
// Print relocation after then full (potentially multi-line) instruction.
if (options_->relocs &&
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeBare() {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
LogOpcode(0, nullptr);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
string_view name;
if (current_opcode == Opcode::Call &&
!(name = GetFunctionName(value)).empty()) {
- LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if (current_opcode == Opcode::Throw &&
+ !(name = GetTagName(value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
WABT_PRINTF_STRING_VIEW_ARG(name));
} else if ((current_opcode == Opcode::GlobalGet ||
current_opcode == Opcode::GlobalSet) &&
!(name = GetGlobalName(value)).empty()) {
- LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if ((current_opcode == Opcode::LocalGet ||
+ current_opcode == Opcode::LocalSet) &&
+ !(name = GetLocalName(current_function_index, value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
WABT_PRINTF_STRING_VIEW_ARG(name));
} else {
- LogOpcode(immediate_len, "%d", value);
+ LogOpcode("%d", value);
}
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value,
Index value2) {
- Offset immediate_len = state->offset - current_opcode_offset;
- LogOpcode(immediate_len, "%" PRIindex " %" PRIindex, value, value2);
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%" PRIindex " %" PRIindex, value, value2);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
string_view name;
if (current_opcode == Opcode::DataDrop &&
!(name = GetSegmentName(value)).empty()) {
- LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ LogOpcode("%d <" PRIstringview ">", value,
WABT_PRINTF_STRING_VIEW_ARG(name));
} else {
- LogOpcode(immediate_len, "%u", value);
+ LogOpcode("%u", value);
}
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value,
uint32_t value2) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body)
+ return Result::Ok;
string_view name;
if (current_opcode == Opcode::MemoryInit &&
!(name = GetSegmentName(value)).empty()) {
- LogOpcode(immediate_len, "%u %u <" PRIstringview ">", value, value2,
+ LogOpcode("%u %u <" PRIstringview ">", value, value2,
WABT_PRINTF_STRING_VIEW_ARG(name));
} else {
- LogOpcode(immediate_len, "%u %u", value, value2);
+ LogOpcode("%u %u", value, value2);
}
return Result::Ok;
}
uint32_t value,
uint32_t value2,
uint32_t value3) {
- Offset immediate_len = state->offset - current_opcode_offset;
- LogOpcode(immediate_len, "%u %u %u", value, value2, value3);
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%u %u %u", value, value2, value3);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) {
- Offset immediate_len = state->offset - current_opcode_offset;
- LogOpcode(immediate_len, "%" PRId64, value);
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%" PRId64, value);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
char buffer[WABT_MAX_FLOAT_HEX];
WriteFloatHex(buffer, sizeof(buffer), value);
- LogOpcode(immediate_len, buffer);
+ LogOpcode(buffer);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
char buffer[WABT_MAX_DOUBLE_HEX];
WriteDoubleHex(buffer, sizeof(buffer), value);
- LogOpcode(immediate_len, buffer);
+ LogOpcode(buffer);
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
// v128 is always dumped as i32x4:
- LogOpcode(immediate_len, "0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0),
- value.u32(1), value.u32(2), value.u32(3));
+ LogOpcode("0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0), value.u32(1),
+ value.u32(2), value.u32(3));
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeType(Type type) {
- Offset immediate_len = state->offset - current_opcode_offset;
- LogOpcode(immediate_len, type.GetRefKindName());
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ if (current_opcode == Opcode::SelectT) {
+ LogOpcode(type.GetName().c_str());
+ } else {
+ LogOpcode(type.GetRefKindName());
+ }
return Result::Ok;
}
Index num_targets,
Index* target_depths,
Index default_target_depth) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
std::string buffer = std::string();
for (Index i = 0; i < num_targets; i++) {
}
buffer.append(std::to_string(default_target_depth));
- LogOpcode(immediate_len, "%s", buffer.c_str());
+ LogOpcode("%s", buffer.c_str());
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
// Because `delegate` ends the block we need to dedent here, and
// we don't need to dedent it in LogOpcode.
if (indent_level > 0) {
return Result::Ok;
}
-Result BinaryReaderObjdumpDisassemble::OnEndFunc() {
- LogOpcode(0, nullptr);
- return Result::Ok;
-}
-
Result BinaryReaderObjdumpDisassemble::OnEndExpr() {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
if (indent_level > 0) {
indent_level--;
}
printf(":\n");
last_opcode_end = 0;
+ in_function_body = true;
+ current_function_index = index;
+ local_index_ = objdump_state_->function_param_counts[index];
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::EndFunctionBody(Index index) {
+ assert(in_function_body);
+ in_function_body = false;
return Result::Ok;
}
Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) {
- Offset immediate_len = state->offset - current_opcode_offset;
+ if (!in_function_body) {
+ return Result::Ok;
+ }
if (sig_type != Type::Void) {
- LogOpcode(immediate_len, "%s", BlockSigToString(sig_type).c_str());
+ LogOpcode("%s", BlockSigToString(sig_type).c_str());
} else {
- LogOpcode(immediate_len, nullptr);
+ LogOpcode(nullptr);
}
indent_level++;
return Result::Ok;
Result OnFunctionBodyCount(Index count) override;
Result BeginFunctionBody(Index index, Offset size) override;
- Result BeginElemSection(Offset size) override {
- in_elem_section_ = true;
- return Result::Ok;
- }
- Result EndElemSection() override {
- in_elem_section_ = false;
- return Result::Ok;
- }
-
Result OnElemSegmentCount(Index count) override;
Result BeginElemSegment(Index index,
Index table_index,
Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
Index func_index) override;
- Result BeginDataSection(Offset size) override {
- in_data_section_ = true;
+ Result BeginElemSegmentInitExpr(Index index) override {
+ reading_elem_init_expr_ = true;
return Result::Ok;
}
- Result EndDataSection() override {
- in_data_section_ = false;
+
+ Result EndElemSegmentInitExpr(Index index) override {
+ reading_elem_init_expr_ = false;
+ return Result::Ok;
+ }
+
+ Result BeginDataSegmentInitExpr(Index index) override {
+ reading_data_init_expr_ = true;
+ return Result::Ok;
+ }
+
+ Result EndDataSegmentInitExpr(Index index) override {
+ reading_data_init_expr_ = false;
+ return Result::Ok;
+ }
+
+ Result BeginGlobalInitExpr(Index index) override {
+ reading_global_init_expr_ = true;
+ return Result::Ok;
+ }
+
+ Result EndGlobalInitExpr(Index index) override {
+ reading_global_init_expr_ = false;
return Result::Ok;
}
Index index,
string_view name) override;
- Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprV128ConstExpr(Index index, v128 value) override;
- Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
- Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprRefNull(Index index, Type type) override;
- Result OnInitExprRefFunc(Index index, Index func_index) override;
-
Result OnDylinkInfo(uint32_t mem_size,
uint32_t mem_align_log2,
uint32_t table_size,
uint32_t table_align_log2) override;
Result OnDylinkNeededCount(Index count) override;
Result OnDylinkNeeded(string_view so_name) override;
+ Result OnDylinkImportCount(Index count) override;
+ Result OnDylinkExportCount(Index count) override;
+ Result OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) override;
+ Result OnDylinkExport(string_view name, uint32_t flags) override;
Result OnRelocCount(Index count, Index section_index) override;
Result OnReloc(RelocType type,
Index index,
uint32_t addend) override;
+ Result OnFeature(uint8_t prefix, string_view name) override;
+
Result OnSymbolCount(Index count) override;
Result OnDataSymbol(Index index,
uint32_t flags,
Result OnComdatCount(Index count) override;
Result OnComdatBegin(string_view name, uint32_t flags, Index count) override;
Result OnComdatEntry(ComdatType kind, Index index) override;
- Result EndLinkingSection() override { return Result::Ok; }
Result OnTagCount(Index count) override;
Result OnTagType(Index index, Index sig_index) override;
+ Result OnI32ConstExpr(uint32_t value) override;
+ Result OnI64ConstExpr(uint64_t value) override;
+ Result OnF32ConstExpr(uint32_t value) override;
+ Result OnF64ConstExpr(uint64_t value) override;
+ Result OnGlobalGetExpr(Index global_index) override;
+
private:
- Result InitExprToConstOffset(const InitExpr& expr, uint32_t* out_offset);
+ Result InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset);
Result HandleInitExpr(const InitExpr& expr);
bool ShouldPrintDetails();
void PrintDetails(const char* fmt, ...);
Index elem_index_ = 0;
Index table_index_ = 0;
Index next_data_reloc_ = 0;
- bool in_data_section_ = false;
- bool in_elem_section_ = false;
+ bool reading_elem_init_expr_ = false;
+ bool reading_data_init_expr_ = false;
+ bool reading_global_init_expr_ = false;
InitExpr data_init_expr_;
InitExpr elem_init_expr_;
uint8_t data_flags_ = 0;
uint8_t elem_flags_ = 0;
Index data_mem_index_ = 0;
- uint32_t data_offset_ = 0;
- uint32_t elem_offset_ = 0;
+ uint64_t data_offset_ = 0;
+ uint64_t elem_offset_ = 0;
+
+ bool ReadingInitExpr() {
+ return reading_elem_init_expr_ || reading_data_init_expr_ ||
+ reading_global_init_expr_;
+ }
};
BinaryReaderObjdump::BinaryReaderObjdump(const uint8_t* data,
return Result::Error;
}
- if (options_->relocs) {
+ if (options_->relocs && ShouldPrintDetails()) {
if (next_data_reloc_ != objdump_state_->data_relocations.size()) {
- err_stream_->Writef("Data reloctions outside of segments\n");
+ err_stream_->Writef("Data reloctions outside of segments!:\n");
+ for (size_t i = next_data_reloc_;
+ i < objdump_state_->data_relocations.size(); i++) {
+ const Reloc& reloc = objdump_state_->data_relocations[i];
+ PrintRelocation(reloc, reloc.offset);
+ }
+
return Result::Error;
}
}
if (i != 0) {
printf(", ");
}
- printf("%s", param_types[i].GetName());
+ printf("%s", param_types[i].GetName().c_str());
}
printf(") -> ");
switch (result_count) {
printf("nil");
break;
case 1:
- printf("%s", result_types[0].GetName());
+ printf("%s", result_types[0].GetName().c_str());
break;
default:
printf("(");
if (i != 0) {
printf(", ");
}
- printf("%s", result_types[i].GetName());
+ printf("%s", result_types[i].GetName().c_str());
}
printf(")");
break;
if (fields[i].mutable_) {
printf(" (mut");
}
- printf(" %s", fields[i].type.GetName());
+ printf(" %s", fields[i].type.GetName().c_str());
if (fields[i].mutable_) {
printf(")");
}
if (field.mutable_) {
printf(" (mut");
}
- printf(" %s", field.type.GetName());
+ printf(" %s", field.type.GetName().c_str());
if (field.mutable_) {
printf(")");
}
Type elem_type,
const Limits* elem_limits) {
PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, table_index,
- elem_type.GetName(), elem_limits->initial);
+ elem_type.GetName().c_str(), elem_limits->initial);
if (elem_limits->has_max) {
PrintDetails(" max=%" PRId64, elem_limits->max);
}
Type type,
bool mutable_) {
PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index,
- type.GetName(), mutable_);
+ type.GetName().c_str(), mutable_);
PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
WABT_PRINTF_STRING_VIEW_ARG(module_name),
WABT_PRINTF_STRING_VIEW_ARG(field_name));
Type elem_type,
const Limits* elem_limits) {
PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index,
- elem_type.GetName(), elem_limits->initial);
+ elem_type.GetName().c_str(), elem_limits->initial);
if (elem_limits->has_max) {
PrintDetails(" max=%" PRId64, elem_limits->max);
}
Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefNull(Index segment_index,
Type type) {
- PrintDetails(" - elem[%" PRIindex "] = ref.null %s\n",
- elem_offset_ + elem_index_, type.GetName());
+ PrintDetails(" - elem[%" PRIzd "] = ref.null %s\n",
+ elem_offset_ + elem_index_, type.GetName().c_str());
elem_index_++;
return Result::Ok;
}
Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index,
Index func_index) {
- PrintDetails(" - elem[%" PRIindex "] = func[%" PRIindex "]",
+ PrintDetails(" - elem[%" PRIzd "] = func[%" PRIindex "]",
elem_offset_ + elem_index_, func_index);
auto name = GetFunctionName(func_index);
if (!name.empty()) {
}
Result BinaryReaderObjdump::BeginGlobal(Index index, Type type, bool mutable_) {
- PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index, type.GetName(),
- mutable_);
+ PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index,
+ type.GetName().c_str(), mutable_);
string_view name = GetGlobalName(index);
if (!name.empty()) {
PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
}
Result BinaryReaderObjdump::InitExprToConstOffset(const InitExpr& expr,
- uint32_t* out_offset) {
+ uint64_t* out_offset) {
switch (expr.type) {
case InitExprType::I32:
*out_offset = expr.value.i32;
break;
+ case InitExprType::I64:
+ *out_offset = expr.value.i64;
+ break;
case InitExprType::Global:
*out_offset = 0;
break;
- case InitExprType::I64:
case InitExprType::F32:
case InitExprType::F64:
case InitExprType::V128:
case InitExprType::FuncRef:
case InitExprType::NullRef:
- err_stream_->Writef("Segment/Elem offset must be an i32 init expr");
+ err_stream_->Writef("Invalid init expr for segment/elem offset");
return Result::Error;
break;
}
}
Result BinaryReaderObjdump::HandleInitExpr(const InitExpr& expr) {
- if (in_data_section_) {
+ if (reading_data_init_expr_) {
data_init_expr_ = expr;
return InitExprToConstOffset(expr, &data_offset_);
- } else if (in_elem_section_) {
+ } else if (reading_elem_init_expr_) {
elem_init_expr_ = expr;
return InitExprToConstOffset(expr, &elem_offset_);
- } else {
+ } else if (reading_global_init_expr_) {
PrintInitExpr(expr);
+ return Result::Ok;
+ } else {
+ WABT_UNREACHABLE;
}
- return Result::Ok;
-}
-
-Result BinaryReaderObjdump::OnInitExprF32ConstExpr(Index index,
- uint32_t value) {
- InitExpr expr;
- expr.type = InitExprType::F32;
- expr.value.f32 = value;
- HandleInitExpr(expr);
- return Result::Ok;
-}
-
-Result BinaryReaderObjdump::OnInitExprF64ConstExpr(Index index,
- uint64_t value) {
- InitExpr expr;
- expr.type = InitExprType::F64;
- expr.value.f64 = value;
- HandleInitExpr(expr);
- return Result::Ok;
-}
-
-Result BinaryReaderObjdump::OnInitExprV128ConstExpr(Index index, v128 value) {
- InitExpr expr;
- expr.type = InitExprType::V128;
- expr.value.v128_v = value;
- HandleInitExpr(expr);
- return Result::Ok;
}
-Result BinaryReaderObjdump::OnInitExprGlobalGetExpr(Index index,
- Index global_index) {
- InitExpr expr;
- expr.type = InitExprType::Global;
- expr.value.index = global_index;
- HandleInitExpr(expr);
+Result BinaryReaderObjdump::OnI32ConstExpr(uint32_t value) {
+ if (ReadingInitExpr()) {
+ InitExpr expr;
+ expr.type = InitExprType::I32;
+ expr.value.i32 = value;
+ return HandleInitExpr(expr);
+ }
return Result::Ok;
}
-Result BinaryReaderObjdump::OnInitExprI32ConstExpr(Index index,
- uint32_t value) {
- InitExpr expr;
- expr.type = InitExprType::I32;
- expr.value.i32 = value;
- HandleInitExpr(expr);
+Result BinaryReaderObjdump::OnI64ConstExpr(uint64_t value) {
+ if (ReadingInitExpr()) {
+ InitExpr expr;
+ expr.type = InitExprType::I64;
+ expr.value.i64 = value;
+ return HandleInitExpr(expr);
+ }
return Result::Ok;
}
-Result BinaryReaderObjdump::OnInitExprI64ConstExpr(Index index,
- uint64_t value) {
- InitExpr expr;
- expr.type = InitExprType::I64;
- expr.value.i64 = value;
- HandleInitExpr(expr);
+Result BinaryReaderObjdump::OnF32ConstExpr(uint32_t value) {
+ if (ReadingInitExpr()) {
+ InitExpr expr;
+ expr.type = InitExprType::F32;
+ expr.value.f32 = value;
+ return HandleInitExpr(expr);
+ }
return Result::Ok;
}
-Result BinaryReaderObjdump::OnInitExprRefNull(Index index, Type type) {
- InitExpr expr;
- expr.type = InitExprType::NullRef;
- expr.value.type = type;
- HandleInitExpr(expr);
+Result BinaryReaderObjdump::OnF64ConstExpr(uint64_t value) {
+ if (ReadingInitExpr()) {
+ InitExpr expr;
+ expr.type = InitExprType::F64;
+ expr.value.f64 = value;
+ return HandleInitExpr(expr);
+ }
return Result::Ok;
}
-Result BinaryReaderObjdump::OnInitExprRefFunc(Index index, Index func_index) {
- InitExpr expr{InitExprType::FuncRef, {func_index}};
- HandleInitExpr(expr);
+Result BinaryReaderObjdump::OnGlobalGetExpr(Index global_index) {
+ if (ReadingInitExpr()) {
+ InitExpr expr;
+ expr.type = InitExprType::Global;
+ expr.value.index = global_index;
+ return HandleInitExpr(expr);
+ }
return Result::Ok;
}
return Result::Ok;
}
+Result BinaryReaderObjdump::OnDylinkImportCount(Index count) {
+ PrintDetails(" - imports[%u]:\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkExportCount(Index count) {
+ PrintDetails(" - exports[%u]:\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkExport(string_view name, uint32_t flags) {
+ PrintDetails(" - " PRIstringview, WABT_PRINTF_STRING_VIEW_ARG(name));
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) {
+ PrintDetails(" - " PRIstringview "." PRIstringview,
+ WABT_PRINTF_STRING_VIEW_ARG(module),
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return PrintSymbolFlags(flags);
+}
+
Result BinaryReaderObjdump::OnDylinkNeeded(string_view so_name) {
PrintDetails(" - " PRIstringview "\n", WABT_PRINTF_STRING_VIEW_ARG(so_name));
return Result::Ok;
return Result::Ok;
}
+Result BinaryReaderObjdump::OnFeature(uint8_t prefix, string_view name) {
+ PrintDetails(" - [%c] " PRIstringview "\n", prefix,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
Result BinaryReaderObjdump::OnSymbolCount(Index count) {
PrintDetails(" - symbol table [count=%d]\n", count);
return Result::Ok;
PrintDetails(" no_strip");
flags &= ~WABT_SYMBOL_FLAG_NO_STRIP;
}
+ if (flags & WABT_SYMBOL_FLAG_TLS) {
+ PrintDetails(" tls");
+ flags &= ~WABT_SYMBOL_FLAG_TLS;
+ }
if (flags != 0) {
PrintDetails(" unknown_flags=%#x", flags);
}
string_view name,
Address alignment_log2,
uint32_t flags) {
- PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress,
- index, WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2);
+ PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2);
return PrintSegmentFlags(flags);
}
return Result::Ok;
}
-Result BinaryReaderObjdump::OnComdatBegin(string_view name, uint32_t flags, Index count) {
+Result BinaryReaderObjdump::OnComdatBegin(string_view name,
+ uint32_t flags,
+ Index count) {
PrintDetails(" - " PRIstringview ": [count=%d]\n",
WABT_PRINTF_STRING_VIEW_ARG(name), count);
return Result::Ok;
names[index] = name.to_string();
}
+string_view ObjdumpLocalNames::Get(Index function_index,
+ Index local_index) const {
+ auto iter = names.find(std::pair<Index, Index>(function_index, local_index));
+ if (iter == names.end())
+ return string_view();
+ return iter->second;
+}
+
+void ObjdumpLocalNames::Set(Index function_index,
+ Index local_index,
+ string_view name) {
+ names[std::pair<Index, Index>(function_index, local_index)] =
+ name.to_string();
+}
+
Result ReadBinaryObjdump(const uint8_t* data,
size_t size,
ObjdumpOptions* options,
std::map<Index, std::string> names;
};
+struct ObjdumpLocalNames {
+ string_view Get(Index function_index, Index local_index) const;
+ void Set(Index function_index, Index local_index, string_view name);
+
+ std::map<std::pair<Index, Index>, std::string> names;
+};
+
// read_binary_objdump uses this state to store information from previous runs
// and use it to display more useful information.
struct ObjdumpState {
ObjdumpNames tag_names;
ObjdumpNames segment_names;
ObjdumpNames table_names;
+ ObjdumpLocalNames local_names;
std::vector<ObjdumpSymbol> symtab;
+ std::map<Index, Index> function_param_counts;
};
Result ReadBinaryObjdump(const uint8_t* data,
break;
}
+ case Kind::V128: {
+ auto data = *GetData<v128>();
+ auto l0 = data.u32(0);
+ auto l1 = data.u32(1);
+ auto l2 = data.u32(2);
+ auto l3 = data.u32(3);
+ stream.Writef(" %u %u %u %u (0x%x 0x%x 0x%x 0x%x)", l0, l1, l2, l3, l0,
+ l1, l2, l3);
+ break;
+ }
+
case Kind::Uint32Uint32:
WriteArray<uint32_t>(
stream, [&stream](uint32_t value) { stream.Writef("%u", value); });
break;
+ case Kind::Uint32Uint32Uint32:
+ WriteArray<uint32_t>(
+ stream, [&stream](uint32_t value) { stream.Writef("%u", value); });
+ break;
+
case Kind::BlockSig: {
auto type = *GetData<Type>();
if (type.IsIndex()) {
stream.Writef(" type:%d", type.GetIndex());
} else if (type != Type::Void) {
- stream.Writef(" %s", type.GetName());
+ stream.Writef(" %s", type.GetName().c_str());
}
break;
}
Result OnOpcodeUint32(uint32_t value) override;
Result OnOpcodeIndex(Index value) override;
Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override;
+ Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) override;
Result OnOpcodeUint64(uint64_t value) override;
Result OnOpcodeF32(uint32_t value) override;
Result OnOpcodeF64(uint64_t value) override;
+ Result OnOpcodeV128(v128 value) override;
Result OnOpcodeBlockSig(Type sig_type) override;
Result OnBrTableExpr(Index num_targets,
Index* target_depths,
Index default_target_depth) override;
Result OnEndExpr() override;
- Result OnEndFunc() override;
private:
template <typename... Args>
return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32, array, 2);
}
+Result BinaryReaderOpcnt::OnOpcodeUint32Uint32Uint32(uint32_t value0,
+ uint32_t value1,
+ uint32_t value2) {
+ uint32_t array[3] = {value0, value1, value2};
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32Uint32, array,
+ 3);
+}
+
Result BinaryReaderOpcnt::OnOpcodeUint64(uint64_t value) {
return Emplace(current_opcode_, OpcodeInfo::Kind::Uint64, &value);
}
return Emplace(current_opcode_, OpcodeInfo::Kind::Float64, &value);
}
+Result BinaryReaderOpcnt::OnOpcodeV128(v128 value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::V128, &value);
+}
+
Result BinaryReaderOpcnt::OnOpcodeBlockSig(Type sig_type) {
return Emplace(current_opcode_, OpcodeInfo::Kind::BlockSig, &sig_type);
}
return Emplace(Opcode::End, OpcodeInfo::Kind::Bare);
}
-Result BinaryReaderOpcnt::OnEndFunc() {
- return Emplace(Opcode::End, OpcodeInfo::Kind::Bare);
-}
-
} // end anonymous namespace
Result ReadBinaryOpcnt(const void* data,
Float32,
Float64,
Uint32Uint32,
+ Uint32Uint32Uint32,
BlockSig,
BrTable,
+ V128,
};
explicit OpcodeInfo(Opcode, Kind);
Result ReadIndex(Index* index, const char* desc) WABT_WARN_UNUSED;
Result ReadOffset(Offset* offset, const char* desc) WABT_WARN_UNUSED;
Result ReadAlignment(Address* align_log2, const char* desc) WABT_WARN_UNUSED;
+ Result ReadMemidx(Index* memidx, const char* desc) WABT_WARN_UNUSED;
Result ReadCount(Index* index, const char* desc) WABT_WARN_UNUSED;
Result ReadField(TypeMut* out_value) WABT_WARN_UNUSED;
Index NumTotalFuncs();
- Result ReadInitExpr(Index index, Type required = Type::Any) WABT_WARN_UNUSED;
+ Result ReadInitExpr(Index index) WABT_WARN_UNUSED;
Result ReadTable(Type* out_elem_type,
Limits* out_elem_limits) WABT_WARN_UNUSED;
Result ReadMemory(Limits* out_page_limits) WABT_WARN_UNUSED;
Index memory,
const char* desc) WABT_WARN_UNUSED;
Result ReadFunctionBody(Offset end_offset) WABT_WARN_UNUSED;
+ // ReadInstructions either until and END instruction, or until
+ // the given end_offset.
+ Result ReadInstructions(bool stop_on_end,
+ Offset end_offset,
+ Opcode* final_opcode) WABT_WARN_UNUSED;
Result ReadNameSection(Offset section_size) WABT_WARN_UNUSED;
Result ReadRelocSection(Offset section_size) WABT_WARN_UNUSED;
Result ReadDylinkSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadDylink0Section(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadTargetFeaturesSections(Offset section_size) WABT_WARN_UNUSED;
Result ReadLinkingSection(Offset section_size) WABT_WARN_UNUSED;
Result ReadCustomSection(Index section_index,
Offset section_size) WABT_WARN_UNUSED;
Result BinaryReader::ReadType(Type* out_value, const char* desc) {
uint32_t type = 0;
CHECK_RESULT(ReadS32Leb128(&type, desc));
- *out_value = static_cast<Type>(type);
+ if (static_cast<Type::Enum>(type) == Type::Reference) {
+ uint32_t heap_type = 0;
+ CHECK_RESULT(ReadS32Leb128(&heap_type, desc));
+ *out_value = Type(Type::Reference, heap_type);
+ } else {
+ *out_value = static_cast<Type>(type);
+ }
return Result::Ok;
}
Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) {
uint32_t value;
CHECK_RESULT(ReadU32Leb128(&value, desc));
- if (value >= 32) {
+ if (value >= 128 ||
+ (value >= 32 && !options_.features.multi_memory_enabled())) {
PrintError("invalid %s: %u", desc, value);
return Result::Error;
}
return Result::Ok;
}
+Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) {
+ CHECK_RESULT(ReadIndex(memidx, desc));
+ ERROR_UNLESS(*memidx < memories.size(), "memory index %u out of range",
+ *memidx);
+ return Result::Ok;
+}
+
Result BinaryReader::ReadCount(Index* count, const char* desc) {
CHECK_RESULT(ReadIndex(count, desc));
case Type::ExternRef:
return options_.features.reference_types_enabled();
+ case Type::Reference:
+ return options_.features.function_references_enabled();
+
default:
return false;
}
return num_func_imports_ + num_function_signatures_;
}
-Result BinaryReader::ReadInitExpr(Index index, Type required) {
- Opcode opcode;
- CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
- ERROR_UNLESS_OPCODE_ENABLED(opcode);
-
- switch (opcode) {
- case Opcode::I32Const: {
- uint32_t value = 0;
- CHECK_RESULT(ReadS32Leb128(&value, "init_expr i32.const value"));
- CALLBACK(OnInitExprI32ConstExpr, index, value);
- break;
- }
-
- case Opcode::I64Const: {
- uint64_t value = 0;
- CHECK_RESULT(ReadS64Leb128(&value, "init_expr i64.const value"));
- CALLBACK(OnInitExprI64ConstExpr, index, value);
- break;
- }
-
- case Opcode::F32Const: {
- uint32_t value_bits = 0;
- CHECK_RESULT(ReadF32(&value_bits, "init_expr f32.const value"));
- CALLBACK(OnInitExprF32ConstExpr, index, value_bits);
- break;
- }
-
- case Opcode::F64Const: {
- uint64_t value_bits = 0;
- CHECK_RESULT(ReadF64(&value_bits, "init_expr f64.const value"));
- CALLBACK(OnInitExprF64ConstExpr, index, value_bits);
- break;
- }
-
- case Opcode::V128Const: {
- v128 value_bits;
- ZeroMemory(value_bits);
- CHECK_RESULT(ReadV128(&value_bits, "init_expr v128.const value"));
- CALLBACK(OnInitExprV128ConstExpr, index, value_bits);
- break;
- }
-
- case Opcode::GlobalGet: {
- Index global_index;
- CHECK_RESULT(ReadIndex(&global_index, "init_expr global.get index"));
- CALLBACK(OnInitExprGlobalGetExpr, index, global_index);
- break;
- }
-
- case Opcode::RefNull: {
- Type type;
- CHECK_RESULT(ReadRefType(&type, "ref.null type"));
- CALLBACK(OnInitExprRefNull, index, type);
- break;
- }
-
- case Opcode::RefFunc: {
- Index func_index;
- CHECK_RESULT(ReadIndex(&func_index, "init_expr ref.func index"));
- CALLBACK(OnInitExprRefFunc, index, func_index);
- break;
- }
-
- case Opcode::End:
- return Result::Ok;
-
- default:
- return ReportUnexpectedOpcode(opcode, "in initializer expression");
- }
-
- if (required == Type::I32 && opcode != Opcode::I32Const &&
- opcode != Opcode::GlobalGet) {
- PrintError("expected i32 init_expr");
- return Result::Error;
- }
- if (required == Type::I64 && opcode != Opcode::I64Const &&
- opcode != Opcode::GlobalGet) {
- PrintError("expected i64 init_expr");
- return Result::Error;
- }
-
- CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
- ERROR_UNLESS(opcode == Opcode::End,
- "expected END opcode after initializer expression");
- return Result::Ok;
+Result BinaryReader::ReadInitExpr(Index index) {
+ // Read instructions until END opcode is reached.
+ return ReadInstructions(/*stop_on_end=*/true, read_end_, NULL);
}
Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) {
}
Result BinaryReader::ReadFunctionBody(Offset end_offset) {
- bool seen_end_opcode = false;
+ Opcode final_opcode(Opcode::Invalid);
+ CHECK_RESULT(
+ ReadInstructions(/*stop_on_end=*/false, end_offset, &final_opcode));
+ ERROR_UNLESS(state_.offset == end_offset,
+ "function body longer than given size");
+ ERROR_UNLESS(final_opcode == Opcode::End,
+ "function body must end with END opcode");
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadInstructions(bool stop_on_end,
+ Offset end_offset,
+ Opcode* final_opcode) {
while (state_.offset < end_offset) {
Opcode opcode;
CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
CALLBACK(OnOpcode, opcode);
ERROR_UNLESS_OPCODE_ENABLED(opcode);
+ if (final_opcode) {
+ *final_opcode = opcode;
+ }
switch (opcode) {
case Opcode::Unreachable:
result_types_[i] = result_type;
}
- Type* result_types = num_results ? result_types_.data() : nullptr;
- CALLBACK(OnSelectExpr, num_results, result_types);
- CALLBACK0(OnOpcodeBare);
+ if (num_results) {
+ CALLBACK(OnSelectExpr, num_results, result_types_.data());
+ CALLBACK(OnOpcodeType, result_types_[0]);
+ } else {
+ CALLBACK(OnSelectExpr, 0, NULL);
+ CALLBACK0(OnOpcodeBare);
+ }
break;
}
break;
case Opcode::End:
- if (state_.offset == end_offset) {
- seen_end_opcode = true;
- CALLBACK0(OnEndFunc);
- } else {
- CALLBACK0(OnEndExpr);
+ CALLBACK0(OnEndExpr);
+ if (stop_on_end) {
+ return Result::Ok;
}
break;
CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
Address offset;
CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
- CALLBACK(OnLoadExpr, opcode, alignment_log2, offset);
- CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ Index memidx = 0;
+ if (alignment_log2 >> 6) {
+ ERROR_IF(!options_.features.multi_memory_enabled(),
+ "multi_memory not allowed");
+ CHECK_RESULT(ReadMemidx(&memidx, "store memidx"));
+ alignment_log2 = alignment_log2 & ((1 << 6) - 1);
+ }
+
+ CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset);
+ if (memidx) {
+ CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, memidx);
+ } else {
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ }
break;
}
CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment"));
Address offset;
CHECK_RESULT(ReadAddress(&offset, 0, "store offset"));
+ Index memidx = 0;
+ if (alignment_log2 >> 6) {
+ ERROR_IF(!options_.features.multi_memory_enabled(),
+ "multi_memory not allowed");
+ CHECK_RESULT(ReadMemidx(&memidx, "store memidx"));
+ alignment_log2 = alignment_log2 & ((1 << 6) - 1);
+ }
- CALLBACK(OnStoreExpr, opcode, alignment_log2, offset);
- CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset);
+ if (memidx) {
+ CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, memidx);
+ } else {
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ }
break;
}
case Opcode::MemorySize: {
- uint8_t reserved;
- CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
- ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
- CALLBACK0(OnMemorySizeExpr);
- CALLBACK(OnOpcodeUint32, reserved);
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx"));
+ }
+ CALLBACK(OnMemorySizeExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
break;
}
case Opcode::MemoryGrow: {
- uint8_t reserved;
- CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
- ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
- CALLBACK0(OnMemoryGrowExpr);
- CALLBACK(OnOpcodeUint32, reserved);
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx"));
+ }
+ CALLBACK(OnMemoryGrowExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
break;
}
case Opcode::V128Load64Lane: {
Address alignment_log2;
CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Index memidx = 0;
+ if (alignment_log2 >> 6) {
+ ERROR_IF(!options_.features.multi_memory_enabled(),
+ "multi_memory not allowed");
+ CHECK_RESULT(ReadMemidx(&memidx, "store memidx"));
+ alignment_log2 = alignment_log2 & ((1 << 6) - 1);
+ }
Address offset;
CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
uint8_t lane_val;
CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
- CALLBACK(OnSimdLoadLaneExpr, opcode, alignment_log2, offset, lane_val);
+ CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset,
+ lane_val);
CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val);
break;
}
case Opcode::V128Store64Lane: {
Address alignment_log2;
CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Index memidx = 0;
+ if (alignment_log2 >> 6) {
+ ERROR_IF(!options_.features.multi_memory_enabled(),
+ "multi_memory not allowed");
+ CHECK_RESULT(ReadMemidx(&memidx, "store memidx"));
+ alignment_log2 = alignment_log2 & ((1 << 6) - 1);
+ }
Address offset;
CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
uint8_t lane_val;
CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
- CALLBACK(OnSimdStoreLaneExpr, opcode, alignment_log2, offset, lane_val);
+ CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset,
+ lane_val);
CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val);
break;
}
ERROR_IF(data_count_ == kInvalidIndex,
"memory.init requires data count section");
CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
- uint8_t reserved;
- CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
- ERROR_UNLESS(reserved == 0, "reserved value must be 0");
- CALLBACK(OnMemoryInitExpr, segment);
- CALLBACK(OnOpcodeUint32Uint32, segment, reserved);
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx"));
+ }
+ CALLBACK(OnMemoryInitExpr, segment, memidx);
+ CALLBACK(OnOpcodeUint32Uint32, segment, memidx);
break;
}
}
case Opcode::MemoryFill: {
- uint8_t reserved;
- CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
- ERROR_UNLESS(reserved == 0, "reserved value must be 0");
- CALLBACK(OnMemoryFillExpr);
- CALLBACK(OnOpcodeUint32, reserved);
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx"));
+ }
+ CALLBACK(OnMemoryFillExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
break;
}
+
case Opcode::MemoryCopy: {
- uint8_t reserved;
- CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
- ERROR_UNLESS(reserved == 0, "reserved value must be 0");
- CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
- ERROR_UNLESS(reserved == 0, "reserved value must be 0");
- CALLBACK(OnMemoryCopyExpr);
- CALLBACK(OnOpcodeUint32Uint32, reserved, reserved);
+ Index srcmemidx = 0;
+ Index destmemidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx"));
+ CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex"));
+ }
+ CALLBACK(OnMemoryCopyExpr, srcmemidx, destmemidx);
+ CALLBACK(OnOpcodeUint32Uint32, srcmemidx, destmemidx);
break;
}
return ReportUnexpectedOpcode(opcode);
}
}
- ERROR_UNLESS(state_.offset == end_offset,
- "function body longer than given size");
- ERROR_UNLESS(seen_end_opcode, "function body must end with END opcode");
return Result::Ok;
}
case NameSectionSubsection::Global:
case NameSectionSubsection::ElemSegment:
case NameSectionSubsection::DataSegment:
+ case NameSectionSubsection::Tag:
if (subsection_size) {
Index num_names;
CHECK_RESULT(ReadCount(&num_names, "name count"));
return Result::Ok;
}
+Result BinaryReader::ReadDylink0Section(Offset section_size) {
+ CALLBACK(BeginDylinkSection, section_size);
+
+ while (state_.offset < read_end_) {
+ uint32_t dylink_type;
+ Offset subsection_size;
+ CHECK_RESULT(ReadU32Leb128(&dylink_type, "type"));
+ CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
+ size_t subsection_end = state_.offset + subsection_size;
+ ERROR_UNLESS(subsection_end <= read_end_,
+ "invalid sub-section size: extends past end");
+ ReadEndRestoreGuard guard(this);
+ read_end_ = subsection_end;
+
+ uint32_t count;
+ switch (static_cast<DylinkEntryType>(dylink_type)) {
+ case DylinkEntryType::MemInfo: {
+ uint32_t mem_size;
+ uint32_t mem_align;
+ uint32_t table_size;
+ uint32_t table_align;
+
+ CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
+ CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
+ CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
+ CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
+ CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
+ break;
+ }
+ case DylinkEntryType::Needed:
+ CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
+ CALLBACK(OnDylinkNeededCount, count);
+ while (count--) {
+ string_view so_name;
+ CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
+ CALLBACK(OnDylinkNeeded, so_name);
+ }
+ break;
+ case DylinkEntryType::ImportInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnDylinkImportCount, count);
+ for (Index i = 0; i < count; ++i) {
+ uint32_t flags = 0;
+ string_view module;
+ string_view field;
+ CHECK_RESULT(ReadStr(&module, "module"));
+ CHECK_RESULT(ReadStr(&field, "field"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
+ CALLBACK(OnDylinkImport, module, field, flags);
+ }
+ break;
+ case DylinkEntryType::ExportInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnDylinkExportCount, count);
+ for (Index i = 0; i < count; ++i) {
+ uint32_t flags = 0;
+ string_view name;
+ CHECK_RESULT(ReadStr(&name, "name"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
+ CALLBACK(OnDylinkExport, name, flags);
+ }
+ break;
+ default:
+ // Unknown subsection, skip it.
+ state_.offset = subsection_end;
+ break;
+ }
+ ERROR_UNLESS(state_.offset == subsection_end,
+ "unfinished sub-section (expected end: 0x%" PRIzx ")",
+ subsection_end);
+ }
+
+ CALLBACK0(EndDylinkSection);
+ return Result::Ok;
+}
+
Result BinaryReader::ReadDylinkSection(Offset section_size) {
CALLBACK(BeginDylinkSection, section_size);
uint32_t mem_size;
return Result::Ok;
}
+Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) {
+ CALLBACK(BeginTargetFeaturesSection, section_size);
+ uint32_t count;
+ CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
+ CALLBACK(OnFeatureCount, count);
+ while (count--) {
+ uint8_t prefix;
+ string_view name;
+ CHECK_RESULT(ReadU8(&prefix, "prefix"));
+ CHECK_RESULT(ReadStr(&name, "feature name"));
+ CALLBACK(OnFeature, prefix, name);
+ }
+ CALLBACK0(EndTargetFeaturesSection);
+ return Result::Ok;
+}
+
Result BinaryReader::ReadLinkingSection(Offset section_size) {
CALLBACK(BeginLinkingSection, section_size);
uint32_t version;
CHECK_RESULT(ReadU32Leb128(&kind, "sym type"));
CHECK_RESULT(ReadU32Leb128(&flags, "sym flags"));
SymbolType sym_type = static_cast<SymbolType>(kind);
- CALLBACK(OnSymbol, i, sym_type, flags);
switch (sym_type) {
case SymbolType::Function:
case SymbolType::Global:
if (options_.read_debug_names && section_name == WABT_BINARY_SECTION_NAME) {
CHECK_RESULT(ReadNameSection(section_size));
did_read_names_section_ = true;
+ } else if (section_name == WABT_BINARY_SECTION_DYLINK0) {
+ CHECK_RESULT(ReadDylink0Section(section_size));
} else if (section_name == WABT_BINARY_SECTION_DYLINK) {
CHECK_RESULT(ReadDylinkSection(section_size));
} else if (section_name.rfind(WABT_BINARY_SECTION_RELOC, 0) == 0) {
// Reloc sections always begin with "reloc."
CHECK_RESULT(ReadRelocSection(section_size));
+ } else if (section_name == WABT_BINARY_SECTION_TARGET_FEATURES) {
+ CHECK_RESULT(ReadTargetFeaturesSections(section_size));
} else if (section_name == WABT_BINARY_SECTION_LINKING) {
CHECK_RESULT(ReadLinkingSection(section_size));
} else {
} else {
uint8_t type;
CHECK_RESULT(ReadU8(&type, "type form"));
- ERROR_UNLESS(type == 0x60, "unexpected type form (got " "%#x" ")", type);
+ ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type);
form = Type::Func;
}
if (!(flags & SegPassive)) {
CALLBACK(BeginElemSegmentInitExpr, i);
- CHECK_RESULT(ReadInitExpr(i, Type::I32));
+ CHECK_RESULT(ReadInitExpr(i));
CALLBACK(EndElemSegmentInitExpr, i);
}
CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
ERROR_UNLESS(kind == ExternalKind::Func,
"segment elem type must be func (%s)",
- elem_type.GetName());
+ elem_type.GetName().c_str());
elem_type = Type::FuncRef;
}
}
}
CALLBACK(BeginDataSegment, i, memory_index, flags);
if (!(flags & SegPassive)) {
+ ERROR_UNLESS(memories.size() > 0, "no memory to copy data to");
CALLBACK(BeginDataSegmentInitExpr, i);
- CHECK_RESULT(ReadInitExpr(i, memories[0].IndexType()));
+ CHECK_RESULT(ReadInitExpr(i));
CALLBACK(EndDataSegmentInitExpr, i);
}
virtual Result OnDropExpr() = 0;
virtual Result OnElseExpr() = 0;
virtual Result OnEndExpr() = 0;
- virtual Result OnEndFunc() = 0;
virtual Result OnF32ConstExpr(uint32_t value_bits) = 0;
virtual Result OnF64ConstExpr(uint64_t value_bits) = 0;
virtual Result OnV128ConstExpr(v128 value_bits) = 0;
virtual Result OnI64ConstExpr(uint64_t value) = 0;
virtual Result OnIfExpr(Type sig_type) = 0;
virtual Result OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) = 0;
virtual Result OnLocalGetExpr(Index local_index) = 0;
virtual Result OnLocalSetExpr(Index local_index) = 0;
virtual Result OnLocalTeeExpr(Index local_index) = 0;
virtual Result OnLoopExpr(Type sig_type) = 0;
- virtual Result OnMemoryCopyExpr() = 0;
+ virtual Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) = 0;
virtual Result OnDataDropExpr(Index segment_index) = 0;
- virtual Result OnMemoryFillExpr() = 0;
- virtual Result OnMemoryGrowExpr() = 0;
- virtual Result OnMemoryInitExpr(Index segment_index) = 0;
- virtual Result OnMemorySizeExpr() = 0;
+ virtual Result OnMemoryFillExpr(Index memidx) = 0;
+ virtual Result OnMemoryGrowExpr(Index memidx) = 0;
+ virtual Result OnMemoryInitExpr(Index segment_index, Index memidx) = 0;
+ virtual Result OnMemorySizeExpr(Index memidx) = 0;
virtual Result OnTableCopyExpr(Index dst_index, Index src_index) = 0;
virtual Result OnElemDropExpr(Index segment_index) = 0;
virtual Result OnTableInitExpr(Index segment_index, Index table_index) = 0;
Index table_index) = 0;
virtual Result OnSelectExpr(Index result_count, Type* result_types) = 0;
virtual Result OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) = 0;
virtual Result OnThrowExpr(Index tag_index) = 0;
virtual Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) = 0;
virtual Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) = 0;
virtual Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) = 0;
virtual Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) = 0;
/* Reloc section */
virtual Result BeginRelocSection(Offset size) = 0;
- virtual Result OnRelocCount(Index count,
- Index section_index) = 0;
+ virtual Result OnRelocCount(Index count, Index section_index) = 0;
virtual Result OnReloc(RelocType type,
Offset offset,
Index index,
uint32_t mem_align_log2,
uint32_t table_size,
uint32_t table_align_log2) = 0;
+ virtual Result OnDylinkImportCount(Index count) = 0;
+ virtual Result OnDylinkExportCount(Index count) = 0;
+ virtual Result OnDylinkImport(string_view module,
+ string_view name,
+ uint32_t flags) = 0;
+ virtual Result OnDylinkExport(string_view name, uint32_t flags) = 0;
virtual Result OnDylinkNeededCount(Index count) = 0;
virtual Result OnDylinkNeeded(string_view so_name) = 0;
virtual Result EndDylinkSection() = 0;
+ /* target_features section */
+ virtual Result BeginTargetFeaturesSection(Offset size) = 0;
+ virtual Result OnFeatureCount(Index count) = 0;
+ virtual Result OnFeature(uint8_t prefix, string_view name) = 0;
+ virtual Result EndTargetFeaturesSection() = 0;
+
/* Linking section */
virtual Result BeginLinkingSection(Offset size) = 0;
virtual Result OnSymbolCount(Index count) = 0;
- virtual Result OnSymbol(Index index, SymbolType type, uint32_t flags) = 0;
virtual Result OnDataSymbol(Index index,
uint32_t flags,
string_view name,
virtual Result OnTagType(Index index, Index sig_index) = 0;
virtual Result EndTagSection() = 0;
- /* InitExpr - used by elem, data and global sections; these functions are
- * only called between calls to Begin*InitExpr and End*InitExpr */
- virtual Result OnInitExprF32ConstExpr(Index index, uint32_t value) = 0;
- virtual Result OnInitExprF64ConstExpr(Index index, uint64_t value) = 0;
- virtual Result OnInitExprV128ConstExpr(Index index, v128 value) = 0;
- virtual Result OnInitExprGlobalGetExpr(Index index, Index global_index) = 0;
- virtual Result OnInitExprI32ConstExpr(Index index, uint32_t value) = 0;
- virtual Result OnInitExprI64ConstExpr(Index index, uint64_t value) = 0;
- virtual Result OnInitExprRefNull(Index index, Type type) = 0;
- virtual Result OnInitExprRefFunc(Index index, Index func_index) = 0;
-
const State* state = nullptr;
};
"assert_return",
"assert_trap",
"assert_exhaustion",
+ "assert_exception",
};
WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount);
void BinaryWriterSpec::WriteTypeObject(Type type) {
json_stream_->Writef("{");
WriteKey("type");
- WriteString(type.GetName());
+ WriteString(type.GetName().c_str());
json_stream_->Writef("}");
}
WriteString("v128");
WriteSeparator();
WriteKey("lane_type");
- WriteString(const_.lane_type().GetName());
+ WriteString(const_.lane_type().GetName().c_str());
WriteSeparator();
WriteKey("value");
json_stream_->Writef("[");
WriteActionResultType(*assert_exhaustion_command->action);
break;
}
+
+ case CommandType::AssertException: {
+ auto* assert_exception_command = cast<AssertExceptionCommand>(command);
+ WriteLocation(assert_exception_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_exception_command->action);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(*assert_exception_command->action);
+ break;
+ }
}
json_stream_->Writef("}");
}
void WriteType(Stream* stream, Type type, const char* desc) {
- WriteS32Leb128(stream, type, desc ? desc : type.GetName());
+ WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str());
+ if (type.IsReferenceWithIndex()) {
+ WriteS32Leb128(stream, type.GetReferenceIndex(),
+ desc ? desc : type.GetName().c_str());
+ }
}
void WriteLimits(Stream* stream, const Limits* limits) {
WriteU32Leb128(stream, limits->initial, "limits: initial");
if (limits->has_max) {
WriteU32Leb128(stream, limits->max, "limits: max");
- }
+ }
}
}
bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; }
bool defined() const { return !undefined(); }
bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; }
- bool explicit_name() const { return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME; }
+ bool explicit_name() const {
+ return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME;
+ }
bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; }
bool IsFunction() const { return type() == Function::type; }
Result EnsureUnique(const string_view& name) {
if (seen_names_.count(name)) {
- fprintf(stderr, "error: duplicate symbol when writing relocatable "
- "binary: %s\n", &name[0]);
+ fprintf(stderr,
+ "error: duplicate symbol when writing relocatable "
+ "binary: %s\n",
+ &name[0]);
return Result::Error;
}
seen_names_.insert(name);
};
template <typename T>
- Result AddSymbol(std::vector<Index>* map, string_view name,
- bool imported, bool exported, T&& sym) {
+ Result AddSymbol(std::vector<Index>* map,
+ string_view name,
+ bool imported,
+ bool exported,
+ T&& sym) {
uint8_t flags = 0;
if (imported) {
flags |= WABT_SYMBOL_FLAG_UNDEFINED;
for (const Export* export_ : module->exports) {
switch (export_->kind) {
- case ExternalKind::Func:
- exported_funcs.insert(module->GetFuncIndex(export_->var));
- break;
- case ExternalKind::Table:
- exported_tables.insert(module->GetTableIndex(export_->var));
- break;
- case ExternalKind::Memory:
- break;
- case ExternalKind::Global:
- exported_globals.insert(module->GetGlobalIndex(export_->var));
- break;
- case ExternalKind::Tag:
- exported_tags.insert(module->GetTagIndex(export_->var));
- break;
+ case ExternalKind::Func:
+ exported_funcs.insert(module->GetFuncIndex(export_->var));
+ break;
+ case ExternalKind::Table:
+ exported_tables.insert(module->GetTableIndex(export_->var));
+ break;
+ case ExternalKind::Memory:
+ break;
+ case ExternalKind::Global:
+ exported_globals.insert(module->GetGlobalIndex(export_->var));
+ break;
+ case ExternalKind::Tag:
+ exported_tags.insert(module->GetTagIndex(export_->var));
+ break;
}
}
template <typename T>
void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc);
template <typename T>
- void WriteSimdLoadStoreLaneExpr(const Func* func, const Expr* expr, const char* desc);
+ void WriteMemoryLoadStoreExpr(const Func* func,
+ const Expr* expr,
+ const char* desc);
+ template <typename T>
+ void WriteSimdLoadStoreLaneExpr(const Func* func,
+ const Expr* expr,
+ const char* desc);
void WriteExpr(const Func* func, const Expr* expr);
void WriteExprList(const Func* func, const ExprList& exprs);
void WriteInitExpr(const ExprList& expr);
Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var)
: module_->GetFuncTypeIndex(decl.sig);
assert(index != kInvalidIndex);
- WriteS32Leb128WithReloc(index, "block type function index", RelocType::TypeIndexLEB);
+ WriteS32Leb128WithReloc(index, "block type function index",
+ RelocType::TypeIndexLEB);
}
void BinaryWriter::WriteSectionHeader(const char* desc,
// Add a new reloc section if needed
if (!current_reloc_section_ ||
current_reloc_section_->section_index != section_count_) {
- reloc_sections_.emplace_back(GetSectionName(last_section_type_), section_count_);
+ reloc_sections_.emplace_back(GetSectionName(last_section_type_),
+ section_count_);
current_reloc_section_ = &reloc_sections_.back();
}
}
}
-void BinaryWriter::WriteTableNumberWithReloc(Index value,
- const char* desc) {
+void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) {
// Unless reference types are enabled, all references to tables refer to table
// 0, so no relocs need be emitted when making relocatable binaries.
if (options_.relocatable && options_.features.reference_types_enabled()) {
WriteU32Leb128(stream_, typed_expr->offset, desc);
}
+template <typename T>
+void BinaryWriter::WriteMemoryLoadStoreExpr(const Func* func,
+ const Expr* expr,
+ const char* desc) {
+ auto* typed_expr = cast<T>(expr);
+ WriteOpcode(stream_, typed_expr->opcode);
+ Address align = typed_expr->opcode.GetAlignment(typed_expr->align);
+ Index memidx = module_->GetMemoryIndex(typed_expr->memidx);
+ if (memidx != 0) {
+ stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment");
+ WriteU32Leb128(stream_, typed_expr->offset, desc);
+ WriteU32Leb128(stream_, memidx, "memidx");
+ } else {
+ stream_->WriteU8(log2_u32(align), "alignment");
+ WriteU32Leb128(stream_, typed_expr->offset, desc);
+ }
+};
+
template <typename T>
void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func,
const Expr* expr,
WriteU32Leb128(stream_, depth, "break depth for default");
break;
}
- case ExprType::Call:{
+ case ExprType::Call: {
Index index = module_->GetFuncIndex(cast<CallExpr>(expr)->var);
WriteOpcode(stream_, Opcode::Call);
WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
break;
}
- case ExprType::CallIndirect:{
+ case ExprType::CallIndirect: {
Index sig_index =
- module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl);
+ module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl);
Index table_index =
- module_->GetTableIndex(cast<CallIndirectExpr>(expr)->table);
+ module_->GetTableIndex(cast<CallIndirectExpr>(expr)->table);
WriteOpcode(stream_, Opcode::CallIndirect);
- WriteU32Leb128WithReloc(sig_index, "signature index", RelocType::TypeIndexLEB);
+ WriteU32Leb128WithReloc(sig_index, "signature index",
+ RelocType::TypeIndexLEB);
WriteTableNumberWithReloc(table_index, "table index");
break;
}
- case ExprType::CallRef:{
+ case ExprType::CallRef: {
WriteOpcode(stream_, Opcode::CallRef);
break;
}
Index table_index =
module_->GetTableIndex(cast<ReturnCallIndirectExpr>(expr)->table);
WriteOpcode(stream_, Opcode::ReturnCallIndirect);
- WriteU32Leb128WithReloc(sig_index, "signature index", RelocType::TypeIndexLEB);
+ WriteU32Leb128WithReloc(sig_index, "signature index",
+ RelocType::TypeIndexLEB);
WriteTableNumberWithReloc(table_index, "table index");
break;
}
break;
}
case ExprType::Load:
- WriteLoadStoreExpr<LoadExpr>(func, expr, "load offset");
+ WriteMemoryLoadStoreExpr<LoadExpr>(func, expr, "load offset");
break;
case ExprType::LocalGet: {
Index index = GetLocalIndex(func, cast<LocalGetExpr>(expr)->var);
WriteExprList(func, cast<LoopExpr>(expr)->block.exprs);
WriteOpcode(stream_, Opcode::End);
break;
- case ExprType::MemoryCopy:
+ case ExprType::MemoryCopy: {
+ Index srcmemidx =
+ module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->srcmemidx);
+ Index destmemidx =
+ module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->destmemidx);
WriteOpcode(stream_, Opcode::MemoryCopy);
- WriteU32Leb128(stream_, 0, "memory.copy reserved");
- WriteU32Leb128(stream_, 0, "memory.copy reserved");
+ WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx");
+ WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx");
break;
+ }
case ExprType::DataDrop: {
- Index index =
- module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var);
+ Index index = module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var);
WriteOpcode(stream_, Opcode::DataDrop);
WriteU32Leb128(stream_, index, "data.drop segment");
has_data_segment_instruction_ = true;
break;
}
- case ExprType::MemoryFill:
+ case ExprType::MemoryFill: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryFillExpr>(expr)->memidx);
WriteOpcode(stream_, Opcode::MemoryFill);
- WriteU32Leb128(stream_, 0, "memory.fill reserved");
+ WriteU32Leb128(stream_, memidx, "memory.fill memidx");
break;
- case ExprType::MemoryGrow:
+ }
+ case ExprType::MemoryGrow: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryGrowExpr>(expr)->memidx);
WriteOpcode(stream_, Opcode::MemoryGrow);
- WriteU32Leb128(stream_, 0, "memory.grow reserved");
+ WriteU32Leb128(stream_, memidx, "memory.grow memidx");
break;
+ }
case ExprType::MemoryInit: {
Index index =
module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var);
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryInitExpr>(expr)->memidx);
WriteOpcode(stream_, Opcode::MemoryInit);
WriteU32Leb128(stream_, index, "memory.init segment");
- WriteU32Leb128(stream_, 0, "memory.init reserved");
+ WriteU32Leb128(stream_, memidx, "memory.init memidx");
has_data_segment_instruction_ = true;
break;
}
- case ExprType::MemorySize:
+ case ExprType::MemorySize: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemorySizeExpr>(expr)->memidx);
WriteOpcode(stream_, Opcode::MemorySize);
- WriteU32Leb128(stream_, 0, "memory.size reserved");
+ WriteU32Leb128(stream_, memidx, "memory.size memidx");
break;
+ }
case ExprType::TableCopy: {
auto* copy_expr = cast<TableCopyExpr>(expr);
Index dst = module_->GetTableIndex(copy_expr->dst_table);
break;
}
case ExprType::ElemDrop: {
- Index index =
- module_->GetElemSegmentIndex(cast<ElemDropExpr>(expr)->var);
+ Index index = module_->GetElemSegmentIndex(cast<ElemDropExpr>(expr)->var);
WriteOpcode(stream_, Opcode::ElemDrop);
WriteU32Leb128(stream_, index, "elem.drop segment");
break;
break;
}
case ExprType::TableGet: {
- Index index =
- module_->GetTableIndex(cast<TableGetExpr>(expr)->var);
+ Index index = module_->GetTableIndex(cast<TableGetExpr>(expr)->var);
WriteOpcode(stream_, Opcode::TableGet);
WriteTableNumberWithReloc(index, "table.get table index");
break;
}
case ExprType::TableSet: {
- Index index =
- module_->GetTableIndex(cast<TableSetExpr>(expr)->var);
+ Index index = module_->GetTableIndex(cast<TableSetExpr>(expr)->var);
WriteOpcode(stream_, Opcode::TableSet);
WriteTableNumberWithReloc(index, "table.set table index");
break;
}
case ExprType::TableGrow: {
- Index index =
- module_->GetTableIndex(cast<TableGrowExpr>(expr)->var);
+ Index index = module_->GetTableIndex(cast<TableGrowExpr>(expr)->var);
WriteOpcode(stream_, Opcode::TableGrow);
WriteTableNumberWithReloc(index, "table.grow table index");
break;
}
case ExprType::TableSize: {
- Index index =
- module_->GetTableIndex(cast<TableSizeExpr>(expr)->var);
+ Index index = module_->GetTableIndex(cast<TableSizeExpr>(expr)->var);
WriteOpcode(stream_, Opcode::TableSize);
WriteTableNumberWithReloc(index, "table.size table index");
break;
}
case ExprType::TableFill: {
- Index index =
- module_->GetTableIndex(cast<TableFillExpr>(expr)->var);
+ Index index = module_->GetTableIndex(cast<TableFillExpr>(expr)->var);
WriteOpcode(stream_, Opcode::TableFill);
WriteTableNumberWithReloc(index, "table.fill table index");
break;
break;
}
case ExprType::Store:
- WriteLoadStoreExpr<StoreExpr>(func, expr, "store offset");
+ WriteMemoryLoadStoreExpr<StoreExpr>(func, expr, "store offset");
break;
case ExprType::Throw:
WriteOpcode(stream_, Opcode::Throw);
break;
case TryKind::Delegate:
WriteOpcode(stream_, Opcode::Delegate);
- WriteU32Leb128(stream_,
- GetLabelVarDepth(&try_expr->delegate_target),
+ WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target),
"delegate depth");
break;
case TryKind::Plain:
// preceeded by length
WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems");
if (flags & SegUseElemExprs) {
- for (const ElemExpr& elem_expr : segment->elem_exprs) {
- switch (elem_expr.kind) {
- case ElemExprKind::RefNull:
- WriteOpcode(stream_, Opcode::RefNull);
- WriteType(stream_, elem_expr.type, "elem expr ref.null type");
- break;
-
- case ElemExprKind::RefFunc:
- WriteOpcode(stream_, Opcode::RefFunc);
- WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem expr function index");
- break;
- }
- WriteOpcode(stream_, Opcode::End);
+ for (const ExprList& elem_expr : segment->elem_exprs) {
+ WriteInitExpr(elem_expr);
}
} else {
- for (const ElemExpr& elem_expr : segment->elem_exprs) {
- assert(elem_expr.kind == ElemExprKind::RefFunc);
- WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem function index");
+ for (const ExprList& elem_expr : segment->elem_exprs) {
+ assert(elem_expr.size() == 1);
+ const Expr* expr = &elem_expr.front();
+ assert(expr->type() == ExprType::RefFunc);
+ WriteU32Leb128(stream_,
+ module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var),
+ "elem function index");
}
}
}
EndSection();
}
- if (options_.features.bulk_memory_enabled()) {
+ if (options_.features.bulk_memory_enabled() &&
+ module_->data_segments.size()) {
// Keep track of the data count section offset so it can be removed if
// it isn't needed.
data_count_start_ = stream_->offset();
auto func_start_offset = body_size_offset - last_section_payload_offset_;
auto func_end_offset = stream_->offset() - last_section_payload_offset_;
auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess,
- "FIXUP func body size");
+ "FIXUP func body size");
if (current_reloc_section_ && delta != 0) {
for (Reloc& reloc : current_reloc_section_->relocations) {
- if (reloc.offset >= func_start_offset && reloc.offset <= func_end_offset) {
+ if (reloc.offset >= func_start_offset &&
+ reloc.offset <= func_end_offset) {
reloc.offset += delta;
}
}
// Remove the DataCount section if there are no instructions that require it.
if (options_.features.bulk_memory_enabled() &&
- !has_data_segment_instruction_) {
+ module_->data_segments.size() && !has_data_segment_instruction_) {
Offset size = stream_->offset() - data_count_end_;
if (size) {
// If the DataCount section was followed by anything, assert that it's
uint8_t flags = segment->GetFlags(module_);
stream_->WriteU8(flags, "segment flags");
if (!(flags & SegPassive)) {
- assert(module_->GetMemoryIndex(segment->memory_var) == 0);
+ if (options_.features.multi_memory_enabled() &&
+ (flags & SegExplicitIndex)) {
+ WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var),
+ "memidx");
+ } else {
+ assert(module_->GetMemoryIndex(segment->memory_var) == 0);
+ }
WriteInitExpr(segment->offset);
}
WriteU32Leb128(stream_, segment->data.size(), "data segment size");
for (size_t i = 0; i < module_->funcs.size(); ++i) {
const Func* func = module_->funcs[i];
Index num_params_and_locals = func->GetNumParamsAndLocals();
+ MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings,
+ &index_to_name);
+ Index num_named = 0;
+ for (auto s : index_to_name) {
+ if (!s.empty()) {
+ num_named++;
+ }
+ }
WriteU32Leb128(stream_, i, "function index");
- WriteU32Leb128(stream_, num_params_and_locals, "num locals");
+ WriteU32Leb128(stream_, num_named, "num locals");
- MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings,
- &index_to_name);
for (size_t j = 0; j < num_params_and_locals; ++j) {
const std::string& name = index_to_name[j];
- wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
- WriteU32Leb128(stream_, j, "local index");
- WriteDebugName(stream_, name, desc);
+ if (!name.empty()) {
+ wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
+ WriteU32Leb128(stream_, j, "local index");
+ WriteDebugName(stream_, name, desc);
+ }
}
}
EndSubsection();
NameSectionSubsection::ElemSegment);
WriteNames<DataSegment>(module_->data_segments,
NameSectionSubsection::DataSegment);
+ WriteNames<Tag>(module_->tags, NameSectionSubsection::Tag);
EndSection();
}
return BinarySectionOrder::Name;
WABT_FOREACH_BINARY_SECTION(V)
#undef V
- default:
- WABT_UNREACHABLE;
+ default:
+ WABT_UNREACHABLE;
}
}
}
}
+// clang-format off
const char* NameSubsectionName[] = {
- "module",
- "function",
- "local",
- "label",
- "type",
- "table",
- "memory",
- "global",
- "elemseg",
- "dataseg",
+ "module",
+ "function",
+ "local",
+ "label",
+ "type",
+ "table",
+ "memory",
+ "global",
+ "elemseg",
+ "dataseg",
+ "tag",
};
+// clang-format on
const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) {
+ static_assert(WABT_ENUM_COUNT(NameSectionSubsection) ==
+ WABT_ARRAY_SIZE(NameSubsectionName),
+ "Malformed ExprTypeName array");
return NameSubsectionName[size_t(subsec)];
}
#define WABT_BINARY_SECTION_NAME "name"
#define WABT_BINARY_SECTION_RELOC "reloc"
#define WABT_BINARY_SECTION_LINKING "linking"
+#define WABT_BINARY_SECTION_TARGET_FEATURES "target_features"
#define WABT_BINARY_SECTION_DYLINK "dylink"
+#define WABT_BINARY_SECTION_DYLINK0 "dylink.0"
#define WABT_FOREACH_BINARY_SECTION(V) \
V(Custom, custom, 0) \
BinarySectionOrder GetSectionOrder(BinarySection);
const char* GetSectionName(BinarySection);
+// See
+// https://github.com/WebAssembly/extended-name-section/blob/main/proposals/extended-name-section/Overview.md
enum class NameSectionSubsection {
Module = 0,
Function = 1,
Global = 7,
ElemSegment = 8,
DataSegment = 9,
- Last = DataSegment,
+ // tag names are yet part of the extended-name-section proposal (because it
+ // only deals with naming things that are in the spec already). However, we
+ // include names for Tags in wabt using this enum value on the basis that tags
+ // can only exist when exceptions are enabled and that engines should ignore
+ // unknown name types.
+ Tag = 10,
+
+ First = Module,
+ Last = Tag,
};
const char* GetNameSectionSubsectionName(NameSectionSubsection subsec);
type_stack_size(type_stack_size),
used(used) {}
- bool HasValue() const {
- return !sig.empty();
- }
+ bool HasValue() const { return !sig.empty(); }
LabelType label_type;
const std::string& name;
Index offset = type_stack_.size() - label->type_stack_size - amount;
if (offset != 0) {
for (Index i = 0; i < amount; ++i) {
- Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ", StackVar(amount - i - 1), "; ");
+ Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ",
+ StackVar(amount - i - 1), "; ");
}
}
}
}
}
-
void CWriter::WriteFuncTypes() {
Write(Newline());
Writef("static u32 func_types[%" PRIzd "];", module_->types.size());
Write(Newline());
- assert(module_->memories.size() <= 1);
Index memory_index = 0;
for (const Memory* memory : module_->memories) {
bool is_import = memory_index < module_->num_memory_imports;
}
Write(Newline(), "static void init_memory(void) ", OpenBrace());
- if (memory && module_->num_memory_imports == 0) {
- uint32_t max =
- memory->page_limits.has_max ? memory->page_limits.max : 65536;
- Write("wasm_rt_allocate_memory(", ExternalPtr(memory->name), ", ",
- memory->page_limits.initial, ", ", max, ");", Newline());
+ if (module_->memories.size() > module_->num_memory_imports) {
+ Index memory_idx = module_->num_memory_imports;
+ for (Index i = memory_idx; i < module_->memories.size(); i++) {
+ memory = module_->memories[i];
+ uint32_t max =
+ memory->page_limits.has_max ? memory->page_limits.max : 65536;
+ Write("wasm_rt_allocate_memory(", ExternalPtr(memory->name), ", ",
+ memory->page_limits.initial, ", ", max, ");", Newline());
+ }
}
data_segment_index = 0;
for (const DataSegment* data_segment : module_->data_segments) {
}
Index elem_segment_index = 0;
for (const ElemSegment* elem_segment : module_->elem_segments) {
+ if (elem_segment->kind == SegmentKind::Passive) {
+ continue;
+ }
Write("offset = ");
WriteInitExpr(elem_segment->offset);
Write(";", Newline());
size_t i = 0;
- for (const ElemExpr& elem_expr : elem_segment->elem_exprs) {
+ for (const ExprList& elem_expr : elem_segment->elem_exprs) {
// We don't support the bulk-memory proposal here, so we know that we
// don't have any passive segments (where ref.null can be used).
- assert(elem_expr.kind == ElemExprKind::RefFunc);
- const Func* func = module_->GetFunc(elem_expr.var);
+ assert(elem_expr.size() == 1);
+ const Expr* expr = &elem_expr.front();
+ assert(expr->type() == ExprType::RefFunc);
+ const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var);
Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var);
Write(ExternalRef(table->name), ".data[offset + ", i,
"] = (wasm_rt_elem_t){func_types[", func_type_index,
- "], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), "};", Newline());
+ "], (wasm_rt_funcref_t)", ExternalPtr(func->name), "};", Newline());
++i;
}
++elem_segment_index;
break;
case ExprType::MemoryGrow: {
- assert(module_->memories.size() == 1);
- Memory* memory = module_->memories[0];
+ Memory* memory = module_->memories[module_->GetMemoryIndex(
+ cast<MemoryGrowExpr>(&expr)->memidx)];
Write(StackVar(0), " = wasm_rt_grow_memory(", ExternalPtr(memory->name),
", ", StackVar(0), ");", Newline());
}
case ExprType::MemorySize: {
- assert(module_->memories.size() == 1);
- Memory* memory = module_->memories[0];
+ Memory* memory = module_->memories[module_->GetMemoryIndex(
+ cast<MemorySizeExpr>(&expr)->memidx)];
PushType(Type::I32);
Write(StackVar(0), " = ", ExternalRef(memory->name), ".pages;",
WABT_UNREACHABLE;
}
- assert(module_->memories.size() == 1);
- Memory* memory = module_->memories[0];
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Type result_type = expr.opcode.GetResultType();
Write(StackVar(0, result_type), " = ", func, "(", ExternalPtr(memory->name),
WABT_UNREACHABLE;
}
- assert(module_->memories.size() == 1);
- Memory* memory = module_->memories[0];
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Write(func, "(", ExternalPtr(memory->name), ", (u64)(", StackVar(1), ")");
if (expr.offset != 0)
#include <cstdio>
#include <cstring>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#if COMPILER_IS_MSVC
#include <fcntl.h>
#include "config.h"
+#include "src/base-types.h"
#include "src/make-unique.h"
#include "src/result.h"
+#include "src/string-format.h"
#include "src/string-view.h"
#include "src/type.h"
#define WABT_USE(x) static_cast<void>(x)
-#define WABT_PAGE_SIZE 0x10000 /* 64k */
-#define WABT_MAX_PAGES32 0x10000 /* # of pages that fit in 32-bit address \
- space */
-#define WABT_MAX_PAGES64 0x1000000000000 /* # of pages that fit in 64-bit \
- address space */
+// 64k
+#define WABT_PAGE_SIZE 0x10000
+// # of pages that fit in 32-bit address space
+#define WABT_MAX_PAGES32 0x10000
+// # of pages that fit in 64-bit address space
+#define WABT_MAX_PAGES64 0x1000000000000
#define WABT_BYTES_TO_PAGES(x) ((x) >> 16)
#define WABT_ALIGN_UP_TO_PAGE(x) \
(((x) + WABT_PAGE_SIZE - 1) & ~(WABT_PAGE_SIZE - 1))
-#define PRIstringview "%.*s"
-#define WABT_PRINTF_STRING_VIEW_ARG(x) \
- static_cast<int>((x).length()), (x).data()
-
-#define PRItypecode "%s%#x"
-#define WABT_PRINTF_TYPE_CODE(x) \
- (static_cast<int32_t>(x) < 0 ? "-" : ""), std::abs(static_cast<int32_t>(x))
-
-#define WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE 128
-#define WABT_SNPRINTF_ALLOCA(buffer, len, format) \
- va_list args; \
- va_list args_copy; \
- va_start(args, format); \
- va_copy(args_copy, args); \
- char fixed_buf[WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE]; \
- char* buffer = fixed_buf; \
- size_t len = wabt_vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); \
- va_end(args); \
- if (len + 1 > sizeof(fixed_buf)) { \
- buffer = static_cast<char*>(alloca(len + 1)); \
- len = wabt_vsnprintf(buffer, len + 1, format, args_copy); \
- } \
- va_end(args_copy)
-
#define WABT_ENUM_COUNT(name) \
(static_cast<int>(name::Last) - static_cast<int>(name::First) + 1)
namespace wabt {
#if WABT_BIG_ENDIAN
- inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) {
- memcpy(static_cast<char*>(dst) + (dsize) - (len) - (doff),
- static_cast<const char*>(src) + (ssize) - (len) - (soff),
- (len));
- }
+inline void MemcpyEndianAware(void* dst,
+ const void* src,
+ size_t dsize,
+ size_t ssize,
+ size_t doff,
+ size_t soff,
+ size_t len) {
+ memcpy(static_cast<char*>(dst) + (dsize) - (len) - (doff),
+ static_cast<const char*>(src) + (ssize) - (len) - (soff), (len));
+}
#else
- inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) {
- memcpy(static_cast<char*>(dst) + (doff),
- static_cast<const char*>(src) + (soff),
- (len));
- }
+inline void MemcpyEndianAware(void* dst,
+ const void* src,
+ size_t dsize,
+ size_t ssize,
+ size_t doff,
+ size_t soff,
+ size_t len) {
+ memcpy(static_cast<char*>(dst) + (doff),
+ static_cast<const char*>(src) + (soff), (len));
+}
#endif
}
static_assert(sizeof(T) <= sizeof(v), "Invalid cast!");
assert((lane + 1) * sizeof(T) <= sizeof(v));
T result;
- wabt::MemcpyEndianAware(&result, v, sizeof(result), sizeof(v), 0, lane * sizeof(T), sizeof(result));
+ wabt::MemcpyEndianAware(&result, v, sizeof(result), sizeof(v), 0,
+ lane * sizeof(T), sizeof(result));
return result;
}
void From(int lane, T data) {
static_assert(sizeof(T) <= sizeof(v), "Invalid cast!");
assert((lane + 1) * sizeof(T) <= sizeof(v));
- wabt::MemcpyEndianAware(v, &data, sizeof(v), sizeof(data), lane * sizeof(T), 0, sizeof(data));
+ wabt::MemcpyEndianAware(v, &data, sizeof(v), sizeof(data), lane * sizeof(T),
+ 0, sizeof(data));
}
uint8_t v[16];
namespace wabt {
-typedef uint32_t Index; // An index into one of the many index spaces.
-typedef uint64_t Address; // An address or size in linear memory.
-typedef size_t Offset; // An offset into a host's file or memory buffer.
-
-static const Address kInvalidAddress = ~0;
-static const Index kInvalidIndex = ~0;
-static const Offset kInvalidOffset = ~0;
-
template <typename Dst, typename Src>
Dst WABT_VECTORCALL Bitcast(Src&& value) {
static_assert(sizeof(Src) == sizeof(Dst), "Bitcast sizes must match.");
placement.~T();
}
-inline std::string WABT_PRINTF_FORMAT(1, 2)
- StringPrintf(const char* format, ...) {
- va_list args;
- va_list args_copy;
- va_start(args, format);
- va_copy(args_copy, args);
- size_t len = wabt_vsnprintf(nullptr, 0, format, args) + 1; // For \0.
- std::vector<char> buffer(len);
- va_end(args);
- wabt_vsnprintf(buffer.data(), len, format, args_copy);
- va_end(args_copy);
- return std::string(buffer.data(), len - 1);
-}
-
enum class LabelType {
Func,
+ InitExpr,
Block,
Loop,
If,
MemoryAddressSLEB = 4, // e.g. Memory address in i32.const
MemoryAddressI32 = 5, // e.g. Memory address in DATA
TypeIndexLEB = 6, // e.g. Immediate type in call_indirect
- GlobalIndexLEB = 7, // e.g. Immediate of get_global inst
+ GlobalIndexLEB = 7, // e.g. Immediate of global.get inst
FunctionOffsetI32 = 8, // e.g. Code offset in DWARF metadata
SectionOffsetI32 = 9, // e.g. Section offset in DWARF metadata
TagIndexLEB = 10, // Used in throw instructions
SymbolTable = 8,
};
+enum class DylinkEntryType {
+ MemInfo = 1,
+ Needed = 2,
+ ExportInfo = 3,
+ ImportInfo = 4,
+};
+
enum class SymbolType {
Function = 0,
Data = 1,
#define WABT_SYMBOL_FLAG_EXPORTED 0x20
#define WABT_SYMBOL_FLAG_EXPLICIT_NAME 0x40
#define WABT_SYMBOL_FLAG_NO_STRIP 0x80
-#define WABT_SYMBOL_FLAG_MAX 0xff
+#define WABT_SYMBOL_FLAG_TLS 0x100
+#define WABT_SYMBOL_FLAG_MAX 0x1ff
#define WABT_SEGMENT_FLAG_STRINGS 0x1
#define WABT_SEGMENT_FLAG_TLS 0x2
static WABT_INLINE const char* GetKindName(ExternalKind kind) {
return static_cast<size_t>(kind) < kExternalKindCount
- ? g_kind_name[static_cast<size_t>(kind)]
- : "<error_kind>";
+ ? g_kind_name[static_cast<size_t>(kind)]
+ : "<error_kind>";
}
/* reloc */
static WABT_INLINE const char* GetRelocTypeName(RelocType reloc) {
return static_cast<size_t>(reloc) < kRelocTypeCount
- ? g_reloc_type_name[static_cast<size_t>(reloc)]
- : "<error_reloc_type>";
+ ? g_reloc_type_name[static_cast<size_t>(reloc)]
+ : "<error_reloc_type>";
}
/* symbol */
ConvertBackslashToSlash(s->begin(), s->end());
}
-inline void SwapBytesSized(void *addr, size_t size) {
+inline void SwapBytesSized(void* addr, size_t size) {
auto bytes = static_cast<uint8_t*>(addr);
for (size_t i = 0; i < size / 2; i++) {
- std::swap(bytes[i], bytes[size-1-i]);
+ std::swap(bytes[i], bytes[size - 1 - i]);
}
}
// Allow the following functions to change the floating-point environment (e.g.
// update to 64-bit precision in the mantissa). This is only needed for x87
// floats, which are only used on MSVC 32-bit.
-#pragma fenv_access (on)
+#pragma fenv_access(on)
namespace {
typedef unsigned int FPControl;
}
#if COMPILER_IS_MSVC && _M_IX86
-#pragma fenv_access (off)
+#pragma fenv_access(off)
#endif
#include "src/cast.h"
#include "src/generate-names.h"
-#include "src/ir.h"
#include "src/ir-util.h"
+#include "src/ir.h"
#include <map>
std::vector<Node> children;
// Node specific annotations.
union {
- struct { Index var_start, var_count; }; // FlushedVar/FlushToVars
+ struct {
+ Index var_start, var_count; // FlushedVar/FlushToVars
+ };
const Var* var; // Decl/DeclInit.
- LabelType lt; // br/br_if target.
+ LabelType lt; // br/br_if target.
} u;
- Node() : ntype(NodeType::Uninitialized), etype(ExprType::Nop), e(nullptr) {
- }
+ Node() : ntype(NodeType::Uninitialized), etype(ExprType::Nop), e(nullptr) {}
Node(NodeType ntype, ExprType etype, const Expr* e, const Var* v)
- : ntype(ntype), etype(etype), e(e) {
+ : ntype(ntype), etype(etype), e(e) {
u.var = v;
}
};
struct AST {
- AST(ModuleContext& mc, const Func *f) : mc(mc), f(f) {
+ AST(ModuleContext& mc, const Func* f) : mc(mc), f(f) {
if (f) {
mc.BeginFunc(*f);
for (Index i = 0; i < f->GetNumParams(); i++) {
auto name = "$" + IndexToAlphaName(i);
- vars_defined.insert({ name, { 0, false }});
+ vars_defined.insert({name, {0, false}});
}
}
}
~AST() {
- if (f) mc.EndFunc();
+ if (f) {
+ mc.EndFunc();
+ }
}
// Create a new node, take nargs existing nodes on the exp stack as children.
Node& InsertNode(NodeType ntype, ExprType etype, const Expr* e, Index nargs) {
assert(exp_stack.size() >= nargs);
- Node n { ntype, etype, e, nullptr };
+ Node n{ntype, etype, e, nullptr};
n.children.reserve(nargs);
std::move(exp_stack.end() - nargs, exp_stack.end(),
std::back_inserter(n.children));
return exp_stack.back();
}
- template<ExprType T> void PreDecl(const VarExpr<T>& ve) {
+ template <ExprType T>
+ void PreDecl(const VarExpr<T>& ve) {
// FIXME: this is slow, and would be better to avoid in callers.
// See https://github.com/WebAssembly/wabt/issues/1565
// And https://github.com/WebAssembly/wabt/issues/1665
predecls.emplace_back(NodeType::Decl, ExprType::Nop, nullptr, &ve.var);
}
- template<ExprType T> void Get(const VarExpr<T>& ve, bool local) {
+ template <ExprType T>
+ void Get(const VarExpr<T>& ve, bool local) {
if (local) {
- auto ret = vars_defined.insert({ ve.var.name(), { cur_block_id, false }});
+ auto ret = vars_defined.insert({ve.var.name(), {cur_block_id, false}});
if (ret.second) {
// Use before def, may happen since locals are guaranteed 0.
PreDecl(ve);
InsertNode(NodeType::Expr, T, &ve, 0);
}
- template<ExprType T> void Set(const VarExpr<T>& ve, bool local) {
+ template <ExprType T>
+ void Set(const VarExpr<T>& ve, bool local) {
// Seen this var before?
if (local &&
- vars_defined.insert({ ve.var.name(), { cur_block_id, false }}).second) {
+ vars_defined.insert({ve.var.name(), {cur_block_id, false}}).second) {
if (value_stack_depth == 1) {
// Top level, declare it here.
InsertNode(NodeType::DeclInit, ExprType::Nop, nullptr, 1).u.var =
InsertNode(NodeType::Expr, T, &ve, 1);
}
- template<ExprType T> void Block(const BlockExprBase<T>& be, LabelType label) {
+ template <ExprType T>
+ void Block(const BlockExprBase<T>& be, LabelType label) {
mc.BeginBlock(label, be.block);
- Construct(be.block.exprs, be.block.decl.GetNumResults(), be.block.decl.GetNumParams(), false);
+ Construct(be.block.exprs, be.block.decl.GetNumResults(),
+ be.block.decl.GetNumParams(), false);
mc.EndBlock();
InsertNode(NodeType::Expr, T, &be, 1);
}
auto& lt = *cast<LocalTeeExpr>(&e);
Set(lt, true);
if (value_stack_depth == 1) { // Tee is the only thing on there.
- Get(lt, true); // Now Set + Get instead.
+ Get(lt, true); // Now Set + Get instead.
} else {
// Things are above us on the stack so the Tee can't be eliminated.
// The Set makes this work as a Tee when consumed by a parent.
auto ife = cast<IfExpr>(&e);
value_stack_depth--; // Condition.
mc.BeginBlock(LabelType::Block, ife->true_);
- Construct(ife->true_.exprs, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false);
+ Construct(ife->true_.exprs, ife->true_.decl.GetNumResults(),
+ ife->true_.decl.GetNumParams(), false);
if (!ife->false_.empty()) {
- Construct(ife->false_, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false);
+ Construct(ife->false_, ife->true_.decl.GetNumResults(),
+ ife->true_.decl.GetNumParams(), false);
}
mc.EndBlock();
value_stack_depth++; // Put Condition back.
- InsertNode(NodeType::Expr, ExprType::If, &e, ife->false_.empty() ? 2 : 3);
+ InsertNode(NodeType::Expr, ExprType::If, &e,
+ ife->false_.empty() ? 2 : 3);
return;
}
case ExprType::Block: {
}
}
- void Construct(const ExprList& es, Index nresults, Index nparams, bool is_function_body) {
+ void Construct(const ExprList& es,
+ Index nresults,
+ Index nparams,
+ bool is_function_body) {
block_stack.push_back(cur_block_id);
cur_block_id = blocks_closed.size();
blocks_closed.push_back(false);
Construct(e);
auto arity = mc.GetExprArity(e);
value_stack_depth -= arity.nargs;
- value_stack_in_variables = std::min(value_stack_in_variables,
- value_stack_depth);
+ value_stack_in_variables =
+ std::min(value_stack_in_variables, value_stack_depth);
unreachable = unreachable || arity.unreachable;
assert(unreachable || value_stack_depth >= value_stack_depth_start);
value_stack_depth += arity.nreturns;
auto num_vars = value_stack_in_variables - value_stack_depth_start;
auto num_vals = value_stack_depth - value_stack_in_variables;
auto GenFlushVars = [&](int nargs) {
- auto& ftv = InsertNode(NodeType::FlushToVars, ExprType::Nop, nullptr,
- nargs);
+ auto& ftv =
+ InsertNode(NodeType::FlushToVars, ExprType::Nop, nullptr, nargs);
ftv.u.var_start = flushed_vars;
ftv.u.var_count = num_vals;
};
auto MoveStatementsBelowVars = [&](size_t amount) {
std::rotate(exp_stack.end() - num_vars - amount,
- exp_stack.end() - amount,
- exp_stack.end());
+ exp_stack.end() - amount, exp_stack.end());
};
auto GenFlushedVars = [&]() {
// Re-generate these values as vars.
for (int i = 0; i < num_vals; i++) {
- auto& fv = InsertNode(NodeType::FlushedVar, ExprType::Nop, nullptr,
- 0);
+ auto& fv =
+ InsertNode(NodeType::FlushedVar, ExprType::Nop, nullptr, 0);
fv.u.var_start = flushed_vars++;
fv.u.var_count = 1;
}
// Special optimisation: for constant instructions, we can mark these
// as if they were variables, so they can be re-ordered for free with
// the above code, without needing new variables!
- // TODO: this would be nice to also do for get_local and maybe others,
- // though that needs a way to ensure there's no set_local in between
+ // TODO: this would be nice to also do for local.get and maybe others,
+ // though that needs a way to ensure there's no local.set in between
// when they get lifted, so complicates matters a bit.
if (e.type() == ExprType::Const &&
value_stack_in_variables == value_stack_depth - 1) {
}
}
}
- assert(unreachable ||
- value_stack_depth - value_stack_depth_start ==
- static_cast<int>(nresults));
+ assert(unreachable || value_stack_depth - value_stack_depth_start ==
+ static_cast<int>(nresults));
// Undo any changes to value_stack_depth, since parent takes care of arity
// changes.
value_stack_depth = value_stack_depth_start;
ModuleContext& mc;
std::vector<Node> exp_stack;
std::vector<Node> predecls;
- const Func *f;
+ const Func* f;
int value_stack_depth = 0;
- struct Variable { size_t block_id; bool defined; };
+ struct Variable {
+ size_t block_id;
+ bool defined;
+ };
std::map<std::string, Variable> vars_defined;
Index flushed_vars = 0;
size_t cur_block_id = 0;
namespace wabt {
// Names starting with "u" are unsigned, the rest are "signed or doesn't matter"
-inline const char *GetDecompTypeName(Type t) {
+inline const char* GetDecompTypeName(Type t) {
switch (t) {
case Type::I8: return "byte";
case Type::I8U: return "ubyte";
};
void Track(const Node& n) {
- for (auto& c : n.children) Track(c);
+ for (auto& c : n.children) {
+ Track(c);
+ }
switch (n.etype) {
case ExprType::Load: {
auto& le = *cast<LoadExpr>(n.e);
- LoadStore(le.offset, le.opcode, le.opcode.GetResultType(),
- le.align, n.children[0]);
+ LoadStore(le.offset, le.opcode, le.opcode.GetResultType(), le.align,
+ n.children[0]);
break;
}
case ExprType::Store: {
auto& se = *cast<StoreExpr>(n.e);
- LoadStore(se.offset, se.opcode, se.opcode.GetParamType2(),
- se.align, n.children[0]);
+ LoadStore(se.offset, se.opcode, se.opcode.GetParamType2(), se.align,
+ n.children[0]);
break;
}
default:
}
}
- void LoadStore(uint64_t offset, Opcode opc, Type type, Address align,
+ void LoadStore(uint64_t offset,
+ Opcode opc,
+ Type type,
+ Address align,
const Node& addr_exp) {
auto byte_size = opc.GetMemorySize();
type = GetMemoryType(type, opc);
auto& access = var.accesses[offset];
// Check if previous access at this offset (if any) is of same size
// and type (see Checklayouts below).
- if (access.byte_size &&
- ((access.byte_size != byte_size) ||
- (access.type != type) ||
- (access.align != align)))
+ if (access.byte_size && ((access.byte_size != byte_size) ||
+ (access.type != type) || (access.align != align)))
access.is_uniform = false;
// Also exclude weird alignment accesses from structs.
if (!opc.IsNaturallyAligned(align))
}
std::string GenAlign(Address align, Opcode opc) const {
- return opc.IsNaturallyAligned(align)
- ? ""
- : cat("@", std::to_string(align));
+ return opc.IsNaturallyAligned(align) ? "" : cat("@", std::to_string(align));
}
std::string GenTypeDecl(const std::string& name) const {
if (it->second.struct_layout) {
std::string s = "{ ";
for (auto& access : it->second.accesses) {
- if (access.second.idx) s += ", ";
+ if (access.second.idx) {
+ s += ", ";
+ }
s += IdxToName(access.second.idx);
s += ':';
s += GetDecompTypeName(access.second.type);
}
if (it->second.struct_layout) {
auto ait = it->second.accesses.find(offset);
- assert (ait != it->second.accesses.end());
+ assert(ait != it->second.accesses.end());
return IdxToName(ait->second.idx);
}
// Not a struct, see if it is a typed pointer.
return "";
}
- void Clear() {
- vars.clear();
- }
+ void Clear() { vars.clear(); }
std::map<std::string, LSVar> vars;
};
namespace wabt {
-inline void RenameToIdentifier(std::string& name, Index i,
+inline void RenameToIdentifier(std::string& name,
+ Index i,
BindingHash& bh,
const std::set<string_view>* filter) {
// Filter out non-identifier characters, and try to reduce the size of
bh.emplace(s, Binding(i));
}
-template<typename T>
-void RenameToIdentifiers(std::vector<T*>& things, BindingHash& bh,
+template <typename T>
+void RenameToIdentifiers(std::vector<T*>& things,
+ BindingHash& bh,
const std::set<string_view>* filter) {
Index i = 0;
for (auto thing : things) {
// but not if it is the reverse.
Precedence precedence;
- Value(std::vector<std::string>&& v, Precedence p)
- : v(v), precedence(p) {}
+ Value(std::vector<std::string>&& v, Precedence p) : v(v), precedence(p) {}
size_t width() {
size_t w = 0;
- for (auto &s : v) {
+ for (auto& s : v) {
w = std::max(w, s.size());
}
return w;
return s;
}
- std::string Indent(size_t amount) {
- return std::string(amount, ' ');
- }
+ std::string Indent(size_t amount) { return std::string(amount, ' '); }
std::string OpcodeToToken(Opcode opcode) {
std::string s = opcode.GetDecomp();
return s;
}
- void IndentValue(Value &val, size_t amount, string_view first_indent) {
+ void IndentValue(Value& val, size_t amount, string_view first_indent) {
auto indent = Indent(amount);
for (auto& stat : val.v) {
auto is = (&stat != &val.v[0] || first_indent.empty())
}
}
- Value WrapChild(Value &child, string_view prefix, string_view postfix,
+ Value WrapChild(Value& child,
+ string_view prefix,
+ string_view postfix,
Precedence precedence) {
auto width = prefix.size() + postfix.size() + child.width();
- auto &v = child.v;
+ auto& v = child.v;
if (width < target_exp_width ||
(prefix.size() <= indent_amount && postfix.size() <= indent_amount)) {
if (v.size() == 1) {
return std::move(child);
}
- void BracketIfNeeded(Value &val, Precedence parent_precedence) {
+ void BracketIfNeeded(Value& val, Precedence parent_precedence) {
if (parent_precedence < val.precedence ||
(parent_precedence == val.precedence &&
- Associative(parent_precedence))) return;
+ Associative(parent_precedence))) {
+ return;
+ }
val = WrapChild(val, "(", ")", Precedence::Atomic);
}
- Value WrapBinary(std::vector<Value>& args, string_view infix,
- bool indent_right, Precedence precedence) {
+ Value WrapBinary(std::vector<Value>& args,
+ string_view infix,
+ bool indent_right,
+ Precedence precedence) {
assert(args.size() == 2);
auto& left = args[0];
auto& right = args[1];
BracketIfNeeded(left, precedence);
BracketIfNeeded(right, precedence);
- auto width = infix.size() + left.width() + right.width();
+ auto width = infix.size() + left.width() + right.width() + 2;
if (width < target_exp_width && left.v.size() == 1 && right.v.size() == 1) {
- return Value{{left.v[0] + infix + right.v[0]}, precedence};
+ return Value{{cat(left.v[0], " ", infix, " ", right.v[0])}, precedence};
} else {
- Value bin { {}, precedence };
+ Value bin{{}, precedence};
std::move(left.v.begin(), left.v.end(), std::back_inserter(bin.v));
+ bin.v.back().append(" ", 1);
bin.v.back().append(infix.data(), infix.size());
- if (indent_right) IndentValue(right, indent_amount, {});
+ if (indent_right) {
+ IndentValue(right, indent_amount, {});
+ }
std::move(right.v.begin(), right.v.end(), std::back_inserter(bin.v));
return bin;
}
}
- Value WrapNAry(std::vector<Value>& args, string_view prefix,
- string_view postfix, Precedence precedence) {
+ Value WrapNAry(std::vector<Value>& args,
+ string_view prefix,
+ string_view postfix,
+ Precedence precedence) {
size_t total_width = 0;
size_t max_width = 0;
bool multiline = false;
return Value{{std::move(s)}, precedence};
} else {
// Multi-line.
- Value ml { {}, precedence };
+ Value ml{{}, precedence};
auto ident_with_name = max_width + prefix.size() < target_exp_width;
size_t i = 0;
for (auto& child : args) {
IndentValue(child, ident_with_name ? prefix.size() : indent_amount,
- !i && ident_with_name ? prefix : string_view {});
- if (i < args.size() - 1) child.v.back() += ",";
- std::move(child.v.begin(), child.v.end(),
- std::back_inserter(ml.v));
+ !i && ident_with_name ? prefix : string_view{});
+ if (i < args.size() - 1) {
+ child.v.back() += ",";
+ }
+ std::move(child.v.begin(), child.v.end(), std::back_inserter(ml.v));
i++;
}
- if (!ident_with_name) ml.v.insert(ml.v.begin(), std::string(prefix));
+ if (!ident_with_name) {
+ ml.v.insert(ml.v.begin(), std::string(prefix));
+ }
ml.v.back() += postfix;
return ml;
}
return name[0] == '$' ? name.substr(1) : name;
}
- template<ExprType T> Value Get(const VarExpr<T>& ve) {
+ template <ExprType T>
+ Value Get(const VarExpr<T>& ve) {
return Value{{std::string(VarName(ve.var.name()))}, Precedence::Atomic};
}
- template<ExprType T> Value Set(Value& child, const VarExpr<T>& ve) {
- return WrapChild(child, VarName(ve.var.name()) + " = ", "", Precedence::Assign);
+ template <ExprType T>
+ Value Set(Value& child, const VarExpr<T>& ve) {
+ return WrapChild(child, VarName(ve.var.name()) + " = ", "",
+ Precedence::Assign);
}
std::string TempVarName(Index n) {
struc.empty() ? GetDecompTypeName(t) : struc);
}
- bool ConstIntVal(const Expr* e, uint64_t &dest) {
+ bool ConstIntVal(const Expr* e, uint64_t& dest) {
dest = 0;
- if (!e || e->type() != ExprType::Const) return false;
+ if (!e || e->type() != ExprType::Const) {
+ return false;
+ }
auto& c = cast<ConstExpr>(e)->const_;
- if (c.type() != Type::I32 && c.type() != Type::I64) return false;
+ if (c.type() != Type::I32 && c.type() != Type::I64) {
+ return false;
+ }
dest = c.type() == Type::I32 ? c.u32() : c.u64();
return true;
}
- void LoadStore(Value &val, const Node& addr_exp, uint64_t offset,
- Opcode opc, Address align, Type op_type) {
+ void LoadStore(Value& val,
+ const Node& addr_exp,
+ uint64_t offset,
+ Opcode opc,
+ Address align,
+ Type op_type) {
bool append_type = true;
auto access = lst.GenAccess(offset, addr_exp);
if (!access.empty()) {
uint64_t dat_base;
if (dat->offset.size() == 1 &&
ConstIntVal(&dat->offset.front(), dat_base) &&
- abs_base >= dat_base &&
- abs_base < dat_base + dat->data.size()) {
+ abs_base >= dat_base && abs_base < dat_base + dat->data.size()) {
// We are inside the range of this data segment!
// Turn expression into data_name[index]
- val = Value { { dat->name }, Precedence::Atomic };
+ val = Value{{dat->name}, Precedence::Atomic};
// The new offset is from the start of the data segment, instead of
// whatever it was.. this may be a different value from both the
// original const and offset!
// cases, but the spec doesn't guarantee it, so we must
// have a backup syntax.
auto index = offset % align == 0
- ? std::to_string(offset / align)
- : cat(std::to_string(offset), "@", std::to_string(align));
+ ? std::to_string(offset / align)
+ : cat(std::to_string(offset), "@", std::to_string(align));
// Detect the very common case of (base + (index << 2))[0]:int etc.
// so we can instead do base[index]:int
// TODO: (index << 2) on the left of + occurs also.
if (addr_exp.etype == ExprType::Binary) {
auto& pe = *cast<BinaryExpr>(addr_exp.e);
auto& shift_exp = addr_exp.children[1];
- if (pe.opcode == Opcode::I32Add &&
- shift_exp.etype == ExprType::Binary) {
+ if (pe.opcode == Opcode::I32Add && shift_exp.etype == ExprType::Binary) {
auto& se = *cast<BinaryExpr>(shift_exp.e);
auto& const_exp = shift_exp.children[1];
- if (se.opcode == Opcode::I32Shl &&
- const_exp.etype == ExprType::Const) {
+ if (se.opcode == Opcode::I32Shl && const_exp.etype == ExprType::Const) {
auto& ce = *cast<ConstExpr>(const_exp.e);
if (ce.const_.type() == Type::I32 &&
(1ULL << ce.const_.u32()) == align) {
BracketIfNeeded(val, Precedence::Indexing);
val.v.back() += cat("[", index, "]");
if (append_type) {
- val.v.back() +=
- cat(":", GetDecompTypeName(GetMemoryType(op_type, opc)),
- lst.GenAlign(align, opc));
+ val.v.back() += cat(":", GetDecompTypeName(GetMemoryType(op_type, opc)),
+ lst.GenAlign(align, opc));
}
val.precedence = Precedence::Indexing;
}
case NodeType::FlushToVars: {
std::string decls = "let ";
for (Index i = 0; i < n.u.var_count; i++) {
- if (i) decls += ", ";
+ if (i) {
+ decls += ", ";
+ }
decls += TempVarName(n.u.var_start + i);
}
decls += " = ";
return WrapNAry(args, decls, "", Precedence::Assign);
}
case NodeType::FlushedVar: {
- return Value { { TempVarName(n.u.var_start) }, Precedence::Atomic };
+ return Value{{TempVarName(n.u.var_start)}, Precedence::Atomic};
}
case NodeType::Statements: {
- Value stats { {}, Precedence::None };
+ Value stats{{}, Precedence::None};
for (size_t i = 0; i < n.children.size(); i++) {
auto& s = args[i].v.back();
- if (s.back() != '}' && s.back() != ':') s += ';';
+ if (s.back() != '}' && s.back() != ':') {
+ s += ';';
+ }
std::move(args[i].v.begin(), args[i].v.end(),
std::back_inserter(stats.v));
}
}
case NodeType::Decl: {
cur_ast->vars_defined[n.u.var->name()].defined = true;
- return Value{
- {"var " + LocalDecl(std::string(n.u.var->name()),
- cur_func->GetLocalType(*n.u.var))},
- Precedence::None};
+ return Value{{"var " + LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var))},
+ Precedence::None};
}
case NodeType::DeclInit: {
if (cur_ast->vars_defined[n.u.var->name()].defined) {
return WrapChild(args[0], cat(VarName(n.u.var->name()), " = "), "",
Precedence::None);
} else {
- return WrapChild(
- args[0],
- cat("var ",
- LocalDecl(std::string(n.u.var->name()),
- cur_func->GetLocalType(*n.u.var)),
- " = "),
- "", Precedence::None);
+ return WrapChild(args[0],
+ cat("var ",
+ LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var)),
+ " = "),
+ "", Precedence::None);
}
}
case NodeType::Expr:
} else if (opcs == "<<" || opcs == ">>") {
prec = Precedence::Shift;
}
- return WrapBinary(args, cat(" ", opcs, " "), false, prec);
+ return WrapBinary(args, opcs, false, prec);
}
case ExprType::Compare: {
auto& ce = *cast<CompareExpr>(n.e);
- return WrapBinary(args, cat(" ", OpcodeToToken(ce.opcode), " "), false,
- Precedence::Equal);
+ return WrapBinary(args, OpcodeToToken(ce.opcode), false,
+ Precedence::Equal);
}
case ExprType::Unary: {
auto& ue = *cast<UnaryExpr>(n.e);
- //BracketIfNeeded(stack.back());
+ // BracketIfNeeded(stack.back());
// TODO: also version without () depending on precedence.
return WrapChild(args[0], OpcodeToToken(ue.opcode) + "(", ")",
Precedence::Atomic);
auto& se = *cast<StoreExpr>(n.e);
LoadStore(args[0], n.children[0], se.offset, se.opcode, se.align,
se.opcode.GetParamType2());
- return WrapBinary(args, " = ", true, Precedence::Assign);
+ return WrapBinary(args, "=", true, Precedence::Assign);
}
case ExprType::If: {
auto ife = cast<IfExpr>(n.e);
- Value *elsep = nullptr;
+ Value* elsep = nullptr;
if (!ife->false_.empty()) {
elsep = &args[2];
}
if (elsep) {
ifs.v.push_back("} else {");
IndentValue(*elsep, indent_amount, {});
- std::move(elsep->v.begin(), elsep->v.end(), std::back_inserter(ifs.v));
+ std::move(elsep->v.begin(), elsep->v.end(),
+ std::back_inserter(ifs.v));
}
ifs.v.push_back("}");
ifs.precedence = Precedence::If;
case ExprType::Block: {
auto& val = args[0];
val.v.push_back(
- cat("label ", VarName(cast<BlockExpr>(n.e)->block.label), ":"));
+ cat("label ", VarName(cast<BlockExpr>(n.e)->block.label), ":"));
// If this block is part of a larger statement scope, it doesn't
// need its own indenting, but if its part of an exp we wrap it in {}.
- if (parent && parent->ntype != NodeType::Statements
- && parent->etype != ExprType::Block
- && parent->etype != ExprType::Loop
- && (parent->etype != ExprType::If ||
- &parent->children[0] == &n)) {
+ if (parent && parent->ntype != NodeType::Statements &&
+ parent->etype != ExprType::Block &&
+ parent->etype != ExprType::Loop &&
+ (parent->etype != ExprType::If || &parent->children[0] == &n)) {
IndentValue(val, indent_amount, {});
val.v.insert(val.v.begin(), "{");
val.v.push_back("}");
case ExprType::BrIf: {
auto bie = cast<BrIfExpr>(n.e);
auto jmp = n.u.lt == LabelType::Loop ? "continue" : "goto";
- return WrapChild(args[0], "if (", cat(") ", jmp, " ",
- VarName(bie->var.name())),
+ return WrapChild(args[0], "if (",
+ cat(") ", jmp, " ", VarName(bie->var.name())),
Precedence::None);
}
case ExprType::Return: {
case ExprType::BrTable: {
auto bte = cast<BrTableExpr>(n.e);
std::string ts = "br_table[";
- for (auto &v : bte->targets) {
+ for (auto& v : bte->targets) {
ts += VarName(v.name());
ts += ", ";
}
return is_import;
}
- std::string InitExp(const ExprList &el) {
+ std::string InitExp(const ExprList& el) {
assert(!el.empty());
AST ast(mc, nullptr);
ast.Construct(el, 1, 0, false);
}
// FIXME: Merge with WatWriter::WriteQuotedData somehow.
- std::string BinaryToString(const std::vector<uint8_t> &in) {
+ std::string BinaryToString(const std::vector<uint8_t>& in) {
std::string s = "\"";
size_t line_start = 0;
static const char s_hexdigits[] = "0123456789abcdef";
// Data.
for (auto dat : mc.module.data_segments) {
- s += cat("data ", dat->name, "(offset: ", InitExp(dat->offset), ") = ");
+ s += cat("data ", dat->name, "(offset: ", InitExp(dat->offset), ") =");
auto ds = BinaryToString(dat->data);
if (ds.size() > target_exp_width / 2) {
s += "\n";
+ } else {
+ s += " ";
}
s += ds;
s += ";\n";
struct Module;
class Stream;
-struct DecompileOptions {
-};
+struct DecompileOptions {};
void RenameAll(Module&);
#define WABT_ERROR_FORMATTER_H_
#include <cstdio>
-#include <string>
#include <memory>
+#include <string>
#include "src/color.h"
#include "src/error.h"
CHECK_RESULT(delegate_->OnStoreExpr(cast<StoreExpr>(expr)));
break;
-
case ExprType::Throw:
CHECK_RESULT(delegate_->OnThrowExpr(cast<ThrowExpr>(expr)));
break;
}
// Reference types requires bulk memory.
- if (reference_types_enabled_) {
- bulk_memory_enabled_ = true;
+ if (!bulk_memory_enabled_) {
+ reference_types_enabled_ = false;
}
}
WABT_FEATURE(mutable_globals, "mutable-globals", true, "Import/export mutable globals")
WABT_FEATURE(sat_float_to_int, "saturating-float-to-int", true, "Saturating float-to-int operators")
WABT_FEATURE(sign_extension, "sign-extension", true, "Sign-extension operators")
-WABT_FEATURE(simd, "simd", false, "SIMD support")
+WABT_FEATURE(simd, "simd", true, "SIMD support")
WABT_FEATURE(threads, "threads", false, "Threading support")
WABT_FEATURE(function_references, "function-references", false, "Typed function references")
WABT_FEATURE(multi_value, "multi-value", true, "Multi-value")
WABT_FEATURE(tail_call, "tail-call", false, "Tail-call support")
-WABT_FEATURE(bulk_memory, "bulk-memory", false, "Bulk-memory operations")
-WABT_FEATURE(reference_types, "reference-types", false, "Reference types (externref)")
+WABT_FEATURE(bulk_memory, "bulk-memory", true, "Bulk-memory operations")
+WABT_FEATURE(reference_types, "reference-types", true, "Reference types (externref)")
WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax")
WABT_FEATURE(gc, "gc", false, "Garbage collection")
WABT_FEATURE(memory64, "memory64", false, "64-bit memory")
+WABT_FEATURE(multi_memory, "multi-memory", false, "Multi-memory")
std::string* out_str);
// Like GenerateName, but only generates a name if |out_str| is empty.
- void MaybeGenerateName(const char* prefix,
- Index index,
- std::string* out_str);
+ void MaybeGenerateName(const char* prefix, Index index, std::string* out_str);
// Generate a name via GenerateName and bind it to the given binding hash. If
// the name already exists, the name will be disambiguated until it can be
NameOpts opts_;
};
-NameGenerator::NameGenerator(NameOpts opts)
- : visitor_(this), opts_(opts) {}
+NameGenerator::NameGenerator(NameOpts opts) : visitor_(this), opts_(opts) {}
// static
bool NameGenerator::HasName(const std::string& str) {
}
}
+// This is only used to distinguish try blocks and all other blocks,
+// so there are only two kinds.
+enum class LabelKind { Block, Try };
+
struct Label {
+ LabelKind kind;
Istream::Offset offset;
Istream::Offset fixup_offset;
+ // Only needs to be set for try blocks.
+ u32 handler_desc_index;
};
struct FixupMap {
class BinaryReaderInterp : public BinaryReaderNop {
public:
BinaryReaderInterp(ModuleDesc* module,
+ string_view filename,
Errors* errors,
const Features& features);
Index global_index,
Type type,
bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
Result OnFunctionCount(Index count) override;
Result OnFunction(Index index, Index sig_index) override;
Result OnGlobalCount(Index count) override;
Result BeginGlobal(Index index, Type type, bool mutable_) override;
+ Result BeginGlobalInitExpr(Index index) override;
Result EndGlobalInitExpr(Index index) override;
+ Result OnTagCount(Index count) override;
+ Result OnTagType(Index index, Index sig_index) override;
+
Result OnExport(Index index,
ExternalKind kind,
Index item_index,
Index default_target_depth) override;
Result OnCallExpr(Index func_index) override;
Result OnCallIndirectExpr(Index sig_index, Index table_index) override;
+ Result OnCatchExpr(Index tag_index) override;
+ Result OnCatchAllExpr() override;
+ Result OnDelegateExpr(Index depth) override;
Result OnReturnCallExpr(Index func_index) override;
Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override;
Result OnCompareExpr(Opcode opcode) override;
Result OnI64ConstExpr(uint64_t value) override;
Result OnIfExpr(Type sig_type) override;
Result OnLoadExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnLocalGetExpr(Index local_index) override;
Result OnLocalSetExpr(Index local_index) override;
Result OnLocalTeeExpr(Index local_index) override;
Result OnLoopExpr(Type sig_type) override;
- Result OnMemoryCopyExpr() override;
+ Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override;
Result OnDataDropExpr(Index segment_index) override;
- Result OnMemoryGrowExpr() override;
- Result OnMemoryFillExpr() override;
- Result OnMemoryInitExpr(Index segment_index) override;
- Result OnMemorySizeExpr() override;
+ Result OnMemoryGrowExpr(Index memidx) override;
+ Result OnMemoryFillExpr(Index memidx) override;
+ Result OnMemoryInitExpr(Index segment_index, Index memidx) override;
+ Result OnMemorySizeExpr(Index memidx) override;
Result OnRefFuncExpr(Index func_index) override;
Result OnRefNullExpr(Type type) override;
Result OnRefIsNullExpr() override;
Result OnNopExpr() override;
+ Result OnRethrowExpr(Index depth) override;
Result OnReturnExpr() override;
Result OnSelectExpr(Index result_count, Type* result_types) override;
Result OnStoreExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset) override;
Result OnUnaryExpr(Opcode opcode) override;
Result OnElemDropExpr(Index segment_index) override;
Result OnTableInitExpr(Index segment_index, Index table_index) override;
Result OnTernaryExpr(Opcode opcode) override;
+ Result OnThrowExpr(Index tag_index) override;
+ Result OnTryExpr(Type sig_type) override;
Result OnUnreachableExpr() override;
Result EndFunctionBody(Index index) override;
Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) override;
Result BeginElemSegment(Index index,
Index table_index,
uint8_t flags) override;
+ Result BeginElemSegmentInitExpr(Index index) override;
Result EndElemSegmentInitExpr(Index index) override;
Result OnElemSegmentElemType(Index index, Type elem_type) override;
Result OnElemSegmentElemExprCount(Index index, Index count) override;
Index func_index) override;
Result OnDataCount(Index count) override;
+ Result BeginDataSegmentInitExpr(Index index) override;
Result EndDataSegmentInitExpr(Index index) override;
Result BeginDataSegment(Index index,
Index memory_index,
const void* data,
Address size) override;
- Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprV128ConstExpr(Index index, v128 value) override;
- Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
- Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
- Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
- Result OnInitExprRefNull(Index index, Type type) override;
- Result OnInitExprRefFunc(Index index, Index func_index) override;
-
private:
+ Location GetLocation() const;
Label* GetLabel(Index depth);
+ Label* GetNearestTryLabel(Index depth);
Label* TopLabel();
- void PushLabel(Istream::Offset offset = Istream::kInvalidOffset,
- Istream::Offset fixup_offset = Istream::kInvalidOffset);
+ void PushLabel(LabelKind label = LabelKind::Block,
+ Istream::Offset offset = Istream::kInvalidOffset,
+ Istream::Offset fixup_offset = Istream::kInvalidOffset,
+ u32 handler_desc_index = kInvalidIndex);
void PopLabel();
void PrintError(const char* format, ...);
Index keep_extra,
Index* out_drop_count,
Index* out_keep_count);
- void EmitBr(Index depth, Index drop_count, Index keep_count);
+ Result BeginInitExpr(Type type);
+ Result EndInitExpr();
+
+ void EmitBr(Index depth,
+ Index drop_count,
+ Index keep_count,
+ Index catch_drop_count);
void FixupTopLabel();
u32 GetFuncOffset(Index func_index);
FixupMap depth_fixups_;
FixupMap func_fixups_;
+ bool reading_init_expr_ = false;
InitExpr init_expr_;
u32 local_decl_count_;
u32 local_count_;
std::vector<TagType> tag_types_; // Includes imported and defined.
static const Index kMemoryIndex0 = 0;
-
- // TODO: Use this in all locations below, for now. In the future we'll want
- // to use the real locations.
- static const Location loc;
+ string_view filename_;
};
-// static
-const Location BinaryReaderInterp::loc{kInvalidOffset};
+Location BinaryReaderInterp::GetLocation() const {
+ Location loc;
+ loc.filename = filename_;
+ loc.offset = state->offset;
+ return loc;
+}
void FixupMap::Clear() {
map.clear();
}
BinaryReaderInterp::BinaryReaderInterp(ModuleDesc* module,
+ string_view filename,
Errors* errors,
const Features& features)
: errors_(errors),
module_(*module),
istream_(module->istream),
- validator_(errors, ValidateOptions(features)) {}
+ validator_(errors, ValidateOptions(features)),
+ filename_(filename) {}
Label* BinaryReaderInterp::GetLabel(Index depth) {
assert(depth < label_stack_.size());
return &label_stack_[label_stack_.size() - depth - 1];
}
+Label* BinaryReaderInterp::GetNearestTryLabel(Index depth) {
+ for (size_t i = depth; i < label_stack_.size(); i++) {
+ Label* label = &label_stack_[label_stack_.size() - i - 1];
+ if (label->kind == LabelKind::Try) {
+ return label;
+ }
+ }
+ return nullptr;
+}
+
Label* BinaryReaderInterp::TopLabel() {
return GetLabel(0);
}
void BinaryReaderInterp::EmitBr(Index depth,
Index drop_count,
- Index keep_count) {
+ Index keep_count,
+ Index catch_drop_count) {
istream_.EmitDropKeep(drop_count, keep_count);
+ istream_.EmitCatchDrop(catch_drop_count);
Istream::Offset offset = GetLabel(depth)->offset;
istream_.Emit(Opcode::Br);
if (offset == Istream::kInvalidOffset) {
Type* param_types,
Index result_count,
Type* result_types) {
- CHECK_RESULT(validator_.OnFuncType(loc, param_count, param_types,
- result_count, result_types));
+ CHECK_RESULT(validator_.OnFuncType(GetLocation(), param_count, param_types,
+ result_count, result_types, index));
module_.func_types.push_back(FuncType(ToInterp(param_count, param_types),
ToInterp(result_count, result_types)));
return Result::Ok;
string_view field_name,
Index func_index,
Index sig_index) {
- CHECK_RESULT(validator_.OnFunction(loc, Var(sig_index)));
+ CHECK_RESULT(validator_.OnFunction(GetLocation(), Var(sig_index)));
FuncType& func_type = module_.func_types[sig_index];
module_.imports.push_back(ImportDesc{ImportType(
module_name.to_string(), field_name.to_string(), func_type.Clone())});
Index table_index,
Type elem_type,
const Limits* elem_limits) {
- CHECK_RESULT(validator_.OnTable(loc, elem_type, *elem_limits));
+ CHECK_RESULT(validator_.OnTable(GetLocation(), elem_type, *elem_limits));
TableType table_type{elem_type, *elem_limits};
module_.imports.push_back(ImportDesc{ImportType(
module_name.to_string(), field_name.to_string(), table_type.Clone())});
string_view field_name,
Index memory_index,
const Limits* page_limits) {
- CHECK_RESULT(validator_.OnMemory(loc, *page_limits));
+ CHECK_RESULT(validator_.OnMemory(GetLocation(), *page_limits));
MemoryType memory_type{*page_limits};
module_.imports.push_back(ImportDesc{ImportType(
module_name.to_string(), field_name.to_string(), memory_type.Clone())});
Index global_index,
Type type,
bool mutable_) {
- CHECK_RESULT(validator_.OnGlobalImport(loc, type, mutable_));
+ CHECK_RESULT(validator_.OnGlobalImport(GetLocation(), type, mutable_));
GlobalType global_type{type, ToMutability(mutable_)};
module_.imports.push_back(ImportDesc{ImportType(
module_name.to_string(), field_name.to_string(), global_type.Clone())});
return Result::Ok;
}
+Result BinaryReaderInterp::OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) {
+ CHECK_RESULT(validator_.OnTag(GetLocation(), Var(sig_index)));
+ FuncType& func_type = module_.func_types[sig_index];
+ TagType tag_type{TagAttr::Exception, func_type.params};
+ module_.imports.push_back(ImportDesc{ImportType(
+ module_name.to_string(), field_name.to_string(), tag_type.Clone())});
+ tag_types_.push_back(tag_type);
+ return Result::Ok;
+}
+
Result BinaryReaderInterp::OnFunctionCount(Index count) {
module_.funcs.reserve(count);
return Result::Ok;
}
Result BinaryReaderInterp::OnFunction(Index index, Index sig_index) {
- CHECK_RESULT(validator_.OnFunction(loc, Var(sig_index)));
+ CHECK_RESULT(validator_.OnFunction(GetLocation(), Var(sig_index)));
FuncType& func_type = module_.func_types[sig_index];
- module_.funcs.push_back(FuncDesc{func_type, {}, 0});
+ module_.funcs.push_back(FuncDesc{func_type, {}, Istream::kInvalidOffset, {}});
func_types_.push_back(func_type);
return Result::Ok;
}
Result BinaryReaderInterp::OnTable(Index index,
Type elem_type,
const Limits* elem_limits) {
- CHECK_RESULT(validator_.OnTable(loc, elem_type, *elem_limits));
+ CHECK_RESULT(validator_.OnTable(GetLocation(), elem_type, *elem_limits));
TableType table_type{elem_type, *elem_limits};
module_.tables.push_back(TableDesc{table_type});
table_types_.push_back(table_type);
}
Result BinaryReaderInterp::OnMemory(Index index, const Limits* limits) {
- CHECK_RESULT(validator_.OnMemory(loc, *limits));
+ CHECK_RESULT(validator_.OnMemory(GetLocation(), *limits));
MemoryType memory_type{*limits};
module_.memories.push_back(MemoryDesc{memory_type});
memory_types_.push_back(memory_type);
}
Result BinaryReaderInterp::BeginGlobal(Index index, Type type, bool mutable_) {
- CHECK_RESULT(validator_.OnGlobal(loc, type, mutable_));
+ CHECK_RESULT(validator_.OnGlobal(GetLocation(), type, mutable_));
GlobalType global_type{type, ToMutability(mutable_)};
module_.globals.push_back(GlobalDesc{global_type, InitExpr{}});
global_types_.push_back(global_type);
return Result::Ok;
}
-Result BinaryReaderInterp::EndGlobalInitExpr(Index index) {
- switch (init_expr_.kind) {
- case InitExprKind::I32:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::I32));
- break;
-
- case InitExprKind::I64:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::I64));
- break;
-
- case InitExprKind::F32:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::F32));
- break;
-
- case InitExprKind::F64:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::F64));
- break;
-
- case InitExprKind::V128:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::V128));
- break;
-
- case InitExprKind::GlobalGet:
- CHECK_RESULT(
- validator_.OnGlobalInitExpr_GlobalGet(loc, Var(init_expr_.index_)));
- break;
-
- case InitExprKind::RefNull:
- CHECK_RESULT(validator_.OnGlobalInitExpr_RefNull(loc, init_expr_.type_));
- break;
-
- case InitExprKind::RefFunc:
- CHECK_RESULT(
- validator_.OnGlobalInitExpr_RefFunc(loc, Var(init_expr_.index_)));
- break;
-
- default:
- CHECK_RESULT(validator_.OnGlobalInitExpr_Other(loc));
- break;
- }
-
- GlobalDesc& global = module_.globals.back();
- global.init = init_expr_;
- return Result::Ok;
-}
-
-Result BinaryReaderInterp::OnInitExprF32ConstExpr(Index index,
- uint32_t value_bits) {
- init_expr_.kind = InitExprKind::F32;
- init_expr_.f32_ = Bitcast<f32>(value_bits);
- return Result::Ok;
-}
-
-Result BinaryReaderInterp::OnInitExprF64ConstExpr(Index index,
- uint64_t value_bits) {
- init_expr_.kind = InitExprKind::F64;
- init_expr_.f64_ = Bitcast<f64>(value_bits);
- return Result::Ok;
-}
-
-Result BinaryReaderInterp::OnInitExprV128ConstExpr(Index index,
- v128 value_bits) {
- init_expr_.kind = InitExprKind::V128;
- init_expr_.v128_ = Bitcast<v128>(value_bits);
- return Result::Ok;
+Result BinaryReaderInterp::BeginGlobalInitExpr(Index index) {
+ GlobalType type = global_types_[index];
+ return BeginInitExpr(type.type);
}
-Result BinaryReaderInterp::OnInitExprGlobalGetExpr(Index index,
- Index global_index) {
- init_expr_.kind = InitExprKind::GlobalGet;
- init_expr_.index_ = global_index;
+Result BinaryReaderInterp::EndInitExpr() {
+ assert(reading_init_expr_);
+ reading_init_expr_ = false;
+ CHECK_RESULT(validator_.EndInitExpr());
return Result::Ok;
}
-Result BinaryReaderInterp::OnInitExprI32ConstExpr(Index index, uint32_t value) {
- init_expr_.kind = InitExprKind::I32;
- init_expr_.i32_ = value;
+Result BinaryReaderInterp::BeginInitExpr(Type type) {
+ assert(!reading_init_expr_);
+ reading_init_expr_ = true;
+ init_expr_.kind = InitExprKind::None;
+ CHECK_RESULT(validator_.BeginInitExpr(GetLocation(), type));
return Result::Ok;
}
-Result BinaryReaderInterp::OnInitExprI64ConstExpr(Index index, uint64_t value) {
- init_expr_.kind = InitExprKind::I64;
- init_expr_.i64_ = value;
+Result BinaryReaderInterp::EndGlobalInitExpr(Index index) {
+ CHECK_RESULT(EndInitExpr());
+ GlobalDesc& global = module_.globals.back();
+ global.init = init_expr_;
return Result::Ok;
}
-Result BinaryReaderInterp::OnInitExprRefNull(Index index, Type type) {
- init_expr_.kind = InitExprKind::RefNull;
- init_expr_.type_ = type;
+Result BinaryReaderInterp::OnTagCount(Index count) {
+ module_.tags.reserve(count);
return Result::Ok;
}
-Result BinaryReaderInterp::OnInitExprRefFunc(Index index, Index func_index) {
- init_expr_.kind = InitExprKind::RefFunc;
- init_expr_.index_ = func_index;
+Result BinaryReaderInterp::OnTagType(Index index, Index sig_index) {
+ CHECK_RESULT(validator_.OnTag(GetLocation(), Var(sig_index)));
+ FuncType& func_type = module_.func_types[sig_index];
+ TagType tag_type{TagAttr::Exception, func_type.params};
+ module_.tags.push_back(TagDesc{tag_type});
+ tag_types_.push_back(tag_type);
return Result::Ok;
}
ExternalKind kind,
Index item_index,
string_view name) {
- CHECK_RESULT(validator_.OnExport(loc, kind, Var(item_index), name));
+ CHECK_RESULT(validator_.OnExport(GetLocation(), kind, Var(item_index), name));
std::unique_ptr<ExternType> type;
switch (kind) {
}
Result BinaryReaderInterp::OnStartFunction(Index func_index) {
- CHECK_RESULT(validator_.OnStart(loc, Var(func_index)));
+ CHECK_RESULT(validator_.OnStart(GetLocation(), Var(func_index)));
module_.starts.push_back(StartDesc{func_index});
return Result::Ok;
}
Index table_index,
uint8_t flags) {
auto mode = ToSegmentMode(flags);
- CHECK_RESULT(validator_.OnElemSegment(loc, Var(table_index), mode));
+ CHECK_RESULT(validator_.OnElemSegment(GetLocation(), Var(table_index), mode));
ElemDesc desc;
desc.type = ValueType::Void; // Initialized later in OnElemSegmentElemType.
return Result::Ok;
}
-Result BinaryReaderInterp::EndElemSegmentInitExpr(Index index) {
- switch (init_expr_.kind) {
- case InitExprKind::I32:
- CHECK_RESULT(validator_.OnElemSegmentInitExpr_Const(loc, ValueType::I32));
- break;
-
- case InitExprKind::GlobalGet:
- CHECK_RESULT(validator_.OnElemSegmentInitExpr_GlobalGet(
- loc, Var(init_expr_.index_)));
- break;
-
- default:
- CHECK_RESULT(validator_.OnElemSegmentInitExpr_Other(loc));
- break;
- }
+Result BinaryReaderInterp::BeginElemSegmentInitExpr(Index index) {
+ return BeginInitExpr(Type::I32);
+}
+Result BinaryReaderInterp::EndElemSegmentInitExpr(Index index) {
+ CHECK_RESULT(EndInitExpr());
ElemDesc& elem = module_.elems.back();
elem.offset = init_expr_;
return Result::Ok;
Result BinaryReaderInterp::OnElemSegmentElemExpr_RefNull(Index segment_index,
Type type) {
- CHECK_RESULT(validator_.OnElemSegmentElemExpr_RefNull(loc, type));
+ CHECK_RESULT(validator_.OnElemSegmentElemExpr_RefNull(GetLocation(), type));
ElemDesc& elem = module_.elems.back();
elem.elements.push_back(ElemExpr{ElemKind::RefNull, 0});
return Result::Ok;
Result BinaryReaderInterp::OnElemSegmentElemExpr_RefFunc(Index segment_index,
Index func_index) {
- CHECK_RESULT(validator_.OnElemSegmentElemExpr_RefFunc(loc, Var(func_index)));
+ CHECK_RESULT(
+ validator_.OnElemSegmentElemExpr_RefFunc(GetLocation(), Var(func_index)));
ElemDesc& elem = module_.elems.back();
elem.elements.push_back(ElemExpr{ElemKind::RefFunc, func_index});
return Result::Ok;
return Result::Ok;
}
-Result BinaryReaderInterp::EndDataSegmentInitExpr(Index index) {
- switch (init_expr_.kind) {
- case InitExprKind::I32:
- CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I32));
- break;
-
- case InitExprKind::I64:
- CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I64));
- break;
-
- case InitExprKind::GlobalGet:
- CHECK_RESULT(validator_.OnDataSegmentInitExpr_GlobalGet(
- loc, Var(init_expr_.index_)));
- break;
-
- default:
- CHECK_RESULT(validator_.OnDataSegmentInitExpr_Other(loc));
- break;
- }
+Result BinaryReaderInterp::BeginDataSegmentInitExpr(Index index) {
+ MemoryType t = memory_types_[0];
+ return BeginInitExpr(t.limits.is_64 ? Type::I64 : Type::I32);
+}
+Result BinaryReaderInterp::EndDataSegmentInitExpr(Index index) {
+ CHECK_RESULT(EndInitExpr());
DataDesc& data = module_.datas.back();
data.offset = init_expr_;
return Result::Ok;
Index memory_index,
uint8_t flags) {
auto mode = ToSegmentMode(flags);
- CHECK_RESULT(validator_.OnDataSegment(loc, Var(memory_index), mode));
+ CHECK_RESULT(
+ validator_.OnDataSegment(GetLocation(), Var(memory_index), mode));
DataDesc desc;
desc.mode = mode;
return Result::Ok;
}
-void BinaryReaderInterp::PushLabel(Istream::Offset offset,
- Istream::Offset fixup_offset) {
- label_stack_.push_back(Label{offset, fixup_offset});
+void BinaryReaderInterp::PushLabel(LabelKind kind,
+ Istream::Offset offset,
+ Istream::Offset fixup_offset,
+ u32 handler_desc_index) {
+ label_stack_.push_back(Label{kind, offset, fixup_offset, handler_desc_index});
}
void BinaryReaderInterp::PopLabel() {
func_fixups_.Resolve(istream_, defined_index);
- CHECK_RESULT(validator_.BeginFunctionBody(loc, index));
+ CHECK_RESULT(validator_.BeginFunctionBody(GetLocation(), index));
// Push implicit func label (equivalent to return).
- PushLabel();
+ // With exception handling it acts as a catch-less try block, which is
+ // needed to support delegating to the caller of a function using the
+ // try-delegate instruction.
+ PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
+ func_->handlers.size());
+ func_->handlers.push_back(HandlerDesc{HandlerKind::Catch,
+ istream_.end(),
+ Istream::kInvalidOffset,
+ {},
+ {Istream::kInvalidOffset},
+ static_cast<u32>(func_->locals.size()),
+ 0});
return Result::Ok;
}
FixupTopLabel();
Index drop_count, keep_count;
CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
- CHECK_RESULT(validator_.EndFunctionBody(loc));
+ CHECK_RESULT(validator_.EndFunctionBody(GetLocation()));
istream_.EmitDropKeep(drop_count, keep_count);
istream_.Emit(Opcode::Return);
PopLabel();
Result BinaryReaderInterp::OnLocalDecl(Index decl_index,
Index count,
Type type) {
- CHECK_RESULT(validator_.OnLocalDecl(loc, count, type));
+ CHECK_RESULT(validator_.OnLocalDecl(GetLocation(), count, type));
local_count_ += count;
func_->locals.push_back(LocalDesc{type, count, local_count_});
}
Result BinaryReaderInterp::OnOpcode(Opcode opcode) {
- if (func_ == nullptr || label_stack_.empty()) {
+ if ((func_ == nullptr || label_stack_.empty()) && !reading_init_expr_) {
PrintError("Unexpected instruction after end of function");
return Result::Error;
}
}
Result BinaryReaderInterp::OnUnaryExpr(Opcode opcode) {
- CHECK_RESULT(validator_.OnUnary(loc, opcode));
+ CHECK_RESULT(validator_.OnUnary(GetLocation(), opcode));
istream_.Emit(opcode);
return Result::Ok;
}
Result BinaryReaderInterp::OnTernaryExpr(Opcode opcode) {
- CHECK_RESULT(validator_.OnTernary(loc, opcode));
+ CHECK_RESULT(validator_.OnTernary(GetLocation(), opcode));
istream_.Emit(opcode);
return Result::Ok;
}
Result BinaryReaderInterp::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) {
- CHECK_RESULT(validator_.OnSimdLaneOp(loc, opcode, value));
+ CHECK_RESULT(validator_.OnSimdLaneOp(GetLocation(), opcode, value));
istream_.Emit(opcode, static_cast<u8>(value));
return Result::Ok;
}
}
Result BinaryReaderInterp::OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
Address alignment_log2,
Address offset,
uint64_t value) {
- CHECK_RESULT(validator_.OnSimdLoadLane(loc, opcode, GetAlignment(alignment_log2), value));
- istream_.Emit(opcode, kMemoryIndex0, offset, static_cast<u8>(value));
+ CHECK_RESULT(validator_.OnSimdLoadLane(GetLocation(), opcode,
+ GetAlignment(alignment_log2), value));
+ istream_.Emit(opcode, memidx, offset, static_cast<u8>(value));
return Result::Ok;
}
Result BinaryReaderInterp::OnSimdStoreLaneExpr(Opcode opcode,
- Address alignment_log2,
- Address offset,
- uint64_t value) {
- CHECK_RESULT(validator_.OnSimdStoreLane(loc, opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ CHECK_RESULT(validator_.OnSimdStoreLane(GetLocation(), opcode,
GetAlignment(alignment_log2), value));
- istream_.Emit(opcode, kMemoryIndex0, offset, static_cast<u8>(value));
+ istream_.Emit(opcode, memidx, offset, static_cast<u8>(value));
return Result::Ok;
}
Result BinaryReaderInterp::OnSimdShuffleOpExpr(Opcode opcode, v128 value) {
- CHECK_RESULT(validator_.OnSimdShuffleOp(loc, opcode, value));
+ CHECK_RESULT(validator_.OnSimdShuffleOp(GetLocation(), opcode, value));
istream_.Emit(opcode, value);
return Result::Ok;
}
Result BinaryReaderInterp::OnLoadSplatExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnLoadSplat(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(
+ validator_.OnLoadSplat(GetLocation(), opcode, GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnLoadZeroExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnLoadZero(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(
+ validator_.OnLoadZero(GetLocation(), opcode, GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicLoadExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnAtomicLoad(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(
+ validator_.OnAtomicLoad(GetLocation(), opcode, GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicStoreExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnAtomicStore(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(validator_.OnAtomicStore(GetLocation(), opcode,
+ GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicRmwExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnAtomicRmw(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(
+ validator_.OnAtomicRmw(GetLocation(), opcode, GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicRmwCmpxchgExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(
- validator_.OnAtomicRmwCmpxchg(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(validator_.OnAtomicRmwCmpxchg(GetLocation(), opcode,
+ GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnBinaryExpr(Opcode opcode) {
- CHECK_RESULT(validator_.OnBinary(loc, opcode));
+ CHECK_RESULT(validator_.OnBinary(GetLocation(), opcode));
istream_.Emit(opcode);
return Result::Ok;
}
Result BinaryReaderInterp::OnBlockExpr(Type sig_type) {
- CHECK_RESULT(validator_.OnBlock(loc, sig_type));
+ CHECK_RESULT(validator_.OnBlock(GetLocation(), sig_type));
PushLabel();
return Result::Ok;
}
Result BinaryReaderInterp::OnLoopExpr(Type sig_type) {
- CHECK_RESULT(validator_.OnLoop(loc, sig_type));
- PushLabel(istream_.end());
+ CHECK_RESULT(validator_.OnLoop(GetLocation(), sig_type));
+ PushLabel(LabelKind::Block, istream_.end());
return Result::Ok;
}
Result BinaryReaderInterp::OnIfExpr(Type sig_type) {
- CHECK_RESULT(validator_.OnIf(loc, sig_type));
+ CHECK_RESULT(validator_.OnIf(GetLocation(), sig_type));
istream_.Emit(Opcode::InterpBrUnless);
auto fixup = istream_.EmitFixupU32();
- PushLabel(Istream::kInvalidOffset, fixup);
+ PushLabel(LabelKind::Block, Istream::kInvalidOffset, fixup);
return Result::Ok;
}
Result BinaryReaderInterp::OnElseExpr() {
- CHECK_RESULT(validator_.OnElse(loc));
+ CHECK_RESULT(validator_.OnElse(GetLocation()));
Label* label = TopLabel();
Istream::Offset fixup_cond_offset = label->fixup_offset;
istream_.Emit(Opcode::Br);
}
Result BinaryReaderInterp::OnEndExpr() {
+ if (reading_init_expr_ || label_stack_.size() == 1) {
+ return Result::Ok;
+ }
SharedValidator::Label* label;
CHECK_RESULT(validator_.GetLabel(0, &label));
LabelType label_type = label->label_type;
- CHECK_RESULT(validator_.OnEnd(loc));
+ CHECK_RESULT(validator_.OnEnd(GetLocation()));
if (label_type == LabelType::If || label_type == LabelType::Else) {
istream_.ResolveFixupU32(TopLabel()->fixup_offset);
+ } else if (label_type == LabelType::Try) {
+ // Catch-less try blocks need to fill in the handler description
+ // so that it can trigger an exception rethrow when it's reached.
+ Label* local_label = TopLabel();
+ HandlerDesc& desc = func_->handlers[local_label->handler_desc_index];
+ desc.try_end_offset = istream_.end();
+ assert(desc.catches.size() == 0);
+ } else if (label_type == LabelType::Catch) {
+ istream_.EmitCatchDrop(1);
}
FixupTopLabel();
PopLabel();
}
Result BinaryReaderInterp::OnBrExpr(Index depth) {
- Index drop_count, keep_count;
+ Index drop_count, keep_count, catch_drop_count;
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
- CHECK_RESULT(validator_.OnBr(loc, Var(depth)));
- EmitBr(depth, drop_count, keep_count);
+ CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count));
+ CHECK_RESULT(validator_.OnBr(GetLocation(), Var(depth)));
+ EmitBr(depth, drop_count, keep_count, catch_drop_count);
return Result::Ok;
}
Result BinaryReaderInterp::OnBrIfExpr(Index depth) {
- Index drop_count, keep_count;
- CHECK_RESULT(validator_.OnBrIf(loc, Var(depth)));
+ Index drop_count, keep_count, catch_drop_count;
+ CHECK_RESULT(validator_.OnBrIf(GetLocation(), Var(depth)));
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
+ CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count));
// Flip the br_if so if <cond> is true it can drop values from the stack.
istream_.Emit(Opcode::InterpBrUnless);
auto fixup = istream_.EmitFixupU32();
- EmitBr(depth, drop_count, keep_count);
+ EmitBr(depth, drop_count, keep_count, catch_drop_count);
istream_.ResolveFixupU32(fixup);
return Result::Ok;
}
Result BinaryReaderInterp::OnBrTableExpr(Index num_targets,
Index* target_depths,
Index default_target_depth) {
- CHECK_RESULT(validator_.BeginBrTable(loc));
- Index drop_count, keep_count;
+ CHECK_RESULT(validator_.BeginBrTable(GetLocation()));
+ Index drop_count, keep_count, catch_drop_count;
istream_.Emit(Opcode::BrTable, num_targets);
for (Index i = 0; i < num_targets; ++i) {
Index depth = target_depths[i];
- CHECK_RESULT(validator_.OnBrTableTarget(loc, Var(depth)));
+ CHECK_RESULT(validator_.OnBrTableTarget(GetLocation(), Var(depth)));
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
+ CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count));
// Emit DropKeep directly (instead of using EmitDropKeep) so the
- // instruction has a fixed size.
+ // instruction has a fixed size. Same for CatchDrop as well.
istream_.Emit(Opcode::InterpDropKeep, drop_count, keep_count);
- EmitBr(depth, 0, 0);
+ istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
+ EmitBr(depth, 0, 0, 0);
}
- CHECK_RESULT(validator_.OnBrTableTarget(loc, Var(default_target_depth)));
+ CHECK_RESULT(
+ validator_.OnBrTableTarget(GetLocation(), Var(default_target_depth)));
CHECK_RESULT(
GetBrDropKeepCount(default_target_depth, &drop_count, &keep_count));
+ CHECK_RESULT(
+ validator_.GetCatchCount(default_target_depth, &catch_drop_count));
// The default case doesn't need a fixed size, since it is never jumped over.
istream_.EmitDropKeep(drop_count, keep_count);
- EmitBr(default_target_depth, 0, 0);
+ istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
+ EmitBr(default_target_depth, 0, 0, 0);
- CHECK_RESULT(validator_.EndBrTable(loc));
+ CHECK_RESULT(validator_.EndBrTable(GetLocation()));
return Result::Ok;
}
Result BinaryReaderInterp::OnCallExpr(Index func_index) {
- CHECK_RESULT(validator_.OnCall(loc, Var(func_index)));
+ CHECK_RESULT(validator_.OnCall(GetLocation(), Var(func_index)));
if (func_index >= num_func_imports()) {
istream_.Emit(Opcode::Call, func_index);
Result BinaryReaderInterp::OnCallIndirectExpr(Index sig_index,
Index table_index) {
- CHECK_RESULT(
- validator_.OnCallIndirect(loc, Var(sig_index), Var(table_index)));
+ CHECK_RESULT(validator_.OnCallIndirect(GetLocation(), Var(sig_index),
+ Var(table_index)));
istream_.Emit(Opcode::CallIndirect, table_index, sig_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnReturnCallExpr(Index func_index) {
FuncType& func_type = func_types_[func_index];
- Index drop_count, keep_count;
+ Index drop_count, keep_count, catch_drop_count;
CHECK_RESULT(
GetReturnCallDropKeepCount(func_type, 0, &drop_count, &keep_count));
+ CHECK_RESULT(
+ validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count));
// The validator must be run after we get the drop/keep counts, since it
// will change the type stack.
- CHECK_RESULT(validator_.OnReturnCall(loc, Var(func_index)));
+ CHECK_RESULT(validator_.OnReturnCall(GetLocation(), Var(func_index)));
istream_.EmitDropKeep(drop_count, keep_count);
+ istream_.EmitCatchDrop(catch_drop_count);
if (func_index >= num_func_imports()) {
- istream_.Emit(Opcode::Br, GetFuncOffset(func_index));
+ istream_.Emit(Opcode::InterpAdjustFrameForReturnCall, func_index);
+ istream_.Emit(Opcode::Br);
+ // We emit this separately to ensure that the fixup generated by
+ // GetFuncOffset comes after the Br opcode.
+ istream_.Emit(GetFuncOffset(func_index));
} else {
istream_.Emit(Opcode::InterpCallImport, func_index);
istream_.Emit(Opcode::Return);
Index table_index) {
FuncType& func_type = module_.func_types[sig_index];
- Index drop_count, keep_count;
+ Index drop_count, keep_count, catch_drop_count;
// +1 to include the index of the function.
CHECK_RESULT(
GetReturnCallDropKeepCount(func_type, +1, &drop_count, &keep_count));
+ CHECK_RESULT(
+ validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count));
// The validator must be run after we get the drop/keep counts, since it
// changes the type stack.
- CHECK_RESULT(
- validator_.OnReturnCallIndirect(loc, Var(sig_index), Var(table_index)));
+ CHECK_RESULT(validator_.OnReturnCallIndirect(GetLocation(), Var(sig_index),
+ Var(table_index)));
istream_.EmitDropKeep(drop_count, keep_count);
+ istream_.EmitCatchDrop(catch_drop_count);
istream_.Emit(Opcode::ReturnCallIndirect, table_index, sig_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnCompareExpr(Opcode opcode) {
- CHECK_RESULT(validator_.OnCompare(loc, opcode));
+ CHECK_RESULT(validator_.OnCompare(GetLocation(), opcode));
istream_.Emit(opcode);
return Result::Ok;
}
Result BinaryReaderInterp::OnConvertExpr(Opcode opcode) {
- CHECK_RESULT(validator_.OnConvert(loc, opcode));
+ CHECK_RESULT(validator_.OnConvert(GetLocation(), opcode));
istream_.Emit(opcode);
return Result::Ok;
}
Result BinaryReaderInterp::OnDropExpr() {
- CHECK_RESULT(validator_.OnDrop(loc));
+ CHECK_RESULT(validator_.OnDrop(GetLocation()));
istream_.Emit(Opcode::Drop);
return Result::Ok;
}
Result BinaryReaderInterp::OnI32ConstExpr(uint32_t value) {
- CHECK_RESULT(validator_.OnConst(loc, Type::I32));
+ CHECK_RESULT(validator_.OnConst(GetLocation(), Type::I32));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::I32;
+ init_expr_.i32_ = value;
+ return Result::Ok;
+ }
istream_.Emit(Opcode::I32Const, value);
return Result::Ok;
}
Result BinaryReaderInterp::OnI64ConstExpr(uint64_t value) {
- CHECK_RESULT(validator_.OnConst(loc, Type::I64));
+ CHECK_RESULT(validator_.OnConst(GetLocation(), Type::I64));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::I64;
+ init_expr_.i64_ = value;
+ return Result::Ok;
+ }
istream_.Emit(Opcode::I64Const, value);
return Result::Ok;
}
Result BinaryReaderInterp::OnF32ConstExpr(uint32_t value_bits) {
- CHECK_RESULT(validator_.OnConst(loc, Type::F32));
+ CHECK_RESULT(validator_.OnConst(GetLocation(), Type::F32));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::F32;
+ init_expr_.f32_ = Bitcast<f32>(value_bits);
+ return Result::Ok;
+ }
istream_.Emit(Opcode::F32Const, value_bits);
return Result::Ok;
}
Result BinaryReaderInterp::OnF64ConstExpr(uint64_t value_bits) {
- CHECK_RESULT(validator_.OnConst(loc, Type::F64));
+ CHECK_RESULT(validator_.OnConst(GetLocation(), Type::F64));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::F64;
+ init_expr_.f64_ = Bitcast<f64>(value_bits);
+ return Result::Ok;
+ }
istream_.Emit(Opcode::F64Const, value_bits);
return Result::Ok;
}
Result BinaryReaderInterp::OnV128ConstExpr(v128 value_bits) {
- CHECK_RESULT(validator_.OnConst(loc, Type::V128));
+ CHECK_RESULT(validator_.OnConst(GetLocation(), Type::V128));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::V128;
+ init_expr_.v128_ = Bitcast<v128>(value_bits);
+ return Result::Ok;
+ }
istream_.Emit(Opcode::V128Const, value_bits);
return Result::Ok;
}
Result BinaryReaderInterp::OnGlobalGetExpr(Index global_index) {
- CHECK_RESULT(validator_.OnGlobalGet(loc, Var(global_index)));
+ CHECK_RESULT(validator_.OnGlobalGet(GetLocation(), Var(global_index)));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::GlobalGet;
+ init_expr_.index_ = global_index;
+ return Result::Ok;
+ }
istream_.Emit(Opcode::GlobalGet, global_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnGlobalSetExpr(Index global_index) {
- CHECK_RESULT(validator_.OnGlobalSet(loc, Var(global_index)));
+ CHECK_RESULT(validator_.OnGlobalSet(GetLocation(), Var(global_index)));
istream_.Emit(Opcode::GlobalSet, global_index);
return Result::Ok;
}
// will update the type stack size. We need the index to be relative to the
// old stack size.
Index translated_local_index = TranslateLocalIndex(local_index);
- CHECK_RESULT(validator_.OnLocalGet(loc, Var(local_index)));
+ CHECK_RESULT(validator_.OnLocalGet(GetLocation(), Var(local_index)));
istream_.Emit(Opcode::LocalGet, translated_local_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnLocalSetExpr(Index local_index) {
// See comment in OnLocalGetExpr above.
Index translated_local_index = TranslateLocalIndex(local_index);
- CHECK_RESULT(validator_.OnLocalSet(loc, Var(local_index)));
+ CHECK_RESULT(validator_.OnLocalSet(GetLocation(), Var(local_index)));
istream_.Emit(Opcode::LocalSet, translated_local_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnLocalTeeExpr(Index local_index) {
- CHECK_RESULT(validator_.OnLocalTee(loc, Var(local_index)));
+ CHECK_RESULT(validator_.OnLocalTee(GetLocation(), Var(local_index)));
istream_.Emit(Opcode::LocalTee, TranslateLocalIndex(local_index));
return Result::Ok;
}
Result BinaryReaderInterp::OnLoadExpr(Opcode opcode,
+ Index memidx,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnLoad(loc, opcode, GetAlignment(align_log2)));
- istream_.Emit(opcode, kMemoryIndex0, offset);
+ CHECK_RESULT(validator_.OnLoad(GetLocation(), opcode, Var(memidx),
+ GetAlignment(align_log2)));
+ istream_.Emit(opcode, memidx, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnStoreExpr(Opcode opcode,
+ Index memidx,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnStore(loc, opcode, GetAlignment(align_log2)));
- istream_.Emit(opcode, kMemoryIndex0, offset);
+ CHECK_RESULT(validator_.OnStore(GetLocation(), opcode, Var(memidx),
+ GetAlignment(align_log2)));
+ istream_.Emit(opcode, memidx, offset);
return Result::Ok;
}
-Result BinaryReaderInterp::OnMemoryGrowExpr() {
- CHECK_RESULT(validator_.OnMemoryGrow(loc));
- istream_.Emit(Opcode::MemoryGrow, kMemoryIndex0);
+Result BinaryReaderInterp::OnMemoryGrowExpr(Index memidx) {
+ CHECK_RESULT(validator_.OnMemoryGrow(GetLocation(), Var(memidx)));
+ istream_.Emit(Opcode::MemoryGrow, memidx);
return Result::Ok;
}
-Result BinaryReaderInterp::OnMemorySizeExpr() {
- CHECK_RESULT(validator_.OnMemorySize(loc));
- istream_.Emit(Opcode::MemorySize, kMemoryIndex0);
+Result BinaryReaderInterp::OnMemorySizeExpr(Index memidx) {
+ CHECK_RESULT(validator_.OnMemorySize(GetLocation(), Var(memidx)));
+ istream_.Emit(Opcode::MemorySize, memidx);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableGrowExpr(Index table_index) {
- CHECK_RESULT(validator_.OnTableGrow(loc, Var(table_index)));
+ CHECK_RESULT(validator_.OnTableGrow(GetLocation(), Var(table_index)));
istream_.Emit(Opcode::TableGrow, table_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableSizeExpr(Index table_index) {
- CHECK_RESULT(validator_.OnTableSize(loc, Var(table_index)));
+ CHECK_RESULT(validator_.OnTableSize(GetLocation(), Var(table_index)));
istream_.Emit(Opcode::TableSize, table_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableFillExpr(Index table_index) {
- CHECK_RESULT(validator_.OnTableFill(loc, Var(table_index)));
+ CHECK_RESULT(validator_.OnTableFill(GetLocation(), Var(table_index)));
istream_.Emit(Opcode::TableFill, table_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnRefFuncExpr(Index func_index) {
- CHECK_RESULT(validator_.OnRefFunc(loc, Var(func_index)));
+ CHECK_RESULT(validator_.OnRefFunc(GetLocation(), Var(func_index)));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::RefFunc;
+ init_expr_.index_ = func_index;
+ return Result::Ok;
+ }
istream_.Emit(Opcode::RefFunc, func_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnRefNullExpr(Type type) {
- CHECK_RESULT(validator_.OnRefNull(loc, type));
+ CHECK_RESULT(validator_.OnRefNull(GetLocation(), type));
+ if (reading_init_expr_) {
+ init_expr_.kind = InitExprKind::RefNull;
+ init_expr_.type_ = type;
+ return Result::Ok;
+ }
istream_.Emit(Opcode::RefNull);
return Result::Ok;
}
Result BinaryReaderInterp::OnRefIsNullExpr() {
- CHECK_RESULT(validator_.OnRefIsNull(loc));
+ CHECK_RESULT(validator_.OnRefIsNull(GetLocation()));
istream_.Emit(Opcode::RefIsNull);
return Result::Ok;
}
Result BinaryReaderInterp::OnNopExpr() {
- CHECK_RESULT(validator_.OnNop(loc));
+ CHECK_RESULT(validator_.OnNop(GetLocation()));
return Result::Ok;
}
Result BinaryReaderInterp::OnReturnExpr() {
- Index drop_count, keep_count;
+ Index drop_count, keep_count, catch_drop_count;
CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
- CHECK_RESULT(validator_.OnReturn(loc));
+ CHECK_RESULT(
+ validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count));
+ CHECK_RESULT(validator_.OnReturn(GetLocation()));
istream_.EmitDropKeep(drop_count, keep_count);
+ istream_.EmitCatchDrop(catch_drop_count);
istream_.Emit(Opcode::Return);
return Result::Ok;
}
Result BinaryReaderInterp::OnSelectExpr(Index result_count,
Type* result_types) {
- CHECK_RESULT(validator_.OnSelect(loc, result_count, result_types));
+ CHECK_RESULT(validator_.OnSelect(GetLocation(), result_count, result_types));
istream_.Emit(Opcode::Select);
return Result::Ok;
}
Result BinaryReaderInterp::OnUnreachableExpr() {
- CHECK_RESULT(validator_.OnUnreachable(loc));
+ CHECK_RESULT(validator_.OnUnreachable(GetLocation()));
istream_.Emit(Opcode::Unreachable);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicWaitExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(validator_.OnAtomicWait(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(
+ validator_.OnAtomicWait(GetLocation(), opcode, GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicFenceExpr(uint32_t consistency_model) {
- CHECK_RESULT(validator_.OnAtomicFence(loc, consistency_model));
+ CHECK_RESULT(validator_.OnAtomicFence(GetLocation(), consistency_model));
istream_.Emit(Opcode::AtomicFence, consistency_model);
return Result::Ok;
}
Result BinaryReaderInterp::OnAtomicNotifyExpr(Opcode opcode,
Address align_log2,
Address offset) {
- CHECK_RESULT(
- validator_.OnAtomicNotify(loc, opcode, GetAlignment(align_log2)));
+ CHECK_RESULT(validator_.OnAtomicNotify(GetLocation(), opcode,
+ GetAlignment(align_log2)));
istream_.Emit(opcode, kMemoryIndex0, offset);
return Result::Ok;
}
-Result BinaryReaderInterp::OnMemoryCopyExpr() {
- CHECK_RESULT(validator_.OnMemoryCopy(loc));
- istream_.Emit(Opcode::MemoryCopy, kMemoryIndex0, kMemoryIndex0);
+Result BinaryReaderInterp::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) {
+ CHECK_RESULT(
+ validator_.OnMemoryCopy(GetLocation(), Var(srcmemidx), Var(destmemidx)));
+ istream_.Emit(Opcode::MemoryCopy, srcmemidx, destmemidx);
return Result::Ok;
}
Result BinaryReaderInterp::OnDataDropExpr(Index segment_index) {
- CHECK_RESULT(validator_.OnDataDrop(loc, Var(segment_index)));
+ CHECK_RESULT(validator_.OnDataDrop(GetLocation(), Var(segment_index)));
istream_.Emit(Opcode::DataDrop, segment_index);
return Result::Ok;
}
-Result BinaryReaderInterp::OnMemoryFillExpr() {
- CHECK_RESULT(validator_.OnMemoryFill(loc));
- istream_.Emit(Opcode::MemoryFill, kMemoryIndex0);
+Result BinaryReaderInterp::OnMemoryFillExpr(Index memidx) {
+ CHECK_RESULT(validator_.OnMemoryFill(GetLocation(), Var(memidx)));
+ istream_.Emit(Opcode::MemoryFill, memidx);
return Result::Ok;
}
-Result BinaryReaderInterp::OnMemoryInitExpr(Index segment_index) {
- CHECK_RESULT(validator_.OnMemoryInit(loc, Var(segment_index)));
- istream_.Emit(Opcode::MemoryInit, kMemoryIndex0, segment_index);
+Result BinaryReaderInterp::OnMemoryInitExpr(Index segment_index, Index memidx) {
+ CHECK_RESULT(
+ validator_.OnMemoryInit(GetLocation(), Var(segment_index), Var(memidx)));
+ istream_.Emit(Opcode::MemoryInit, memidx, segment_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableGetExpr(Index table_index) {
- CHECK_RESULT(validator_.OnTableGet(loc, Var(table_index)));
+ CHECK_RESULT(validator_.OnTableGet(GetLocation(), Var(table_index)));
istream_.Emit(Opcode::TableGet, table_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableSetExpr(Index table_index) {
- CHECK_RESULT(validator_.OnTableSet(loc, Var(table_index)));
+ CHECK_RESULT(validator_.OnTableSet(GetLocation(), Var(table_index)));
istream_.Emit(Opcode::TableSet, table_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableCopyExpr(Index dst_index, Index src_index) {
- CHECK_RESULT(validator_.OnTableCopy(loc, Var(dst_index), Var(src_index)));
+ CHECK_RESULT(
+ validator_.OnTableCopy(GetLocation(), Var(dst_index), Var(src_index)));
istream_.Emit(Opcode::TableCopy, dst_index, src_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnElemDropExpr(Index segment_index) {
- CHECK_RESULT(validator_.OnElemDrop(loc, Var(segment_index)));
+ CHECK_RESULT(validator_.OnElemDrop(GetLocation(), Var(segment_index)));
istream_.Emit(Opcode::ElemDrop, segment_index);
return Result::Ok;
}
Result BinaryReaderInterp::OnTableInitExpr(Index segment_index,
Index table_index) {
- CHECK_RESULT(
- validator_.OnTableInit(loc, Var(segment_index), Var(table_index)));
+ CHECK_RESULT(validator_.OnTableInit(GetLocation(), Var(segment_index),
+ Var(table_index)));
istream_.Emit(Opcode::TableInit, table_index, segment_index);
return Result::Ok;
}
+Result BinaryReaderInterp::OnThrowExpr(Index tag_index) {
+ CHECK_RESULT(validator_.OnThrow(GetLocation(), Var(tag_index)));
+ istream_.Emit(Opcode::Throw, tag_index);
+ return Result::Ok;
+}
+
+Result BinaryReaderInterp::OnRethrowExpr(Index depth) {
+ Index catch_depth;
+ CHECK_RESULT(validator_.OnRethrow(GetLocation(), Var(depth)));
+ CHECK_RESULT(validator_.GetCatchCount(depth, &catch_depth));
+ // The rethrow opcode takes an index into the exception stack rather than
+ // the number of catch nestings, so we subtract one here.
+ istream_.Emit(Opcode::Rethrow, catch_depth - 1);
+ return Result::Ok;
+}
+
+Result BinaryReaderInterp::OnTryExpr(Type sig_type) {
+ u32 exn_stack_height;
+ CHECK_RESULT(
+ validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height));
+ u32 value_stack_height = validator_.type_stack_size();
+ CHECK_RESULT(validator_.OnTry(GetLocation(), sig_type));
+ // Push a label that tracks mapping of exn -> catch
+ PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
+ func_->handlers.size());
+ func_->handlers.push_back(HandlerDesc{HandlerKind::Catch,
+ istream_.end(),
+ Istream::kInvalidOffset,
+ {},
+ {Istream::kInvalidOffset},
+ value_stack_height,
+ exn_stack_height});
+ return Result::Ok;
+}
+
+Result BinaryReaderInterp::OnCatchExpr(Index tag_index) {
+ CHECK_RESULT(validator_.OnCatch(GetLocation(), Var(tag_index), false));
+ Label* label = TopLabel();
+ HandlerDesc& desc = func_->handlers[label->handler_desc_index];
+ desc.kind = HandlerKind::Catch;
+ // Drop the previous block's exception if it was a catch.
+ if (label->kind == LabelKind::Block) {
+ istream_.EmitCatchDrop(1);
+ }
+ // Jump to the end of the block at the end of the previous try or catch.
+ Istream::Offset offset = label->offset;
+ istream_.Emit(Opcode::Br);
+ assert(offset == Istream::kInvalidOffset);
+ depth_fixups_.Append(label_stack_.size() - 1, istream_.end());
+ istream_.Emit(offset);
+ // The offset is only set after the first catch block, as the offset range
+ // should only cover the try block itself.
+ if (desc.try_end_offset == Istream::kInvalidOffset) {
+ desc.try_end_offset = istream_.end();
+ }
+ // The label kind is switched to Block from Try in order to distinguish
+ // catch blocks from try blocks. This is used to ensure that a try-delegate
+ // inside this catch will not delegate to the catch, and instead find outer
+ // try blocks to use as a delegate target.
+ label->kind = LabelKind::Block;
+ desc.catches.push_back(CatchDesc{tag_index, istream_.end()});
+ return Result::Ok;
+}
+
+Result BinaryReaderInterp::OnCatchAllExpr() {
+ CHECK_RESULT(validator_.OnCatch(GetLocation(), Var(), true));
+ Label* label = TopLabel();
+ HandlerDesc& desc = func_->handlers[label->handler_desc_index];
+ desc.kind = HandlerKind::Catch;
+ if (label->kind == LabelKind::Block) {
+ istream_.EmitCatchDrop(1);
+ }
+ Istream::Offset offset = label->offset;
+ istream_.Emit(Opcode::Br);
+ assert(offset == Istream::kInvalidOffset);
+ depth_fixups_.Append(label_stack_.size() - 1, istream_.end());
+ istream_.Emit(offset);
+ if (desc.try_end_offset == Istream::kInvalidOffset) {
+ desc.try_end_offset = istream_.end();
+ }
+ label->kind = LabelKind::Block;
+ desc.catch_all_offset = istream_.end();
+ return Result::Ok;
+}
+
+Result BinaryReaderInterp::OnDelegateExpr(Index depth) {
+ CHECK_RESULT(validator_.OnDelegate(GetLocation(), Var(depth)));
+ Label* label = TopLabel();
+ assert(label->kind == LabelKind::Try);
+ HandlerDesc& desc = func_->handlers[label->handler_desc_index];
+ desc.kind = HandlerKind::Delegate;
+ Istream::Offset offset = label->offset;
+ istream_.Emit(Opcode::Br);
+ assert(offset == Istream::kInvalidOffset);
+ depth_fixups_.Append(label_stack_.size() - 1, istream_.end());
+ istream_.Emit(offset);
+ desc.try_end_offset = istream_.end();
+ Label* target_label = GetNearestTryLabel(depth + 1);
+ assert(target_label);
+ desc.delegate_handler_index = target_label->handler_desc_index;
+ FixupTopLabel();
+ PopLabel();
+ return Result::Ok;
+}
+
} // namespace
-Result ReadBinaryInterp(const void* data,
+Result ReadBinaryInterp(string_view filename,
+ const void* data,
size_t size,
const ReadBinaryOptions& options,
Errors* errors,
ModuleDesc* out_module) {
- BinaryReaderInterp reader(out_module, errors, options.features);
+ BinaryReaderInterp reader(out_module, filename, errors, options.features);
return ReadBinary(data, size, &reader, options);
}
namespace interp {
-Result ReadBinaryInterp(const void* data,
+Result ReadBinaryInterp(string_view filename,
+ const void* data,
size_t size,
const ReadBinaryOptions& options,
Errors*,
//// Frame ////
inline Frame::Frame(Ref func,
u32 values,
+ u32 exceptions,
u32 offset,
Instance* inst,
Module* mod)
- : func(func), values(values), offset(offset), inst(inst), mod(mod) {}
+ : func(func),
+ values(values),
+ exceptions(exceptions),
+ offset(offset),
+ inst(inst),
+ mod(mod) {}
//// FreeList ////
template <typename T>
template <typename T>
template <typename U>
-RefPtr<T>& RefPtr<T>::operator=(const RefPtr<U>& other){
+RefPtr<T>& RefPtr<T>::operator=(const RefPtr<U>& other) {
obj_ = other.obj_;
store_ = other.store_;
root_index_ = store_ ? store_->CopyRoot(other.root_index_) : 0;
template <> inline bool HasType<f64>(ValueType type) { return type == ValueType::F64; }
template <> inline bool HasType<Ref>(ValueType type) { return IsReference(type); }
-template <typename T> void RequireType(ValueType type) {
+template <typename T>
+void RequireType(ValueType type) {
assert(HasType<T>(type));
}
return features_;
}
+inline std::set<Thread*>& Store::threads() {
+ return threads_;
+}
+
//// Object ////
// static
inline bool Object::classof(const Object* obj) {
return message_;
}
+//// Exception ////
+// static
+inline bool Exception::classof(const Object* obj) {
+ return obj->kind() == skind;
+}
+
+// static
+inline Exception::Ptr Exception::New(Store& store, Ref tag, Values& args) {
+ return store.Alloc<Exception>(store, tag, args);
+}
+
+inline Ref Exception::tag() const {
+ return tag_;
+}
+
+inline Values& Exception::args() {
+ return args_;
+}
+
//// Extern ////
// static
inline bool Extern::classof(const Object* obj) {
inline bool Memory::IsValidAccess(u64 offset, u64 addend, u64 size) const {
// FIXME: make this faster.
- return offset <= data_.size() &&
- addend <= data_.size() &&
- size <= data_.size() &&
- offset + addend + size <= data_.size();
+ return offset <= data_.size() && addend <= data_.size() &&
+ size <= data_.size() && offset + addend + size <= data_.size();
}
inline bool Memory::IsValidAtomicAccess(u64 offset,
if (!IsValidAccess(offset, addend, sizeof(T))) {
return Result::Error;
}
- wabt::MemcpyEndianAware(out, data_.data(), sizeof(T), data_.size(), 0, offset + addend, sizeof(T));
+ wabt::MemcpyEndianAware(out, data_.data(), sizeof(T), data_.size(), 0,
+ offset + addend, sizeof(T));
return Result::Ok;
}
T WABT_VECTORCALL Memory::UnsafeLoad(u64 offset, u64 addend) const {
assert(IsValidAccess(offset, addend, sizeof(T)));
T val;
- wabt::MemcpyEndianAware(&val, data_.data(), sizeof(T), data_.size(), 0, offset + addend, sizeof(T));
+ wabt::MemcpyEndianAware(&val, data_.data(), sizeof(T), data_.size(), 0,
+ offset + addend, sizeof(T));
return val;
}
if (!IsValidAccess(offset, addend, sizeof(T))) {
return Result::Error;
}
- wabt::MemcpyEndianAware(data_.data(), &val, data_.size(), sizeof(T), offset + addend, 0, sizeof(T));
+ wabt::MemcpyEndianAware(data_.data(), &val, data_.size(), sizeof(T),
+ offset + addend, 0, sizeof(T));
return Result::Ok;
}
if (!IsValidAtomicAccess(offset, addend, sizeof(T))) {
return Result::Error;
}
- wabt::MemcpyEndianAware(out, data_.data(), sizeof(T), data_.size(), 0, offset + addend, sizeof(T));
+ wabt::MemcpyEndianAware(out, data_.data(), sizeof(T), data_.size(), 0,
+ offset + addend, sizeof(T));
return Result::Ok;
}
if (!IsValidAtomicAccess(offset, addend, sizeof(T))) {
return Result::Error;
}
- wabt::MemcpyEndianAware(data_.data(), &val, data_.size(), sizeof(T), offset + addend, 0, sizeof(T));
+ wabt::MemcpyEndianAware(data_.data(), &val, data_.size(), sizeof(T),
+ offset + addend, 0, sizeof(T));
return Result::Ok;
}
}
//// Thread ////
-// static
-inline bool Thread::classof(const Object* obj) {
- return obj->kind() == skind;
-}
-
-// static
-inline Thread::Ptr Thread::New(Store& store, const Options& options) {
- return store.Alloc<Thread>(store, options);
-}
-
inline Store& Thread::store() {
return store_;
}
template <>
inline f32 WABT_VECTORCALL FloatCopysign(f32 lhs, f32 rhs) {
return _mm_cvtss_f32(
- _mm_or_ps(
- _mm_and_ps(_mm_set1_ps(lhs), _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))),
- _mm_and_ps(_mm_set1_ps(rhs), _mm_castsi128_ps(_mm_set1_epi32(0x80000000)))));
+ _mm_or_ps(_mm_and_ps(_mm_set1_ps(lhs),
+ _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))),
+ _mm_and_ps(_mm_set1_ps(rhs),
+ _mm_castsi128_ps(_mm_set1_epi32(0x80000000)))));
}
template <>
inline f64 WABT_VECTORCALL FloatCopysign(f64 lhs, f64 rhs) {
- return _mm_cvtsd_f64(
- _mm_or_pd(
- _mm_and_pd(_mm_set1_pd(lhs), _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull))),
- _mm_and_pd(_mm_set1_pd(rhs), _mm_castsi128_pd(_mm_set1_epi64x(0x8000000000000000ull)))));
+ return _mm_cvtsd_f64(_mm_or_pd(
+ _mm_and_pd(_mm_set1_pd(lhs),
+ _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull))),
+ _mm_and_pd(_mm_set1_pd(rhs),
+ _mm_castsi128_pd(_mm_set1_epi64x(0x8000000000000000ull)))));
}
#else
case Type::ExternRef:
return StringPrintf("externref:%" PRIzd, tv.value.Get<Ref>().index);
+ case Type::Reference:
case Type::Func:
case Type::Struct:
case Type::Array:
} // namespace interp
} // namespace wabt
-#endif // WABT_INTERP_UTIL_H_
+#endif // WABT_INTERP_UTIL_H_
static_assert(sizeof(__wasi_fdstat_t) == 24, "witx calculated size");
static_assert(alignof(__wasi_fdstat_t) == 8, "witx calculated align");
static_assert(offsetof(__wasi_fdstat_t, fs_filetype) == 0,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_fdstat_t, fs_rights_base) == 8,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16,
- "witx calculated offset");
+ "witx calculated offset");
struct __wasi_iovec_t {
__wasi_ptr_t buf;
static_assert(offsetof(__wasi_filestat_t, dev) == 0, "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, ino) == 8, "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, filetype) == 16,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, nlink) == 24,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, size) == 32,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, atim) == 40,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, mtim) == 48,
- "witx calculated offset");
+ "witx calculated offset");
static_assert(offsetof(__wasi_filestat_t, ctim) == 56,
- "witx calculated offset");
+ "witx calculated offset");
#define __WASI_ERRNO_SUCCESS (UINT16_C(0))
#define __WASI_ERRNO_NOENT (UINT16_C(44))
* __wasi_timestamp_t *time)
*/
__wasi_timestamp_t t;
- results[0].Set<u32>(
- uvwasi_clock_time_get(uvwasi, params[0].Get<u32>(), params[1].Get<u64>(), &t));
+ results[0].Set<u32>(uvwasi_clock_time_get(uvwasi, params[0].Get<u32>(),
+ params[1].Get<u64>(), &t));
uint32_t time_ptr = params[2].Get<u32>();
CHECK_RESULT(writeValue<__wasi_timestamp_t>(t, time_ptr, trap));
return Result::Ok;
trace_stream->Writef("path_open : %s\n", path);
}
uvwasi_fd_t outfd;
- results[0].Set<u32>(
- uvwasi_path_open(uvwasi, dirfd, dirflags, path, path_len, oflags,
- fs_rights_base, fs_rights_inherting, fs_flags, &outfd));
+ results[0].Set<u32>(uvwasi_path_open(
+ uvwasi, dirfd, dirflags, path, path_len, oflags, fs_rights_base,
+ fs_rights_inherting, fs_flags, &outfd));
if (trace_stream) {
trace_stream->Writef("path_open -> %d\n", results[0].Get<u32>());
}
filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap));
uvwasi_serdes_write_filestat_t(filestat, 0, &buf);
if (trace_stream) {
- trace_stream->Writef("path_filestat_get -> size=%" PRIu64 " %d\n", buf.st_size,
- results[0].Get<u32>());
+ trace_stream->Writef("path_filestat_get -> size=%" PRIu64 " %d\n",
+ buf.st_size, results[0].Get<u32>());
}
return Result::Ok;
}
filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap));
uvwasi_serdes_write_filestat_t(filestat, 0, &buf);
if (trace_stream) {
- trace_stream->Writef("fd_filestat_get -> size=%" PRIu64 " %d\n", buf.st_size,
- results[0].Get<u32>());
+ trace_stream->Writef("fd_filestat_get -> size=%" PRIu64 " %d\n",
+ buf.st_size, results[0].Get<u32>());
}
return Result::Ok;
}
reinterpret_cast<const uint8_t**>(&iovs[i].buf), trap));
}
__wasi_ptr_t* out_addr;
- CHECK_RESULT(getMemPtr<__wasi_ptr_t>(params[3].Get<u32>(), 1, &out_addr, trap));
+ CHECK_RESULT(
+ getMemPtr<__wasi_ptr_t>(params[3].Get<u32>(), 1, &out_addr, trap));
results[0].Set<u32>(
uvwasi_fd_write(uvwasi, fd, iovs.data(), iovs.size(), out_addr));
return Result::Ok;
uvwasi_size_t environ_buf_size;
uvwasi_environ_sizes_get(uvwasi, &environc, &environ_buf_size);
CHECK_RESULT(writeValue<uint32_t>(environc, params[0].Get<u32>(), trap));
- CHECK_RESULT(writeValue<uint32_t>(environ_buf_size, params[1].Get<u32>(), trap));
+ CHECK_RESULT(
+ writeValue<uint32_t>(environ_buf_size, params[1].Get<u32>(), trap));
if (trace_stream) {
trace_stream->Writef("environ_sizes_get -> %d %d\n", environc,
environ_buf_size);
uvwasi_size_t arg_buf_size;
uvwasi_args_sizes_get(uvwasi, &argc, &arg_buf_size);
CHECK_RESULT(writeValue<uint32_t>(argc, params[0].Get<u32>(), trap));
- CHECK_RESULT(writeValue<uint32_t>(arg_buf_size, params[1].Get<u32>(), trap));
+ CHECK_RESULT(
+ writeValue<uint32_t>(arg_buf_size, params[1].Get<u32>(), trap));
if (trace_stream) {
trace_stream->Writef("args_sizes_get -> %d %d\n", argc, arg_buf_size);
}
static ValueType ToWabtValueType(wasm_valkind_t);
static wasm_valkind_t FromWabtValueType(ValueType);
-static wasm_externkind_t FromWabtExternKind(ExternKind );
+static wasm_externkind_t FromWabtExternKind(ExternKind);
static ValueTypes ToWabtValueTypes(const wasm_valtype_vec_t* types);
static void FromWabtValueTypes(const ValueTypes&, wasm_valtype_vec_t* out);
struct wasm_externtype_t {
static std::unique_ptr<wasm_externtype_t> New(std::unique_ptr<ExternType>);
- std::unique_ptr<wasm_externtype_t> Clone() const {
- return New(I->Clone());
- }
+ std::unique_ptr<wasm_externtype_t> Clone() const { return New(I->Clone()); }
virtual ~wasm_externtype_t() {}
wasm_externtype_t(const wasm_externtype_t& other) = delete;
- wasm_externtype_t& operator=(const wasm_externtype_t& other) = delete;
+ wasm_externtype_t& operator=(const wasm_externtype_t& other) = delete;
template <typename T>
- T* As() const { return cast<T>(I.get()); }
+ T* As() const {
+ return cast<T>(I.get());
+ }
std::unique_ptr<ExternType> I;
wasm_ref_t(RefPtr<Object> ptr) : I(ptr) {}
template <typename T>
- T* As() const { return cast<T>(I.get()); }
+ T* As() const {
+ return cast<T>(I.get());
+ }
RefPtr<Object> I;
};
return *this;
}
- ~wasm_module_t() {
- wasm_byte_vec_delete(&binary);
- }
+ ~wasm_module_t() { wasm_byte_vec_delete(&binary); }
// TODO: This is used for wasm_module_serialize/wasm_module_deserialize.
// Currently the standard wasm binary bytes are cached here, but it would be
// better to have a serialization of ModuleDesc instead.
#ifndef NDEBUG
fprintf(stderr, "(");
bool first = true;
- for (auto Type : sig.params) {
+ for (auto type : sig.params) {
if (!first) {
fprintf(stderr, ", ");
}
first = false;
- fprintf(stderr, "%s", Type.GetName());
+ fprintf(stderr, "%s", type.GetName().c_str());
}
fprintf(stderr, ") -> (");
first = true;
- for (auto Type : sig.results) {
+ for (auto type : sig.results) {
if (!first) {
fprintf(stderr, ", ");
}
first = false;
- fprintf(stderr, "%s", Type.GetName());
+ fprintf(stderr, "%s", type.GetName().c_str());
}
fprintf(stderr, ")\n");
#endif
const wasm_byte_vec_t* binary) {
Errors errors;
ModuleDesc module_desc;
- if (Failed(ReadBinaryInterp(binary->data, binary->size, GetOptions(), &errors,
- &module_desc))) {
+ if (Failed(ReadBinaryInterp("<internal>", binary->data, binary->size,
+ GetOptions(), &errors, &module_desc))) {
FormatErrorsToFile(errors, Location::Type::Binary);
return nullptr;
}
return kNames[int(mut)];
}
-const char* GetName(ValueType type) {
+const std::string GetName(ValueType type) {
return type.GetName();
}
const TableType& actual,
std::string* out_msg) {
if (expected.element != actual.element) {
- *out_msg =
- StringPrintf("type mismatch in imported table, expected %s but got %s.",
- GetName(expected.element), GetName(actual.element));
+ *out_msg = StringPrintf(
+ "type mismatch in imported table, expected %s but got %s.",
+ GetName(expected.element).c_str(), GetName(actual.element).c_str());
return Result::Error;
}
!TypesMatch(expected.type, actual.type))) {
*out_msg = StringPrintf(
"type mismatch in imported global, expected %s but got %s.",
- GetName(expected.type), GetName(actual.type));
+ GetName(expected.type).c_str(), GetName(actual.type).c_str());
return Result::Error;
}
Result Match(const TagType& expected,
const TagType& actual,
std::string* out_msg) {
- // TODO signature
+ if (expected.signature != actual.signature) {
+ if (out_msg) {
+ *out_msg = "signature mismatch in imported tag";
+ }
+ return Result::Error;
+ }
return Result::Ok;
}
}
}
+ for (auto thread : threads_) {
+ thread->Mark();
+ }
+
// TODO: better GC algo.
// Loop through all newly marked objects and mark their referents.
std::vector<bool> all_marked(object_count, false);
}
void Store::Mark(const RefVec& refs) {
- for (auto&& ref: refs) {
+ for (auto&& ref : refs) {
Mark(ref);
}
}
}
}
+//// Exception ////
+Exception::Exception(Store& store, Ref tag, Values& args)
+ : Object(skind), tag_(tag), args_(args) {}
+
+void Exception::Mark(Store& store) {
+ Tag::Ptr tag(store, tag_);
+ store.Mark(tag_);
+ ValueTypes params = tag->type().signature;
+ for (size_t i = 0; i < params.size(); i++) {
+ if (params[i].IsRef()) {
+ store.Mark(args_[i].Get<Ref>());
+ }
+ }
+}
+
//// Extern ////
template <typename T>
Result Extern::MatchImpl(Store& store,
Values& results,
Trap::Ptr* out_trap,
Stream* trace_stream) {
- Thread::Options options;
- options.trace_stream = trace_stream;
- Thread::Ptr thread = Thread::New(store, options);
- return DoCall(*thread, params, results, out_trap);
+ Thread thread(store, trace_stream);
+ return DoCall(thread, params, results, out_trap);
}
Result Func::Call(Thread& thread,
result = thread.Run(out_trap);
if (result == RunResult::Trap) {
return Result::Error;
+ } else if (result == RunResult::Exception) {
+ // While this is not actually a trap, it is a convenient way
+ // to report an uncaught exception.
+ *out_trap = Trap::New(thread.store(), "uncaught exception");
+ return Result::Error;
}
thread.PopValues(type_.results, &results);
return Result::Ok;
u32 new_size;
if (store.HasValueType(ref, type_.element) &&
CanGrow<u32>(type_.limits, old_size, count, &new_size)) {
+ // Grow the limits of the table too, so that if it is used as an
+ // import to another module its new size is honored.
+ type_.limits.initial += count;
elements_.resize(new_size);
Fill(store, old_size, ref, new_size - old_size);
return Result::Ok;
}
Result Table::Fill(Store& store, u32 offset, Ref ref, u32 size) {
- if (IsValidRange(offset, size) &&
- store.HasValueType(ref, type_.element)) {
+ if (IsValidRange(offset, size) && store.HasValueType(ref, type_.element)) {
std::fill(elements_.begin() + offset, elements_.begin() + offset + size,
ref);
return Result::Ok;
Result Memory::Grow(u64 count) {
u64 new_pages;
if (CanGrow<u64>(type_.limits, pages_, count, &new_pages)) {
+ // Grow the limits of the memory too, so that if it is used as an
+ // import to another module its new size is honored.
+ type_.limits.initial += count;
#if WABT_BIG_ENDIAN
auto old_size = data_.size();
#endif
//// Module ////
Module::Module(Store&, ModuleDesc desc)
: Object(skind), desc_(std::move(desc)) {
- for (auto&& import: desc_.imports) {
+ for (auto&& import : desc_.imports) {
import_types_.emplace_back(import.type);
}
- for (auto&& export_: desc_.exports) {
+ for (auto&& export_ : desc_.exports) {
export_types_.emplace_back(export_.type);
}
}
}
// Exports.
- for (auto&& desc : mod->desc().exports){
+ for (auto&& desc : mod->desc().exports) {
Ref ref;
switch (desc.type.type->kind) {
case ExternKind::Func: ref = inst->funcs_[desc.index]; break;
if (Failed(result)) {
*out_trap = Trap::New(
- store,
- StringPrintf("out of bounds memory access: data segment is "
- "out of bounds: [%" PRIu64 ", %" PRIu64
- ") >= max value %"
- PRIu64, offset, offset + segment.size(),
- memory->ByteSize()));
+ store, StringPrintf(
+ "out of bounds memory access: data segment is "
+ "out of bounds: [%" PRIu64 ", %" PRIu64
+ ") >= max value %" PRIu64,
+ offset, offset + segment.size(), memory->ByteSize()));
return {};
}
} else if (desc.mode == SegmentMode::Declared) {
}
//// Thread ////
-Thread::Thread(Store& store, const Options& options)
- : Object(skind), store_(store) {
+Thread::Thread(Store& store, Stream* trace_stream)
+ : store_(store), trace_stream_(trace_stream) {
+ store.threads().insert(this);
+
+ Thread::Options options;
frames_.reserve(options.call_stack_size);
values_.reserve(options.value_stack_size);
- trace_stream_ = options.trace_stream;
- if (options.trace_stream) {
+ if (trace_stream) {
trace_source_ = MakeUnique<TraceSource>(this);
}
}
-void Thread::Mark(Store& store) {
+Thread::~Thread() {
+ store_.threads().erase(this);
+}
+
+void Thread::Mark() {
for (auto&& frame : frames_) {
- frame.Mark(store);
+ frame.Mark(store_);
}
- for (auto index: refs_) {
- store.Mark(values_[index].Get<Ref>());
+ for (auto index : refs_) {
+ store_.Mark(values_[index].Get<Ref>());
}
+ store_.Mark(exceptions_);
}
void Thread::PushValues(const ValueTypes& types, const Values& values) {
RunResult Thread::PushCall(Ref func, u32 offset, Trap::Ptr* out_trap) {
TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted");
- frames_.emplace_back(func, values_.size(), offset, inst_, mod_);
+ frames_.emplace_back(func, values_.size(), exceptions_.size(), offset, inst_,
+ mod_);
return RunResult::Ok;
}
TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted");
inst_ = store_.UnsafeGet<Instance>(func.instance()).get();
mod_ = store_.UnsafeGet<Module>(inst_->module()).get();
- frames_.emplace_back(func.self(), values_.size(), func.desc().code_offset,
- inst_, mod_);
+ frames_.emplace_back(func.self(), values_.size(), exceptions_.size(),
+ func.desc().code_offset, inst_, mod_);
return RunResult::Ok;
}
TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted");
inst_ = nullptr;
mod_ = nullptr;
- frames_.emplace_back(func.self(), values_.size(), 0, inst_, mod_);
+ frames_.emplace_back(func.self(), values_.size(), exceptions_.size(), 0,
+ inst_, mod_);
return RunResult::Ok;
}
RunResult Thread::PopCall() {
+ // Sanity check that the exception stack was popped correctly.
+ assert(frames_.back().exceptions == exceptions_.size());
+
frames_.pop_back();
if (frames_.empty()) {
return RunResult::Return;
RunResult Thread::Run(int num_instructions, Trap::Ptr* out_trap) {
DefinedFunc::Ptr func{store_, frames_.back().func};
- for (;num_instructions > 0; --num_instructions) {
+ for (; num_instructions > 0; --num_instructions) {
auto result = StepInternal(out_trap);
if (result != RunResult::Ok) {
return result;
break;
}
+ case O::InterpCatchDrop: {
+ auto drop = instr.imm_u32;
+ for (u32 i = 0; i < drop; i++) {
+ exceptions_.pop_back();
+ }
+ break;
+ }
+
+ // This operation adjusts the function reference of the reused frame
+ // after a return_call. This ensures the correct exception handlers are
+ // used for the call.
+ case O::InterpAdjustFrameForReturnCall: {
+ Ref new_func_ref = inst_->funcs()[instr.imm_u32];
+ Frame& current_frame = frames_.back();
+ current_frame.func = new_func_ref;
+ break;
+ }
+
case O::I32TruncSatF32S: return DoUnop(IntTruncSat<s32, f32>);
case O::I32TruncSatF32U: return DoUnop(IntTruncSat<u32, f32>);
case O::I32TruncSatF64S: return DoUnop(IntTruncSat<s32, f64>);
case O::I64AtomicRmw16CmpxchgU: return DoAtomicRmwCmpxchg<u64, u16>(instr, out_trap);
case O::I64AtomicRmw32CmpxchgU: return DoAtomicRmwCmpxchg<u64, u32>(instr, out_trap);
+ case O::Throw: {
+ u32 tag_index = instr.imm_u32;
+ Values params;
+ Ref tag_ref = inst_->tags()[tag_index];
+ Tag::Ptr tag{store_, tag_ref};
+ PopValues(tag->type().signature, ¶ms);
+ Exception::Ptr exn = Exception::New(store_, tag_ref, params);
+ return DoThrow(exn);
+ }
+ case O::Rethrow: {
+ u32 exn_index = instr.imm_u32;
+ Exception::Ptr exn{store_,
+ exceptions_[exceptions_.size() - exn_index - 1]};
+ return DoThrow(exn);
+ }
+
// The following opcodes are either never generated or should never be
// executed.
case O::Nop:
case O::Catch:
case O::CatchAll:
case O::Delegate:
- case O::Throw:
- case O::Rethrow:
case O::InterpData:
case O::Invalid:
WABT_UNREACHABLE;
auto lhs = Pop<S>();
S result;
for (u8 i = 0; i < S::lanes; ++i) {
- result[i] =
- sel[i] < S::lanes ? lhs[sel[i]] : rhs[sel[i] - S::lanes];
+ result[i] = sel[i] < S::lanes ? lhs[sel[i]] : rhs[sel[i] - S::lanes];
}
Push(result);
return RunResult::Ok;
using U = typename S::LaneType;
for (u8 i = 0; i < S::lanes; ++i) {
u8 laneidx = i * 2;
- result[i] = U(val[laneidx]) + U(val[laneidx+1]);
+ result[i] = U(val[laneidx]) + U(val[laneidx + 1]);
}
Push(result);
return RunResult::Ok;
for (u8 i = 0; i < S::lanes; ++i) {
u8 laneidx = i * 2;
SL lo = SL(lhs[laneidx]) * SL(rhs[laneidx]);
- SL hi = SL(lhs[laneidx+1]) * SL(rhs[laneidx+1]);
+ SL hi = SL(lhs[laneidx + 1]) * SL(rhs[laneidx + 1]);
result[i] = Add(lo, hi);
}
Push(result);
return RunResult::Ok;
}
+RunResult Thread::DoThrow(Exception::Ptr exn) {
+ Istream::Offset target_offset = Istream::kInvalidOffset;
+ u32 target_values, target_exceptions;
+ Tag::Ptr exn_tag{store_, exn->tag()};
+ bool popped_frame = false;
+ bool had_catch_all = false;
+
+ // DoThrow is responsible for unwinding the stack at the point at which an
+ // exception is thrown, and also branching to the appropriate catch within
+ // the target try-catch. In a compiler, the tag dispatch might be done in
+ // generated code in a landing pad, but this is easier for the interpreter.
+ while (!frames_.empty()) {
+ const Frame& frame = frames_.back();
+ DefinedFunc::Ptr func{store_, frame.func};
+ u32 pc = frame.offset;
+ auto handlers = func->desc().handlers;
+
+ // We iterate in reverse order, in order to traverse handlers from most
+ // specific (pushed last) to least specific within a nested stack of
+ // try-catch blocks.
+ auto iter = handlers.rbegin();
+ while (iter != handlers.rend()) {
+ const HandlerDesc& handler = *iter;
+ if (pc >= handler.try_start_offset && pc < handler.try_end_offset) {
+ // For a try-delegate, skip part of the traversal by directly going
+ // up to an outer handler specified by the delegate depth.
+ if (handler.kind == HandlerKind::Delegate) {
+ // Subtract one as we're trying to get a reverse iterator that is
+ // offset by `delegate_handler_index` from the first item.
+ iter = handlers.rend() - handler.delegate_handler_index - 1;
+ continue;
+ }
+ // Otherwise, check for a matching catch tag or catch_all.
+ for (auto _catch : handler.catches) {
+ // Here we have to be careful to use the target frame's instance
+ // to look up the tag rather than the throw's instance.
+ Ref catch_tag_ref = frame.inst->tags()[_catch.tag_index];
+ Tag::Ptr catch_tag{store_, catch_tag_ref};
+ if (exn_tag == catch_tag) {
+ target_offset = _catch.offset;
+ target_values = (*iter).values;
+ target_exceptions = (*iter).exceptions;
+ goto found_handler;
+ }
+ }
+ if (handler.catch_all_offset != Istream::kInvalidOffset) {
+ target_offset = handler.catch_all_offset;
+ target_values = (*iter).values;
+ target_exceptions = (*iter).exceptions;
+ had_catch_all = true;
+ goto found_handler;
+ }
+ }
+ iter++;
+ }
+ frames_.pop_back();
+ popped_frame = true;
+ }
+
+ // If the call frames are empty now, the exception is uncaught.
+ assert(frames_.empty());
+ return RunResult::Exception;
+
+found_handler:
+ assert(target_offset != Istream::kInvalidOffset);
+
+ Frame& target_frame = frames_.back();
+ // If the throw crosses call frames, we need to reset the state to that
+ // call frame's values. The stack heights may need to be offset by the
+ // handler's heights as we may be jumping into the middle of the function
+ // code after some stack height changes.
+ if (popped_frame) {
+ inst_ = target_frame.inst;
+ mod_ = target_frame.mod;
+ }
+ values_.resize(target_frame.values + target_values);
+ exceptions_.resize(target_frame.exceptions + target_exceptions);
+ // Jump to the handler.
+ target_frame.offset = target_offset;
+ // When an exception is caught, it needs to be tracked in a stack
+ // to allow for rethrows. This stack is popped on leaving the try-catch
+ // or by control instructions such as `br`.
+ exceptions_.push_back(exn.ref());
+ // Also push exception payload values if applicable.
+ if (!had_catch_all) {
+ PushValues(exn_tag->type().signature, exn->args());
+ }
+ return RunResult::Ok;
+}
+
Thread::TraceSource::TraceSource(Thread* thread) : thread_(thread) {}
std::string Thread::TraceSource::Header(Istream::Offset offset) {
}
}
auto type = index > num_operands
- ? Type(ValueType::Void)
- : instr.op.GetParamType(num_operands - index + 1);
+ ? Type(ValueType::Void)
+ : instr.op.GetParamType(num_operands - index + 1);
if (type == ValueType::Void) {
// Void should never be displayed normally; we only expect to see it when
// the stack may have different a different type. This is likely to occur
#include <cstdint>
#include <functional>
#include <memory>
+#include <set>
#include <string>
#include <type_traits>
#include <vector>
class Module;
class Instance;
class Thread;
-template <typename T> class RefPtr;
+template <typename T>
+class RefPtr;
using s8 = int8_t;
using u8 = uint8_t;
using ValueType = wabt::Type;
using ValueTypes = std::vector<ValueType>;
-template <typename T> bool HasType(ValueType);
-template <typename T> void RequireType(ValueType);
+template <typename T>
+bool HasType(ValueType);
+template <typename T>
+void RequireType(ValueType);
bool IsReference(ValueType);
bool TypesMatch(ValueType expected, ValueType actual);
Null,
Foreign,
Trap,
+ Exception,
DefinedFunc,
HostFunc,
Table,
Tag,
Module,
Instance,
- Thread,
};
const char* GetName(Mutability);
-const char* GetName(ValueType);
+const std::string GetName(ValueType);
const char* GetName(ExternKind);
const char* GetName(ObjectKind);
inline T& operator[](u8 idx) {
#if WABT_BIG_ENDIAN
- idx = (~idx) & (L-1);
+ idx = (~idx) & (L - 1);
#endif
return v[idx];
}
inline T operator[](u8 idx) const {
#if WABT_BIG_ENDIAN
- idx = (~idx) & (L-1);
+ idx = (~idx) & (L - 1);
#endif
return v[idx];
}
u32 end;
};
+// Metadata for representing exception handlers associated with a function's
+// code. This is needed to look up exceptions from call frames from interpreter
+// instructions.
+struct CatchDesc {
+ Index tag_index;
+ u32 offset;
+};
+
+// Handlers for a catch-less `try` or `try-catch` block are included in the
+// Catch kind. `try-delegate` instructions create a Delegate handler.
+enum class HandlerKind { Catch, Delegate };
+
+struct HandlerDesc {
+ HandlerKind kind;
+ u32 try_start_offset;
+ u32 try_end_offset;
+ std::vector<CatchDesc> catches;
+ union {
+ u32 catch_all_offset;
+ u32 delegate_handler_index;
+ };
+ // Local stack heights at the handler site that need to be restored.
+ u32 values;
+ u32 exceptions;
+};
+
struct FuncDesc {
// Includes params.
ValueType GetLocalType(Index) const;
FuncType type;
std::vector<LocalDesc> locals;
u32 code_offset;
+ std::vector<HandlerDesc> handlers;
};
struct TableDesc {
//// Runtime ////
struct Frame {
- explicit Frame(Ref func, u32 values, u32 offset, Instance*, Module*);
+ explicit Frame(Ref func,
+ u32 values,
+ u32 exceptions,
+ u32 offset,
+ Instance*,
+ Module*);
void Mark(Store&);
Ref func;
- u32 values; // Height of the value stack at this activation.
- u32 offset; // Istream offset; either the return PC, or the current PC.
+ u32 values; // Height of the value stack at this activation.
+ u32 exceptions; // Height of the exception stack at this activation.
+ u32 offset; // Istream offset; either the return PC, or the current PC.
// Cached for convenience. Both are null if func is a HostFunc.
Instance* inst;
const T& Get(Index) const;
T& Get(Index);
- Index size() const; // 1 greater than the maximum index.
- Index count() const; // The number of used elements.
+ Index size() const; // 1 greater than the maximum index.
+ Index count() const; // The number of used elements.
private:
// TODO: Optimize memory layout? We could probably store all of this
ObjectList::Index object_count() const;
const Features& features() const;
+ void setFeatures(const Features& features) { features_ = features; }
+
+ std::set<Thread*>& threads();
private:
template <typename T>
friend class RefPtr;
Features features_;
+ // This set contains the currently active Thread objects.
+ std::set<Thread*> threads_;
ObjectList objects_;
RootList roots_;
std::vector<bool> marks_;
std::vector<Frame> trace_;
};
+class Exception : public Object {
+ public:
+ static bool classof(const Object* obj);
+ static const ObjectKind skind = ObjectKind::Exception;
+ static const char* GetTypeName() { return "Exception"; }
+ using Ptr = RefPtr<Exception>;
+
+ static Exception::Ptr New(Store&, Ref tag, Values& args);
+
+ Ref tag() const;
+ Values& args();
+
+ private:
+ friend Store;
+ explicit Exception(Store&, Ref, Values&);
+ void Mark(Store&) override;
+
+ Ref tag_;
+ Values args_;
+};
+
class Extern : public Object {
public:
static bool classof(const Object* obj);
Ok,
Return,
Trap,
+ Exception,
};
-// TODO: Kinda weird to have a thread as an object, but it makes reference
-// marking simpler.
-class Thread : public Object {
+class Thread {
public:
- static bool classof(const Object* obj);
- static const ObjectKind skind = ObjectKind::Thread;
- static const char* GetTypeName() { return "Thread"; }
- using Ptr = RefPtr<Thread>;
-
struct Options {
static const u32 kDefaultValueStackSize = 64 * 1024 / sizeof(Value);
static const u32 kDefaultCallStackSize = 64 * 1024 / sizeof(Frame);
Stream* trace_stream = nullptr;
};
- static Thread::Ptr New(Store&, const Options&);
+ Thread(Store& store, Stream* trace_stream = nullptr);
+ ~Thread();
RunResult Run(Trap::Ptr* out_trap);
RunResult Run(int num_instructions, Trap::Ptr* out_trap);
RunResult Step(Trap::Ptr* out_trap);
Store& store();
+ void Mark();
Instance* GetCallerInstance();
struct TraceSource;
- explicit Thread(Store&, const Options&);
- void Mark(Store&) override;
-
RunResult PushCall(Ref func, u32 offset, Trap::Ptr* out_trap);
RunResult PushCall(const DefinedFunc&, Trap::Ptr* out_trap);
RunResult PushCall(const HostFunc&, Trap::Ptr* out_trap);
template <typename T, typename V = T>
RunResult DoAtomicRmwCmpxchg(Instr, Trap::Ptr* out_trap);
+ RunResult DoThrow(Exception::Ptr exn_ref);
+
RunResult StepInternal(Trap::Ptr* out_trap);
std::vector<Frame> frames_;
std::vector<Value> values_;
std::vector<u32> refs_; // Index into values_.
+ // Exception handling requires tracking a separate stack of caught
+ // exceptions for catch blocks.
+ RefVec exceptions_;
+
// Cached for convenience.
Store& store_;
Instance* inst_ = nullptr;
}
}
+void Istream::EmitCatchDrop(u32 drop) {
+ if (drop > 0) {
+ Emit(Opcode::InterpCatchDrop, drop);
+ }
+}
+
Istream::Offset Istream::EmitFixupU32() {
auto result = end();
EmitInternal(kInvalidOffset);
case Opcode::DataDrop:
case Opcode::ElemDrop:
case Opcode::RefFunc:
+ case Opcode::Throw:
+ case Opcode::Rethrow:
// Index immediate, 0 operands.
instr.kind = InstrKind::Imm_Index_Op_0;
instr.imm_u32 = ReadAt<u32>(offset);
case Opcode::AtomicFence:
case Opcode::I32Const:
case Opcode::InterpAlloca:
+ case Opcode::InterpCatchDrop:
+ case Opcode::InterpAdjustFrameForReturnCall:
// i32/f32 immediate, 0 operands.
instr.kind = InstrKind::Imm_I32_Op_0;
instr.imm_u32 = ReadAt<u32>(offset);
case Opcode::InterpData:
case Opcode::Invalid:
case Opcode::Loop:
- case Opcode::Rethrow:
- case Opcode::Throw:
case Opcode::Try:
case Opcode::ReturnCall:
// Not used.
break;
case InstrKind::Imm_Index_Op_N:
- stream->Writef(" $%u\n", instr.imm_u32); // TODO param/result count?
+ stream->Writef(" $%u\n", instr.imm_u32); // TODO param/result count?
break;
case InstrKind::Imm_Index_Index_Op_3:
break;
case InstrKind::Imm_Index_Offset_Lane_Op_2:
- stream->Writef(" $%u:%s+$%u, %s (Lane imm: $%u)\n", instr.imm_u32x2_u8.fst,
- source->Pick(2, instr).c_str(), instr.imm_u32x2_u8.snd,
- source->Pick(1, instr).c_str(), instr.imm_u32x2_u8.idx);
+ stream->Writef(" $%u:%s+$%u, %s (Lane imm: $%u)\n",
+ instr.imm_u32x2_u8.fst, source->Pick(2, instr).c_str(),
+ instr.imm_u32x2_u8.snd, source->Pick(1, instr).c_str(),
+ instr.imm_u32x2_u8.idx);
break;
case InstrKind::Imm_I32_Op_0:
#define WABT_INTERP_ISTREAM_H_
#include <cstdint>
-#include <vector>
#include <string>
+#include <vector>
#include "src/common.h"
#include "src/opcode.h"
// simplify instruction decoding, disassembling, and tracing. There is an
// example of an instruction that uses this encoding on the right.
enum class InstrKind {
- Imm_0_Op_0, // Nop
- Imm_0_Op_1, // i32.eqz
- Imm_0_Op_2, // i32.add
- Imm_0_Op_3, // select
- Imm_Jump_Op_0, // br
- Imm_Jump_Op_1, // br_if
- Imm_Index_Op_0, // global.get
- Imm_Index_Op_1, // global.set
- Imm_Index_Op_2, // table.set
- Imm_Index_Op_3, // memory.fill
- Imm_Index_Op_N, // call
- Imm_Index_Index_Op_3, // memory.init
- Imm_Index_Index_Op_N, // call_indirect
- Imm_Index_Offset_Op_1, // i32.load
- Imm_Index_Offset_Op_2, // i32.store
- Imm_Index_Offset_Op_3, // i32.atomic.rmw.cmpxchg
- Imm_Index_Offset_Lane_Op_2, // v128.load8_lane
- Imm_I32_Op_0, // i32.const
- Imm_I64_Op_0, // i64.const
- Imm_F32_Op_0, // f32.const
- Imm_F64_Op_0, // f64.const
- Imm_I32_I32_Op_0, // drop_keep
- Imm_I8_Op_1, // i32x4.extract_lane
- Imm_I8_Op_2, // i32x4.replace_lane
- Imm_V128_Op_0, // v128.const
- Imm_V128_Op_2, // i8x16.shuffle
+ Imm_0_Op_0, // Nop
+ Imm_0_Op_1, // i32.eqz
+ Imm_0_Op_2, // i32.add
+ Imm_0_Op_3, // select
+ Imm_Jump_Op_0, // br
+ Imm_Jump_Op_1, // br_if
+ Imm_Index_Op_0, // global.get
+ Imm_Index_Op_1, // global.set
+ Imm_Index_Op_2, // table.set
+ Imm_Index_Op_3, // memory.fill
+ Imm_Index_Op_N, // call
+ Imm_Index_Index_Op_3, // memory.init
+ Imm_Index_Index_Op_N, // call_indirect
+ Imm_Index_Offset_Op_1, // i32.load
+ Imm_Index_Offset_Op_2, // i32.store
+ Imm_Index_Offset_Op_3, // i32.atomic.rmw.cmpxchg
+ Imm_Index_Offset_Lane_Op_2, // v128.load8_lane
+ Imm_I32_Op_0, // i32.const
+ Imm_I64_Op_0, // i64.const
+ Imm_F32_Op_0, // f32.const
+ Imm_F64_Op_0, // f64.const
+ Imm_I32_I32_Op_0, // drop_keep
+ Imm_I8_Op_1, // i32x4.extract_lane
+ Imm_I8_Op_2, // i32x4.replace_lane
+ Imm_V128_Op_0, // v128.const
+ Imm_V128_Op_2, // i8x16.shuffle
};
struct Instr {
u64 imm_u64;
f64 imm_f64;
v128 imm_v128;
- struct { u32 fst, snd; } imm_u32x2;
- struct { u32 fst, snd; u8 idx; } imm_u32x2_u8;
+ struct {
+ u32 fst, snd;
+ } imm_u32x2;
+ struct {
+ u32 fst, snd;
+ u8 idx;
+ } imm_u32x2_u8;
};
};
using SerializedOpcode = u32; // TODO: change to u16
using Offset = u32;
static const Offset kInvalidOffset = ~0;
- // Each br_table entry is made up of two instructions:
+ // Each br_table entry is made up of three instructions:
//
// interp_drop_keep $drop $keep
+ // interp_catch_drop $catches
// br $label
//
// Each opcode is a SerializedOpcode, and each immediate is a u32.
static const Offset kBrTableEntrySize =
- sizeof(SerializedOpcode) * 2 + 3 * sizeof(u32);
+ sizeof(SerializedOpcode) * 3 + 4 * sizeof(u32);
// Emit API.
void Emit(u32);
void Emit(Opcode::Enum, u32, u32);
void Emit(Opcode::Enum, u32, u32, u8);
void EmitDropKeep(u32 drop, u32 keep);
+ void EmitCatchDrop(u32 drop);
Offset EmitFixupU32();
void ResolveFixupU32(Offset);
Offset Trace(Stream*, Offset, TraceSource*) const;
-private:
+ private:
template <typename T>
void WABT_VECTORCALL EmitAt(Offset, T val);
template <typename T>
}
template <typename T>
-inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const
- noexcept {
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end()
+ const noexcept {
return const_iterator(*this, nullptr);
}
}
template <typename T>
-inline typename intrusive_list<T>::size_type intrusive_list<T>::size() const
- noexcept {
+inline typename intrusive_list<T>::size_type intrusive_list<T>::size()
+ const noexcept {
return size_;
}
#include "src/cast.h"
#include "src/common.h"
#include "src/expr-visitor.h"
-#include "src/ir.h"
#include "src/ir-util.h"
+#include "src/ir.h"
#include "src/literal.h"
#include "src/stream.h"
case ExprType::Binary:
case ExprType::Compare:
case ExprType::TableGrow:
- return { 2, 1 };
+ return {2, 1};
case ExprType::AtomicStore:
case ExprType::Store:
case ExprType::TableSet:
- return { 2, 0 };
+ return {2, 0};
case ExprType::Block:
- return { 0, cast<BlockExpr>(&expr)->block.decl.sig.GetNumResults() };
+ return {0, cast<BlockExpr>(&expr)->block.decl.sig.GetNumResults()};
case ExprType::Br:
- return { GetLabelArity(cast<BrExpr>(&expr)->var), 1, true };
+ return {GetLabelArity(cast<BrExpr>(&expr)->var), 1, true};
case ExprType::BrIf: {
Index arity = GetLabelArity(cast<BrIfExpr>(&expr)->var);
- return { arity + 1, arity };
+ return {arity + 1, arity};
}
case ExprType::BrTable:
- return { GetLabelArity(cast<BrTableExpr>(&expr)->default_target) + 1, 1,
- true };
+ return {GetLabelArity(cast<BrTableExpr>(&expr)->default_target) + 1, 1,
+ true};
case ExprType::Call: {
const Var& var = cast<CallExpr>(&expr)->var;
- return { GetFuncParamCount(var), GetFuncResultCount(var) };
+ return {GetFuncParamCount(var), GetFuncResultCount(var)};
}
case ExprType::ReturnCall: {
const Var& var = cast<ReturnCallExpr>(&expr)->var;
- return { GetFuncParamCount(var), GetFuncResultCount(var), true };
+ return {GetFuncParamCount(var), GetFuncResultCount(var), true};
}
case ExprType::CallIndirect: {
const auto* ci_expr = cast<CallIndirectExpr>(&expr);
- return { ci_expr->decl.GetNumParams() + 1,
- ci_expr->decl.GetNumResults() };
+ return {ci_expr->decl.GetNumParams() + 1, ci_expr->decl.GetNumResults()};
}
case ExprType::CallRef: {
const Var& var = cast<CallRefExpr>(&expr)->function_type_index;
- return { GetFuncParamCount(var) + 1, GetFuncResultCount(var) };
+ return {GetFuncParamCount(var) + 1, GetFuncResultCount(var)};
}
case ExprType::ReturnCallIndirect: {
const auto* rci_expr = cast<ReturnCallIndirectExpr>(&expr);
- return { rci_expr->decl.GetNumParams() + 1,
- rci_expr->decl.GetNumResults(), true };
+ return {rci_expr->decl.GetNumParams() + 1, rci_expr->decl.GetNumResults(),
+ true};
}
case ExprType::Const:
case ExprType::TableSize:
case ExprType::RefNull:
case ExprType::RefFunc:
- return { 0, 1 };
+ return {0, 1};
case ExprType::Unreachable:
- return { 0, 1, true };
+ return {0, 1, true};
case ExprType::DataDrop:
case ExprType::ElemDrop:
case ExprType::AtomicFence:
- return { 0, 0 };
+ return {0, 0};
case ExprType::MemoryInit:
case ExprType::TableInit:
case ExprType::MemoryCopy:
case ExprType::TableCopy:
case ExprType::TableFill:
- return { 3, 0 };
+ return {3, 0};
case ExprType::AtomicLoad:
case ExprType::Convert:
case ExprType::RefIsNull:
case ExprType::LoadSplat:
case ExprType::LoadZero:
- return { 1, 1 };
+ return {1, 1};
case ExprType::Drop:
case ExprType::GlobalSet:
case ExprType::LocalSet:
- return { 1, 0 };
+ return {1, 0};
case ExprType::If:
- return { 1, cast<IfExpr>(&expr)->true_.decl.sig.GetNumResults() };
+ return {1, cast<IfExpr>(&expr)->true_.decl.sig.GetNumResults()};
case ExprType::Loop:
- return { 0, cast<LoopExpr>(&expr)->block.decl.sig.GetNumResults() };
+ return {0, cast<LoopExpr>(&expr)->block.decl.sig.GetNumResults()};
case ExprType::Nop:
- return { 0, 0 };
+ return {0, 0};
case ExprType::Return:
- return
- { static_cast<Index>(current_func_->decl.sig.result_types.size()), 1,
- true };
+ return {static_cast<Index>(current_func_->decl.sig.result_types.size()),
+ 1, true};
case ExprType::Rethrow:
- return { 0, 0, true };
+ return {0, 0, true};
case ExprType::AtomicRmwCmpxchg:
case ExprType::AtomicWait:
case ExprType::Select:
- return { 3, 1 };
+ return {3, 1};
case ExprType::Throw: {
auto throw_ = cast<ThrowExpr>(&expr);
if (Tag* tag = module.GetTag(throw_->var)) {
operand_count = tag->decl.sig.param_types.size();
}
- return { operand_count, 0, true };
+ return {operand_count, 0, true};
}
case ExprType::Try:
- return { 0, cast<TryExpr>(&expr)->block.decl.sig.GetNumResults() };
+ return {0, cast<TryExpr>(&expr)->block.decl.sig.GetNumResults()};
case ExprType::Ternary:
- return { 3, 1 };
+ return {3, 1};
case ExprType::SimdLaneOp: {
const Opcode opcode = cast<SimdLaneOpExpr>(&expr)->opcode;
case Opcode::I64X2ExtractLane:
case Opcode::F32X4ExtractLane:
case Opcode::F64X2ExtractLane:
- return { 1, 1 };
+ return {1, 1};
case Opcode::I8X16ReplaceLane:
case Opcode::I16X8ReplaceLane:
case Opcode::I64X2ReplaceLane:
case Opcode::F32X4ReplaceLane:
case Opcode::F64X2ReplaceLane:
- return { 2, 1 };
+ return {2, 1};
default:
fprintf(stderr, "Invalid Opcode for expr type: %s\n",
GetExprTypeName(expr));
assert(0);
- return { 0, 0 };
+ return {0, 0};
}
}
case ExprType::SimdLoadLane:
case ExprType::SimdStoreLane: {
- return { 2, 1 };
+ return {2, 1};
}
case ExprType::SimdShuffleOp:
- return { 2, 1 };
+ return {2, 1};
}
WABT_UNREACHABLE;
};
struct ModuleContext {
- ModuleContext(const Module &module) : module(module) {}
+ ModuleContext(const Module& module) : module(module) {}
Index GetLabelStackSize() const { return label_stack_.size(); }
const Label* GetLabel(const Var& var) const;
Index nreturns;
bool unreachable;
Arities(Index na, Index nr, bool ur = false)
- : nargs(na), nreturns(nr), unreachable(ur) {}
+ : nargs(na), nreturns(nr), unreachable(ur) {}
};
Arities GetExprArity(const Expr& expr) const;
- const Module &module;
+ const Module& module;
+
private:
const Func* current_func_ = nullptr;
std::vector<Label> label_stack_;
namespace {
const char* ExprTypeName[] = {
- "AtomicFence",
- "AtomicLoad",
- "AtomicRmw",
- "AtomicRmwCmpxchg",
- "AtomicStore",
- "AtomicNotify",
- "AtomicWait",
- "Binary",
- "Block",
- "Br",
- "BrIf",
- "BrTable",
- "Call",
- "CallIndirect",
- "CallRef",
- "Compare",
- "Const",
- "Convert",
- "Drop",
- "GlobalGet",
- "GlobalSet",
- "If",
- "Load",
- "LocalGet",
- "LocalSet",
- "LocalTee",
- "Loop",
- "MemoryCopy",
- "DataDrop",
- "MemoryFill",
- "MemoryGrow",
- "MemoryInit",
- "MemorySize",
- "Nop",
- "RefIsNull",
- "RefFunc",
- "RefNull",
- "Rethrow",
- "Return",
- "ReturnCall",
- "ReturnCallIndirect",
- "Select",
- "SimdLaneOp",
- "SimdLoadLane",
- "SimdStoreLane",
- "SimdShuffleOp",
- "LoadSplat",
- "LoadZero",
- "Store",
- "TableCopy",
- "ElemDrop",
- "TableInit",
- "TableGet",
- "TableGrow",
- "TableSize",
- "TableSet",
- "TableFill",
- "Ternary",
- "Throw",
- "Try",
- "Unary",
- "Unreachable",
+ "AtomicFence",
+ "AtomicLoad",
+ "AtomicRmw",
+ "AtomicRmwCmpxchg",
+ "AtomicStore",
+ "AtomicNotify",
+ "AtomicWait",
+ "Binary",
+ "Block",
+ "Br",
+ "BrIf",
+ "BrTable",
+ "Call",
+ "CallIndirect",
+ "CallRef",
+ "Compare",
+ "Const",
+ "Convert",
+ "Drop",
+ "GlobalGet",
+ "GlobalSet",
+ "If",
+ "Load",
+ "LocalGet",
+ "LocalSet",
+ "LocalTee",
+ "Loop",
+ "MemoryCopy",
+ "DataDrop",
+ "MemoryFill",
+ "MemoryGrow",
+ "MemoryInit",
+ "MemorySize",
+ "Nop",
+ "RefIsNull",
+ "RefFunc",
+ "RefNull",
+ "Rethrow",
+ "Return",
+ "ReturnCall",
+ "ReturnCallIndirect",
+ "Select",
+ "SimdLaneOp",
+ "SimdLoadLane",
+ "SimdStoreLane",
+ "SimdShuffleOp",
+ "LoadSplat",
+ "LoadZero",
+ "Store",
+ "TableCopy",
+ "ElemDrop",
+ "TableInit",
+ "TableGet",
+ "TableGrow",
+ "TableSize",
+ "TableSet",
+ "TableFill",
+ "Ternary",
+ "Throw",
+ "Try",
+ "Unary",
+ "Unreachable",
};
} // end of anonymous namespace
Type LocalTypes::operator[](Index i) const {
Index count = 0;
- for (auto decl: decls_) {
+ for (auto decl : decls_) {
if (i < count + decl.second) {
return decl.first;
}
break;
}
- all_ref_func = all_ref_func &&
- std::all_of(elem_exprs.begin(), elem_exprs.end(),
- [](const ElemExpr& elem_expr) {
- return elem_expr.kind == ElemExprKind::RefFunc;
- });
+ all_ref_func =
+ all_ref_func &&
+ std::all_of(elem_exprs.begin(), elem_exprs.end(),
+ [](const ExprList& elem_expr) {
+ return elem_expr.front().type() == ExprType::RefFunc;
+ });
if (!all_ref_func) {
flags |= SegUseElemExprs;
}
return flags;
}
-
} // namespace wabt
bool is_index() const { return type_ == VarType::Index; }
bool is_name() const { return type_ == VarType::Name; }
- Index index() const { assert(is_index()); return index_; }
- const std::string& name() const { assert(is_name()); return name_; }
+ Index index() const {
+ assert(is_index());
+ return index_;
+ }
+ const std::string& name() const {
+ assert(is_name());
+ return name_;
+ }
void set_index(Index);
void set_name(std::string&&);
}
Type type() const { return type_; }
- Type lane_type() const { assert(type_ == Type::V128); return lane_type_; }
+ Type lane_type() const {
+ assert(type_ == Type::V128);
+ return lane_type_;
+ }
int lane_count() const {
switch (lane_type()) {
v128 vec128() const { return data_; }
template <typename T>
- T v128_lane(int lane) const { return data_.To<T>(lane); }
+ T v128_lane(int lane) const {
+ return data_.To<T>(lane);
+ }
void set_u32(uint32_t x) { From(Type::I32, x); }
void set_u64(uint64_t x) { From(Type::I64, x); }
void set_v128_f64(int lane, uint64_t x) { set_v128_lane(lane, Type::F64, x); }
// Only used for expectations. (e.g. wast assertions)
- void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); }
- void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); }
- void set_funcref() { From<uintptr_t>(Type::FuncRef, 0); }
+ void set_f32(ExpectedNan nan) {
+ set_f32(0);
+ set_expected_nan(0, nan);
+ }
+ void set_f64(ExpectedNan nan) {
+ set_f64(0);
+ set_expected_nan(0, nan);
+ }
+ void set_funcref() { From<uintptr_t>(Type::FuncRef, 0); }
void set_externref(uintptr_t x) { From(Type::ExternRef, x); }
- void set_null(Type type) { From<uintptr_t>(type, kRefNullBits); }
+ void set_null(Type type) { From<uintptr_t>(type, kRefNullBits); }
bool is_expected_nan(int lane = 0) const {
return expected_nan(lane) != ExpectedNan::None;
}
Type type_;
- Type lane_type_; // Only valid if type_ == Type::V128.
+ Type lane_type_; // Only valid if type_ == Type::V128.
v128 data_;
ExpectedNan nan_[4];
};
TypeVector param_types;
TypeVector result_types;
+ // Some types can have names, for example (ref $foo) has type $foo.
+ // So to use this type we need to translate its name into
+ // a proper index from the module type section.
+ // This is the mapping from parameter/result index to its name.
+ std::unordered_map<uint32_t, std::string> param_type_names;
+ std::unordered_map<uint32_t, std::string> result_type_names;
+
Index GetNumParams() const { return param_types.size(); }
Index GetNumResults() const { return result_types.size(); }
Type GetParamType(Index index) const { return param_types[index]; }
};
typedef std::vector<Catch> CatchVector;
-enum class TryKind {
- Plain,
- Catch,
- Delegate
-};
+enum class TryKind { Plain, Catch, Delegate };
class Expr : public intrusive_list_base<Expr> {
public:
explicit ExprMixin(const Location& loc = Location()) : Expr(TypeEnum, loc) {}
};
+template <ExprType TypeEnum>
+class MemoryExpr : public ExprMixin<TypeEnum> {
+ public:
+ MemoryExpr(Var memidx, const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc), memidx(memidx) {}
+
+ Var memidx;
+};
+
+template <ExprType TypeEnum>
+class MemoryBinaryExpr : public ExprMixin<TypeEnum> {
+ public:
+ MemoryBinaryExpr(Var srcmemidx,
+ Var destmemidx,
+ const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc),
+ srcmemidx(srcmemidx),
+ destmemidx(destmemidx) {}
+
+ Var srcmemidx;
+ Var destmemidx;
+};
+
typedef ExprMixin<ExprType::Drop> DropExpr;
-typedef ExprMixin<ExprType::MemoryGrow> MemoryGrowExpr;
-typedef ExprMixin<ExprType::MemorySize> MemorySizeExpr;
-typedef ExprMixin<ExprType::MemoryCopy> MemoryCopyExpr;
-typedef ExprMixin<ExprType::MemoryFill> MemoryFillExpr;
typedef ExprMixin<ExprType::Nop> NopExpr;
typedef ExprMixin<ExprType::Return> ReturnExpr;
typedef ExprMixin<ExprType::Unreachable> UnreachableExpr;
+typedef MemoryExpr<ExprType::MemoryGrow> MemoryGrowExpr;
+typedef MemoryExpr<ExprType::MemorySize> MemorySizeExpr;
+typedef MemoryExpr<ExprType::MemoryFill> MemoryFillExpr;
+
+typedef MemoryBinaryExpr<ExprType::MemoryCopy> MemoryCopyExpr;
+
template <ExprType TypeEnum>
class RefTypeExpr : public ExprMixin<TypeEnum> {
public:
uint64_t val;
};
-class SimdLoadLaneExpr : public OpcodeExpr<ExprType::SimdLoadLane> {
+class SimdLoadLaneExpr : public MemoryExpr<ExprType::SimdLoadLane> {
public:
SimdLoadLaneExpr(Opcode opcode,
+ Var memidx,
Address align,
Address offset,
uint64_t val,
const Location& loc = Location())
- : OpcodeExpr<ExprType::SimdLoadLane>(opcode, loc),
+ : MemoryExpr<ExprType::SimdLoadLane>(memidx, loc),
+ opcode(opcode),
align(align),
offset(offset),
val(val) {}
+ Opcode opcode;
Address align;
Address offset;
uint64_t val;
};
-class SimdStoreLaneExpr : public OpcodeExpr<ExprType::SimdStoreLane> {
+class SimdStoreLaneExpr : public MemoryExpr<ExprType::SimdStoreLane> {
public:
SimdStoreLaneExpr(Opcode opcode,
+ Var memidx,
Address align,
Address offset,
uint64_t val,
const Location& loc = Location())
- : OpcodeExpr<ExprType::SimdStoreLane>(opcode, loc),
+ : MemoryExpr<ExprType::SimdStoreLane>(memidx, loc),
+ opcode(opcode),
align(align),
offset(offset),
val(val) {}
+ Opcode opcode;
Address align;
Address offset;
uint64_t val;
Var var;
};
+template <ExprType TypeEnum>
+class MemoryVarExpr : public MemoryExpr<TypeEnum> {
+ public:
+ MemoryVarExpr(const Var& var, Var memidx, const Location& loc = Location())
+ : MemoryExpr<TypeEnum>(memidx, loc), var(var) {}
+
+ Var var;
+};
+
typedef VarExpr<ExprType::Br> BrExpr;
typedef VarExpr<ExprType::BrIf> BrIfExpr;
typedef VarExpr<ExprType::Call> CallExpr;
typedef VarExpr<ExprType::Throw> ThrowExpr;
typedef VarExpr<ExprType::Rethrow> RethrowExpr;
-typedef VarExpr<ExprType::MemoryInit> MemoryInitExpr;
typedef VarExpr<ExprType::DataDrop> DataDropExpr;
typedef VarExpr<ExprType::ElemDrop> ElemDropExpr;
typedef VarExpr<ExprType::TableGet> TableGetExpr;
typedef VarExpr<ExprType::TableSize> TableSizeExpr;
typedef VarExpr<ExprType::TableFill> TableFillExpr;
+typedef MemoryVarExpr<ExprType::MemoryInit> MemoryInitExpr;
+
class SelectExpr : public ExprMixin<ExprType::Select> {
public:
SelectExpr(TypeVector type, const Location& loc = Location())
class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> {
public:
- explicit ReturnCallIndirectExpr(const Location &loc = Location())
+ explicit ReturnCallIndirectExpr(const Location& loc = Location())
: ExprMixin<ExprType::ReturnCallIndirect>(loc) {}
FuncDeclaration decl;
class CallRefExpr : public ExprMixin<ExprType::CallRef> {
public:
- explicit CallRefExpr(const Location &loc = Location())
+ explicit CallRefExpr(const Location& loc = Location())
: ExprMixin<ExprType::CallRef>(loc) {}
// This field is setup only during Validate phase,
Address offset;
};
-typedef LoadStoreExpr<ExprType::Load> LoadExpr;
-typedef LoadStoreExpr<ExprType::Store> StoreExpr;
+template <ExprType TypeEnum>
+class MemoryLoadStoreExpr : public MemoryExpr<TypeEnum> {
+ public:
+ MemoryLoadStoreExpr(Opcode opcode,
+ Var memidx,
+ Address align,
+ Address offset,
+ const Location& loc = Location())
+ : MemoryExpr<TypeEnum>(memidx, loc),
+ opcode(opcode),
+ align(align),
+ offset(offset) {}
+
+ Opcode opcode;
+ Address align;
+ Address offset;
+};
+
+typedef MemoryLoadStoreExpr<ExprType::Load> LoadExpr;
+typedef MemoryLoadStoreExpr<ExprType::Store> StoreExpr;
+
typedef LoadStoreExpr<ExprType::AtomicLoad> AtomicLoadExpr;
typedef LoadStoreExpr<ExprType::AtomicStore> AtomicStoreExpr;
typedef LoadStoreExpr<ExprType::AtomicRmw> AtomicRmwExpr;
Type elem_type;
};
-enum class ElemExprKind {
- RefNull,
- RefFunc,
-};
-
-struct ElemExpr {
- ElemExpr() : kind(ElemExprKind::RefNull), type(Type::FuncRef) {}
- explicit ElemExpr(Var var) : kind(ElemExprKind::RefFunc), var(var) {}
- explicit ElemExpr(Type type) : kind(ElemExprKind::RefNull), type(type) {}
-
- ElemExprKind kind;
- Var var; // Only used when kind == RefFunc.
- Type type; // Only used when kind == RefNull
-};
-
-typedef std::vector<ElemExpr> ElemExprVector;
+typedef std::vector<ExprList> ExprListVector;
struct ElemSegment {
explicit ElemSegment(string_view name) : name(name.to_string()) {}
Var table_var;
Type elem_type;
ExprList offset;
- ElemExprVector elem_exprs;
+ ExprListVector elem_exprs;
};
struct Memory {
AssertReturn,
AssertTrap,
AssertExhaustion,
+ AssertException,
First = Module,
- Last = AssertExhaustion,
+ Last = AssertException,
};
static const int kCommandTypeCount = WABT_ENUM_COUNT(CommandType);
typedef AssertModuleCommand<CommandType::AssertUninstantiable>
AssertUninstantiableCommand;
+class AssertExceptionCommand
+ : public CommandMixin<CommandType::AssertException> {
+ public:
+ ActionPtr action;
+};
+
typedef std::unique_ptr<Command> CommandPtr;
typedef std::vector<CommandPtr> CommandPtrVector;
};
%%
array, Type::Array, TokenType::Array
+assert_exception, TokenType::AssertException
assert_exhaustion, TokenType::AssertExhaustion
assert_invalid, TokenType::AssertInvalid
assert_malformed, TokenType::AssertMalformed
call, TokenType::Call, Opcode::Call
catch, TokenType::Catch, Opcode::Catch
catch_all, TokenType::CatchAll, Opcode::CatchAll
-current_memory, TokenType::MemorySize, Opcode::MemorySize
data.drop, TokenType::DataDrop, Opcode::DataDrop
data, TokenType::Data
declare, TokenType::Declare
global.get, TokenType::GlobalGet, Opcode::GlobalGet
global.set, TokenType::GlobalSet, Opcode::GlobalSet
global, TokenType::Global
-grow_memory, TokenType::MemoryGrow, Opcode::MemoryGrow
i16x8.abs, TokenType::Unary, Opcode::I16X8Abs
i16x8.add_sat_s, TokenType::Binary, Opcode::I16X8AddSatS
i16x8.add_sat_u, TokenType::Binary, Opcode::I16X8AddSatU
offset, TokenType::Offset
output, TokenType::Output
param, TokenType::Param
+ref, TokenType::Ref
quote, TokenType::Quote
ref.extern, TokenType::RefExtern
ref.func, TokenType::RefFunc, Opcode::RefFunc
v128.store64_lane, TokenType::SimdStoreLane, Opcode::V128Store64Lane
i8x16.shuffle, TokenType::SimdShuffleOp, Opcode::I8X16Shuffle
i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle
-# Deprecated names.
-atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify
-i32.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait32
-i64.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait64
-anyfunc, Type::FuncRef
-f32.convert_s/i32, TokenType::Convert, Opcode::F32ConvertI32S
-f32.convert_s/i64, TokenType::Convert, Opcode::F32ConvertI64S
-f32.convert_u/i32, TokenType::Convert, Opcode::F32ConvertI32U
-f32.convert_u/i64, TokenType::Convert, Opcode::F32ConvertI64U
-f32.demote/f64, TokenType::Convert, Opcode::F32DemoteF64
-f32.reinterpret/i32, TokenType::Convert, Opcode::F32ReinterpretI32
-f64.convert_s/i32, TokenType::Convert, Opcode::F64ConvertI32S
-f64.convert_s/i64, TokenType::Convert, Opcode::F64ConvertI64S
-f64.convert_u/i32, TokenType::Convert, Opcode::F64ConvertI32U
-f64.convert_u/i64, TokenType::Convert, Opcode::F64ConvertI64U
-f64.promote/f32, TokenType::Convert, Opcode::F64PromoteF32
-f64.reinterpret/i64, TokenType::Convert, Opcode::F64ReinterpretI64
-get_global, TokenType::GlobalGet, Opcode::GlobalGet
-get_local, TokenType::LocalGet, Opcode::LocalGet
-i32.reinterpret/f32, TokenType::Convert, Opcode::I32ReinterpretF32
-i32.trunc_s/f32, TokenType::Convert, Opcode::I32TruncF32S
-i32.trunc_s/f64, TokenType::Convert, Opcode::I32TruncF64S
-i32.trunc_s:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32S
-i32.trunc_s:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64S
-i32.trunc_u/f32, TokenType::Convert, Opcode::I32TruncF32U
-i32.trunc_u/f64, TokenType::Convert, Opcode::I32TruncF64U
-i32.trunc_u:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32U
-i32.trunc_u:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64U
-i32.wrap/i64, TokenType::Convert, Opcode::I32WrapI64
-i64.extend_s/i32, TokenType::Convert, Opcode::I64ExtendI32S
-i64.extend_u/i32, TokenType::Convert, Opcode::I64ExtendI32U
-i64.reinterpret/f64, TokenType::Convert, Opcode::I64ReinterpretF64
-i64.trunc_s/f32, TokenType::Convert, Opcode::I64TruncF32S
-i64.trunc_s/f64, TokenType::Convert, Opcode::I64TruncF64S
-i64.trunc_s:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32S
-i64.trunc_s:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64S
-i64.trunc_u/f32, TokenType::Convert, Opcode::I64TruncF32U
-i64.trunc_u/f64, TokenType::Convert, Opcode::I64TruncF64U
-i64.trunc_u:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32U
-i64.trunc_u:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64U
-set_global, TokenType::GlobalSet, Opcode::GlobalSet
-set_local, TokenType::LocalSet, Opcode::LocalSet
-tee_local, TokenType::LocalTee, Opcode::LocalTee
namespace {
uint32_t AddWithCarry(uint32_t x, uint32_t y, uint32_t* carry) {
// Increments *carry if the addition overflows, otherwise leaves carry alone.
- if ((0xffffffff - x) < y) ++*carry;
+ if ((0xffffffff - x) < y) {
+ ++*carry;
+ }
return x + y;
}
v->set_u32(2, AddWithCarry(v->u32(2), carry_into_v2, &carry_into_v3));
v->set_u32(3, v->u32(3) * 10 + carry_into_v3);
}
-}
+} // namespace
-Result ParseUint128(const char* s,
- const char* end,
- v128* out) {
+Result ParseUint128(const char* s, const char* end, v128* out) {
if (s == end) {
return Result::Error;
}
for (int i = 3; i != 0; --i) {
digits = remainder / 10;
- remainder = ((remainder - digits * 10) << 32) + bits.u32(i-1);
+ remainder = ((remainder - digits * 10) << 32) + bits.u32(i - 1);
bits.set_u32(i, digits);
}
len = size - 1;
}
std::reverse_copy(reversed_buffer + truncated_tail,
- reversed_buffer + len + truncated_tail,
- buffer);
+ reversed_buffer + len + truncated_tail, buffer);
buffer[len] = '\0';
}
#ifndef WABT_OPCODE_CODE_TABLE_H_
#define WABT_OPCODE_CODE_TABLE_H_
-#include <stdlib.h>
#include <stdint.h>
+#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
};
#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
- text, decomp) \
+ text, decomp) \
/* static */ Opcode Opcode::Name##_Opcode(Opcode::Name);
#include "src/opcode.def"
#undef WABT_OPCODE
WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe2, InterpCallImport, "call_import", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe3, InterpData, "data", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe4, InterpDropKeep, "drop_keep", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe5, InterpCatchDrop, "catch_drop", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe6, InterpAdjustFrameForReturnCall, "adjust_frame_for_return_call", "")
/* Saturating float-to-int opcodes (--enable-saturating-float-to-int) */
WABT_OPCODE(I32, F32, ___, ___, 0, 0xfc, 0x00, I32TruncSatF32S, "i32.trunc_sat_f32_s", "")
WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x06, I64TruncSatF64S, "i64.trunc_sat_f64_s", "")
WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x07, I64TruncSatF64U, "i64.trunc_sat_f64_u", "")
-/* Bulk-memory (--enable-bulk-memory) */
+/* Bulk-memory */
WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x08, MemoryInit, "memory.init", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x09, DataDrop, "data.drop", "")
WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0a, MemoryCopy,"memory.copy", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x0d, ElemDrop, "elem.drop", "")
WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0e, TableCopy, "table.copy", "")
-/* Reference types (--enable-reference-types) */
+/* Reference types */
WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x25, TableGet, "table.get", "")
WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x26, TableSet, "table.set", "")
WABT_OPCODE(___, ___, I32, ___, 0, 0xfc, 0x0f, TableGrow, "table.grow", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd1, RefIsNull, "ref.is_null", "")
WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd2, RefFunc, "ref.func", "")
-/* Simd opcodes (--enable-simd) */
+/* Simd opcodes */
WABT_OPCODE(V128, I32, ___, ___, 16, 0xfd, 0x00, V128Load, "v128.load", "")
WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x01, V128Load8X8S, "v128.load8x8_s", "")
WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x02, V128Load8X8U, "v128.load8x8_u", "")
#include <vector>
#include "src/common.h"
-#include "src/opcode-code-table.h"
#include "src/leb128.h"
+#include "src/opcode-code-table.h"
namespace wabt {
return Opcode(EncodeInvalidOpcode(prefix_code));
}
-
} // namespace wabt
#endif // WABT_OPCODE_H_
: program_name_(program_name),
description_(description),
on_error_([this](const std::string& message) { DefaultError(message); }) {
-
// Add common options
AddOption("help", "Print this help message", [this]() {
PrintHelp();
/* C++ code produced by gperf version 3.1 */
/* Command-line: gperf -m 50 -L C++ -N InWordSet -E -t -c --output-file=src/prebuilt/lexer-keywords.cc src/lexer-keywords.txt */
-/* Computed positions: -k'1,3,5-9,11-14,16-19,23,$' */
+/* Computed positions: -k'1,3,5-8,10,12,15,17-19,23,$' */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
Opcode opcode;
};
};
-/* maximum key range = 2613, duplicates = 0 */
+/* maximum key range = 2358, duplicates = 0 */
class Perfect_Hash
{
{
static unsigned short asso_values[] =
{
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 12, 111, 2632, 60,
- 8, 44, 7, 294, 126, 601, 359, 575, 17, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 8, 30, 67, 24, 77,
- 27, 13, 7, 561, 624, 10, 13, 25, 40, 8,
- 9, 53, 353, 537, 140, 7, 7, 9, 162, 131,
- 317, 751, 95, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
- 2632, 2632, 2632, 2632, 2632, 2632, 2632
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 13, 2380, 2380, 540,
+ 325, 4, 181, 3, 173, 368, 142, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 34, 4, 6, 776, 40,
+ 31, 9, 621, 3, 399, 214, 5, 8, 5, 126,
+ 25, 75, 471, 396, 20, 5, 17, 3, 626, 356,
+ 58, 515, 271, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
+ 2380, 2380, 2380, 2380, 2380, 2380, 2380
};
unsigned int hval = len;
hval += asso_values[static_cast<unsigned char>(str[17])];
/*FALLTHROUGH*/
case 17:
- hval += asso_values[static_cast<unsigned char>(str[16]+1)];
+ hval += asso_values[static_cast<unsigned char>(str[16])];
/*FALLTHROUGH*/
case 16:
- hval += asso_values[static_cast<unsigned char>(str[15])];
- /*FALLTHROUGH*/
case 15:
- case 14:
- hval += asso_values[static_cast<unsigned char>(str[13])];
+ hval += asso_values[static_cast<unsigned char>(str[14])];
/*FALLTHROUGH*/
+ case 14:
case 13:
- hval += asso_values[static_cast<unsigned char>(str[12])];
- /*FALLTHROUGH*/
case 12:
hval += asso_values[static_cast<unsigned char>(str[11])];
/*FALLTHROUGH*/
case 11:
- hval += asso_values[static_cast<unsigned char>(str[10])];
- /*FALLTHROUGH*/
case 10:
- case 9:
- hval += asso_values[static_cast<unsigned char>(str[8])];
+ hval += asso_values[static_cast<unsigned char>(str[9])];
/*FALLTHROUGH*/
+ case 9:
case 8:
hval += asso_values[static_cast<unsigned char>(str[7])];
/*FALLTHROUGH*/
hval += asso_values[static_cast<unsigned char>(str[5])];
/*FALLTHROUGH*/
case 5:
- hval += asso_values[static_cast<unsigned char>(str[4]+1)];
+ hval += asso_values[static_cast<unsigned char>(str[4])];
/*FALLTHROUGH*/
case 4:
case 3:
- hval += asso_values[static_cast<unsigned char>(str[2])];
+ hval += asso_values[static_cast<unsigned char>(str[2]+1)];
/*FALLTHROUGH*/
case 2:
case 1:
- hval += asso_values[static_cast<unsigned char>(str[0])];
+ hval += asso_values[static_cast<unsigned char>(str[0]+1)];
break;
}
return hval + asso_values[static_cast<unsigned char>(str[len - 1])];
{
enum
{
- TOTAL_KEYWORDS = 611,
+ TOTAL_KEYWORDS = 569,
MIN_WORD_LENGTH = 2,
MAX_WORD_LENGTH = 29,
- MIN_HASH_VALUE = 19,
- MAX_HASH_VALUE = 2631
+ MIN_HASH_VALUE = 22,
+ MAX_HASH_VALUE = 2379
};
static struct TokenInfo wordlist[] =
{
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 507 "src/lexer-keywords.txt"
- {"if", TokenType::If, Opcode::If},
{""}, {""}, {""}, {""},
-#line 140 "src/lexer-keywords.txt"
- {"f64", Type::F64},
-#line 527 "src/lexer-keywords.txt"
- {"mut", TokenType::Mut},
-#line 82 "src/lexer-keywords.txt"
- {"f32", Type::F32},
-#line 439 "src/lexer-keywords.txt"
- {"i64", Type::I64},
- {""},
-#line 301 "src/lexer-keywords.txt"
- {"i32", Type::I32},
- {""}, {""}, {""},
-#line 557 "src/lexer-keywords.txt"
- {"then", TokenType::Then},
- {""},
-#line 511 "src/lexer-keywords.txt"
- {"item", TokenType::Item},
+#line 40 "src/lexer-keywords.txt"
+ {"data", TokenType::Data},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""},
-#line 47 "src/lexer-keywords.txt"
- {"else", TokenType::Else, Opcode::Else},
-#line 46 "src/lexer-keywords.txt"
- {"elem", TokenType::Elem},
- {""}, {""}, {""},
+#line 123 "src/lexer-keywords.txt"
+ {"f64.ge", TokenType::Compare, Opcode::F64Ge},
+#line 66 "src/lexer-keywords.txt"
+ {"f32.ge", TokenType::Compare, Opcode::F32Ge},
+#line 125 "src/lexer-keywords.txt"
+ {"f64.le", TokenType::Compare, Opcode::F64Le},
+#line 68 "src/lexer-keywords.txt"
+ {"f32.le", TokenType::Compare, Opcode::F32Le},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 526 "src/lexer-keywords.txt"
+ {"mut", TokenType::Mut},
+#line 124 "src/lexer-keywords.txt"
+ {"f64.gt", TokenType::Compare, Opcode::F64Gt},
+#line 67 "src/lexer-keywords.txt"
+ {"f32.gt", TokenType::Compare, Opcode::F32Gt},
#line 127 "src/lexer-keywords.txt"
{"f64.lt", TokenType::Compare, Opcode::F64Lt},
#line 70 "src/lexer-keywords.txt"
{"f32.lt", TokenType::Compare, Opcode::F32Lt},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 50 "src/lexer-keywords.txt"
- {"extern", Type::ExternRef, TokenType::Extern},
+#line 132 "src/lexer-keywords.txt"
+ {"f64.neg", TokenType::Unary, Opcode::F64Neg},
+#line 75 "src/lexer-keywords.txt"
+ {"f32.neg", TokenType::Unary, Opcode::F32Neg},
+#line 133 "src/lexer-keywords.txt"
+ {"f64.ne", TokenType::Compare, Opcode::F64Ne},
+#line 76 "src/lexer-keywords.txt"
+ {"f32.ne", TokenType::Compare, Opcode::F32Ne},
+#line 414 "src/lexer-keywords.txt"
+ {"i64.ne", TokenType::Compare, Opcode::I64Ne},
+#line 277 "src/lexer-keywords.txt"
+ {"i32.ne", TokenType::Compare, Opcode::I32Ne},
+#line 538 "src/lexer-keywords.txt"
+ {"ref.null", TokenType::RefNull, Opcode::RefNull},
+ {""}, {""},
+#line 33 "src/lexer-keywords.txt"
+ {"br", TokenType::Br, Opcode::Br},
+#line 525 "src/lexer-keywords.txt"
+ {"module", TokenType::Module},
{""}, {""},
-#line 125 "src/lexer-keywords.txt"
- {"f64.le", TokenType::Compare, Opcode::F64Le},
-#line 68 "src/lexer-keywords.txt"
- {"f32.le", TokenType::Compare, Opcode::F32Le},
#line 556 "src/lexer-keywords.txt"
{"table", TokenType::Table},
-#line 170 "src/lexer-keywords.txt"
- {"funcref", Type::FuncRef},
-#line 129 "src/lexer-keywords.txt"
- {"f64.min", TokenType::Binary, Opcode::F64Min},
-#line 72 "src/lexer-keywords.txt"
- {"f32.min", TokenType::Binary, Opcode::F32Min},
- {""}, {""},
+#line 540 "src/lexer-keywords.txt"
+ {"result", TokenType::Result},
+#line 399 "src/lexer-keywords.txt"
+ {"i64.ge_u", TokenType::Compare, Opcode::I64GeU},
+#line 264 "src/lexer-keywords.txt"
+ {"i32.ge_u", TokenType::Compare, Opcode::I32GeU},
+#line 403 "src/lexer-keywords.txt"
+ {"i64.le_u", TokenType::Compare, Opcode::I64LeU},
+#line 268 "src/lexer-keywords.txt"
+ {"i32.le_u", TokenType::Compare, Opcode::I32LeU},
+#line 398 "src/lexer-keywords.txt"
+ {"i64.ge_s", TokenType::Compare, Opcode::I64GeS},
+#line 263 "src/lexer-keywords.txt"
+ {"i32.ge_s", TokenType::Compare, Opcode::I32GeS},
+#line 402 "src/lexer-keywords.txt"
+ {"i64.le_s", TokenType::Compare, Opcode::I64LeS},
+#line 267 "src/lexer-keywords.txt"
+ {"i32.le_s", TokenType::Compare, Opcode::I32LeS},
+#line 401 "src/lexer-keywords.txt"
+ {"i64.gt_u", TokenType::Compare, Opcode::I64GtU},
+#line 266 "src/lexer-keywords.txt"
+ {"i32.gt_u", TokenType::Compare, Opcode::I32GtU},
#line 412 "src/lexer-keywords.txt"
- {"i64.lt_s", TokenType::Compare, Opcode::I64LtS},
+ {"i64.lt_u", TokenType::Compare, Opcode::I64LtU},
#line 275 "src/lexer-keywords.txt"
+ {"i32.lt_u", TokenType::Compare, Opcode::I32LtU},
+#line 400 "src/lexer-keywords.txt"
+ {"i64.gt_s", TokenType::Compare, Opcode::I64GtS},
+#line 265 "src/lexer-keywords.txt"
+ {"i32.gt_s", TokenType::Compare, Opcode::I32GtS},
+#line 411 "src/lexer-keywords.txt"
+ {"i64.lt_s", TokenType::Compare, Opcode::I64LtS},
+#line 274 "src/lexer-keywords.txt"
{"i32.lt_s", TokenType::Compare, Opcode::I32LtS},
+#line 544 "src/lexer-keywords.txt"
+ {"return", TokenType::Return, Opcode::Return},
{""},
-#line 169 "src/lexer-keywords.txt"
- {"field", TokenType::Field},
-#line 413 "src/lexer-keywords.txt"
- {"i64.lt_u", TokenType::Compare, Opcode::I64LtU},
-#line 276 "src/lexer-keywords.txt"
- {"i32.lt_u", TokenType::Compare, Opcode::I32LtU},
-#line 403 "src/lexer-keywords.txt"
- {"i64.le_s", TokenType::Compare, Opcode::I64LeS},
-#line 268 "src/lexer-keywords.txt"
- {"i32.le_s", TokenType::Compare, Opcode::I32LeS},
-#line 48 "src/lexer-keywords.txt"
- {"end", TokenType::End, Opcode::End},
-#line 168 "src/lexer-keywords.txt"
- {"f64x2", TokenType::F64X2},
-#line 404 "src/lexer-keywords.txt"
- {"i64.le_u", TokenType::Compare, Opcode::I64LeU},
-#line 269 "src/lexer-keywords.txt"
- {"i32.le_u", TokenType::Compare, Opcode::I32LeU},
-#line 469 "src/lexer-keywords.txt"
- {"i64x2", TokenType::I64X2},
-#line 526 "src/lexer-keywords.txt"
- {"module", TokenType::Module},
-#line 419 "src/lexer-keywords.txt"
- {"i64.rem_s", TokenType::Binary, Opcode::I64RemS},
-#line 282 "src/lexer-keywords.txt"
- {"i32.rem_s", TokenType::Binary, Opcode::I32RemS},
- {""}, {""},
-#line 420 "src/lexer-keywords.txt"
- {"i64.rem_u", TokenType::Binary, Opcode::I64RemU},
-#line 283 "src/lexer-keywords.txt"
- {"i32.rem_u", TokenType::Binary, Opcode::I32RemU},
#line 43 "src/lexer-keywords.txt"
{"do", TokenType::Do},
-#line 111 "src/lexer-keywords.txt"
- {"f64.abs", TokenType::Unary, Opcode::F64Abs},
-#line 53 "src/lexer-keywords.txt"
- {"f32.abs", TokenType::Unary, Opcode::F32Abs},
-#line 138 "src/lexer-keywords.txt"
- {"f64.sub", TokenType::Binary, Opcode::F64Sub},
-#line 80 "src/lexer-keywords.txt"
- {"f32.sub", TokenType::Binary, Opcode::F32Sub},
-#line 531 "src/lexer-keywords.txt"
- {"offset", TokenType::Offset},
-#line 430 "src/lexer-keywords.txt"
- {"i64.sub", TokenType::Binary, Opcode::I64Sub},
-#line 292 "src/lexer-keywords.txt"
- {"i32.sub", TokenType::Binary, Opcode::I32Sub},
- {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 41 "src/lexer-keywords.txt"
+ {"declare", TokenType::Declare},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 131 "src/lexer-keywords.txt"
+ {"f64.nearest", TokenType::Unary, Opcode::F64Nearest},
+#line 74 "src/lexer-keywords.txt"
+ {"f32.nearest", TokenType::Unary, Opcode::F32Nearest},
+ {""},
+#line 548 "src/lexer-keywords.txt"
+ {"struct", Type::Struct, TokenType::Struct},
+#line 551 "src/lexer-keywords.txt"
+ {"table.get", TokenType::TableGet, Opcode::TableGet},
+ {""},
#line 554 "src/lexer-keywords.txt"
{"table.set", TokenType::TableSet, Opcode::TableSet},
-#line 545 "src/lexer-keywords.txt"
- {"select", TokenType::Select, Opcode::Select},
-#line 547 "src/lexer-keywords.txt"
- {"start", TokenType::Start},
-#line 553 "src/lexer-keywords.txt"
- {"table.init", TokenType::TableInit, Opcode::TableInit},
+ {""}, {""},
+#line 349 "src/lexer-keywords.txt"
+ {"i64.and", TokenType::Binary, Opcode::I64And},
+#line 226 "src/lexer-keywords.txt"
+ {"i32.and", TokenType::Binary, Opcode::I32And},
+ {""}, {""},
+#line 112 "src/lexer-keywords.txt"
+ {"f64.add", TokenType::Binary, Opcode::F64Add},
+#line 54 "src/lexer-keywords.txt"
+ {"f32.add", TokenType::Binary, Opcode::F32Add},
+#line 348 "src/lexer-keywords.txt"
+ {"i64.add", TokenType::Binary, Opcode::I64Add},
+#line 225 "src/lexer-keywords.txt"
+ {"i32.add", TokenType::Binary, Opcode::I32Add},
+ {""}, {""}, {""}, {""},
+#line 139 "src/lexer-keywords.txt"
+ {"f64.trunc", TokenType::Unary, Opcode::F64Trunc},
+#line 81 "src/lexer-keywords.txt"
+ {"f32.trunc", TokenType::Unary, Opcode::F32Trunc},
#line 171 "src/lexer-keywords.txt"
{"func", Type::FuncRef, TokenType::Func},
- {""},
-#line 133 "src/lexer-keywords.txt"
- {"f64.ne", TokenType::Compare, Opcode::F64Ne},
-#line 76 "src/lexer-keywords.txt"
- {"f32.ne", TokenType::Compare, Opcode::F32Ne},
- {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
#line 415 "src/lexer-keywords.txt"
- {"i64.ne", TokenType::Compare, Opcode::I64Ne},
+ {"i64.or", TokenType::Binary, Opcode::I64Or},
#line 278 "src/lexer-keywords.txt"
- {"i32.ne", TokenType::Compare, Opcode::I32Ne},
- {""},
-#line 40 "src/lexer-keywords.txt"
- {"data", TokenType::Data},
- {""}, {""}, {""}, {""}, {""},
-#line 350 "src/lexer-keywords.txt"
- {"i64.and", TokenType::Binary, Opcode::I64And},
-#line 227 "src/lexer-keywords.txt"
- {"i32.and", TokenType::Binary, Opcode::I32And},
-#line 157 "src/lexer-keywords.txt"
- {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne},
+ {"i32.or", TokenType::Binary, Opcode::I32Or},
{""},
-#line 153 "src/lexer-keywords.txt"
- {"f64x2.min", TokenType::Binary, Opcode::F64X2Min},
-#line 446 "src/lexer-keywords.txt"
- {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne},
+#line 431 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U},
+#line 293 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U},
{""}, {""},
+#line 430 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S},
+#line 292 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S},
+#line 420 "src/lexer-keywords.txt"
+ {"i64.rotl", TokenType::Binary, Opcode::I64Rotl},
+#line 283 "src/lexer-keywords.txt"
+ {"i32.rotl", TokenType::Binary, Opcode::I32Rotl},
+ {""},
+#line 137 "src/lexer-keywords.txt"
+ {"f64.store", TokenType::Store, Opcode::F64Store},
+#line 79 "src/lexer-keywords.txt"
+ {"f32.store", TokenType::Store, Opcode::F32Store},
+#line 428 "src/lexer-keywords.txt"
+ {"i64.store", TokenType::Store, Opcode::I64Store},
+#line 290 "src/lexer-keywords.txt"
+ {"i32.store", TokenType::Store, Opcode::I32Store},
+ {""}, {""}, {""},
+#line 543 "src/lexer-keywords.txt"
+ {"return_call", TokenType::ReturnCall, Opcode::ReturnCall},
+ {""}, {""}, {""},
#line 130 "src/lexer-keywords.txt"
{"f64.mul", TokenType::Binary, Opcode::F64Mul},
#line 73 "src/lexer-keywords.txt"
{"f32.mul", TokenType::Binary, Opcode::F32Mul},
- {""},
-#line 414 "src/lexer-keywords.txt"
+#line 413 "src/lexer-keywords.txt"
{"i64.mul", TokenType::Binary, Opcode::I64Mul},
-#line 277 "src/lexer-keywords.txt"
+#line 276 "src/lexer-keywords.txt"
{"i32.mul", TokenType::Binary, Opcode::I32Mul},
- {""}, {""},
-#line 112 "src/lexer-keywords.txt"
- {"f64.add", TokenType::Binary, Opcode::F64Add},
-#line 54 "src/lexer-keywords.txt"
- {"f32.add", TokenType::Binary, Opcode::F32Add},
- {""},
-#line 349 "src/lexer-keywords.txt"
- {"i64.add", TokenType::Binary, Opcode::I64Add},
-#line 226 "src/lexer-keywords.txt"
- {"i32.add", TokenType::Binary, Opcode::I32Add},
+ {""}, {""}, {""}, {""}, {""},
+#line 535 "src/lexer-keywords.txt"
+ {"ref.extern", TokenType::RefExtern},
+#line 126 "src/lexer-keywords.txt"
+ {"f64.load", TokenType::Load, Opcode::F64Load},
+#line 69 "src/lexer-keywords.txt"
+ {"f32.load", TokenType::Load, Opcode::F32Load},
+#line 410 "src/lexer-keywords.txt"
+ {"i64.load", TokenType::Load, Opcode::I64Load},
+#line 273 "src/lexer-keywords.txt"
+ {"i32.load", TokenType::Load, Opcode::I32Load},
+#line 36 "src/lexer-keywords.txt"
+ {"call", TokenType::Call, Opcode::Call},
{""},
-#line 151 "src/lexer-keywords.txt"
- {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt},
+#line 421 "src/lexer-keywords.txt"
+ {"i64.rotr", TokenType::Binary, Opcode::I64Rotr},
+#line 284 "src/lexer-keywords.txt"
+ {"i32.rotr", TokenType::Binary, Opcode::I32Rotr},
+ {""}, {""},
+#line 514 "src/lexer-keywords.txt"
+ {"local", TokenType::Local},
+#line 409 "src/lexer-keywords.txt"
+ {"i64.load8_u", TokenType::Load, Opcode::I64Load8U},
+#line 272 "src/lexer-keywords.txt"
+ {"i32.load8_u", TokenType::Load, Opcode::I32Load8U},
+#line 408 "src/lexer-keywords.txt"
+ {"i64.load8_s", TokenType::Load, Opcode::I64Load8S},
+#line 271 "src/lexer-keywords.txt"
+ {"i32.load8_s", TokenType::Load, Opcode::I32Load8S},
#line 114 "src/lexer-keywords.txt"
{"f64.const", TokenType::Const, Opcode::F64Const},
#line 56 "src/lexer-keywords.txt"
{"f32.const", TokenType::Const, Opcode::F32Const},
- {""},
-#line 388 "src/lexer-keywords.txt"
+#line 387 "src/lexer-keywords.txt"
{"i64.const", TokenType::Const, Opcode::I64Const},
-#line 256 "src/lexer-keywords.txt"
+#line 255 "src/lexer-keywords.txt"
{"i32.const", TokenType::Const, Opcode::I32Const},
{""}, {""}, {""},
-#line 546 "src/lexer-keywords.txt"
- {"shared", TokenType::Shared},
- {""},
-#line 163 "src/lexer-keywords.txt"
- {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub},
-#line 150 "src/lexer-keywords.txt"
- {"f64x2.le", TokenType::Compare, Opcode::F64X2Le},
-#line 447 "src/lexer-keywords.txt"
- {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS},
-#line 464 "src/lexer-keywords.txt"
- {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub},
-#line 29 "src/lexer-keywords.txt"
- {"block", TokenType::Block, Opcode::Block},
- {""}, {""}, {""},
-#line 449 "src/lexer-keywords.txt"
- {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS},
-#line 113 "src/lexer-keywords.txt"
- {"f64.ceil", TokenType::Unary, Opcode::F64Ceil},
-#line 55 "src/lexer-keywords.txt"
- {"f32.ceil", TokenType::Unary, Opcode::F32Ceil},
-#line 31 "src/lexer-keywords.txt"
- {"br_table", TokenType::BrTable, Opcode::BrTable},
- {""}, {""},
-#line 550 "src/lexer-keywords.txt"
- {"table.fill", TokenType::TableFill, Opcode::TableFill},
- {""}, {""}, {""},
-#line 35 "src/lexer-keywords.txt"
- {"call", TokenType::Call, Opcode::Call},
- {""}, {""}, {""}, {""},
-#line 32 "src/lexer-keywords.txt"
- {"br", TokenType::Br, Opcode::Br},
- {""}, {""}, {""},
-#line 515 "src/lexer-keywords.txt"
- {"local", TokenType::Local},
+#line 395 "src/lexer-keywords.txt"
+ {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S},
+#line 262 "src/lexer-keywords.txt"
+ {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S},
{""},
-#line 421 "src/lexer-keywords.txt"
- {"i64.rotl", TokenType::Binary, Opcode::I64Rotl},
-#line 284 "src/lexer-keywords.txt"
- {"i32.rotl", TokenType::Binary, Opcode::I32Rotl},
+#line 42 "src/lexer-keywords.txt"
+ {"delegate", TokenType::Delegate},
+#line 469 "src/lexer-keywords.txt"
+ {"i64.xor", TokenType::Binary, Opcode::I64Xor},
+#line 347 "src/lexer-keywords.txt"
+ {"i32.xor", TokenType::Binary, Opcode::I32Xor},
+#line 140 "src/lexer-keywords.txt"
+ {"f64", Type::F64},
{""},
-#line 540 "src/lexer-keywords.txt"
- {"result", TokenType::Result},
-#line 154 "src/lexer-keywords.txt"
- {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul},
+#line 438 "src/lexer-keywords.txt"
+ {"i64", Type::I64},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 419 "src/lexer-keywords.txt"
+ {"i64.rem_u", TokenType::Binary, Opcode::I64RemU},
+#line 282 "src/lexer-keywords.txt"
+ {"i32.rem_u", TokenType::Binary, Opcode::I32RemU},
+#line 418 "src/lexer-keywords.txt"
+ {"i64.rem_s", TokenType::Binary, Opcode::I64RemS},
+#line 281 "src/lexer-keywords.txt"
+ {"i32.rem_s", TokenType::Binary, Opcode::I32RemS},
+#line 511 "src/lexer-keywords.txt"
+ {"local.get", TokenType::LocalGet, Opcode::LocalGet},
{""},
-#line 544 "src/lexer-keywords.txt"
- {"return", TokenType::Return, Opcode::Return},
-#line 444 "src/lexer-keywords.txt"
- {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul},
-#line 513 "src/lexer-keywords.txt"
+#line 512 "src/lexer-keywords.txt"
{"local.set", TokenType::LocalSet, Opcode::LocalSet},
{""}, {""}, {""},
-#line 141 "src/lexer-keywords.txt"
- {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs},
-#line 555 "src/lexer-keywords.txt"
- {"table.size", TokenType::TableSize, Opcode::TableSize},
- {""},
-#line 451 "src/lexer-keywords.txt"
- {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs},
- {""}, {""}, {""}, {""},
-#line 514 "src/lexer-keywords.txt"
+#line 513 "src/lexer-keywords.txt"
{"local.tee", TokenType::LocalTee, Opcode::LocalTee},
- {""},
-#line 548 "src/lexer-keywords.txt"
- {"struct", Type::Struct, TokenType::Struct},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 126 "src/lexer-keywords.txt"
- {"f64.load", TokenType::Load, Opcode::F64Load},
-#line 69 "src/lexer-keywords.txt"
- {"f32.load", TokenType::Load, Opcode::F32Load},
-#line 155 "src/lexer-keywords.txt"
- {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest},
-#line 411 "src/lexer-keywords.txt"
- {"i64.load", TokenType::Load, Opcode::I64Load},
-#line 274 "src/lexer-keywords.txt"
- {"i32.load", TokenType::Load, Opcode::I32Load},
{""}, {""}, {""},
-#line 51 "src/lexer-keywords.txt"
- {"externref", Type::ExternRef},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 143 "src/lexer-keywords.txt"
- {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil},
+#line 545 "src/lexer-keywords.txt"
+ {"select", TokenType::Select, Opcode::Select},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 142 "src/lexer-keywords.txt"
- {"f64x2.add", TokenType::Binary, Opcode::F64X2Add},
- {""}, {""},
-#line 440 "src/lexer-keywords.txt"
- {"i64x2.add", TokenType::Binary, Opcode::I64X2Add},
- {""}, {""},
-#line 390 "src/lexer-keywords.txt"
- {"i64.div_s", TokenType::Binary, Opcode::I64DivS},
-#line 258 "src/lexer-keywords.txt"
- {"i32.div_s", TokenType::Binary, Opcode::I32DivS},
- {""}, {""},
-#line 391 "src/lexer-keywords.txt"
- {"i64.div_u", TokenType::Binary, Opcode::I64DivU},
-#line 259 "src/lexer-keywords.txt"
- {"i32.div_u", TokenType::Binary, Opcode::I32DivU},
+#line 92 "src/lexer-keywords.txt"
+ {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge},
+#line 309 "src/lexer-keywords.txt"
+ {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU},
+#line 94 "src/lexer-keywords.txt"
+ {"f32x4.le", TokenType::Compare, Opcode::F32X4Le},
+#line 313 "src/lexer-keywords.txt"
+ {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU},
{""},
-#line 386 "src/lexer-keywords.txt"
- {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore},
-#line 254 "src/lexer-keywords.txt"
- {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore},
-#line 532 "src/lexer-keywords.txt"
- {"output", TokenType::Output},
+#line 308 "src/lexer-keywords.txt"
+ {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS},
{""},
-#line 510 "src/lexer-keywords.txt"
- {"invoke", TokenType::Invoke},
- {""}, {""}, {""},
-#line 389 "src/lexer-keywords.txt"
- {"i64.ctz", TokenType::Unary, Opcode::I64Ctz},
-#line 257 "src/lexer-keywords.txt"
- {"i32.ctz", TokenType::Unary, Opcode::I32Ctz},
-#line 407 "src/lexer-keywords.txt"
- {"i64.load32_s", TokenType::Load, Opcode::I64Load32S},
-#line 454 "src/lexer-keywords.txt"
- {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask},
-#line 384 "src/lexer-keywords.txt"
- {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32},
+#line 312 "src/lexer-keywords.txt"
+ {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS},
{""},
-#line 408 "src/lexer-keywords.txt"
- {"i64.load32_u", TokenType::Load, Opcode::I64Load32U},
+#line 311 "src/lexer-keywords.txt"
+ {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU},
{""},
-#line 137 "src/lexer-keywords.txt"
- {"f64.store", TokenType::Store, Opcode::F64Store},
-#line 79 "src/lexer-keywords.txt"
- {"f32.store", TokenType::Store, Opcode::F32Store},
+#line 317 "src/lexer-keywords.txt"
+ {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU},
{""},
-#line 429 "src/lexer-keywords.txt"
- {"i64.store", TokenType::Store, Opcode::I64Store},
-#line 291 "src/lexer-keywords.txt"
- {"i32.store", TokenType::Store, Opcode::I32Store},
- {""}, {""}, {""},
-#line 427 "src/lexer-keywords.txt"
- {"i64.store32", TokenType::Store, Opcode::I64Store32},
+#line 310 "src/lexer-keywords.txt"
+ {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS},
{""},
-#line 405 "src/lexer-keywords.txt"
- {"i64.load16_s", TokenType::Load, Opcode::I64Load16S},
-#line 270 "src/lexer-keywords.txt"
- {"i32.load16_s", TokenType::Load, Opcode::I32Load16S},
- {""}, {""},
-#line 406 "src/lexer-keywords.txt"
- {"i64.load16_u", TokenType::Load, Opcode::I64Load16U},
-#line 271 "src/lexer-keywords.txt"
- {"i32.load16_u", TokenType::Load, Opcode::I32Load16U},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 62 "src/lexer-keywords.txt"
- {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64},
+#line 316 "src/lexer-keywords.txt"
+ {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS},
+#line 93 "src/lexer-keywords.txt"
+ {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt},
+#line 100 "src/lexer-keywords.txt"
+ {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg},
+#line 95 "src/lexer-keywords.txt"
+ {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt},
+#line 324 "src/lexer-keywords.txt"
+ {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg},
{""}, {""},
-#line 387 "src/lexer-keywords.txt"
- {"i64.clz", TokenType::Unary, Opcode::I64Clz},
-#line 255 "src/lexer-keywords.txt"
- {"i32.clz", TokenType::Unary, Opcode::I32Clz},
- {""},
-#line 537 "src/lexer-keywords.txt"
- {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""},
-#line 37 "src/lexer-keywords.txt"
- {"catch_all", TokenType::CatchAll, Opcode::CatchAll},
+#line 101 "src/lexer-keywords.txt"
+ {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne},
{""},
-#line 22 "src/lexer-keywords.txt"
- {"assert_invalid", TokenType::AssertInvalid},
-#line 41 "src/lexer-keywords.txt"
- {"declare", TokenType::Declare},
-#line 26 "src/lexer-keywords.txt"
- {"assert_unlinkable", TokenType::AssertUnlinkable},
- {""}, {""}, {""}, {""}, {""},
-#line 630 "src/lexer-keywords.txt"
- {"set_local", TokenType::LocalSet, Opcode::LocalSet},
- {""}, {""}, {""}, {""}, {""},
-#line 631 "src/lexer-keywords.txt"
- {"tee_local", TokenType::LocalTee, Opcode::LocalTee},
- {""}, {""}, {""}, {""}, {""},
-#line 33 "src/lexer-keywords.txt"
- {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect},
-#line 110 "src/lexer-keywords.txt"
- {"f32x4", TokenType::F32X4},
-#line 34 "src/lexer-keywords.txt"
- {"call_ref", TokenType::CallRef, Opcode::CallRef},
+#line 325 "src/lexer-keywords.txt"
+ {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 128 "src/lexer-keywords.txt"
+ {"f64.max", TokenType::Binary, Opcode::F64Max},
+#line 71 "src/lexer-keywords.txt"
+ {"f32.max", TokenType::Binary, Opcode::F32Max},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 85 "src/lexer-keywords.txt"
+ {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 84 "src/lexer-keywords.txt"
+ {"f32x4.add", TokenType::Binary, Opcode::F32X4Add},
{""},
-#line 339 "src/lexer-keywords.txt"
- {"i32x4", TokenType::I32X4},
-#line 131 "src/lexer-keywords.txt"
- {"f64.nearest", TokenType::Unary, Opcode::F64Nearest},
-#line 74 "src/lexer-keywords.txt"
- {"f32.nearest", TokenType::Unary, Opcode::F32Nearest},
+#line 303 "src/lexer-keywords.txt"
+ {"i32x4.add", TokenType::Binary, Opcode::I32X4Add},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 354 "src/lexer-keywords.txt"
+#line 113 "src/lexer-keywords.txt"
+ {"f64.ceil", TokenType::Unary, Opcode::F64Ceil},
+#line 55 "src/lexer-keywords.txt"
+ {"f32.ceil", TokenType::Unary, Opcode::F32Ceil},
+ {""},
+#line 99 "src/lexer-keywords.txt"
+ {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest},
+#line 304 "src/lexer-keywords.txt"
+ {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 433 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U},
+#line 295 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U},
+#line 542 "src/lexer-keywords.txt"
+ {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect},
+ {""},
+#line 432 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S},
+#line 294 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S},
+#line 537 "src/lexer-keywords.txt"
+ {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull},
+ {""}, {""}, {""}, {""},
+#line 555 "src/lexer-keywords.txt"
+ {"table.size", TokenType::TableSize, Opcode::TableSize},
+ {""},
+#line 108 "src/lexer-keywords.txt"
+ {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc},
+ {""},
+#line 405 "src/lexer-keywords.txt"
+ {"i64.load16_u", TokenType::Load, Opcode::I64Load16U},
+#line 270 "src/lexer-keywords.txt"
+ {"i32.load16_u", TokenType::Load, Opcode::I32Load16U},
+ {""}, {""},
+#line 404 "src/lexer-keywords.txt"
+ {"i64.load16_s", TokenType::Load, Opcode::I64Load16S},
+#line 269 "src/lexer-keywords.txt"
+ {"i32.load16_s", TokenType::Load, Opcode::I32Load16S},
+#line 385 "src/lexer-keywords.txt"
+ {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore},
+#line 253 "src/lexer-keywords.txt"
+ {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore},
+#line 393 "src/lexer-keywords.txt"
+ {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S},
+#line 261 "src/lexer-keywords.txt"
+ {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 82 "src/lexer-keywords.txt"
+ {"f32", Type::F32},
+ {""},
+#line 300 "src/lexer-keywords.txt"
+ {"i32", Type::I32},
+ {""}, {""}, {""},
+#line 104 "src/lexer-keywords.txt"
+ {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane},
+#line 365 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU},
+#line 326 "src/lexer-keywords.txt"
+ {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane},
+#line 98 "src/lexer-keywords.txt"
+ {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul},
+ {""},
+#line 323 "src/lexer-keywords.txt"
+ {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul},
+ {""},
+#line 553 "src/lexer-keywords.txt"
+ {"table.init", TokenType::TableInit, Opcode::TableInit},
+ {""}, {""}, {""}, {""}, {""},
+#line 353 "src/lexer-keywords.txt"
{"i64.atomic.load", TokenType::AtomicLoad, Opcode::I64AtomicLoad},
-#line 230 "src/lexer-keywords.txt"
+#line 229 "src/lexer-keywords.txt"
{"i32.atomic.load", TokenType::AtomicLoad, Opcode::I32AtomicLoad},
- {""}, {""},
-#line 538 "src/lexer-keywords.txt"
- {"ref.null", TokenType::RefNull, Opcode::RefNull},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 139 "src/lexer-keywords.txt"
- {"f64.trunc", TokenType::Unary, Opcode::F64Trunc},
-#line 81 "src/lexer-keywords.txt"
- {"f32.trunc", TokenType::Unary, Opcode::F32Trunc},
- {""}, {""}, {""}, {""},
-#line 431 "src/lexer-keywords.txt"
- {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S},
-#line 293 "src/lexer-keywords.txt"
- {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S},
-#line 432 "src/lexer-keywords.txt"
- {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U},
-#line 294 "src/lexer-keywords.txt"
- {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U},
+#line 378 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr},
+#line 247 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr},
+ {""},
+#line 362 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU},
+#line 352 "src/lexer-keywords.txt"
+ {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U},
+#line 228 "src/lexer-keywords.txt"
+ {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U},
{""}, {""}, {""},
-#line 27 "src/lexer-keywords.txt"
- {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence},
+#line 361 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU},
+ {""},
+#line 148 "src/lexer-keywords.txt"
+ {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge},
+#line 110 "src/lexer-keywords.txt"
+ {"f32x4", TokenType::F32X4},
+#line 150 "src/lexer-keywords.txt"
+ {"f64x2.le", TokenType::Compare, Opcode::F64X2Le},
+#line 338 "src/lexer-keywords.txt"
+ {"i32x4", TokenType::I32X4},
+ {""},
+#line 449 "src/lexer-keywords.txt"
+ {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS},
+ {""},
+#line 448 "src/lexer-keywords.txt"
+ {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS},
+ {""}, {""},
+#line 319 "src/lexer-keywords.txt"
+ {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU},
+ {""},
+#line 318 "src/lexer-keywords.txt"
+ {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS},
+#line 447 "src/lexer-keywords.txt"
+ {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS},
+ {""},
+#line 446 "src/lexer-keywords.txt"
+ {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS},
+#line 149 "src/lexer-keywords.txt"
+ {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt},
+#line 156 "src/lexer-keywords.txt"
+ {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg},
+#line 151 "src/lexer-keywords.txt"
+ {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt},
+#line 451 "src/lexer-keywords.txt"
+ {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg},
+#line 509 "src/lexer-keywords.txt"
+ {"invoke", TokenType::Invoke},
+#line 90 "src/lexer-keywords.txt"
+ {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane},
+#line 157 "src/lexer-keywords.txt"
+ {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne},
+#line 307 "src/lexer-keywords.txt"
+ {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane},
+#line 445 "src/lexer-keywords.txt"
+ {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne},
+ {""},
+#line 397 "src/lexer-keywords.txt"
+ {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U},
+#line 96 "src/lexer-keywords.txt"
+ {"f32x4.max", TokenType::Binary, Opcode::F32X4Max},
+#line 396 "src/lexer-keywords.txt"
+ {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S},
+ {""},
+#line 129 "src/lexer-keywords.txt"
+ {"f64.min", TokenType::Binary, Opcode::F64Min},
+#line 72 "src/lexer-keywords.txt"
+ {"f32.min", TokenType::Binary, Opcode::F32Min},
{""}, {""}, {""},
-#line 592 "src/lexer-keywords.txt"
- {"i64.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
-#line 591 "src/lexer-keywords.txt"
- {"i32.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
+#line 49 "src/lexer-keywords.txt"
+ {"tag", TokenType::Tag},
{""},
-#line 101 "src/lexer-keywords.txt"
- {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne},
-#line 567 "src/lexer-keywords.txt"
- {"v128.not", TokenType::Unary, Opcode::V128Not},
-#line 97 "src/lexer-keywords.txt"
- {"f32x4.min", TokenType::Binary, Opcode::F32X4Min},
-#line 326 "src/lexer-keywords.txt"
- {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne},
-#line 120 "src/lexer-keywords.txt"
- {"f64.div", TokenType::Binary, Opcode::F64Div},
-#line 63 "src/lexer-keywords.txt"
- {"f32.div", TokenType::Binary, Opcode::F32Div},
+#line 376 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd},
+#line 245 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd},
+#line 560 "src/lexer-keywords.txt"
+ {"type", TokenType::Type},
+#line 143 "src/lexer-keywords.txt"
+ {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil},
{""}, {""},
+#line 375 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd},
+#line 244 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd},
+ {""},
+#line 427 "src/lexer-keywords.txt"
+ {"i64.store8", TokenType::Store, Opcode::I64Store8},
+#line 289 "src/lexer-keywords.txt"
+ {"i32.store8", TokenType::Store, Opcode::I32Store8},
+#line 142 "src/lexer-keywords.txt"
+ {"f64x2.add", TokenType::Binary, Opcode::F64X2Add},
+#line 172 "src/lexer-keywords.txt"
+ {"get", TokenType::Get},
+#line 439 "src/lexer-keywords.txt"
+ {"i64x2.add", TokenType::Binary, Opcode::I64X2Add},
+ {""}, {""}, {""}, {""},
+#line 364 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU},
#line 422 "src/lexer-keywords.txt"
- {"i64.rotr", TokenType::Binary, Opcode::I64Rotr},
+ {"i64.shl", TokenType::Binary, Opcode::I64Shl},
#line 285 "src/lexer-keywords.txt"
- {"i32.rotr", TokenType::Binary, Opcode::I32Rotr},
- {""}, {""},
-#line 321 "src/lexer-keywords.txt"
- {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS},
-#line 560 "src/lexer-keywords.txt"
- {"type", TokenType::Type},
+ {"i32.shl", TokenType::Binary, Opcode::I32Shl},
{""}, {""},
-#line 322 "src/lexer-keywords.txt"
- {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU},
-#line 598 "src/lexer-keywords.txt"
- {"f32.demote/f64", TokenType::Convert, Opcode::F32DemoteF64},
+#line 155 "src/lexer-keywords.txt"
+ {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest},
+#line 452 "src/lexer-keywords.txt"
+ {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue},
{""},
-#line 95 "src/lexer-keywords.txt"
- {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt},
-#line 509 "src/lexer-keywords.txt"
- {"input", TokenType::Input},
+#line 366 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU},
{""},
-#line 115 "src/lexer-keywords.txt"
- {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S},
-#line 57 "src/lexer-keywords.txt"
- {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S},
- {""}, {""},
+#line 381 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor},
+#line 250 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor},
#line 508 "src/lexer-keywords.txt"
- {"import", TokenType::Import},
+ {"input", TokenType::Input},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
#line 164 "src/lexer-keywords.txt"
{"f64x2.trunc", TokenType::Unary, Opcode::F64X2Trunc},
- {""},
-#line 52 "src/lexer-keywords.txt"
- {"export", TokenType::Export},
-#line 107 "src/lexer-keywords.txt"
- {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub},
-#line 94 "src/lexer-keywords.txt"
- {"f32x4.le", TokenType::Compare, Opcode::F32X4Le},
-#line 317 "src/lexer-keywords.txt"
- {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS},
-#line 332 "src/lexer-keywords.txt"
- {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub},
-#line 318 "src/lexer-keywords.txt"
- {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU},
{""}, {""}, {""},
-#line 313 "src/lexer-keywords.txt"
- {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS},
+#line 507 "src/lexer-keywords.txt"
+ {"import", TokenType::Import},
+ {""}, {""}, {""},
+#line 426 "src/lexer-keywords.txt"
+ {"i64.store32", TokenType::Store, Opcode::I64Store32},
{""},
-#line 314 "src/lexer-keywords.txt"
- {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU},
+#line 367 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU},
+#line 407 "src/lexer-keywords.txt"
+ {"i64.load32_u", TokenType::Load, Opcode::I64Load32U},
+#line 136 "src/lexer-keywords.txt"
+ {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt},
+#line 78 "src/lexer-keywords.txt"
+ {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt},
{""},
-#line 543 "src/lexer-keywords.txt"
- {"return_call", TokenType::ReturnCall, Opcode::ReturnCall},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 453 "src/lexer-keywords.txt"
- {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue},
+#line 406 "src/lexer-keywords.txt"
+ {"i64.load32_s", TokenType::Load, Opcode::I64Load32S},
{""},
-#line 562 "src/lexer-keywords.txt"
- {"v128.andnot", TokenType::Binary, Opcode::V128Andnot},
#line 563 "src/lexer-keywords.txt"
{"v128.and", TokenType::Binary, Opcode::V128And},
-#line 529 "src/lexer-keywords.txt"
- {"nan:canonical", TokenType::NanCanonical},
- {""}, {""}, {""},
-#line 352 "src/lexer-keywords.txt"
- {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U},
- {""}, {""},
-#line 98 "src/lexer-keywords.txt"
- {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul},
{""},
-#line 24 "src/lexer-keywords.txt"
- {"assert_return", TokenType::AssertReturn},
-#line 324 "src/lexer-keywords.txt"
- {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul},
-#line 433 "src/lexer-keywords.txt"
- {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S},
-#line 295 "src/lexer-keywords.txt"
- {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S},
-#line 434 "src/lexer-keywords.txt"
- {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U},
-#line 296 "src/lexer-keywords.txt"
- {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U},
-#line 83 "src/lexer-keywords.txt"
- {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs},
+#line 394 "src/lexer-keywords.txt"
+ {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S},
{""},
-#line 44 "src/lexer-keywords.txt"
- {"drop", TokenType::Drop, Opcode::Drop},
-#line 303 "src/lexer-keywords.txt"
- {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs},
+#line 424 "src/lexer-keywords.txt"
+ {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU},
+#line 287 "src/lexer-keywords.txt"
+ {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU},
+#line 423 "src/lexer-keywords.txt"
+ {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS},
+#line 286 "src/lexer-keywords.txt"
+ {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS},
{""},
-#line 144 "src/lexer-keywords.txt"
- {"f64x2.div", TokenType::Binary, Opcode::F64X2Div},
- {""}, {""}, {""},
-#line 565 "src/lexer-keywords.txt"
- {"v128.const", TokenType::Const, Opcode::V128Const},
- {""}, {""}, {""}, {""},
-#line 23 "src/lexer-keywords.txt"
- {"assert_malformed", TokenType::AssertMalformed},
-#line 516 "src/lexer-keywords.txt"
- {"loop", TokenType::Loop, Opcode::Loop},
- {""}, {""},
-#line 621 "src/lexer-keywords.txt"
- {"i64.trunc_s/f32", TokenType::Convert, Opcode::I64TruncF32S},
-#line 609 "src/lexer-keywords.txt"
- {"i32.trunc_s/f32", TokenType::Convert, Opcode::I32TruncF32S},
-#line 625 "src/lexer-keywords.txt"
- {"i64.trunc_u/f32", TokenType::Convert, Opcode::I64TruncF32U},
-#line 613 "src/lexer-keywords.txt"
- {"i32.trunc_u/f32", TokenType::Convert, Opcode::I32TruncF32U},
-#line 99 "src/lexer-keywords.txt"
- {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest},
-#line 395 "src/lexer-keywords.txt"
- {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S},
+#line 539 "src/lexer-keywords.txt"
+ {"register", TokenType::Register},
+#line 160 "src/lexer-keywords.txt"
+ {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane},
{""},
-#line 159 "src/lexer-keywords.txt"
- {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin},
-#line 147 "src/lexer-keywords.txt"
- {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 117 "src/lexer-keywords.txt"
- {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S},
-#line 59 "src/lexer-keywords.txt"
- {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S},
-#line 397 "src/lexer-keywords.txt"
- {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S},
-#line 85 "src/lexer-keywords.txt"
- {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil},
+#line 458 "src/lexer-keywords.txt"
+ {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane},
+#line 154 "src/lexer-keywords.txt"
+ {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul},
+ {""},
+#line 443 "src/lexer-keywords.txt"
+ {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul},
{""}, {""},
-#line 398 "src/lexer-keywords.txt"
- {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U},
+#line 38 "src/lexer-keywords.txt"
+ {"catch_all", TokenType::CatchAll, Opcode::CatchAll},
+ {""}, {""},
+#line 568 "src/lexer-keywords.txt"
+ {"v128.or", TokenType::Binary, Opcode::V128Or},
+ {""}, {""},
+#line 572 "src/lexer-keywords.txt"
+ {"v128.store", TokenType::Store, Opcode::V128Store},
+#line 566 "src/lexer-keywords.txt"
+ {"v128.load", TokenType::Load, Opcode::V128Load},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 573 "src/lexer-keywords.txt"
+ {"v128", Type::V128},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 534 "src/lexer-keywords.txt"
+ {"quote", TokenType::Quote},
+#line 567 "src/lexer-keywords.txt"
+ {"v128.not", TokenType::Unary, Opcode::V128Not},
{""},
-#line 541 "src/lexer-keywords.txt"
- {"rethrow", TokenType::Rethrow, Opcode::Rethrow},
+#line 531 "src/lexer-keywords.txt"
+ {"output", TokenType::Output},
+ {""}, {""},
+#line 369 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU},
+#line 238 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU},
{""},
-#line 84 "src/lexer-keywords.txt"
- {"f32x4.add", TokenType::Binary, Opcode::F32X4Add},
-#line 435 "src/lexer-keywords.txt"
- {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S},
-#line 297 "src/lexer-keywords.txt"
- {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S},
-#line 304 "src/lexer-keywords.txt"
- {"i32x4.add", TokenType::Binary, Opcode::I32X4Add},
+#line 530 "src/lexer-keywords.txt"
+ {"offset", TokenType::Offset},
+ {""}, {""},
+#line 368 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU},
+#line 237 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU},
+ {""}, {""}, {""}, {""},
+#line 30 "src/lexer-keywords.txt"
+ {"block", TokenType::Block, Opcode::Block},
{""},
-#line 436 "src/lexer-keywords.txt"
- {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U},
-#line 298 "src/lexer-keywords.txt"
- {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U},
+#line 528 "src/lexer-keywords.txt"
+ {"nan:canonical", TokenType::NanCanonical},
{""}, {""},
-#line 564 "src/lexer-keywords.txt"
- {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect},
+#line 146 "src/lexer-keywords.txt"
+ {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane},
+#line 562 "src/lexer-keywords.txt"
+ {"v128.andnot", TokenType::Binary, Opcode::V128Andnot},
+#line 440 "src/lexer-keywords.txt"
+ {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane},
+#line 350 "src/lexer-keywords.txt"
+ {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U},
+#line 227 "src/lexer-keywords.txt"
+ {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U},
+#line 442 "src/lexer-keywords.txt"
+ {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U},
+#line 152 "src/lexer-keywords.txt"
+ {"f64x2.max", TokenType::Binary, Opcode::F64X2Max},
+ {""}, {""},
+#line 441 "src/lexer-keywords.txt"
+ {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S},
+#line 581 "src/lexer-keywords.txt"
+ {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane},
+ {""}, {""}, {""},
+#line 34 "src/lexer-keywords.txt"
+ {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect},
{""},
-#line 383 "src/lexer-keywords.txt"
- {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16},
-#line 252 "src/lexer-keywords.txt"
- {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16},
+#line 576 "src/lexer-keywords.txt"
+ {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat},
+#line 574 "src/lexer-keywords.txt"
+ {"v128.xor", TokenType::Binary, Opcode::V128Xor},
{""},
-#line 373 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU},
-#line 242 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU},
+#line 187 "src/lexer-keywords.txt"
+ {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU},
+#line 565 "src/lexer-keywords.txt"
+ {"v128.const", TokenType::Const, Opcode::V128Const},
+#line 191 "src/lexer-keywords.txt"
+ {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU},
+ {""},
+#line 186 "src/lexer-keywords.txt"
+ {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS},
+ {""},
+#line 190 "src/lexer-keywords.txt"
+ {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS},
+#line 583 "src/lexer-keywords.txt"
+ {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane},
+#line 189 "src/lexer-keywords.txt"
+ {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU},
{""},
-#line 161 "src/lexer-keywords.txt"
- {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat},
- {""}, {""},
-#line 463 "src/lexer-keywords.txt"
- {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat},
+#line 195 "src/lexer-keywords.txt"
+ {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU},
{""},
-#line 426 "src/lexer-keywords.txt"
- {"i64.store16", TokenType::Store, Opcode::I64Store16},
-#line 289 "src/lexer-keywords.txt"
- {"i32.store16", TokenType::Store, Opcode::I32Store16},
-#line 306 "src/lexer-keywords.txt"
- {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask},
+#line 188 "src/lexer-keywords.txt"
+ {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS},
{""},
-#line 566 "src/lexer-keywords.txt"
- {"v128.load", TokenType::Load, Opcode::V128Load},
+#line 194 "src/lexer-keywords.txt"
+ {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS},
+ {""}, {""}, {""},
+#line 203 "src/lexer-keywords.txt"
+ {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg},
+#line 97 "src/lexer-keywords.txt"
+ {"f32x4.min", TokenType::Binary, Opcode::F32X4Min},
+ {""}, {""}, {""},
+#line 205 "src/lexer-keywords.txt"
+ {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 572 "src/lexer-keywords.txt"
- {"v128.store", TokenType::Store, Opcode::V128Store},
-#line 370 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU},
-#line 239 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU},
+#line 371 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU},
+#line 240 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU},
+ {""}, {""}, {""},
+#line 321 "src/lexer-keywords.txt"
+ {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU},
{""},
-#line 533 "src/lexer-keywords.txt"
- {"param", TokenType::Param},
-#line 542 "src/lexer-keywords.txt"
- {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 600 "src/lexer-keywords.txt"
- {"f64.convert_s/i32", TokenType::Convert, Opcode::F64ConvertI32S},
-#line 594 "src/lexer-keywords.txt"
- {"f32.convert_s/i32", TokenType::Convert, Opcode::F32ConvertI32S},
-#line 602 "src/lexer-keywords.txt"
- {"f64.convert_u/i32", TokenType::Convert, Opcode::F64ConvertI32U},
-#line 596 "src/lexer-keywords.txt"
- {"f32.convert_u/i32", TokenType::Convert, Opcode::F32ConvertI32U},
- {""}, {""},
-#line 369 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU},
-#line 238 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU},
+#line 320 "src/lexer-keywords.txt"
+ {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS},
+ {""}, {""}, {""},
+#line 212 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU},
+#line 384 "src/lexer-keywords.txt"
+ {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8},
+#line 252 "src/lexer-keywords.txt"
+ {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8},
{""},
-#line 573 "src/lexer-keywords.txt"
- {"v128", Type::V128},
-#line 622 "src/lexer-keywords.txt"
- {"i64.trunc_s/f64", TokenType::Convert, Opcode::I64TruncF64S},
-#line 610 "src/lexer-keywords.txt"
- {"i32.trunc_s/f64", TokenType::Convert, Opcode::I32TruncF64S},
-#line 626 "src/lexer-keywords.txt"
- {"i64.trunc_u/f64", TokenType::Convert, Opcode::I64TruncF64U},
-#line 614 "src/lexer-keywords.txt"
- {"i32.trunc_u/f64", TokenType::Convert, Opcode::I32TruncF64U},
-#line 618 "src/lexer-keywords.txt"
- {"i64.extend_s/i32", TokenType::Convert, Opcode::I64ExtendI32S},
- {""},
-#line 619 "src/lexer-keywords.txt"
- {"i64.extend_u/i32", TokenType::Convert, Opcode::I64ExtendI32U},
- {""}, {""},
-#line 116 "src/lexer-keywords.txt"
- {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U},
-#line 58 "src/lexer-keywords.txt"
- {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 211 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS},
+#line 386 "src/lexer-keywords.txt"
+ {"i64.clz", TokenType::Unary, Opcode::I64Clz},
+#line 254 "src/lexer-keywords.txt"
+ {"i32.clz", TokenType::Unary, Opcode::I32Clz},
+#line 527 "src/lexer-keywords.txt"
+ {"nan:arithmetic", TokenType::NanArithmetic},
+#line 179 "src/lexer-keywords.txt"
+ {"i16x8.add", TokenType::Binary, Opcode::I16X8Add},
{""}, {""},
-#line 409 "src/lexer-keywords.txt"
- {"i64.load8_s", TokenType::Load, Opcode::I64Load8S},
-#line 272 "src/lexer-keywords.txt"
- {"i32.load8_s", TokenType::Load, Opcode::I32Load8S},
+#line 570 "src/lexer-keywords.txt"
+ {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero},
{""}, {""},
-#line 410 "src/lexer-keywords.txt"
- {"i64.load8_u", TokenType::Load, Opcode::I64Load8U},
-#line 273 "src/lexer-keywords.txt"
- {"i32.load8_u", TokenType::Load, Opcode::I32Load8U},
-#line 376 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd},
-#line 245 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
-#line 366 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU},
+#line 374 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU},
+#line 243 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU},
{""},
-#line 172 "src/lexer-keywords.txt"
- {"get", TokenType::Get},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 388 "src/lexer-keywords.txt"
+ {"i64.ctz", TokenType::Unary, Opcode::I64Ctz},
+#line 256 "src/lexer-keywords.txt"
+ {"i32.ctz", TokenType::Unary, Opcode::I32Ctz},
+#line 180 "src/lexer-keywords.txt"
+ {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue},
{""}, {""}, {""}, {""},
-#line 394 "src/lexer-keywords.txt"
- {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S},
-#line 262 "src/lexer-keywords.txt"
- {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 558 "src/lexer-keywords.txt"
- {"throw", TokenType::Throw, Opcode::Throw},
+#line 327 "src/lexer-keywords.txt"
+ {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl},
+ {""}, {""}, {""}, {""},
+#line 178 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU},
{""},
-#line 377 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd},
-#line 246 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd},
+#line 506 "src/lexer-keywords.txt"
+ {"if", TokenType::If, Opcode::If},
{""},
-#line 30 "src/lexer-keywords.txt"
- {"br_if", TokenType::BrIf, Opcode::BrIf},
-#line 428 "src/lexer-keywords.txt"
- {"i64.store8", TokenType::Store, Opcode::I64Store8},
-#line 290 "src/lexer-keywords.txt"
- {"i32.store8", TokenType::Store, Opcode::I32Store8},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 534 "src/lexer-keywords.txt"
- {"quote", TokenType::Quote},
- {""}, {""}, {""}, {""},
+#line 177 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS},
+#line 39 "src/lexer-keywords.txt"
+ {"data.drop", TokenType::DataDrop, Opcode::DataDrop},
+#line 533 "src/lexer-keywords.txt"
+ {"ref", TokenType::Ref},
+ {""}, {""}, {""}, {""}, {""}, {""},
#line 579 "src/lexer-keywords.txt"
{"v128.load8_lane", TokenType::SimdLoadLane, Opcode::V128Load8Lane},
-#line 568 "src/lexer-keywords.txt"
- {"v128.or", TokenType::Binary, Opcode::V128Or},
- {""}, {""}, {""},
-#line 118 "src/lexer-keywords.txt"
- {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U},
-#line 60 "src/lexer-keywords.txt"
- {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U},
-#line 379 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr},
-#line 248 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr},
- {""}, {""}, {""},
-#line 582 "src/lexer-keywords.txt"
- {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane},
-#line 581 "src/lexer-keywords.txt"
- {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane},
-#line 585 "src/lexer-keywords.txt"
- {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane},
- {""},
-#line 363 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU},
{""}, {""}, {""}, {""}, {""},
-#line 108 "src/lexer-keywords.txt"
- {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc},
- {""}, {""},
-#line 380 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub},
-#line 249 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub},
-#line 551 "src/lexer-keywords.txt"
- {"table.get", TokenType::TableGet, Opcode::TableGet},
-#line 21 "src/lexer-keywords.txt"
- {"assert_exhaustion", TokenType::AssertExhaustion},
+#line 578 "src/lexer-keywords.txt"
+ {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat},
+#line 106 "src/lexer-keywords.txt"
+ {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt},
+ {""}, {""}, {""}, {""},
+#line 47 "src/lexer-keywords.txt"
+ {"else", TokenType::Else, Opcode::Else},
{""},
-#line 535 "src/lexer-keywords.txt"
- {"ref.extern", TokenType::RefExtern},
- {""}, {""},
-#line 149 "src/lexer-keywords.txt"
- {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt},
-#line 362 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU},
+#line 557 "src/lexer-keywords.txt"
+ {"then", TokenType::Then},
{""},
-#line 416 "src/lexer-keywords.txt"
- {"i64.or", TokenType::Binary, Opcode::I64Or},
-#line 279 "src/lexer-keywords.txt"
- {"i32.or", TokenType::Binary, Opcode::I32Or},
-#line 124 "src/lexer-keywords.txt"
- {"f64.gt", TokenType::Compare, Opcode::F64Gt},
-#line 67 "src/lexer-keywords.txt"
- {"f32.gt", TokenType::Compare, Opcode::F32Gt},
- {""}, {""}, {""}, {""}, {""},
-#line 148 "src/lexer-keywords.txt"
- {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge},
-#line 448 "src/lexer-keywords.txt"
- {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS},
-#line 305 "src/lexer-keywords.txt"
- {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue},
- {""}, {""},
-#line 123 "src/lexer-keywords.txt"
- {"f64.ge", TokenType::Compare, Opcode::F64Ge},
-#line 66 "src/lexer-keywords.txt"
- {"f32.ge", TokenType::Compare, Opcode::F32Ge},
-#line 450 "src/lexer-keywords.txt"
- {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS},
- {""}, {""}, {""}, {""}, {""},
-#line 401 "src/lexer-keywords.txt"
- {"i64.gt_s", TokenType::Compare, Opcode::I64GtS},
-#line 266 "src/lexer-keywords.txt"
- {"i32.gt_s", TokenType::Compare, Opcode::I32GtS},
- {""}, {""},
-#line 402 "src/lexer-keywords.txt"
- {"i64.gt_u", TokenType::Compare, Opcode::I64GtU},
-#line 267 "src/lexer-keywords.txt"
- {"i32.gt_u", TokenType::Compare, Opcode::I32GtU},
-#line 399 "src/lexer-keywords.txt"
- {"i64.ge_s", TokenType::Compare, Opcode::I64GeS},
-#line 264 "src/lexer-keywords.txt"
- {"i32.ge_s", TokenType::Compare, Opcode::I32GeS},
- {""}, {""},
-#line 400 "src/lexer-keywords.txt"
- {"i64.ge_u", TokenType::Compare, Opcode::I64GeU},
-#line 265 "src/lexer-keywords.txt"
- {"i32.ge_u", TokenType::Compare, Opcode::I32GeU},
+#line 329 "src/lexer-keywords.txt"
+ {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU},
{""},
-#line 88 "src/lexer-keywords.txt"
- {"f32x4.div", TokenType::Binary, Opcode::F32X4Div},
+#line 328 "src/lexer-keywords.txt"
+ {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS},
+#line 532 "src/lexer-keywords.txt"
+ {"param", TokenType::Param},
{""}, {""},
-#line 359 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU},
-#line 235 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU},
+#line 168 "src/lexer-keywords.txt"
+ {"f64x2", TokenType::F64X2},
+#line 220 "src/lexer-keywords.txt"
+ {"i16x8", TokenType::I16X8},
+#line 468 "src/lexer-keywords.txt"
+ {"i64x2", TokenType::I64X2},
+#line 48 "src/lexer-keywords.txt"
+ {"end", TokenType::End, Opcode::End},
+ {""}, {""}, {""},
+#line 206 "src/lexer-keywords.txt"
+ {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane},
+ {""}, {""},
+#line 200 "src/lexer-keywords.txt"
+ {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 169 "src/lexer-keywords.txt"
+ {"field", TokenType::Field},
+#line 351 "src/lexer-keywords.txt"
+ {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U},
+#line 584 "src/lexer-keywords.txt"
+ {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 103 "src/lexer-keywords.txt"
- {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin},
-#line 91 "src/lexer-keywords.txt"
- {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor},
-#line 571 "src/lexer-keywords.txt"
- {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero},
-#line 570 "src/lexer-keywords.txt"
- {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero},
+#line 50 "src/lexer-keywords.txt"
+ {"extern", Type::ExternRef, TokenType::Extern},
#line 586 "src/lexer-keywords.txt"
{"v128.store64_lane", TokenType::SimdStoreLane, Opcode::V128Store64Lane},
-#line 353 "src/lexer-keywords.txt"
- {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U},
-#line 229 "src/lexer-keywords.txt"
- {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U},
-#line 530 "src/lexer-keywords.txt"
- {"nop", TokenType::Nop, Opcode::Nop},
- {""},
-#line 136 "src/lexer-keywords.txt"
- {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt},
-#line 78 "src/lexer-keywords.txt"
- {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 197 "src/lexer-keywords.txt"
+ {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU},
{""},
-#line 36 "src/lexer-keywords.txt"
- {"catch", TokenType::Catch, Opcode::Catch},
-#line 175 "src/lexer-keywords.txt"
- {"global", TokenType::Global},
- {""}, {""},
-#line 174 "src/lexer-keywords.txt"
- {"global.set", TokenType::GlobalSet, Opcode::GlobalSet},
- {""}, {""},
-#line 146 "src/lexer-keywords.txt"
- {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane},
-#line 128 "src/lexer-keywords.txt"
- {"f64.max", TokenType::Binary, Opcode::F64Max},
-#line 71 "src/lexer-keywords.txt"
- {"f32.max", TokenType::Binary, Opcode::F32Max},
-#line 441 "src/lexer-keywords.txt"
- {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane},
-#line 512 "src/lexer-keywords.txt"
- {"local.get", TokenType::LocalGet, Opcode::LocalGet},
-#line 423 "src/lexer-keywords.txt"
- {"i64.shl", TokenType::Binary, Opcode::I64Shl},
-#line 286 "src/lexer-keywords.txt"
- {"i32.shl", TokenType::Binary, Opcode::I32Shl},
- {""}, {""}, {""}, {""},
-#line 319 "src/lexer-keywords.txt"
- {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS},
- {""}, {""}, {""},
-#line 320 "src/lexer-keywords.txt"
- {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU},
+#line 196 "src/lexer-keywords.txt"
+ {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS},
#line 105 "src/lexer-keywords.txt"
{"f32x4.splat", TokenType::Unary, Opcode::F32X4Splat},
{""},
-#line 455 "src/lexer-keywords.txt"
- {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S},
-#line 331 "src/lexer-keywords.txt"
+#line 330 "src/lexer-keywords.txt"
{"i32x4.splat", TokenType::Unary, Opcode::I32X4Splat},
-#line 457 "src/lexer-keywords.txt"
- {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U},
-#line 580 "src/lexer-keywords.txt"
- {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane},
-#line 365 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU},
+ {""}, {""},
+#line 582 "src/lexer-keywords.txt"
+ {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane},
{""},
-#line 356 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU},
-#line 232 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU},
+#line 153 "src/lexer-keywords.txt"
+ {"f64x2.min", TokenType::Binary, Opcode::F64X2Min},
+ {""}, {""}, {""},
+#line 577 "src/lexer-keywords.txt"
+ {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat},
{""}, {""},
-#line 393 "src/lexer-keywords.txt"
- {"i64.eqz", TokenType::Convert, Opcode::I64Eqz},
-#line 261 "src/lexer-keywords.txt"
- {"i32.eqz", TokenType::Convert, Opcode::I32Eqz},
+#line 558 "src/lexer-keywords.txt"
+ {"throw", TokenType::Throw, Opcode::Throw},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 481 "src/lexer-keywords.txt"
+ {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU},
{""},
-#line 465 "src/lexer-keywords.txt"
- {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S},
+#line 485 "src/lexer-keywords.txt"
+ {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU},
{""},
-#line 467 "src/lexer-keywords.txt"
- {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U},
-#line 396 "src/lexer-keywords.txt"
- {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S},
-#line 263 "src/lexer-keywords.txt"
- {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S},
-#line 206 "src/lexer-keywords.txt"
- {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne},
+#line 480 "src/lexer-keywords.txt"
+ {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS},
{""},
-#line 584 "src/lexer-keywords.txt"
- {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane},
+#line 484 "src/lexer-keywords.txt"
+ {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS},
+ {""},
+#line 483 "src/lexer-keywords.txt"
+ {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU},
+ {""},
+#line 487 "src/lexer-keywords.txt"
+ {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU},
+#line 536 "src/lexer-keywords.txt"
+ {"ref.func", TokenType::RefFunc, Opcode::RefFunc},
+#line 482 "src/lexer-keywords.txt"
+ {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS},
+ {""},
+#line 486 "src/lexer-keywords.txt"
+ {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS},
{""}, {""},
-#line 162 "src/lexer-keywords.txt"
- {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt},
-#line 355 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU},
-#line 231 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU},
+#line 185 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU},
+#line 494 "src/lexer-keywords.txt"
+ {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg},
+#line 184 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS},
+ {""}, {""},
+#line 510 "src/lexer-keywords.txt"
+ {"item", TokenType::Item},
+#line 496 "src/lexer-keywords.txt"
+ {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne},
+ {""}, {""},
+#line 193 "src/lexer-keywords.txt"
+ {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U},
{""},
-#line 199 "src/lexer-keywords.txt"
- {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS},
- {""}, {""}, {""},
-#line 200 "src/lexer-keywords.txt"
- {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 152 "src/lexer-keywords.txt"
- {"f64x2.max", TokenType::Binary, Opcode::F64X2Max},
- {""}, {""}, {""}, {""},
-#line 460 "src/lexer-keywords.txt"
+#line 192 "src/lexer-keywords.txt"
+ {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S},
+ {""},
+#line 459 "src/lexer-keywords.txt"
{"i64x2.shl", TokenType::Binary, Opcode::I64X2Shl},
+ {""}, {""}, {""}, {""},
+#line 380 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg},
+#line 249 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg},
+ {""}, {""}, {""}, {""},
+#line 503 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU},
{""},
-#line 195 "src/lexer-keywords.txt"
- {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS},
-#line 214 "src/lexer-keywords.txt"
- {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub},
-#line 196 "src/lexer-keywords.txt"
- {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU},
+#line 571 "src/lexer-keywords.txt"
+ {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero},
{""},
-#line 42 "src/lexer-keywords.txt"
- {"delegate", TokenType::Delegate},
+#line 502 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS},
+ {""}, {""}, {""},
+#line 473 "src/lexer-keywords.txt"
+ {"i8x16.add", TokenType::Binary, Opcode::I8X16Add},
+ {""}, {""}, {""}, {""}, {""},
+#line 162 "src/lexer-keywords.txt"
+ {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt},
+ {""}, {""},
+#line 559 "src/lexer-keywords.txt"
+ {"try", TokenType::Try, Opcode::Try},
{""},
-#line 191 "src/lexer-keywords.txt"
- {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS},
+#line 474 "src/lexer-keywords.txt"
+ {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue},
+ {""}, {""}, {""},
+#line 461 "src/lexer-keywords.txt"
+ {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU},
{""},
-#line 192 "src/lexer-keywords.txt"
- {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU},
-#line 165 "src/lexer-keywords.txt"
- {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S},
+#line 460 "src/lexer-keywords.txt"
+ {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS},
+ {""}, {""}, {""},
+#line 472 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU},
+#line 111 "src/lexer-keywords.txt"
+ {"f64.abs", TokenType::Unary, Opcode::F64Abs},
+#line 53 "src/lexer-keywords.txt"
+ {"f32.abs", TokenType::Unary, Opcode::F32Abs},
{""},
-#line 166 "src/lexer-keywords.txt"
- {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U},
-#line 77 "src/lexer-keywords.txt"
- {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32},
+#line 471 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS},
+ {""},
+#line 552 "src/lexer-keywords.txt"
+ {"table.grow", TokenType::TableGrow, Opcode::TableGrow},
+#line 122 "src/lexer-keywords.txt"
+ {"f64.floor", TokenType::Unary, Opcode::F64Floor},
+#line 65 "src/lexer-keywords.txt"
+ {"f32.floor", TokenType::Unary, Opcode::F32Floor},
+#line 121 "src/lexer-keywords.txt"
+ {"f64.eq", TokenType::Compare, Opcode::F64Eq},
+#line 64 "src/lexer-keywords.txt"
+ {"f32.eq", TokenType::Compare, Opcode::F32Eq},
+#line 391 "src/lexer-keywords.txt"
+ {"i64.eq", TokenType::Compare, Opcode::I64Eq},
+#line 259 "src/lexer-keywords.txt"
+ {"i32.eq", TokenType::Compare, Opcode::I32Eq},
+ {""}, {""}, {""}, {""}, {""},
+#line 541 "src/lexer-keywords.txt"
+ {"rethrow", TokenType::Rethrow, Opcode::Rethrow},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 547 "src/lexer-keywords.txt"
+ {"start", TokenType::Start},
+ {""}, {""}, {""}, {""},
+#line 37 "src/lexer-keywords.txt"
+ {"catch", TokenType::Catch, Opcode::Catch},
{""}, {""}, {""},
-#line 437 "src/lexer-keywords.txt"
- {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S},
-#line 299 "src/lexer-keywords.txt"
- {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S},
- {""}, {""},
-#line 438 "src/lexer-keywords.txt"
- {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U},
-#line 300 "src/lexer-keywords.txt"
- {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U},
- {""}, {""},
-#line 424 "src/lexer-keywords.txt"
- {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS},
-#line 287 "src/lexer-keywords.txt"
- {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS},
+#line 456 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U},
+ {""},
+#line 454 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S},
+ {""},
+#line 585 "src/lexer-keywords.txt"
+ {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane},
+#line 497 "src/lexer-keywords.txt"
+ {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane},
{""}, {""},
#line 425 "src/lexer-keywords.txt"
- {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU},
+ {"i64.store16", TokenType::Store, Opcode::I64Store16},
#line 288 "src/lexer-keywords.txt"
- {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU},
-#line 158 "src/lexer-keywords.txt"
- {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax},
+ {"i32.store16", TokenType::Store, Opcode::I32Store16},
{""}, {""}, {""},
-#line 201 "src/lexer-keywords.txt"
- {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul},
-#line 523 "src/lexer-keywords.txt"
- {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit},
-#line 442 "src/lexer-keywords.txt"
- {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S},
+#line 161 "src/lexer-keywords.txt"
+ {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat},
{""},
-#line 443 "src/lexer-keywords.txt"
- {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U},
- {""}, {""}, {""},
-#line 177 "src/lexer-keywords.txt"
- {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs},
- {""}, {""},
-#line 524 "src/lexer-keywords.txt"
- {"memory.size", TokenType::MemorySize, Opcode::MemorySize},
+#line 462 "src/lexer-keywords.txt"
+ {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat},
+#line 32 "src/lexer-keywords.txt"
+ {"br_table", TokenType::BrTable, Opcode::BrTable},
+#line 103 "src/lexer-keywords.txt"
+ {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""},
-#line 601 "src/lexer-keywords.txt"
- {"f64.convert_s/i64", TokenType::Convert, Opcode::F64ConvertI64S},
-#line 595 "src/lexer-keywords.txt"
- {"f32.convert_s/i64", TokenType::Convert, Opcode::F32ConvertI64S},
-#line 603 "src/lexer-keywords.txt"
- {"f64.convert_u/i64", TokenType::Convert, Opcode::F64ConvertI64U},
-#line 597 "src/lexer-keywords.txt"
- {"f32.convert_u/i64", TokenType::Convert, Opcode::F32ConvertI64U},
- {""}, {""}, {""},
-#line 607 "src/lexer-keywords.txt"
- {"get_local", TokenType::LocalGet, Opcode::LocalGet},
+ {""}, {""},
+#line 546 "src/lexer-keywords.txt"
+ {"shared", TokenType::Shared},
+#line 505 "src/lexer-keywords.txt"
+ {"i8x16", TokenType::I8X16},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 461 "src/lexer-keywords.txt"
- {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS},
#line 358 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU},
+ {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU},
#line 234 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU},
-#line 629 "src/lexer-keywords.txt"
- {"set_global", TokenType::GlobalSet, Opcode::GlobalSet},
-#line 462 "src/lexer-keywords.txt"
- {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU},
-#line 212 "src/lexer-keywords.txt"
- {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS},
+ {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 489 "src/lexer-keywords.txt"
+ {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU},
{""},
-#line 213 "src/lexer-keywords.txt"
- {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU},
+#line 488 "src/lexer-keywords.txt"
+ {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS},
{""}, {""}, {""},
-#line 180 "src/lexer-keywords.txt"
- {"i16x8.add", TokenType::Binary, Opcode::I16X8Add},
- {""}, {""}, {""}, {""}, {""},
-#line 135 "src/lexer-keywords.txt"
- {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64},
- {""},
-#line 536 "src/lexer-keywords.txt"
- {"ref.func", TokenType::RefFunc, Opcode::RefFunc},
- {""}, {""}, {""}, {""}, {""},
-#line 521 "src/lexer-keywords.txt"
- {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill},
- {""}, {""}, {""}, {""}, {""},
-#line 134 "src/lexer-keywords.txt"
- {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32},
-#line 183 "src/lexer-keywords.txt"
- {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask},
- {""}, {""},
-#line 93 "src/lexer-keywords.txt"
- {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt},
-#line 160 "src/lexer-keywords.txt"
- {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane},
-#line 578 "src/lexer-keywords.txt"
- {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat},
- {""},
-#line 459 "src/lexer-keywords.txt"
- {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane},
- {""},
-#line 599 "src/lexer-keywords.txt"
- {"f32.reinterpret/i32", TokenType::Convert, Opcode::F32ReinterpretI32},
- {""}, {""}, {""}, {""}, {""},
-#line 92 "src/lexer-keywords.txt"
- {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge},
-#line 311 "src/lexer-keywords.txt"
- {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS},
+#line 175 "src/lexer-keywords.txt"
+ {"global", TokenType::Global},
{""},
-#line 312 "src/lexer-keywords.txt"
- {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU},
+#line 523 "src/lexer-keywords.txt"
+ {"memory.size", TokenType::MemorySize, Opcode::MemorySize},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 355 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU},
+#line 231 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU},
+#line 363 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU},
{""}, {""}, {""},
-#line 309 "src/lexer-keywords.txt"
- {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS},
+#line 354 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU},
+#line 230 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 199 "src/lexer-keywords.txt"
+ {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU},
{""},
-#line 310 "src/lexer-keywords.txt"
- {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU},
- {""}, {""}, {""}, {""},
-#line 368 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 574 "src/lexer-keywords.txt"
- {"v128.xor", TokenType::Binary, Opcode::V128Xor},
+#line 198 "src/lexer-keywords.txt"
+ {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS},
+ {""},
+#line 550 "src/lexer-keywords.txt"
+ {"table.fill", TokenType::TableFill, Opcode::TableFill},
{""}, {""}, {""},
-#line 577 "src/lexer-keywords.txt"
- {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat},
-#line 576 "src/lexer-keywords.txt"
- {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat},
-#line 552 "src/lexer-keywords.txt"
- {"table.grow", TokenType::TableGrow, Opcode::TableGrow},
- {""}, {""},
-#line 315 "src/lexer-keywords.txt"
- {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S},
+#line 102 "src/lexer-keywords.txt"
+ {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax},
+#line 390 "src/lexer-keywords.txt"
+ {"i64.div_u", TokenType::Binary, Opcode::I64DivU},
+#line 258 "src/lexer-keywords.txt"
+ {"i32.div_u", TokenType::Binary, Opcode::I32DivU},
+#line 389 "src/lexer-keywords.txt"
+ {"i64.div_s", TokenType::Binary, Opcode::I64DivS},
+#line 257 "src/lexer-keywords.txt"
+ {"i32.div_s", TokenType::Binary, Opcode::I32DivS},
+#line 479 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU},
{""},
-#line 316 "src/lexer-keywords.txt"
- {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U},
+#line 478 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS},
+#line 466 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U},
+#line 91 "src/lexer-keywords.txt"
+ {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor},
+#line 464 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 173 "src/lexer-keywords.txt"
+ {"global.get", TokenType::GlobalGet, Opcode::GlobalGet},
{""},
-#line 372 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU},
-#line 241 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU},
+#line 174 "src/lexer-keywords.txt"
+ {"global.set", TokenType::GlobalSet, Opcode::GlobalSet},
{""},
-#line 178 "src/lexer-keywords.txt"
- {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS},
+#line 529 "src/lexer-keywords.txt"
+ {"nop", TokenType::Nop, Opcode::Nop},
+#line 207 "src/lexer-keywords.txt"
+ {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 44 "src/lexer-keywords.txt"
+ {"drop", TokenType::Drop, Opcode::Drop},
+#line 373 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU},
+#line 242 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU},
{""},
-#line 179 "src/lexer-keywords.txt"
- {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU},
- {""}, {""},
#line 25 "src/lexer-keywords.txt"
- {"assert_trap", TokenType::AssertTrap},
- {""}, {""},
-#line 323 "src/lexer-keywords.txt"
- {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S},
-#line 367 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 90 "src/lexer-keywords.txt"
- {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane},
- {""},
-#line 497 "src/lexer-keywords.txt"
- {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne},
-#line 308 "src/lexer-keywords.txt"
- {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane},
- {""}, {""}, {""}, {""},
-#line 605 "src/lexer-keywords.txt"
- {"f64.reinterpret/i64", TokenType::Convert, Opcode::F64ReinterpretI64},
- {""}, {""},
-#line 491 "src/lexer-keywords.txt"
- {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS},
- {""}, {""}, {""},
-#line 492 "src/lexer-keywords.txt"
- {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU},
-#line 351 "src/lexer-keywords.txt"
- {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U},
-#line 228 "src/lexer-keywords.txt"
- {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U},
- {""}, {""}, {""},
-#line 561 "src/lexer-keywords.txt"
- {"unreachable", TokenType::Unreachable, Opcode::Unreachable},
-#line 604 "src/lexer-keywords.txt"
- {"f64.promote/f32", TokenType::Convert, Opcode::F64PromoteF32},
-#line 583 "src/lexer-keywords.txt"
- {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane},
- {""}, {""}, {""}, {""}, {""},
-#line 122 "src/lexer-keywords.txt"
- {"f64.floor", TokenType::Unary, Opcode::F64Floor},
-#line 65 "src/lexer-keywords.txt"
- {"f32.floor", TokenType::Unary, Opcode::F32Floor},
-#line 487 "src/lexer-keywords.txt"
- {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS},
-#line 505 "src/lexer-keywords.txt"
- {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub},
-#line 488 "src/lexer-keywords.txt"
- {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU},
-#line 344 "src/lexer-keywords.txt"
- {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S},
-#line 539 "src/lexer-keywords.txt"
- {"register", TokenType::Register},
-#line 345 "src/lexer-keywords.txt"
- {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U},
-#line 485 "src/lexer-keywords.txt"
- {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS},
- {""},
-#line 486 "src/lexer-keywords.txt"
- {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU},
-#line 528 "src/lexer-keywords.txt"
- {"nan:arithmetic", TokenType::NanArithmetic},
-#line 106 "src/lexer-keywords.txt"
- {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt},
- {""}, {""}, {""}, {""}, {""},
-#line 335 "src/lexer-keywords.txt"
- {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S},
+ {"assert_return", TokenType::AssertReturn},
{""},
-#line 337 "src/lexer-keywords.txt"
- {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 96 "src/lexer-keywords.txt"
- {"f32x4.max", TokenType::Binary, Opcode::F32X4Max},
- {""}, {""}, {""}, {""},
-#line 328 "src/lexer-keywords.txt"
- {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl},
+#line 569 "src/lexer-keywords.txt"
+ {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue},
+#line 392 "src/lexer-keywords.txt"
+ {"i64.eqz", TokenType::Convert, Opcode::I64Eqz},
+#line 260 "src/lexer-keywords.txt"
+ {"i32.eqz", TokenType::Convert, Opcode::I32Eqz},
+#line 357 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU},
+#line 233 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU},
{""}, {""}, {""},
-#line 361 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU},
-#line 237 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU},
- {""}, {""}, {""}, {""},
-#line 471 "src/lexer-keywords.txt"
- {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs},
-#line 302 "src/lexer-keywords.txt"
- {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64},
+#line 383 "src/lexer-keywords.txt"
+ {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32},
{""}, {""},
-#line 20 "src/lexer-keywords.txt"
- {"array", Type::Array, TokenType::Array},
-#line 506 "src/lexer-keywords.txt"
- {"i8x16", TokenType::I8X16},
+#line 359 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU},
+#line 235 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU},
+#line 437 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U},
+#line 299 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U},
{""}, {""},
-#line 575 "src/lexer-keywords.txt"
- {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat},
-#line 417 "src/lexer-keywords.txt"
- {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt},
-#line 280 "src/lexer-keywords.txt"
- {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt},
+#line 436 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S},
+#line 298 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S},
+#line 209 "src/lexer-keywords.txt"
+ {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU},
{""},
-#line 181 "src/lexer-keywords.txt"
- {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue},
+#line 208 "src/lexer-keywords.txt"
+ {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS},
+ {""}, {""},
+#line 377 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg},
+#line 246 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 107 "src/lexer-keywords.txt"
+ {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub},
{""},
-#line 86 "src/lexer-keywords.txt"
- {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S},
+#line 331 "src/lexer-keywords.txt"
+ {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub},
+#line 83 "src/lexer-keywords.txt"
+ {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs},
{""},
-#line 87 "src/lexer-keywords.txt"
- {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U},
+#line 302 "src/lexer-keywords.txt"
+ {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs},
{""},
-#line 45 "src/lexer-keywords.txt"
- {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop},
- {""}, {""},
-#line 221 "src/lexer-keywords.txt"
- {"i16x8", TokenType::I16X8},
-#line 102 "src/lexer-keywords.txt"
- {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax},
- {""}, {""}, {""},
-#line 39 "src/lexer-keywords.txt"
- {"data.drop", TokenType::DataDrop, Opcode::DataDrop},
+#line 159 "src/lexer-keywords.txt"
+ {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin},
{""},
#line 360 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU},
+ {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU},
#line 236 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 503 "src/lexer-keywords.txt"
- {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS},
- {""},
-#line 504 "src/lexer-keywords.txt"
- {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU},
- {""}, {""}, {""},
-#line 474 "src/lexer-keywords.txt"
- {"i8x16.add", TokenType::Binary, Opcode::I8X16Add},
- {""}, {""}, {""},
-#line 121 "src/lexer-keywords.txt"
- {"f64.eq", TokenType::Compare, Opcode::F64Eq},
-#line 64 "src/lexer-keywords.txt"
- {"f32.eq", TokenType::Compare, Opcode::F32Eq},
+ {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU},
+ {""}, {""}, {""}, {""}, {""},
+#line 89 "src/lexer-keywords.txt"
+ {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq},
{""},
-#line 392 "src/lexer-keywords.txt"
- {"i64.eq", TokenType::Compare, Opcode::I64Eq},
-#line 260 "src/lexer-keywords.txt"
- {"i32.eq", TokenType::Compare, Opcode::I32Eq},
+#line 306 "src/lexer-keywords.txt"
+ {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 118 "src/lexer-keywords.txt"
+ {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U},
+#line 60 "src/lexer-keywords.txt"
+ {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U},
{""}, {""},
-#line 470 "src/lexer-keywords.txt"
- {"i64.xor", TokenType::Binary, Opcode::I64Xor},
-#line 348 "src/lexer-keywords.txt"
- {"i32.xor", TokenType::Binary, Opcode::I32Xor},
-#line 623 "src/lexer-keywords.txt"
- {"i64.trunc_s:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32S},
-#line 611 "src/lexer-keywords.txt"
- {"i32.trunc_s:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32S},
-#line 627 "src/lexer-keywords.txt"
- {"i64.trunc_u:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32U},
-#line 615 "src/lexer-keywords.txt"
- {"i32.trunc_u:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32U},
+#line 117 "src/lexer-keywords.txt"
+ {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S},
+#line 59 "src/lexer-keywords.txt"
+ {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S},
+ {""}, {""},
+#line 210 "src/lexer-keywords.txt"
+ {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat},
{""}, {""}, {""}, {""},
-#line 477 "src/lexer-keywords.txt"
- {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask},
-#line 167 "src/lexer-keywords.txt"
- {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4},
-#line 329 "src/lexer-keywords.txt"
- {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS},
- {""}, {""}, {""},
-#line 330 "src/lexer-keywords.txt"
- {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU},
+#line 521 "src/lexer-keywords.txt"
+ {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 322 "src/lexer-keywords.txt"
+ {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S},
{""},
-#line 518 "src/lexer-keywords.txt"
- {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
- {""}, {""}, {""}, {""}, {""},
-#line 49 "src/lexer-keywords.txt"
- {"tag", TokenType::Tag},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 197 "src/lexer-keywords.txt"
- {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS},
- {""}, {""}, {""},
-#line 198 "src/lexer-keywords.txt"
- {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU},
+#line 522 "src/lexer-keywords.txt"
+ {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""},
-#line 211 "src/lexer-keywords.txt"
- {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat},
+#line 158 "src/lexer-keywords.txt"
+ {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax},
+ {""}, {""}, {""}, {""},
+#line 515 "src/lexer-keywords.txt"
+ {"loop", TokenType::Loop, Opcode::Loop},
+#line 62 "src/lexer-keywords.txt"
+ {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64},
+ {""}, {""},
+#line 147 "src/lexer-keywords.txt"
+ {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor},
+#line 52 "src/lexer-keywords.txt"
+ {"export", TokenType::Export},
+#line 315 "src/lexer-keywords.txt"
+ {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U},
+ {""}, {""},
+#line 88 "src/lexer-keywords.txt"
+ {"f32x4.div", TokenType::Binary, Opcode::F32X4Div},
+#line 314 "src/lexer-keywords.txt"
+ {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S},
+#line 580 "src/lexer-keywords.txt"
+ {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane},
+ {""}, {""}, {""}, {""},
+#line 109 "src/lexer-keywords.txt"
+ {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero},
+#line 575 "src/lexer-keywords.txt"
+ {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat},
+ {""}, {""}, {""}, {""},
+#line 491 "src/lexer-keywords.txt"
+ {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU},
{""},
-#line 593 "src/lexer-keywords.txt"
- {"anyfunc", Type::FuncRef},
+#line 490 "src/lexer-keywords.txt"
+ {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS},
{""}, {""}, {""}, {""}, {""},
-#line 104 "src/lexer-keywords.txt"
- {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane},
- {""},
-#line 617 "src/lexer-keywords.txt"
- {"i32.wrap/i64", TokenType::Convert, Opcode::I32WrapI64},
-#line 327 "src/lexer-keywords.txt"
- {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane},
+#line 370 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU},
+#line 239 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU},
{""}, {""},
-#line 385 "src/lexer-keywords.txt"
- {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8},
-#line 253 "src/lexer-keywords.txt"
- {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8},
+#line 524 "src/lexer-keywords.txt"
+ {"memory", TokenType::Memory},
+#line 561 "src/lexer-keywords.txt"
+ {"unreachable", TokenType::Unreachable, Opcode::Unreachable},
{""},
-#line 145 "src/lexer-keywords.txt"
- {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq},
+#line 416 "src/lexer-keywords.txt"
+ {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt},
+#line 279 "src/lexer-keywords.txt"
+ {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 435 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U},
+#line 297 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U},
{""}, {""},
-#line 445 "src/lexer-keywords.txt"
- {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq},
- {""}, {""}, {""},
-#line 472 "src/lexer-keywords.txt"
- {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS},
+#line 434 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S},
+#line 296 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S},
+#line 498 "src/lexer-keywords.txt"
+ {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl},
{""},
-#line 473 "src/lexer-keywords.txt"
- {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU},
- {""}, {""}, {""}, {""}, {""},
-#line 193 "src/lexer-keywords.txt"
- {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S},
- {""}, {""}, {""},
-#line 194 "src/lexer-keywords.txt"
- {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U},
- {""}, {""}, {""}, {""}, {""},
-#line 624 "src/lexer-keywords.txt"
- {"i64.trunc_s:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64S},
-#line 612 "src/lexer-keywords.txt"
- {"i32.trunc_s:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64S},
-#line 628 "src/lexer-keywords.txt"
- {"i64.trunc_u:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64U},
-#line 616 "src/lexer-keywords.txt"
- {"i32.trunc_u:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64U},
+#line 24 "src/lexer-keywords.txt"
+ {"assert_malformed", TokenType::AssertMalformed},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""},
-#line 519 "src/lexer-keywords.txt"
- {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
{""},
-#line 132 "src/lexer-keywords.txt"
- {"f64.neg", TokenType::Unary, Opcode::F64Neg},
-#line 75 "src/lexer-keywords.txt"
- {"f32.neg", TokenType::Unary, Opcode::F32Neg},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
-#line 156 "src/lexer-keywords.txt"
- {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg},
- {""}, {""},
-#line 452 "src/lexer-keywords.txt"
- {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 588 "src/lexer-keywords.txt"
- {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 163 "src/lexer-keywords.txt"
+ {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub},
+ {""},
+#line 463 "src/lexer-keywords.txt"
+ {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub},
+#line 141 "src/lexer-keywords.txt"
+ {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs},
{""},
-#line 364 "src/lexer-keywords.txt"
- {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU},
+#line 450 "src/lexer-keywords.txt"
+ {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs},
{""}, {""}, {""}, {""}, {""}, {""},
#line 333 "src/lexer-keywords.txt"
- {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S},
-#line 173 "src/lexer-keywords.txt"
- {"global.get", TokenType::GlobalGet, Opcode::GlobalGet},
-#line 334 "src/lexer-keywords.txt"
{"i32x4.extadd_pairwise_i16x8_u", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U},
-#line 475 "src/lexer-keywords.txt"
- {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""},
-#line 549 "src/lexer-keywords.txt"
- {"table.copy", TokenType::TableCopy, Opcode::TableCopy},
- {""}, {""},
-#line 340 "src/lexer-keywords.txt"
- {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S},
- {""}, {""}, {""},
-#line 341 "src/lexer-keywords.txt"
- {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U},
- {""}, {""},
-#line 569 "src/lexer-keywords.txt"
- {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue},
+#line 332 "src/lexer-keywords.txt"
+ {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S},
+ {""},
+#line 145 "src/lexer-keywords.txt"
+ {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq},
+ {""},
+#line 444 "src/lexer-keywords.txt"
+ {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq},
{""}, {""},
-#line 109 "src/lexer-keywords.txt"
- {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 189 "src/lexer-keywords.txt"
- {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS},
+#line 500 "src/lexer-keywords.txt"
+ {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU},
{""},
-#line 190 "src/lexer-keywords.txt"
- {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU},
- {""}, {""}, {""},
-#line 187 "src/lexer-keywords.txt"
- {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS},
+#line 499 "src/lexer-keywords.txt"
+ {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS},
+#line 224 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U},
{""},
-#line 188 "src/lexer-keywords.txt"
- {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 223 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 29 "src/lexer-keywords.txt"
+ {"binary", TokenType::Bin},
+ {""}, {""},
+#line 116 "src/lexer-keywords.txt"
+ {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U},
+#line 58 "src/lexer-keywords.txt"
+ {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U},
+ {""}, {""},
+#line 115 "src/lexer-keywords.txt"
+ {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S},
+#line 57 "src/lexer-keywords.txt"
+ {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""},
-#line 489 "src/lexer-keywords.txt"
- {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS},
-#line 281 "src/lexer-keywords.txt"
- {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32},
+#line 181 "src/lexer-keywords.txt"
+ {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU},
{""}, {""},
-#line 490 "src/lexer-keywords.txt"
- {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU},
- {""}, {""}, {""},
-#line 502 "src/lexer-keywords.txt"
- {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
#line 382 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor},
+ {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16},
#line 251 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 185 "src/lexer-keywords.txt"
- {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS},
- {""},
-#line 186 "src/lexer-keywords.txt"
- {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU},
- {""},
-#line 357 "src/lexer-keywords.txt"
- {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU},
-#line 233 "src/lexer-keywords.txt"
- {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU},
+ {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 501 "src/lexer-keywords.txt"
+ {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat},
{""}, {""}, {""},
-#line 375 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU},
-#line 244 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 371 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU},
-#line 240 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU},
+#line 549 "src/lexer-keywords.txt"
+ {"table.copy", TokenType::TableCopy, Opcode::TableCopy},
+ {""}, {""}, {""},
+#line 144 "src/lexer-keywords.txt"
+ {"f64x2.div", TokenType::Binary, Opcode::F64X2Div},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 89 "src/lexer-keywords.txt"
- {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq},
- {""}, {""},
-#line 307 "src/lexer-keywords.txt"
- {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 606 "src/lexer-keywords.txt"
- {"get_global", TokenType::GlobalGet, Opcode::GlobalGet},
+#line 344 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U},
+#line 202 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U},
+#line 343 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S},
+#line 201 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S},
{""}, {""},
-#line 346 "src/lexer-keywords.txt"
- {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero},
+#line 520 "src/lexer-keywords.txt"
+ {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill},
{""},
-#line 347 "src/lexer-keywords.txt"
- {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero},
- {""}, {""}, {""}, {""}, {""},
-#line 418 "src/lexer-keywords.txt"
- {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64},
- {""}, {""}, {""}, {""}, {""},
-#line 208 "src/lexer-keywords.txt"
- {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 372 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU},
+#line 241 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 608 "src/lexer-keywords.txt"
- {"i32.reinterpret/f32", TokenType::Convert, Opcode::I32ReinterpretF32},
+#line 218 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U},
+ {""},
+#line 216 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
-#line 100 "src/lexer-keywords.txt"
- {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg},
- {""}, {""},
-#line 325 "src/lexer-keywords.txt"
- {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 559 "src/lexer-keywords.txt"
- {"try", TokenType::Try, Opcode::Try},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 209 "src/lexer-keywords.txt"
- {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS},
+#line 31 "src/lexer-keywords.txt"
+ {"br_if", TokenType::BrIf, Opcode::BrIf},
{""}, {""}, {""},
-#line 210 "src/lexer-keywords.txt"
- {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 525 "src/lexer-keywords.txt"
- {"memory", TokenType::Memory},
+#line 340 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U},
+ {""}, {""}, {""},
+#line 339 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 213 "src/lexer-keywords.txt"
+ {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub},
+ {""}, {""},
+#line 176 "src/lexer-keywords.txt"
+ {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs},
+#line 336 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U},
{""},
-#line 483 "src/lexer-keywords.txt"
- {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS},
+#line 334 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S},
+#line 51 "src/lexer-keywords.txt"
+ {"externref", Type::ExternRef},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 183 "src/lexer-keywords.txt"
+ {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq},
+#line 495 "src/lexer-keywords.txt"
+ {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt},
+ {""}, {""},
+#line 342 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U},
{""},
-#line 484 "src/lexer-keywords.txt"
- {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU},
+#line 341 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S},
+ {""},
+#line 305 "src/lexer-keywords.txt"
+ {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 170 "src/lexer-keywords.txt"
+ {"funcref", Type::FuncRef},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 588 "src/lexer-keywords.txt"
+ {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle},
{""}, {""}, {""},
-#line 481 "src/lexer-keywords.txt"
- {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS},
-#line 620 "src/lexer-keywords.txt"
- {"i64.reinterpret/f64", TokenType::Convert, Opcode::I64ReinterpretF64},
-#line 482 "src/lexer-keywords.txt"
- {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 28 "src/lexer-keywords.txt"
- {"binary", TokenType::Bin},
+#line 26 "src/lexer-keywords.txt"
+ {"assert_trap", TokenType::AssertTrap},
{""}, {""},
-#line 182 "src/lexer-keywords.txt"
- {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU},
+#line 46 "src/lexer-keywords.txt"
+ {"elem", TokenType::Elem},
+ {""}, {""}, {""},
+#line 475 "src/lexer-keywords.txt"
+ {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU},
{""}, {""}, {""}, {""}, {""}, {""},
-#line 207 "src/lexer-keywords.txt"
- {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane},
+#line 119 "src/lexer-keywords.txt"
+ {"f64.copysign", TokenType::Binary, Opcode::F64Copysign},
+#line 61 "src/lexer-keywords.txt"
+ {"f32.copysign", TokenType::Binary, Opcode::F32Copysign},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 346 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero},
+ {""},
+#line 345 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 134 "src/lexer-keywords.txt"
+ {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 564 "src/lexer-keywords.txt"
+ {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect},
+ {""}, {""},
+#line 337 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U},
+ {""},
+#line 335 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S},
+ {""}, {""}, {""}, {""},
+#line 356 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU},
+#line 232 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 301 "src/lexer-keywords.txt"
+ {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 479 "src/lexer-keywords.txt"
- {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS},
{""},
-#line 480 "src/lexer-keywords.txt"
- {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 499 "src/lexer-keywords.txt"
- {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl},
- {""}, {""}, {""}, {""}, {""},
-#line 587 "src/lexer-keywords.txt"
- {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 35 "src/lexer-keywords.txt"
+ {"call_ref", TokenType::CallRef, Opcode::CallRef},
{""}, {""}, {""},
#line 215 "src/lexer-keywords.txt"
- {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S},
- {""},
-#line 216 "src/lexer-keywords.txt"
{"i16x8.extadd_pairwise_i8x16_u", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U},
+ {""},
+#line 214 "src/lexer-keywords.txt"
+ {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 453 "src/lexer-keywords.txt"
+ {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 456 "src/lexer-keywords.txt"
- {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S},
{""},
-#line 458 "src/lexer-keywords.txt"
- {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U},
- {""}, {""}, {""}, {""},
-#line 224 "src/lexer-keywords.txt"
- {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S},
+#line 504 "src/lexer-keywords.txt"
+ {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub},
+#line 21 "src/lexer-keywords.txt"
+ {"assert_exception", TokenType::AssertException},
{""},
-#line 225 "src/lexer-keywords.txt"
- {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U},
+#line 470 "src/lexer-keywords.txt"
+ {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 120 "src/lexer-keywords.txt"
+ {"f64.div", TokenType::Binary, Opcode::F64Div},
+#line 63 "src/lexer-keywords.txt"
+ {"f32.div", TokenType::Binary, Opcode::F32Div},
{""}, {""}, {""},
-#line 466 "src/lexer-keywords.txt"
- {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S},
+#line 477 "src/lexer-keywords.txt"
+ {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 457 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U},
{""},
-#line 468 "src/lexer-keywords.txt"
- {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U},
+#line 455 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""},
-#line 217 "src/lexer-keywords.txt"
- {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S},
- {""},
-#line 219 "src/lexer-keywords.txt"
- {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U},
- {""}, {""}, {""}, {""}, {""},
-#line 496 "src/lexer-keywords.txt"
- {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt},
- {""},
-#line 500 "src/lexer-keywords.txt"
- {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS},
- {""}, {""}, {""},
-#line 501 "src/lexer-keywords.txt"
- {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 378 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg},
-#line 247 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg},
+#line 135 "src/lexer-keywords.txt"
+ {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 522 "src/lexer-keywords.txt"
- {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow},
{""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 476 "src/lexer-keywords.txt"
- {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 498 "src/lexer-keywords.txt"
- {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""},
-#line 119 "src/lexer-keywords.txt"
- {"f64.copysign", TokenType::Binary, Opcode::F64Copysign},
-#line 61 "src/lexer-keywords.txt"
- {"f32.copysign", TokenType::Binary, Opcode::F32Copysign},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 22 "src/lexer-keywords.txt"
+ {"assert_exhaustion", TokenType::AssertExhaustion},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 590 "src/lexer-keywords.txt"
- {"atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
-#line 517 "src/lexer-keywords.txt"
- {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
-#line 184 "src/lexer-keywords.txt"
- {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 138 "src/lexer-keywords.txt"
+ {"f64.sub", TokenType::Binary, Opcode::F64Sub},
+#line 80 "src/lexer-keywords.txt"
+ {"f32.sub", TokenType::Binary, Opcode::F32Sub},
+#line 429 "src/lexer-keywords.txt"
+ {"i64.sub", TokenType::Binary, Opcode::I64Sub},
+#line 291 "src/lexer-keywords.txt"
+ {"i32.sub", TokenType::Binary, Opcode::I32Sub},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 467 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U},
+ {""},
+#line 465 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
-#line 204 "src/lexer-keywords.txt"
- {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg},
+#line 519 "src/lexer-keywords.txt"
+ {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 77 "src/lexer-keywords.txt"
+ {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""},
-#line 374 "src/lexer-keywords.txt"
- {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU},
-#line 243 "src/lexer-keywords.txt"
- {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 182 "src/lexer-keywords.txt"
+ {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""},
-#line 205 "src/lexer-keywords.txt"
+#line 204 "src/lexer-keywords.txt"
{"i16x8.q15mulr_sat_s", TokenType::Binary, Opcode::I16X8Q15mulrSatS},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""},
-#line 202 "src/lexer-keywords.txt"
- {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 222 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U},
{""},
-#line 203 "src/lexer-keywords.txt"
- {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U},
+#line 221 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 27 "src/lexer-keywords.txt"
+ {"assert_unlinkable", TokenType::AssertUnlinkable},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 38 "src/lexer-keywords.txt"
- {"current_memory", TokenType::MemorySize, Opcode::MemorySize},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 23 "src/lexer-keywords.txt"
+ {"assert_invalid", TokenType::AssertInvalid},
+ {""}, {""}, {""},
+#line 587 "src/lexer-keywords.txt"
+ {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle},
+ {""}, {""}, {""}, {""}, {""},
+#line 87 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U},
+ {""},
+#line 86 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 219 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U},
+ {""},
+#line 217 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 478 "src/lexer-keywords.txt"
- {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 20 "src/lexer-keywords.txt"
+ {"array", Type::Array, TokenType::Array},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""},
-#line 495 "src/lexer-keywords.txt"
- {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 517 "src/lexer-keywords.txt"
+ {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 476 "src/lexer-keywords.txt"
+ {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask},
+ {""}, {""}, {""}, {""}, {""},
+#line 518 "src/lexer-keywords.txt"
+ {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 45 "src/lexer-keywords.txt"
+ {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 379 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub},
+#line 248 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 493 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U},
+ {""},
+#line 492 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 166 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U},
+ {""},
+#line 165 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 417 "src/lexer-keywords.txt"
+ {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 176 "src/lexer-keywords.txt"
- {"grow_memory", TokenType::MemoryGrow, Opcode::MemoryGrow},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""},
-#line 342 "src/lexer-keywords.txt"
- {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S},
- {""},
-#line 343 "src/lexer-keywords.txt"
- {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U},
+ {""}, {""},
+#line 280 "src/lexer-keywords.txt"
+ {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 336 "src/lexer-keywords.txt"
- {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S},
- {""},
-#line 338 "src/lexer-keywords.txt"
- {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""},
-#line 520 "src/lexer-keywords.txt"
- {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 28 "src/lexer-keywords.txt"
+ {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 222 "src/lexer-keywords.txt"
- {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S},
- {""},
-#line 223 "src/lexer-keywords.txt"
- {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 218 "src/lexer-keywords.txt"
- {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S},
- {""},
-#line 220 "src/lexer-keywords.txt"
- {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
-#line 381 "src/lexer-keywords.txt"
- {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg},
-#line 250 "src/lexer-keywords.txt"
- {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 516 "src/lexer-keywords.txt"
+ {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 493 "src/lexer-keywords.txt"
- {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S},
{""},
-#line 494 "src/lexer-keywords.txt"
- {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U}
+#line 167 "src/lexer-keywords.txt"
+ {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
Result OnCallIndirectExpr(CallIndirectExpr*) override;
Result OnCatchExpr(TryExpr*, Catch*) override;
Result OnDelegateExpr(TryExpr*) override;
- Result OnReturnCallExpr(ReturnCallExpr *) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
Result OnGlobalGetExpr(GlobalGetExpr*) override;
Result OnGlobalSetExpr(GlobalSetExpr*) override;
Result BeginIfExpr(IfExpr*) override;
Result EndIfExpr(IfExpr*) override;
+ Result OnLoadExpr(LoadExpr*) override;
Result OnLocalGetExpr(LocalGetExpr*) override;
Result OnLocalSetExpr(LocalSetExpr*) override;
Result OnLocalTeeExpr(LocalTeeExpr*) override;
Result BeginLoopExpr(LoopExpr*) override;
Result EndLoopExpr(LoopExpr*) override;
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override;
Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryFillExpr(MemoryFillExpr*) override;
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override;
Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnMemorySizeExpr(MemorySizeExpr*) override;
Result OnElemDropExpr(ElemDropExpr*) override;
Result OnTableCopyExpr(TableCopyExpr*) override;
Result OnTableInitExpr(TableInitExpr*) override;
Result OnTableSizeExpr(TableSizeExpr*) override;
Result OnTableFillExpr(TableFillExpr*) override;
Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
Result BeginTryExpr(TryExpr*) override;
Result EndTryExpr(TryExpr*) override;
Result OnThrowExpr(ThrowExpr*) override;
};
NameResolver::NameResolver(Script* script, Errors* errors)
- : errors_(errors),
- script_(script),
- visitor_(this) {}
+ : errors_(errors), script_(script), visitor_(this) {}
} // end anonymous namespace
return Result::Ok;
}
+Result NameResolver::OnLoadExpr(LoadExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
Result NameResolver::OnLocalGetExpr(LocalGetExpr* expr) {
ResolveLocalVar(&expr->var);
return Result::Ok;
return Result::Ok;
}
+Result NameResolver::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ ResolveMemoryVar(&expr->srcmemidx);
+ ResolveMemoryVar(&expr->destmemidx);
+ return Result::Ok;
+}
+
Result NameResolver::OnDataDropExpr(DataDropExpr* expr) {
ResolveDataSegmentVar(&expr->var);
return Result::Ok;
}
+Result NameResolver::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) {
ResolveDataSegmentVar(&expr->var);
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
return Result::Ok;
}
return Result::Ok;
}
+Result NameResolver::OnStoreExpr(StoreExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
Result NameResolver::BeginTryExpr(TryExpr* expr) {
PushLabel(expr->block.label);
ResolveBlockDeclarationVar(&expr->block.decl);
void NameResolver::VisitElemSegment(ElemSegment* segment) {
ResolveTableVar(&segment->table_var);
visitor_.VisitExprList(segment->offset);
- for (ElemExpr& elem_expr : segment->elem_exprs) {
- if (elem_expr.kind == ElemExprKind::RefFunc) {
- ResolveFuncVar(&elem_expr.var);
+ for (ExprList& elem_expr : segment->elem_exprs) {
+ if (elem_expr.size() == 1 &&
+ elem_expr.front().type() == ExprType::RefFunc) {
+ ResolveFuncVar(&cast<RefFuncExpr>(&elem_expr.front())->var);
}
}
}
case CommandType::AssertReturn:
case CommandType::AssertTrap:
case CommandType::AssertExhaustion:
+ case CommandType::AssertException:
case CommandType::Register:
/* Don't resolve a module_var, since it doesn't really behave like other
* vars. You can't reference a module by index. */
}
void SharedValidator::OnTypecheckerError(const char* msg) {
- PrintError(*expr_loc_, "%s", msg);
+ PrintError(expr_loc_, "%s", msg);
}
Result SharedValidator::OnFuncType(const Location& loc,
Index param_count,
const Type* param_types,
Index result_count,
- const Type* result_types) {
+ const Type* result_types,
+ Index type_index) {
Result result = Result::Ok;
if (!options_.features.multi_value_enabled() && result_count > 1) {
- result |=
- PrintError(loc, "multiple result values not currently supported.");
+ result |= PrintError(loc,
+ "multiple result values are not supported without "
+ "multi-value enabled.");
}
- func_types_.emplace(num_types_++,
- FuncType{ToTypeVector(param_count, param_types),
- ToTypeVector(result_count, result_types)});
+ func_types_.emplace(
+ num_types_++,
+ FuncType{ToTypeVector(param_count, param_types),
+ ToTypeVector(result_count, result_types), type_index});
return result;
}
Result SharedValidator::OnMemory(const Location& loc, const Limits& limits) {
Result result = Result::Ok;
- if (memories_.size() > 0) {
+ if (memories_.size() > 0 && !options_.features.multi_memory_enabled()) {
result |= PrintError(loc, "only one memory block allowed");
}
result |= CheckLimits(
const char* desc) {
if (Failed(TypeChecker::CheckType(actual, expected))) {
PrintError(loc, "type mismatch at %s. got %s, expected %s", desc,
- actual.GetName(), expected.GetName());
+ actual.GetName().c_str(), expected.GetName().c_str());
return Result::Error;
}
return Result::Ok;
}
-Result SharedValidator::OnGlobalInitExpr_Const(const Location& loc,
- Type actual) {
- return CheckType(loc, actual, globals_.back().type,
- "global initializer expression");
-}
-
-Result SharedValidator::OnGlobalInitExpr_GlobalGet(const Location& loc,
- Var ref_global_var) {
- Result result = Result::Ok;
- GlobalType ref_global;
- CHECK_RESULT(CheckGlobalIndex(ref_global_var, &ref_global));
-
- if (ref_global_var.index() >= num_imported_globals_) {
- result |= PrintError(
- ref_global_var.loc,
- "initializer expression can only reference an imported global");
- }
-
- if (ref_global.mutable_) {
- result |= PrintError(
- loc, "initializer expression cannot reference a mutable global");
- }
-
- result |= CheckType(loc, ref_global.type, globals_.back().type,
- "global initializer expression");
- return result;
-}
-
-Result SharedValidator::OnGlobalInitExpr_RefNull(const Location& loc,
- Type type) {
- return CheckType(loc, type, globals_.back().type,
- "global initializer expression");
-}
-
-Result SharedValidator::OnGlobalInitExpr_RefFunc(const Location& loc,
- Var func_var) {
- Result result = Result::Ok;
- result |= CheckFuncIndex(func_var);
- init_expr_funcs_.push_back(func_var);
- result |= CheckType(loc, Type::FuncRef, globals_.back().type,
- "global initializer expression");
- return result;
-}
-
-Result SharedValidator::OnGlobalInitExpr_Other(const Location& loc) {
- return PrintError(
- loc,
- "invalid global initializer expression, must be a constant expression");
-}
-
Result SharedValidator::OnTag(const Location& loc, Var sig_var) {
Result result = Result::Ok;
FuncType type;
elems_.back().element = elem_type;
}
-Result SharedValidator::OnElemSegmentInitExpr_Const(const Location& loc,
- Type type) {
- return CheckType(loc, type, Type::I32, "elem segment offset");
-}
-
-Result SharedValidator::OnElemSegmentInitExpr_GlobalGet(const Location& loc,
- Var global_var) {
- Result result = Result::Ok;
- GlobalType ref_global;
- result |= CheckGlobalIndex(global_var, &ref_global);
-
- if (ref_global.mutable_) {
- result |= PrintError(
- loc, "initializer expression cannot reference a mutable global");
- }
-
- result |= CheckType(loc, ref_global.type, Type::I32, "elem segment offset");
- return result;
-}
-
-Result SharedValidator::OnElemSegmentInitExpr_Other(const Location& loc) {
- return PrintError(loc,
- "invalid elem segment offset, must be a constant "
- "expression; either i32.const or "
- "global.get.");
-}
-
Result SharedValidator::OnElemSegmentElemExpr_RefNull(const Location& loc,
Type type) {
return CheckType(loc, type, elems_.back().element, "elem expression");
return result;
}
-Result SharedValidator::OnDataSegmentInitExpr_Const(const Location& loc,
- Type type) {
- auto required =
- memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType();
- return CheckType(loc, type, required, "data segment offset");
-}
-
-Result SharedValidator::OnDataSegmentInitExpr_GlobalGet(const Location& loc,
- Var global_var) {
- Result result = Result::Ok;
- GlobalType ref_global;
- result |= CheckGlobalIndex(global_var, &ref_global);
-
- if (ref_global.mutable_) {
- result |= PrintError(
- loc, "initializer expression cannot reference a mutable global");
- }
-
- auto required =
- memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType();
- result |= CheckType(loc, ref_global.type, required, "data segment offset");
- return result;
-}
-
-Result SharedValidator::OnDataSegmentInitExpr_Other(const Location& loc) {
- return PrintError(loc,
- "invalid data segment offset, must be a constant "
- "expression; either iXX.const or "
- "global.get.");
-}
-
Result SharedValidator::CheckDeclaredFunc(Var func_var) {
if (declared_funcs_.count(func_var.index()) == 0) {
return PrintError(func_var.loc,
- "function is not declared in any elem sections");
+ "function %" PRIindex
+ " is not declared in any elem sections",
+ func_var.index());
}
return Result::Ok;
}
// mentioned in an elems section. This can't be done while process the
// globals because the global section comes before the elem section.
Result result = Result::Ok;
- for (Var func_var : init_expr_funcs_) {
+ for (Var func_var : check_declared_funcs_) {
result |= CheckDeclaredFunc(func_var);
}
return result;
return result;
}
+Index SharedValidator::GetFunctionTypeIndex(Index func_index) const {
+ assert(func_index < funcs_.size());
+ return funcs_[func_index].type_index;
+}
+
+Result SharedValidator::BeginInitExpr(const Location& loc, Type type) {
+ expr_loc_ = loc;
+ in_init_expr_ = true;
+ return typechecker_.BeginInitExpr(type);
+}
+
+Result SharedValidator::EndInitExpr() {
+ in_init_expr_ = false;
+ return typechecker_.EndInitExpr();
+}
+
Result SharedValidator::BeginFunctionBody(const Location& loc,
Index func_index) {
- expr_loc_ = &loc;
+ expr_loc_ = loc;
locals_.clear();
if (func_index < funcs_.size()) {
for (Type type : funcs_[func_index].params) {
return Result::Ok;
}
+static bool ValidInitOpcode(Opcode opcode) {
+ return opcode == Opcode::GlobalGet || opcode == Opcode::I32Const ||
+ opcode == Opcode::I64Const || opcode == Opcode::F32Const ||
+ opcode == Opcode::F64Const || opcode == Opcode::RefFunc ||
+ opcode == Opcode::RefNull;
+}
+
+Result SharedValidator::CheckInstr(Opcode opcode, const Location& loc) {
+ expr_loc_ = loc;
+ if (in_init_expr_ && !ValidInitOpcode(opcode)) {
+ PrintError(loc,
+ "invalid initializer: instruction not valid in initializer "
+ "expression: %s",
+ opcode.GetName());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
Result SharedValidator::OnAtomicFence(const Location& loc,
uint32_t consistency_model) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::AtomicFence, loc);
if (consistency_model != 0) {
result |= PrintError(
loc, "unexpected atomic.fence consistency model (expected 0): %u",
Result SharedValidator::OnAtomicLoad(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicLoad(opcode, mt.limits);
Result SharedValidator::OnAtomicNotify(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicNotify(opcode, mt.limits);
Result SharedValidator::OnAtomicRmwCmpxchg(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicRmwCmpxchg(opcode, mt.limits);
Result SharedValidator::OnAtomicRmw(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicRmw(opcode, mt.limits);
Result SharedValidator::OnAtomicStore(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicStore(opcode, mt.limits);
Result SharedValidator::OnAtomicWait(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnAtomicWait(opcode, mt.limits);
}
Result SharedValidator::OnBinary(const Location& loc, Opcode opcode) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnBinary(opcode);
return result;
}
Result SharedValidator::OnBlock(const Location& loc, Type sig_type) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::Block, loc);
TypeVector param_types, result_types;
- expr_loc_ = &loc;
result |= CheckBlockSignature(loc, Opcode::Block, sig_type, ¶m_types,
&result_types);
result |= typechecker_.OnBlock(param_types, result_types);
}
Result SharedValidator::OnBr(const Location& loc, Var depth) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Br, loc);
result |= typechecker_.OnBr(depth.index());
return result;
}
Result SharedValidator::OnBrIf(const Location& loc, Var depth) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::BrIf, loc);
result |= typechecker_.OnBrIf(depth.index());
return result;
}
Result SharedValidator::BeginBrTable(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::BrTable, loc);
result |= typechecker_.BeginBrTable();
return result;
}
Result SharedValidator::OnBrTableTarget(const Location& loc, Var depth) {
Result result = Result::Ok;
- expr_loc_ = &loc;
+ expr_loc_ = loc;
result |= typechecker_.OnBrTableTarget(depth.index());
return result;
}
Result SharedValidator::EndBrTable(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::BrTable, loc);
result |= typechecker_.EndBrTable();
return result;
}
Result SharedValidator::OnCall(const Location& loc, Var func_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Call, loc);
FuncType func_type;
result |= CheckFuncIndex(func_var, &func_type);
result |= typechecker_.OnCall(func_type.params, func_type.results);
Result SharedValidator::OnCallIndirect(const Location& loc,
Var sig_var,
Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::CallIndirect, loc);
FuncType func_type;
result |= CheckFuncTypeIndex(sig_var, &func_type);
result |= CheckTableIndex(table_var);
return result;
}
-Result SharedValidator::OnCallRef(const Location& loc, Index* function_type_index) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+Result SharedValidator::OnCallRef(const Location& loc,
+ Index* function_type_index) {
+ Result result = CheckInstr(Opcode::CallRef, loc);
Index func_index;
- result |= typechecker_.OnFuncRef(&func_index);
+ result |= typechecker_.OnIndexedFuncRef(&func_index);
if (Failed(result)) {
return result;
}
Result SharedValidator::OnCatch(const Location& loc,
Var tag_var,
bool is_catch_all) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Catch, loc);
if (is_catch_all) {
TypeVector empty;
result |= typechecker_.OnCatch(empty);
}
Result SharedValidator::OnCompare(const Location& loc, Opcode opcode) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnCompare(opcode);
return result;
}
Result SharedValidator::OnConst(const Location& loc, Type type) {
Result result = Result::Ok;
- expr_loc_ = &loc;
+ expr_loc_ = loc;
result |= typechecker_.OnConst(type);
return result;
}
Result SharedValidator::OnConvert(const Location& loc, Opcode opcode) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnConvert(opcode);
return result;
}
Result SharedValidator::OnDataDrop(const Location& loc, Var segment_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::DataDrop, loc);
result |= CheckDataSegmentIndex(segment_var);
result |= typechecker_.OnDataDrop(segment_var.index());
return result;
}
Result SharedValidator::OnDelegate(const Location& loc, Var depth) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Delegate, loc);
result |= typechecker_.OnDelegate(depth.index());
return result;
}
Result SharedValidator::OnDrop(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Drop, loc);
result |= typechecker_.OnDrop();
return result;
}
Result SharedValidator::OnElemDrop(const Location& loc, Var segment_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::ElemDrop, loc);
result |= CheckElemSegmentIndex(segment_var);
result |= typechecker_.OnElemDrop(segment_var.index());
return result;
}
Result SharedValidator::OnElse(const Location& loc) {
+ // Don't call CheckInstr or update expr_loc_ here because if we fail we want
+ // the last expression in the If block to be reported as the error location,
+ // not the else itself.
Result result = Result::Ok;
result |= typechecker_.OnElse();
return result;
}
Result SharedValidator::OnEnd(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::End, loc);
result |= typechecker_.OnEnd();
return result;
}
Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::GlobalGet, loc);
GlobalType global_type;
result |= CheckGlobalIndex(global_var, &global_type);
result |= typechecker_.OnGlobalGet(global_type.type);
+ if (Succeeded(result) && in_init_expr_) {
+ if (global_var.index() >= num_imported_globals_) {
+ result |= PrintError(
+ global_var.loc,
+ "initializer expression can only reference an imported global");
+ }
+ if (global_type.mutable_) {
+ result |= PrintError(
+ loc, "initializer expression cannot reference a mutable global");
+ }
+ }
+
return result;
}
Result SharedValidator::OnGlobalSet(const Location& loc, Var global_var) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::GlobalSet, loc);
GlobalType global_type;
result |= CheckGlobalIndex(global_var, &global_type);
if (!global_type.mutable_) {
loc, "can't global.set on immutable global at index %" PRIindex ".",
global_var.index());
}
- expr_loc_ = &loc;
result |= typechecker_.OnGlobalSet(global_type.type);
return result;
}
Result SharedValidator::OnIf(const Location& loc, Type sig_type) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::If, loc);
TypeVector param_types, result_types;
- expr_loc_ = &loc;
result |= CheckBlockSignature(loc, Opcode::If, sig_type, ¶m_types,
&result_types);
result |= typechecker_.OnIf(param_types, result_types);
Result SharedValidator::OnLoad(const Location& loc,
Opcode opcode,
+ Var memidx,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(memidx, &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnLoad(opcode, mt.limits);
return result;
Result SharedValidator::OnLoadSplat(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnLoad(opcode, mt.limits);
Result SharedValidator::OnLoadZero(const Location& loc,
Opcode opcode,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnLoad(opcode, mt.limits);
}
Result SharedValidator::OnLocalGet(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalGet, loc));
Result result = Result::Ok;
Type type = Type::Any;
- expr_loc_ = &loc;
result |= CheckLocalIndex(local_var, &type);
result |= typechecker_.OnLocalGet(type);
return result;
}
Result SharedValidator::OnLocalSet(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalSet, loc));
Result result = Result::Ok;
Type type = Type::Any;
- expr_loc_ = &loc;
result |= CheckLocalIndex(local_var, &type);
result |= typechecker_.OnLocalSet(type);
return result;
}
Result SharedValidator::OnLocalTee(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalTee, loc));
Result result = Result::Ok;
Type type = Type::Any;
- expr_loc_ = &loc;
result |= CheckLocalIndex(local_var, &type);
result |= typechecker_.OnLocalTee(type);
return result;
}
Result SharedValidator::OnLoop(const Location& loc, Type sig_type) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::Loop, loc);
TypeVector param_types, result_types;
- expr_loc_ = &loc;
result |= CheckBlockSignature(loc, Opcode::Loop, sig_type, ¶m_types,
&result_types);
result |= typechecker_.OnLoop(param_types, result_types);
return result;
}
-Result SharedValidator::OnMemoryCopy(const Location& loc) {
- Result result = Result::Ok;
+Result SharedValidator::OnMemoryCopy(const Location& loc,
+ Var srcmemidx,
+ Var destmemidx) {
+ Result result = CheckInstr(Opcode::MemoryCopy, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(srcmemidx, &mt);
+ result |= CheckMemoryIndex(destmemidx, &mt);
result |= typechecker_.OnMemoryCopy(mt.limits);
return result;
}
-Result SharedValidator::OnMemoryFill(const Location& loc) {
- Result result = Result::Ok;
+Result SharedValidator::OnMemoryFill(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryFill, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= typechecker_.OnMemoryFill(mt.limits);
return result;
}
-Result SharedValidator::OnMemoryGrow(const Location& loc) {
- Result result = Result::Ok;
+Result SharedValidator::OnMemoryGrow(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryGrow, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(memidx, &mt);
result |= typechecker_.OnMemoryGrow(mt.limits);
return result;
}
-Result SharedValidator::OnMemoryInit(const Location& loc, Var segment_var) {
- Result result = Result::Ok;
+Result SharedValidator::OnMemoryInit(const Location& loc,
+ Var segment_var,
+ Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryInit, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(memidx, &mt);
result |= CheckDataSegmentIndex(segment_var);
result |= typechecker_.OnMemoryInit(segment_var.index(), mt.limits);
return result;
}
-Result SharedValidator::OnMemorySize(const Location& loc) {
- Result result = Result::Ok;
+Result SharedValidator::OnMemorySize(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemorySize, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(memidx, &mt);
result |= typechecker_.OnMemorySize(mt.limits);
return result;
}
Result SharedValidator::OnNop(const Location& loc) {
- expr_loc_ = &loc;
- return Result::Ok;
+ Result result = CheckInstr(Opcode::Nop, loc);
+ return result;
}
Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
- result |= CheckDeclaredFunc(func_var);
- result |= typechecker_.OnRefFuncExpr(func_var.index());
+ Result result = CheckInstr(Opcode::RefFunc, loc);
+ result |= CheckFuncIndex(func_var);
+ if (Succeeded(result)) {
+ check_declared_funcs_.push_back(func_var);
+ Index func_type = GetFunctionTypeIndex(func_var.index());
+ result |= typechecker_.OnRefFuncExpr(func_type);
+ }
return result;
}
Result SharedValidator::OnRefIsNull(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::RefIsNull, loc);
result |= typechecker_.OnRefIsNullExpr();
return result;
}
Result SharedValidator::OnRefNull(const Location& loc, Type type) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::RefNull, loc);
result |= typechecker_.OnRefNullExpr(type);
return result;
}
Result SharedValidator::OnRethrow(const Location& loc, Var depth) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Rethrow, loc);
result |= typechecker_.OnRethrow(depth.index());
return result;
}
Result SharedValidator::OnReturnCall(const Location& loc, Var func_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::ReturnCall, loc);
FuncType func_type;
result |= CheckFuncIndex(func_var, &func_type);
result |= typechecker_.OnReturnCall(func_type.params, func_type.results);
Result SharedValidator::OnReturnCallIndirect(const Location& loc,
Var sig_var,
Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::CallIndirect, loc);
result |= CheckTableIndex(table_var);
FuncType func_type;
result |= CheckFuncTypeIndex(sig_var, &func_type);
}
Result SharedValidator::OnReturn(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Return, loc);
result |= typechecker_.OnReturn();
return result;
}
Result SharedValidator::OnSelect(const Location& loc,
Index result_count,
Type* result_types) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Select, loc);
if (result_count > 1) {
result |=
PrintError(loc, "invalid arity in select instruction: %" PRIindex ".",
Result SharedValidator::OnSimdLaneOp(const Location& loc,
Opcode opcode,
uint64_t value) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnSimdLaneOp(opcode, value);
return result;
}
Result SharedValidator::OnSimdLoadLane(const Location& loc,
- Opcode opcode,
- Address alignment,
- uint64_t value) {
- Result result = Result::Ok;
+ Opcode opcode,
+ Address alignment,
+ uint64_t value) {
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnSimdLoadLane(opcode, mt.limits, value);
Opcode opcode,
Address alignment,
uint64_t value) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
result |= CheckMemoryIndex(Var(0, loc), &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnSimdStoreLane(opcode, mt.limits, value);
Result SharedValidator::OnSimdShuffleOp(const Location& loc,
Opcode opcode,
v128 value) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnSimdShuffleOp(opcode, value);
return result;
}
Result SharedValidator::OnStore(const Location& loc,
Opcode opcode,
+ Var memidx,
Address alignment) {
- Result result = Result::Ok;
+ Result result = CheckInstr(opcode, loc);
MemoryType mt;
- expr_loc_ = &loc;
- result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckMemoryIndex(memidx, &mt);
result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
result |= typechecker_.OnStore(opcode, mt.limits);
return result;
Result SharedValidator::OnTableCopy(const Location& loc,
Var dst_var,
Var src_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableCopy, loc);
TableType dst_table;
TableType src_table;
result |= CheckTableIndex(dst_var, &dst_table);
}
Result SharedValidator::OnTableFill(const Location& loc, Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableFill, loc);
TableType table_type;
result |= CheckTableIndex(table_var, &table_type);
result |= typechecker_.OnTableFill(table_type.element);
}
Result SharedValidator::OnTableGet(const Location& loc, Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableGet, loc);
TableType table_type;
result |= CheckTableIndex(table_var, &table_type);
result |= typechecker_.OnTableGet(table_type.element);
}
Result SharedValidator::OnTableGrow(const Location& loc, Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableGrow, loc);
TableType table_type;
result |= CheckTableIndex(table_var, &table_type);
result |= typechecker_.OnTableGrow(table_type.element);
Result SharedValidator::OnTableInit(const Location& loc,
Var segment_var,
Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableInit, loc);
TableType table_type;
ElemType elem_type;
result |= CheckTableIndex(table_var, &table_type);
}
Result SharedValidator::OnTableSet(const Location& loc, Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableSet, loc);
TableType table_type;
result |= CheckTableIndex(table_var, &table_type);
result |= typechecker_.OnTableSet(table_type.element);
}
Result SharedValidator::OnTableSize(const Location& loc, Var table_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::TableSize, loc);
result |= CheckTableIndex(table_var);
result |= typechecker_.OnTableSize();
return result;
}
Result SharedValidator::OnTernary(const Location& loc, Opcode opcode) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnTernary(opcode);
return result;
}
Result SharedValidator::OnThrow(const Location& loc, Var tag_var) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Throw, loc);
TagType tag_type;
result |= CheckTagIndex(tag_var, &tag_type);
result |= typechecker_.OnThrow(tag_type.params);
}
Result SharedValidator::OnTry(const Location& loc, Type sig_type) {
- Result result = Result::Ok;
+ Result result = CheckInstr(Opcode::Try, loc);
TypeVector param_types, result_types;
- expr_loc_ = &loc;
result |= CheckBlockSignature(loc, Opcode::Try, sig_type, ¶m_types,
&result_types);
result |= typechecker_.OnTry(param_types, result_types);
}
Result SharedValidator::OnUnary(const Location& loc, Opcode opcode) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(opcode, loc);
result |= typechecker_.OnUnary(opcode);
return result;
}
Result SharedValidator::OnUnreachable(const Location& loc) {
- Result result = Result::Ok;
- expr_loc_ = &loc;
+ Result result = CheckInstr(Opcode::Unreachable, loc);
result |= typechecker_.OnUnreachable();
return result;
}
Result GetLabel(Index depth, Label** out_label) {
return typechecker_.GetLabel(depth, out_label);
}
+ Result GetCatchCount(Index depth, Index* out_count) {
+ return typechecker_.GetCatchCount(depth, out_count);
+ }
Result WABT_PRINTF_FORMAT(3, 4)
PrintError(const Location& loc, const char* fmt, ...);
Index param_count,
const Type* param_types,
Index result_count,
- const Type* result_types);
+ const Type* result_types,
+ Index type_index);
Result OnStructType(const Location&, Index field_count, TypeMut* fields);
Result OnArrayType(const Location&, TypeMut field);
Result OnMemory(const Location&, const Limits&);
Result OnGlobalImport(const Location&, Type type, bool mutable_);
Result OnGlobal(const Location&, Type type, bool mutable_);
- Result OnGlobalInitExpr_Const(const Location&, Type);
- Result OnGlobalInitExpr_GlobalGet(const Location&, Var global_var);
- Result OnGlobalInitExpr_RefNull(const Location&, Type type);
- Result OnGlobalInitExpr_RefFunc(const Location&, Var func_var);
- Result OnGlobalInitExpr_Other(const Location&);
Result OnTag(const Location&, Var sig_var);
Result OnExport(const Location&,
Result OnElemSegment(const Location&, Var table_var, SegmentKind);
void OnElemSegmentElemType(Type elem_type);
- Result OnElemSegmentInitExpr_Const(const Location&, Type);
- Result OnElemSegmentInitExpr_GlobalGet(const Location&, Var global_var);
- Result OnElemSegmentInitExpr_Other(const Location&);
Result OnElemSegmentElemExpr_RefNull(const Location&, Type type);
Result OnElemSegmentElemExpr_RefFunc(const Location&, Var func_var);
Result OnElemSegmentElemExpr_Other(const Location&);
void OnDataCount(Index count);
-
Result OnDataSegment(const Location&, Var memory_var, SegmentKind);
- Result OnDataSegmentInitExpr_Const(const Location&, Type);
- Result OnDataSegmentInitExpr_GlobalGet(const Location&, Var global_var);
- Result OnDataSegmentInitExpr_Other(const Location&);
+
+ Result BeginInitExpr(const Location&, Type type);
+ Result EndInitExpr();
Result BeginFunctionBody(const Location&, Index func_index);
Result EndFunctionBody(const Location&);
Result OnGlobalGet(const Location&, Var);
Result OnGlobalSet(const Location&, Var);
Result OnIf(const Location&, Type sig_type);
- Result OnLoad(const Location&, Opcode, Address align);
+ Result OnLoad(const Location&, Opcode, Var memidx, Address align);
Result OnLoadSplat(const Location&, Opcode, Address align);
Result OnLoadZero(const Location&, Opcode, Address align);
Result OnLocalGet(const Location&, Var);
Result OnLocalSet(const Location&, Var);
Result OnLocalTee(const Location&, Var);
Result OnLoop(const Location&, Type sig_type);
- Result OnMemoryCopy(const Location&);
- Result OnMemoryFill(const Location&);
- Result OnMemoryGrow(const Location&);
- Result OnMemoryInit(const Location&, Var segment_var);
- Result OnMemorySize(const Location&);
+ Result OnMemoryCopy(const Location&, Var srcmemidx, Var destmemidx);
+ Result OnMemoryFill(const Location&, Var memidx);
+ Result OnMemoryGrow(const Location&, Var memidx);
+ Result OnMemoryInit(const Location&, Var segment_var, Var memidx);
+ Result OnMemorySize(const Location&, Var memidx);
Result OnNop(const Location&);
Result OnRefFunc(const Location&, Var func_var);
Result OnRefIsNull(const Location&);
Result OnReturn(const Location&);
Result OnSelect(const Location&, Index result_count, Type* result_types);
Result OnSimdLaneOp(const Location&, Opcode, uint64_t lane_idx);
- Result OnSimdLoadLane(const Location&, Opcode, Address align, uint64_t lane_idx);
- Result OnSimdStoreLane(const Location&, Opcode, Address align, uint64_t lane_idx);
+ Result OnSimdLoadLane(const Location&,
+ Opcode,
+ Address align,
+ uint64_t lane_idx);
+ Result OnSimdStoreLane(const Location&,
+ Opcode,
+ Address align,
+ uint64_t lane_idx);
Result OnSimdShuffleOp(const Location&, Opcode, v128 lane_idx);
- Result OnStore(const Location&, Opcode, Address align);
+ Result OnStore(const Location&, Opcode, Var memidx, Address align);
Result OnTableCopy(const Location&, Var dst_var, Var src_var);
Result OnTableFill(const Location&, Var table_var);
Result OnTableGet(const Location&, Var table_var);
private:
struct FuncType {
FuncType() = default;
- FuncType(const TypeVector& params, const TypeVector& results)
- : params(params), results(results) {}
+ FuncType(const TypeVector& params,
+ const TypeVector& results,
+ Index type_index)
+ : params(params), results(results), type_index(type_index) {}
TypeVector params;
TypeVector results;
+ Index type_index;
};
struct StructType {
Index end;
};
+ Result CheckInstr(Opcode opcode, const Location& loc);
Result CheckType(const Location&,
Type actual,
Type expected,
Result CheckDataSegmentIndex(Var data_segment_var);
Result CheckAlign(const Location&, Address align, Address natural_align);
- Result CheckAtomicAlign(const Location&, Address align, Address natural_align);
+ Result CheckAtomicAlign(const Location&,
+ Address align,
+ Address natural_align);
Result CheckBlockSignature(const Location&,
Opcode,
TypeVector* out_param_types,
TypeVector* out_result_types);
+ Index GetFunctionTypeIndex(Index func_index) const;
+
TypeVector ToTypeVector(Index count, const Type* types);
ValidateOptions options_;
Errors* errors_;
TypeChecker typechecker_; // TODO: Move into SharedValidator.
// Cached for access by OnTypecheckerError.
- const Location* expr_loc_ = nullptr;
+ Location expr_loc_ = Location(kInvalidOffset);
+ bool in_init_expr_ = false;
Index num_types_ = 0;
std::map<Index, FuncType> func_types_;
std::set<std::string> export_names_; // Used to check for duplicates.
std::set<Index> declared_funcs_; // TODO: optimize?
- std::vector<Var> init_expr_funcs_;
+ std::vector<Var> check_declared_funcs_;
};
} // namespace wabt
}
void FileStream::Flush() {
- if (file_) fflush(file_);
+ if (file_) {
+ fflush(file_);
+ }
}
Result FileStream::WriteDataImpl(size_t at, const void* data, size_t size) {
--- /dev/null
+/*
+ * Copyright 2021 WebAssembly Community Group participants
+ *
+ * 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 WABT_STRING_FORMAT_H_
+#define WABT_STRING_FORMAT_H_
+
+#include <string>
+#include <vector>
+
+#include "config.h"
+
+#define PRIstringview "%.*s"
+#define WABT_PRINTF_STRING_VIEW_ARG(x) \
+ static_cast<int>((x).length()), (x).data()
+
+#define PRItypecode "%s%#x"
+#define WABT_PRINTF_TYPE_CODE(x) \
+ (static_cast<int32_t>(x) < 0 ? "-" : ""), std::abs(static_cast<int32_t>(x))
+
+#define WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE 128
+#define WABT_SNPRINTF_ALLOCA(buffer, len, format) \
+ va_list args; \
+ va_list args_copy; \
+ va_start(args, format); \
+ va_copy(args_copy, args); \
+ char fixed_buf[WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE]; \
+ char* buffer = fixed_buf; \
+ size_t len = wabt_vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); \
+ va_end(args); \
+ if (len + 1 > sizeof(fixed_buf)) { \
+ buffer = static_cast<char*>(alloca(len + 1)); \
+ len = wabt_vsnprintf(buffer, len + 1, format, args_copy); \
+ } \
+ va_end(args_copy)
+
+namespace wabt {
+
+inline std::string WABT_PRINTF_FORMAT(1, 2)
+ StringPrintf(const char* format, ...) {
+ va_list args;
+ va_list args_copy;
+ va_start(args, format);
+ va_copy(args_copy, args);
+ size_t len = wabt_vsnprintf(nullptr, 0, format, args) + 1; // For \0.
+ std::vector<char> buffer(len);
+ va_end(args);
+ wabt_vsnprintf(buffer.data(), len, format, args_copy);
+ va_end(args_copy);
+ return std::string(buffer.data(), len - 1);
+}
+
+} // namespace wabt
+
+#endif // WABT_STRING_FORMAT_H_
return substr(pos1, n1).compare(string_view(s, n2));
}
-string_view::size_type string_view::find(string_view s, size_type pos) const
- noexcept {
+string_view::size_type string_view::find(string_view s,
+ size_type pos) const noexcept {
pos = std::min(pos, size_);
const_iterator iter = std::search(begin() + pos, end(), s.begin(), s.end());
return iter == end() ? npos : iter - begin();
return find(string_view(s), pos);
}
-string_view::size_type string_view::rfind(string_view s, size_type pos) const
- noexcept {
+string_view::size_type string_view::rfind(string_view s,
+ size_type pos) const noexcept {
pos = std::min(std::min(pos, size_ - s.size_) + s.size_, size_);
reverse_iterator iter = std::search(reverse_iterator(begin() + pos), rend(),
s.rbegin(), s.rend());
return iter == rend() ? npos : (rend() - iter - s.size_);
}
-string_view::size_type string_view::rfind(char c, size_type pos) const
- noexcept {
+string_view::size_type string_view::rfind(char c,
+ size_type pos) const noexcept {
return rfind(string_view(&c, 1), pos);
}
return rfind(string_view(s), pos);
}
-string_view::size_type string_view::find_first_of(string_view s,
- size_type pos) const
- noexcept {
+string_view::size_type string_view::find_first_of(string_view s, size_type pos)
+ const noexcept {
pos = std::min(pos, size_);
const_iterator iter =
std::find_first_of(begin() + pos, end(), s.begin(), s.end());
return iter == end() ? npos : iter - begin();
}
-string_view::size_type string_view::find_first_of(char c, size_type pos) const
- noexcept {
+string_view::size_type string_view::find_first_of(char c, size_type pos)
+ const noexcept {
return find_first_of(string_view(&c, 1), pos);
}
return iter == rend() ? npos : (rend() - iter - 1);
}
-string_view::size_type string_view::find_last_of(char c, size_type pos) const
- noexcept {
+string_view::size_type string_view::find_last_of(char c,
+ size_type pos) const noexcept {
return find_last_of(string_view(&c, 1), pos);
}
/*constexpr*/ size_type find(char c, size_type pos = 0) const noexcept;
/*constexpr*/ size_type find(const char* s, size_type pos, size_type n) const;
/*constexpr*/ size_type find(const char* s, size_type pos = 0) const;
- /*constexpr*/ size_type rfind(string_view s, size_type pos = npos) const
- noexcept;
+ /*constexpr*/ size_type rfind(string_view s,
+ size_type pos = npos) const noexcept;
/*constexpr*/ size_type rfind(char c, size_type pos = npos) const noexcept;
/*constexpr*/ size_type rfind(const char* s,
size_type pos,
size_type n) const;
/*constexpr*/ size_type rfind(const char* s, size_type pos = npos) const;
- /*constexpr*/ size_type find_first_of(string_view s, size_type pos = 0) const
- noexcept;
- /*constexpr*/ size_type find_first_of(char c, size_type pos = 0) const
- noexcept;
+ /*constexpr*/ size_type find_first_of(string_view s,
+ size_type pos = 0) const noexcept;
+ /*constexpr*/ size_type find_first_of(char c,
+ size_type pos = 0) const noexcept;
/*constexpr*/ size_type find_first_of(const char* s,
size_type pos,
size_type n) const;
/*constexpr*/ size_type find_first_of(const char* s, size_type pos = 0) const;
/*constexpr*/ size_type find_last_of(string_view s,
size_type pos = npos) const noexcept;
- /*constexpr*/ size_type find_last_of(char c, size_type pos = npos) const
- noexcept;
+ /*constexpr*/ size_type find_last_of(char c,
+ size_type pos = npos) const noexcept;
/*constexpr*/ size_type find_last_of(const char* s,
size_type pos,
size_type n) const;
inline void cat_concatenate(std::string&) {}
-template<typename T, typename ...Ts>
+template <typename T, typename... Ts>
void cat_concatenate(std::string& s, const T& t, const Ts&... args) {
- s += t;
- cat_concatenate(s, args...);
+ s += t;
+ cat_concatenate(s, args...);
}
-inline size_t cat_compute_size() { return 0; }
+inline size_t cat_compute_size() {
+ return 0;
+}
-template<typename T, typename ...Ts>
+template <typename T, typename... Ts>
size_t cat_compute_size(const T& t, const Ts&... args) {
- return string_view(t).size() + cat_compute_size(args...);
+ return string_view(t).size() + cat_compute_size(args...);
}
// Is able to concatenate any combination of string/string_view/char*
-template<typename ...Ts> std::string cat(const Ts&... args) {
- std::string s;
- s.reserve(cat_compute_size(args...));
- cat_concatenate(s, args...);
- return s;
+template <typename... Ts>
+std::string cat(const Ts&... args) {
+ std::string s;
+ s.reserve(cat_compute_size(args...));
+ cat_concatenate(s, args...);
+ return s;
}
inline constexpr string_view::string_view() noexcept
inline constexpr string_view::string_view(const char* str, size_type len)
: data_(str), size_(len) {}
-inline constexpr string_view::const_iterator string_view::begin() const
- noexcept {
+inline constexpr string_view::const_iterator string_view::begin()
+ const noexcept {
return data_;
}
return data_ + size_;
}
-inline constexpr string_view::const_iterator string_view::cbegin() const
- noexcept {
+inline constexpr string_view::const_iterator string_view::cbegin()
+ const noexcept {
return data_;
}
-inline constexpr string_view::const_iterator string_view::cend() const
- noexcept {
+inline constexpr string_view::const_iterator string_view::cend()
+ const noexcept {
return data_ + size_;
}
-inline string_view::const_reverse_iterator string_view::rbegin() const
- noexcept {
+inline string_view::const_reverse_iterator string_view::rbegin()
+ const noexcept {
return const_reverse_iterator(end());
}
return const_reverse_iterator(begin());
}
-inline string_view::const_reverse_iterator string_view::crbegin() const
- noexcept {
+inline string_view::const_reverse_iterator string_view::crbegin()
+ const noexcept {
return const_reverse_iterator(cend());
}
}
inline std::ostream& operator<<(std::ostream& os, string_view sv) {
- os.write(sv.data(), sv.size());
- return os;
+ os.write(sv.data(), sv.size());
+ return os;
}
} // namespace wabt
#include "gtest/gtest.h"
-#include "src/binary-reader.h"
#include "src/binary-reader-nop.h"
+#include "src/binary-reader.h"
#include "src/leb128.h"
#include "src/opcode.h"
void ReadModule(const std::vector<u8>& data) {
Errors errors;
ReadBinaryOptions options;
- Result result = ReadBinaryInterp(data.data(), data.size(), options, &errors,
- &module_desc_);
+ Result result = ReadBinaryInterp("<internal>", data.data(), data.size(),
+ options, &errors, &module_desc_);
ASSERT_EQ(Result::Ok, result)
<< FormatErrorsToString(errors, Location::Type::Binary);
}
Instance::Ptr inst_;
};
-
TEST_F(InterpTest, Empty) {
ASSERT_TRUE(mod_.empty());
ReadModule({0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00});
0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
});
- auto host_func = HostFunc::New(
- store_, FuncType{{ValueType::I32}, {ValueType::I32}},
- [&](Thread& thread, const Values& params, Values& results,
- Trap::Ptr* out_trap) -> Result {
- auto val = params[0].Get<u32>();
- if (val < 10) {
- return GetFuncExport(0)->Call(store_, {Value::Make(val * 2)}, results,
- out_trap);
- }
- results[0] = Value::Make(val);
- return Result::Ok;
- });
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ auto val = params[0].Get<u32>();
+ if (val < 10) {
+ return GetFuncExport(0)->Call(
+ store_, {Value::Make(val * 2)}, results, out_trap);
+ }
+ results[0] = Value::Make(val);
+ return Result::Ok;
+ });
Instantiate({host_func->self()});
Values results;
Trap::Ptr trap;
- Result result = GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap);
+ Result result =
+ GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap);
ASSERT_EQ(Result::Ok, result);
EXPECT_EQ(1u, results.size());
0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
});
- auto thread = Thread::New(store_, {});
+ Thread thread(store_);
auto host_func =
HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
Values results;
Trap::Ptr trap;
- Result result = GetFuncExport(0)->Call(*thread, {Value::Make(1)}, results, &trap);
+ Result result =
+ GetFuncExport(0)->Call(thread, {Value::Make(1)}, results, &trap);
ASSERT_EQ(Result::Ok, result);
EXPECT_EQ(1u, results.size());
// (local $uc i32)
//
// ;; No change if < 'A'.
- // (if (i32.lt_u (get_local $c) (i32.const 65))
- // (return (get_local $c)))
+ // (if (i32.lt_u (local.get $c) (i32.const 65))
+ // (return (local.get $c)))
//
// ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111
- // (set_local $uc (i32.and (get_local $c) (i32.const 0xdf)))
+ // (local.set $uc (i32.and (local.get $c) (i32.const 0xdf)))
//
// ;; In range ['A', 'M'] return |c| + 13.
- // (if (i32.le_u (get_local $uc) (i32.const 77))
- // (return (i32.add (get_local $c) (i32.const 13))))
+ // (if (i32.le_u (local.get $uc) (i32.const 77))
+ // (return (i32.add (local.get $c) (i32.const 13))))
//
// ;; In range ['N', 'Z'] return |c| - 13.
- // (if (i32.le_u (get_local $uc) (i32.const 90))
- // (return (i32.sub (get_local $c) (i32.const 13))))
+ // (if (i32.le_u (local.get $uc) (i32.const 90))
+ // (return (i32.sub (local.get $c) (i32.const 13))))
//
// ;; No change for everything else.
- // (return (get_local $c))
+ // (return (local.get $c))
// )
//
// (func (export "rot13")
// (call $fill_buf (i32.const 0) (i32.const 1024))
//
// ;; The host returns the size filled.
- // (set_local $size)
+ // (local.set $size)
//
// ;; Loop over all bytes and rot13 them.
// (block $exit
// (loop $top
// ;; if (i >= size) break
- // (if (i32.ge_u (get_local $i) (get_local $size)) (br $exit))
+ // (if (i32.ge_u (local.get $i) (local.get $size)) (br $exit))
//
// ;; mem[i] = rot13c(mem[i])
// (i32.store8
- // (get_local $i)
+ // (local.get $i)
// (call $rot13c
- // (i32.load8_u (get_local $i))))
+ // (i32.load8_u (local.get $i))))
//
// ;; i++
- // (set_local $i (i32.add (get_local $i) (i32.const 1)))
+ // (local.set $i (i32.add (local.get $i) (i32.const 1)))
// (br $top)
// )
// )
//
- // (call $buf_done (i32.const 0) (get_local $size))
+ // (call $buf_done (i32.const 0) (local.get $size))
// )
ReadModule({
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60,
class InterpGCTest : public InterpTest {
public:
- void SetUp() override {
- before_new = store_.object_count();
- }
+ void SetUp() override { before_new = store_.object_count(); }
void TearDown() override {
// Reset instance and module, in case they were allocated.
AssertIntEquals(expected, s, ParseInt64, parse_type);
}
-void AssertUint128Equals(v128 expected,
- const char* s) {
+void AssertUint128Equals(v128 expected, const char* s) {
const char* const end = s + strlen(s);
v128 actual;
ASSERT_EQ(Result::Ok, ParseUint128(s, end, &actual)) << s;
const char* input;
uint32_t output;
} kTests[] = {
- {"+0x1.00000100000000000p-50", 0x26800000},
- {"-0x1.00000100000000000p-50", 0xa6800000},
- {"+0x1.00000100000000001p-50", 0x26800001},
- {"-0x1.00000100000000001p-50", 0xa6800001},
- {"+0x1.000001fffffffffffp-50", 0x26800001},
- {"-0x1.000001fffffffffffp-50", 0xa6800001},
- {"+0x1.00000200000000000p-50", 0x26800001},
- {"-0x1.00000200000000000p-50", 0xa6800001},
- {"+0x1.00000200000000001p-50", 0x26800001},
- {"-0x1.00000200000000001p-50", 0xa6800001},
- {"+0x1.000002fffffffffffp-50", 0x26800001},
- {"-0x1.000002fffffffffffp-50", 0xa6800001},
- {"+0x1.00000300000000000p-50", 0x26800002},
- {"-0x1.00000300000000000p-50", 0xa6800002},
- {"+0x1.00000300000000001p-50", 0x26800002},
- {"-0x1.00000300000000001p-50", 0xa6800002},
- {"+0x1.000003fffffffffffp-50", 0x26800002},
- {"-0x1.000003fffffffffffp-50", 0xa6800002},
- {"+0x1.00000400000000000p-50", 0x26800002},
- {"-0x1.00000400000000000p-50", 0xa6800002},
- {"+0x1.00000400000000001p-50", 0x26800002},
- {"-0x1.00000400000000001p-50", 0xa6800002},
- {"+0x1.000004fffffffffffp-50", 0x26800002},
- {"-0x1.000004fffffffffffp-50", 0xa6800002},
- {"+0x1.00000500000000000p-50", 0x26800002},
- {"-0x1.00000500000000000p-50", 0xa6800002},
- {"+0x1.00000500000000001p-50", 0x26800003},
- {"-0x1.00000500000000001p-50", 0xa6800003},
- {"+0x4000.004000000p-64", 0x26800000},
- {"-0x4000.004000000p-64", 0xa6800000},
- {"+0x4000.004000001p-64", 0x26800001},
- {"-0x4000.004000001p-64", 0xa6800001},
- {"+0x4000.007ffffffp-64", 0x26800001},
- {"-0x4000.007ffffffp-64", 0xa6800001},
- {"+0x4000.008000000p-64", 0x26800001},
- {"-0x4000.008000000p-64", 0xa6800001},
- {"+0x4000.008000001p-64", 0x26800001},
- {"-0x4000.008000001p-64", 0xa6800001},
- {"+0x4000.00bffffffp-64", 0x26800001},
- {"-0x4000.00bffffffp-64", 0xa6800001},
- {"+0x4000.00c000000p-64", 0x26800002},
- {"-0x4000.00c000000p-64", 0xa6800002},
- {"+0x4000.00c000001p-64", 0x26800002},
- {"-0x4000.00c000001p-64", 0xa6800002},
- {"+0x4000.00fffffffp-64", 0x26800002},
- {"-0x4000.00fffffffp-64", 0xa6800002},
- {"+0x4000.010000001p-64", 0x26800002},
- {"-0x4000.010000001p-64", 0xa6800002},
- {"+0x4000.013ffffffp-64", 0x26800002},
- {"-0x4000.013ffffffp-64", 0xa6800002},
- {"+0x4000.014000001p-64", 0x26800003},
- {"-0x4000.014000001p-64", 0xa6800003},
- {"+0x1.00000100000000000p+50", 0x58800000},
- {"-0x1.00000100000000000p+50", 0xd8800000},
- {"+0x1.00000100000000001p+50", 0x58800001},
- {"-0x1.00000100000000001p+50", 0xd8800001},
- {"+0x1.000001fffffffffffp+50", 0x58800001},
- {"-0x1.000001fffffffffffp+50", 0xd8800001},
- {"+0x1.00000200000000000p+50", 0x58800001},
- {"-0x1.00000200000000000p+50", 0xd8800001},
- {"+0x1.00000200000000001p+50", 0x58800001},
- {"-0x1.00000200000000001p+50", 0xd8800001},
- {"+0x1.000002fffffffffffp+50", 0x58800001},
- {"-0x1.000002fffffffffffp+50", 0xd8800001},
- {"+0x1.00000300000000000p+50", 0x58800002},
- {"-0x1.00000300000000000p+50", 0xd8800002},
- {"+0x1.00000300000000001p+50", 0x58800002},
- {"-0x1.00000300000000001p+50", 0xd8800002},
- {"+0x1.000003fffffffffffp+50", 0x58800002},
- {"-0x1.000003fffffffffffp+50", 0xd8800002},
- {"+0x1.00000400000000000p+50", 0x58800002},
- {"-0x1.00000400000000000p+50", 0xd8800002},
- {"+0x1.00000400000000001p+50", 0x58800002},
- {"-0x1.00000400000000001p+50", 0xd8800002},
- {"+0x1.000004fffffffffffp+50", 0x58800002},
- {"-0x1.000004fffffffffffp+50", 0xd8800002},
- {"+0x1.00000500000000000p+50", 0x58800002},
- {"-0x1.00000500000000000p+50", 0xd8800002},
- {"+0x1.00000500000000001p+50", 0x58800003},
- {"-0x1.00000500000000001p+50", 0xd8800003},
- {"+0x4000004000000", 0x58800000},
- {"-0x4000004000000", 0xd8800000},
- {"+0x4000004000001", 0x58800001},
- {"-0x4000004000001", 0xd8800001},
- {"+0x4000007ffffff", 0x58800001},
- {"-0x4000007ffffff", 0xd8800001},
- {"+0x4000008000000", 0x58800001},
- {"-0x4000008000000", 0xd8800001},
- {"+0x4000008000001", 0x58800001},
- {"-0x4000008000001", 0xd8800001},
- {"+0x400000bffffff", 0x58800001},
- {"-0x400000bffffff", 0xd8800001},
- {"+0x400000c000000", 0x58800002},
- {"-0x400000c000000", 0xd8800002},
- {"+0x0.00000100000000000p-126", 0x0},
- {"-0x0.00000100000000000p-126", 0x80000000},
- {"+0x0.00000100000000001p-126", 0x1},
- {"-0x0.00000100000000001p-126", 0x80000001},
- {"+0x0.00000101000000000p-126", 0x1},
- {"+0x0.000001fffffffffffp-126", 0x1},
- {"-0x0.000001fffffffffffp-126", 0x80000001},
- {"+0x0.00000200000000000p-126", 0x1},
- {"-0x0.00000200000000000p-126", 0x80000001},
- {"+0x0.00000200000000001p-126", 0x1},
- {"-0x0.00000200000000001p-126", 0x80000001},
- {"+0x0.000002fffffffffffp-126", 0x1},
- {"-0x0.000002fffffffffffp-126", 0x80000001},
- {"+0x0.00000300000000000p-126", 0x2},
- {"-0x0.00000300000000000p-126", 0x80000002},
- {"+0x0.00000300000000001p-126", 0x2},
- {"-0x0.00000300000000001p-126", 0x80000002},
- {"+0x0.000003fffffffffffp-126", 0x2},
- {"-0x0.000003fffffffffffp-126", 0x80000002},
- {"+0x0.00000400000000000p-126", 0x2},
- {"-0x0.00000400000000000p-126", 0x80000002},
- {"+0x0.00000400000000001p-126", 0x2},
- {"-0x0.00000400000000001p-126", 0x80000002},
- {"+0x0.000004fffffffffffp-126", 0x2},
- {"-0x0.000004fffffffffffp-126", 0x80000002},
- {"+0x0.00000500000000000p-126", 0x2},
- {"-0x0.00000500000000000p-126", 0x80000002},
- {"+0x0.00000500000000001p-126", 0x3},
- {"-0x0.00000500000000001p-126", 0x80000003},
- {"+0x1.fffffe8p127", 0x7f7fffff},
- {"-0x1.fffffe8p127", 0xff7fffff},
- {"+0x1.fffffefffffff8p127", 0x7f7fffff},
- {"-0x1.fffffefffffff8p127", 0xff7fffff},
- {"+0x1.fffffefffffffffffp127", 0x7f7fffff},
- {"-0x1.fffffefffffffffffp127", 0xff7fffff},
+ {"+0x1.00000100000000000p-50", 0x26800000},
+ {"-0x1.00000100000000000p-50", 0xa6800000},
+ {"+0x1.00000100000000001p-50", 0x26800001},
+ {"-0x1.00000100000000001p-50", 0xa6800001},
+ {"+0x1.000001fffffffffffp-50", 0x26800001},
+ {"-0x1.000001fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000200000000000p-50", 0x26800001},
+ {"-0x1.00000200000000000p-50", 0xa6800001},
+ {"+0x1.00000200000000001p-50", 0x26800001},
+ {"-0x1.00000200000000001p-50", 0xa6800001},
+ {"+0x1.000002fffffffffffp-50", 0x26800001},
+ {"-0x1.000002fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000300000000000p-50", 0x26800002},
+ {"-0x1.00000300000000000p-50", 0xa6800002},
+ {"+0x1.00000300000000001p-50", 0x26800002},
+ {"-0x1.00000300000000001p-50", 0xa6800002},
+ {"+0x1.000003fffffffffffp-50", 0x26800002},
+ {"-0x1.000003fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000400000000000p-50", 0x26800002},
+ {"-0x1.00000400000000000p-50", 0xa6800002},
+ {"+0x1.00000400000000001p-50", 0x26800002},
+ {"-0x1.00000400000000001p-50", 0xa6800002},
+ {"+0x1.000004fffffffffffp-50", 0x26800002},
+ {"-0x1.000004fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000500000000000p-50", 0x26800002},
+ {"-0x1.00000500000000000p-50", 0xa6800002},
+ {"+0x1.00000500000000001p-50", 0x26800003},
+ {"-0x1.00000500000000001p-50", 0xa6800003},
+ {"+0x4000.004000000p-64", 0x26800000},
+ {"-0x4000.004000000p-64", 0xa6800000},
+ {"+0x4000.004000001p-64", 0x26800001},
+ {"-0x4000.004000001p-64", 0xa6800001},
+ {"+0x4000.007ffffffp-64", 0x26800001},
+ {"-0x4000.007ffffffp-64", 0xa6800001},
+ {"+0x4000.008000000p-64", 0x26800001},
+ {"-0x4000.008000000p-64", 0xa6800001},
+ {"+0x4000.008000001p-64", 0x26800001},
+ {"-0x4000.008000001p-64", 0xa6800001},
+ {"+0x4000.00bffffffp-64", 0x26800001},
+ {"-0x4000.00bffffffp-64", 0xa6800001},
+ {"+0x4000.00c000000p-64", 0x26800002},
+ {"-0x4000.00c000000p-64", 0xa6800002},
+ {"+0x4000.00c000001p-64", 0x26800002},
+ {"-0x4000.00c000001p-64", 0xa6800002},
+ {"+0x4000.00fffffffp-64", 0x26800002},
+ {"-0x4000.00fffffffp-64", 0xa6800002},
+ {"+0x4000.010000001p-64", 0x26800002},
+ {"-0x4000.010000001p-64", 0xa6800002},
+ {"+0x4000.013ffffffp-64", 0x26800002},
+ {"-0x4000.013ffffffp-64", 0xa6800002},
+ {"+0x4000.014000001p-64", 0x26800003},
+ {"-0x4000.014000001p-64", 0xa6800003},
+ {"+0x1.00000100000000000p+50", 0x58800000},
+ {"-0x1.00000100000000000p+50", 0xd8800000},
+ {"+0x1.00000100000000001p+50", 0x58800001},
+ {"-0x1.00000100000000001p+50", 0xd8800001},
+ {"+0x1.000001fffffffffffp+50", 0x58800001},
+ {"-0x1.000001fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000200000000000p+50", 0x58800001},
+ {"-0x1.00000200000000000p+50", 0xd8800001},
+ {"+0x1.00000200000000001p+50", 0x58800001},
+ {"-0x1.00000200000000001p+50", 0xd8800001},
+ {"+0x1.000002fffffffffffp+50", 0x58800001},
+ {"-0x1.000002fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000300000000000p+50", 0x58800002},
+ {"-0x1.00000300000000000p+50", 0xd8800002},
+ {"+0x1.00000300000000001p+50", 0x58800002},
+ {"-0x1.00000300000000001p+50", 0xd8800002},
+ {"+0x1.000003fffffffffffp+50", 0x58800002},
+ {"-0x1.000003fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000400000000000p+50", 0x58800002},
+ {"-0x1.00000400000000000p+50", 0xd8800002},
+ {"+0x1.00000400000000001p+50", 0x58800002},
+ {"-0x1.00000400000000001p+50", 0xd8800002},
+ {"+0x1.000004fffffffffffp+50", 0x58800002},
+ {"-0x1.000004fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000500000000000p+50", 0x58800002},
+ {"-0x1.00000500000000000p+50", 0xd8800002},
+ {"+0x1.00000500000000001p+50", 0x58800003},
+ {"-0x1.00000500000000001p+50", 0xd8800003},
+ {"+0x4000004000000", 0x58800000},
+ {"-0x4000004000000", 0xd8800000},
+ {"+0x4000004000001", 0x58800001},
+ {"-0x4000004000001", 0xd8800001},
+ {"+0x4000007ffffff", 0x58800001},
+ {"-0x4000007ffffff", 0xd8800001},
+ {"+0x4000008000000", 0x58800001},
+ {"-0x4000008000000", 0xd8800001},
+ {"+0x4000008000001", 0x58800001},
+ {"-0x4000008000001", 0xd8800001},
+ {"+0x400000bffffff", 0x58800001},
+ {"-0x400000bffffff", 0xd8800001},
+ {"+0x400000c000000", 0x58800002},
+ {"-0x400000c000000", 0xd8800002},
+ {"+0x0.00000100000000000p-126", 0x0},
+ {"-0x0.00000100000000000p-126", 0x80000000},
+ {"+0x0.00000100000000001p-126", 0x1},
+ {"-0x0.00000100000000001p-126", 0x80000001},
+ {"+0x0.00000101000000000p-126", 0x1},
+ {"+0x0.000001fffffffffffp-126", 0x1},
+ {"-0x0.000001fffffffffffp-126", 0x80000001},
+ {"+0x0.00000200000000000p-126", 0x1},
+ {"-0x0.00000200000000000p-126", 0x80000001},
+ {"+0x0.00000200000000001p-126", 0x1},
+ {"-0x0.00000200000000001p-126", 0x80000001},
+ {"+0x0.000002fffffffffffp-126", 0x1},
+ {"-0x0.000002fffffffffffp-126", 0x80000001},
+ {"+0x0.00000300000000000p-126", 0x2},
+ {"-0x0.00000300000000000p-126", 0x80000002},
+ {"+0x0.00000300000000001p-126", 0x2},
+ {"-0x0.00000300000000001p-126", 0x80000002},
+ {"+0x0.000003fffffffffffp-126", 0x2},
+ {"-0x0.000003fffffffffffp-126", 0x80000002},
+ {"+0x0.00000400000000000p-126", 0x2},
+ {"-0x0.00000400000000000p-126", 0x80000002},
+ {"+0x0.00000400000000001p-126", 0x2},
+ {"-0x0.00000400000000001p-126", 0x80000002},
+ {"+0x0.000004fffffffffffp-126", 0x2},
+ {"-0x0.000004fffffffffffp-126", 0x80000002},
+ {"+0x0.00000500000000000p-126", 0x2},
+ {"-0x0.00000500000000000p-126", 0x80000002},
+ {"+0x0.00000500000000001p-126", 0x3},
+ {"-0x0.00000500000000001p-126", 0x80000003},
+ {"+0x1.fffffe8p127", 0x7f7fffff},
+ {"-0x1.fffffe8p127", 0xff7fffff},
+ {"+0x1.fffffefffffff8p127", 0x7f7fffff},
+ {"-0x1.fffffefffffff8p127", 0xff7fffff},
+ {"+0x1.fffffefffffffffffp127", 0x7f7fffff},
+ {"-0x1.fffffefffffffffffp127", 0xff7fffff},
};
- for (auto test: kTests) {
+ for (auto test : kTests) {
AssertHexFloatEquals(test.output, test.input);
}
}
const char* input;
uint64_t output;
} kTests[] = {
- {"+0x1.000000000000080000000000p-600", 1905022642377719808ull},
- {"-0x1.000000000000080000000000p-600", 11128394679232495616ull},
- {"+0x1.000000000000080000000001p-600", 1905022642377719809ull},
- {"-0x1.000000000000080000000001p-600", 11128394679232495617ull},
- {"+0x1.0000000000000fffffffffffp-600", 1905022642377719809ull},
- {"-0x1.0000000000000fffffffffffp-600", 11128394679232495617ull},
- {"+0x1.000000000000100000000000p-600", 1905022642377719809ull},
- {"-0x1.000000000000100000000000p-600", 11128394679232495617ull},
- {"+0x1.000000000000100000000001p-600", 1905022642377719809ull},
- {"-0x1.000000000000100000000001p-600", 11128394679232495617ull},
- {"+0x1.00000000000017ffffffffffp-600", 1905022642377719809ull},
- {"-0x1.00000000000017ffffffffffp-600", 11128394679232495617ull},
- {"+0x1.000000000000180000000000p-600", 1905022642377719810ull},
- {"-0x1.000000000000180000000000p-600", 11128394679232495618ull},
- {"+0x1.000000000000180000000001p-600", 1905022642377719810ull},
- {"-0x1.000000000000180000000001p-600", 11128394679232495618ull},
- {"+0x1.0000000000001fffffffffffp-600", 1905022642377719810ull},
- {"-0x1.0000000000001fffffffffffp-600", 11128394679232495618ull},
- {"+0x1.000000000000200000000000p-600", 1905022642377719810ull},
- {"-0x1.000000000000200000000000p-600", 11128394679232495618ull},
- {"+0x1.000000000000200000000001p-600", 1905022642377719810ull},
- {"-0x1.000000000000200000000001p-600", 11128394679232495618ull},
- {"+0x1.00000000000027ffffffffffp-600", 1905022642377719810ull},
- {"-0x1.00000000000027ffffffffffp-600", 11128394679232495618ull},
- {"+0x1.000000000000280000000001p-600", 1905022642377719811ull},
- {"-0x1.000000000000280000000001p-600", 11128394679232495619ull},
- {"+0x8000000.000000400000000000p-627", 1905022642377719808ull},
- {"-0x8000000.000000400000000000p-627", 11128394679232495616ull},
- {"+0x8000000.000000400000000001p-627", 1905022642377719809ull},
- {"-0x8000000.000000400000000001p-627", 11128394679232495617ull},
- {"+0x8000000.0000007fffffffffffp-627", 1905022642377719809ull},
- {"-0x8000000.0000007fffffffffffp-627", 11128394679232495617ull},
- {"+0x8000000.000000800000000000p-627", 1905022642377719809ull},
- {"-0x8000000.000000800000000000p-627", 11128394679232495617ull},
- {"+0x8000000.000000800000000001p-627", 1905022642377719809ull},
- {"-0x8000000.000000800000000001p-627", 11128394679232495617ull},
- {"+0x8000000.000000bfffffffffffp-627", 1905022642377719809ull},
- {"-0x8000000.000000bfffffffffffp-627", 11128394679232495617ull},
- {"+0x8000000.000000c00000000000p-627", 1905022642377719810ull},
- {"-0x8000000.000000c00000000000p-627", 11128394679232495618ull},
- {"+0x8000000.000000c00000000001p-627", 1905022642377719810ull},
- {"-0x8000000.000000c00000000001p-627", 11128394679232495618ull},
- {"+0x8000000.000000ffffffffffffp-627", 1905022642377719810ull},
- {"-0x8000000.000000ffffffffffffp-627", 11128394679232495618ull},
- {"+0x8000000.000001000000000000p-627", 1905022642377719810ull},
- {"-0x8000000.000001000000000000p-627", 11128394679232495618ull},
- {"+0x8000000.000001000000000001p-627", 1905022642377719810ull},
- {"-0x8000000.000001000000000001p-627", 11128394679232495618ull},
- {"+0x8000000.0000013fffffffffffp-627", 1905022642377719810ull},
- {"-0x8000000.0000013fffffffffffp-627", 11128394679232495618ull},
- {"+0x8000000.000001400000000001p-627", 1905022642377719811ull},
- {"-0x8000000.000001400000000001p-627", 11128394679232495619ull},
- {"+0x1.000000000000080000000000p+600", 7309342195222315008ull},
- {"-0x1.000000000000080000000000p+600", 16532714232077090816ull},
- {"+0x1.000000000000080000000001p+600", 7309342195222315009ull},
- {"-0x1.000000000000080000000001p+600", 16532714232077090817ull},
- {"+0x1.0000000000000fffffffffffp+600", 7309342195222315009ull},
- {"-0x1.0000000000000fffffffffffp+600", 16532714232077090817ull},
- {"+0x1.000000000000100000000000p+600", 7309342195222315009ull},
- {"-0x1.000000000000100000000000p+600", 16532714232077090817ull},
- {"+0x1.000000000000100000000001p+600", 7309342195222315009ull},
- {"-0x1.000000000000100000000001p+600", 16532714232077090817ull},
- {"+0x1.00000000000017ffffffffffp+600", 7309342195222315009ull},
- {"-0x1.00000000000017ffffffffffp+600", 16532714232077090817ull},
- {"+0x1.000000000000180000000000p+600", 7309342195222315010ull},
- {"-0x1.000000000000180000000000p+600", 16532714232077090818ull},
- {"+0x1.000000000000180000000001p+600", 7309342195222315010ull},
- {"-0x1.000000000000180000000001p+600", 16532714232077090818ull},
- {"+0x1.0000000000001fffffffffffp+600", 7309342195222315010ull},
- {"-0x1.0000000000001fffffffffffp+600", 16532714232077090818ull},
- {"+0x1.000000000000200000000000p+600", 7309342195222315010ull},
- {"-0x1.000000000000200000000000p+600", 16532714232077090818ull},
- {"+0x1.000000000000200000000001p+600", 7309342195222315010ull},
- {"-0x1.000000000000200000000001p+600", 16532714232077090818ull},
- {"+0x1.00000000000027ffffffffffp+600", 7309342195222315010ull},
- {"-0x1.00000000000027ffffffffffp+600", 16532714232077090818ull},
- {"+0x1.000000000000280000000000p+600", 7309342195222315010ull},
- {"-0x1.000000000000280000000000p+600", 16532714232077090818ull},
- {"+0x1.000000000000280000000001p+600", 7309342195222315011ull},
- {"-0x1.000000000000280000000001p+600", 16532714232077090819ull},
- {"+0x2000000000000100000000000", 5044031582654955520ull},
- {"-0x2000000000000100000000000", 14267403619509731328ull},
- {"+0x2000000000000100000000001", 5044031582654955521ull},
- {"-0x2000000000000100000000001", 14267403619509731329ull},
- {"+0x20000000000001fffffffffff", 5044031582654955521ull},
- {"-0x20000000000001fffffffffff", 14267403619509731329ull},
- {"+0x2000000000000200000000000", 5044031582654955521ull},
- {"-0x2000000000000200000000000", 14267403619509731329ull},
- {"+0x2000000000000200000000001", 5044031582654955521ull},
- {"-0x2000000000000200000000001", 14267403619509731329ull},
- {"+0x20000000000002fffffffffff", 5044031582654955521ull},
- {"-0x20000000000002fffffffffff", 14267403619509731329ull},
- {"+0x2000000000000300000000000", 5044031582654955522ull},
- {"-0x2000000000000300000000000", 14267403619509731330ull},
- {"+0x2000000000000300000000001", 5044031582654955522ull},
- {"-0x2000000000000300000000001", 14267403619509731330ull},
- {"+0x20000000000003fffffffffff", 5044031582654955522ull},
- {"-0x20000000000003fffffffffff", 14267403619509731330ull},
- {"+0x2000000000000400000000000", 5044031582654955522ull},
- {"-0x2000000000000400000000000", 14267403619509731330ull},
- {"+0x2000000000000400000000001", 5044031582654955522ull},
- {"-0x2000000000000400000000001", 14267403619509731330ull},
- {"+0x20000000000004fffffffffff", 5044031582654955522ull},
- {"-0x20000000000004fffffffffff", 14267403619509731330ull},
- {"+0x2000000000000500000000000", 5044031582654955522ull},
- {"-0x2000000000000500000000000", 14267403619509731330ull},
- {"+0x2000000000000500000000001", 5044031582654955523ull},
- {"-0x2000000000000500000000001", 14267403619509731331ull},
- {"+0x0.000000000000080000000000p-1022", 0ull},
- {"-0x0.000000000000080000000000p-1022", 9223372036854775808ull},
- {"+0x0.000000000000080000000001p-1022", 1ull},
- {"-0x0.000000000000080000000001p-1022", 9223372036854775809ull},
- {"+0x0.0000000000000fffffffffffp-1022", 1ull},
- {"-0x0.0000000000000fffffffffffp-1022", 9223372036854775809ull},
- {"+0x0.000000000000100000000000p-1022", 1ull},
- {"-0x0.000000000000100000000000p-1022", 9223372036854775809ull},
- {"+0x0.000000000000100000000001p-1022", 1ull},
- {"-0x0.000000000000100000000001p-1022", 9223372036854775809ull},
- {"+0x0.00000000000017ffffffffffp-1022", 1ull},
- {"-0x0.00000000000017ffffffffffp-1022", 9223372036854775809ull},
- {"+0x0.000000000000180000000000p-1022", 2ull},
- {"-0x0.000000000000180000000000p-1022", 9223372036854775810ull},
- {"+0x0.000000000000180000000001p-1022", 2ull},
- {"-0x0.000000000000180000000001p-1022", 9223372036854775810ull},
- {"+0x0.0000000000001fffffffffffp-1022", 2ull},
- {"-0x0.0000000000001fffffffffffp-1022", 9223372036854775810ull},
- {"+0x0.000000000000200000000000p-1022", 2ull},
- {"-0x0.000000000000200000000000p-1022", 9223372036854775810ull},
- {"+0x0.000000000000200000000001p-1022", 2ull},
- {"-0x0.000000000000200000000001p-1022", 9223372036854775810ull},
- {"+0x0.00000000000027ffffffffffp-1022", 2ull},
- {"-0x0.00000000000027ffffffffffp-1022", 9223372036854775810ull},
- {"+0x0.000000000000280000000000p-1022", 2ull},
- {"-0x0.000000000000280000000000p-1022", 9223372036854775810ull},
- {"+0x1.000000000000280000000001p-1022", 4503599627370499ull},
- {"-0x1.000000000000280000000001p-1022", 9227875636482146307ull},
- {"+0x1.fffffffffffff4p1023", 9218868437227405311ull},
- {"-0x1.fffffffffffff4p1023", 18442240474082181119ull},
- {"+0x1.fffffffffffff7ffffffp1023", 9218868437227405311ull},
- {"-0x1.fffffffffffff7ffffffp1023", 18442240474082181119ull},
+ {"+0x1.000000000000080000000000p-600", 1905022642377719808ull},
+ {"-0x1.000000000000080000000000p-600", 11128394679232495616ull},
+ {"+0x1.000000000000080000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000080000000001p-600", 11128394679232495617ull},
+ {"+0x1.0000000000000fffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.0000000000000fffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000000p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000000p-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000001p-600", 11128394679232495617ull},
+ {"+0x1.00000000000017ffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.00000000000017ffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000180000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000180000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000001p-600", 11128394679232495618ull},
+ {"+0x1.0000000000001fffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.0000000000001fffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000001p-600", 11128394679232495618ull},
+ {"+0x1.00000000000027ffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.00000000000027ffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000280000000001p-600", 1905022642377719811ull},
+ {"-0x1.000000000000280000000001p-600", 11128394679232495619ull},
+ {"+0x8000000.000000400000000000p-627", 1905022642377719808ull},
+ {"-0x8000000.000000400000000000p-627", 11128394679232495616ull},
+ {"+0x8000000.000000400000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000400000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.0000007fffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.0000007fffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000000p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000000p-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.000000bfffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.000000bfffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000c00000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000000c00000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.000000ffffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.000000ffffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.0000013fffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.0000013fffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001400000000001p-627", 1905022642377719811ull},
+ {"-0x8000000.000001400000000001p-627", 11128394679232495619ull},
+ {"+0x1.000000000000080000000000p+600", 7309342195222315008ull},
+ {"-0x1.000000000000080000000000p+600", 16532714232077090816ull},
+ {"+0x1.000000000000080000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000080000000001p+600", 16532714232077090817ull},
+ {"+0x1.0000000000000fffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.0000000000000fffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000000p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000000p+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000001p+600", 16532714232077090817ull},
+ {"+0x1.00000000000017ffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.00000000000017ffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000180000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000180000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000001p+600", 16532714232077090818ull},
+ {"+0x1.0000000000001fffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.0000000000001fffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000001p+600", 16532714232077090818ull},
+ {"+0x1.00000000000027ffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.00000000000027ffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000280000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000001p+600", 7309342195222315011ull},
+ {"-0x1.000000000000280000000001p+600", 16532714232077090819ull},
+ {"+0x2000000000000100000000000", 5044031582654955520ull},
+ {"-0x2000000000000100000000000", 14267403619509731328ull},
+ {"+0x2000000000000100000000001", 5044031582654955521ull},
+ {"-0x2000000000000100000000001", 14267403619509731329ull},
+ {"+0x20000000000001fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000001fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000200000000000", 5044031582654955521ull},
+ {"-0x2000000000000200000000000", 14267403619509731329ull},
+ {"+0x2000000000000200000000001", 5044031582654955521ull},
+ {"-0x2000000000000200000000001", 14267403619509731329ull},
+ {"+0x20000000000002fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000002fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000300000000000", 5044031582654955522ull},
+ {"-0x2000000000000300000000000", 14267403619509731330ull},
+ {"+0x2000000000000300000000001", 5044031582654955522ull},
+ {"-0x2000000000000300000000001", 14267403619509731330ull},
+ {"+0x20000000000003fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000003fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000400000000000", 5044031582654955522ull},
+ {"-0x2000000000000400000000000", 14267403619509731330ull},
+ {"+0x2000000000000400000000001", 5044031582654955522ull},
+ {"-0x2000000000000400000000001", 14267403619509731330ull},
+ {"+0x20000000000004fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000004fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000500000000000", 5044031582654955522ull},
+ {"-0x2000000000000500000000000", 14267403619509731330ull},
+ {"+0x2000000000000500000000001", 5044031582654955523ull},
+ {"-0x2000000000000500000000001", 14267403619509731331ull},
+ {"+0x0.000000000000080000000000p-1022", 0ull},
+ {"-0x0.000000000000080000000000p-1022", 9223372036854775808ull},
+ {"+0x0.000000000000080000000001p-1022", 1ull},
+ {"-0x0.000000000000080000000001p-1022", 9223372036854775809ull},
+ {"+0x0.0000000000000fffffffffffp-1022", 1ull},
+ {"-0x0.0000000000000fffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000000p-1022", 1ull},
+ {"-0x0.000000000000100000000000p-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000001p-1022", 1ull},
+ {"-0x0.000000000000100000000001p-1022", 9223372036854775809ull},
+ {"+0x0.00000000000017ffffffffffp-1022", 1ull},
+ {"-0x0.00000000000017ffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000180000000000p-1022", 2ull},
+ {"-0x0.000000000000180000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000180000000001p-1022", 2ull},
+ {"-0x0.000000000000180000000001p-1022", 9223372036854775810ull},
+ {"+0x0.0000000000001fffffffffffp-1022", 2ull},
+ {"-0x0.0000000000001fffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000000p-1022", 2ull},
+ {"-0x0.000000000000200000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000001p-1022", 2ull},
+ {"-0x0.000000000000200000000001p-1022", 9223372036854775810ull},
+ {"+0x0.00000000000027ffffffffffp-1022", 2ull},
+ {"-0x0.00000000000027ffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000280000000000p-1022", 2ull},
+ {"-0x0.000000000000280000000000p-1022", 9223372036854775810ull},
+ {"+0x1.000000000000280000000001p-1022", 4503599627370499ull},
+ {"-0x1.000000000000280000000001p-1022", 9227875636482146307ull},
+ {"+0x1.fffffffffffff4p1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff4p1023", 18442240474082181119ull},
+ {"+0x1.fffffffffffff7ffffffp1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff7ffffffp1023", 18442240474082181119ull},
};
- for (auto test: kTests) {
+ for (auto test : kTests) {
AssertHexDoubleEquals(test.output, test.input);
}
}
EXPECT_EQ(error, "prog: unexpected argument 'stuff'" ERROR_ENDING);
}
-
TEST(OptionParser, OneArgument) {
std::string argument;
OptionParser parser("prog", "desc");
/* Tokens with no additional data (i.e. bare). */
WABT_TOKEN(Invalid, "Invalid")
WABT_TOKEN(Array, "array")
+WABT_TOKEN(AssertException, "assert_exception")
WABT_TOKEN(AssertExhaustion, "assert_exhaustion")
WABT_TOKEN(AssertInvalid, "assert_invalid")
WABT_TOKEN(AssertMalformed, "assert_malformed")
WABT_TOKEN(Offset, "offset")
WABT_TOKEN(Output, "output")
WABT_TOKEN(Param, "param")
+WABT_TOKEN(Ref, "ref")
WABT_TOKEN(Quote, "quote")
WABT_TOKEN(Register, "register")
WABT_TOKEN(Result, "result")
using namespace wabt::interp;
static int s_verbose;
-static const char* s_infile;
+static std::string s_infile;
static Thread::Options s_thread_options;
static Stream* s_trace_stream;
static Features s_features;
[]() { s_trace_stream = s_stdout_stream.get(); });
parser.AddArgument("filename", OptionParser::ArgumentCount::One,
- [](const char* argument) { s_infile = argument; });
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
parser.Parse(argc, argv);
}
struct ExpectedValue {
TypedValue value;
- Type lane_type; // Only valid if value.type == Type::V128.
+ Type lane_type; // Only valid if value.type == Type::V128.
// Up to 4 NaN values used, depending on |value.type| and |lane_type|:
// | type | lane_type | valid |
// | f32 | | nan[0] |
typedef AssertModuleCommand<CommandType::AssertUninstantiable>
AssertUninstantiableCommand;
+class AssertExceptionCommand
+ : public CommandMixin<CommandType::AssertException> {
+ public:
+ Action action;
+};
+
// An extremely simple JSON parser that only knows how to parse the expected
// format from wat2wasm.
class JSONParser {
}
default:
- PrintError("unknown concrete type: \"%s\"", lane_type.GetName());
+ PrintError("unknown concrete type: \"%s\"", lane_type.GetName().c_str());
return wabt::Result::Error;
}
break;
default:
- PrintError("unknown concrete type: \"%s\"", type.GetName());
+ PrintError("unknown concrete type: \"%s\"", type.GetName().c_str());
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
-wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) {
+wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types,
+ Values* out_values) {
out_values->clear();
EXPECT("[");
bool first = true;
EXPECT(",");
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
+ } else if (Match("\"assert_exception\"")) {
+ if (!s_features.exceptions_enabled()) {
+ PrintError("invalid command: exceptions not allowed");
+ return wabt::Result::Error;
+ }
+ auto command = MakeUnique<AssertExceptionCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *out_command = std::move(command);
} else {
PrintError("unknown command type");
return wabt::Result::Error;
wabt::Result OnAssertReturnCommand(const AssertReturnCommand*);
wabt::Result OnAssertTrapCommand(const AssertTrapCommand*);
wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*);
+ wabt::Result OnAssertExceptionCommand(const AssertExceptionCommand*);
wabt::Result CheckAssertReturnResult(const AssertReturnCommand* command,
int index,
wabt::Result ReadInvalidTextModule(string_view module_filename,
const std::string& header);
wabt::Result ReadInvalidModule(int line_number,
- string_view module_filename,
- ModuleType module_type,
- const char* desc);
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
wabt::Result ReadUnlinkableModule(int line_number,
- string_view module_filename,
- ModuleType module_type,
- const char* desc);
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
Store store_;
- Registry registry_; // Used when importing.
+ Registry registry_; // Used when importing.
Registry instances_; // Used when referencing module by name in invoke.
ExportMap last_instance_;
int passed_ = 0;
for (auto&& print : print_funcs) {
auto import_name = StringPrintf("spectest.%s", print.name);
- spectest[print.name] = HostFunc::New(
- store_, print.type,
- [=](Thread& inst, const Values& params, Values& results,
- Trap::Ptr* trap) -> wabt::Result {
- printf("called host ");
- WriteCall(s_stdout_stream.get(), import_name, print.type, params,
- results, *trap);
- return wabt::Result::Ok;
- });
+ spectest[print.name] =
+ HostFunc::New(store_, print.type,
+ [=](Thread& inst, const Values& params, Values& results,
+ Trap::Ptr* trap) -> wabt::Result {
+ printf("called host ");
+ WriteCall(s_stdout_stream.get(), import_name,
+ print.type, params, results, *trap);
+ return wabt::Result::Ok;
+ });
}
spectest["table"] =
spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}});
- spectest["global_i32"] = interp::Global::New(
- store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666}));
- spectest["global_i64"] = interp::Global::New(
- store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666}));
- spectest["global_f32"] = interp::Global::New(
- store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666}));
- spectest["global_f64"] = interp::Global::New(
- store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666}));
+ spectest["global_i32"] =
+ interp::Global::New(store_, GlobalType{ValueType::I32, Mutability::Const},
+ Value::Make(u32{666}));
+ spectest["global_i64"] =
+ interp::Global::New(store_, GlobalType{ValueType::I64, Mutability::Const},
+ Value::Make(u64{666}));
+ spectest["global_f32"] =
+ interp::Global::New(store_, GlobalType{ValueType::F32, Mutability::Const},
+ Value::Make(f32{666}));
+ spectest["global_f64"] =
+ interp::Global::New(store_, GlobalType{ValueType::F64, Mutability::Const},
+ Value::Make(f64{666}));
}
wabt::Result CommandRunner::Run(const Script& script) {
TallyCommand(OnAssertExhaustionCommand(
cast<AssertExhaustionCommand>(command.get())));
break;
+
+ case CommandType::AssertException:
+ TallyCommand(OnAssertExceptionCommand(
+ cast<AssertExceptionCommand>(command.get())));
+ break;
}
}
}
wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename,
- const std::string& header) {
+ const std::string& header) {
std::vector<uint8_t> file_data;
wabt::Result result = ReadFile(module_filename, &file_data);
std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
}
interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename,
- Errors* errors) {
+ Errors* errors) {
std::vector<uint8_t> file_data;
if (Failed(ReadFile(module_filename, &file_data))) {
ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
kStopOnFirstError, kFailOnCustomSectionError);
ModuleDesc module_desc;
- if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options,
- errors, &module_desc))) {
+ if (Failed(ReadBinaryInterp(module_filename, file_data.data(),
+ file_data.size(), options, errors,
+ &module_desc))) {
return {};
}
}
wabt::Result CommandRunner::ReadInvalidModule(int line_number,
- string_view module_filename,
- ModuleType module_type,
- const char* desc) {
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc) {
std::string header = StringPrintf(
"%s:%d: %s passed", source_filename_.c_str(), line_number, desc);
wabt::Result CommandRunner::OnAssertMalformedCommand(
const AssertMalformedCommand* command) {
wabt::Result result = ReadInvalidModule(command->line, command->filename,
- command->type, "assert_malformed");
+ command->type, "assert_malformed");
if (Succeeded(result)) {
PrintError(command->line, "expected module to be malformed: \"%s\"",
command->filename.c_str());
wabt::Result CommandRunner::OnAssertInvalidCommand(
const AssertInvalidCommand* command) {
wabt::Result result = ReadInvalidModule(command->line, command->filename,
- command->type, "assert_invalid");
+ command->type, "assert_invalid");
if (Succeeded(result)) {
PrintError(command->line, "expected module to be invalid: \"%s\"",
command->filename.c_str());
return TypedValueToString(ev.value);
case ExpectedNan::Arithmetic:
- return StringPrintf("%s:nan:arithmetic", ev.value.type.GetName());
+ return StringPrintf("%s:nan:arithmetic",
+ ev.value.type.GetName().c_str());
case ExpectedNan::Canonical:
- return StringPrintf("%s:nan:canonical", ev.value.type.GetName());
+ return StringPrintf("%s:nan:canonical",
+ ev.value.type.GetName().c_str());
}
break;
return wabt::Result::Ok;
}
+wabt::Result CommandRunner::OnAssertExceptionCommand(
+ const AssertExceptionCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+ if (!result.trap || result.trap->message() != "uncaught exception") {
+ PrintError(command->line, "expected an exception to be thrown");
+ return wabt::Result::Error;
+ }
+ PrintError(command->line, "assert_exception passed");
+
+ return wabt::Result::Ok;
+}
+
void CommandRunner::TallyCommand(wabt::Result result) {
if (Succeeded(result)) {
passed_++;
#include <cstdlib>
#include "src/apply-names.h"
-#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
+#include "src/decompiler.h"
#include "src/error-formatter.h"
#include "src/feature.h"
#include "src/generate-names.h"
#include "src/stream.h"
#include "src/validator.h"
#include "src/wast-lexer.h"
-#include "src/decompiler.h"
using namespace wabt;
{
const char s_description[] =
- " Read a file in the WebAssembly binary format, and convert it to\n"
- " a decompiled text file.\n"
- "\n"
- "examples:\n"
- " # parse binary file test.wasm and write text file test.dcmp\n"
- " $ wasm-decompile test.wasm -o test.dcmp\n";
+ " Read a file in the WebAssembly binary format, and convert it to\n"
+ " a decompiled text file.\n"
+ "\n"
+ "examples:\n"
+ " # parse binary file test.wasm and write text file test.dcmp\n"
+ " $ wasm-decompile test.wasm -o test.dcmp\n";
OptionParser parser("wasm-decompile", s_description);
parser.AddOption(
'o', "output", "FILENAME",
Errors errors;
Module module;
const bool kStopOnFirstError = true;
- ReadBinaryOptions options(features, nullptr,
- true, kStopOnFirstError,
+ ReadBinaryOptions options(features, nullptr, true, kStopOnFirstError,
fail_on_custom_section_error);
result = ReadBinaryIr(infile.c_str(), file_data.data(), file_data.size(),
options, &errors, &module);
ValidateOptions options(features);
result = ValidateModule(&module, &errors, options);
if (Succeeded(result)) {
- result = GenerateNames(&module,
- static_cast<NameOpts>(NameOpts::AlphaNames));
+ result =
+ GenerateNames(&module, static_cast<NameOpts>(NameOpts::AlphaNames));
}
if (Succeeded(result)) {
// Must be called after ReadBinaryIr & GenerateNames, and before
if (Succeeded(result)) {
auto s = Decompile(module, decompile_options);
FileStream stream(!outfile.empty() ? FileStream(outfile)
- : FileStream(stdout));
+ : FileStream(stdout));
stream.WriteData(s.data(), s.size());
}
}
auto import_name = StringPrintf("%s.%s", import.type.module.c_str(),
import.type.name.c_str());
- auto host_func =
- HostFunc::New(s_store, func_type,
- [=](Thread& thread, const Values& params,
- Values& results, Trap::Ptr* trap) -> Result {
- printf("called host ");
- WriteCall(stream, import_name, func_type, params,
- results, *trap);
- return Result::Ok;
- });
+ auto host_func = HostFunc::New(
+ s_store, func_type,
+ [=](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* trap) -> Result {
+ printf("called host ");
+ WriteCall(stream, import_name, func_type, params, results, *trap);
+ return Result::Ok;
+ });
imports.push_back(host_func.ref());
continue;
}
const bool kFailOnCustomSectionError = true;
ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
kStopOnFirstError, kFailOnCustomSectionError);
- CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options,
- errors, &module_desc));
+ CHECK_RESULT(ReadBinaryInterp(module_filename, file_data.data(),
+ file_data.size(), options, errors,
+ &module_desc));
if (s_verbose) {
module_desc.istream.Disassemble(stream);
} else {
BindImports(module, imports);
}
- BindImports(module, imports);
Instance::Ptr instance;
CHECK_RESULT(InstantiateModule(imports, module, &instance));
s_stderr_stream = FileStream::CreateStderr();
ParseOptions(argc, argv);
+ s_store.setFeatures(s_features);
wabt::Result result = ReadAndRunModule(s_infile);
return result != wabt::Result::Ok;
#include <cstdlib>
#include <cstring>
+#include "src/binary-reader-objdump.h"
+#include "src/binary-reader.h"
#include "src/common.h"
#include "src/option-parser.h"
#include "src/stream.h"
-#include "src/binary-reader.h"
-#include "src/binary-reader-objdump.h"
using namespace wabt;
static const char s_description[] =
-R"( Print information about the contents of wasm binaries.
+ R"( Print information about the contents of wasm binaries.
examples:
$ wasm-objdump test.wasm
return 1;
}
- for (const char* filename: s_infiles) {
+ for (const char* filename : s_infiles) {
if (Failed(dump_file(filename))) {
return 1;
}
#include <map>
#include <vector>
-#include "src/binary-reader.h"
#include "src/binary-reader-opcnt.h"
+#include "src/binary-reader.h"
#include "src/option-parser.h"
#include "src/stream.h"
static Features s_features;
static const char s_description[] =
-R"( Read a file in the wasm binary format, and count opcode usage for
+ R"( Read a file in the wasm binary format, and count opcode usage for
instructions.
examples:
template <typename T>
struct WithinCutoff {
- bool operator()(const T& pair) const {
- return pair.second >= s_cutoff;
- }
+ bool operator()(const T& pair) const { return pair.second >= s_cutoff; }
};
static size_t SumCounts(const OpcodeInfoCounts& info_counts) {
typedef std::pair<Opcode, size_t> OpcodeCountPair;
std::map<Opcode, size_t> counts;
- for (auto& info_count_pair: info_counts) {
+ for (auto& info_count_pair : info_counts) {
Opcode opcode = info_count_pair.first.opcode();
size_t count = info_count_pair.second;
counts[opcode] += count;
}
}
-void WriteCountsWithImmediates(Stream& stream,
- const OpcodeInfoCounts& counts) {
+void WriteCountsWithImmediates(Stream& stream, const OpcodeInfoCounts& counts) {
// Remove const from the key type so we can sort below.
typedef std::pair<std::remove_const<OpcodeInfoCounts::key_type>::type,
OpcodeInfoCounts::mapped_type>
* limitations under the License.
*/
-#include "src/binary.h"
-#include "src/binary-reader.h"
#include "src/binary-reader-nop.h"
+#include "src/binary-reader.h"
+#include "src/binary.h"
#include "src/error-formatter.h"
#include "src/leb128.h"
#include "src/option-parser.h"
static std::string s_filename;
static const char s_description[] =
-R"( Remove sections of a WebAssembly binary file.
+ R"( Remove sections of a WebAssembly binary file.
examples:
# Remove all custom sections from test.wasm
class BinaryReaderStrip : public BinaryReaderNop {
public:
- explicit BinaryReaderStrip(Errors* errors)
- : errors_(errors) {
+ explicit BinaryReaderStrip(Errors* errors) : errors_(errors) {
stream_.WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
stream_.WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
}
if (Succeeded(result)) {
Errors errors;
Features features;
+ features.EnableAll();
const bool kReadDebugNames = false;
const bool kStopOnFirstError = true;
const bool kFailOnCustomSectionError = false;
#include <cstdio>
#include <cstdlib>
-#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
#include "src/error-formatter.h"
#include "src/ir.h"
#include "src/option-parser.h"
static std::unique_ptr<FileStream> s_log_stream;
static const char s_description[] =
-R"( Read a file in the WebAssembly binary format, and validate it.
+ R"( Read a file in the WebAssembly binary format, and validate it.
examples:
# validate binary file test.wasm
#include <cstdlib>
#include "src/apply-names.h"
-#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
#include "src/error-formatter.h"
#include "src/feature.h"
+#include "src/filenames.h"
#include "src/generate-names.h"
#include "src/ir.h"
#include "src/option-parser.h"
static std::unique_ptr<FileStream> s_log_stream;
static const char s_description[] =
-R"( Read a file in the WebAssembly binary format, and convert it to
+ R"( Read a file in the WebAssembly binary format, and convert it to
a C source file and header.
examples:
#undef WABT_FEATURE
if (any_non_default_feature) {
- fprintf(stderr, "wasm2c currently support only default feature flags.\n");
+ fprintf(stderr,
+ "wasm2c currently only supports a fixed set of features.\n");
exit(1);
}
+ s_features.disable_bulk_memory();
}
// TODO(binji): copied from binary-writer-spec.cc, probably should share.
if (Succeeded(result)) {
if (!s_outfile.empty()) {
- std::string header_name =
+ std::string header_name_full =
strip_extension(s_outfile).to_string() + ".h";
FileStream c_stream(s_outfile.c_str());
- FileStream h_stream(header_name);
- result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module,
- s_write_c_options);
+ FileStream h_stream(header_name_full);
+ string_view header_name = GetBasename(header_name_full);
+ result = WriteC(&c_stream, &h_stream, header_name.to_string().c_str(),
+ &module, s_write_c_options);
} else {
FileStream stream(stdout);
result =
return ProgramMain(argc, argv);
WABT_CATCH_BAD_ALLOC_AND_EXIT
}
-
#include <cstdlib>
#include "src/apply-names.h"
-#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
#include "src/error-formatter.h"
#include "src/feature.h"
#include "src/generate-names.h"
static bool s_validate = true;
static const char s_description[] =
-R"( Read a file in the WebAssembly binary format, and convert it to
+ R"( Read a file in the WebAssembly binary format, and convert it to
the WebAssembly text format.
examples:
#include <cassert>
#include <cstdarg>
#include <cstdint>
-#include <cstdlib>
#include <cstdio>
+#include <cstdlib>
#include <string>
#include "config.h"
-#include "src/binary-writer.h"
#include "src/binary-writer-spec.h"
+#include "src/binary-writer.h"
#include "src/common.h"
#include "src/error-formatter.h"
#include "src/feature.h"
static std::unique_ptr<FileStream> s_log_stream;
static const char s_description[] =
-R"( read a file in the wasm spec test format, check it for errors, and
+ R"( read a file in the wasm spec test format, check it for errors, and
convert it to a JSON file and associated wasm binary files.
examples:
static Features s_features;
static const char s_description[] =
-R"( read a file in the wasm s-expression format and format it.
+ R"( read a file in the wasm s-expression format and format it.
examples:
# write output to stdout
#include <cassert>
#include <cstdarg>
#include <cstdint>
-#include <cstdlib>
#include <cstdio>
+#include <cstdlib>
#include <string>
#include "config.h"
static std::unique_ptr<FileStream> s_log_stream;
static const char s_description[] =
-R"( read a file in the wasm text format, check it for errors, and
+ R"( read a file in the wasm text format, check it for errors, and
convert it to the wasm binary format.
examples:
return Result::Error;
}
+Result TypeChecker::GetCatchCount(Index depth, Index* out_count) {
+ Label* unused;
+ if (Failed(GetLabel(depth, &unused))) {
+ return Result::Error;
+ }
+
+ Index catch_count = 0;
+ for (Index idx = 0; idx <= depth; idx++) {
+ LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type;
+ if (type == LabelType::Catch) {
+ catch_count++;
+ }
+ }
+ *out_count = catch_count;
+
+ return Result::Ok;
+}
+
Result TypeChecker::TopLabel(Label** out_label) {
return GetLabel(0, out_label);
}
Result TypeChecker::Check2LabelTypes(Label* label,
LabelType label_type1,
LabelType label_type2) {
- return label->label_type == label_type1 ||
- label->label_type == label_type2 ? Result::Ok : Result::Error;
+ return label->label_type == label_type1 || label->label_type == label_type2
+ ? Result::Ok
+ : Result::Error;
}
Result TypeChecker::GetThisFunctionLabel(Label** label) {
Result result = (type_stack_.size() == label->type_stack_limit)
? Result::Ok
: Result::Error;
- PrintStackIfFailed(result, desc);
+ PrintStackIfFailedV(result, desc, {}, /*is_end=*/true);
return result;
}
if (expected == Type::Any || actual == Type::Any) {
return Result::Ok;
}
+
+ if (expected == Type::Reference && actual == Type::Reference) {
+ return expected.GetReferenceIndex() == actual.GetReferenceIndex()
+ ? Result::Ok
+ : Result::Error;
+ }
if (actual != expected) {
return Result::Error;
}
return result;
}
-void TypeChecker::PrintStackIfFailed(Result result,
- const char* desc,
- const TypeVector& expected) {
+void TypeChecker::PrintStackIfFailedV(Result result,
+ const char* desc,
+ const TypeVector& expected,
+ bool is_end) {
if (Succeeded(result)) {
return;
}
}
std::string message = "type mismatch in ";
+ if (is_end) {
+ message = "type mismatch at end of ";
+ }
message += desc;
message += ", expected ";
message += TypesToString(expected);
return result;
}
-Result TypeChecker::OnFuncRef(Index* out_index) {
+Result TypeChecker::OnIndexedFuncRef(Index* out_index) {
Type type;
Result result = PeekType(0, &type);
- if (!type.IsIndex()) {
+ if (!(type == Type::Any || type.IsReferenceWithIndex())) {
TypeVector actual;
if (Succeeded(result)) {
actual.push_back(type);
result = Result::Error;
}
if (Succeeded(result)) {
- *out_index = type.GetIndex();
+ *out_index = type.GetReferenceIndex();
}
result |= DropTypes(1);
return result;
// Delegate starts counting after the current try, as the delegate
// instruction is not actually in the try block.
CHECK_RESULT(GetLabel(depth + 1, &label));
- if (Failed(Check2LabelTypes(label, LabelType::Try, LabelType::Func))) {
- PrintError("try-delegate must target a try block or function label");
- result = Result::Error;
- }
Label* try_label;
CHECK_RESULT(TopLabel(&try_label));
Label* label;
CHECK_RESULT(TopLabel(&label));
result |= CheckLabelType(label, LabelType::If);
- result |= PopAndCheckSignature(label->result_types, "if true branch");
- result |= CheckTypeStackEnd("if true branch");
+ result |= PopAndCheckSignature(label->result_types, "`if true` branch");
+ result |= CheckTypeStackEnd("`if true` branch");
ResetTypeStackToLabel(label);
PushTypes(label->param_types);
label->label_type = LabelType::Else;
Result TypeChecker::OnEnd() {
Result result = Result::Ok;
static const char* s_label_type_name[] = {
- "function", "block", "loop", "if", "if false branch", "try", "try catch"};
+ "function", "initializer expression", "block", "loop",
+ "if", "`if false` branch", "try", "try catch"};
WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name) == kLabelTypeCount);
Label* label;
CHECK_RESULT(TopLabel(&label));
return PopAndCheck3Types(Type::I32, elem_type, Type::I32, "table.fill");
}
-Result TypeChecker::OnRefFuncExpr(Index func_index) {
+Result TypeChecker::OnRefFuncExpr(Index func_type) {
if (features_.function_references_enabled()) {
- PushType(Type(func_index));
+ PushType(Type(Type::Reference, func_type));
} else {
PushType(Type::FuncRef);
}
Result TypeChecker::OnRefIsNullExpr() {
Type type;
Result result = PeekType(0, &type);
- if (!type.IsRef()) {
+ if (!(type == Type::Any || type.IsRef())) {
TypeVector actual;
if (Succeeded(result)) {
actual.push_back(type);
return result;
}
-Result TypeChecker::OnSimdLoadLane(Opcode opcode, const Limits& limits, uint64_t lane_idx) {
+Result TypeChecker::OnSimdLoadLane(Opcode opcode,
+ const Limits& limits,
+ uint64_t lane_idx) {
Result result = Result::Ok;
uint32_t lane_count = opcode.GetSimdLaneCount();
if (lane_idx >= lane_count) {
return result;
}
-Result TypeChecker::OnSimdStoreLane(Opcode opcode, const Limits& limits, uint64_t lane_idx) {
+Result TypeChecker::OnSimdStoreLane(Opcode opcode,
+ const Limits& limits,
+ uint64_t lane_idx) {
Result result = Result::Ok;
uint32_t lane_count = opcode.GetSimdLaneCount();
if (lane_idx >= lane_count) {
return result;
}
+Result TypeChecker::BeginInitExpr(Type type) {
+ type_stack_.clear();
+ label_stack_.clear();
+ PushLabel(LabelType::InitExpr, TypeVector(), {type});
+ return Result::Ok;
+}
+
+Result TypeChecker::EndInitExpr() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::InitExpr);
+ result |= OnEnd(label, "initializer expression", "initializer expression");
+ return result;
+}
+
} // namespace wabt
bool IsUnreachable();
Result GetLabel(Index depth, Label** out_label);
Result GetRethrowLabel(Index depth, Label** out_label);
+ Result GetCatchCount(Index depth, Index* out_depth);
Result BeginFunction(const TypeVector& sig);
Result OnAtomicFence(uint32_t consistency_model);
Result OnCall(const TypeVector& param_types, const TypeVector& result_types);
Result OnCallIndirect(const TypeVector& param_types,
const TypeVector& result_types);
- Result OnFuncRef(Index* out_index);
- Result OnReturnCall(const TypeVector& param_types, const TypeVector& result_types);
- Result OnReturnCallIndirect(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnIndexedFuncRef(Index* out_index);
+ Result OnReturnCall(const TypeVector& param_types,
+ const TypeVector& result_types);
+ Result OnReturnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types);
Result OnCatch(const TypeVector& sig);
Result OnCompare(Opcode);
Result OnConst(Type);
Result OnTableGrow(Type elem_type);
Result OnTableSize();
Result OnTableFill(Type elem_type);
- Result OnRefFuncExpr(Index func_index);
+ Result OnRefFuncExpr(Index func_type);
Result OnRefNullExpr(Type type);
Result OnRefIsNullExpr();
Result OnRethrow(Index depth);
Result OnUnreachable();
Result EndFunction();
+ Result BeginInitExpr(Type type);
+ Result EndInitExpr();
+
static Result CheckType(Type actual, Type expected);
private:
const TypeVector& result_types);
Result PopLabel();
Result CheckLabelType(Label* label, LabelType label_type);
- Result Check2LabelTypes(Label* label, LabelType label_type1, LabelType label_type2);
- Result GetThisFunctionLabel(Label **label);
+ Result Check2LabelTypes(Label* label,
+ LabelType label_type1,
+ LabelType label_type2);
+ Result GetThisFunctionLabel(Label** label);
Result PeekType(Index depth, Type* out_type);
Result PeekAndCheckType(Index depth, Type expected);
Result DropTypes(size_t drop_count);
void PushType(Type type);
void PushTypes(const TypeVector& types);
Result CheckTypeStackEnd(const char* desc);
- Result CheckTypes(const TypeVector &actual, const TypeVector &expected);
+ Result CheckTypes(const TypeVector& actual, const TypeVector& expected);
Result CheckSignature(const TypeVector& sig, const char* desc);
- Result CheckReturnSignature(const TypeVector& sig, const TypeVector &expected,const char *desc);
+ Result CheckReturnSignature(const TypeVector& sig,
+ const TypeVector& expected,
+ const char* desc);
Result PopAndCheckSignature(const TypeVector& sig, const char* desc);
Result PopAndCheckCall(const TypeVector& param_types,
const TypeVector& result_types,
// Minor optimization, check result before constructing the vector to pass
// to the other overload of PrintStackIfFailed.
if (Failed(result)) {
- PrintStackIfFailed(result, desc, {args...});
+ PrintStackIfFailedV(result, desc, {args...}, /*is_end=*/false);
}
}
- void PrintStackIfFailed(Result, const char* desc, const TypeVector&);
+ void PrintStackIfFailedV(Result,
+ const char* desc,
+ const TypeVector&,
+ bool is_end);
ErrorCallback error_callback_;
TypeVector type_stack_;
#include <vector>
#include "config.h"
+#include "src/base-types.h"
+#include "src/string-format.h"
namespace wabt {
class Type;
-using Index = uint32_t;
using TypeVector = std::vector<Type>;
class Type {
I16 = -0x07, // 0x79 : packed-type only, used in gc and as v128 lane
FuncRef = -0x10, // 0x70
ExternRef = -0x11, // 0x6f
+ Reference = -0x15, // 0x6b
Func = -0x20, // 0x60
Struct = -0x21, // 0x5f
Array = -0x22, // 0x5e
Void = -0x40, // 0x40
___ = Void, // Convenient for the opcode table in opcode.h
- Any = 0, // Not actually specified, but useful for type-checking
+ Any = 0, // Not actually specified, but useful for type-checking
I8U = 4, // Not actually specified, but used internally with load/store
I16U = 6, // Not actually specified, but used internally with load/store
I32U = 7, // Not actually specified, but used internally with load/store
};
Type() = default; // Provided so Type can be member of a union.
- Type(int32_t code) : enum_(static_cast<Enum>(code)) {}
- Type(Enum e) : enum_(e) {}
+ Type(int32_t code)
+ : enum_(static_cast<Enum>(code)), type_index_(kInvalidIndex) {}
+ Type(Enum e) : enum_(e), type_index_(kInvalidIndex) {}
+ Type(Enum e, Index type_index) : enum_(e), type_index_(type_index) {
+ assert(e == Enum::Reference);
+ }
operator Enum() const { return enum_; }
bool IsRef() const {
- return enum_ == Type::ExternRef || enum_ == Type::FuncRef;
+ return enum_ == Type::ExternRef || enum_ == Type::FuncRef ||
+ enum_ == Type::Reference;
}
+ bool IsReferenceWithIndex() const { return enum_ == Type::Reference; }
+
bool IsNullableRef() const {
// Currently all reftypes are nullable
return IsRef();
}
- const char* GetName() const {
+ std::string GetName() const {
switch (enum_) {
case Type::I32: return "i32";
case Type::I64: return "i64";
case Type::Void: return "void";
case Type::Any: return "any";
case Type::ExternRef: return "externref";
- default: return "<type_index>";
+ case Type::Reference:
+ return StringPrintf("(ref %d)", type_index_);
+ default:
+ return StringPrintf("<type_index[%d]>", enum_);
}
}
// (type $T (func (result i32 i64)))
// ...
// (block (type $T) ...)
- //
+ //
bool IsIndex() const { return static_cast<int32_t>(enum_) >= 0; }
Index GetIndex() const {
return static_cast<Index>(enum_);
}
+ Index GetReferenceIndex() const {
+ assert(enum_ == Enum::Reference);
+ return type_index_;
+ }
+
TypeVector GetInlineVector() const {
assert(!IsIndex());
switch (enum_) {
case Type::V128:
case Type::FuncRef:
case Type::ExternRef:
+ case Type::Reference:
return TypeVector(this, this + 1);
default:
private:
Enum enum_;
+ Index type_index_; // Only used for for Type::Reference
};
} // namespace wabt
namespace {
+// clang-format off
const int s_utf8_length[256] = {
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
};
+// clang-format on
// Returns true if this is a valid continuation byte.
bool IsCont(uint8_t c) {
if (Failed(TypeChecker::CheckType(actual, expected))) {
PrintError(loc,
"type mismatch for %s %" PRIindex " of %s. got %s, expected %s",
- index_kind, index, desc, actual.GetName(), expected.GetName());
+ index_kind, index, desc, actual.GetName().c_str(),
+ expected.GetName().c_str());
}
}
}
Result Validator::OnLoadExpr(LoadExpr* expr) {
- result_ |= validator_.OnLoad(expr->loc, expr->opcode,
+ result_ |= validator_.OnLoad(expr->loc, expr->opcode, expr->memidx,
expr->opcode.GetAlignment(expr->align));
return Result::Ok;
}
}
Result Validator::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
- result_ |= validator_.OnMemoryCopy(expr->loc);
+ result_ |=
+ validator_.OnMemoryCopy(expr->loc, expr->srcmemidx, expr->destmemidx);
return Result::Ok;
}
}
Result Validator::OnMemoryFillExpr(MemoryFillExpr* expr) {
- result_ |= validator_.OnMemoryFill(expr->loc);
+ result_ |= validator_.OnMemoryFill(expr->loc, expr->memidx);
return Result::Ok;
}
Result Validator::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
- result_ |= validator_.OnMemoryGrow(expr->loc);
+ result_ |= validator_.OnMemoryGrow(expr->loc, expr->memidx);
return Result::Ok;
}
Result Validator::OnMemoryInitExpr(MemoryInitExpr* expr) {
- result_ |= validator_.OnMemoryInit(expr->loc, expr->var);
+ result_ |= validator_.OnMemoryInit(expr->loc, expr->var, expr->memidx);
return Result::Ok;
}
Result Validator::OnMemorySizeExpr(MemorySizeExpr* expr) {
- result_ |= validator_.OnMemorySize(expr->loc);
+ result_ |= validator_.OnMemorySize(expr->loc, expr->memidx);
return Result::Ok;
}
}
Result Validator::OnStoreExpr(StoreExpr* expr) {
- result_ |= validator_.OnStore(expr->loc, expr->opcode,
+ result_ |= validator_.OnStore(expr->loc, expr->opcode, expr->memidx,
expr->opcode.GetAlignment(expr->align));
return Result::Ok;
}
}
Result Validator::OnCatchExpr(TryExpr*, Catch* catch_) {
- result_ |= validator_.OnCatch(catch_->loc, catch_->var,
- catch_->IsCatchAll());
+ result_ |= validator_.OnCatch(catch_->loc, catch_->var, catch_->IsCatchAll());
return Result::Ok;
}
}
Result Validator::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
- result_ |= validator_.OnSimdLoadLane(
- expr->loc, expr->opcode, expr->opcode.GetAlignment(expr->align),
- expr->val);
+ result_ |= validator_.OnSimdLoadLane(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align),
+ expr->val);
return Result::Ok;
}
switch (f->type->kind()) {
case TypeEntryKind::Func: {
FuncType* func_type = cast<FuncType>(f->type.get());
- result_ |= validator_.OnFuncType(field.loc,
- func_type->sig.param_types.size(),
- func_type->sig.param_types.data(),
- func_type->sig.result_types.size(),
- func_type->sig.result_types.data());
+ result_ |= validator_.OnFuncType(
+ field.loc, func_type->sig.param_types.size(),
+ func_type->sig.param_types.data(),
+ func_type->sig.result_types.size(),
+ func_type->sig.result_types.data(),
+ module->GetFuncTypeIndex(func_type->sig));
break;
}
result_ |=
validator_.OnGlobal(field.loc, f->global.type, f->global.mutable_);
- if (f->global.init_expr.size() == 1) {
- const Expr* expr = &f->global.init_expr.front();
-
- switch (expr->type()) {
- case ExprType::Const:
- result_ |= validator_.OnGlobalInitExpr_Const(
- expr->loc, cast<ConstExpr>(expr)->const_.type());
- break;
-
- case ExprType::GlobalGet: {
- Var var = cast<GlobalGetExpr>(expr)->var;
- result_ |= validator_.OnGlobalInitExpr_GlobalGet(expr->loc, var);
- break;
- }
-
- case ExprType::RefFunc:
- result_ |= validator_.OnGlobalInitExpr_RefFunc(
- expr->loc, cast<RefFuncExpr>(expr)->var);
- break;
-
- case ExprType::RefNull:
- result_ |= validator_.OnGlobalInitExpr_RefNull(
- expr->loc, cast<RefNullExpr>(expr)->type);
- break;
-
- default:
- result_ |= validator_.OnGlobalInitExpr_Other(field.loc);
- break;
- }
- } else {
- result_ |= validator_.OnGlobalInitExpr_Other(field.loc);
- }
+ // Init expr.
+ result_ |= validator_.BeginInitExpr(field.loc, f->global.type);
+ ExprVisitor visitor(this);
+ result_ |=
+ visitor.VisitExprList(const_cast<ExprList&>(f->global.init_expr));
+ result_ |= validator_.EndInitExpr();
}
}
validator_.OnElemSegmentElemType(f->elem_segment.elem_type);
// Init expr.
- if (f->elem_segment.offset.size() == 1) {
- const Expr* expr = &f->elem_segment.offset.front();
-
- switch (expr->type()) {
- case ExprType::Const:
- result_ |= validator_.OnElemSegmentInitExpr_Const(
- expr->loc, cast<ConstExpr>(expr)->const_.type());
- break;
-
- case ExprType::GlobalGet: {
- Var var = cast<GlobalGetExpr>(expr)->var;
- result_ |=
- validator_.OnElemSegmentInitExpr_GlobalGet(expr->loc, var);
- break;
- }
-
- default:
- result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc);
- break;
- }
- } else if (f->elem_segment.offset.size() > 1) {
- result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc);
+ if (f->elem_segment.offset.size()) {
+ result_ |= validator_.BeginInitExpr(field.loc, Type::I32);
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(
+ const_cast<ExprList&>(f->elem_segment.offset));
+ result_ |= validator_.EndInitExpr();
}
// Element expr.
for (auto&& elem_expr : f->elem_segment.elem_exprs) {
- switch (elem_expr.kind) {
- case ElemExprKind::RefNull:
- // TODO: better location?
- result_ |= validator_.OnElemSegmentElemExpr_RefNull(field.loc,
- elem_expr.type);
- break;
-
- case ElemExprKind::RefFunc:
- result_ |= validator_.OnElemSegmentElemExpr_RefFunc(
- elem_expr.var.loc, elem_expr.var);
- break;
+ if (elem_expr.size() == 1) {
+ const Expr* expr = &elem_expr.front();
+ switch (expr->type()) {
+ case ExprType::RefNull:
+ result_ |= validator_.OnElemSegmentElemExpr_RefNull(
+ expr->loc, cast<RefNullExpr>(expr)->type);
+ break;
+ case ExprType::RefFunc:
+ result_ |= validator_.OnElemSegmentElemExpr_RefFunc(
+ expr->loc, cast<RefFuncExpr>(expr)->var);
+ break;
+ default:
+ result_ |= validator_.OnElemSegmentElemExpr_Other(expr->loc);
+ break;
+ }
+ } else if (elem_expr.size() > 1) {
+ result_ |= validator_.OnElemSegmentElemExpr_Other(field.loc);
}
}
}
// Data segment section.
for (const ModuleField& field : module->fields) {
if (auto* f = dyn_cast<DataSegmentModuleField>(&field)) {
- result_ |= validator_.OnDataSegment(
- field.loc, f->data_segment.memory_var, f->data_segment.kind);
+ result_ |= validator_.OnDataSegment(field.loc, f->data_segment.memory_var,
+ f->data_segment.kind);
// Init expr.
- if (f->data_segment.offset.size() == 1) {
- const Expr* expr = &f->data_segment.offset.front();
-
- switch (expr->type()) {
- case ExprType::Const:
- result_ |= validator_.OnDataSegmentInitExpr_Const(
- expr->loc, cast<ConstExpr>(expr)->const_.type());
- break;
-
- case ExprType::GlobalGet: {
- Var var = cast<GlobalGetExpr>(expr)->var;
- result_ |=
- validator_.OnDataSegmentInitExpr_GlobalGet(expr->loc, var);
- break;
- }
-
- default:
- result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc);
- break;
+ if (f->data_segment.offset.size()) {
+ Type offset_type = Type::I32;
+ Index memory_index = module->GetMemoryIndex(f->data_segment.memory_var);
+ if (memory_index < module->memories.size() &&
+ module->memories[memory_index]->page_limits.is_64) {
+ offset_type = Type::I64;
}
- } else if (f->data_segment.offset.size() > 1) {
- result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc);
+ result_ |= validator_.BeginInitExpr(field.loc, offset_type);
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(
+ const_cast<ExprList&>(f->data_segment.offset));
+ result_ |= validator_.EndInitExpr();
}
}
}
// ignore result type.
CheckAction(cast<AssertExhaustionCommand>(command)->action.get());
break;
+ case CommandType::AssertException:
+ // ignore result type.
+ CheckAction(cast<AssertExceptionCommand>(command)->action.get());
+ break;
}
}
} // namespace wabt
-#endif // WABT_VALIDATOR_H_
+#endif // WABT_VALIDATOR_H_
}
switch (pair[1]) {
+ case TokenType::AssertException:
case TokenType::AssertExhaustion:
case TokenType::AssertInvalid:
case TokenType::AssertMalformed:
return false;
}
+void ResolveTypeName(
+ const Module& module,
+ Type& type,
+ Index index,
+ const std::unordered_map<uint32_t, std::string>& bindings) {
+ if (type != Type::Reference || type.GetReferenceIndex() != kInvalidIndex) {
+ return;
+ }
+
+ const auto name_iterator = bindings.find(index);
+ assert(name_iterator != bindings.cend());
+ const auto type_index = module.type_bindings.FindIndex(name_iterator->second);
+ assert(type_index != kInvalidIndex);
+ type = Type(Type::Reference, type_index);
+}
+
+void ResolveTypeNames(const Module& module, FuncDeclaration* decl) {
+ assert(decl);
+ auto& signature = decl->sig;
+
+ for (uint32_t param_index = 0; param_index < signature.GetNumParams();
+ ++param_index) {
+ ResolveTypeName(module, signature.param_types[param_index], param_index,
+ signature.param_type_names);
+ }
+
+ for (uint32_t result_index = 0; result_index < signature.GetNumResults();
+ ++result_index) {
+ ResolveTypeName(module, signature.result_types[result_index], result_index,
+ signature.result_type_names);
+ }
+}
+
void ResolveImplicitlyDefinedFunctionType(const Location& loc,
Module* module,
const FuncDeclaration& decl) {
Errors* errors) {
// Types must match exactly; no subtyping should be allowed.
if (actual != expected) {
- errors->emplace_back(ErrorLevel::Error, loc,
- StringPrintf("type mismatch for %s %" PRIindex
- " of %s. got %s, expected %s",
- index_kind, index, desc, actual.GetName(),
- expected.GetName()));
+ errors->emplace_back(
+ ErrorLevel::Error, loc,
+ StringPrintf("type mismatch for %s %" PRIindex
+ " of %s. got %s, expected %s",
+ index_kind, index, desc, actual.GetName().c_str(),
+ expected.GetName().c_str()));
return Result::Error;
}
return Result::Ok;
// ResolveFuncTypeWithEmptySignature), but if they are provided then we
// have to check. If we get here then the type var is invalid, so we
// can't check whether they match.
- errors->emplace_back(ErrorLevel::Error, loc,
- StringPrintf("invalid func type index %" PRIindex,
- decl.type_var.index()));
+ if (decl.type_var.is_index()) {
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("invalid func type index %" PRIindex,
+ decl.type_var.index()));
+ } else {
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("expected func type identifier %s",
+ decl.type_var.name().c_str()));
+ }
result = Result::Error;
}
}
: module_(module), errors_(errors) {}
void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) {
+ ResolveTypeNames(*module_, decl);
ResolveFuncTypeWithEmptySignature(*module_, decl);
if (!IsInlinableFuncSignature(decl->sig)) {
ResolveImplicitlyDefinedFunctionType(loc, module_, *decl);
bool has_func_type_and_empty_signature = false;
if (decl) {
+ ResolveTypeNames(*module, decl);
has_func_type_and_empty_signature =
ResolveFuncTypeWithEmptySignature(*module, decl);
ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl);
// local variables share the same index space, we need to increment the
// local indexes bound to a given name by the number of parameters in
// the function.
- for (auto& pair: func->bindings) {
+ for (auto& pair : func->bindings) {
pair.second.index += func->GetNumParams();
}
}
return TokenTypePair{{Peek(), Peek(1)}};
}
-bool WastParser::PeekMatch(TokenType type) {
- return Peek() == type;
+bool WastParser::PeekMatch(TokenType type, size_t n) {
+ return Peek(n) == type;
}
bool WastParser::PeekMatchLpar(TokenType type) {
return IsExpr(PeekPair());
}
+bool WastParser::PeekMatchRefType() {
+ return options_->features.function_references_enabled() &&
+ PeekMatchLpar(TokenType::Ref);
+}
+
bool WastParser::Match(TokenType type) {
if (PeekMatch(type)) {
Consume();
}
}
-bool WastParser::ParseElemExprOpt(ElemExpr* out_elem_expr) {
- Location loc = GetLocation();
+bool WastParser::ParseElemExprOpt(ExprList* out_elem_expr) {
+ WABT_TRACE(ParseElemExprOpt);
bool item = MatchLpar(TokenType::Item);
- bool lpar = Match(TokenType::Lpar);
- if (Match(TokenType::RefNull)) {
- if (!(options_->features.bulk_memory_enabled() ||
- options_->features.reference_types_enabled())) {
- Error(loc, "ref.null not allowed");
- }
- Type type;
- CHECK_RESULT(ParseRefKind(&type));
- *out_elem_expr = ElemExpr(type);
- } else if (Match(TokenType::RefFunc)) {
- Var var;
- CHECK_RESULT(ParseVar(&var));
- *out_elem_expr = ElemExpr(var);
- } else {
- return false;
- }
- if (lpar) {
- EXPECT(Rpar);
- }
+ ExprList exprs;
if (item) {
+ if (ParseTerminatingInstrList(&exprs) != Result::Ok) {
+ return false;
+ }
EXPECT(Rpar);
+ } else {
+ if (ParseExpr(&exprs) != Result::Ok) {
+ return false;
+ }
+ }
+ if (!exprs.size()) {
+ return false;
}
+ *out_elem_expr = std::move(exprs);
return true;
}
-bool WastParser::ParseElemExprListOpt(ElemExprVector* out_list) {
- ElemExpr elem_expr;
+bool WastParser::ParseElemExprListOpt(ExprListVector* out_list) {
+ ExprList elem_expr;
while (ParseElemExprOpt(&elem_expr)) {
- out_list->push_back(elem_expr);
+ out_list->push_back(std::move(elem_expr));
}
return !out_list->empty();
}
-bool WastParser::ParseElemExprVarListOpt(ElemExprVector* out_list) {
+bool WastParser::ParseElemExprVarListOpt(ExprListVector* out_list) {
WABT_TRACE(ParseElemExprVarListOpt);
Var var;
+ ExprList init_expr;
while (ParseVarOpt(&var)) {
- out_list->emplace_back(var);
+ init_expr.push_back(MakeUnique<RefFuncExpr>(var));
+ out_list->push_back(std::move(init_expr));
}
return !out_list->empty();
}
-Result WastParser::ParseValueType(Type* out_type) {
+Result WastParser::ParseValueType(Var* out_type) {
WABT_TRACE(ParseValueType);
- if (!PeekMatch(TokenType::ValueType)) {
+
+ const bool is_ref_type = PeekMatchRefType();
+ const bool is_value_type = PeekMatch(TokenType::ValueType);
+
+ if (!is_value_type && !is_ref_type) {
return ErrorExpected({"i32", "i64", "f32", "f64", "v128", "externref"});
}
+ if (is_ref_type) {
+ EXPECT(Lpar);
+ EXPECT(Ref);
+ CHECK_RESULT(ParseVar(out_type));
+ EXPECT(Rpar);
+ return Result::Ok;
+ }
+
Token token = Consume();
Type type = token.type();
bool is_enabled;
}
if (!is_enabled) {
- Error(token.loc, "value type not allowed: %s", type.GetName());
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
return Result::Error;
}
- *out_type = type;
+ *out_type = Var(type);
return Result::Ok;
}
-Result WastParser::ParseValueTypeList(TypeVector* out_type_list) {
+Result WastParser::ParseValueTypeList(
+ TypeVector* out_type_list,
+ std::unordered_map<uint32_t, std::string>* type_names) {
WABT_TRACE(ParseValueTypeList);
- while (PeekMatch(TokenType::ValueType))
- out_type_list->push_back(Consume().type());
+ while (true) {
+ if (!PeekMatchRefType() && !PeekMatch(TokenType::ValueType)) {
+ break;
+ }
+
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+
+ if (type.is_index()) {
+ out_type_list->push_back(Type(type.index()));
+ } else {
+ assert(type.is_name());
+ assert(options_->features.function_references_enabled());
+ type_names->emplace(out_type_list->size(), type.name());
+ out_type_list->push_back(Type(Type::Reference, kInvalidIndex));
+ }
+ }
return Result::Ok;
}
!options_->features.reference_types_enabled()) ||
((type == Type::Struct || type == Type::Array) &&
!options_->features.gc_enabled())) {
- Error(token.loc, "value type not allowed: %s", type.GetName());
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
return Result::Error;
}
Type type = token.type();
if (type == Type::ExternRef &&
!options_->features.reference_types_enabled()) {
- Error(token.loc, "value type not allowed: %s", type.GetName());
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
return Result::Error;
}
}
}
+Result WastParser::ParseMemidx(Location loc, Var* out_memidx) {
+ WABT_TRACE(ParseMemidx);
+ if (PeekMatchLpar(TokenType::Memory)) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ EXPECT(Lpar);
+ EXPECT(Memory);
+ CHECK_RESULT(ParseVar(out_memidx));
+ EXPECT(Rpar);
+ } else {
+ if (ParseVarOpt(out_memidx, Var(0, loc)) &&
+ !options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ }
+ return Result::Ok;
+}
+
Result WastParser::ParseLimitsIndex(Limits* out_limits) {
WABT_TRACE(ParseLimitsIndex);
Result WastParser::ParseTagModuleField(Module* module) {
WABT_TRACE(ParseTagModuleField);
EXPECT(Lpar);
- auto field = MakeUnique<TagModuleField>(GetLocation());
EXPECT(Tag);
- ParseBindVarOpt(&field->tag.name);
- CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl));
- CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig));
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Tag));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = MakeUnique<TagImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig));
+ auto field =
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = MakeUnique<TagModuleField>(GetLocation(), name);
+ CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->tags.size() - 1);
EXPECT(Rpar);
- module->AppendField(std::move(field));
return Result::Ok;
}
CHECK_RESULT(ParseTypeUseOpt(&func.decl));
CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
TypeVector local_types;
- CHECK_RESULT(ParseBoundValueTypeList(TokenType::Local, &local_types,
- &func.bindings, func.GetNumParams()));
+ CHECK_RESULT(ParseBoundValueTypeList(
+ TokenType::Local, &local_types, &func.bindings,
+ &func.decl.sig.param_type_names, func.GetNumParams()));
func.local_types.Set(local_types);
CHECK_RESULT(ParseTerminatingInstrList(&func.exprs));
module->AppendField(std::move(field));
// TODO: Share with ParseGlobalType?
if (MatchLpar(TokenType::Mut)) {
field->mutable_ = true;
- CHECK_RESULT(ParseValueType(&field->type));
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ field->type = Type(type.index());
EXPECT(Rpar);
} else {
field->mutable_ = false;
- CHECK_RESULT(ParseValueType(&field->type));
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ field->type = Type(type.index());
}
return Result::Ok;
};
Consume();
ParseBindVarOpt(&name);
auto import = MakeUnique<FuncImport>(name);
- if (PeekMatchLpar(TokenType::Type)) {
- import->func.decl.has_func_type = true;
- CHECK_RESULT(ParseTypeUseOpt(&import->func.decl));
- EXPECT(Rpar);
- } else {
- CHECK_RESULT(
- ParseFuncSignature(&import->func.decl.sig, &import->func.bindings));
- CHECK_RESULT(ErrorIfLpar({"param", "result"}));
- EXPECT(Rpar);
- }
+ CHECK_RESULT(ParseTypeUseOpt(&import->func.decl));
+ CHECK_RESULT(
+ ParseFuncSignature(&import->func.decl.sig, &import->func.bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ EXPECT(Rpar);
field = MakeUnique<ImportModuleField>(std::move(import), loc);
break;
}
elem_segment.elem_type = elem_type;
// Syntax is either an optional list of var (legacy), or a non-empty list
// of elem expr.
- ElemExpr elem_expr;
+ ExprList elem_expr;
if (ParseElemExprOpt(&elem_expr)) {
- elem_segment.elem_exprs.push_back(elem_expr);
+ elem_segment.elem_exprs.push_back(std::move(elem_expr));
// Parse the rest.
ParseElemExprListOpt(&elem_segment.elem_exprs);
} else {
BindingHash* param_bindings) {
WABT_TRACE(ParseFuncSignature);
CHECK_RESULT(ParseBoundValueTypeList(TokenType::Param, &sig->param_types,
- param_bindings));
- CHECK_RESULT(ParseResultList(&sig->result_types));
+ param_bindings, &sig->param_type_names));
+ CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
return Result::Ok;
}
Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) {
WABT_TRACE(ParseUnboundFuncSignature);
- CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types));
- CHECK_RESULT(ParseResultList(&sig->result_types));
+ CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types,
+ &sig->param_type_names));
+ CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
return Result::Ok;
}
-Result WastParser::ParseBoundValueTypeList(TokenType token,
- TypeVector* types,
- BindingHash* bindings,
- Index binding_index_offset) {
+Result WastParser::ParseBoundValueTypeList(
+ TokenType token,
+ TypeVector* types,
+ BindingHash* bindings,
+ std::unordered_map<uint32_t, std::string>* type_names,
+ Index binding_index_offset) {
WABT_TRACE(ParseBoundValueTypeList);
while (MatchLpar(token)) {
if (PeekMatch(TokenType::Var)) {
std::string name;
- Type type;
+ Var type;
Location loc = GetLocation();
ParseBindVarOpt(&name);
CHECK_RESULT(ParseValueType(&type));
bindings->emplace(name,
Binding(loc, binding_index_offset + types->size()));
- types->push_back(type);
+ if (type.is_index()) {
+ types->push_back(Type(type.index()));
+ } else {
+ assert(type.is_name());
+ assert(options_->features.function_references_enabled());
+ type_names->emplace(binding_index_offset + types->size(), type.name());
+ types->push_back(Type(Type::Reference, kInvalidIndex));
+ }
} else {
- CHECK_RESULT(ParseValueTypeList(types));
+ CHECK_RESULT(ParseValueTypeList(types, type_names));
}
EXPECT(Rpar);
}
return Result::Ok;
}
-Result WastParser::ParseUnboundValueTypeList(TokenType token,
- TypeVector* types) {
+Result WastParser::ParseUnboundValueTypeList(
+ TokenType token,
+ TypeVector* types,
+ std::unordered_map<uint32_t, std::string>* type_names) {
WABT_TRACE(ParseUnboundValueTypeList);
while (MatchLpar(token)) {
- CHECK_RESULT(ParseValueTypeList(types));
+ CHECK_RESULT(ParseValueTypeList(types, type_names));
EXPECT(Rpar);
}
return Result::Ok;
}
-Result WastParser::ParseResultList(TypeVector* result_types) {
+Result WastParser::ParseResultList(
+ TypeVector* result_types,
+ std::unordered_map<uint32_t, std::string>* type_names) {
WABT_TRACE(ParseResultList);
- return ParseUnboundValueTypeList(TokenType::Result, result_types);
+ return ParseUnboundValueTypeList(TokenType::Result, result_types, type_names);
}
Result WastParser::ParseInstrList(ExprList* exprs) {
return Result::Ok;
}
+template <typename T>
+Result WastParser::ParseMemoryInstrVar(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var memidx;
+ Var var;
+ if (PeekMatchLpar(TokenType::Memory)) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ CHECK_RESULT(ParseVar(&var));
+ out_expr->reset(new T(var, memidx, loc));
+ } else {
+ CHECK_RESULT(ParseVar(&memidx));
+ if (ParseVarOpt(&var, Var(0, loc))) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifiying memory variable is not allowed");
+ return Result::Error;
+ }
+ out_expr->reset(new T(var, memidx, loc));
+ } else {
+ out_expr->reset(new T(memidx, var, loc));
+ }
+ }
+ return Result::Ok;
+}
+
template <typename T>
Result WastParser::ParsePlainLoadStoreInstr(Location loc,
Token token,
return Result::Ok;
}
+template <typename T>
+Result WastParser::ParseMemoryLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr) {
+ Opcode opcode = token.opcode();
+ Var memidx;
+ Address offset;
+ Address align;
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+ out_expr->reset(new T(opcode, memidx, align, offset, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseSIMDLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr) {
+ ErrorUnlessOpcodeEnabled(token);
+
+ Var memidx(0, loc);
+
+ if (options_->features.multi_memory_enabled()) {
+ // We have to be a little careful when reading the memeory index.
+ // If there is just a single integer folloing the instruction that
+ // represents the lane index, so we check for either a pair of intergers
+ // or an integers followed by offset= or align=.
+ bool try_read_mem_index = true;
+ if (PeekMatch(TokenType::Nat)) {
+ // The next token could be a memory index or a lane index
+ if (!PeekMatch(TokenType::OffsetEqNat, 1) &&
+ !PeekMatch(TokenType::AlignEqNat, 1) &&
+ !PeekMatch(TokenType::Nat, 1)) {
+ try_read_mem_index = false;
+ }
+ }
+ if (try_read_mem_index) {
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ }
+ }
+ Address offset;
+ Address align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new T(token.opcode(), memidx, align, offset, lane_idx, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseMemoryExpr(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var memidx;
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ out_expr->reset(new T(memidx, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseMemoryBinaryExpr(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var srcmemidx;
+ Var destmemidx;
+ CHECK_RESULT(ParseMemidx(loc, &srcmemidx));
+ CHECK_RESULT(ParseMemidx(loc, &destmemidx));
+ out_expr->reset(new T(srcmemidx, destmemidx, loc));
+ return Result::Ok;
+}
+
Result WastParser::ParseSimdLane(Location loc, uint64_t* lane_idx) {
if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) {
return ErrorExpected({"a natural number in range [0, 32)"});
Literal literal = Consume().literal();
- Result result = ParseInt64(literal.text.begin(), literal.text.end(),
- lane_idx, ParseIntType::UnsignedOnly);
+ Result result = ParseInt64(literal.text.begin(), literal.text.end(), lane_idx,
+ ParseIntType::UnsignedOnly);
if (Failed(result)) {
Error(loc, "invalid literal \"" PRIstringview "\"",
TypeVector result;
if (options_->features.reference_types_enabled() &&
MatchLpar(TokenType::Result)) {
- CHECK_RESULT(ParseValueTypeList(&result));
+ CHECK_RESULT(ParseValueTypeList(&result, nullptr));
EXPECT(Rpar);
}
out_expr->reset(new SelectExpr(result, loc));
case TokenType::Load:
CHECK_RESULT(
- ParsePlainLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr));
+ ParseMemoryLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr));
break;
case TokenType::Store:
CHECK_RESULT(
- ParsePlainLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr));
+ ParseMemoryLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr));
break;
case TokenType::Const: {
case TokenType::MemoryCopy:
ErrorUnlessOpcodeEnabled(Consume());
- out_expr->reset(new MemoryCopyExpr(loc));
+ CHECK_RESULT(ParseMemoryBinaryExpr<MemoryCopyExpr>(loc, out_expr));
break;
case TokenType::MemoryFill:
ErrorUnlessOpcodeEnabled(Consume());
- out_expr->reset(new MemoryFillExpr(loc));
+ CHECK_RESULT(ParseMemoryExpr<MemoryFillExpr>(loc, out_expr));
break;
case TokenType::DataDrop:
case TokenType::MemoryInit:
ErrorUnlessOpcodeEnabled(Consume());
- CHECK_RESULT(ParsePlainInstrVar<MemoryInitExpr>(loc, out_expr));
+ CHECK_RESULT(ParseMemoryInstrVar<MemoryInitExpr>(loc, out_expr));
break;
case TokenType::MemorySize:
Consume();
- out_expr->reset(new MemorySizeExpr(loc));
+ CHECK_RESULT(ParseMemoryExpr<MemorySizeExpr>(loc, out_expr));
break;
case TokenType::MemoryGrow:
Consume();
- out_expr->reset(new MemoryGrowExpr(loc));
+ CHECK_RESULT(ParseMemoryExpr<MemoryGrowExpr>(loc, out_expr));
break;
case TokenType::TableCopy: {
}
case TokenType::SimdLoadLane: {
- Token token = Consume();
- ErrorUnlessOpcodeEnabled(token);
-
- Address offset;
- Address align;
- ParseOffsetOpt(&offset);
- ParseAlignOpt(&align);
-
- uint64_t lane_idx = 0;
- Result result = ParseSimdLane(loc, &lane_idx);
-
- if (Failed(result)) {
- return Result::Error;
- }
-
- out_expr->reset(new SimdLoadLaneExpr(token.opcode(), align, offset, lane_idx, loc));
+ CHECK_RESULT(
+ ParseSIMDLoadStoreInstr<SimdLoadLaneExpr>(loc, Consume(), out_expr));
break;
}
case TokenType::SimdStoreLane: {
- Token token = Consume();
- ErrorUnlessOpcodeEnabled(token);
-
- Address offset;
- Address align;
- ParseOffsetOpt(&offset);
- ParseAlignOpt(&align);
-
- uint64_t lane_idx = 0;
- Result result = ParseSimdLane(loc, &lane_idx);
-
- if (Failed(result)) {
- return Result::Error;
- }
-
- out_expr->reset(new SimdStoreLaneExpr(token.opcode(), align, offset, lane_idx, loc));
+ CHECK_RESULT(
+ ParseSIMDLoadStoreInstr<SimdStoreLaneExpr>(loc, Consume(), out_expr));
break;
}
values.set_u8(lane, static_cast<uint8_t>(lane_idx));
}
- out_expr->reset(
- new SimdShuffleOpExpr(token.opcode(), values, loc));
+ out_expr->reset(new SimdShuffleOpExpr(token.opcode(), values, loc));
break;
}
case TokenType::F32X4: { lane_count = 4; integer = false; break; }
case TokenType::F64X2: { lane_count = 2; integer = false; break; }
default: {
- Error(
- const_->loc,
- "Unexpected type at start of simd constant. "
- "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. "
- "Found \"%s\".",
- GetTokenTypeName(token_type)
- );
+ Error(const_->loc,
+ "Unexpected type at start of simd constant. "
+ "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. "
+ "Found \"%s\".",
+ GetTokenTypeName(token_type));
return Result::Error;
}
}
const_->set_f32(expected);
return Result::Ok;
}
- auto literal = Consume().literal();
+
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+
+ auto literal = token.literal();
uint32_t f32_bits;
Result result = ParseFloat(literal.type, literal.text.begin(),
literal.text.end(), &f32_bits);
const_->set_f64(expected);
return Result::Ok;
}
- auto literal = Consume().literal();
+
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+
+ auto literal = token.literal();
uint64_t f64_bits;
Result result = ParseDouble(literal.type, literal.text.begin(),
literal.text.end(), &f64_bits);
Result result;
switch (opcode) {
case Opcode::I32Const: {
- auto sv = Consume().literal().text;
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+ auto sv = token.literal().text;
uint32_t u32;
result = ParseInt32(sv.begin(), sv.end(), &u32,
ParseIntType::SignedAndUnsigned);
}
case Opcode::I64Const: {
- auto sv = Consume().literal().text;
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+ auto sv = token.literal().text;
uint64_t u64;
result = ParseInt64(sv.begin(), sv.end(), &u64,
ParseIntType::SignedAndUnsigned);
WABT_TRACE(ParseGlobalType);
if (MatchLpar(TokenType::Mut)) {
global->mutable_ = true;
- CHECK_RESULT(ParseValueType(&global->type));
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ global->type = Type(type.index());
CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"}));
EXPECT(Rpar);
} else {
- CHECK_RESULT(ParseValueType(&global->type));
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ global->type = Type(type.index());
}
return Result::Ok;
Result WastParser::ParseCommand(Script* script, CommandPtr* out_command) {
WABT_TRACE(ParseCommand);
switch (Peek(1)) {
+ case TokenType::AssertException:
+ return ParseAssertExceptionCommand(out_command);
+
case TokenType::AssertExhaustion:
return ParseAssertExhaustionCommand(out_command);
}
}
+Result WastParser::ParseAssertExceptionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertExceptionCommand);
+ return ParseAssertActionCommand<AssertExceptionCommand>(
+ TokenType::AssertException, out_command);
+}
+
Result WastParser::ParseAssertExhaustionCommand(CommandPtr* out_command) {
WABT_TRACE(ParseAssertExhaustionCommand);
return ParseAssertActionTextCommand<AssertExhaustionCommand>(
&errors, &module);
module.name = bsm->name;
module.loc = bsm->loc;
- for (const auto& error: errors) {
+ for (const auto& error : errors) {
assert(error.error_level == ErrorLevel::Error);
if (error.loc.offset == kInvalidOffset) {
Error(bsm->loc, "error in binary module: %s", error.message.c_str());
#define WABT_WAST_PARSER_H_
#include <array>
+#include <unordered_map>
#include "src/circular-array.h"
#include "src/error.h"
TokenTypePair PeekPair();
// Returns true if the next token's type is equal to the parameter.
- bool PeekMatch(TokenType);
+ bool PeekMatch(TokenType, size_t n = 0);
// Returns true if the next token's type is '(' and the following token is
// equal to the parameter.
// folded expressions, plain instructions and block instructions.
bool PeekMatchExpr();
+ // Returns true if the next two tokens are form reference type - (ref $t)
+ bool PeekMatchRefType();
+
// Returns true if the next token's type is equal to the parameter. If so,
// then the token is consumed.
bool Match(TokenType);
Result ParseTextList(std::vector<uint8_t>* out_data);
bool ParseTextListOpt(std::vector<uint8_t>* out_data);
Result ParseVarList(VarVector* out_var_list);
- bool ParseElemExprOpt(ElemExpr* out_elem_expr);
- bool ParseElemExprListOpt(ElemExprVector* out_list);
- bool ParseElemExprVarListOpt(ElemExprVector* out_list);
- Result ParseValueType(Type* out_type);
- Result ParseValueTypeList(TypeVector* out_type_list);
+ bool ParseElemExprOpt(ExprList* out_elem_expr);
+ bool ParseElemExprListOpt(ExprListVector* out_list);
+ bool ParseElemExprVarListOpt(ExprListVector* out_list);
+ Result ParseValueType(Var* out_type);
+ Result ParseValueTypeList(
+ TypeVector* out_type_list,
+ std::unordered_map<uint32_t, std::string>* type_names);
Result ParseRefKind(Type* out_type);
Result ParseRefType(Type* out_type);
bool ParseRefTypeOpt(Type* out_type);
Result ParseQuotedText(std::string* text);
bool ParseOffsetOpt(Address* offset);
bool ParseAlignOpt(Address* align);
+ Result ParseMemidx(Location loc, Var* memidx);
Result ParseLimitsIndex(Limits*);
Result ParseLimits(Limits*);
Result ParseNat(uint64_t*, bool is_64);
Result ParseBoundValueTypeList(TokenType,
TypeVector*,
BindingHash*,
+ std::unordered_map<uint32_t, std::string>*,
Index binding_index_offset = 0);
- Result ParseUnboundValueTypeList(TokenType, TypeVector*);
- Result ParseResultList(TypeVector*);
+ Result ParseUnboundValueTypeList(TokenType,
+ TypeVector*,
+ std::unordered_map<uint32_t, std::string>*);
+ Result ParseResultList(TypeVector*,
+ std::unordered_map<uint32_t, std::string>*);
Result ParseInstrList(ExprList*);
Result ParseTerminatingInstrList(ExprList*);
Result ParseInstr(ExprList*);
template <typename T>
Result ParsePlainInstrVar(Location, std::unique_ptr<Expr>*);
template <typename T>
+ Result ParseMemoryInstrVar(Location, std::unique_ptr<Expr>*);
+ template <typename T>
Result ParsePlainLoadStoreInstr(Location, Token, std::unique_ptr<Expr>*);
+ template <typename T>
+ Result ParseMemoryLoadStoreInstr(Location, Token, std::unique_ptr<Expr>*);
+ template <typename T>
+ Result ParseSIMDLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr);
+ template <typename T>
+ Result ParseMemoryExpr(Location, std::unique_ptr<Expr>*);
+ template <typename T>
+ Result ParseMemoryBinaryExpr(Location, std::unique_ptr<Expr>*);
Result ParseSimdLane(Location, uint64_t*);
Result ParseCommandList(Script*, CommandPtrVector*);
Result ParseCommand(Script*, CommandPtr*);
+ Result ParseAssertExceptionCommand(CommandPtr*);
Result ParseAssertExhaustionCommand(CommandPtr*);
Result ParseAssertInvalidCommand(CommandPtr*);
Result ParseAssertMalformedCommand(CommandPtr*);
#include "src/cast.h"
#include "src/common.h"
#include "src/expr-visitor.h"
-#include "src/ir.h"
#include "src/ir-util.h"
+#include "src/ir.h"
#include "src/literal.h"
#include "src/stream.h"
class WatWriter : ModuleContext {
public:
- WatWriter(Stream* stream, const WriteWatOptions& options,
- const Module &module)
+ WatWriter(Stream* stream,
+ const WriteWatOptions& options,
+ const Module& module)
: ModuleContext(module), options_(options), stream_(stream) {}
Result WriteModule();
void WriteQuotedString(string_view str, NextChar next_char);
void WriteVar(const Var& var, NextChar next_char);
void WriteVarUnlessZero(const Var& var, NextChar next_char);
+ void WriteMemoryVarUnlessZero(const Var& memidx, NextChar next_char);
+ void WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ NextChar next_char);
void WriteBrVar(const Var& var, NextChar next_char);
void WriteRefKind(Type type, NextChar next_char);
void WriteType(Type type, NextChar next_char);
void WriteExpr(const Expr* expr);
template <typename T>
void WriteLoadStoreExpr(const Expr* expr);
+ template <typename T>
+ void WriteMemoryLoadStoreExpr(const Expr* expr);
void WriteExprList(const ExprList& exprs);
void WriteInitExpr(const ExprList& expr);
template <typename T>
}
}
+void WatWriter::WriteMemoryVarUnlessZero(const Var& memidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(memidx) != 0) {
+ WriteVar(memidx, next_char);
+ } else {
+ next_char_ = next_char;
+ }
+}
+
+void WatWriter::WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(srcmemidx) != 0 ||
+ module.GetMemoryIndex(destmemidx) != 0) {
+ WriteVar(srcmemidx, NextChar::Space);
+ WriteVar(destmemidx, next_char);
+ } else {
+ next_char_ = next_char;
+ }
+}
+
void WatWriter::WriteBrVar(const Var& var, NextChar next_char) {
if (var.is_index()) {
if (var.index() < GetLabelStackSize()) {
}
void WatWriter::WriteType(Type type, NextChar next_char) {
- const char* type_name = type.GetName();
- assert(type_name);
- WritePuts(type_name, next_char);
+ WritePuts(type.GetName().c_str(), next_char);
}
void WatWriter::WriteTypes(const TypeVector& types, const char* name) {
WriteNewline(NO_FORCE_NEWLINE);
}
+template <typename T>
+void WatWriter::WriteMemoryLoadStoreExpr(const Expr* expr) {
+ auto typed_expr = cast<T>(expr);
+ WritePutsSpace(typed_expr->opcode.GetName());
+ WriteMemoryVarUnlessZero(typed_expr->memidx, NextChar::Space);
+ if (typed_expr->offset) {
+ Writef("offset=%" PRIaddress, typed_expr->offset);
+ }
+ if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) {
+ Writef("align=%" PRIaddress, typed_expr->align);
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+}
+
class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate {
public:
explicit ExprVisitorDelegate(WatWriter* writer) : writer_(writer) {}
return Result::Ok;
}
-Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(
- CallRefExpr* expr) {
+Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(CallRefExpr* expr) {
writer_->WritePutsSpace(Opcode::CallRef_Opcode.GetName());
return Result::Ok;
}
}
Result WatWriter::ExprVisitorDelegate::OnLoadExpr(LoadExpr* expr) {
- writer_->WriteLoadStoreExpr<LoadExpr>(expr);
+ writer_->WriteMemoryLoadStoreExpr<LoadExpr>(expr);
return Result::Ok;
}
}
Result WatWriter::ExprVisitorDelegate::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
- writer_->WritePutsNewline(Opcode::MemoryCopy_Opcode.GetName());
+ writer_->WritePutsSpace(Opcode::MemoryCopy_Opcode.GetName());
+ writer_->WriteTwoMemoryVarsUnlessBothZero(expr->srcmemidx, expr->destmemidx,
+ NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
return Result::Ok;
}
}
Result WatWriter::ExprVisitorDelegate::OnMemoryFillExpr(MemoryFillExpr* expr) {
- writer_->WritePutsNewline(Opcode::MemoryFill_Opcode.GetName());
+ writer_->WritePutsSpace(Opcode::MemoryFill_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
return Result::Ok;
}
Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
- writer_->WritePutsNewline(Opcode::MemoryGrow_Opcode.GetName());
+ writer_->WritePutsSpace(Opcode::MemoryGrow_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
return Result::Ok;
}
Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) {
- writer_->WritePutsNewline(Opcode::MemorySize_Opcode.GetName());
+ writer_->WritePutsSpace(Opcode::MemorySize_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
return Result::Ok;
}
Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) {
writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName());
- writer_->WriteVar(expr->var, NextChar::Newline);
+ writer_->WriteVar(expr->var, NextChar::Space);
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
return Result::Ok;
}
}
Result WatWriter::ExprVisitorDelegate::OnStoreExpr(StoreExpr* expr) {
- writer_->WriteLoadStoreExpr<StoreExpr>(expr);
+ writer_->WriteMemoryLoadStoreExpr<StoreExpr>(expr);
return Result::Ok;
}
return Result::Ok;
}
-Result WatWriter::ExprVisitorDelegate::OnCatchExpr(
- TryExpr* expr, Catch* catch_) {
+Result WatWriter::ExprVisitorDelegate::OnCatchExpr(TryExpr* expr,
+ Catch* catch_) {
writer_->Dedent();
if (catch_->IsCatchAll()) {
writer_->WritePutsNewline(Opcode::CatchAll_Opcode.GetName());
return Result::Ok;
}
-Result WatWriter::ExprVisitorDelegate::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+Result WatWriter::ExprVisitorDelegate::OnSimdLoadLaneExpr(
+ SimdLoadLaneExpr* expr) {
writer_->WritePutsSpace(expr->opcode.GetName());
if (expr->offset) {
writer_->Writef("offset=%" PRIaddress, expr->offset);
WritePuts("func", NextChar::Space);
}
- for (const ElemExpr& expr : segment.elem_exprs) {
+ for (const ExprList& expr : segment.elem_exprs) {
if (flags & SegUseElemExprs) {
- if (expr.kind == ElemExprKind::RefNull) {
- WriteOpenSpace("ref.null");
- WriteRefKind(expr.type, NextChar::Space);
- WriteCloseSpace();
- } else {
- WriteOpenSpace("ref.func");
- WriteVar(expr.var, NextChar::Space);
- WriteCloseSpace();
- }
+ WriteInitExpr(expr);
} else {
- assert(expr.kind == ElemExprKind::RefFunc);
- WriteVar(expr.var, NextChar::Space);
+ assert(expr.size() == 1);
+ assert(expr.front().type() == ExprType::RefFunc);
+ WriteVar(cast<const RefFuncExpr>(&expr.front())->var, NextChar::Space);
}
}
WriteCloseNewline();
WriteOpenSpace("data");
WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space);
if (segment.kind != SegmentKind::Passive) {
+ WriteMemoryVarUnlessZero(segment.memory_var, NextChar::Space);
WriteInitExpr(segment.offset);
}
WriteQuotedData(segment.data.data(), segment.data.size());
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2015-present Samsung Electronics 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.
-
-from __future__ import print_function
-
-import os
-import subprocess
-import sys
-
-from argparse import ArgumentParser
-from difflib import unified_diff
-#from check_license import CheckLicenser
-from os.path import abspath, dirname, join, relpath, splitext
-
-DEFAULT_DIR = dirname(dirname(abspath(__file__)))
-
-TERM_RED = '\033[1;31m'
-TERM_GREEN = '\033[1;32m'
-TERM_YELLOW = '\033[1;33m'
-TERM_PURPLE = '\033[35m'
-TERM_EMPTY = '\033[0m'
-
-
-clang_format_exts = ['.cpp', '.h']
-skip_dirs = ['build', 'CMakeFiles', 'docs', 'out', 'tools', 'third_party', 'bin', 'kangax', 'octane', 'test262', 'vendortest', '.git']
-skip_files = []
-
-
-class Stats(object):
- def __init__(self):
- self.files = 0
- self.lines = 0
- self.empty_lines = 0
- self.errors = 0
-
-
-def is_checked_by_clang(file):
- _, ext = splitext(file)
- return ext in clang_format_exts and file not in skip_files
-
-
-def check_tidy(src_dir, update, clang_format, stats):
- print('processing directory: %s' % src_dir)
-
- for dirpath, _, filenames in os.walk(src_dir):
- if any(d in relpath(dirpath, src_dir) for d in skip_dirs):
- continue
-
- for file in [join(dirpath, name) for name in filenames if is_checked_by_clang(name)]:
- def report_error(msg, line=None):
- print('%s%s:%s %s%s' % (TERM_YELLOW, file, '%d:' % line if line else '', msg, TERM_EMPTY))
- stats.errors += 1
-
- with open(file, 'r') as f:
- original = f.readlines()
- formatted = subprocess.check_output([clang_format, '-style=file', file])
-
- if update:
- with open(file, 'w') as f:
- f.write(formatted)
-
- stats.files += 1
- stats.lines += len(original)
-
- for lineno, line in enumerate(original):
- lineno += 1
-
- if '\t' in line:
- report_error('TAB character', lineno)
- if '\r' in line:
- report_error('CR character', lineno)
- if line.endswith(' \n') or line.endswith('\t\n'):
- report_error('trailing whitespace', lineno)
- if not line.endswith('\n'):
- report_error('line ends without NEW LINE character', lineno)
-
- if not line.strip():
- stats.empty_lines += 1
-
- diff = list(unified_diff(original, formatted.splitlines(True)))
- if diff:
- report_error('format error')
- for diffline in diff:
- print(diffline, end='')
-
- # if not CheckLicenser.check(file):
- # report_error('incorrect license')
-
-
-def main():
- parser = ArgumentParser(description='Escargot Source Format Checker and Updater')
- parser.add_argument('--clang-format', metavar='PATH', default='clang-format-6.0',
- help='path to clang-format (default: %(default)s)')
- parser.add_argument('--update', action='store_true',
- help='reformat files')
- parser.add_argument('--dir', metavar='PATH', default=DEFAULT_DIR,
- help='directory to process (default: %(default)s)')
- args = parser.parse_args()
-
- stats = Stats()
-
- check_tidy(args.dir, args.update, args.clang_format, stats)
-
- print()
- print('* Total number of files: %d' % stats.files)
- print('* Total lines of code: %d' % stats.lines)
- print('* Total non-empty lines of code: %d' % (stats.lines - stats.empty_lines))
- print('%s* Total number of errors: %d%s' % (TERM_RED if stats.errors else TERM_GREEN,
- stats.errors,
- TERM_EMPTY))
-
- if args.update:
- print()
- print('All files reformatted, check for changes with `git diff`.');
-
- sys.exit(1 if stats.errors else 0)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#!/usr/bin/env python2.7
-
-# Copyright 2015-present Samsung Electronics 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.
-
-from __future__ import print_function
-from cmd import Cmd
-from pprint import pprint
-import math
-import socket
-import sys
-import logging
-import time
-import debugger_core
-import codecs
-import locale
-
-# Wrap sys.stdout into a StreamWriter to allow writing unicode.
-sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
-
-from debugger_websocket import WebSocket
-from debugger_tcp import TcpSocket
-
-def write(string):
- print(string, end='')
-
-class DebuggerPrompt(Cmd):
- # pylint: disable=too-many-instance-attributes,too-many-arguments
- def __init__(self, debugger):
- Cmd.__init__(self)
- self.debugger = debugger
- self.stop = False
- self.quit = False
-
- def precmd(self, line):
- self.stop = False
- if self.debugger.non_interactive:
- print("%s" % line)
- return line
-
- def postcmd(self, stop, line):
- return self.stop
-
- def do_quit(self, _):
- """ Exit debugger """
- self.debugger.quit()
- self.quit = True
- self.stop = True
-
- def do_EOF(self, _):
- """ Exit Escargot debugger """
- print("Unexpected end of input. Connection closed.")
- self.debugger.quit()
- self.quit = True
- self.stop = True
-
- def do_display(self, args):
- """ Toggle source code display after breakpoints """
- if args:
- line_num = src_check_args(args)
- if line_num >= 0:
- self.debugger.display = line_num
- else:
- print("Non-negative integer number expected, 0 turns off this function")
-
- def do_break(self, args):
- """ Insert breakpoints on the given lines or functions """
- if not args:
- write("Error: Argument expected\n")
- else:
- write(self.debugger.set_break(args))
- do_b = do_break
-
- def do_list(self, _):
- """ Lists the available breakpoints """
- write(self.debugger.breakpoint_list())
-
- def do_delete(self, args):
- """ Delete the given breakpoint, use 'delete all|active|pending' to clear all the given breakpoints """
- write(self.debugger.delete(args))
-
- def do_continue(self, _):
- """ Continue execution """
- self.debugger.do_continue()
- self.stop = True
- if not self.debugger.non_interactive:
- print("Press enter to stop JavaScript execution.")
- do_c = do_continue
-
- def do_step(self, _):
- """ Next breakpoint, step into functions """
- self.debugger.step()
- self.stop = True
- do_s = do_step
-
- def do_next(self, args):
- """ Next breakpoint in the same context """
- self.stop = True
- if not args:
- args = 0
- self.debugger.next()
- return
-
- try:
- args = int(args)
- if args <= 0:
- raise ValueError(args)
-
- while args > 0:
- self.debugger.next()
- time.sleep(0.1)
-
- while True:
- result = self.debugger.process_messages()
- res_type = result.get_type()
-
- if res_type == result.END:
- self.quit = True
- return
- elif res_type == result.TEXT:
- write(result.get_text())
- elif res_type == result.PROMPT:
- break
-
- args -= 1
- except ValueError as val_errno:
- print("Error: expected a positive integer: %s" % val_errno)
- do_n = do_next
-
- def do_finish(self, _):
- """ Continue running until the current function returns """
- self.debugger.finish()
- self.stop = True
- do_f = do_finish
-
- def do_src(self, args):
- """ Get current source code """
- if args:
- line_num = src_check_args(args)
- if line_num >= 0:
- write(self.debugger.print_source(line_num, 0))
- else:
- write(self.debugger.print_source(0, 0))
- do_source = do_src
-
- def do_scroll(self, _):
- """ Scroll the source up or down """
- while True:
- key = sys.stdin.readline()
- if key == 'w\n':
- _scroll_direction(self.debugger, "up")
- elif key == 's\n':
- _scroll_direction(self.debugger, "down")
- elif key == 'q\n':
- break
- else:
- print("Invalid key")
-
- def do_eval(self, args):
- """ Evaluate JavaScript source code """
- self.debugger.eval(args)
- self.stop = True
- do_e = do_eval
-
- def do_backtrace(self, args):
- """ Get backtrace data from debugger """
- write(self.debugger.backtrace(args))
- self.stop = True
- do_bt = do_backtrace
-
- def do_scope(self, args):
- """ Get lexical environment chain """
- self.debugger.scope_chain(args)
- self.stop = True
-
- def do_variables(self, args):
- """ Get scope variables """
- write(self.debugger.scope_variables(args))
- self.stop = True
-
- def do_object(self, args):
- """ Get object by index """
- if not args:
- write("Error: Argument expected")
- else:
- write(self.debugger.object(args))
- self.stop = True
-
- def do_dump(self, args):
- """ Dump all of the debugger data """
- if args:
- print("Error: No argument expected")
- else:
- pprint(self.debugger.function_list)
-
-
-def _scroll_direction(debugger, direction):
- """ Helper function for do_scroll """
- debugger.src_offset_diff = int(max(math.floor(debugger.display / 3), 1))
- if direction == "up":
- debugger.src_offset -= debugger.src_offset_diff
- else:
- debugger.src_offset += debugger.src_offset_diff
- print(debugger.print_source(debugger.display, debugger.src_offset)['value'])
-
-
-def src_check_args(args):
- try:
- line_num = int(args)
- if line_num < 0:
- print("Error: Non-negative integer number expected")
- return -1
-
- return line_num
- except ValueError as val_errno:
- print("Error: Non-negative integer number expected: %s" % (val_errno))
- return -1
-
-
-# pylint: disable=too-many-branches,too-many-locals,too-many-statements,redefined-variable-type
-def main():
- args = debugger_core.arguments_parse()
-
- tranport = TcpSocket(args.address)
- protocol = WebSocket(tranport)
- debugger = debugger_core.Debugger(protocol)
-
- debugger.non_interactive = args.non_interactive
-
- logging.debug("Connected to Escargot")
-
- prompt = DebuggerPrompt(debugger)
- prompt.prompt = "(escargot-debugger) "
-
- if args.color:
- debugger.set_colors()
-
- if args.display:
- debugger.display = args.display
- prompt.do_display(args.display)
- else:
- prompt.stop = False
-
- if args.exception is not None:
- prompt.do_exception(str(args.exception))
-
- if args.client_source:
- debugger.store_client_sources(args.client_source)
-
- while True:
- if prompt.quit:
- break
-
- result = debugger.process_messages()
- res_type = result.get_type()
-
- if res_type == result.END:
- break
- elif res_type == result.PROMPT:
- prompt.cmdloop()
- elif res_type == result.TEXT:
- write(result.get_text())
- continue
-
-
-if __name__ == "__main__":
- try:
- main()
- sys.exit("Connection closed.")
- except socket.error as error_msg:
- ERRNO = error_msg.errno
- MSG = str(error_msg)
- if ERRNO == 111:
- sys.exit("Failed to connect to the Escargot debugger.")
- elif ERRNO == 32 or ERRNO == 104:
- sys.exit("Connection closed.")
- else:
- sys.exit("Failed to connect to the Escargot debugger.\nError: %s" % (MSG))
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2015-present Samsung Electronics 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.
-
-from __future__ import print_function
-import argparse
-import logging
-import re
-import select
-import struct
-import sys
-
-# Escargot debugger configuration
-ESCARGOT_DEBUGGER_VERSION = 1
-
-# Messages sent by Escargot to the debugger client.
-ESCARGOT_MESSAGE_VERSION = 0
-ESCARGOT_MESSAGE_CONFIGURATION = 1
-ESCARGOT_MESSAGE_CLOSE_CONNECTION = 2
-ESCARGOT_MESSAGE_RELEASE_FUNCTION = 3
-ESCARGOT_MESSAGE_PARSE_DONE = 4
-ESCARGOT_MESSAGE_PARSE_ERROR = 5
-ESCARGOT_MESSAGE_SOURCE_8BIT = 6
-ESCARGOT_MESSAGE_SOURCE_8BIT_END = 7
-ESCARGOT_MESSAGE_SOURCE_16BIT = 8
-ESCARGOT_MESSAGE_SOURCE_16BIT_END = 9
-ESCARGOT_MESSAGE_FILE_NAME_8BIT = 10
-ESCARGOT_MESSAGE_FILE_NAME_8BIT_END = 11
-ESCARGOT_MESSAGE_FILE_NAME_16BIT = 12
-ESCARGOT_MESSAGE_FILE_NAME_16BIT_END = 13
-ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT = 14
-ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT_END = 15
-ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT = 16
-ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT_END = 17
-ESCARGOT_MESSAGE_BREAKPOINT_LOCATION = 18
-ESCARGOT_MESSAGE_FUNCTION_PTR = 19
-ESCARGOT_MESSAGE_BREAKPOINT_HIT = 20
-ESCARGOT_MESSAGE_EXCEPTION_HIT = 21
-ESCARGOT_MESSAGE_EVAL_RESULT_8BIT = 22
-ESCARGOT_MESSAGE_EVAL_RESULT_8BIT_END = 23
-ESCARGOT_MESSAGE_EVAL_RESULT_16BIT = 24
-ESCARGOT_MESSAGE_EVAL_RESULT_16BIT_END = 25
-ESCARGOT_MESSAGE_EVAL_FAILED_8BIT = 26
-ESCARGOT_MESSAGE_EVAL_FAILED_8BIT_END = 27
-ESCARGOT_MESSAGE_EVAL_FAILED_16BIT = 28
-ESCARGOT_MESSAGE_EVAL_FAILED_16BIT_END = 29
-ESCARGOT_MESSAGE_BACKTRACE_TOTAL = 30
-ESCARGOT_MESSAGE_BACKTRACE = 31
-ESCARGOT_MESSAGE_BACKTRACE_END = 32
-ESCARGOT_MESSAGE_SCOPE_CHAIN = 33
-ESCARGOT_MESSAGE_SCOPE_CHAIN_END = 34
-ESCARGOT_MESSAGE_STRING_8BIT = 35
-ESCARGOT_MESSAGE_STRING_8BIT_END = 36
-ESCARGOT_MESSAGE_STRING_16BIT = 37
-ESCARGOT_MESSAGE_STRING_16BIT_END = 38
-ESCARGOT_MESSAGE_VARIABLE = 39
-ESCARGOT_MESSAGE_PRINT = 40
-ESCARGOT_MESSAGE_EXCEPTION = 41
-ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE = 42
-ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE = 43
-ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING = 44
-
-
-# Messages sent by the debugger client to Escargot.
-ESCARGOT_MESSAGE_FUNCTION_RELEASED = 0
-ESCARGOT_MESSAGE_UPDATE_BREAKPOINT = 1
-ESCARGOT_MESSAGE_CONTINUE = 2
-ESCARGOT_MESSAGE_STEP = 3
-ESCARGOT_MESSAGE_NEXT = 4
-ESCARGOT_MESSAGE_FINISH = 5
-ESCARGOT_MESSAGE_EVAL_8BIT_START = 6
-ESCARGOT_MESSAGE_EVAL_8BIT = 7
-ESCARGOT_MESSAGE_EVAL_16BIT_START = 8
-ESCARGOT_MESSAGE_EVAL_16BIT = 9
-ESCARGOT_MESSAGE_GET_BACKTRACE = 10
-ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 11
-ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 12
-ESCARGOT_MESSAGE_GET_OBJECT = 13
-ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 14
-ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 15
-ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 16
-ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 17
-ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 18
-ESCARGOT_DEBUGGER_PENDING_CONFIG = 19
-ESCARGOT_DEBUGGER_PENDING_RESUME = 20
-
-
-# Environment record types
-ESCARGOT_RECORD_GLOBAL_ENVIRONMENT = 0
-ESCARGOT_RECORD_FUNCTION_ENVIRONMENT = 1
-ESCARGOT_RECORD_DECLARATIVE_ENVIRONMENT = 2
-ESCARGOT_RECORD_OBJECT_ENVIRONMENT = 3
-ESCARGOT_RECORD_MODULE_ENVIRONMENT = 4
-ESCARGOT_RECORD_UNKNOWN_ENVIRONMENT = 5
-
-
-# Variable types
-ESCARGOT_VARIABLE_END = 0
-ESCARGOT_VARIABLE_UNACCESSIBLE = 1
-ESCARGOT_VARIABLE_UNDEFINED = 2
-ESCARGOT_VARIABLE_NULL = 3
-ESCARGOT_VARIABLE_TRUE = 4
-ESCARGOT_VARIABLE_FALSE = 5
-ESCARGOT_VARIABLE_NUMBER = 6
-ESCARGOT_VARIABLE_STRING = 7
-ESCARGOT_VARIABLE_SYMBOL = 8
-ESCARGOT_VARIABLE_BIGINT = 9
-ESCARGOT_VARIABLE_OBJECT = 10
-ESCARGOT_VARIABLE_ARRAY = 11
-ESCARGOT_VARIABLE_FUNCTION = 12
-ESCARGOT_VARIABLE_LONG_NAME = 0x40
-ESCARGOT_VARIABLE_LONG_VALUE = 0x80
-
-
-def arguments_parse():
- parser = argparse.ArgumentParser(description="Escargot debugger client")
-
- parser.add_argument("address", action="store", nargs="?", default="localhost:6501",
- help="specify a unique network address for tcp connection (default: %(default)s)")
- parser.add_argument("-v", "--verbose", action="store_true", default=False,
- help="increase verbosity (default: %(default)s)")
- parser.add_argument("--non-interactive", action="store_true", default=False,
- help="disable stop when newline is pressed (default: %(default)s)")
- parser.add_argument("--color", action="store_true", default=False,
- help="enable color highlighting on source commands (default: %(default)s)")
- parser.add_argument("--display", action="store", default=None, type=int,
- help="set display range")
- parser.add_argument("--exception", action="store", default=None, type=int, choices=[0, 1],
- help="set exception config, usage 1: [Enable] or 0: [Disable]")
- parser.add_argument("--client-source", action="store", default=[], type=str, nargs="+",
- help="specify a javascript source file to execute")
- args = parser.parse_args()
-
- if args.verbose:
- logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG)
- logging.debug("Debug logging mode: ON")
-
- return args
-
-
-def _parse_int(value):
- try:
- index = int(value)
- if index < 0:
- return "Error: A non negative integer number expected"
- return index
-
- except ValueError as val_errno:
- return "Error: Non negative integer number expected, %s\n" % (val_errno)
-
-
-class Breakpoint(object):
- def __init__(self, line, offset, function):
- self.line = line
- self.offset = offset
- self.function = function
- self.active_index = -1
-
- def __str__(self):
- result = self.function.source_name or "<unknown>"
- result += ":%d" % (self.line)
-
- if self.function.is_func:
- result += " (in "
- result += self.function.name or "function"
- result += "() at line:%d, col:%d)" % (self.function.line, self.function.column)
- return result
-
- def __repr__(self):
- return ("Breakpoint(line:%d, offset:%d, active_index:%d)"
- % (self.line, self.offset, self.active_index))
-
-class PendingBreakpoint(object):
- def __init__(self, line=None, source_name=None, function=None):
- self.function = function
- self.line = line
- self.source_name = source_name
-
- self.index = -1
-
- def __str__(self):
- result = self.source_name or ""
- if self.line:
- result += ":%d" % (self.line)
- else:
- result += "%s()" % (self.function)
- return result
-
-class EscargotFunction(object):
- # pylint: disable=too-many-instance-attributes,too-many-arguments
- def __init__(self, is_func, function_info, source, source_name, name, locations):
- self.is_func = is_func
- self.byte_code_ptr = function_info[0]
- self.source = re.split("\r\n|[\r\n]", source)
- self.source_name = source_name
- self.name = name
- self.lines = {}
- self.offsets = {}
- self.line = function_info[1]
- self.column = function_info[2]
- self.first_breakpoint_line = locations[0][0]
- self.first_breakpoint_offset = locations[0][1]
-
- if len(self.source) > 1 and not self.source[-1]:
- self.source.pop()
-
- for location in locations:
- breakpoint = Breakpoint(location[0], location[1], self)
- self.lines[location[0]] = breakpoint
- self.offsets[location[1]] = breakpoint
-
- def __repr__(self):
- result = ("Function(byte_code_ptr:0x%x, source_name:%r, name:%r { "
- % (self.byte_code_ptr, self.source_name, self.name))
-
- result += ','.join([str(breakpoint) for breakpoint in self.lines.values()])
-
- return result + " })"
-
-
-class Multimap(object):
-
- def __init__(self):
- self.map = {}
-
- def get(self, key):
- if key in self.map:
- return self.map[key]
- return []
-
- def insert(self, key, value):
- if key in self.map:
- self.map[key].append(value)
- else:
- self.map[key] = [value]
-
- def delete(self, key, value):
- items = self.map[key]
-
- if len(items) == 1:
- del self.map[key]
- else:
- del items[items.index(value)]
-
- def __repr__(self):
- return "Multimap(%r)" % (self.map)
-
-
-class DebuggerAction(object):
- END = 0
- WAIT = 1
- TEXT = 2
- PROMPT = 3
-
- def __init__(self, action_type, action_text):
- self.action_type = action_type
- self.action_text = action_text
-
- def get_type(self):
- return self.action_type
-
- def get_text(self):
- return self.action_text
-
-
-class Debugger(object):
- # pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use,redefined-variable-type
- def __init__(self, channel):
- self.prompt = False
- self.function_list = {}
- self.source = ''
- self.source_name = ''
- self.exception_string = ''
- self.frame_index = 0
- self.scope_vars = ""
- self.scopes = ""
- self.no_scope = 4294967295
- self.client_sources = []
- self.last_breakpoint_hit = None
- self.next_breakpoint_index = 0
- self.active_breakpoint_list = {}
- self.pending_breakpoint_list = {}
- self.line_list = Multimap()
- self.display = 0
- self.green = ''
- self.red = ''
- self.yellow = ''
- self.green_bg = ''
- self.yellow_bg = ''
- self.blue = ''
- self.nocolor = ''
- self.src_offset = 0
- self.src_offset_diff = 0
- self.non_interactive = False
- self.current_out = b""
- self.current_log = b""
- self.channel = channel
-
- # The server will send the version message after connection established
- # type [1]
- # littleEndian [1]
- # version [4]
- result = self.channel.connect()
-
- if len(result) != 6 or ord(result[0]) != ESCARGOT_MESSAGE_VERSION:
- raise Exception("Unexpected version info")
-
- self.little_endian = ord(result[1]) != 0
-
- if self.little_endian:
- self.byte_order = "<"
- logging.debug("Little-endian machine")
- else:
- self.byte_order = ">"
- logging.debug("Big-endian machine")
-
- self.version = struct.unpack(self.byte_order + 'I', result[2:6])[0]
- if self.version != ESCARGOT_DEBUGGER_VERSION:
- raise Exception("Incorrect debugger version from target: %d expected: %d" %
- (self.version, ESCARGOT_DEBUGGER_VERSION))
-
- result = self.channel.get_message(True)
-
- print("Connection created!!!")
-
- self.max_message_size = ord(result[1])
- self.pointer_size = ord(result[2])
-
- if self.pointer_size == 8:
- self.pointer_format = "Q"
- elif self.pointer_size == 4:
- self.pointer_format = "I"
- else:
- raise Exception("Unsupported pointer size: %d" % (self.pointer_size))
-
- self.idx_format = "I"
-
- logging.debug("Pointer size: %d", self.pointer_size)
-
- def __del__(self):
- if self.channel is not None:
- self.channel.close()
-
- def decode8(self, string):
- return string.decode("latin1")
-
- def decode16(self, string):
- return string.decode("UTF-16LE" if self.little_endian else "UTF-16BE", "namereplace")
-
- # pylint: disable=too-many-branches,too-many-locals,too-many-statements,too-many-return-statements
- def process_messages(self):
- result = ""
- while True:
- data = self.channel.get_message(False)
- if not self.non_interactive:
- if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
- sys.stdin.readline()
- self.step()
-
- if data == b'':
- action_type = DebuggerAction.PROMPT if self.prompt else DebuggerAction.WAIT
- return DebuggerAction(action_type, "")
-
- if not data: # Break the while loop if there is no more data.
- return DebuggerAction(DebuggerAction.END, "")
-
- buffer_type = ord(data[0])
- buffer_size = len(data) - 1
-
- logging.debug("Main buffer type: %d, message size: %d", buffer_type, buffer_size)
-
- if buffer_type in [ESCARGOT_MESSAGE_PARSE_ERROR,
- ESCARGOT_MESSAGE_SOURCE_8BIT,
- ESCARGOT_MESSAGE_SOURCE_8BIT_END,
- ESCARGOT_MESSAGE_SOURCE_16BIT,
- ESCARGOT_MESSAGE_SOURCE_16BIT_END]:
- result = self._parse_source(data)
- if result:
- return DebuggerAction(DebuggerAction.TEXT, result)
-
- elif buffer_type == ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING:
- self._exec_command(ESCARGOT_DEBUGGER_PENDING_RESUME)
-
- elif buffer_type == ESCARGOT_MESSAGE_CLOSE_CONNECTION:
- return DebuggerAction(DebuggerAction.END, "")
-
- elif buffer_type == ESCARGOT_MESSAGE_RELEASE_FUNCTION:
- self._release_function(data)
-
- elif buffer_type == ESCARGOT_MESSAGE_BREAKPOINT_HIT:
- breakpoint_data = struct.unpack(self.byte_order + self.pointer_format + self.idx_format, data[1:])
-
- breakpoint = self._get_breakpoint(breakpoint_data)
- self.last_breakpoint_hit = breakpoint[0]
-
- if breakpoint[1]:
- breakpoint_info = "at"
- else:
- breakpoint_info = "around"
-
- if breakpoint[0].active_index >= 0:
- breakpoint_info += " breakpoint:%s%d%s" % (self.red, breakpoint[0].active_index, self.nocolor)
-
- result += "Stopped %s %s\n" % (breakpoint_info, breakpoint[0])
-
- if self.display > 0:
- result += self.print_source(self.display, self.src_offset)
-
- self.prompt = True
- return DebuggerAction(DebuggerAction.TEXT, result)
-
- elif buffer_type == ESCARGOT_MESSAGE_BACKTRACE_TOTAL:
- total = struct.unpack(self.byte_order + self.idx_format, data[1:])[0]
- result += "Total number of frames: %d\n" % (total)
- return DebuggerAction(DebuggerAction.TEXT, result)
-
- elif buffer_type in [ESCARGOT_MESSAGE_EVAL_RESULT_8BIT,
- ESCARGOT_MESSAGE_EVAL_RESULT_8BIT_END,
- ESCARGOT_MESSAGE_EVAL_RESULT_16BIT,
- ESCARGOT_MESSAGE_EVAL_RESULT_16BIT_END]:
- self.prompt = True
- return DebuggerAction(DebuggerAction.TEXT, self._receive_string(ESCARGOT_MESSAGE_EVAL_RESULT_8BIT, data));
-
- elif buffer_type in [ESCARGOT_MESSAGE_EVAL_FAILED_8BIT,
- ESCARGOT_MESSAGE_EVAL_FAILED_8BIT_END,
- ESCARGOT_MESSAGE_EVAL_FAILED_16BIT,
- ESCARGOT_MESSAGE_EVAL_FAILED_16BIT_END]:
- self.prompt = True
- result = self._receive_string(ESCARGOT_MESSAGE_EVAL_RESULT_8BIT, data);
- return DebuggerAction(DebuggerAction.TEXT, "%sException: %s%s" % (self.red, result, self.no_color));
-
- elif buffer_type in [ESCARGOT_MESSAGE_BACKTRACE,
- ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE]:
- backtrace_info = struct.unpack(self.byte_order + self.pointer_format + self.idx_format + self.idx_format + self.idx_format, data[1:])
- function = self.function_list.get(backtrace_info[0])
-
- depth = ""
- if backtrace_info[3] != self.no_scope:
- depth = " [depth:%d]" % backtrace_info[3]
-
- if function is not None:
- result = "%s:%d:%d%s" % (function.source_name, backtrace_info[1], backtrace_info[2], depth)
- if function.name != "":
- result += " (in %s)" % (function.name)
- else:
- result = "unknown dynamic function:%d:%d%s" % (backtrace_info[1], backtrace_info[2], depth)
-
- if buffer_type == ESCARGOT_MESSAGE_BACKTRACE:
- return DebuggerAction(DebuggerAction.TEXT, result + "\n")
- return DebuggerAction(DebuggerAction.TEXT, "%s%s%s\n" % (self.red, result, self.nocolor))
-
- elif buffer_type == ESCARGOT_MESSAGE_BACKTRACE_END:
- self.prompt = True
- return DebuggerAction(DebuggerAction.WAIT, "")
-
- elif buffer_type in [ESCARGOT_MESSAGE_SCOPE_CHAIN,
- ESCARGOT_MESSAGE_SCOPE_CHAIN_END]:
- scope_chain = ""
-
- while buffer_type == ESCARGOT_MESSAGE_SCOPE_CHAIN:
- scope_chain += data[1:]
- data = self.channel.get_message(True)
- buffer_type = ord(data[0])
-
- if buffer_type != ESCARGOT_MESSAGE_SCOPE_CHAIN_END:
- raise Exception("Unexpected message")
-
- scope_chain += data[1:]
- result = ""
-
- for env_type in scope_chain:
- env_type = ord(env_type)
- if env_type == ESCARGOT_RECORD_GLOBAL_ENVIRONMENT:
- result += "Global Environment\n"
- elif env_type == ESCARGOT_RECORD_FUNCTION_ENVIRONMENT:
- result += "Function Environment\n"
- elif env_type == ESCARGOT_RECORD_DECLARATIVE_ENVIRONMENT:
- result += "Declarative Environment\n"
- elif env_type == ESCARGOT_RECORD_OBJECT_ENVIRONMENT:
- result += "Object Environment\n"
- elif env_type == ESCARGOT_RECORD_MODULE_ENVIRONMENT:
- result += "Module Environment\n"
- else:
- result += "Unknown Environment\n"
-
- self.prompt = True
- return DebuggerAction(DebuggerAction.TEXT, result)
-
- elif buffer_type == ESCARGOT_MESSAGE_VARIABLE:
- variable_full_type = ord(data[1])
- variable_type = variable_full_type & 0x3f
- variable_has_value = False
-
- if variable_type == ESCARGOT_VARIABLE_END:
- self.prompt = True
- return DebuggerAction(DebuggerAction.WAIT, "")
- elif variable_type == ESCARGOT_VARIABLE_UNACCESSIBLE:
- value_str = "unaccessible"
- elif variable_type == ESCARGOT_VARIABLE_UNDEFINED:
- value_str = "undefined"
- elif variable_type == ESCARGOT_VARIABLE_NULL:
- value_str = "null"
- elif variable_type == ESCARGOT_VARIABLE_TRUE:
- value_str = "boolean: true"
- elif variable_type == ESCARGOT_VARIABLE_FALSE:
- value_str = "boolean: false"
- elif variable_type == ESCARGOT_VARIABLE_NUMBER:
- value_str = "number: "
- variable_has_value = True
- elif variable_type == ESCARGOT_VARIABLE_STRING:
- value_str = "string: "
- variable_has_value = True
- elif variable_type == ESCARGOT_VARIABLE_SYMBOL:
- value_str = "symbol: "
- variable_has_value = True
- elif variable_type == ESCARGOT_VARIABLE_BIGINT:
- value_str = "bigint: "
- variable_has_value = True
- elif variable_type == ESCARGOT_VARIABLE_OBJECT:
- value_str = "object"
- elif variable_type == ESCARGOT_VARIABLE_ARRAY:
- value_str = "array"
- elif variable_type == ESCARGOT_VARIABLE_FUNCTION:
- value_str = "function"
-
- name = self._receive_string(ESCARGOT_MESSAGE_STRING_8BIT, self.channel.get_message(True));
- if variable_full_type & ESCARGOT_VARIABLE_LONG_NAME != 0:
- name += "..."
-
- if variable_has_value:
- value_str += self._receive_string(ESCARGOT_MESSAGE_STRING_8BIT, self.channel.get_message(True));
- if variable_full_type & ESCARGOT_VARIABLE_LONG_VALUE:
- value_str += "..."
- elif variable_type >= ESCARGOT_VARIABLE_OBJECT:
- value_str += " [id: %d]" % (struct.unpack(self.byte_order + self.idx_format, data[2:])[0])
-
- return DebuggerAction(DebuggerAction.TEXT, "%s: %s\n" % (name, value_str))
-
- elif buffer_type == ESCARGOT_MESSAGE_PRINT:
- printMessage ="Print: %s\n" % (self._receive_string(ESCARGOT_MESSAGE_STRING_8BIT, self.channel.get_message(True)))
- return DebuggerAction(DebuggerAction.TEXT, printMessage);
-
- elif buffer_type == ESCARGOT_MESSAGE_EXCEPTION:
- exceptionMessage ="%sException: %s%s\n" % (self.red, self._receive_string(ESCARGOT_MESSAGE_STRING_8BIT, self.channel.get_message(True)), self.nocolor)
- return DebuggerAction(DebuggerAction.TEXT, exceptionMessage);
- elif buffer_type == ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE:
- self.send_client_source()
- else:
- raise Exception("Unknown message: %d" % (buffer_type))
-
- def set_colors(self):
- self.nocolor = '\033[0m'
- self.green = '\033[92m'
- self.red = '\033[31m'
- self.yellow = '\033[93m'
- self.green_bg = '\033[42m\033[30m'
- self.yellow_bg = '\033[43m\033[30m'
- self.blue = '\033[94m'
-
- def store_client_sources(self, args):
- self.client_sources = args
-
- def send_client_source(self):
- if not self.client_sources:
- self._exec_command(ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE)
- return
- path = self.client_sources.pop(0)
- if not path.endswith('.js'):
- sys.exit("Error: Javascript file expected!")
- return
- with open(path, 'r') as src_file:
- content = path + '\0'+ src_file.read()
- self._send_string(content, ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START)
-
- def _exec_command(self, command_id):
- message = struct.pack(self.byte_order + "BB",
- 1,
- command_id)
- self.channel.send_message(self.byte_order, message)
-
- def do_continue(self):
- self.prompt = False
- self._exec_command(ESCARGOT_MESSAGE_CONTINUE)
-
- def step(self):
- self.prompt = False
- self._exec_command(ESCARGOT_MESSAGE_STEP)
-
- def next(self):
- self.prompt = False
- self._exec_command(ESCARGOT_MESSAGE_NEXT)
-
- def finish(self):
- self.prompt = False
- self._exec_command(ESCARGOT_MESSAGE_FINISH)
-
- def quit(self):
- self.prompt = False
- self._exec_command(ESCARGOT_MESSAGE_CONTINUE)
-
- def set_break(self, args):
- if not args:
- return "Error: Breakpoint index expected"
-
- if ':' in args:
- try:
- if int(args.split(':', 1)[1]) <= 0:
- return "Error: Positive breakpoint index expected"
-
- return self._set_breakpoint(args, False)
-
- except ValueError as val_errno:
- return "Error: Positive breakpoint index expected: %s" % (val_errno)
-
- return self._set_breakpoint(args, False)
-
- def delete(self, args):
- if not args:
- return "Error: Breakpoint index expected\n" \
- "Delete the given breakpoint, use 'delete all|active|pending' " \
- "to clear all the given breakpoints\n "
- elif args == 'all':
- self.delete_active()
- self.delete_pending()
- elif args == "pending":
- self.delete_pending()
- elif args == "active":
- self.delete_active()
- return ""
-
- try:
- breakpoint_index = int(args)
- except ValueError as val_errno:
- return "Error: Integer number expected, %s\n" % (val_errno)
-
- if breakpoint_index in self.active_breakpoint_list:
- breakpoint = self.active_breakpoint_list[breakpoint_index]
- del self.active_breakpoint_list[breakpoint_index]
- breakpoint.active_index = -1
- self._send_breakpoint(breakpoint)
- return "Breakpoint %d deleted\n" % (breakpoint_index)
- elif breakpoint_index in self.pending_breakpoint_list:
- del self.pending_breakpoint_list[breakpoint_index]
- if not self.pending_breakpoint_list:
- self._send_pending_config(0)
- return "Pending breakpoint %d deleted\n" % (breakpoint_index)
-
- return "Error: Breakpoint %d not found\n" % (breakpoint_index)
-
- def breakpoint_list(self):
- result = ""
- if self.active_breakpoint_list:
- result += "=== %sActive breakpoints%s ===\n" % (self.green_bg, self.nocolor)
- for breakpoint in self.active_breakpoint_list.values():
- result += " %d: %s\n" % (breakpoint.active_index, breakpoint)
-
- if self.pending_breakpoint_list:
- result += "=== %sPending breakpoints%s ===\n" % (self.yellow_bg, self.nocolor)
- for breakpoint in self.pending_breakpoint_list.values():
- result += " %d: %s (pending)\n" % (breakpoint.index, breakpoint)
-
- if not self.active_breakpoint_list and not self.pending_breakpoint_list:
- result += "No breakpoints\n"
-
- return result
-
- def print_source(self, line_num, offset):
- msg = ""
- last_bp = self.last_breakpoint_hit
-
- if not last_bp:
- return ""
-
- lines = last_bp.function.source
- if last_bp.function.source_name:
- msg += "Source: %s\n" % (last_bp.function.source_name)
-
- if line_num == 0:
- start = 0
- end = len(last_bp.function.source)
- else:
- start = max(last_bp.line - line_num, 0)
- end = min(last_bp.line + line_num - 1, len(last_bp.function.source))
- if offset:
- if start + offset < 0:
- self.src_offset += self.src_offset_diff
- offset += self.src_offset_diff
- elif end + offset > len(last_bp.function.source):
- self.src_offset -= self.src_offset_diff
- offset -= self.src_offset_diff
-
- start = max(start + offset, 0)
- end = min(end + offset, len(last_bp.function.source))
-
- for i in range(start, end):
- if i == last_bp.line - 1:
- msg += "%s%4d%s %s>%s %s\n" % (self.green, i + 1, self.nocolor, self.red, self.nocolor, lines[i])
- else:
- msg += "%s%4d%s %s\n" % (self.green, i + 1, self.nocolor, lines[i])
-
- return msg
-
- def eval(self, code):
- self._send_string(code, ESCARGOT_MESSAGE_EVAL_8BIT_START)
- self.prompt = False
-
- def backtrace(self, args):
- max_depth = 0
- min_depth = 0
- get_total = 0
-
- if args:
- args = args.split(" ")
- try:
- if "t" in args:
- get_total = 1
- args.remove("t")
-
- if len(args) >= 2:
- min_depth = int(args[0])
- max_depth = int(args[1])
- if max_depth <= 0 or min_depth < 0:
- return "Error: Positive integer number expected\n"
- if min_depth > max_depth:
- return "Error: Start depth needs to be lower than or equal to max depth\n"
- elif len(args) >= 1:
- max_depth = int(args[0])
- if max_depth <= 0:
- return "Error: Positive integer number expected\n"
-
- except ValueError as val_errno:
- return "Error: Positive integer number expected, %s\n" % (val_errno)
-
- self.frame_index = min_depth
-
- message = struct.pack(self.byte_order + "BB" + self.idx_format + self.idx_format + "B",
- 1 + 4 + 4 + 1,
- ESCARGOT_MESSAGE_GET_BACKTRACE,
- min_depth,
- max_depth,
- get_total)
-
- self.channel.send_message(self.byte_order, message)
-
- self.prompt = False
- return ""
-
- def scope_chain(self, args):
- index = 0
- if args != "":
- index = _parse_int(args)
-
- message = struct.pack(self.byte_order + "BB" + self.idx_format,
- 1 + 4,
- ESCARGOT_MESSAGE_GET_SCOPE_CHAIN,
- index)
-
- self.channel.send_message(self.byte_order, message)
-
- self.prompt = False
- return ""
-
- def scope_variables(self, args):
- args = args.split(" ", 1)
- stateIndex = 0
- index = 0
-
- if len(args) == 1:
- if args[0] != "":
- index = _parse_int(args[0])
- else:
- stateIndex = _parse_int(args[0])
- index = _parse_int(args[1])
-
- message = struct.pack(self.byte_order + "BB" + self.idx_format + self.idx_format,
- 1 + 4 + 4,
- ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES,
- stateIndex,
- index)
-
- self.channel.send_message(self.byte_order, message)
-
- self.prompt = False
- return ""
-
- def object(self, args):
- index = _parse_int(args)
-
- message = struct.pack(self.byte_order + "BB" + self.idx_format,
- 1 + 4,
- ESCARGOT_MESSAGE_GET_OBJECT,
- index)
-
- self.channel.send_message(self.byte_order, message)
-
- self.prompt = False
- return ""
-
- # pylint: disable=too-many-branches,too-many-locals,too-many-statements
- def _parse_source(self, data):
- source = ""
- source_name = ""
- is_func = False
- name = ""
- locations = []
-
- result = ""
- function_list = []
-
- while True:
- if data is None:
- return "Error: connection lost during source code receiving"
-
- buffer_type = ord(data[0])
- buffer_size = len(data) - 1
-
- logging.debug("Parser buffer type: %d, message size: %d", buffer_type, buffer_size)
-
- if buffer_type == ESCARGOT_MESSAGE_PARSE_DONE:
- break
-
- elif buffer_type == ESCARGOT_MESSAGE_PARSE_ERROR:
- logging.debug("Syntax error encountered")
- error_str = self._receive_string(ESCARGOT_MESSAGE_STRING_8BIT, self.channel.get_message(True))
- return "%sSyntaxError: %s%s\n" % (self.red, error_str, self.nocolor)
-
- elif buffer_type in [ESCARGOT_MESSAGE_SOURCE_8BIT, ESCARGOT_MESSAGE_SOURCE_8BIT_END]:
- source += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_SOURCE_8BIT_END:
- source = self.decode8(source)
-
- elif buffer_type in [ESCARGOT_MESSAGE_SOURCE_16BIT, ESCARGOT_MESSAGE_SOURCE_16BIT_END]:
- source += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_SOURCE_16BIT_END:
- source = self.decode16(source)
-
- elif buffer_type in [ESCARGOT_MESSAGE_FILE_NAME_8BIT, ESCARGOT_MESSAGE_FILE_NAME_8BIT_END]:
- source_name += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_FILE_NAME_8BIT_END:
- source_name = self.decode8(source_name)
-
- elif buffer_type in [ESCARGOT_MESSAGE_FILE_NAME_16BIT, ESCARGOT_MESSAGE_FILE_NAME_16BIT_END]:
- source_name += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_FILE_NAME_16BIT_END:
- source_name = self.decode16(source_name)
-
- elif buffer_type in [ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT, ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT_END]:
- name += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT_END:
- name = self.decode8(name)
-
- elif buffer_type in [ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT, ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT_END]:
- name += data[1:]
- if buffer_type == ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT_END:
- name = self.decode16(name)
-
- elif buffer_type == ESCARGOT_MESSAGE_BREAKPOINT_LOCATION:
- logging.debug("Breakpoint %s received", source_name)
-
- buffer_pos = 1
- while buffer_size > 0:
- location = struct.unpack(self.byte_order + self.idx_format + self.idx_format,
- data[buffer_pos: buffer_pos + 8])
- locations.append(location)
- buffer_pos += 8
- buffer_size -= 8
-
- elif buffer_type == ESCARGOT_MESSAGE_FUNCTION_PTR:
- function_info = struct.unpack(self.byte_order + self.pointer_format + self.idx_format + self.idx_format, data[1:])
- logging.debug("Pointer %s received %d", source_name, function_info[0])
-
- function_list.append(EscargotFunction(is_func, function_info, source, source_name, name, locations))
-
- is_func = True
- name = ""
- locations = []
-
- elif buffer_type == ESCARGOT_MESSAGE_RELEASE_FUNCTION:
- self._release_function(data)
-
- else:
- logging.error("Parser error!")
- raise Exception("Unexpected message: %d" % (buffer_type))
-
- data = self.channel.get_message(True)
-
- # Only append these functions if parsing is successful
- for function in function_list:
- for line, breakpoint in function.lines.items():
- self.line_list.insert(line, breakpoint)
- self.function_list[function.byte_code_ptr] = function
-
- # Try to set the pending breakpoints
- if self.pending_breakpoint_list:
- logging.debug("Pending breakpoints available")
- bp_list = self.pending_breakpoint_list
-
- for breakpoint_index, breakpoint in bp_list.items():
- source_lines = 0
- for src in function_list:
- if src.name == breakpoint.source_name:
- source_lines = len(src.source)
- break
- if breakpoint.line:
- if breakpoint.line <= source_lines:
- command = src.source_name + ":" + str(breakpoint.line)
- set_result = self._set_breakpoint(command, True)
- if set_result:
- result += set_result
- del bp_list[breakpoint_index]
- elif breakpoint.function:
- command = breakpoint.function
- set_result = self._set_breakpoint(command, True)
- if set_result:
- result += set_result
- del bp_list[breakpoint_index]
- if not bp_list:
- self._send_pending_config(0)
- return result
-
- def _release_function(self, data):
- byte_code_ptr = struct.unpack(self.byte_order + self.pointer_format, data[1:])[0]
-
- function = self.function_list.get(byte_code_ptr)
- if function is None:
- return
-
- for line, breakpoint in function.lines.items():
- self.line_list.delete(line, breakpoint)
- if breakpoint.active_index >= 0:
- del self.active_breakpoint_list[breakpoint.active_index]
-
- del self.function_list[byte_code_ptr]
-
- message = struct.pack(self.byte_order + "BB" + self.pointer_format,
- 1 + self.pointer_size,
- ESCARGOT_MESSAGE_FUNCTION_RELEASED,
- byte_code_ptr)
- self.channel.send_message(self.byte_order, message)
- logging.debug("Function {0x%x} byte-code released", byte_code_ptr)
-
- def _send_breakpoint(self, breakpoint):
- message = struct.pack(self.byte_order + "BBB" + self.pointer_format + self.idx_format,
- 1 + 1 + self.pointer_size + 4,
- ESCARGOT_MESSAGE_UPDATE_BREAKPOINT,
- int(breakpoint.active_index >= 0),
- breakpoint.function.byte_code_ptr,
- breakpoint.offset)
- self.channel.send_message(self.byte_order, message)
-
- def _send_pending_config(self, enable):
- message = struct.pack(self.byte_order + "BBB",
- 1 + 1,
- ESCARGOT_DEBUGGER_PENDING_CONFIG,
- enable)
- self.channel.send_message(self.byte_order, message)
-
- def _get_breakpoint(self, breakpoint_data):
- function = self.function_list[breakpoint_data[0]]
- offset = breakpoint_data[1]
-
- if offset in function.offsets:
- return (function.offsets[offset], True)
-
- if offset < function.first_breakpoint_offset:
- return (function.offsets[function.first_breakpoint_offset], False)
-
- nearest_offset = -1
-
- for current_offset in function.offsets:
- if current_offset <= offset and current_offset > nearest_offset:
- nearest_offset = current_offset
-
- return (function.offsets[nearest_offset], False)
-
- def _enable_breakpoint(self, breakpoint):
- if isinstance(breakpoint, PendingBreakpoint):
- if self.breakpoint_pending_exists(breakpoint):
- return "%sPending breakpoint%s already exists\n" % (self.yellow, self.nocolor)
-
- self.next_breakpoint_index += 1
- breakpoint.index = self.next_breakpoint_index
- self.pending_breakpoint_list[self.next_breakpoint_index] = breakpoint
- return ("%sPending breakpoint %d%s at %s\n" % (self.yellow,
- breakpoint.index,
- self.nocolor,
- breakpoint))
- if breakpoint.active_index < 0:
- self.next_breakpoint_index += 1
- self.active_breakpoint_list[self.next_breakpoint_index] = breakpoint
- breakpoint.active_index = self.next_breakpoint_index
- self._send_breakpoint(breakpoint)
-
- return "%sBreakpoint %d%s at %s\n" % (self.green,
- breakpoint.active_index,
- self.nocolor,
- breakpoint)
-
- def _set_breakpoint(self, string, pending):
- line = re.match("(.*):(\\d+)$", string)
- result = ""
-
- if line:
- source_name = line.group(1)
- new_line = int(line.group(2))
-
- for breakpoint in self.line_list.get(new_line):
- func_source = breakpoint.function.source_name
- if (source_name == func_source or
- func_source.endswith("/" + source_name) or
- func_source.endswith("\\" + source_name)):
-
- result += self._enable_breakpoint(breakpoint)
-
- else:
- functions_to_enable = []
- for function in self.function_list.values():
- if function.name == string:
- functions_to_enable.append(function)
-
- functions_to_enable.sort(key=lambda x: x.line)
-
- for function in functions_to_enable:
- result += self._enable_breakpoint(function.lines[function.first_breakpoint_line])
-
- if not result and not pending:
- print("No breakpoint found, do you want to add a %spending breakpoint%s? (y or [n])" % \
- (self.yellow, self.nocolor))
-
- ans = sys.stdin.readline()
- if ans in ['yes\n', 'y\n']:
- if not self.pending_breakpoint_list:
- self._send_pending_config(1)
-
- if line:
- breakpoint = PendingBreakpoint(int(line.group(2)), line.group(1))
- else:
- breakpoint = PendingBreakpoint(function=string)
- result += self._enable_breakpoint(breakpoint)
-
- return result
-
- def _send_string(self, args, message_type):
- # 1: length of type byte
- # 4: length of an uint32 value
- message_header = 1 + 4
-
- if not isinstance(args, unicode):
- try:
- args = args.decode("ascii")
- except UnicodeDecodeError:
- args = args.decode("utf-8")
-
- try:
- args = args.encode("latin1")
- except UnicodeEncodeError:
- args = args.encode("UTF-16LE" if self.little_endian else "UTF-16BE", "namereplace")
- message_type += 2
-
- size = len(args)
- max_fragment = min(self.max_message_size - message_header, size)
-
- message = struct.pack(self.byte_order + "BBI",
- max_fragment + message_header,
- message_type,
- size)
-
- if size == max_fragment:
- self.channel.send_message(self.byte_order, message + args)
- return
-
- self.channel.send_message(self.byte_order, message + args[0:max_fragment])
- offset = max_fragment
-
- # 1: length of type byte
- message_header = 1
- message_type += 1
-
- max_fragment = self.max_message_size - message_header
- while offset < size:
- next_fragment = min(max_fragment, size - offset)
-
- message = struct.pack(self.byte_order + "BB",
- next_fragment + message_header,
- message_type)
-
- prev_offset = offset
- offset += next_fragment
-
- self.channel.send_message(self.byte_order, message + args[prev_offset:offset])
-
- def _receive_string(self, message_type, data):
- result = b'';
- buffer_type = ord(data[0])
- end_type = message_type + 1;
-
- if buffer_type > end_type:
- end_type += 2
-
- while buffer_type == end_type - 1:
- result += data[1:]
- data = self.channel.get_message(True)
- buffer_type = ord(data[0])
-
- if buffer_type != end_type:
- raise Exception("Unexpected message")
-
- result += data[1:]
-
- if end_type == message_type + 1:
- result = self.decode8(result)
- else:
- result = self.decode16(result)
-
- return result;
-
- def delete_active(self):
- for i in self.active_breakpoint_list.values():
- breakpoint = self.active_breakpoint_list[i.active_index]
- del self.active_breakpoint_list[i.active_index]
- breakpoint.active_index = -1
- self._send_breakpoint(breakpoint)
-
- def delete_pending(self):
- if self.pending_breakpoint_list:
- self.pending_breakpoint_list.clear()
- self._send_pending_config(0)
-
- def breakpoint_pending_exists(self, breakpoint):
- for existing_bp in self.pending_breakpoint_list.values():
- if (breakpoint.line and existing_bp.source_name == breakpoint.source_name and \
- existing_bp.line == breakpoint.line) \
- or (not breakpoint.line and existing_bp.function == breakpoint.function):
- return True
-
- return False
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2015-present Samsung Electronics 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.
-
-import socket
-import select
-
-# pylint: disable=too-many-arguments,superfluous-parens
-class TcpSocket(object):
- """ Create a new TCP/IP socket. """
- def __init__(self, address):
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0, None)
- if ":" not in address:
- self.address = (address, 6501)
- else:
- host, port = address.split(":")
- self.address = (host, int(port))
-
- def connect(self):
- """ Connect to a remote socket at address (host, port). """
- print("Connecting to: %s:%s" % (self.address[0], self.address[1]))
- self.socket.connect(self.address)
-
- def close(self):
- """ Mark the socket closed. """
- self.socket.close()
-
- def receive_data(self, max_size=1024):
- """ Receive data (maximum amount: max_size). """
- return self.socket.recv(max_size)
-
- def send_data(self, data):
- """ Send data to the remote peer. """
- return self.socket.send(data)
-
- def ready(self):
- """ Checks whether data is available. """
- result = select.select([self.socket], [], [], 0)[0]
-
- return self.socket in result
+++ /dev/null
-#!/bin/bash
-
-ESCARGOT=$1
-TEST_CASE=$2
-DEBUGGER_CLIENT=$3
-IS_CLIENT_SOURCE=$4
-RESULT_TEMP=`mktemp ${TEST_CASE}.out.XXXXXXXXXX`
-if [[ $IS_CLIENT_SOURCE == 0 ]] && [[ $TEST_CASE != *"tools/debugger/tests/client_source_multiple"* ]]; then
- ${ESCARGOT} --start-debug-server ${TEST_CASE}.js &
- sleep 1s
- (cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --non-interactive) >${RESULT_TEMP} 2>&1
- diff -u ${TEST_CASE}.expected ${RESULT_TEMP}
-elif [[ $IS_CLIENT_SOURCE == 1 ]]; then
- ${ESCARGOT} --start-debug-server --debugger-wait-source &
- sleep 1s
- if [[ $TEST_CASE == *"client_source"* ]]; then
- (cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --client-source ${TEST_CASE}_2.js ${TEST_CASE}_1.js --non-interactive) >${RESULT_TEMP} 2>&1
- else
- (cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --client-source ${TEST_CASE}.js --non-interactive) >${RESULT_TEMP} 2>&1
- fi
- diff -u ${TEST_CASE}.expected ${RESULT_TEMP}
-fi
-
-
-STATUS_CODE=$?
-
-rm -f ${RESULT_TEMP}
-
-exit ${STATUS_CODE}
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2015-present Samsung Electronics 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.
-
-import struct
-
-MAX_BUFFER_SIZE = 128
-WEBSOCKET_BINARY_FRAME = 2
-WEBSOCKET_FIN_BIT = 0x80
-
-class WebSocket(object):
- def __init__(self, transport):
-
- self.data_buffer = b""
- self.transport = transport
-
- def connect(self):
- """ WebSockets connection. """
- self.transport.connect()
- self.receive_buffer = b""
-
- self.__send_data(b"GET /escargot-debugger HTTP/1.1\r\n" +
- b"Upgrade: websocket\r\n" +
- b"Connection: Upgrade\r\n" +
- b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
-
- # Expected answer from the handshake.
- expected = (b"HTTP/1.1 101 Switching Protocols\r\n" +
- b"Upgrade: websocket\r\n" +
- b"Connection: Upgrade\r\n" +
- b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")
-
- len_expected = len(expected)
-
- while len(self.receive_buffer) < len_expected:
- self.receive_buffer += self.transport.receive_data()
-
- if self.receive_buffer[0:len_expected] != expected:
- raise Exception("Unexpected handshake")
-
- if len(self.receive_buffer) > len_expected:
- self.receive_buffer = self.receive_buffer[len_expected:]
- else:
- self.receive_buffer = b""
-
-
- # First the version info must be returned
- # header [2] - opcode[1], size[1]
- # MessageVersion [6 bytes]
- len_expected = 2 + 6
-
- while len(self.receive_buffer) < len_expected:
- self.receive_buffer += self.transport.receive_data()
-
- expected = struct.pack("BB",
- WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
- 6)
-
- if self.receive_buffer[0:2] != expected:
- raise Exception("Unexpected version info")
-
- result = self.receive_buffer[2:len_expected]
- self.receive_buffer = self.receive_buffer[len_expected:]
-
- return result
-
- def __send_data(self, data):
- """ Private function to send data using the given transport. """
- size = len(data)
-
- while size > 0:
- bytes_send = self.transport.send_data(data)
- if bytes_send < size:
- data = data[bytes_send:]
- size -= bytes_send
-
- def send_message(self, byte_order, packed_data):
- """ Send message. """
- message = struct.pack(byte_order + "BBI",
- WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
- WEBSOCKET_FIN_BIT + struct.unpack(byte_order + "B", packed_data[0])[0],
- 0) + packed_data[1:]
-
- self.__send_data(message)
-
- def close(self):
- """ Close the WebSocket. """
- self.transport.close()
-
- def get_message(self, blocking):
- """ Receive message. """
-
- # Connection was closed
- if self.receive_buffer is None:
- return None
-
- while True:
- if len(self.receive_buffer) >= 2:
- if ord(self.receive_buffer[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT:
- raise Exception("Unexpected data frame")
-
- size = ord(self.receive_buffer[1])
- if size == 0 or size >= 126:
- raise Exception("Unexpected data frame")
-
- if len(self.receive_buffer) >= size + 2:
- result = self.receive_buffer[2:size + 2]
- self.receive_buffer = self.receive_buffer[size + 2:]
- return result
-
- if not blocking and not self.transport.ready():
- return b''
-
- data = self.transport.receive_data(MAX_BUFFER_SIZE)
-
- if not data:
- self.receive_buffer = None
- return None
- self.receive_buffer += data
+++ /dev/null
-n
-n
-s
-s
-s
-s
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/client_source_multiple_2.js:21
-(escargot-debugger) n
-Print: multiple-client-source-test-file-2
-Stopped at tools/debugger/tests/client_source_multiple_1.js:21
-(escargot-debugger) n
-Print: multiple-client-source-test-file-1
-Stopped at tools/debugger/tests/client_source_multiple_1.js:33
-(escargot-debugger) s
-Stopped at tools/debugger/tests/client_source_multiple_1.js:24 (in foo() at line:23, col:1)
-(escargot-debugger) s
-Print: foo
-Stopped at tools/debugger/tests/client_source_multiple_1.js:25 (in foo() at line:23, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/client_source_multiple_2.js:24 (in bar() at line:23, col:1)
-(escargot-debugger) s
-Print: bar
-Stopped at tools/debugger/tests/client_source_multiple_2.js:25 (in bar() at line:23, col:1)
-(escargot-debugger) c
-Print: str-argument: called-from-test-file-1
-Print: crossFoo
-Print: str-argument: called-from-test-file-2
-Connection closed.
+++ /dev/null
-s
-backtrace
-s
-s
-s
-bt t
-bt 2
-bt 1 3
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_backtrace.js:20
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_backtrace.js:34
-(escargot-debugger) backtrace
-tools/debugger/tests/do_backtrace.js:34:1 [depth:0]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_backtrace.js:31 (in h() at line:30, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_backtrace.js:27 (in g() at line:26, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_backtrace.js:23 (in f() at line:22, col:1)
-(escargot-debugger) bt t
-Total number of frames: 4
-tools/debugger/tests/do_backtrace.js:23:5 [depth:0] (in f)
-tools/debugger/tests/do_backtrace.js:27:5 [depth:1] (in g)
-tools/debugger/tests/do_backtrace.js:31:5 [depth:2] (in h)
-tools/debugger/tests/do_backtrace.js:34:1 [depth:3]
-(escargot-debugger) bt 2
-tools/debugger/tests/do_backtrace.js:23:5 [depth:0] (in f)
-tools/debugger/tests/do_backtrace.js:27:5 [depth:1] (in g)
-(escargot-debugger) bt 1 3
-tools/debugger/tests/do_backtrace.js:27:5 [depth:1] (in g)
-tools/debugger/tests/do_backtrace.js:31:5 [depth:2] (in h)
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-break do_break.js:53
-b do_break.js:39
-b f
-c
-c
-c
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break.js:21
-(escargot-debugger) break do_break.js:53
-Breakpoint 1 at tools/debugger/tests/do_break.js:53
-(escargot-debugger) b do_break.js:39
-Breakpoint 2 at tools/debugger/tests/do_break.js:39 (in test() at line:23, col:1)
-(escargot-debugger) b f
-Breakpoint 3 at tools/debugger/tests/do_break.js:26 (in f() at line:25, col:3)
-Breakpoint 4 at tools/debugger/tests/do_break.js:31 (in f() at line:29, col:3)
-Breakpoint 5 at tools/debugger/tests/do_break.js:36 (in f() at line:34, col:3)
-Breakpoint 6 at tools/debugger/tests/do_break.js:47 (in f() at line:45, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break.js:53
-(escargot-debugger) c
-Stopped at breakpoint:2 tools/debugger/tests/do_break.js:39 (in test() at line:23, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:5 tools/debugger/tests/do_break.js:36 (in f() at line:34, col:3)
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-s
-bt
-b do_break_async.js:40
-b do_break_async.js:31
-b do_break_async.js:22
-c
-bt
-c
-bt
-c
-bt
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_async.js:43
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async.js:39 (in j() at line:38, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async.js:39:5 [depth:0] (in j)
-tools/debugger/tests/do_break_async.js:43:1 [depth:1]
-(escargot-debugger) b do_break_async.js:40
-Breakpoint 1 at tools/debugger/tests/do_break_async.js:40 (in j() at line:38, col:1)
-(escargot-debugger) b do_break_async.js:31
-Breakpoint 2 at tools/debugger/tests/do_break_async.js:31 (in h() at line:29, col:1)
-(escargot-debugger) b do_break_async.js:22
-Breakpoint 3 at tools/debugger/tests/do_break_async.js:22 (in f() at line:20, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_async.js:40 (in j() at line:38, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async.js:43:1
-(escargot-debugger) c
-Stopped at breakpoint:2 tools/debugger/tests/do_break_async.js:31 (in h() at line:29, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async.js:35:5 (in i)
-tools/debugger/tests/do_break_async.js:43:1
-(escargot-debugger) c
-Stopped at breakpoint:3 tools/debugger/tests/do_break_async.js:22 (in f() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async.js:26:5 (in g)
-tools/debugger/tests/do_break_async.js:35:5 (in i)
-tools/debugger/tests/do_break_async.js:43:1
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-s
-s
-s
-bt
-s
-s
-s
-s
-b do_break_async_generator.js:21
-c
-bt
-c
-bt
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_async_generator.js:38
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:39
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:33 (in g() at line:32, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_async_generator.js:38:10
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:44
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:42 (in h() at line:41, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:43 (in h() at line:41, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_async_generator.js:46
-(escargot-debugger) b do_break_async_generator.js:21
-Breakpoint 1 at tools/debugger/tests/do_break_async_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_async_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_async_generator.js:25:12 [depth:1] (in f2)
-tools/debugger/tests/do_break_async_generator.js:38:10
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_async_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_async_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_async_generator.js:25:12 [depth:1] (in f2)
-tools/debugger/tests/do_break_async_generator.js:29:12 [depth:2] (in f3)
-tools/debugger/tests/do_break_async_generator.js:38:10
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-b do_break_class.js:37
-n
-b do_break_class.js:36
-c
-bt
-b do_break_class.js:66
-c
-c
-bt
-b do_break_class.js:69
-c
-b do_break_class.js:26
-b do_break_class.js:50
-b do_break_class.js:55
-c
-c
-bt
-c
-bt
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_class.js:20
-(escargot-debugger) b do_break_class.js:37
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-(escargot-debugger) b do_break_class.js:36
-Breakpoint 1 at tools/debugger/tests/do_break_class.js:36 (in function() at line:37, col:7)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_class.js:36 (in function() at line:37, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_class.js:37:6 [depth:0]
-tools/debugger/tests/do_break_class.js:22:14 [depth:1]
-(escargot-debugger) b do_break_class.js:66
-Breakpoint 2 at tools/debugger/tests/do_break_class.js:66 (in function() at line:66, col:24)
-Breakpoint 3 at tools/debugger/tests/do_break_class.js:66 (in function() at line:66, col:48)
-(escargot-debugger) c
-Stopped at breakpoint:2 tools/debugger/tests/do_break_class.js:66 (in function() at line:66, col:24)
-(escargot-debugger) c
-Stopped at breakpoint:3 tools/debugger/tests/do_break_class.js:66 (in function() at line:66, col:48)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_class.js:66:47 [depth:0]
-tools/debugger/tests/do_break_class.js:46:29 [depth:1]
-(escargot-debugger) b do_break_class.js:69
-Breakpoint 4 at tools/debugger/tests/do_break_class.js:69
-(escargot-debugger) c
-Stopped at breakpoint:4 tools/debugger/tests/do_break_class.js:69
-(escargot-debugger) b do_break_class.js:26
-Breakpoint 5 at tools/debugger/tests/do_break_class.js:26 (in function() at line:28, col:7)
-(escargot-debugger) b do_break_class.js:50
-Breakpoint 6 at tools/debugger/tests/do_break_class.js:50 (in function() at line:50, col:16)
-(escargot-debugger) b do_break_class.js:55
-Breakpoint 7 at tools/debugger/tests/do_break_class.js:55 (in constructor() at line:53, col:3)
-(escargot-debugger) c
-Stopped at breakpoint:7 tools/debugger/tests/do_break_class.js:55 (in constructor() at line:53, col:3)
-(escargot-debugger) c
-Stopped at breakpoint:5 tools/debugger/tests/do_break_class.js:26 (in function() at line:28, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_class.js:28:6 [depth:0]
-tools/debugger/tests/do_break_class.js:55:5 [depth:2] (in constructor)
-tools/debugger/tests/do_break_class.js:69:9 [depth:3]
-(escargot-debugger) c
-Stopped at breakpoint:6 tools/debugger/tests/do_break_class.js:50 (in function() at line:50, col:16)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_class.js:50:15 [depth:0]
-tools/debugger/tests/do_break_class.js:55:5 [depth:1] (in constructor)
-tools/debugger/tests/do_break_class.js:69:9 [depth:2]
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-b do_break_generator.js:21
-c
-bt
-c
-bt
-c
-bt
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_generator.js:38
-(escargot-debugger) b do_break_generator.js:21
-Breakpoint 1 at tools/debugger/tests/do_break_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_generator.js:39:1 [depth:3]
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_generator.js:25:12 [depth:1] (in f2)
-tools/debugger/tests/do_break_generator.js:42:3 [depth:4] (in h)
-tools/debugger/tests/do_break_generator.js:44:1 [depth:5]
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_generator.js:21 (in f1() at line:20, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_generator.js:21:5 [depth:0] (in f1)
-tools/debugger/tests/do_break_generator.js:25:12 [depth:1] (in f2)
-tools/debugger/tests/do_break_generator.js:29:12 [depth:2] (in f3)
-tools/debugger/tests/do_break_generator.js:46:1 [depth:5]
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-b
-b
-b
-b
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_no_arg.js:24
-(escargot-debugger) b
-Error: Argument expected
-(escargot-debugger) b
-Error: Argument expected
-(escargot-debugger) b
-Error: Argument expected
-(escargot-debugger) b
-Error: Argument expected
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-s
-s
-s
-b do_break_promise_then.js:24
-c
-bt
-c
-bt
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_break_promise_then.js:20
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_promise_then.js:21
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_promise_then.js:27
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_break_promise_then.js:41
-(escargot-debugger) b do_break_promise_then.js:24
-Breakpoint 1 at tools/debugger/tests/do_break_promise_then.js:24 (in f() at line:23, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_promise_then.js:24 (in f() at line:23, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_promise_then.js:24:5 [depth:0] (in f)
-tools/debugger/tests/do_break_promise_then.js:29:9 [depth:1]
-tools/debugger/tests/do_break_promise_then.js:28:5
-tools/debugger/tests/do_break_promise_then.js:27:1
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_break_promise_then.js:24 (in f() at line:23, col:1)
-(escargot-debugger) bt
-tools/debugger/tests/do_break_promise_then.js:24:5 [depth:0] (in f)
-tools/debugger/tests/do_break_promise_then.js:34:5 [depth:1] (in g)
-tools/debugger/tests/do_break_promise_then.js:38:5 (in h)
-tools/debugger/tests/do_break_promise_then.js:41:1
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_continue.js:21
-(escargot-debugger) continue
-Connection closed.
+++ /dev/null
-b do_delete_pending.js:21
-b pending1
-y
-b pending2
-y
-list
-delete pending
-list
-b pending3
-y
-delete all
-list
-quit
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_delete_pending.js:20
-(escargot-debugger) b do_delete_pending.js:21
-Breakpoint 1 at tools/debugger/tests/do_delete_pending.js:21
-(escargot-debugger) b pending1
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 2 at pending1()
-(escargot-debugger) b pending2
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 3 at pending2()
-(escargot-debugger) list
-=== Active breakpoints ===
- 1: tools/debugger/tests/do_delete_pending.js:21
-=== Pending breakpoints ===
- 2: pending1() (pending)
- 3: pending2() (pending)
-(escargot-debugger) delete pending
-(escargot-debugger) list
-=== Active breakpoints ===
- 1: tools/debugger/tests/do_delete_pending.js:21
-(escargot-debugger) b pending3
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 4 at pending3()
-(escargot-debugger) delete all
-(escargot-debugger) list
-No breakpoints
-(escargot-debugger) quit
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_exception.js:28
-(escargot-debugger) c
-Exception: RangeError: Test exception
-tools/debugger/tests/do_exception.js:21:10 [depth:0] (in g)
-tools/debugger/tests/do_exception.js:25:4 [depth:1] (in f)
-tools/debugger/tests/do_exception.js:28:1 [depth:2]
-Connection closed.
+++ /dev/null
-b do_finish.js:24
-c
-finish
-f
-b do_finish.js:41
-f
-f
-f
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_finish.js:20
-(escargot-debugger) b do_finish.js:24
-Breakpoint 1 at tools/debugger/tests/do_finish.js:24 (in f() at line:22, col:1)
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_finish.js:24 (in f() at line:22, col:1)
-(escargot-debugger) finish
-Stopped at tools/debugger/tests/do_finish.js:32 (in g() at line:29, col:1)
-(escargot-debugger) f
-Stopped at tools/debugger/tests/do_finish.js:38
-(escargot-debugger) b do_finish.js:41
-Breakpoint 2 at tools/debugger/tests/do_finish.js:41 (in gen() at line:40, col:1)
-(escargot-debugger) f
-Stopped at breakpoint:2 tools/debugger/tests/do_finish.js:41 (in gen() at line:40, col:1)
-(escargot-debugger) f
-Stopped at tools/debugger/tests/do_finish.js:46
-(escargot-debugger) f
-Connection closed.
+++ /dev/null
-b do_list.js:20
-b do_list.js:21
-list
-quit
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_list.js:20
-(escargot-debugger) b do_list.js:20
-Breakpoint 1 at tools/debugger/tests/do_list.js:20
-(escargot-debugger) b do_list.js:21
-Breakpoint 2 at tools/debugger/tests/do_list.js:21
-(escargot-debugger) list
-=== Active breakpoints ===
- 1: tools/debugger/tests/do_list.js:20
- 2: tools/debugger/tests/do_list.js:21
-(escargot-debugger) quit
-Connection closed.
+++ /dev/null
-s
-s
-s
-s
-variables 0
-object 0
-variables 1 0
-object 1
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_object.js:30
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_object.js:26 (in f() at line:25, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_object.js:27 (in f() at line:25, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_object.js:21 (in g() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_object.js:22 (in g() at line:20, col:1)
-(escargot-debugger) variables 0
-arr: array [id: 0]
-(escargot-debugger) object 0
-0: number: 1
-1: number: 2
-length: number: 2
-(escargot-debugger) variables 1 0
-obj: object [id: 1]
-(escargot-debugger) object 1
-a: number: 1
-b: number: 2
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-b f
-y
-b b
-y
-b non-existing-breakingpoint
-y
-list
-c
-list
-source
-c
-source
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_pending_breakpoints_functions.js:20
-(escargot-debugger) b f
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 1 at f()
-(escargot-debugger) b b
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 2 at b()
-(escargot-debugger) b non-existing-breakingpoint
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 3 at non-existing-breakingpoint()
-(escargot-debugger) list
-=== Pending breakpoints ===
- 1: f() (pending)
- 2: b() (pending)
- 3: non-existing-breakingpoint() (pending)
-(escargot-debugger) c
-Print: pending-breakpoints_functions
-Breakpoint 4 at eval input:2 (in f() at line:1, col:1)
-Breakpoint 5 at eval input:2 (in b() at line:1, col:1)
-Stopped at breakpoint:4 eval input:2 (in f() at line:1, col:1)
-(escargot-debugger) list
-=== Active breakpoints ===
- 4: eval input:2 (in f() at line:1, col:1)
- 5: eval input:2 (in b() at line:1, col:1)
-=== Pending breakpoints ===
- 3: non-existing-breakingpoint() (pending)
-(escargot-debugger) source
-Source: eval input
- 1 function f()
- 2 > { return 5 }
-(escargot-debugger) c
-Print: 5
-Stopped at breakpoint:5 eval input:2 (in b() at line:1, col:1)
-(escargot-debugger) source
-Source: eval input
- 1 function b()
- 2 > { return 8 }
-(escargot-debugger) c
-Print: 8
-Connection closed.
+++ /dev/null
-b :1
-y
-list
-c
-list
-source
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_pending_breakpoints_lines.js:20
-(escargot-debugger) b :1
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 1 at :1
-(escargot-debugger) list
-=== Pending breakpoints ===
- 1: :1 (pending)
-(escargot-debugger) c
-Print: pending-breakpoints_lines
-Breakpoint 2 at eval input:1
-Stopped at breakpoint:2 eval input:1
-(escargot-debugger) list
-=== Active breakpoints ===
- 2: eval input:1
-(escargot-debugger) source
-Source: eval input
- 1 > 1;var a = 1
-(escargot-debugger) c
-Print: 1
-Connection closed.
+++ /dev/null
-b not_existing_func
-y
-b 22
-y
-n
-n
-b not_existing.js:6
-y
-n
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_pending_breakpoints_unavailable.js:20
-(escargot-debugger) b not_existing_func
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 1 at not_existing_func()
-(escargot-debugger) b 22
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 2 at 22()
-(escargot-debugger) n
-Stopped at tools/debugger/tests/do_pending_breakpoints_unavailable.js:21
-(escargot-debugger) n
-Stopped at tools/debugger/tests/do_pending_breakpoints_unavailable.js:22
-(escargot-debugger) b not_existing.js:6
-No breakpoint found, do you want to add a pending breakpoint? (y or [n])
-Pending breakpoint 3 at not_existing.js:6
-(escargot-debugger) n
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_print.js:20
-(escargot-debugger) c
-Print: start
-Print: middle
-Print: end
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_quit.js:25
-(escargot-debugger) quit
-Connection closed.
+++ /dev/null
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_statements.js:61
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:21 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:22 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:23 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:24 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:28 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:31 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:34 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:35 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:35 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:35 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:35 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:38 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:39 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:39 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:43 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:45 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:48 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:49 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:52 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:53 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:56 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:57 (in f() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_statements.js:59 (in f() at line:20, col:1)
-(escargot-debugger) s
-Connection closed.
+++ /dev/null
-step
-step
-next
-next
-s
-n
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_step.js:37
-(escargot-debugger) step
-Stopped at tools/debugger/tests/do_step.js:27 (in f1() at line:20, col:1)
-(escargot-debugger) step
-Stopped at tools/debugger/tests/do_step.js:24 (in g() at line:22, col:3)
-(escargot-debugger) next
-Stopped at tools/debugger/tests/do_step.js:28 (in f1() at line:20, col:1)
-(escargot-debugger) next
-Stopped at tools/debugger/tests/do_step.js:29 (in f1() at line:20, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step.js:38
-(escargot-debugger) n
-Connection closed.
+++ /dev/null
-step
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
-s
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_step2.js:20
-(escargot-debugger) step
-Stopped at tools/debugger/tests/do_step2.js:21
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:20 (in arr() at line:20, col:11)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:27
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:28
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:29
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:35
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:36
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:37
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:39
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:40
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step2.js:41
-(escargot-debugger) s
-Connection closed.
+++ /dev/null
-s
-bt
-s
-s
-s
-s
-bt
-s
-s
-s
-s
-s
-s
-s
-bt
-s
-s
-s
-s
-s
-s
-bt
-s
-s
-s
-s
-s
-s
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_step_class.js:20
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:36 (in function() at line:37, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class.js:37:6 [depth:0]
-tools/debugger/tests/do_step_class.js:22:14 [depth:1]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:40 (in function() at line:42, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:43 (in function() at line:43, col:23)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:43 (in function() at line:43, col:46)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:60 (in function() at line:61, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class.js:61:6 [depth:0]
-tools/debugger/tests/do_step_class.js:46:29 [depth:1]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:64 (in function() at line:65, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:66 (in function() at line:66, col:24)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:66 (in function() at line:66, col:48)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:69
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:54 (in constructor() at line:53, col:3)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:55 (in constructor() at line:53, col:3)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:23 (in function() at line:24, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class.js:24:6 [depth:0]
-tools/debugger/tests/do_step_class.js:55:5 [depth:2] (in constructor)
-tools/debugger/tests/do_step_class.js:69:9 [depth:3]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:26 (in function() at line:28, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:29 (in function() at line:29, col:15)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:29 (in function() at line:29, col:30)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:32 (in constructor() at line:31, col:3)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:33 (in constructor() at line:31, col:3)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:47 (in function() at line:48, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class.js:48:6 [depth:0]
-tools/debugger/tests/do_step_class.js:55:5 [depth:1] (in constructor)
-tools/debugger/tests/do_step_class.js:69:9 [depth:2]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:50 (in function() at line:50, col:16)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:51 (in function() at line:51, col:16)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:51 (in function() at line:51, col:31)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:56 (in constructor() at line:53, col:3)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class.js:57 (in constructor() at line:53, col:3)
-(escargot-debugger) s
-Connection closed.
+++ /dev/null
-s
-bt
-s
-s
-s
-s
-bt
-s
-s
-s
-s
-s
-bt
-s
-s
-s
-s
-bt
-s
-s
-s
-s
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_step_class2.js:20
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:32 (in function() at line:33, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class2.js:33:6 [depth:0]
-tools/debugger/tests/do_step_class2.js:22:14 [depth:1]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:36 (in function() at line:38, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:39 (in function() at line:39, col:23)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:39 (in function() at line:39, col:46)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:50 (in function() at line:51, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class2.js:51:6 [depth:0]
-tools/debugger/tests/do_step_class2.js:42:29 [depth:1]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:54 (in function() at line:55, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:56 (in function() at line:56, col:24)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:56 (in function() at line:56, col:48)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:59
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:23 (in function() at line:24, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class2.js:24:6 [depth:0]
-unknown dynamic function:3:7 [depth:2]
-tools/debugger/tests/do_step_class2.js:59:9 [depth:3]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:26 (in function() at line:28, col:7)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:29 (in function() at line:29, col:15)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:29 (in function() at line:29, col:30)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:43 (in function() at line:44, col:7)
-(escargot-debugger) bt
-tools/debugger/tests/do_step_class2.js:44:6 [depth:0]
-unknown dynamic function:3:7 [depth:1]
-tools/debugger/tests/do_step_class2.js:59:9 [depth:2]
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:46 (in function() at line:46, col:16)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:47 (in function() at line:47, col:16)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_step_class2.js:47 (in function() at line:47, col:31)
-(escargot-debugger) s
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-SyntaxError: Line 21: Identifier 'a' has already been declared
-Connection closed.
+++ /dev/null
-s
-s
-scope
-variables
-variables 1
-s
-s
-s
-s
-scope
-variables
-variables 1
-s
-s
-scope
-variables
-s
-s
-s
-scope
-variables
-s
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_variables.js:21
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:23
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:24
-(escargot-debugger) scope
-Declarative Environment
-Declarative Environment
-Global Environment
-(escargot-debugger) variables
-c: number: 8
-d: unaccessible
-(escargot-debugger) variables 1
-a: number: 6
-b: unaccessible
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:26
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:35
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:30 (in f() at line:29, col:1)
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:32 (in f() at line:29, col:1)
-(escargot-debugger) scope
-Declarative Environment
-Function Environment
-Global Environment
-(escargot-debugger) variables
-c: undefined
-(escargot-debugger) variables 1
-a: number: 1
-b: number: 2
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:37
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:38
-(escargot-debugger) scope
-Object Environment
-Global Environment
-(escargot-debugger) variables
-p1: number: 1
-p2: number: 2
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:43
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:44
-(escargot-debugger) s
-Stopped at tools/debugger/tests/do_variables.js:45
-(escargot-debugger) scope
-Declarative Environment
-Global Environment
-(escargot-debugger) variables
-a1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567...: number: 1
-b: string: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678...
-(escargot-debugger) s
-Connection closed.
+++ /dev/null
-b do_variables2.js:33
-c
-variables
-c
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_variables2.js:22
-(escargot-debugger) b do_variables2.js:33
-Breakpoint 1 at tools/debugger/tests/do_variables2.js:33
-(escargot-debugger) c
-Stopped at breakpoint:1 tools/debugger/tests/do_variables2.js:33
-(escargot-debugger) variables
-a: undefined
-b: null
-c: boolean: true
-d: boolean: false
-e: number: 3.14
-f: string: str
-g: symbol: sym
-h: object [id: 0]
-i: array [id: 1]
-j: function [id: 2]
-l: bigint: 900719925474099123
-k: unaccessible
-(escargot-debugger) c
-Connection closed.
+++ /dev/null
-Connecting to: localhost:6501
-Connection created!!!
-Stopped at tools/debugger/tests/do_한글문자.js:20
-(escargot-debugger) c
-Print: 모음 쓰기 및 발음
-Print: 이중모음
-Print: 자음
-Print: 자음 발음
-Print: 음절 법칙
-Connection closed.
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2020-present Samsung Electronics 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.
-
-import subprocess
-import sys
-import os
-
-from argparse import ArgumentParser
-
-
-PARAGRAPH_SEP = "# ================================================\n"
-ID_START_TITLE = "# Derived Property: ID_Start\n"
-ID_CONTINUE_TITLE = "# Derived Property: ID_Continue\n"
-
-
-BASIC_PLANE_START = dict()
-LONG_RANGE_LENGHT = []
-BASIC_PLANE_CONTINUE = dict()
-
-
-SUPPLEMENTARY_PLANE = dict()
-MERGED_ID_START = dict()
-
-
-TERM_RED = '\033[1;31m'
-TERM_GREEN = '\033[1;32m'
-TERM_YELLOW = '\033[1;33m'
-TERM_EMPTY = '\033[0m'
-
-
-class UnicodeTable:
- def __init__(self):
- self.header = []
- self.data = []
- self.footer = []
-
- def add_header(self, header_text):
- self.header.append(header_text)
- self.header.append("")
-
-
- def add_footer(self, footer_text):
- self.footer.append(footer_text)
- self.footer.append("")
-
-
- def add_table(self, table, table_name, table_type, table_descr):
- self.data.append("/* %s */" % (table_descr))
- self.data.append("const %s identRange%s[%s] = {" % (table_type,table_name,len(table)))
- self.data.append(' ' + ', '.join(map(str, table)))
- self.data.append("};")
- self.data.append("")
-
- def generate(self):
- with open(os.path.dirname(os.path.abspath(__file__)) + "/../src/parser/UnicodeIdentifierTables.cpp", 'w') as unicode_table_file:
- unicode_table_file.write("\n".join([str(i) for i in self.header]))
- unicode_table_file.write("\n".join([str(i) for i in self.data]))
- unicode_table_file.write("\n".join([str(i) for i in self.footer]))
-
-
-def create_basic_plane_table():
-
- # zero_width_non_joiner and zero_width_joiner needs to be added here
- # http://www.ecma-international.org/ecma-262/10.0/#prod-IdentifierName
- # $ and _ are handled directly in the Escargot Lexer
- other_starters = [0x200C,0x200D]
- for key in other_starters:
- BASIC_PLANE_START[key] = 1
-
- # Create new merged dict
- for k, v in BASIC_PLANE_START.items():
- MERGED_ID_START[k] = v
-
- # Copy unicodes not present in base
- for continue_key in BASIC_PLANE_CONTINUE.keys():
- if not MERGED_ID_START.has_key(continue_key):
- MERGED_ID_START[continue_key] = BASIC_PLANE_CONTINUE[continue_key]
-
- # Merge ranges if possible
- # Get collection of keys
- ordered_keys = sorted(MERGED_ID_START.keys())
- for key in range(len(MERGED_ID_START)-1, 1, -1):
- # If key value is equal than
- if ordered_keys[key] == ordered_keys[key-1]+MERGED_ID_START[ordered_keys[key-1]]+1:
- # combine the length of the two
- # refresh key length value with the sum of the sperate values
- # pop next value out from the dict
- new_length = MERGED_ID_START[ordered_keys[key]] + MERGED_ID_START[ordered_keys[key-1]] + 1
- MERGED_ID_START[ordered_keys[key-1]] = new_length
- error = MERGED_ID_START.pop(ordered_keys[key])
-
- # Create the long range table
- long_length_index = 0
- for key, value in MERGED_ID_START.iteritems():
- if value >= 200:
- LONG_RANGE_LENGHT.append(value)
- new_value = 200+long_length_index
- long_length_index+=1
- MERGED_ID_START[key] = new_value
-
-
-def unify_non_basic_plane():
- # Merge ranges if possible
- # Get collection of keys
- ordered_keys = sorted(SUPPLEMENTARY_PLANE.keys())
- for key in range(len(SUPPLEMENTARY_PLANE)-1, 1, -1):
- # If key value is equal than
- if ordered_keys[key] == ordered_keys[key-1]+SUPPLEMENTARY_PLANE[ordered_keys[key-1]]+1:
- # Combine the length of the two
- # Refresh key length value with the sum of the sperate values
- # Pop next value out from the dict
- new_length = SUPPLEMENTARY_PLANE[ordered_keys[key]] + SUPPLEMENTARY_PLANE[ordered_keys[key-1]]
- SUPPLEMENTARY_PLANE[ordered_keys[key-1]] = new_length
- error = SUPPLEMENTARY_PLANE.pop(ordered_keys[key])
-
-
-def generate_ranges(file):
- bigger = 0
- long_range_index = 0
- with open(file, "r") as fp:
- line = fp.readline()
- while line:
- line = fp.readline()
- if (line == PARAGRAPH_SEP):
- # Read twice to skip empty line
- paragraph_title = fp.readline()
- paragraph_title = fp.readline()
- if (paragraph_title == ID_START_TITLE) or (paragraph_title == ID_CONTINUE_TITLE):
- long_range_index = 0
- # Skip 8 lines, containing definition of ID_START
- # or skip 9 in case of ID_CONTINUE
- skip = 8
- if (paragraph_title == ID_CONTINUE_TITLE):
- skip = 9
-
- for _ in range(skip):
- fp.readline()
- unicode_range_line = fp.readline()
- while (unicode_range_line != "\n"):
- sub_lines = ""
- ranges = ""
- # Cut out ranges
- sub_lines = unicode_range_line.split(';')
- # Cut ranges to check length
- # Convert into int
- ranges = sub_lines[0].split('..')
- start = (int(ranges[0],16))
-
- if (len(ranges) == 2):
- length = (int(ranges[1],16) - int(ranges[0],16))
- else:
- length = 1
-
- # Basic plane for Escargot's uint16_t
- # Skip the ascii part as it is handled directly
- # in the Lexer.cpp
- if (128 < start) and (start <= 65535):
- if skip == 8:
- if not BASIC_PLANE_START.has_key(start):
- BASIC_PLANE_START[start] = length
- else:
- if not BASIC_PLANE_CONTINUE.has_key(start):
- BASIC_PLANE_CONTINUE[start] = length
- # Supplementary Plane data
- elif start > 65535:
- SUPPLEMENTARY_PLANE[start] = length
- unicode_range_line = fp.readline()
-
-
-def main():
- print("\n%sAutomated Unicode Identifier Table Generator for Escargot %s\n" % (TERM_GREEN,TERM_EMPTY))
- parser = ArgumentParser(description='Kangax runner for the Escargot engine')
- parser.add_argument('--derived_core_properties', metavar='FILE', action='store', required=True, help='specify the unicode data file')
- args = parser.parse_args()
-
- if not os.path.isfile(args.derived_core_properties):
- parser.error("\n\n%sArgument file is non-existent!%s\nGiven: %s\n" % (TERM_RED,TERM_EMPTY,args.derived_core_properties))
-
- # Process Derived File
- print("%s..Processing Derived Core Properties file %s\n" % (TERM_YELLOW,TERM_EMPTY))
- generate_ranges(args.derived_core_properties)
-
- # Porcess data for the basic plane
- # and unify the supplementary
- print("%s..Calculating proper ranges %s\n" % (TERM_YELLOW,TERM_EMPTY))
- create_basic_plane_table()
- unify_non_basic_plane()
-
- # Start collecting output data
- print("%s..Collecting output data %s\n" % (TERM_YELLOW,TERM_EMPTY))
- generated_data = UnicodeTable()
- licence = "/* * Copyright (c) 2020-present Samsung Electronics Co., Ltd\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301\n * USA\n */\n"
- header = "/* This file is automatically generated by the %s script\n* from https://www.unicode.org/Public/13.0.0/ucd/DerivedCoreProperties.txt\n* DO NOT EDIT!\n*/" % (os.path.basename(__file__))
- ifdef_namespace ="#include \"UnicodeIdentifierTables.h\"\n\nnamespace Escargot {\nnamespace EscargotLexer {\n"
- footer_namespace = "const uint16_t basic_plane_length = sizeof(identRangeStart);\nconst uint16_t supplementary_plane_length = sizeof(identRangeStartSupplementaryPlane);\n}\n}"
-
- # Append Generated Warning, licence, ifdef
- generated_data.add_header(licence)
- generated_data.add_header(header)
- generated_data.add_header(ifdef_namespace)
-
- # Fetch data for the basic plane
- keys = sorted(MERGED_ID_START.keys())
- values = []
- for key in keys:
- values.append(MERGED_ID_START[key])
-
- # Append basic plane values
- generated_data.add_table(keys,"Start","uint16_t","Starting codepoints of identifier ranges.")
- generated_data.add_table(values,"Length","uint16_t","Lengths of identifier ranges.")
- generated_data.add_table(LONG_RANGE_LENGHT,"LongLength","uint16_t","Lengths of identifier ranges greater than LEXER IDENT_RANGE_LONG.")
-
- # Fetch data for the supplementary plane
- keys = sorted(SUPPLEMENTARY_PLANE.keys())
- values = []
- for key in keys:
- values.append(SUPPLEMENTARY_PLANE[key])
-
- # Append suppelmentary values
- generated_data.add_table(keys,"StartSupplementaryPlane","uint32_t","Identifier starting codepoints for the supplementary plane")
- generated_data.add_table(values,"LengthSupplementaryPlane","uint16_t","Lengths of identifier ranges for the supplementary plane")
- # Append footer
- generated_data.add_footer(footer_namespace)
-
- # Writeout data
- print("%s..Writing out data %s\n" % (TERM_YELLOW,TERM_EMPTY))
- generated_data.generate()
- print("\n%sTables generated into src/parser/UnicodeIdentifierTables.cpp %s\n" % (TERM_GREEN,TERM_EMPTY))
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#!/usr/bin/env bash
-
-# Copyright 2021-present Samsung Electronics 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.
-
-UCD_URL="https://www.unicode.org/Public/UCD/latest/ucd/UCD.zip"
-WEBKIT_GENERATOR_URL="https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/yarr/generateYarrUnicodePropertyTables.py"
-WEBKIT_HASHER_URL="https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/yarr/hasher.py"
-PATTERN_FILE_PATH="./third_party/yarr/UnicodePatternTables.h"
-
-echo -e "\nAutomated Unicode Pattern Table Tool for Escargot \n"
-
-#Fetching data and scripts
-echo -e "\n\nCurl UCD data"
-curl $UCD_URL -O
-
-echo -e "\n\nCurl Webkit pattern generator script"
-curl $WEBKIT_GENERATOR_URL -o "generateYarrUnicodePropertyTables.py"
-
-echo -e "\n\nCurl Hasher for Generator"
-curl $WEBKIT_HASHER_URL -o "hasher.py"
-
-echo -e "\n\nUnzip and move UCD files"
-unzip ./UCD.zip -d UCD
-mv ./UCD/emoji/emoji-data.txt ./UCD
-mv ./UCD/extracted/* ./UCD
-
-echo -e "\n\nGenerate new patter table"
-chmod +x generateYarrUnicodePropertyTables.py
-./generateYarrUnicodePropertyTables.py ./UCD $PATTERN_FILE_PATH
-
-
-#Reformatting for Escargot Usage
-echo -e "\nReformat for Escargot"
-sed -i 's/makeUnique/std::make_unique/g' $PATTERN_FILE_PATH
-grep -v "generated" $PATTERN_FILE_PATH > temp_file; mv temp_file $PATTERN_FILE_PATH
-
-perl -0777 -i -p -e 's/,\n CharacterClassWidths::HasBothBMPAndNonBMP\);/\);\n characterClass->m_hasNonBMPCharacters = true;/g' $PATTERN_FILE_PATH
-perl -0777 -i -p -e 's/,\n CharacterClassWidths::HasBMPChars\);/\);\n characterClass->m_hasNonBMPCharacters = false;/g' $PATTERN_FILE_PATH
-perl -0777 -i -p -e 's/,\n CharacterClassWidths::HasNonBMPChars\);/\);\n characterClass->m_hasNonBMPCharacters = true;/g' $PATTERN_FILE_PATH
-sed -i 's/\s*$//g' $PATTERN_FILE_PATH
-
-#Cleanup
-echo -e "\nCleaning up files"
-rm -rf ./UCD
-rm ./UCD.zip
-rm generateYarrUnicodePropertyTables.py
-rm hasher.py
+++ /dev/null
-diff --git a/environments.json b/environments.json
-index f7b14fd7..eca521de 100644
---- a/environments.json
-+++ b/environments.json
-@@ -3204,6 +3204,18 @@
- "es6"
- ]
- },
-+ "escargot": {
-+ "full": "Escargot Master",
-+ "family": "Escargot",
-+ "short": "Esc",
-+ "platformtype": "engine",
-+ "release": "2019-11-15",
-+ "obsolete": false,
-+ "test_suites": [
-+ "es6",
-+ "es2016plus"
-+ ]
-+ },
- "nashorn1_8": {
- "full": "Oracle Nashorn 1.8",
- "family": "Nashorn",
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2020-present Samsung Electronics 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.
-
-import subprocess
-import sys
-import os
-
-from argparse import ArgumentParser
-
-PATH = os.path.dirname(os.path.abspath(__file__))
-
-TERM_RED = '\033[1;31m'
-TERM_GREEN = '\033[1;32m'
-TERM_YELLOW = '\033[1;33m'
-TERM_EMPTY = '\033[0m'
-
-
-def separator_line():
- print("{0}=====================================\n {1}".format(TERM_YELLOW, TERM_EMPTY))
-
-
-def get_results(filename, no_of_lines=1):
- res = []
- file = open(filename,'r')
- lines = file.readlines()
- last_lines = lines[-no_of_lines:]
- for line in last_lines:
- res.append(line)
- file.close()
- return res
-
-
-def run_testsuite(suite,engine):
- separator_line()
- print("{0}Running kangax suite(s):\n {1}{2}".format(TERM_YELLOW, 'es6\n es2016plus\n' if suite=='""' else suite, TERM_EMPTY))
- separator_line()
-
- if engine != 'local':
- engine = engine
- else:
- engine = '../../escargot'
-
- os.chdir(PATH + '/../../test/kangax/')
- if suite == '""':
- es6res = open("./kangaxES6Res.txt", "w")
- es6plusres = open("./kangaxES6PLUSRes.txt", "w")
- nodeFails = open("./kangaxRunErrors.txt", "w")
- subprocess.call(['node', './escargot.js', engine, 'es6'], stdout = es6res, stderr = nodeFails)
- print("{0}ES6 RESULTS:\n{1}".format(TERM_YELLOW, TERM_EMPTY))
- grepProc = subprocess.Popen(['grep -hr "actual: false" ./kangaxES6Res.txt'], stdout=subprocess.PIPE, shell=True)
- (fails, err) = grepProc.communicate()
- print(''.join(get_results("./kangaxES6Res.txt", 2)))
- print("{0}ES6 Fails:\n{1}".format(TERM_RED, TERM_EMPTY))
- print(''.join(fails))
- print("{0}Errors:\n{1}".format(TERM_RED, TERM_EMPTY))
- es6res.close()
- nodeFails.close()
- with open("./kangaxRunErrors.txt", "r") as lines:
- print(lines.read())
- print('\n')
-
- nodeFails = open("./kangaxRunErrors.txt", "w")
- subprocess.call(['node', './escargot.js', engine, 'es2016plus'], stdout = es6plusres, stderr = nodeFails)
- print("{0}ES6PLUS RESULTS:\n{1}".format(TERM_YELLOW, TERM_EMPTY))
- grepProc = subprocess.Popen(['grep -hr "actual: false" ./kangaxES6PLUSRes.txt'], stdout=subprocess.PIPE, shell=True)
- (fails, err) = grepProc.communicate()
- print(''.join(get_results("./kangaxES6PLUSRes.txt", 2)))
- print("{0}ES6PLUS FAILS:\n{1}".format(TERM_RED, TERM_EMPTY))
- print(''.join(fails))
- print("{0}Errors:\n{1}".format(TERM_RED, TERM_EMPTY))
- es6plusres.close()
- nodeFails.close()
- with open("./kangaxRunErrors.txt", "r") as lines:
- print(lines.read())
- print('\n')
- else:
- res = open("./kangaxRes.txt", "w")
- nodeFails = open("./kangaxRunErrors.txt", "w")
- subprocess.call(['node', './escargot.js', engine, suite], stdout = res, stderr = nodeFails)
- grepProc = subprocess.Popen(['grep -hr "actual: false" ./kangaxRes.txt'], stdout=subprocess.PIPE, shell=True)
- (fails, err) = grepProc.communicate()
- print("{0}{1} RESULTS:\n\n{2}".format(TERM_YELLOW, suite, TERM_EMPTY))
- print( ''.join(get_results("./kangaxRes.txt", 2)))
- print("{0}FAILS:\n{1}".format(TERM_RED, TERM_EMPTY))
- print(''.join(fails))
- print("{0}Errors:\n{1}".format(TERM_RED, TERM_EMPTY))
- res.close()
- nodeFails.close()
- with open("./kangaxRunErrors.txt", "r") as lines:
- print(lines.read())
- print('\n')
-
-
-def kangax_setup():
- os.chdir(PATH + '/../../test/')
- if os.path.isdir("./kangax/"):
- print("{0}/test/kangax directory is present\nAssuming correct repository there{1}\n".format(TERM_YELLOW, TERM_EMPTY))
- else:
- print("{0}Update kangax repository\n{1}{2}\n".format(TERM_YELLOW, PATH + '/../../test/' + 'kangax', TERM_EMPTY))
- os.system("git submodule update {0}".format(PATH + '/../../test/kangax'))
-
-
- separator_line()
- print("{0}Patching kangax enviroment\n applying patch{1}".format(TERM_YELLOW, TERM_EMPTY))
- os.chdir(PATH + '/../../test/kangax')
- os.system('git apply {0}'.format(PATH + '/escargot.patch'))
- os.chdir(PATH)
- print("{0} adding Escargot runner{1}\n".format(TERM_YELLOW, TERM_EMPTY))
- os.system('cp {0} {1}'.format(PATH + '/escargot.js',PATH + '/../../test/kangax/'))
-
-
-
-def main():
- parser = ArgumentParser(description='Kangax runner for the Escargot engine')
- parser.add_argument('--suite', nargs='?', default='""', choices=['es6', 'es2016plus'], help='Run kangax suite (choices: %(choices)s)')
- parser.add_argument('--engine', nargs='?', default='local', help='Define escargot path. Leave empty for project root ./escargot file')
- args = parser.parse_args()
-
- separator_line()
- print("{0}KANGAX RUNNER FOR \nESCARGOT REPOSITORY\n{1}".format(TERM_YELLOW, TERM_EMPTY))
- separator_line()
- kangax_setup()
- run_testsuite(args.suite,args.engine)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2018-present Samsung Electronics 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.
-
-from __future__ import print_function
-
-import os
-import traceback
-import sys
-import time
-import re, fnmatch
-
-from argparse import ArgumentParser
-from difflib import unified_diff
-from glob import glob
-from os.path import abspath, basename, dirname, join, relpath
-from shutil import copy
-from subprocess import PIPE, Popen
-
-
-PROJECT_SOURCE_DIR = dirname(dirname(abspath(__file__)))
-DEFAULT_ESCARGOT = join(PROJECT_SOURCE_DIR, 'escargot')
-
-
-COLOR_RED = '\033[31m'
-COLOR_GREEN = '\033[32m'
-COLOR_YELLOW = '\033[33m'
-COLOR_BLUE = '\033[34m'
-COLOR_PURPLE = '\033[35m'
-COLOR_RESET = '\033[0m'
-
-
-RUNNERS = {}
-DEFAULT_RUNNERS = []
-
-
-class runner(object):
-
- def __init__(self, suite, default=False):
- self.suite = suite
- self.default = default
-
- def __call__(self, fn):
- RUNNERS[self.suite] = fn
- if self.default:
- DEFAULT_RUNNERS.append(self.suite)
- return fn
-
-
-def run(args, cwd=None, env=None, stdout=None, checkresult=True, report=False):
- if cwd:
- print(COLOR_BLUE + 'cd ' + cwd + ' && \\' + COLOR_RESET)
- if env:
- for var, val in sorted(env.items()):
- print(COLOR_BLUE + var + '=' + val + ' \\' + COLOR_RESET)
- print(COLOR_BLUE + ' '.join(args) + COLOR_RESET)
-
- if env is not None:
- full_env = dict(os.environ)
- full_env.update(env)
- env = full_env
-
- proc = Popen(args, cwd=cwd, env=env, stdout=PIPE if report else stdout)
-
- counter = 0
-
- while report:
- nextline = proc.stdout.readline()
- if nextline == '' and proc.poll() is not None:
- break
- stdout.write(nextline)
- stdout.flush()
- if counter % 250 == 0:
- print('Ran %d tests..' % (counter))
- counter += 1
-
- out, _ = proc.communicate()
-
- if out:
- print(out)
-
- if checkresult and proc.returncode:
- raise Exception('command `%s` exited with %s' % (' '.join(args), proc.returncode))
- return out
-
-
-def readfile(filename):
- with open(filename, 'r') as f:
- return f.readlines()
-
-
-@runner('sunspider')
-def run_sunspider(engine, arch):
- run([join('.', 'sunspider'),
- '--shell', engine,
- '--suite', 'sunspider-1.0.2'],
- cwd=join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'SunSpider'))
-
-
-@runner('sunspider-js', default=True)
-def run_sunspider_js(engine, arch):
- run([engine] + sorted(glob(join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'SunSpider', 'tests', 'sunspider-1.0.2', '*.js'))))
-
-
-@runner('octane', default=True)
-def run_octane(engine, arch):
- max_retry_count = 5
- try_count = 0
- last_error = None
- while try_count < max_retry_count:
- try:
- OCTANE_DIR = join(PROJECT_SOURCE_DIR, 'test', 'octane')
-
- stdout = run(['/usr/bin/time', '-f', '%M', '-o', 'mem.txt', engine, 'run.js'],
- cwd=OCTANE_DIR,
- stdout=PIPE)
- f = open(join(OCTANE_DIR, 'mem.txt'))
- for s in f:
- rss = s.strip("\n")
- mem = int(rss)
- print('Octane maximum resident set size: ' + str(mem) + 'KB')
-
- if arch == str('x86_64') and mem > 250000:
- raise Exception('Exceed memory consumption')
- if arch == str('x86') and mem > 150000:
- raise Exception('Exceed memory consumption')
-
- if 'Score' not in stdout:
- raise Exception('no "Score" in stdout')
- return
- except Exception as e:
- last_error = e
- try_count = try_count + 1
-
-
- raise last_error
-
-
-@runner('octane-loading', default=True)
-def run_octane_loading(engine, arch):
- OCTANE_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'octane')
- OCTANE_DIR = join(PROJECT_SOURCE_DIR, 'test', 'octane')
- copy(join(OCTANE_OVERRIDE_DIR, 'runLoading.js'), join(OCTANE_DIR, 'runLoading.js'))
-
- run([engine, 'runLoading.js'],
- cwd=OCTANE_DIR)
-
-
-@runner('modifiedVendorTest', default=True)
-def run_internal_test(engine, arch):
- INTERNAL_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'ModifiedVendorTest')
- INTERNAL_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'ModifiedVendorTest')
-
- copy(join(INTERNAL_OVERRIDE_DIR, 'internal-test-cases.txt'), join(INTERNAL_DIR, 'internal-test-cases.txt'))
- copy(join(INTERNAL_OVERRIDE_DIR, 'internal-test-driver.py'), join(INTERNAL_DIR, 'driver.py'))
-
- run(['python', 'driver.py', engine, 'internal-test-cases.txt'],
- cwd=INTERNAL_DIR)
-
-
-@runner('test262', default=True)
-def run_test262(engine, arch):
- TEST262_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'test262')
- TEST262_DIR = join(PROJECT_SOURCE_DIR, 'test', 'test262')
-
- copy(join(TEST262_OVERRIDE_DIR, 'excludelist.orig.xml'), join(TEST262_DIR, 'excludelist.xml'))
- copy(join(TEST262_OVERRIDE_DIR, 'cth.js'), join(TEST262_DIR, 'harness', 'cth.js'))
- copy(join(TEST262_OVERRIDE_DIR, 'testIntl.js'), join(TEST262_DIR, 'harness', 'testIntl.js'))
-
- copy(join(TEST262_OVERRIDE_DIR, 'parseTestRecord.py'), join(TEST262_DIR, 'tools', 'packaging', 'parseTestRecord.py'))
- copy(join(TEST262_OVERRIDE_DIR, 'test262.py'), join(TEST262_DIR, 'tools', 'packaging', 'test262.py')) # for parallel running (we should re-implement this for es6 suite)
-
- stdout = run(['pypy', join('tools', 'packaging', 'test262.py'),
- '--command', engine,
- '--full-summary'],
- cwd=TEST262_DIR,
- env={'TZ': 'US/Pacific'},
- stdout=PIPE)
-
- summary = stdout.split('=== Test262 Summary ===')[1]
- if summary.find('- All tests succeeded') < 0:
- raise Exception('test262 failed')
- print('test262: All tests passed')
-
-@runner('test262-strict', default=True)
-def run_test262_strict(engine, arch):
- TEST262_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'test262')
- TEST262_DIR = join(PROJECT_SOURCE_DIR, 'test', 'test262')
-
- copy(join(TEST262_OVERRIDE_DIR, 'excludelist.orig.xml'), join(TEST262_DIR, 'excludelist.xml'))
- copy(join(TEST262_OVERRIDE_DIR, 'test262.py'), join(TEST262_DIR, 'tools', 'packaging', 'test262.py')) # for parallel running (we should re-implement this for es6 suite)
-
- out = open('test262-strict_out', 'w')
-
- run(['pypy', join('tools', 'packaging', 'test262.py'),
- '--command', engine,
- '--full-summary',
- '--strict_only'],
- cwd=TEST262_DIR,
- env={'TZ': 'US/Pacific'},
- stdout=out,
- report=True)
-
- out.close()
-
- with open('test262-strict_out', 'r') as out:
- full = out.read()
- summary = full.split('=== Summary ===')[1]
- if summary.find('- All tests succeeded') < 0:
- print(summary)
- raise Exception('test262-strict failed')
-
- print('test262-strict: All tests passed')
-
-
-@runner('test262-nonstrict', default=True)
-def run_test262_nonstrict(engine, arch):
- TEST262_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'test262')
- TEST262_DIR = join(PROJECT_SOURCE_DIR, 'test', 'test262')
-
- copy(join(TEST262_OVERRIDE_DIR, 'excludelist.orig.xml'), join(TEST262_DIR, 'excludelist.xml'))
- copy(join(TEST262_OVERRIDE_DIR, 'test262.py'), join(TEST262_DIR, 'tools', 'packaging', 'test262.py')) # for parallel running (we should re-implement this for es6 suite)
-
- out = open('test262-nonstrict_out', 'w')
-
- run(['pypy', join('tools', 'packaging', 'test262.py'),
- '--command', engine,
- '--full-summary',
- '--non_strict_only'],
- cwd=TEST262_DIR,
- env={'TZ': 'US/Pacific'},
- stdout=out,
- report=True)
-
- out.close()
-
- with open('test262-nonstrict_out', 'r') as out:
- full = out.read()
- summary = full.split('=== Summary ===')[1]
- if summary.find('- All tests succeeded') < 0:
- print(summary)
- raise Exception('test262-nonstrict failed')
-
- print('test262-nonstrict: All tests passed')
-
-
-@runner('spidermonkey', default=True)
-def run_spidermonkey(engine, arch):
- SPIDERMONKEY_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'spidermonkey')
- SPIDERMONKEY_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'SpiderMonkey')
-
- run([join(SPIDERMONKEY_DIR, 'jstests.py'),
- '--no-progress', '-s',
- '--timeout', '500',
- '--xul-info', '%s-gcc3:Linux:false' % arch,
- '--exclude-file', join(SPIDERMONKEY_OVERRIDE_DIR, 'excludelist.txt'),
- engine,
- '--output-file', join(SPIDERMONKEY_OVERRIDE_DIR, '%s.log.txt' % arch),
- '--failure-file', join(SPIDERMONKEY_OVERRIDE_DIR, '%s.gen.txt' % arch),
- 'non262/Array', 'non262/ArrayBuffer', 'non262/arrow-functions', 'non262/BigInt', 'non262/Boolean', 'non262/class', 'non262/comprehensions', 'non262/DataView', 'non262/Date',
- 'non262/destructuring', 'non262/Error', 'non262/eval', 'non262/Exceptions', 'non262/execution-contexts', 'non262/expressions', 'non262/extensions', 'non262/fields', 'non262/Function',
- 'non262/GC', 'non262/generators', 'non262/get-set', 'non262/global', 'non262/Intl', 'non262/iterable', 'non262/jit', 'non262/JSON', 'non262/lexical', 'non262/lexical-conventions',
- 'non262/lexical-environment', 'non262/literals', 'non262/Map', 'non262/Math', 'non262/misc', 'non262/module', 'non262/Number', 'non262/object', 'non262/operators', 'non262/pipeline',
- 'non262/Promise', 'non262/Proxy', 'non262/Reflect', 'non262/reflect-parse', 'non262/RegExp', 'non262/regress', 'non262/Scope', 'non262/Script', 'non262/Set', 'non262/statements',
- 'non262/strict', 'non262/String', 'non262/Symbol', 'non262/syntax', 'non262/template-strings', 'non262/TypedArray', 'non262/TypedObject', 'non262/types', 'non262/Unicode', 'non262/WeakMap'],
- env={'LOCALE': 'en_US'})
-
- orig = sorted(readfile(join(SPIDERMONKEY_OVERRIDE_DIR, '%s.orig.txt' % arch)))
- gen = sorted(readfile(join(SPIDERMONKEY_OVERRIDE_DIR, '%s.gen.txt' % arch)))
- diff = list(unified_diff(orig, gen))
- if diff:
- for diffline in diff:
- print(diffline)
- raise Exception('failure files differ')
-
-
-@runner('jsc-stress', default=True)
-def run_jsc_stress(engine, arch):
- JSC_DIR = join('test', 'vendortest', 'driver')
-
- run([join(JSC_DIR, 'driver.py'),
- '-s', 'stress',
- '-e', engine,
- '-a', arch],
- cwd=PROJECT_SOURCE_DIR,
- env={'PYTHONPATH': '.'})
-
-
-def _run_regression_tests(engine, assert_js, files, is_fail):
- fails = 0
- for file in files:
- proc = Popen([engine, assert_js, file], stdout=PIPE)
- out, _ = proc.communicate()
-
- if is_fail and proc.returncode or not is_fail and not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
- print(out)
-
- fails += 1
-
- return fails
-
-
-@runner('regression-tests', default=True)
-def run_regression_tests(engine, arch):
- REGRESSION_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'Escargot', 'regression-tests')
- REGRESSION_XFAIL_DIR = join(REGRESSION_DIR, 'xfail')
- REGRESSION_ASSERT_JS = join(REGRESSION_DIR, 'assert.js')
-
- print('Running regression tests:')
- xpass = glob(join(REGRESSION_DIR, 'issue-*.js'))
- xpass_result = _run_regression_tests(engine, REGRESSION_ASSERT_JS, xpass, False)
-
- print('Running regression tests expected to fail:')
- xfail = glob(join(REGRESSION_XFAIL_DIR, 'issue-*.js'))
- xfail_result = _run_regression_tests(engine, REGRESSION_ASSERT_JS, xfail, True)
-
- tests_total = len(xpass) + len(xfail)
- fail_total = xfail_result + xpass_result
- print('TOTAL: %d' % (tests_total))
- print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET))
- print('%sFAIL : %d%s' % (COLOR_RED, fail_total, COLOR_RESET))
-
- if fail_total > 0:
- raise Exception("Regression tests failed")
-
-
-def _run_jetstream(engine, target_test):
- JETSTREAM_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'jetstream')
- JETSTREAM_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'JetStream-1.1')
-
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.CDjsSetup.js'), join(JETSTREAM_DIR, 'CDjsSetup.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.OctaneSetup.js'), join(JETSTREAM_DIR, 'OctaneSetup.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.Octane2Setup.js'), join(JETSTREAM_DIR, 'Octane2Setup.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.SimpleSetup.js'), join(JETSTREAM_DIR, 'SimpleSetup.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.SunSpiderSetup.js'), join(JETSTREAM_DIR, 'SunSpiderSetup.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.cdjs.util.js'), join(JETSTREAM_DIR, 'cdjs', 'util.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.runOnePlan.js'), join(JETSTREAM_DIR, 'runOnePlan.js'))
- copy(join(JETSTREAM_OVERRIDE_DIR, 'jetstream.run.sh'), join(JETSTREAM_DIR, 'run.sh'))
-
- run([join('.', 'run.sh'), engine, target_test],
- cwd=JETSTREAM_DIR)
- run(['python', join(JETSTREAM_OVERRIDE_DIR, 'parsingResults.py'),
- join(JETSTREAM_OVERRIDE_DIR, 'jetstream-result-raw.res'),
- target_test])
- if 'NaN' in ''.join(readfile(join(JETSTREAM_OVERRIDE_DIR, 'jetstream-result-raw.res'))):
- raise Exception('result contains "NaN"')
-
-
-@runner('jetstream-only-simple-parallel-1')
-def run_jetstream_only_simple_parallel_1(engine, arch):
- _run_jetstream(engine, 'simple-1')
-
-
-@runner('jetstream-only-simple-parallel-2')
-def run_jetstream_only_simple_parallel_2(engine, arch):
- _run_jetstream(engine, 'simple-2')
-
-
-@runner('jetstream-only-simple-parallel-3')
-def run_jetstream_only_simple_parallel_3(engine, arch):
- _run_jetstream(engine, 'simple-3')
-
-
-@runner('jetstream-only-simple', default=True)
-def run_jetstream_only_simple(engine, arch):
- _run_jetstream(engine, 'simple')
-
-
-@runner('jetstream-only-cdjs', default=True)
-def run_jetstream_only_cdjs(engine, arch):
- _run_jetstream(engine, 'cdjs')
-
-
-@runner('jetstream-only-sunspider')
-def run_jetstream_only_sunspider(engine, arch):
- _run_jetstream(engine, 'sunspider')
-
-
-@runner('jetstream-only-octane')
-def run_jetstream_only_octane(engine, arch):
- _run_jetstream(engine, 'octane')
-
-
-@runner('chakracore', default=True)
-def run_chakracore(engine, arch):
- CHAKRACORE_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'chakracore')
- CHAKRACORE_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'ChakraCore')
-
- copy(join(CHAKRACORE_OVERRIDE_DIR, 'chakracore.run.sh'), join(CHAKRACORE_DIR, 'run.sh'))
- copy(join(CHAKRACORE_OVERRIDE_DIR, 'chakracore.include.js'), join(CHAKRACORE_DIR, 'include.js'))
- copy(join(CHAKRACORE_OVERRIDE_DIR, 'chakracore.rlexedirs.xml'), join(CHAKRACORE_DIR, 'rlexedirs.xml'))
- for rlexexml in glob(join(CHAKRACORE_OVERRIDE_DIR, '*.rlexe.xml')):
- dirname, _, filename = basename(rlexexml).partition('.')
- copy(rlexexml, join(CHAKRACORE_DIR, dirname, filename))
- for js in [join(CHAKRACORE_DIR, 'DynamicCode', 'eval-nativecodedata.js'),
- join(CHAKRACORE_DIR, 'utf8', 'unicode_digit_as_identifier_should_work.js')]:
- with open(js, 'a') as f:
- f.write("WScript.Echo('PASS');")
-
- stdout = run(['bash', join(CHAKRACORE_DIR, 'run.sh'), relpath(engine)],
- stdout=PIPE)
- with open(join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'driver', 'chakracore.%s.gen.txt' % arch), 'w') as gen_txt:
- gen_txt.write(stdout)
- run(['diff',
- join(CHAKRACORE_OVERRIDE_DIR, 'chakracore.%s.orig.txt' % arch),
- join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'driver', 'chakracore.%s.gen.txt' % arch)])
-
-
-@runner('v8', default=True)
-def run_v8(engine, arch):
- V8_OVERRIDE_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'v8')
- V8_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'v8')
-
- copy(join(V8_OVERRIDE_DIR, 'v8.mjsunit.status'), join(V8_DIR, 'test', 'mjsunit', 'mjsunit.status'))
- copy(join(V8_OVERRIDE_DIR, 'v8.mjsunit.js'), join(V8_DIR, 'test', 'mjsunit', 'mjsunit.js'))
- copy(join(V8_OVERRIDE_DIR, 'v8.run-tests.py'), join(V8_DIR, 'tools', 'run-tests.py'))
- copy(join(V8_OVERRIDE_DIR, 'v8.testsuite.py'), join(V8_DIR, 'tools', 'testrunner', 'local', 'testsuite.py'))
- copy(join(V8_OVERRIDE_DIR, 'v8.execution.py'), join(V8_DIR, 'tools', 'testrunner', 'local', 'execution.py'))
- copy(join(V8_OVERRIDE_DIR, 'v8.progress.py'), join(V8_DIR, 'tools', 'testrunner', 'local', 'progress.py'))
-
- arch = {'x86': 'x32', 'x86_64': 'x64'}[arch]
-
- if (engine == "escargot"):
- shell_str = "../../../escargot"
- else:
- shell_str = engine
-
- stdout = run([join(V8_DIR, 'tools', 'run-tests.py'),
- '--timeout=120',
- '--quickcheck',
- '--no-presubmit', '--no-variants',
- '--arch-and-mode=%s.release' % arch,
- '--shell', shell_str,
- '--escargot',
- '--report',
- '-p', 'verbose',
- '--no-sorting',
- '--no-network',
- 'mjsunit'],
- stdout=PIPE,
- checkresult=False)
- # FIXME: V8 test runner tends to exit with 2 (most probably the result of
- # a `self.remaining != 0` in `testrunner.local.execution.Runner.Run()`.
- # Couldn't find out yet why that happens.
- # NOTE: This has also been suppressed in cmake/make-based executors by
- # piping the output of run-test.py into a tee. In case of pipes, the
- # observed exit code is that of the last element of the pipe, which is
- # tee in that case (which is always 0).
-
- if '=== All tests succeeded' not in stdout:
- raise Exception('Not all tests succeeded')
-
-@runner('new-es', default=True)
-def run_new_es(engine, arch):
- NEW_ES_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'Escargot', 'new-es')
- NEW_ES_ASSERT_JS = join(NEW_ES_DIR, 'assert.js')
-
- print('Running new-es test:')
- files = glob(join(NEW_ES_DIR, '*.js'))
- files.remove(NEW_ES_ASSERT_JS)
- fail_total = 0
- for file in files:
- proc = Popen([engine, NEW_ES_ASSERT_JS, file], stdout=PIPE)
- out, _ = proc.communicate()
-
- if not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
- print(out)
- fail_total += 1
-
- tests_total = len(files)
- print('TOTAL: %d' % (tests_total))
- print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET))
- print('%sFAIL : %d%s' % (COLOR_RED, fail_total, COLOR_RESET))
-
- if fail_total > 0:
- raise Exception('new-es tests failed')
-
-@runner('intl', default=True)
-def run_intl(engine, arch):
- INTL_DIR = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'Escargot', 'intl')
- INTL_ASSERT_JS = join(INTL_DIR, 'assert.js')
-
- print('Running Intl test:')
- files = glob(join(INTL_DIR, '*.js'))
- files.remove(INTL_ASSERT_JS)
- fails = 0
- for file in files:
- proc = Popen([engine, INTL_ASSERT_JS, file], stdout=PIPE)
- out, _ = proc.communicate()
-
- if not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
- print(out)
- fails += 1
-
- if fails > 0:
- raise Exception('Intl tests failed')
-
-@runner('wasm-js', default=False)
-def run_wasm_js(engine, arch):
- WASM_TEST_ROOT = join(PROJECT_SOURCE_DIR, 'test', 'vendortest', 'wasm-js')
- WASM_TEST_DIR = join(WASM_TEST_ROOT, 'tests')
- WASM_TEST_MJS = join(WASM_TEST_ROOT, 'mjsunit.js')
- WASM_TEST_HARNESS = join(WASM_TEST_ROOT, 'testharness.js')
-
- copy(join(PROJECT_SOURCE_DIR, 'tools', 'test', 'wasm-js', 'grow-part.any.js'), join(WASM_TEST_DIR, 'memory', 'grow-part.any.js'))
-
-
- EXCLUDE_LIST_FILE = join(PROJECT_SOURCE_DIR, 'tools', 'test', 'wasm-js', 'exclude_list.txt')
- exclude_list = []
- with open(EXCLUDE_LIST_FILE) as f:
- exclude_list = f.read().replace('\n', ' ').split()
-
- files = []
- for root, dirnames, filenames in os.walk(join(WASM_TEST_DIR)):
- if "proposals" in root:
- # skip proposals test
- continue
- for filename in fnmatch.filter(filenames, '*.any.js'):
- full_path = join(root, filename)
- rel_path = full_path[len(WASM_TEST_DIR)+1:]
- if rel_path in exclude_list:
- continue
- files.append(join(root, filename))
- files = sorted(files)
-
- WPT_ROOT = "/wasm/jsapi/"
- META_SCRIPT_REGEXP = re.compile(r"META:\s*script=(.*)")
- fail_total = 0
- for file in files:
- source = ""
- with open(file) as f:
- source = f.read()
-
- script_files = []
- for script in META_SCRIPT_REGEXP.findall(source):
- if (script.startswith(WPT_ROOT)):
- script = join(WASM_TEST_DIR, script[len(WPT_ROOT):])
- else:
- script = join(WASM_TEST_DIR, script)
- script_files.append(script)
-
- script_files.append(file)
- proc = Popen([engine, WASM_TEST_MJS, WASM_TEST_HARNESS] + script_files, stdout=PIPE)
- out, _ = proc.communicate()
-
- if not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
- print(out)
- fail_total += 1
-
- tests_total = len(files)
- print('TOTAL: %d' % (tests_total))
- print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET))
- print('%sFAIL : %d%s' % (COLOR_RED, fail_total, COLOR_RESET))
-
- if fail_total > 0:
- raise Exception('new-es tests failed')
-
-@runner('cctest', default=False)
-def run_cctest(engine, arch):
- if engine == "escargot":
- engine = "cctest"
- proc = Popen([engine], stdout=PIPE)
- stdout, _ = proc.communicate()
- print(stdout)
- if 'FAILED' in stdout:
- raise Exception('Not all tests succeeded')
-
-@runner('debugger-server-source', default=True)
-def run_escargot_debugger(engine, arch):
- ESCARGOT_DEBUGGER_TEST_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'tests')
- ESCARGOT_DEBUGGER_CLIENT = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'debugger.py')
- ESCARGOT_DEBUGGER_TESTER = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'debugger_tester.sh')
- print('Running Escargot-Debugger-Server-Source test:')
- fails = 0
- proc = Popen(['chmod', '+x', ESCARGOT_DEBUGGER_TESTER],stdout=PIPE)
- for files in os.listdir(ESCARGOT_DEBUGGER_TEST_DIR):
- if files.endswith(".cmd"):
- test_case, _ = os.path.splitext(files)
- test_case_path = os.path.join(ESCARGOT_DEBUGGER_TEST_DIR, test_case)
- proc = Popen([ESCARGOT_DEBUGGER_TESTER, engine, os.path.relpath(test_case_path, PROJECT_SOURCE_DIR), ESCARGOT_DEBUGGER_CLIENT, "0"])
- proc.communicate()
- if not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, test_case_path, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, test_case_path, COLOR_RESET))
- fails += 1
- if fails > 0:
- raise Exception('Escargot-Debugger-Server-Source tests failed')
-
-@runner('debugger-client-source', default=True)
-def run_escargot_debugger2(engine, arch):
- ESCARGOT_DEBUGGER_TEST_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'tests')
- ESCARGOT_DEBUGGER_CLIENT = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'debugger.py')
- ESCARGOT_DEBUGGER_TESTER = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'debugger_tester.sh')
- print('Running Escargot-Debugger-Client-Source test:')
- fails = 0
- proc = Popen(['chmod', '+x', ESCARGOT_DEBUGGER_TESTER],stdout=PIPE)
- for files in os.listdir(ESCARGOT_DEBUGGER_TEST_DIR):
- if files.endswith(".cmd"):
- test_case, _ = os.path.splitext(files)
- test_case_path = os.path.join(ESCARGOT_DEBUGGER_TEST_DIR, test_case)
- proc = Popen([ESCARGOT_DEBUGGER_TESTER, engine, os.path.relpath(test_case_path, PROJECT_SOURCE_DIR), ESCARGOT_DEBUGGER_CLIENT, "1"])
- proc.communicate()
- if not proc.returncode:
- print('%sOK: %s%s' % (COLOR_GREEN, test_case_path, COLOR_RESET))
- else:
- print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, test_case_path, COLOR_RESET))
- fails += 1
- if fails > 0:
- raise Exception('Escargot-Debugger-Client-Source tests failed')
-
-def main():
- parser = ArgumentParser(description='Escargot Test Suite Runner')
- parser.add_argument('--engine', metavar='PATH', default=DEFAULT_ESCARGOT,
- help='path to the engine to be tested (default: %(default)s)')
- parser.add_argument('--arch', metavar='NAME', choices=['x86', 'x86_64'], default='x86_64',
- help='architecture the engine was built for (%(choices)s; default: %(default)s)')
- parser.add_argument('suite', metavar='SUITE', nargs='*', default=sorted(DEFAULT_RUNNERS),
- help='test suite to run (%s; default: %s)' % (', '.join(sorted(RUNNERS.keys())), ' '.join(sorted(DEFAULT_RUNNERS))))
- args = parser.parse_args()
-
- for suite in args.suite:
- if suite not in RUNNERS:
- parser.error('invalid test suite: %s' % suite)
-
- success, fail = [], []
-
- for suite in args.suite:
- print(COLOR_PURPLE + 'running test suite: ' + suite + COLOR_RESET)
- try:
- RUNNERS[suite](args.engine, args.arch)
- success += [suite]
- except Exception as e:
- print('\n'.join(COLOR_YELLOW + line + COLOR_RESET for line in traceback.format_exc().splitlines()))
- fail += [suite]
-
- if success:
- print(COLOR_GREEN + sys.argv[0] + ': success: ' + ', '.join(success) + COLOR_RESET)
- sys.exit(COLOR_RED + sys.argv[0] + ': fail: ' + ', '.join(fail) + COLOR_RESET if fail else None)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2015-present Samsung Electronics 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.
-
-import subprocess
-import os
-
-BDWGC_LOGFILE="bdwgcUsage.dat"
-GNUPLOT_DISPLAY_STYLE="lines"
-
-def make_plot_subcmd(col, name):
- return '\"%s\" using 1:%d title \"%s (KB)\" with %s' % (BDWGC_LOGFILE, col, name, GNUPLOT_DISPLAY_STYLE)
-
-def draw_bdwgc_plot():
- if not os.path.exists(BDWGC_LOGFILE):
- print 'Cannot draw plot! No input file %s' % BDWGC_LOGFILE
- print 'Please re-build escargot with `-DPROFILE_BDWGC` and run again.'
- exit(1)
-
- plot_cmd = 'plot '
- plot_cmd += make_plot_subcmd(2, "Peak RSS")
- plot_cmd += ', '
- plot_cmd += make_plot_subcmd(3, "Total Heap")
- plot_cmd += ', '
- plot_cmd += make_plot_subcmd(4, "Marked Heap")
-
- print "gnuplot -p -e '%s'" % plot_cmd
- print
- print "See '%s' to see raw data." % BDWGC_LOGFILE
-
- subprocess.call(['gnuplot', '-p', '-e', plot_cmd])
-
-if __name__ == '__main__':
- draw_bdwgc_plot()
--- /dev/null
+/*
+ * Copyright (c) 2019-present Samsung Electronics 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 NODE_BINDINGS_H
+#define NODE_BINDINGS_H
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace LWNode {
+
+void push_aul_message(const char* message);
+void push_aul_termination_message();
+
+class GmainLoopWork {
+ public:
+ virtual bool RunOnce() = 0;
+};
+
+class GmainLoopNodeBindings {
+ public:
+ GmainLoopNodeBindings(GmainLoopWork* bindingWork);
+ virtual ~GmainLoopNodeBindings(){};
+
+ void StartEventLoop();
+ void RunOnce();
+ bool HasMoreTasks();
+ void TerminateGMainLoop() { m_isTerminated = true; }
+
+ private:
+ bool m_isInitialize = {false};
+ bool m_hasMoreNodeTasks = {true};
+ bool m_isTerminated = {false};
+ GmainLoopWork* gmainLoopWork_{nullptr};
+};
+
+} // namespace LWNode
+
+#endif
--- /dev/null
+{
+ 'variables': {
+ },
+ 'includes': [
+ '../../common.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'node_bindings',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../../../../deps/uv/uv.gyp:libuv',
+ ],
+ 'defines': [
+ ],
+ 'includes': [
+ ],
+ 'include_dirs': [
+ 'include',
+ ],
+ 'sources': [
+ 'src/gmainloop_node_bindings.cc'
+ ],
+ 'cflags': [
+ '<!@(pkg-config --cflags glib-2.0)',
+ '-Wno-missing-field-initializers',
+ ],
+ 'libraries': [
+ '<!@(pkg-config --libs glib-2.0)',
+ ],
+ 'all_dependent_settings': {
+ 'defines': [
+ ],
+ 'include_dirs': [
+ 'include',
+ ],
+ 'cflags': [
+ '<!@(pkg-config --cflags glib-2.0)',
+ ],
+ 'cflags_cc!': ['-fno-exceptions'],
+ 'cflags_cc': [
+ '-fexceptions',
+ ],
+ 'libraries': [
+ '<!@(pkg-config --libs glib-2.0)',
+ ],
+ }
+ },
+ ],
+ 'conditions': [
+ ],
+}
--- /dev/null
+/*
+ * Copyright (c) 2019-present Samsung Electronics 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 <glib.h>
+#include <cassert>
+#include "uv.h"
+#ifdef HOST_TIZEN
+#include "Extension.h"
+#endif
+
+#include "node_bindings.h"
+
+namespace glib {
+
+using namespace LWNode;
+
+struct SourceData {
+ GSource source;
+ gpointer tag;
+ GmainLoopNodeBindings* node_bindings = nullptr;
+};
+
+// TODO: classify EventLoop if needed
+
+static GMainContext* gcontext;
+static GMainLoop* gmainLoop;
+static GSource* uvsource;
+static GSourceFuncs source_funcs;
+static bool gmainLoopDone = false;
+
+static gboolean GmainLoopPrepareCallback(GSource* source, gint* timeout) {
+ uv_update_time(uv_default_loop());
+ *timeout = uv_backend_timeout(uv_default_loop());
+
+ if (!uv_watcher_queue_empty(uv_default_loop())) {
+ return TRUE;
+ }
+
+ return 0 == *timeout;
+}
+
+static gboolean GmainLoopCheckCallback(GSource* source) {
+ if (!uv_backend_timeout(uv_default_loop())) {
+ return TRUE;
+ }
+
+ return (G_IO_IN ==
+ g_source_query_unix_fd(source, ((SourceData*)source)->tag));
+}
+
+static gboolean GmainLoopDispatchCallback(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data) {
+ assert(gcontext);
+ g_main_context_iteration(gcontext, FALSE);
+
+ if (gmainLoopDone) {
+ return G_SOURCE_REMOVE;
+ }
+
+ GmainLoopNodeBindings* node_bindings = ((SourceData*)source)->node_bindings;
+
+ node_bindings->RunOnce();
+
+ if (!node_bindings->HasMoreTasks()) {
+ g_main_loop_quit(gmainLoop);
+ return G_SOURCE_REMOVE;
+ }
+ return G_SOURCE_CONTINUE;
+}
+
+void GmainLoopInit(GmainLoopNodeBindings* self) {
+ gcontext = g_main_context_default();
+ gmainLoop = g_main_loop_new(gcontext, FALSE);
+ source_funcs = {
+ .prepare = GmainLoopPrepareCallback,
+ .check = GmainLoopCheckCallback,
+ .dispatch = GmainLoopDispatchCallback,
+ };
+
+ uvsource = g_source_new(&source_funcs, sizeof(SourceData));
+ ((SourceData*)uvsource)->tag = g_source_add_unix_fd(
+ uvsource,
+ uv_backend_fd(uv_default_loop()),
+ (GIOCondition)(G_IO_IN | G_IO_OUT | G_IO_ERR | G_IO_PRI));
+ ((SourceData*)uvsource)->node_bindings = self;
+
+ g_source_attach(uvsource, gcontext);
+ g_source_unref(uvsource);
+
+#ifdef HOST_TIZEN
+ DeviceAPI::ESPostMessageListener::SetMainThreadIdlerRegister(
+ [](DeviceAPI::ESPostMessageListener::Idler_t idler, void* data) {
+ g_idle_add(idler, data);
+ });
+#endif
+}
+
+void GmainLoopStart() {
+ assert(gmainLoop);
+ assert(gcontext);
+
+ g_main_loop_run(gmainLoop);
+ gmainLoopDone = true;
+ g_main_context_iteration(gcontext, TRUE);
+}
+
+void GmainLoopExit() {
+ if (uvsource) {
+ g_source_destroy(uvsource);
+ }
+ if (gmainLoop) {
+ g_main_loop_unref(gmainLoop);
+ }
+ if (gcontext) {
+ g_main_context_unref(gcontext);
+ }
+}
+
+} // namespace glib
+
+// TODO: pump aul message for Tizen AUL application
+#if 0
+#ifdef HOST_TIZEN
+#include <mutex>
+#include <thread>
+#include "Queue.hpp"
+
+#define NESCARGOT_AUL_TERMINATION_MESSAGE "AUL_TERMINATION"
+
+namespace LWNode {
+
+struct Task {
+ std::string data;
+};
+
+Queue<Task> g_queue;
+
+static void UvNoOp(uv_async_t* handle) {
+ uv_close((uv_handle_t*)handle,
+ [](uv_handle_t* handle) { delete (uv_async_t*)handle; });
+}
+
+void push_aul_message(const char* message) {
+ g_queue.push({.data = message});
+ // wake up the uv queue
+ uv_async_t* async = new uv_async_t;
+ uv_async_init(uv_default_loop(), async, UvNoOp);
+ uv_async_send(async);
+}
+
+void push_aul_termination_message() {
+ push_aul_message(NESCARGOT_AUL_TERMINATION_MESSAGE);
+}
+} // namespace LWNode
+#endif
+
+namespace LWNode {
+
+// static void pump_aul_message(v8::Isolate* isolate, GmainLoopNodeBindings* bindings) {
+// #ifdef HOST_TIZEN
+// // TODO: move the following to m_platform.PumpMessageLoop(isolate)
+// if (!g_queue.empty()) {
+// auto task = g_queue.pop();
+// node::EmitMessage(isolate, task.data.c_str());
+// if (task.data == std::string(NESCARGOT_AUL_TERMINATION_MESSAGE)) {
+// bindings->TerminateGMainLoop();
+// }
+// }
+// #endif
+// }
+}
+#endif
+
+namespace LWNode {
+
+GmainLoopNodeBindings::GmainLoopNodeBindings(GmainLoopWork* gmainLoopWork)
+ : gmainLoopWork_(gmainLoopWork) {
+ m_isInitialize = true;
+}
+
+void GmainLoopNodeBindings::StartEventLoop() {
+ assert(m_isInitialize);
+
+ glib::GmainLoopInit(this);
+
+ RunOnce();
+
+ if (HasMoreTasks()) {
+ glib::GmainLoopStart();
+ }
+
+ glib::GmainLoopExit();
+}
+
+bool GmainLoopNodeBindings::HasMoreTasks() {
+ return (m_hasMoreNodeTasks && !m_isTerminated);
+}
+
+void GmainLoopNodeBindings::RunOnce() {
+ m_hasMoreNodeTasks = gmainLoopWork_->RunOnce();
+}
+
+} // namespace LWNode
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#if defined(TIZEN_DEVICE_API)
-#include "Extension.h"
-
-#include <dlfcn.h>
-
-#include "escargotbase.h"
-#include "ExtensionAdapter.h"
-#include "EscargotPublic.h"
-#include "TizenDeviceAPILoaderForEscargot.h"
-#include "tizen_node.h"
-
-namespace wrt {
-namespace xwalk {
-
-Extension::Extension(const std::string& path, RuntimeVariableProvider* provider)
- : initialized_(false),
- library_path_(path),
- xw_extension_(0),
- use_trampoline_(true),
- created_instance_callback_(NULL),
- destroyed_instance_callback_(NULL),
- shutdown_callback_(NULL),
- handle_msg_callback_(NULL),
- handle_sync_msg_callback_(NULL),
- rv_provider_(provider) {}
-
-Extension::Extension(const std::string& path, const std::string& name,
- const std::vector<std::string>& entry_points,
- RuntimeVariableProvider* provider)
- : initialized_(false),
- handle_(NULL),
- library_path_(path),
- xw_extension_(0),
- name_(name),
- entry_points_(entry_points),
- use_trampoline_(true),
- created_instance_callback_(NULL),
- destroyed_instance_callback_(NULL),
- shutdown_callback_(NULL),
- handle_msg_callback_(NULL),
- handle_sync_msg_callback_(NULL),
- rv_provider_(provider) {}
-
-Extension::~Extension() {
- if (!initialized_) return;
-
- if (handle_) dlclose(handle_);
-
- if (shutdown_callback_) shutdown_callback_(xw_extension_);
- ExtensionAdapter::GetInstance()->UnregisterExtension(this);
-}
-
-bool Extension::Initialize() {
- if (initialized_) return true;
-
- DEVICEAPI_LOG_INFO("========== << Initialize >> ENTER ==========");
- DEVICEAPI_SLOG_INFO("Extension Module library : [%s]", library_path_.c_str());
-
- NESCARGOT_ASSERT(handle_ == NULL);
- handle_ = dlopen(library_path_.c_str(), RTLD_LAZY);
- if (!handle_) {
- const char* error = (const char*)dlerror();
- DEVICEAPI_LOG_ERROR("Error loading extension '%s'. Reason: %s",
- library_path_.c_str(),
- (error != NULL ? error : "unknown"));
- return false;
- }
-
- XW_Initialize_Func initialize =
- reinterpret_cast<XW_Initialize_Func>(dlsym(handle_, "XW_Initialize"));
- if (!initialize) {
- DEVICEAPI_LOG_ERROR("Error loading extension");
- DEVICEAPI_SLOG_ERROR("[%s] couldn't get XW_Initialize function",
- library_path_.c_str());
- dlclose(handle_);
- handle_ = NULL;
- return false;
- }
-
- ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
- xw_extension_ = adapter->GetNextXWExtension();
- adapter->RegisterExtension(this);
-
- int ret = initialize(xw_extension_, ExtensionAdapter::GetInterface);
- if (ret != XW_OK) {
- DEVICEAPI_LOG_ERROR("Error loading extension");
- DEVICEAPI_SLOG_ERROR("[%s] XW_Initialize function returned error value.",
- library_path_.c_str());
- dlclose(handle_);
- handle_ = NULL;
- return false;
- }
-
- initialized_ = true;
- DEVICEAPI_LOG_INFO("========== << Initialize >> END ==========");
- return true;
-}
-
-ExtensionInstance* Extension::CreateInstance() {
- Initialize();
- ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
- XW_Instance xw_instance = adapter->GetNextXWInstance();
- return new ExtensionInstance(this, xw_instance);
-}
-
-void Extension::GetRuntimeVariable(const char* key, char* value,
- size_t value_len) {
-#if 0
- if( rv_provider_ ){
- std::string ret = rv_provider_->GetRuntimeVariable(key);
- strncpy(value, ret.c_str(), value_len);
- }
-#else
- DEVICEAPI_LOG_INFO("GETRUNTIMEVAR: not implemented");
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
-#endif
-}
-int Extension::CheckAPIAccessControl(const char* /*api_name*/) {
- // TODO
- return XW_OK;
-}
-
-int Extension::RegisterPermissions(const char* /*perm_table*/) {
- // TODO
- return XW_OK;
-}
-
-ExtensionInstance::ExtensionInstance(Extension* extension,
- XW_Instance xw_instance)
- : extension_(extension),
- xw_instance_(xw_instance),
- instance_data_(NULL),
- post_message_listener_(NULL),
- post_data_listener_(NULL) {
- DEVICEAPI_LOG_INFO("Enter");
- ExtensionAdapter::GetInstance()->RegisterInstance(this);
- XW_CreatedInstanceCallback callback = extension_->created_instance_callback_;
- if (callback) callback(xw_instance_);
-}
-
-ExtensionInstance::~ExtensionInstance() {
- DEVICEAPI_LOG_INFO("Enter");
- XW_DestroyedInstanceCallback callback =
- extension_->destroyed_instance_callback_;
- if (callback) callback(xw_instance_);
- ExtensionAdapter::GetInstance()->UnregisterInstance(this);
-}
-
-void ExtensionInstance::HandleMessage(const std::string& msg) {
- XW_HandleMessageCallback callback = extension_->handle_msg_callback_;
- if (callback) callback(xw_instance_, msg.c_str());
-}
-
-void ExtensionInstance::HandleSyncMessage(const std::string& msg) {
- XW_HandleSyncMessageCallback callback = extension_->handle_sync_msg_callback_;
- if (callback) {
- sync_reply_msg_.clear();
- callback(xw_instance_, msg.c_str());
- }
-}
-
-void ExtensionInstance::PostMessage(const std::string& msg) {
- if (post_message_listener_) {
- post_message_listener_->PostMessageToJS(msg);
- }
-}
-
-void ExtensionInstance::SyncReply(const std::string& reply) {
- sync_reply_msg_ = reply;
-}
-
-void ExtensionInstance::HandleData(const std::string& msg, uint8_t* buffer,
- size_t len) {
- XW_HandleDataCallback callback = extension_->handle_data_callback_;
- if (callback) callback(xw_instance_, msg.c_str(), buffer, len);
-}
-
-void ExtensionInstance::HandleSyncData(const std::string& msg, uint8_t* buffer,
- size_t len) {
- XW_HandleDataCallback callback = extension_->handle_sync_data_callback_;
- if (callback) {
- sync_reply_msg_.clear();
- sync_reply_buffer_len_ = 0;
- sync_reply_buffer_ = NULL;
- // sync_reply_buffer_ will be freed by XWalkExtensionModule
- callback(xw_instance_, msg.c_str(), buffer, len);
- }
-}
-
-void ExtensionInstance::PostData(const std::string& msg, uint8_t* buffer,
- size_t len) {
- if (post_data_listener_) {
- post_data_listener_->PostDataToJS(msg, buffer, len);
- }
-}
-
-void ExtensionInstance::SyncDataReply(const std::string& reply, uint8_t* buffer,
- size_t len) {
- sync_reply_msg_ = reply;
- sync_reply_buffer_ = buffer;
- sync_reply_buffer_len_ = len;
-}
-
-} // namespace xwalk
-} // namespace wrt
-
-namespace DeviceAPI {
-
-ESPostListener::ESPostListener(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener)
- : context_(context), listener_(listener) {
- DEVICEAPI_LOG_INFO("Enter");
- GC_add_roots(&listener_, &listener_ + sizeof(Escargot::ObjectRef*));
-}
-
-ESPostListener::~ESPostListener() {
- DEVICEAPI_LOG_INFO("Enter");
- finalize();
-}
-
-void ESPostListener::finalize() {
- DEVICEAPI_LOG_INFO("Enter");
- GC_remove_roots(&listener_, &listener_ + sizeof(Escargot::ObjectRef*));
- listener_ = nullptr;
- context_ = nullptr;
-}
-
-void ESPostMessageListener::PostMessageToJS(const std::string& msg) {
- DEVICEAPI_LOG_INFO(
- "ESPostMessageListener::PostMessageToJS (msg %s listener %p context "
- "%p)",
- msg.c_str(), listener_, context_);
-
- ExtensionManagerInstance* extensionManagerInstance =
- ExtensionManagerInstance::get(context_);
- if (!extensionManagerInstance) {
- return;
- }
-
- struct Params {
- Escargot::ContextRef* context;
- Escargot::ObjectRef* listener;
- std::string msg;
- };
-
- Params* params = new Params();
- params->context = context_;
- params->listener = listener_;
- params->msg = msg;
- DEVICEAPI_LOG_INFO("Post message");
- node::tizen::AddIdle(
- [](void* data) {
- DEVICEAPI_LOG_INFO("Add idle\n");
- Params* params = (Params*)data;
- Escargot::ContextRef* context = params->context;
-
- auto result = Escargot::Evaluator::execute(
- context,
- [](Escargot::ExecutionStateRef* state,
- Params* params) -> Escargot::ValueRef* {
- Escargot::ObjectRef* listener = params->listener;
- std::string msg = params->msg;
- Escargot::ValueRef* arguments[] = {Escargot::ValueRef::create(
- Escargot::StringRef::createFromASCII(msg.c_str(),
- msg.size()))};
- return listener->call(state, Escargot::ValueRef::createNull(), 1,
- arguments);
- },
- params);
- if (result.error.hasValue()) {
- DEVICEAPI_LOG_ERROR(
- "Uncaught %s\n",
- result.resultOrErrorToString(context)->toStdUTF8String().c_str());
- }
-
- delete params;
- return 0;
- },
- params);
-}
-
-void ESPostDataListener::PostDataToJS(const std::string& msg, uint8_t* buffer,
- size_t len) {
- DEVICEAPI_LOG_INFO("ESPostDataListener::PostDataToJS (%s, %zu)", msg.c_str(),
- len);
-
- ExtensionManagerInstance* extensionManagerInstance =
- ExtensionManagerInstance::get(context_);
- if (!extensionManagerInstance) {
- return;
- }
-
- auto result = Escargot::Evaluator::execute(
- context_, [](Escargot::ExecutionStateRef* state) -> Escargot::ValueRef* {
-#if 0
- Escargot::ValueRef* arguments[] = {Escargot::ValueRef::create(Escargot::StringRef::createFromASCII(msg.c_str(), msg.size()))};
- return listener_->call(state, Escargot::ValueRef::createNull(), 1, arguments);
-#else
- DEVICEAPI_LOG_ERROR("NOT IMPLEMENTED");
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
- return Escargot::ValueRef::createUndefined();
-#endif
- });
- if (result.error.hasValue()) {
- DEVICEAPI_LOG_ERROR(
- "Uncaught %s\n",
- result.resultOrErrorToString(context_)->toStdUTF8String().c_str());
- }
-}
-
-} // namespace DeviceAPI
-#endif
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WRT_SERVICE_NODE_EXTENSION_H_
-#define WRT_SERVICE_NODE_EXTENSION_H_
-
-#include <string>
-#include <vector>
-
-#include "XW_Extension.h"
-#include "XW_Extension_SyncMessage.h"
-#include "XW_Extension_Data.h"
-
-namespace wrt {
-class RuntimeVariableProvider;
-
-namespace xwalk {
-
- class ExtensionAdapter;
- class ExtensionInstance;
-
- class Extension {
- public:
- Extension(const std::string& path, RuntimeVariableProvider* provider);
- Extension(const std::string& path, const std::string& name,
- const std::vector<std::string>& entry_points,
- RuntimeVariableProvider* provider);
-
- virtual ~Extension();
-
- bool Initialize();
- ExtensionInstance* CreateInstance();
-
- XW_Extension xw_extension()
- {
- return xw_extension_;
- }
-
- std::string name()
- {
- return name_;
- }
-
- std::string javascript_api()
- {
- Initialize();
- return javascript_api_;
- }
-
- std::vector<std::string>& entry_points()
- {
- return entry_points_;
- }
-
- bool use_trampoline()
- {
- return use_trampoline_;
- }
-
- void set_name(const std::string& name)
- {
- name_ = name;
- }
-
- void set_javascript_api(const std::string& javascript_api)
- {
- javascript_api_ = javascript_api;
- }
-
- void set_use_trampoline(bool use_trampoline)
- {
- use_trampoline_ = use_trampoline;
- }
-
- private:
- friend class ExtensionAdapter;
- friend class ExtensionInstance;
-
- void GetRuntimeVariable(const char* key, char* value, size_t value_len);
- int CheckAPIAccessControl(const char* api_name);
- int RegisterPermissions(const char* perm_table);
-
- bool initialized_{ false };
- void* handle_{ nullptr };
- std::string library_path_;
-
- XW_Extension xw_extension_{ 0 };
- std::string name_;
- std::string javascript_api_;
- std::vector<std::string> entry_points_;
- bool use_trampoline_{ true };
-
- XW_CreatedInstanceCallback created_instance_callback_{ nullptr };
- XW_DestroyedInstanceCallback destroyed_instance_callback_{ nullptr };
- XW_ShutdownCallback shutdown_callback_{ nullptr };
- XW_HandleMessageCallback handle_msg_callback_{ nullptr };
- XW_HandleSyncMessageCallback handle_sync_msg_callback_{ nullptr };
- XW_HandleDataCallback handle_data_callback_{ nullptr };
- XW_HandleDataCallback handle_sync_data_callback_{ nullptr };
- RuntimeVariableProvider* rv_provider_{ nullptr };
- };
-
- class PostMessageListener {
- public:
- virtual void PostMessageToJS(const std::string& msg) = 0;
- };
-
- class PostDataListener {
- public:
- virtual void PostDataToJS(const std::string& msg, uint8_t* buffer,
- size_t len) = 0;
- };
-
- class ExtensionInstance {
- public:
- ExtensionInstance(Extension* extension, XW_Instance xw_instance);
- virtual ~ExtensionInstance();
-
- void HandleMessage(const std::string& msg);
- void HandleSyncMessage(const std::string& msg);
-
- void HandleData(const std::string& msg, uint8_t* buffer, size_t len);
- void HandleSyncData(const std::string& msg, uint8_t* buffer,
- size_t len);
-
- XW_Instance xw_instance()
- {
- return xw_instance_;
- }
-
- std::string sync_replay_msg()
- {
- return sync_reply_msg_;
- }
-
- std::string sync_data_reply_msg(uint8_t** buffer, size_t* len)
- {
- *buffer = sync_reply_buffer_;
- *len = sync_reply_buffer_len_;
- return sync_reply_msg_;
- }
-
- void set_post_message_listener(PostMessageListener* listener)
- {
- post_message_listener_ = listener;
- }
-
- void set_post_data_listener(PostDataListener* listener)
- {
- post_data_listener_ = listener;
- }
-
- private:
- friend class ExtensionAdapter;
-
- void PostMessage(const std::string& msg);
- void SyncReply(const std::string& reply);
-
- void PostData(const std::string& msg, uint8_t* buffer, size_t len);
- void SyncDataReply(const std::string& reply, uint8_t* buffer,
- size_t len);
-
- Extension* extension_;
- XW_Instance xw_instance_;
- void* instance_data_;
- std::string sync_reply_msg_;
- uint8_t* sync_reply_buffer_;
- size_t sync_reply_buffer_len_;
-
- PostMessageListener* post_message_listener_;
- PostDataListener* post_data_listener_;
- };
-
-} // namespace xwalk
-} // namespace wrt
-
-namespace Escargot {
-class ContextRef;
-class ObjectRef;
-}
-
-namespace DeviceAPI {
-
-class ESPostListener {
-public:
- virtual ~ESPostListener();
- void finalize();
-
-protected:
- ESPostListener(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener);
-
- Escargot::ContextRef* context_;
- Escargot::ObjectRef* listener_;
-};
-
-class ESPostMessageListener : public wrt::xwalk::PostMessageListener,
- public ESPostListener {
-public:
- static ESPostMessageListener* create(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener)
- {
- return new ESPostMessageListener(context, listener);
- }
- void PostMessageToJS(const std::string& msg);
-
-private:
- ESPostMessageListener(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener)
- : ESPostListener(context, listener)
- {
- }
-};
-
-class ESPostDataListener : public wrt::xwalk::PostDataListener,
- public ESPostListener {
-public:
- static ESPostDataListener* create(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener)
- {
- return new ESPostDataListener(context, listener);
- }
- void PostDataToJS(const std::string& msg, uint8_t* buffer, size_t len);
-
-private:
- ESPostDataListener(Escargot::ContextRef* context,
- Escargot::ObjectRef* listener)
- : ESPostListener(context, listener)
- {
- }
-};
-
-} // namespace DeviceAPI
-
-#endif // WRT_SERVICE_NODE_EXTENSION_H_
+++ /dev/null
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#if defined(TIZEN_DEVICE_API)
-#include "escargotbase.h"
-#include "TizenDeviceAPILoaderForEscargot.h"
-#include "ExtensionAdapter.h"
-
-namespace wrt {
-namespace xwalk {
-
- ExtensionAdapter::ExtensionAdapter()
- : next_xw_extension_(1)
- , next_xw_instance_(1)
- {
- }
-
- ExtensionAdapter::~ExtensionAdapter()
- {
- }
-
- ExtensionAdapter* ExtensionAdapter::GetInstance()
- {
- static ExtensionAdapter self;
- return &self;
- }
-
- XW_Extension ExtensionAdapter::GetNextXWExtension()
- {
- return next_xw_extension_++;
- }
-
- XW_Instance ExtensionAdapter::GetNextXWInstance()
- {
- return next_xw_instance_++;
- }
-
- void ExtensionAdapter::RegisterExtension(Extension* extension)
- {
- XW_Extension xw_extension = extension->xw_extension();
- if (!(xw_extension > 0 && xw_extension < next_xw_extension_)) {
- DEVICEAPI_LOG_WARN("xw_extension (%d) is invalid.", xw_extension);
- return;
- }
- if (extension_map_.find(xw_extension) == extension_map_.end())
- extension_map_[xw_extension] = extension;
- }
-
- void ExtensionAdapter::UnregisterExtension(Extension* extension)
- {
- XW_Extension xw_extension = extension->xw_extension();
- if (!(xw_extension > 0 && xw_extension < next_xw_extension_)) {
- DEVICEAPI_LOG_WARN("xw_extension (%d) is invalid.", xw_extension);
- return;
- }
- if (extension_map_.find(xw_extension) != extension_map_.end())
- extension_map_.erase(xw_extension);
- }
-
- void ExtensionAdapter::RegisterInstance(ExtensionInstance* instance)
- {
- XW_Instance xw_instance = instance->xw_instance();
- if (!(xw_instance > 0 && xw_instance < next_xw_instance_)) {
- DEVICEAPI_LOG_WARN("xw_instance (%d) is invalid.", xw_instance);
- return;
- }
- if (instance_map_.find(xw_instance) == instance_map_.end())
- instance_map_[xw_instance] = instance;
- }
-
- void ExtensionAdapter::UnregisterInstance(ExtensionInstance* instance)
- {
- XW_Instance xw_instance = instance->xw_instance();
- if (!(xw_instance > 0 && xw_instance < next_xw_instance_)) {
- DEVICEAPI_LOG_WARN("xw_instance (%d) is invalid.", xw_instance);
- return;
- }
- if (instance_map_.find(xw_instance) != instance_map_.end())
- instance_map_.erase(xw_instance);
- }
-
- const void* ExtensionAdapter::GetInterface(const char* name)
- {
- if (!strcmp(name, XW_CORE_INTERFACE_1)) {
- static const XW_CoreInterface_1 coreInterface1 = {
- CoreSetExtensionName, CoreSetJavaScriptAPI,
- CoreRegisterInstanceCallbacks, CoreRegisterShutdownCallback,
- CoreSetInstanceData, CoreGetInstanceData
- };
- return &coreInterface1;
- }
-
- if (!strcmp(name, XW_MESSAGING_INTERFACE_1)) {
- static const XW_MessagingInterface_1 messagingInterface1 = {
- MessagingRegister, MessagingPostMessage
- };
- return &messagingInterface1;
- }
-
- if (!strcmp(name, XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1)) {
- static const XW_Internal_SyncMessagingInterface_1
- syncMessagingInterface1 = { SyncMessagingRegister,
- SyncMessagingSetSyncReply };
- return &syncMessagingInterface1;
- }
-
- if (!strcmp(name, XW_INTERNAL_ENTRY_POINTS_INTERFACE_1)) {
- static const XW_Internal_EntryPointsInterface_1
- entryPointsInterface1 = { EntryPointsSetExtraJSEntryPoints };
- return &entryPointsInterface1;
- }
-
- if (!strcmp(name, XW_INTERNAL_RUNTIME_INTERFACE_1)) {
- static const XW_Internal_RuntimeInterface_1 runtimeInterface1 = {
- RuntimeGetStringVariable
- };
- return &runtimeInterface1;
- }
-
- if (!strcmp(name, XW_INTERNAL_PERMISSIONS_INTERFACE_1)) {
- static const XW_Internal_PermissionsInterface_1
- permissionsInterface1 = { PermissionsCheckAPIAccessControl,
- PermissionsRegisterPermissions };
- return &permissionsInterface1;
- }
-
- if (!strcmp(name, XW_INTERNAL_DATA_INTERFACE_1)) {
- static const XW_Internal_DataInterface_1 dataInterface1 = {
- DataRegisterSync, DataRegisterAsync, DataSetSyncReply,
- DataPostData
- };
- return &dataInterface1;
- }
-
- DEVICEAPI_LOG_WARN("Interface '%s' is not supported.", name);
- return NULL;
- }
-
- Extension* ExtensionAdapter::GetExtension(XW_Extension xw_extension)
- {
- ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
- ExtensionMap::iterator it = adapter->extension_map_.find(xw_extension);
- if (it == adapter->extension_map_.end())
- return NULL;
- return it->second;
- }
-
- ExtensionInstance* ExtensionAdapter::GetExtensionInstance(
- XW_Instance xw_instance)
- {
- ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
- InstanceMap::iterator it = adapter->instance_map_.find(xw_instance);
- if (it == adapter->instance_map_.end())
- return NULL;
- return it->second;
- }
-
-#define CHECK(x, xw) \
- if (!x) { \
- DEVICEAPI_LOG_WARN("Ignoring call. Invalid %s = %d", #xw, xw); \
- return; \
- }
-
-#define RETURN_IF_INITIALIZED(x) \
- if (x->initialized_) \
- return;
-
- void ExtensionAdapter::CoreSetExtensionName(XW_Extension xw_extension,
- const char* name)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->name_ = name;
- }
-
- void ExtensionAdapter::CoreSetJavaScriptAPI(XW_Extension xw_extension,
- const char* javascript_api)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->javascript_api_ = javascript_api;
- }
-
- void ExtensionAdapter::CoreRegisterInstanceCallbacks(
- XW_Extension xw_extension, XW_CreatedInstanceCallback created,
- XW_DestroyedInstanceCallback destroyed)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->created_instance_callback_ = created;
- extension->destroyed_instance_callback_ = destroyed;
- }
-
- void ExtensionAdapter::CoreRegisterShutdownCallback(
- XW_Extension xw_extension, XW_ShutdownCallback shutdown)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->shutdown_callback_ = shutdown;
- }
-
- void ExtensionAdapter::CoreSetInstanceData(XW_Instance xw_instance,
- void* data)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- CHECK(instance, xw_instance);
- instance->instance_data_ = data;
- }
-
- void* ExtensionAdapter::CoreGetInstanceData(XW_Instance xw_instance)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- if (instance)
- return instance->instance_data_;
- else
- return NULL;
- }
-
- void ExtensionAdapter::MessagingRegister(
- XW_Extension xw_extension, XW_HandleMessageCallback handle_message)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->handle_msg_callback_ = handle_message;
- }
-
- void ExtensionAdapter::MessagingPostMessage(XW_Instance xw_instance,
- const char* message)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- CHECK(instance, xw_instance);
- instance->PostMessage(message);
- }
-
- void ExtensionAdapter::SyncMessagingRegister(
- XW_Extension xw_extension,
- XW_HandleSyncMessageCallback handle_sync_message)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->handle_sync_msg_callback_ = handle_sync_message;
- }
-
- void ExtensionAdapter::SyncMessagingSetSyncReply(XW_Instance xw_instance,
- const char* reply)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- CHECK(instance, xw_instance);
- instance->SyncReply(reply);
- }
-
- void ExtensionAdapter::EntryPointsSetExtraJSEntryPoints(
- XW_Extension xw_extension, const char** entry_points)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
-
- for (int i = 0; entry_points[i]; ++i) {
- extension->entry_points_.push_back(std::string(entry_points[i]));
- }
- }
-
- void ExtensionAdapter::RuntimeGetStringVariable(XW_Extension xw_extension,
- const char* key,
- char* value,
- unsigned int value_len)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- extension->GetRuntimeVariable(key, value, value_len);
- }
-
- int ExtensionAdapter::PermissionsCheckAPIAccessControl(
- XW_Extension xw_extension, const char* api_name)
- {
- Extension* extension = GetExtension(xw_extension);
- if (extension)
- return extension->CheckAPIAccessControl(api_name);
- else
- return XW_ERROR;
- }
-
- int ExtensionAdapter::PermissionsRegisterPermissions(
- XW_Extension xw_extension, const char* perm_table)
- {
- Extension* extension = GetExtension(xw_extension);
- if (extension)
- return extension->RegisterPermissions(perm_table);
- else
- return XW_ERROR;
- }
-
- void ExtensionAdapter::DataRegisterSync(
- XW_Extension xw_extension, XW_HandleDataCallback handle_sync_data)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->handle_sync_data_callback_ = handle_sync_data;
- }
-
- void ExtensionAdapter::DataRegisterAsync(XW_Extension xw_extension,
- XW_HandleDataCallback handle_data)
- {
- Extension* extension = GetExtension(xw_extension);
- CHECK(extension, xw_extension);
- RETURN_IF_INITIALIZED(extension);
- extension->handle_data_callback_ = handle_data;
- }
-
- void ExtensionAdapter::DataSetSyncReply(XW_Instance xw_instance,
- const char* reply, uint8_t* buffer,
- size_t len)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- CHECK(instance, xw_instance);
- instance->SyncDataReply(reply, buffer, len);
- }
-
- void ExtensionAdapter::DataPostData(XW_Instance xw_instance,
- const char* message, uint8_t* buffer,
- size_t len)
- {
- ExtensionInstance* instance = GetExtensionInstance(xw_instance);
- CHECK(instance, xw_instance);
- instance->PostData(message, buffer, len);
- }
-
-} // namespace xwalk
-} // namespace wrt
-#endif
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WRT_SERVICE_NODE_EXTENSION_ADAPTER_H_
-#define WRT_SERVICE_NODE_EXTENSION_ADAPTER_H_
-
-#include <map>
-
-#include "XW_Extension.h"
-#include "XW_Extension_SyncMessage.h"
-#include "XW_Extension_EntryPoints.h"
-#include "XW_Extension_Runtime.h"
-#include "XW_Extension_Permissions.h"
-#include "XW_Extension_Data.h"
-
-#include "Extension.h"
-
-namespace wrt {
-namespace xwalk {
-
- class ExtensionAdapter {
- public:
- static ExtensionAdapter* GetInstance();
-
- XW_Extension GetNextXWExtension();
- XW_Instance GetNextXWInstance();
-
- void RegisterExtension(Extension* extension);
- void UnregisterExtension(Extension* extension);
-
- void RegisterInstance(ExtensionInstance* instance);
- void UnregisterInstance(ExtensionInstance* instance);
-
- // Returns the correct struct according to interface asked. This is
- // passed to external extensions in XW_Initialize() call.
- static const void* GetInterface(const char* name);
-
- static Extension* GetExtension(XW_Extension xw_extension);
- static ExtensionInstance* GetExtensionInstance(XW_Instance xw_instance);
-
- private:
- ExtensionAdapter();
- virtual ~ExtensionAdapter();
-
- static void CoreSetExtensionName(XW_Extension xw_extension,
- const char* name);
- static void CoreSetJavaScriptAPI(XW_Extension xw_extension,
- const char* javascript_api);
- static void CoreRegisterInstanceCallbacks(
- XW_Extension xw_extension, XW_CreatedInstanceCallback created,
- XW_DestroyedInstanceCallback destroyed);
- static void CoreRegisterShutdownCallback(XW_Extension xw_extension,
- XW_ShutdownCallback shutdown);
- static void CoreSetInstanceData(XW_Instance xw_instance, void* data);
- static void* CoreGetInstanceData(XW_Instance xw_instance);
- static void MessagingRegister(XW_Extension xw_extension,
- XW_HandleMessageCallback handle_message);
- static void MessagingPostMessage(XW_Instance xw_instance,
- const char* message);
- static void SyncMessagingRegister(
- XW_Extension xw_extension,
- XW_HandleSyncMessageCallback handle_sync_message);
- static void SyncMessagingSetSyncReply(XW_Instance xw_instance,
- const char* reply);
- static void EntryPointsSetExtraJSEntryPoints(XW_Extension xw_extension,
- const char** entry_points);
- static void RuntimeGetStringVariable(XW_Extension xw_extension,
- const char* key, char* value,
- unsigned int value_len);
- static int PermissionsCheckAPIAccessControl(XW_Extension xw_extension,
- const char* api_name);
- static int PermissionsRegisterPermissions(XW_Extension xw_extension,
- const char* perm_table);
- static void DataRegisterSync(XW_Extension xw_extension,
- XW_HandleDataCallback handle_sync_data);
- static void DataRegisterAsync(XW_Extension xw_extension,
- XW_HandleDataCallback handle_data);
- static void DataSetSyncReply(XW_Instance instance, const char* reply,
- uint8_t* buffer, size_t len);
- static void DataPostData(XW_Instance instance, const char* message,
- uint8_t* buffer, size_t len);
-
- typedef std::map<XW_Extension, Extension*> ExtensionMap;
- ExtensionMap extension_map_;
-
- typedef std::map<XW_Instance, ExtensionInstance*> InstanceMap;
- InstanceMap instance_map_;
-
- XW_Extension next_xw_extension_;
- XW_Instance next_xw_instance_;
- };
-
-} // namespace xwalk
-} // namespace wrt
-
-#endif // WRT_SERVICE_NODE_EXTENSION_ADAPTER_H_
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#if defined(TIZEN_DEVICE_API)
-#include "escargotbase.h"
-#include "ExtensionManager.h"
-
-#include <glob.h>
-// #include <dpl/log/secure_log.h>
-#include <memory>
-#include <iostream>
-#include <fstream>
-
-// #include "runtime_variable_provider.h"
-// #include "picojson.h"
-#include "TizenDeviceAPILoaderForEscargot.h"
-
-namespace wrt {
-namespace xwalk {
-
- namespace {
- // TODO: need to cleanup
- const char kExtensionDir[] = "/usr/lib/wrt-plugins-widget";
- const char kExtensionPrefix[] = "lib";
- const char kExtensionSuffix[] = ".so";
- const char kExtensionMetadataSuffix[] = ".json";
- }
-
- ExtensionManager::ExtensionManager()
- {
- }
-
- ExtensionManager::~ExtensionManager()
- {
- }
-
- ExtensionManager* ExtensionManager::GetInstance()
- {
- static ExtensionManager self;
- return &self;
- }
-
-#if 0
-void ExtensionManager::RegisterExtensionsByMetadata(
- RuntimeVariableProvider* provider, const std::string& metafile_path) {
- DEVICEAPI_SLOG_INFO("path : [%s]", metafile_path.c_str());
- std::ifstream metafile(metafile_path.c_str());
- if (!metafile.is_open()) {
- DEVICEAPI_LOG_ERROR("Can't open plugin metadata file");
- return;
- }
-
- picojson::value metadata;
- metafile >> metadata;
- if (metadata.is<picojson::array>()) {
- auto& plugins = metadata.get<picojson::array>();
- for (auto plugin = plugins.begin(); plugin != plugins.end(); ++plugin) {
- if (!plugin->is<picojson::object>())
- continue;
-
- std::string name = plugin->get("name").to_str();
- std::string lib = plugin->get("lib").to_str();
- if (lib.at(0) != '/') {
- lib = std::string(kExtensionDir) + "/" + lib;
- }
- std::vector<std::string> entries;
- auto& entry_points_value = plugin->get("entry_points");
- if (entry_points_value.is<picojson::array>()) {
- auto& entry_points = entry_points_value.get<picojson::array>();
- for (auto entry = entry_points.begin(); entry != entry_points.end();
- ++entry) {
- entries.push_back(entry->to_str());
- }
- } else {
- DEVICEAPI_LOG_ERROR("there is no entry points");
- }
- Extension* extension = new Extension(lib, name, entries, provider);
- RegisterExtension(extension);
- }
- } else {
- DEVICEAPI_LOG_ERROR("Not plugin metadata");
- DEVICEAPI_SLOG_ERROR("%s is not plugin metadata", metafile_path.c_str());
- }
- metafile.close();
-}
-
-void ExtensionManager::RegisterExtensionsByMetadata(
- RuntimeVariableProvider* provider) {
- std::string extension_path(kExtensionDir);
- extension_path.append("/");
- extension_path.append("*");
- extension_path.append(kExtensionMetadataSuffix);
-
- DEVICEAPI_LOG_INFO("Register Extension directory");
- DEVICEAPI_SLOG_INFO("path : [%s]", extension_path.c_str());
-
- glob_t glob_result;
- glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
- for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
- RegisterExtensionsByMetadata(provider, glob_result.gl_pathv[i]);
- }
- if (glob_result.gl_pathc == 0) {
- RegisterExtensionsInDirectory(provider);
- }
-}
-#endif
-
- void ExtensionManager::RegisterExtensionsInDirectory(
- RuntimeVariableProvider* provider)
- {
- std::string extension_path(kExtensionDir);
- extension_path.append("/");
- extension_path.append(kExtensionPrefix);
- extension_path.append("*");
- extension_path.append(kExtensionSuffix);
-
- DEVICEAPI_LOG_INFO("Register Extension directory");
- DEVICEAPI_SLOG_INFO("path : [%s]", extension_path.c_str());
-
- glob_t glob_result;
- glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
- for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
- Extension* extension =
- new Extension(glob_result.gl_pathv[i], provider);
- if (extension->Initialize()) {
- RegisterExtension(extension);
- }
- }
- }
-
- bool ExtensionManager::RegisterExtension(Extension* extension)
- {
- DEVICEAPI_LOG_INFO(
- "========== << RegisterExtension >> ENTER ==========");
- if (!extension)
- return false;
-
- std::string name = extension->name();
-
- DEVICEAPI_LOG_INFO("Register Extension name : [%s]", name.c_str());
- if (extension_symbols_.find(name) != extension_symbols_.end()) {
- DEVICEAPI_LOG_WARN(
- "Ignoring extension with name already registred. '%s'",
- name.c_str());
- return false;
- }
-
- std::vector<std::string>& entry_points = extension->entry_points();
- std::vector<std::string>::iterator iter;
- for (iter = entry_points.begin(); iter != entry_points.end(); ++iter) {
- if (extension_symbols_.find(*iter) != extension_symbols_.end()) {
- DEVICEAPI_LOG_WARN(
- "Ignoring extension with entry_point already registred. "
- "'%s'",
- (*iter).c_str());
- return false;
- }
- }
-
- for (iter = entry_points.begin(); iter != entry_points.end(); ++iter) {
- extension_symbols_.insert(*iter);
- }
-
- extension_symbols_.insert(name);
- extensions_[name] = extension;
-
- DEVICEAPI_LOG_INFO("========== << RegisterExtension >> END ==========");
- return true;
- }
-
-} // namespace xwalk
-} // namespace wrt
-#endif
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WRT_SERVICE_NODE_EXTENSION_MANAGER_H_
-#define WRT_SERVICE_NODE_EXTENSION_MANAGER_H_
-
-#include <string>
-#include <set>
-#include <map>
-
-#include "XW_Extension.h"
-#include "XW_Extension_SyncMessage.h"
-#include "Extension.h"
-
-namespace wrt {
-class RuntimeVariableProvider;
-namespace xwalk {
-
- typedef std::map<std::string, Extension*> ExtensionMap;
-
- class ExtensionManager {
- public:
- static ExtensionManager* GetInstance();
-
- void RegisterExtensionsInDirectory(RuntimeVariableProvider* provider);
-#if 0
- void RegisterExtensionsByMetadata(RuntimeVariableProvider* provider);
- void RegisterExtensionsByMetadata(RuntimeVariableProvider* provider,
- const std::string& metafile_path);
-#endif
-
- ExtensionMap& extensions()
- {
- return extensions_;
- }
-
- bool RegisterExtension(Extension* extension);
-
- private:
- ExtensionManager();
- virtual ~ExtensionManager();
-
- ExtensionMap extensions_;
-
- std::set<std::string> extension_symbols_;
- };
-
-} // namespace xwalk
-} // namespace wrt
-
-#endif // WRT_SERVICE_NODE_EXTENSION_MANAGER_H_
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#if defined(TIZEN_DEVICE_API)
-#include "escargotbase.h"
-#include "TizenDeviceAPILoaderForEscargot.h"
-
-#include "EscargotPublic.h"
-
-#include <dlfcn.h>
-#include "ExtensionAdapter.h"
-#include "ExtensionManager.h"
-
-
-using namespace Escargot;
-
-namespace DeviceAPI {
-
-TizenStrings::TizenStrings(ContextRef* context)
- : m_context(context)
- , m_initialized(false)
-{
- initializeEarlyStrings();
-}
-
-#define INIT_TIZEN_STRING(name) \
- name = AtomicStringRef::create(m_context, "" #name);
-void TizenStrings::initializeEarlyStrings()
-{
- DEVICEAPI_LOG_INFO("Enter");
-
- FOR_EACH_EARLY_TIZEN_STRINGS(INIT_TIZEN_STRING)
- SUPPORTED_TIZEN_PROPERTY(INIT_TIZEN_STRING)
- SUPPORTED_TIZEN_ENTRYPOINTS(INIT_TIZEN_STRING)
-}
-
-void TizenStrings::initializeLazyStrings()
-{
- DEVICEAPI_LOG_INFO("Enter");
-
- if (m_initialized)
- return;
-
- FOR_EACH_LAZY_TIZEN_STRINGS(INIT_TIZEN_STRING)
-
- m_initialized = true;
-}
-#undef INIT_TIZEN_STRING
-
-void printArguments(ContextRef* context, size_t argc, ValueRef** argv)
-{
- DEVICEAPI_LOG_INFO("printing %zu arguments", argc);
- Evaluator::execute(
- context,
- [](ExecutionStateRef* state, size_t argc,
- ValueRef** argv) -> ValueRef* {
- for (size_t i = 0; i < argc; i++) {
- DEVICEAPI_LOG_INFO(
- "argument %zu : %s", i,
- argv[i]->toString(state)->toStdUTF8String().c_str());
- }
- return ValueRef::createUndefined();
- },
- argc, argv);
-}
-
-void* ExtensionManagerInstance::operator new(size_t size)
-{
- static bool typeInited = false;
- static GC_descr descr;
- if (!typeInited) {
- GC_word obj_bitmap[GC_BITMAP_SIZE(ExtensionManagerInstance)] = { 0 };
- GC_set_bit(obj_bitmap,
- GC_WORD_OFFSET(ExtensionManagerInstance, m_context));
- GC_set_bit(obj_bitmap,
- GC_WORD_OFFSET(ExtensionManagerInstance, m_strings));
-#if defined(STARFISH_TIZEN_WEARABLE_WIDGET)
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ExtensionManagerInstance,
- m_webWidgetAPIInstance));
-#endif
-#define DECLARE_TIZEN_VALUE(name) \
- GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ExtensionManagerInstance, \
- VALUE_NAME_STRCAT(m_##name)));
- FOR_EACH_EARLY_TIZEN_STRINGS(DECLARE_TIZEN_VALUE);
- FOR_EACH_LAZY_TIZEN_STRINGS(DECLARE_TIZEN_VALUE);
- SUPPORTED_TIZEN_PROPERTY(DECLARE_TIZEN_VALUE);
- SUPPORTED_TIZEN_ENTRYPOINTS(DECLARE_TIZEN_VALUE);
-#undef DECLARE_TIZEN_VALUE
- descr = GC_make_descriptor(obj_bitmap,
- GC_WORD_LEN(ExtensionManagerInstance));
- typeInited = true;
- }
- return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
-}
-
-wrt::xwalk::Extension* ExtensionManagerInstance::getExtension(
- const char* apiName)
-{
- DEVICEAPI_LOG_INFO("Enter");
- wrt::xwalk::ExtensionMap& extensions =
- wrt::xwalk::ExtensionManager::GetInstance()->extensions();
-
- auto it = extensions.find(apiName);
- if (it == extensions.end()) {
- DEVICEAPI_LOG_INFO("Enter");
- char library_path[512];
- if (!strcmp(apiName, "tizen"))
- snprintf(library_path, 512,
- "/usr/lib/tizen-extensions-crosswalk/libtizen.so");
- else if (!strcmp(apiName, "sensorservice"))
- snprintf(library_path, 512,
- "/usr/lib/tizen-extensions-crosswalk/libtizen_sensor.so");
- else if (!strcmp(apiName, "sa")) {
- snprintf(library_path, 512,
- "/usr/lib/tizen-extensions-crosswalk/libwebapis_sa.so");
- } else
- snprintf(library_path, 512,
- "/usr/lib/tizen-extensions-crosswalk/libtizen_%s.so",
- apiName);
- wrt::xwalk::Extension* extension =
- new wrt::xwalk::Extension(library_path, nullptr);
- if (extension->Initialize()) {
- wrt::xwalk::ExtensionManager::GetInstance()->RegisterExtension(
- extension);
- extensions[apiName] = extension;
- return extension;
- } else {
- DEVICEAPI_LOG_INFO("Cannot initialize extension %s", apiName);
- return nullptr;
- }
- } else {
- return it->second;
- }
-}
-
-// Caution: this function is called only inside existing js execution context,
-// so we don't handle JS exception around ESFunctionObject::call()
-ObjectRef* ExtensionManagerInstance::initializeExtensionInstance(
- const char* apiName)
-{
- DEVICEAPI_LOG_INFO("Enter");
-
- return Evaluator::execute(
- m_context,
- [](ExecutionStateRef* state, ExtensionManagerInstance* self,
- const char* apiName) -> ValueRef* {
- wrt::xwalk::Extension* extension = getExtension(apiName);
- if (!extension) {
- DEVICEAPI_LOG_INFO("Cannot load extension %s", apiName);
- return ObjectRef::create(state);
- }
- std::string str;
- str.append("(function(extension){");
- str.append("extension.internal = {};");
- str.append(
- "extension.internal.sendSyncMessage_ = "
- "extension.sendSyncMessage;");
- str.append(
- "extension.internal.sendSyncMessage = function(){ "
- "return "
- "extension.internal.sendSyncMessage_.apply(extension, "
- "arguments); };");
- str.append("delete extension.sendSyncMessage;");
- str.append("var exports = {};");
- str.append("console.log('Start loading ");
- str.append(apiName);
- str.append("');");
- str.append("(function() {'use strict';");
- str.append(extension->javascript_api().c_str());
- str.append("})();");
- str.append("console.log('Loading ");
- str.append(apiName);
- str.append(" done ');");
- str.append("return exports;})");
-
- std::string jsFileName = apiName;
- jsFileName += ".js";
- jsFileName = "tizen_api_internal_" + jsFileName;
- StringRef* apiSource =
- StringRef::createFromUTF8(str.c_str(), str.length());
- FunctionObjectRef* initializer =
- self->m_context->scriptParser()
- ->initializeScript(
- apiSource,
- StringRef::createFromUTF8(jsFileName.c_str(),
- jsFileName.length()))
- .script.value()
- ->execute(state)
- ->asFunctionObject();
- ObjectRef* extensionObject =
- self->createExtensionObject(state);
- wrt::xwalk::ExtensionInstance* extensionInstance =
- extension->CreateInstance();
- self->m_extensionInstances[extensionObject] =
- extensionInstance;
- ValueRef* arguments[] = { ValueRef::create(
- extensionObject) };
- return initializer
- ->call(state, ValueRef::createNull(), 1, arguments)
- ->toObject(state);
- },
- this, apiName)
- .result->asObject();
-}
-
-ObjectRef* ExtensionManagerInstance::createExtensionObject(
- ExecutionStateRef* state)
-{
- DEVICEAPI_LOG_INFO("Enter");
-
- ObjectRef* extensionObject = ObjectRef::create(state);
-
- FunctionObjectRef* postMessageFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->postMessage,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_ERROR("extension.postMessage UNIMPLEMENTED");
- printArguments(state->context(), argc, argv);
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
- return ValueRef::createUndefined();
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->postMessage->string()),
- ValueRef::create(postMessageFn), true, true, true);
-
- FunctionObjectRef* sendSyncMessageFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->sendSyncMessage,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_INFO("extension.sendSyncMessage");
- printArguments(state->context(), argc, argv);
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
- wrt::xwalk::ExtensionInstance* extensionInstance =
- extensionManagerInstance
- ->getExtensionInstanceFromCallingContext(
- state->context(), thisValue);
- if (!extensionInstance || argc != 1) {
- return ValueRef::create(false);
- }
-
- StringRef* message = argv[0]->toString(state);
- extensionInstance->HandleSyncMessage(
- message->toStdUTF8String());
-
- std::string reply = extensionInstance->sync_replay_msg();
- DEVICEAPI_LOG_INFO(
- "extension.sendSyncMessage Done with reply %s",
- reply.c_str());
-
- if (reply.empty()) {
- return ValueRef::createNull();
- }
- return ValueRef::create(
- StringRef::createFromASCII(reply.c_str(), reply.size()));
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->sendSyncMessage->string()),
- ValueRef::create(sendSyncMessageFn), true, true, true);
-
- FunctionObjectRef* sendSyncDataFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->sendSyncData,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_INFO("extension.sendSyncData");
- printArguments(state->context(), argc, argv);
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
- wrt::xwalk::ExtensionInstance* extensionInstance =
- extensionManagerInstance
- ->getExtensionInstanceFromCallingContext(
- state->context(), thisValue);
- if (!extensionInstance || argc < 1) {
- return ValueRef::create(false);
- }
-
- ChunkData chunkData(nullptr, 0);
- if (argc > 1) {
- ValueRef* dataValue = argv[1];
- if (dataValue->isObject()) {
- ObjectRef* arrayData = dataValue->toObject(state);
- size_t length =
- arrayData
- ->get(state,
- ValueRef::create(
- StringRef::createFromASCII("length")))
- ->toLength(state);
- uint8_t* buffer =
- (uint8_t*)malloc(sizeof(uint8_t) * length);
- for (size_t i = 0; i < length; i++) {
- buffer[i] = static_cast<uint8_t>(
- arrayData->get(state, ValueRef::create(i))
- ->toNumber(state));
- }
- chunkData = ChunkData(buffer, length);
- } else if (dataValue->isString()) {
- StringRef* stringData = dataValue->toString(state);
- chunkData = ChunkData(
- (uint8_t*)stringData->toStdUTF8String().c_str(),
- stringData->length());
- }
- }
-
- StringRef* message = argv[0]->toString(state);
- extensionInstance->HandleSyncData(message->toStdUTF8String(),
- chunkData.m_buffer,
- chunkData.m_length);
-
- uint8_t* replyBuffer = nullptr;
- size_t replyLength = 0;
- std::string reply = extensionInstance->sync_data_reply_msg(
- &replyBuffer, &replyLength);
-
- DEVICEAPI_LOG_INFO(
- "extension.sendSyncData Done with reply %s (buffer %s)",
- reply.c_str(), replyBuffer);
-
- if (reply.empty()) {
- return ValueRef::createNull();
- }
-
- ObjectRef* returnObject = ObjectRef::create(state);
- returnObject->defineDataProperty(
- state,
- ValueRef::create(
- extensionManagerInstance->strings()->reply->string()),
- ValueRef::create(StringRef::createFromASCII(reply.c_str(),
- reply.size())),
- true, true, true);
-
- if (replyBuffer || replyLength > 0) {
- size_t chunkID = extensionManagerInstance->addChunk(
- replyBuffer, replyLength);
- returnObject->defineDataProperty(
- state,
- ValueRef::create(extensionManagerInstance->strings()
- ->chunk_id->string()),
- ValueRef::create(chunkID), true, true, true);
- }
-
- return ValueRef::create(returnObject);
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->sendSyncData->string()),
- ValueRef::create(sendSyncDataFn), true, true, true);
-
- FunctionObjectRef* sendRuntimeMessageFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->sendRuntimeMessage,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_ERROR(
- "extension.sendRuntimeMessage UNIMPLEMENTED");
- printArguments(state->context(), argc, argv);
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
- return ValueRef::createUndefined();
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->sendRuntimeMessage->string()),
- ValueRef::create(sendRuntimeMessageFn), true, true, true);
-
- FunctionObjectRef* sendRuntimeAsyncMessageFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->sendRuntimeAsyncMessage,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_ERROR(
- "extension.sendRuntimeAsyncMessage UNIMPLEMENTED");
- printArguments(state->context(), argc, argv);
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
- return ValueRef::createUndefined();
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->sendRuntimeAsyncMessage->string()),
- ValueRef::create(sendRuntimeAsyncMessageFn), true, true, true);
-
- FunctionObjectRef* sendRuntimeSyncMessageFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->sendRuntimeSyncMessage,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_ERROR(
- "extension.sendRuntimeSyncMessage UNIMPLEMENTED");
- printArguments(state->context(), argc, argv);
- NESCARGOT_ASSERT_SHOULD_NOT_BE_HERE();
- return ValueRef::createUndefined();
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->sendRuntimeSyncMessage->string()),
- ValueRef::create(sendRuntimeSyncMessageFn), true, true, true);
-
- FunctionObjectRef* setMessageListenerFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->setMessageListener,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_INFO("extension.setMessageListener");
- printArguments(state->context(), argc, argv);
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
- wrt::xwalk::ExtensionInstance* extensionInstance =
- extensionManagerInstance
- ->getExtensionInstanceFromCallingContext(
- state->context(), thisValue);
-
- if (!extensionInstance || argc != 1) {
- return ValueRef::create(false);
- }
-
- ValueRef* listenerValue = argv[0];
- if (listenerValue->isUndefined()) {
- extensionInstance->set_post_message_listener(nullptr);
- return ValueRef::create(true);
- }
-
- if (!listenerValue->isCallable()) {
- DEVICEAPI_LOG_ERROR("Invalid message listener.");
- return ValueRef::create(false);
- }
-
- ObjectRef* listener = listenerValue->asObject();
- ESPostMessageListener* postMessageListener =
- ESPostMessageListener::create(state->context(), listener);
- extensionInstance->set_post_message_listener(
- postMessageListener);
-
- extensionManagerInstance->m_postListeners.push_back(
- postMessageListener);
-
- return ValueRef::create(true);
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->setMessageListener->string()),
- ValueRef::create(setMessageListenerFn), true, true, true);
-
- FunctionObjectRef* receiveChunkDataFn = FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- m_strings->receiveChunkData,
- [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc,
- ValueRef** argv, bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_ERROR("extension.receiveChunkData");
- printArguments(state->context(), argc, argv);
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
- wrt::xwalk::ExtensionInstance* extensionInstance =
- extensionManagerInstance
- ->getExtensionInstanceFromCallingContext(
- state->context(), thisValue);
-
- if (!extensionInstance || argc < 1) {
- return ValueRef::create(false);
- }
-
- TizenStrings* strings = extensionManagerInstance->strings();
-
- size_t chunkID = argv[0]->toNumber(state);
- ExtensionManagerInstance::ChunkData chunkData =
- extensionManagerInstance->getChunk(chunkID);
- if (!chunkData.m_buffer) {
- return ValueRef::createNull();
- }
-
- StringRef* type = argv[1]->toString(state);
- bool isStringType = (!type->equals(strings->octet->string()));
-
- ValueRef* ret;
- if (isStringType) {
- ret = ValueRef::create(StringRef::createFromUTF8(
- (const char*)chunkData.m_buffer,
- strlen((const char*)chunkData.m_buffer)));
- } else {
- ArrayObjectRef* octetArray = ArrayObjectRef::create(state);
- for (size_t i = 0; i < chunkData.m_length; i++) {
- octetArray->set(
- state, ValueRef::create(i),
- ValueRef::create(chunkData.m_buffer[i]));
- }
- ret = ValueRef::create(octetArray);
- }
- free(chunkData.m_buffer);
- return ret;
- },
- 0, true, true));
-
- extensionObject->defineDataProperty(
- state, ValueRef::create(m_strings->receiveChunkData->string()),
- ValueRef::create(receiveChunkDataFn), true, true, true);
-
- return extensionObject;
-}
-
-wrt::xwalk::ExtensionInstance*
-ExtensionManagerInstance::getExtensionInstanceFromCallingContext(
- ContextRef* context, ValueRef* thisValue)
-{
- DEVICEAPI_LOG_INFO("Enter");
- if (thisValue->isUndefinedOrNull()) {
- return nullptr;
- }
-
- ObjectRef* obj = Evaluator::execute(m_context,
- [](ExecutionStateRef* state,
- ValueRef* thisValue) -> ValueRef* {
- return thisValue->toObject(state);
- },
- thisValue)
- .result->asObject();
-
- auto it = m_extensionInstances.find(obj);
- if (it == m_extensionInstances.end()) {
- return nullptr;
- }
-
- return it->second;
-}
-
-size_t ExtensionManagerInstance::addChunk(uint8_t* buffer, size_t length)
-{
- DEVICEAPI_LOG_INFO("Enter");
- size_t chunkID = m_chunkID++;
- m_chunkDataMap[chunkID] = ChunkData(buffer, length);
- return chunkID;
-}
-
-ExtensionManagerInstance::ChunkData ExtensionManagerInstance::getChunk(
- size_t chunkID)
-{
- DEVICEAPI_LOG_INFO("Enter");
- auto it = m_chunkDataMap.find(chunkID);
- if (it == m_chunkDataMap.end()) {
- return ChunkData(nullptr, 0);
- } else {
- ChunkData chunkData = it->second;
- m_chunkDataMap.erase(it);
- return chunkData;
- }
-}
-
-ExtensionManagerInstance::ExtensionManagerInstanceMap
- ExtensionManagerInstance::s_extensionManagerInstances;
-
-std::mutex ExtensionManagerInstance::s_mutex;
-
-ExtensionManagerInstance::ExtensionManagerInstance(ContextRef* context)
- : m_context(context)
- , m_chunkID(0)
-{
- DEVICEAPI_LOG_INFO("new ExtensionManagerInstance %p", this);
-
-#define DECLARE_TIZEN_OBJECT(name) VALUE_NAME_STRCAT(m_##name) = nullptr;
- FOR_EACH_EARLY_TIZEN_STRINGS(DECLARE_TIZEN_OBJECT);
- FOR_EACH_LAZY_TIZEN_STRINGS(DECLARE_TIZEN_OBJECT);
- SUPPORTED_TIZEN_PROPERTY(DECLARE_TIZEN_OBJECT);
- SUPPORTED_TIZEN_ENTRYPOINTS(DECLARE_TIZEN_OBJECT);
-#undef DECLARE_TIZEN_OBJECT
-
- m_strings = new TizenStrings(m_context);
- Evaluator::execute(
- m_context,
- [](ExecutionStateRef* state,
- ExtensionManagerInstance* self) -> ValueRef* {
- ValueRef* tizenGetter = ValueRef::create(FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- self->m_strings->tizen,
- [](ExecutionStateRef* state, ValueRef* thisValue,
- size_t argc, ValueRef** argv,
- bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_INFO("Enter");
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
-
- if (extensionManagerInstance->m_tizenValue) {
- return extensionManagerInstance->m_tizenValue;
- }
-
- TizenStrings* strings =
- extensionManagerInstance->strings();
- strings->initializeLazyStrings();
-
- // initialize tizen object
- ObjectRef* tizenObject =
- extensionManagerInstance
- ->initializeExtensionInstance("tizen");
- extensionManagerInstance->m_tizenValue =
- ValueRef::create(tizenObject);
-
-#define DEFINE_SUPPORTED_TIZEN_API(name) \
- tizenObject->defineAccessorProperty( \
- state, ValueRef::create(StringRef::createFromASCII("" #name)), \
- ObjectRef::AccessorPropertyDescriptor( \
- ValueRef::create(FunctionObjectRef::create( \
- state, \
- FunctionObjectRef::NativeFunctionInfo( \
- AtomicStringRef::create(state->context(), "" #name), \
- [](ExecutionStateRef* state, ValueRef* thisValue, \
- size_t argc, ValueRef** argv, \
- bool isNewExpression) -> ValueRef* { \
- ExtensionManagerInstance* extensionManagerInstance = \
- get(state->context()); \
- if (extensionManagerInstance->VALUE_NAME_STRCAT( \
- m_##name)) { \
- return extensionManagerInstance \
- ->VALUE_NAME_STRCAT(m_##name); \
- } \
- DEVICEAPI_LOG_INFO("Loading plugin for %s", "" #name); \
- ObjectRef* apiObject = \
- extensionManagerInstance \
- ->initializeExtensionInstance("" #name); \
- extensionManagerInstance->VALUE_NAME_STRCAT( \
- m_##name) = ValueRef::create(apiObject); \
- thisValue->toObject(state)->defineDataProperty( \
- state, ValueRef::create( \
- StringRef::createFromASCII("" #name)), \
- ValueRef::create(apiObject), false, true, false); \
- return ValueRef::create(apiObject); \
- }, \
- 0, true, true))), \
- nullptr, ObjectRef::PresentAttribute::EnumerablePresent));
-
- SUPPORTED_TIZEN_PROPERTY(DEFINE_SUPPORTED_TIZEN_API)
-#undef DEFINE_SUPPORTED_TIZEN_API
-
-#define DEFINE_SUPPORTED_TIZEN_ENTRYPOINTS(name) \
- ObjectRef::NativeDataAccessorPropertyData* nativeData##name = \
- new NativeDataAccessorPropertyDataForEntryPoint( \
- true, true, true, \
- [](ExecutionStateRef* state, ObjectRef* self, \
- ObjectRef::NativeDataAccessorPropertyData* data) -> ValueRef* { \
- ExtensionManagerInstance* extensionManagerInstance = \
- get(state->context()); \
- if (extensionManagerInstance->VALUE_NAME_STRCAT(m_##name)) { \
- return extensionManagerInstance->VALUE_NAME_STRCAT( \
- m_##name); \
- } \
- DEVICEAPI_LOG_INFO("Loading plugin for %s", "" #name); \
- NativeDataAccessorPropertyDataForEntryPoint* myData = \
- (NativeDataAccessorPropertyDataForEntryPoint*)data; \
- extensionManagerInstance->VALUE_NAME_STRCAT(m_##name) = \
- myData->m_data; \
- return myData->m_data; \
- }, \
- [](ExecutionStateRef* state, ObjectRef* self, \
- ObjectRef::NativeDataAccessorPropertyData* data, \
- ValueRef* setterInputData) -> bool { \
- NativeDataAccessorPropertyDataForEntryPoint* myData = \
- (NativeDataAccessorPropertyDataForEntryPoint*)data; \
- myData->m_data = setterInputData; \
- return true; \
- }); \
- tizenObject->defineNativeDataAccessorProperty( \
- state, ValueRef::create(StringRef::createFromASCII(#name)), \
- nativeData##name);
-
- SUPPORTED_TIZEN_ENTRYPOINTS(
- DEFINE_SUPPORTED_TIZEN_ENTRYPOINTS)
-#undef DEFINE_SUPPORTED_TIZEN_ENTRYPOINTS
-
-#if defined(STARFISH_TIZEN_WEARABLE_WIDGET)
- WebWidgetAPIInstance* ww =
- new (GC) WebWidgetAPIInstance();
- extensionManagerInstance->m_webWidgetAPIInstance = ww;
- ObjectRef* widgetAPIObj =
- ww->createWebWidgetAPIObject(state->context());
- tizenObject->defineDataProperty(
- state, ValueRef::create(
- StringRef::createFromASCII("webWidget")),
- ValueRef::create(widgetAPIObj), false, true, false);
-#endif
-
- return ValueRef::create(tizenObject);
- },
- 0, true, true)));
-
- self->m_context->globalObject()->defineAccessorProperty(
- state, ValueRef::create(self->m_strings->tizen->string()),
- ObjectRef::AccessorPropertyDescriptor(
- tizenGetter, nullptr,
- ObjectRef::PresentAttribute::EnumerablePresent));
-
- ValueRef* xwalkGetter = ValueRef::create(FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- self->m_strings->xwalk,
- [](ExecutionStateRef* state, ValueRef* thisValue,
- size_t argc, ValueRef** argv,
- bool isNewExpression) -> ValueRef* {
- DEVICEAPI_LOG_INFO("xwalkGetter Enter");
-
- ExtensionManagerInstance* extensionManagerInstance =
- get(state->context());
- if (extensionManagerInstance->m_xwalkValue) {
- return extensionManagerInstance->m_xwalkValue;
- }
- DEVICEAPI_LOG_INFO("Loading plugin for xwalk.utils");
-
- TizenStrings* strings =
- extensionManagerInstance->strings();
- strings->initializeLazyStrings();
-
- // initialize xwalk object
- ObjectRef* xwalkObject =
- extensionManagerInstance
- ->initializeExtensionInstance("utils");
- extensionManagerInstance->m_xwalkValue =
- ValueRef::create(xwalkObject);
-
- // re-define xwalk object
- thisValue->toObject(state)->defineDataProperty(
- state, ValueRef::create(strings->xwalk->string()),
- ValueRef::create(xwalkObject), false, true, false);
-
- return ValueRef::create(xwalkObject);
- },
- 0, true, true)));
-
- self->m_context->globalObject()->defineAccessorProperty(
- state, ValueRef::create(self->m_strings->xwalk->string()),
- ObjectRef::AccessorPropertyDescriptor(
- xwalkGetter, nullptr,
- ObjectRef::PresentAttribute::EnumerablePresent));
-
- return ValueRef::createUndefined();
-
- },
- this);
-
-#if defined(STARFISH_TIZEN_WEARABLE_WIDGET)
- m_webWidgetAPIInstance = nullptr;
-#endif
- std::lock_guard<std::mutex> guard(s_mutex);
- s_extensionManagerInstances[m_context] = this;
- DEVICEAPI_LOG_INFO("ExtensionManagerInstance %zu => %zu",
- s_extensionManagerInstances.size() - 1,
- s_extensionManagerInstances.size());
-}
-
-ExtensionManagerInstance::~ExtensionManagerInstance()
-{
- std::lock_guard<std::mutex> guard(s_mutex);
- DEVICEAPI_LOG_INFO(
- "ExtensionManagerInstance delete ExtensionManagerInstance %p", this);
- for (auto it : m_extensionInstances)
- delete it.second;
- for (auto it : m_postListeners)
- it->finalize();
- auto it = s_extensionManagerInstances.find(m_context);
- s_extensionManagerInstances.erase(it);
- DEVICEAPI_LOG_INFO("ExtensionManagerInstance %zu => %zu",
- s_extensionManagerInstances.size() + 1,
- s_extensionManagerInstances.size());
-}
-
-ExtensionManagerInstance* ExtensionManagerInstance::get(ContextRef* context)
-{
- std::lock_guard<std::mutex> guard(s_mutex);
- ExtensionManagerInstance* instance = nullptr;
-
- auto it = s_extensionManagerInstances.find(context);
- if (it != s_extensionManagerInstances.end()) {
- instance = it->second;
- }
-
- return instance;
-}
-
-ExtensionManagerInstance* initialize(ContextRef* context)
-{
- DEVICEAPI_LOG_INFO("ExtensionManagerInstance Enter with context %p",
- context);
- return new ExtensionManagerInstance(context);
-}
-
-void close(ContextRef* context)
-{
- DEVICEAPI_LOG_INFO("ExtensionManagerInstance Enter with context %p",
- context);
- delete ExtensionManagerInstance::get(context);
-}
-}
-
-#endif
+++ /dev/null
-// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef __TizenDeviceAPILoaderForEscargot__
-#define __TizenDeviceAPILoaderForEscargot__
-
-#ifdef TIZEN_DEVICE_API
-
-#include <mutex>
-
-#include "escargotbase.h"
-#include "EscargotPublic.h"
-
-#undef LOGGER_TAG
-#define LOGGER_TAG "EscargotNodeDeviceAPI"
-
-#ifndef __MODULE__
-#define __MODULE__ \
- (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
-#endif
-
-// #define _LOGGER_LOG(prio, fmt, args...) \
-// dlog_print(prio, LOGGER_TAG, "%s: %s(%d) > " fmt, __MODULE__, __func__, \
-// __LINE__, ##args);
-
-// #define _LOGGER_SLOG(prio, fmt, args...) \
-// dlog_print(prio, LOGGER_TAG, "%s: %s(%d) > " fmt, __MODULE__, __func__, \
-// __LINE__, ##args);
-
-#define _LOGGER_LOG(prio, fmt, args...) \
- printf("%s: %s(%d) > " fmt "\n", __MODULE__, __func__, \
- __LINE__, ##args);
-
-#define _LOGGER_SLOG(prio, fmt, args...) \
- printf("%s: %s(%d) > " fmt "\n", __MODULE__, __func__, \
- __LINE__, ##args);
-
-#define DEVICEAPI_LOG_INFO(fmt, args...) _LOGGER_LOG(DLOG_INFO, fmt, ##args)
-#define DEVICEAPI_LOG_ERROR(fmt, args...) \
- _LOGGER_LOG(DLOG_ERROR, "Error: " fmt, ##args)
-#define DEVICEAPI_LOG_WARN(fmt, args...) _LOGGER_LOG(DLOG_WARN, fmt, ##args)
-
-#define DEVICEAPI_SLOG_INFO(fmt, args...) _LOGGER_SLOG(DLOG_INFO, fmt, ##args)
-#define DEVICEAPI_SLOG_ERROR(fmt, args...) _LOGGER_SLOG(DLOG_ERROR, fmt, ##args)
-#define DEVICEAPI_SLOG_WARN(fmt, args...) _LOGGER_SLOG(DLOG_WARN, fmt, ##args)
-#define VALUE_NAME_STRCAT(name) name##Value
-
-namespace wrt {
-namespace xwalk {
-class Extension;
-class ExtensionInstance;
-}
-}
-
-namespace DeviceAPI {
-
-class ESPostListener;
-
-#define FOR_EACH_EARLY_TIZEN_STRINGS(F) \
- F(tizen) \
- F(xwalk) \
- F(webapis)
-
-#define FOR_EACH_LAZY_TIZEN_STRINGS(F) \
- F(utils) \
- F(common) \
- F(extension) \
- F(postMessage) \
- F(sendSyncMessage) \
- F(sendSyncData) \
- F(sendRuntimeMessage) \
- F(sendRuntimeSyncMessage) \
- F(sendRuntimeAsyncMessage) \
- F(setMessageListener) \
- F(receiveChunkData) \
- F(reply) \
- F(chunk_id) \
- F(string) \
- F(octet)
-
-#define SUPPORTED_TIZEN_PROPERTY(F) \
- F(application) \
- F(filesystem) \
- F(messageport) \
- F(systeminfo) \
- F(sensorservice) \
- F(preference) \
- F(power) \
- F(time)
-
-#define SUPPORTED_TIZEN_ENTRYPOINTS(F) \
- F(ApplicationControl) \
- F(ApplicationControlData)
-
-class TizenStrings {
- public:
- TizenStrings(Escargot::ContextRef* context);
- void initializeEarlyStrings();
- void initializeLazyStrings();
-
-#define DECLARE_TIZEN_STRING(name) Escargot::AtomicStringRef* name;
- FOR_EACH_EARLY_TIZEN_STRINGS(DECLARE_TIZEN_STRING);
- FOR_EACH_LAZY_TIZEN_STRINGS(DECLARE_TIZEN_STRING);
- SUPPORTED_TIZEN_PROPERTY(DECLARE_TIZEN_STRING);
- SUPPORTED_TIZEN_ENTRYPOINTS(DECLARE_TIZEN_STRING);
-#undef DECLARE_TIZEN_STRING
-
- private:
- Escargot::ContextRef* m_context;
- bool m_initialized;
-};
-
-/*
- * Extension: (tizen, utils, common, messageport, sensorservice...) * 1
- * ExtensionManager: (manager) * 1
- * ExtensionInstance: (tizen, utils, common, messageport, sensorservice...) *
- * number of ESVMInstances
- * ExtensionManagerInstance: (manager) * number of ESVMInstances
- */
-
-class ExtensionManagerInstance : public gc {
- public:
- ExtensionManagerInstance(Escargot::ContextRef* context);
- ~ExtensionManagerInstance();
-
- void* operator new(size_t size);
- void* operator new[](size_t size) = delete;
-
- static ExtensionManagerInstance* get(Escargot::ContextRef* context);
- TizenStrings* strings() { return m_strings; }
- wrt::xwalk::ExtensionInstance* getExtensionInstanceFromCallingContext(
- Escargot::ContextRef*, Escargot::ValueRef* thisValue);
- Escargot::ObjectRef* initializeExtensionInstance(const char*);
-#if defined(STARFISH_TIZEN_WEARABLE_WIDGET)
- WebWidgetAPIInstance* webWidgetAPIInstance() {
- return m_webWidgetAPIInstance;
- }
-#endif
-
- private:
- struct ChunkData {
- ChunkData() {}
- ChunkData(uint8_t* buffer, size_t length)
- : m_buffer(buffer), m_length(length) {}
- uint8_t* m_buffer;
- size_t m_length;
- };
-
- typedef std::map<size_t, ChunkData> ChunkDataMap;
- typedef std::map<Escargot::ObjectRef*, wrt::xwalk::ExtensionInstance*>
- ExtensionInstanceMap;
- typedef std::vector<ESPostListener*> ESPostListenerVector;
-
- Escargot::ObjectRef* createExtensionObject(
- Escargot::ExecutionStateRef* state);
- size_t addChunk(uint8_t* buffer, size_t length);
- ChunkData getChunk(size_t chunkID);
-
- Escargot::ContextRef* m_context;
- ExtensionInstanceMap m_extensionInstances;
- ESPostListenerVector m_postListeners;
- ChunkDataMap m_chunkDataMap;
- size_t m_chunkID;
- TizenStrings* m_strings;
-#if defined(STARFISH_TIZEN_WEARABLE_WIDGET)
- WebWidgetAPIInstance* m_webWidgetAPIInstance;
-#endif
-#define DECLARE_TIZEN_OBJECT(name) \
- Escargot::ValueRef* VALUE_NAME_STRCAT(m_##name);
- FOR_EACH_EARLY_TIZEN_STRINGS(DECLARE_TIZEN_OBJECT);
- FOR_EACH_LAZY_TIZEN_STRINGS(DECLARE_TIZEN_OBJECT);
- SUPPORTED_TIZEN_PROPERTY(DECLARE_TIZEN_OBJECT);
- SUPPORTED_TIZEN_ENTRYPOINTS(DECLARE_TIZEN_OBJECT);
-#undef DECLARE_TIZEN_OBJECT
-
- // static members
- typedef std::map<Escargot::ContextRef*, ExtensionManagerInstance*>
- ExtensionManagerInstanceMap;
- static wrt::xwalk::Extension* getExtension(const char* apiName);
- static ExtensionManagerInstanceMap s_extensionManagerInstances;
- static std::mutex s_mutex;
-};
-
-inline ExtensionManagerInstance* ExtensionManagerInstanceGet(
- Escargot::ContextRef* context) {
- return ExtensionManagerInstance::get(context);
-}
-
-ExtensionManagerInstance* initialize(Escargot::ContextRef* context);
-void close(Escargot::ContextRef* context);
-}
-
-class NativeDataAccessorPropertyDataForEntryPoint
- : public Escargot::ObjectRef::NativeDataAccessorPropertyData {
- public:
- NativeDataAccessorPropertyDataForEntryPoint(
- bool isWritable, bool isEnumerable, bool isConfigurable,
- Escargot::ObjectRef::NativeDataAccessorPropertyGetter getter,
- Escargot::ObjectRef::NativeDataAccessorPropertySetter setter)
- : NativeDataAccessorPropertyData(isWritable, isEnumerable, isConfigurable,
- getter, setter) {
- m_data = Escargot::ValueRef::createUndefined();
- }
-
- void* operator new(size_t size) { return GC_MALLOC(size); }
-
- Escargot::ValueRef* m_data;
-};
-
-#endif // TIZEN_DEVICE_API
-
-#endif // __TizenDeviceAPILoaderForEscargot__
+++ /dev/null
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-
-// Crosswalk Extensions are modules of code loaded by Crosswalk runtime that
-// allow extending its capabilities. The extension is expected to define a
-// XW_Initialize() function as declared below, get the interfaces it need to
-// use and register to whatever callbacks it needs, then return XW_OK.
-//
-// The Extension is represented by the type XW_Extension. Each extension
-// loaded may be used multiple times for different pages, so to each execution
-// there will be an associated XW_Instance. A reasonable analogy is that the
-// XW_Extension represent a "class", and have concrete instances running.
-//
-// An interface is a struct with a set of functions, provided by Crosswalk,
-// that allow the extension code to interact with the web content. Certain
-// functions in an interface are used to register callbacks, so that Crosswalk
-// can call the extension at specific situations.
-//
-// Crosswalk won't call an extension's XW_Initialize() multiple times in the
-// same process.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if __GNUC__ >= 4
-#define XW_EXPORT __attribute__((visibility("default")))
-#elif defined(_MSC_VER)
-#define XW_EXPORT __declspec(dllexport)
-#endif
-
-#include <stddef.h>
-#include <stdint.h>
-
-// XW_Extension is used to identify your extension when calling functions from
-// the API. You should always use the XW_Extension received at XW_Initialize().
-//
-// XW_Instance is used to identify different web contents using your
-// extension. Each time a new web content is created you can be notified
-// registering the XW_CreatedInstanceCallback, that receives the new
-// XW_Instance. When interacting with an Instance (for example to post a
-// message), you should pass the corresponding XW_Instance.
-//
-// In both types the zero value is never used by Crosswalk, so can be used to
-// initialize variables.
-typedef int32_t XW_Extension;
-typedef int32_t XW_Instance;
-
-enum { XW_OK = 0, XW_ERROR = -1 };
-
-// Returns a struct containing functions to be used by the extension. Those
-// structs can be stored statically and used until the extension is unloaded.
-// Extensions should use definitions like XW_CORE_INTERFACE, instead of using
-// the versioned definition or the literal string. Returns NULL if the
-// interface is not supported.
-typedef const void* (*XW_GetInterface)(const char* interface_name);
-
-typedef int32_t (*XW_Initialize_Func)(XW_Extension extension,
- XW_GetInterface get_interface);
-
-// XW_Initialize is called after the extension code is loaded. The 'extension'
-// value should be used in further calls that expect XW_Extension argument.
-//
-// The 'get_interface' function should be used to get access to functions that
-// interact with the web content. It is only valid during the execution of the
-// XW_Initialize() function.
-//
-// This function should return XW_OK when the extension was succesfully
-// loaded, otherwise XW_ERROR.
-XW_EXPORT int32_t XW_Initialize(XW_Extension extension,
- XW_GetInterface get_interface);
-
-//
-// XW_CORE_INTERFACE: Basic functionality for Crosswalk Extensions. All
-// extensions should use this interface to set at least their name.
-//
-
-#define XW_CORE_INTERFACE_1 "XW_CoreInterface_1"
-#define XW_CORE_INTERFACE XW_CORE_INTERFACE_1
-
-typedef void (*XW_CreatedInstanceCallback)(XW_Instance instance);
-typedef void (*XW_DestroyedInstanceCallback)(XW_Instance instance);
-typedef void (*XW_ShutdownCallback)(XW_Extension extension);
-
-struct XW_CoreInterface_1 {
- // Set the name of the extension. It is used as the namespace for the
- // JavaScript code exposed by the extension. So extension named
- // 'my_extension', will expose its JavaScript functionality inside
- // the 'my_extension' namespace.
- //
- // This function should be called only during XW_Initialize().
- void (*SetExtensionName)(XW_Extension extension, const char* name);
-
- // Set the JavaScript code loaded in the web content when the extension is
- // used. This can be used together with the messaging mechanism to implement
- // a higher-level API that posts messages to extensions, see
- // XW_MESSAGING_INTERFACE below.
- //
- // The code will be executed inside a JS function context with the following
- // objects available:
- //
- // - exports: this object should be filled with properties and functions
- // that will be exposed in the namespace associated with this
- // extension.
- //
- // - extension.postMessage(): post a string message to the extension native
- // code. See below for details.
- // - extension.setMessageListener(): allow setting a callback that is called
- // when the native code sends a message
- // to JavaScript. Callback takes a string.
- //
- // This function should be called only during XW_Initialize().
- void (*SetJavaScriptAPI)(XW_Extension extension, const char* api);
-
- // Register callbacks that are called when an instance of this extension
- // is created or destroyed. Everytime a new web content is loaded, it will
- // get a new associated instance.
- //
- // This function should be called only during XW_Initialize().
- void (*RegisterInstanceCallbacks)(XW_Extension extension,
- XW_CreatedInstanceCallback created,
- XW_DestroyedInstanceCallback destroyed);
-
- // Register a callback to be executed when the extension will be unloaded.
- //
- // This function should be called only during XW_Initialize().
- void (*RegisterShutdownCallback)(XW_Extension extension,
- XW_ShutdownCallback shutdown_callback);
-
- // These two functions are conveniences used to associated arbitrary data
- // with a given XW_Instance. They can be used only with instances that were
- // created but not yet completely destroyed. GetInstanceData() can be used
- // during the destroyed instance callback. If not instance data was set,
- // getting it returns NULL.
- void (*SetInstanceData)(XW_Instance instance, void* data);
- void* (*GetInstanceData)(XW_Instance instance);
-};
-
-typedef struct XW_CoreInterface_1 XW_CoreInterface;
-
-//
-// XW_MESSAGING_INTERFACE: Exchange asynchronous messages with JavaScript
-// code provided by extension.
-//
-
-#define XW_MESSAGING_INTERFACE_1 "XW_MessagingInterface_1"
-#define XW_MESSAGING_INTERFACE XW_MESSAGING_INTERFACE_1
-
-typedef void (*XW_HandleMessageCallback)(XW_Instance instance,
- const char* message);
-
-struct XW_MessagingInterface_1 {
- // Register a callback to be called when the JavaScript code associated
- // with the extension posts a message. Note that the callback will be called
- // with the XW_Instance that posted the message as well as the message
- // contents.
- void (*Register)(XW_Extension extension,
- XW_HandleMessageCallback handle_message);
-
- // Post a message to the web content associated with the instance. To
- // receive this message the extension's JavaScript code should set a
- // listener using extension.setMessageListener() function.
- //
- // This function is thread-safe and can be called until the instance is
- // destroyed.
- void (*PostMessage)(XW_Instance instance, const char* message);
-};
-
-typedef struct XW_MessagingInterface_1 XW_MessagingInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+++ /dev/null
-/*
- * Copyright (c) 2017-present Samsung Electronics Co., Ltd
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- * USA
- */
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_DATA_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_DATA_H_
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#error "You should include XW_Extension.h before this file"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// XW_INTERNAL_DATA_INTERFACE: allow extensions to exchange binary chunk data
-// between extension and loader.
-//
-
-#define XW_INTERNAL_DATA_INTERFACE_1 "XW_InternalDataInterface_1"
-#define XW_INTERNAL_DATA_INTERFACE XW_INTERNAL_DATA_INTERFACE_1
-
-// Synchronous / Asynchronous data exchanging interface
-typedef void (*XW_HandleDataCallback)(XW_Instance instance, const char* message,
- uint8_t* buffer, size_t len);
-
-struct XW_Internal_DataInterface_1 {
- void (*RegisterSync)(XW_Extension extension,
- XW_HandleDataCallback handle_data);
-
- void (*RegisterAsync)(XW_Extension extension,
- XW_HandleDataCallback handle_data);
-
- void (*SetSyncReply)(XW_Instance instance, const char* reply,
- uint8_t* buffer, size_t len);
-
- void (*PostData)(XW_Instance instance, const char* message, uint8_t* buffer,
- size_t len);
-};
-
-typedef struct XW_Internal_DataInterface_1 XW_Internal_DataInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_DATA_H_
+++ /dev/null
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
-
-// NOTE: This file and interfaces marked as internal are not considered stable
-// and can be modified in incompatible ways between Crosswalk versions.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#error "You should include XW_Extension.h before this file"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define XW_INTERNAL_ENTRY_POINTS_INTERFACE_1 \
- "XW_Internal_EntryPointsInterface_1"
-#define XW_INTERNAL_ENTRY_POINTS_INTERFACE XW_INTERNAL_ENTRY_POINTS_INTERFACE_1
-
-//
-// XW_INTERNAL_ENTRY_POINTS_INTERFACE: provides a way for extensions to add
-// more information about its implementation. For now, allow extensions to
-// specify more objects that the access should cause the extension to be
-// loaded.
-//
-
-struct XW_Internal_EntryPointsInterface_1 {
- // Register extra entry points for this extension. An "extra" entry points
- // are objects outside the implicit namespace for which the extension should
- // be loaded when they are touched.
- //
- // This function should be called only during XW_Initialize().
- void (*SetExtraJSEntryPoints)(XW_Extension extension,
- const char** entry_points);
-};
-
-typedef struct XW_Internal_EntryPointsInterface_1
- XW_Internal_EntryPointsInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
+++ /dev/null
-// Copyright (c) 2014 Intel Corporation. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
-
-// NOTE: This file and interfaces marked as internal are not considered stable
-// and can be modified in incompatible ways between Crosswalk versions.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#error "You should include XW_Extension.h before this file"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define XW_INTERNAL_PERMISSIONS_INTERFACE_1 "XW_Internal_PermissionsInterface_1"
-#define XW_INTERNAL_PERMISSIONS_INTERFACE XW_INTERNAL_PERMISSIONS_INTERFACE_1
-
-//
-// XW_INTERNAL_PERMISSIONS_INTERFACE: provides a way for extensions
-// check if they have the proper permissions for certain APIs.
-//
-
-struct XW_Internal_PermissionsInterface_1 {
- int (*CheckAPIAccessControl)(XW_Extension extension, const char* api_name);
- int (*RegisterPermissions)(XW_Extension extension, const char* perm_table);
-};
-
-typedef struct XW_Internal_PermissionsInterface_1
- XW_Internal_PermissionsInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
+++ /dev/null
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
-
-// NOTE: This file and interfaces marked as internal are not considered stable
-// and can be modified in incompatible ways between Crosswalk versions.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#error "You should include XW_Extension.h before this file"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define XW_INTERNAL_RUNTIME_INTERFACE_1 "XW_Internal_RuntimeInterface_1"
-#define XW_INTERNAL_RUNTIME_INTERFACE XW_INTERNAL_RUNTIME_INTERFACE_1
-
-//
-// XW_INTERNAL_RUNTIME_INTERFACE: allow extensions to gather information
-// from the runtime.
-//
-
-struct XW_Internal_RuntimeInterface_1 {
- void (*GetRuntimeVariableString)(XW_Extension extension, const char* key,
- char* value, unsigned int value_len);
-};
-
-typedef struct XW_Internal_RuntimeInterface_1 XW_Internal_RuntimeInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
+++ /dev/null
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
-#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
-
-// NOTE: This file and interfaces marked as internal are not considered stable
-// and can be modified in incompatible ways between Crosswalk versions.
-
-#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
-#error "You should include XW_Extension.h before this file"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// XW_INTERNAL_SYNC_MESSAGING_INTERFACE: allow JavaScript code to send a
-// synchronous message to extension code and block until response is
-// available. The response is made available by calling the SetSyncReply
-// function, that can be done from outside the context of the SyncMessage
-// handler.
-//
-
-#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1 \
- "XW_InternalSyncMessagingInterface_1"
-#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE \
- XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1
-
-typedef void (*XW_HandleSyncMessageCallback)(XW_Instance instance,
- const char* message);
-
-struct XW_Internal_SyncMessagingInterface_1 {
- void (*Register)(XW_Extension extension,
- XW_HandleSyncMessageCallback handle_sync_message);
- void (*SetSyncReply)(XW_Instance instance, const char* reply);
-};
-
-typedef struct XW_Internal_SyncMessagingInterface_1
- XW_Internal_SyncMessagingInterface;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
+++ /dev/null
-/*
- * Copyright (c) 2017-present Samsung Electronics Co., Ltd
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- * USA
- */
-
-#ifndef WRT_COMMON_NATIVE_PLUGIN_H_
-#define WRT_COMMON_NATIVE_PLUGIN_H_
-
-#include <string>
-
-namespace wrt {
-namespace common {
-
- class NativePlugin {
- public:
- virtual void OnLoad() = 0;
- virtual std::string OnCallSync(std::string& data) = 0;
- virtual std::string OnCall(std::string& data, int callback_handle) = 0;
- };
-
- typedef NativePlugin* create_native_plugin_t();
-
-} // namespace common
-} // namespace wrt
-
-#define EXPORT_NATIVE_PLUGIN(pluginClass) \
- extern "C" wrt::common::NativePlugin* create_native_plugin() \
- { \
- return (wrt::common::NativePlugin*)(new pluginClass()); \
- }
-
-#endif // WRT_COMMON_NATIVE_PLUGIN_H_
'target_arch%': '<(target_arch)',
'target_os%': '<(target_os)',
'build_host%': '<(build_host)',
- 'build_asan%': '<(build_asan)',
+ 'asan%': '<(asan)',
'escargot_dir%': 'deps/escargot',
"escargot_lib_type%": 'shared_lib', # static_lib | shared_lib
'escargot_threading%': '<(escargot_threading)',
+ 'escargot_debugger%': '<(escargot_debugger)',
'conditions': [
['escargot_lib_type=="shared_lib"', {
'lib_ext': '.so'
'defines': [
'ESCARGOT_ENABLE_TYPEDARRAY=1',
'ESCARGOT_ENABLE_PROMISE=1',
+ 'ESCARGOT_USE_CUSTOM_LOGGING=1',
],
'include_dirs': [
'<(escargot_dir)/src/api',
'cmake', '<(escargot_dir)', '-B<(output_dir)',
'-GNinja',
'-DESCARGOT_SMALL_CONFIG=1',
+ '-DESCARGOT_USE_CUSTOM_LOGGING=ON',
'-DESCARGOT_ARCH=<(target_arch)',
'-DESCARGOT_MODE=<(build_mode)',
'-DESCARGOT_HOST=<(build_host)',
'-DESCARGOT_OUTPUT=<(escargot_lib_type)',
'-DESCARGOT_THREADING=<(escargot_threading)',
- '-DESCARGOT_ASAN=<(build_asan)',
+ '-DESCARGOT_ASAN=<(asan)',
+ '-DESCARGOT_DEBUGGER=<(escargot_debugger)'
],
},
{
'variables': {
'library%': 'static_library',
'tizen_device_api_dir': 'deps/tizen-device-api',
+ 'node_bindings_dir': 'deps/node-bindings',
'enable_escargotshim_asan%': 0,
'enable_reload_script%': 'false',
+ 'escargot_debugger%': 'false',
'enable_experimental%': 'false',
+ 'include_node_bindings%': 'true',
},
'targets': [
{
'src/api-serialization.cc',
'src/api-template.cc',
'src/api-additionals.cc',
+ 'src/api-experimental-serialization.cc',
+ 'src/base.cc',
'src/internal.cc',
'src/init/v8.cc',
'src/execution/v8threads.cc',
'src/api/utils/smaps.cc',
'src/api/utils/string-util.cc',
'src/api/utils/logger/flags.cc',
- 'src/api/utils/logger/logger.cc',
+ 'src/api/utils/logger/logger-impl.cc',
+ 'src/api/utils/logger/logger-util.cc',
'src/api/arraybuffer-allocator.cc',
'src/api/arraybuffer-deleter.cc',
'src/api/es-helper.cc',
'src/api/isolate.cc',
'src/api/context.cc',
'src/api/global-handles.cc',
+ 'src/api/global.cc',
'src/api/function.cc',
'src/api/object.cc',
'src/api/stack-trace.cc',
+ 'src/api/serializer.cc',
+ 'src/api/error-message.cc',
'src/lwnode/lwnode.cc',
'src/lwnode/lwnode-loader.cc',
'src/lwnode/lwnode-gc-strategy.cc',
],
- 'defines': ['V8_PROMISE_INTERNAL_FIELD_COUNT=1'],
+ 'defines': ['V8_PROMISE_INTERNAL_FIELD_COUNT=1',
+ 'LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION=1',
+ ],
'cflags_cc!': ['-fno-exceptions'],
'cflags_cc': [
'-std=gnu++14',
['enable_reload_script == "true"', {
'defines': ['LWNODE_USE_RELOAD_SCRIPT'],
}],
+ ['escargot_debugger == 1', {
+ 'defines': ['LWNODE_ENABLE_DEBUGGER']
+ }],
['enable_experimental == "true"', {
'defines': [
'LWNODE_ENABLE_EXPERIMENTAL=1',
+ 'LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION=1',
+ 'LWNODE_ENABLE_EXPERIMENTAL_PROMISE=1',
+ 'LWNODE_ENABLE_EXPERIMENTAL_STACKTRACE=1',
+ ],
+ }],
+ ['include_node_bindings == "true"', {
+ 'dependencies': [
+ '<(node_bindings_dir)/node_bindings.gyp:node_bindings',
],
}],
],
#include <functional>
#include <memory>
+namespace Escargot {
+class ContextRef;
+class ValueRef;
+} // namespace Escargot
+
namespace LWNode {
void InitializeProcessMethods(v8::Local<v8::Object> target,
v8::Local<v8::Context> context);
-void IdleGC(v8::Isolate* isolate);
+void IdleGC(v8::Isolate* isolate = nullptr);
+void initDebugger();
bool dumpSelfMemorySnapshot();
class MessageLoop {
std::unique_ptr<Internal> internal_;
};
+class Utils {
+ public:
+ static Escargot::ContextRef* ToEsContext(v8::Context* context);
+
+ static v8::Local<v8::Value> NewLocal(v8::Isolate* isolate,
+ Escargot::ValueRef* ptr);
+};
+
} // namespace LWNode
*/
#include "api.h"
+
+#include "api/global.h"
#include "api/utils.h"
#include "base.h"
}
bool Value::IsName() const {
- auto esValue = CVAL(this)->value();
- return esValue->isString() || esValue->isSymbol();
+ auto esSelf = CVAL(this)->value();
+ return esSelf->isString() || esSelf->isSymbol();
}
bool Value::FullIsString() const {
}
bool Value::IsExternal() const {
- auto esValue = CVAL(this)->value();
- if (!esValue->isObject()) {
+ auto esSelf = CVAL(this)->value();
+ if (!esSelf->isObject()) {
return false;
}
- auto data = ObjectRefHelper::getExtraData(esValue->asObject());
+ auto data = ObjectRefHelper::getExtraData(esSelf->asObject());
return data && data->isExternalObjectData();
}
if (esSelf->isInt32()) {
return true;
} else if (esSelf->isNumber()) {
- double val = esSelf->asNumber();
- bool rangeOk = false;
- if (std::numeric_limits<int32_t>::min() <= val &&
- val <= std::numeric_limits<int32_t>::max()) {
- rangeOk = true;
+ double value = esSelf->asNumber();
+ bool isInRange = false;
+ if (std::numeric_limits<int32_t>::min() <= value &&
+ value <= std::numeric_limits<int32_t>::max()) {
+ isInRange = true;
}
- return isInteger(esSelf) && rangeOk;
+ return isInteger(esSelf) && isInRange;
}
return false;
if (esSelf->isUInt32()) {
return true;
} else if (esSelf->isNumber()) {
- double val = esSelf->asNumber();
- bool rangeOk = false;
- if (0 <= val && val <= std::numeric_limits<uint32_t>::max()) {
- rangeOk = true;
+ double value = esSelf->asNumber();
+ bool isInRange = false;
+ if (0 <= value && value <= std::numeric_limits<uint32_t>::max()) {
+ isInRange = true;
}
- return isInteger(esSelf) && rangeOk;
+ return isInteger(esSelf) && isInRange;
}
return false;
}
bool Value::IsAsyncFunction() const {
- auto esValue = CVAL(this)->value();
+ auto esSelf = CVAL(this)->value();
- if (esValue->isFunctionObject() == false) {
+ if (esSelf->isFunctionObject() == false) {
return false;
}
auto esPureContext =
ContextRef::create(IsolateWrap::GetCurrent()->vmInstance());
auto esString =
- ObjectRefHelper::getConstructorName(esPureContext, esValue->asObject());
+ ObjectRefHelper::getConstructorName(esPureContext, esSelf->asObject());
return StringRefHelper::equalsWithASCIIString(esString, "AsyncFunction");
}
auto esContext = VAL(*context)->context()->get();
- auto esValue = CVAL(this)->value();
- if (esValue->isString()) {
- return Utils::NewLocal<String>(lwIsolate->toV8(), esValue);
+ auto esSelf = CVAL(this)->value();
+ if (esSelf->isString()) {
+ return Utils::NewLocal<String>(lwIsolate->toV8(), esSelf);
}
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* state, ValueRef* esValue) -> ValueRef* {
- return esValue->toString(state);
+ [](ExecutionStateRef* state, ValueRef* esSelf) -> ValueRef* {
+ return esSelf->toString(state);
},
- esValue);
+ esSelf);
API_HANDLE_EXCEPTION(r, lwIsolate, MaybeLocal<String>());
}
MaybeLocal<String> Value::ToDetailString(Local<Context> context) const {
- LWNODE_RETURN_LOCAL(String);
+ EsScope scope(context, this);
+
+ auto esString = scope.self()->toStringWithoutException(scope.context());
+ return Utils::NewLocal<String>(scope.v8Isolate(), esString);
}
MaybeLocal<Object> Value::ToObject(Local<Context> context) const {
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Object>());
auto esContext = VAL(*context)->context()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* state, ValueRef* esValue) -> ValueRef* {
- return esValue->toObject(state);
+ [](ExecutionStateRef* state, ValueRef* esSelf) -> ValueRef* {
+ return esSelf->toObject(state);
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, MaybeLocal<Object>());
return Utils::NewLocal<Object>(lwIsolate->toV8(), r.result);
}
bool Value::BooleanValue(Isolate* v8_isolate) const {
- Local<Boolean> val = ToBoolean(v8_isolate);
- return val->Value();
+ return ToBoolean(v8_isolate)->Value();
}
Local<Boolean> Value::ToBoolean(Isolate* v8_isolate) const {
API_ENTER(v8_isolate, Local<Boolean>());
auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* esState, ValueRef* esValue) -> ValueRef* {
- return ValueRef::create(esValue->toBoolean(esState));
+ [](ExecutionStateRef* esState, ValueRef* esSelf) -> ValueRef* {
+ return ValueRef::create(esSelf->toBoolean(esState));
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, Local<Boolean>());
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Number>());
auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* esState, ValueRef* esValue) -> ValueRef* {
- return ValueRef::create(esValue->toNumber(esState));
+ [](ExecutionStateRef* esState, ValueRef* esSelf) -> ValueRef* {
+ return ValueRef::create(esSelf->toNumber(esState));
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, Local<Number>());
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Integer>());
auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* esState, ValueRef* esValue) -> ValueRef* {
- return ValueRef::create(esValue->toInteger(esState));
+ [](ExecutionStateRef* esState, ValueRef* esSelf) -> ValueRef* {
+ return ValueRef::create(esSelf->toInteger(esState));
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, Local<Integer>());
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Int32>());
auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* esState, ValueRef* esValue) -> ValueRef* {
- return ValueRef::create(esValue->toInt32(esState));
+ [](ExecutionStateRef* esState, ValueRef* esSelf) -> ValueRef* {
+ return ValueRef::create(esSelf->toInt32(esState));
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, Local<Int32>());
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Uint32>());
auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esValue = CVAL(this)->value();
EvalResult r = Evaluator::execute(
esContext,
- [](ExecutionStateRef* esState, ValueRef* esValue) -> ValueRef* {
- return ValueRef::create(esValue->toUint32(esState));
+ [](ExecutionStateRef* esState, ValueRef* esSelf) -> ValueRef* {
+ return ValueRef::create(esSelf->toUint32(esState));
},
- esValue);
+ CVAL(this)->value());
API_HANDLE_EXCEPTION(r, lwIsolate, Local<Uint32>());
i::Isolate* i::IsolateFromNeverReadOnlySpaceObject(i::Address obj) {
LWNODE_RETURN_NULLPTR;
- ;
}
bool i::ShouldThrowOnError(i::Isolate* isolate) {
}
#define CHECK_TYPED_ARRAY_CAST(Type, typeName, TYPE, ctype) \
- void v8::Type##Array::CheckCast(Value* that) { LWNODE_UNIMPLEMENT; }
+ void v8::Type##Array::CheckCast(Value* that) { \
+ LWNODE_CHECK(that->Is##Type##Array()); \
+ }
TYPED_ARRAYS(CHECK_TYPED_ARRAY_CAST)
}
Maybe<double> Value::NumberValue(Local<Context> context) const {
- auto lwValue = CVAL(this)->value();
- if (lwValue->isNumber()) {
- return Just(lwValue->asNumber());
+ auto esSelf = CVAL(this)->value();
+ if (esSelf->isNumber()) {
+ return Just(esSelf->asNumber());
}
API_ENTER_WITH_CONTEXT(context, Nothing<double>());
[](ExecutionStateRef* esState, ValueRef* self) {
return ValueRef::create(self->toNumber(esState));
},
- lwValue);
+ esSelf);
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<double>());
return Just(r.result->asNumber());
API_ENTER_WITH_CONTEXT(context, Nothing<uint32_t>());
auto lwContext = lwIsolate->GetCurrentContext();
- uint32_t val = 0;
+ uint32_t value = 0;
auto r = Evaluator::execute(
lwContext->get(),
- [](ExecutionStateRef* esState, ValueRef* self, uint32_t* val) {
- *val = self->toUint32(esState);
- return ValueRef::create(*val);
+ [](ExecutionStateRef* esState, ValueRef* self, uint32_t* value) {
+ *value = self->toUint32(esState);
+ return ValueRef::create(*value);
},
CVAL(this)->value(),
- &val);
+ &value);
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<uint32_t>());
- return Just(val);
+ return Just(value);
}
MaybeLocal<Uint32> Value::ToArrayIndex(Local<Context> context) const {
}
bool Value::StrictEquals(Local<Value> that) const {
- auto esValue = CVAL(this)->value();
auto lwIsolate = IsolateWrap::GetCurrent();
auto lwContext = lwIsolate->GetCurrentContext();
[](ExecutionStateRef* esState,
ValueRef* self,
ValueRef* that) -> ValueRef* {
- bool r = self->equalsTo(esState, that);
- return ValueRef::create(r);
+ return ValueRef::create(self->equalsTo(esState, that));
},
CVAL(this)->value(),
CVAL(*that)->value());
Local<String> Value::TypeOf(v8::Isolate* external_isolate) {
API_ENTER_NO_EXCEPTION(external_isolate);
- auto esSelf = CVAL(this)->value();
auto lwContext = lwIsolate->GetCurrentContext();
auto r = Evaluator::execute(
return ValueRef::create(
StringRef::createFromASCII(type.data(), type.length()));
},
- esSelf);
+ CVAL(this)->value());
+
LWNODE_CHECK(r.isSuccessful());
return Utils::NewLocal<String>(lwIsolate->toV8(), r.result);
Maybe<bool> Value::InstanceOf(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
API_ENTER_WITH_CONTEXT(context, Nothing<bool>());
- auto esSelf = CVAL(this)->value();
auto lwContext = lwIsolate->GetCurrentContext();
- auto esObject = CVAL(*object)->value()->asObject();
auto r = Evaluator::execute(
lwContext->get(),
ObjectRef* object) -> ValueRef* {
return ValueRef::create(self->instanceOf(esState, object));
},
- esSelf,
- esObject);
+ CVAL(this)->value(),
+ CVAL(*object)->value()->asObject());
+
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
return Just(r.result->asBoolean());
}
PropertyDescriptor::PrivateData::~PrivateData() {
- if (!isExternalDescriptor) {
+ if (!isExternalDescriptor_) {
delete descriptor_;
descriptor_ = nullptr;
}
Escargot::ObjectPropertyDescriptorRef* descriptor) {
LWNODE_CHECK_NULL(descriptor_);
descriptor_ = descriptor;
- isExternalDescriptor = true;
+ isExternalDescriptor_ = true;
}
v8::PropertyDescriptor::PropertyDescriptor() : private_(new PrivateData()) {}
auto esObject = CVAL(this)->value()->asObject();
auto esKey = CVAL(*key)->value();
- ObjectRef::PresentAttribute attr = ObjectRef::PresentAttribute::NotPresent;
+ ObjectRef::PresentAttribute attribute =
+ ObjectRef::PresentAttribute::NotPresent;
if (descriptor.has_writable()) {
if (descriptor.writable()) {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::WritablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::WritablePresent);
} else {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::NonWritablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::NonWritablePresent);
}
}
if (descriptor.has_enumerable()) {
if (descriptor.enumerable()) {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::EnumerablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::EnumerablePresent);
} else {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::NonEnumerablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::NonEnumerablePresent);
}
}
if (descriptor.has_configurable()) {
if (descriptor.enumerable()) {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::ConfigurablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::ConfigurablePresent);
} else {
- attr = static_cast<ObjectRef::PresentAttribute>(
- attr | ObjectRef::PresentAttribute::NonConfigurablePresent);
+ attribute = static_cast<ObjectRef::PresentAttribute>(
+ attribute | ObjectRef::PresentAttribute::NonConfigurablePresent);
}
}
if (descriptor.has_value()) {
- ObjectRef::DataPropertyDescriptor desc(CVAL(*descriptor.value())->value(),
- attr);
- auto r =
- ObjectRefHelper::defineDataProperty(esContext, esObject, esKey, desc);
+ ObjectRef::DataPropertyDescriptor dataDescriptor(
+ CVAL(*descriptor.value())->value(), attribute);
+ auto r = ObjectRefHelper::defineDataProperty(
+ esContext, esObject, esKey, dataDescriptor);
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
return Just(r.result->asBoolean());
} else if (descriptor.has_get()) {
if (descriptor.has_set()) {
setter = CVAL(*descriptor.set())->value();
}
- ObjectRef::AccessorPropertyDescriptor desc(
- CVAL(*descriptor.get())->value(), setter, attr);
+ ObjectRef::AccessorPropertyDescriptor accessorDescriptor(
+ CVAL(*descriptor.get())->value(), setter, attribute);
auto r = ObjectRefHelper::defineAccessorProperty(
- esContext, esObject, esKey, desc);
+ esContext, esObject, esKey, accessorDescriptor);
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
return Just(r.result->asBoolean());
} else {
- ObjectRef::DataPropertyDescriptor desc(ValueRef::createUndefined(), attr);
- auto r =
- ObjectRefHelper::defineDataProperty(esContext, esObject, esKey, desc);
+ ObjectRef::DataPropertyDescriptor dataDescriptor(
+ ValueRef::createUndefined(), attribute);
+ auto r = ObjectRefHelper::defineDataProperty(
+ esContext, esObject, esKey, dataDescriptor);
API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
return Just(r.result->asBoolean());
}
-
- return Just(false);
}
Maybe<bool> v8::Object::SetPrivate(Local<Context> context,
return Just(PropertyAttribute::None);
}
- PropertyAttribute attr = static_cast<PropertyAttribute>(r.result->asUInt32());
- return Just(attr);
+ PropertyAttribute attribute =
+ static_cast<PropertyAttribute>(r.result->asUInt32());
+ return Just(attribute);
}
MaybeLocal<Value> v8::Object::GetOwnPropertyDescriptor(Local<Context> context,
return Just(PropertyAttribute::None);
}
- auto attr = static_cast<ObjectRef::PresentAttribute>(r.result->asUInt32());
- return Just(V8Helper::toPropertyAttribute(attr));
+ auto attribute =
+ static_cast<ObjectRef::PresentAttribute>(r.result->asUInt32());
+ return Just(V8Helper::toPropertyAttribute(attribute));
}
Local<v8::Object> v8::Object::Clone() {
arguments.push_back(VAL(*argv[i])->value());
}
+ lwIsolate->increaseCallDepth();
auto r = Evaluator::execute(
esContext,
[](ExecutionStateRef* state,
+ IsolateWrap* lwIsolate,
ValueRef* self,
ValueRef* receiver,
const size_t argc,
ValueRef** argv) -> ValueRef* {
- return self->call(state, receiver, argc, argv);
+ auto r = self->call(state, receiver, argc, argv);
+ lwIsolate->ThrowErrorIfHasException(state);
+ return r;
},
+ lwIsolate,
CVAL(this)->value(),
CVAL(*recv)->value(),
arguments.size(),
arguments.data());
+ lwIsolate->decreaseCallDepth();
if (!r.isSuccessful()) {
- LWNODE_DLOG_ERROR("Evaluate");
+ LWNODE_DLOG_ERROR("Function::Call()");
LWNODE_DLOG_RAW("Internal:\n this: %p (es: %p)\n recv: %p (es: %p)",
this,
CVAL(this)->value(),
toStdStringWithoutException(esContext, esValue).c_str());
}
- LWNODE_DLOG_RAW("Execute:\n %s (%s:%d)\nResource:\n %s\n%s",
- TRACE_ARGS2,
+ LWNODE_DLOG_RAW("Execute:\n %s\nResource:\n %s\n%s",
+ __CODE_LOCATION__,
"N/A",
EvalResultHelper::getErrorString(
lwIsolate->GetCurrentContext()->get(), r)
.c_str());
- lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTraceData);
+ if (EscargotShim::Global::flags()->isOn(
+ Flag::Type::AbortOnUncaughtException)) {
+ if (!lwIsolate->abortOnUncaughtExceptionCallback() ||
+ lwIsolate->abortOnUncaughtExceptionCallback()(lwIsolate->toV8())) {
+ LWNODE_DLOG_INFO("Abort because of uncaught exception callback!");
+ abort();
+ }
+ }
+
+ if (lwIsolate->hasCallDepth()) {
+ lwIsolate->ScheduleThrow(r.error.get());
+ } else {
+ lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTrace);
+ lwIsolate->ReportPendingMessages();
+ }
+ return MaybeLocal<Value>();
+ }
+
+ if (lwIsolate->sholdReportPendingMessage(false)) {
lwIsolate->ReportPendingMessages();
return MaybeLocal<Value>();
}
}
void Function::SetName(v8::Local<v8::String> name) {
- LWNODE_ONCE(LWNODE_UNIMPLEMENT);
+ auto r = Evaluator::execute(
+ IsolateWrap::GetCurrent()->GetCurrentContext()->get(),
+ [](ExecutionStateRef* esState,
+ FunctionObjectRef* esFunction,
+ StringRef* esName) -> ValueRef* {
+ esFunction->setName(
+ AtomicStringRef::create(esState->context(), esName));
+ return ValueRef::createNull();
+ },
+ CVAL(this)->value()->asFunctionObject(),
+ CVAL(*name)->value()->asString());
+ LWNODE_CHECK(r.isSuccessful());
}
Local<Value> Function::GetName() const {
- LWNODE_RETURN_LOCAL(Value);
+ auto lwIsolate = IsolateWrap::GetCurrent();
+
+ auto r = Evaluator::execute(
+ lwIsolate->GetCurrentContext()->get(),
+ [](ExecutionStateRef* esState,
+ FunctionObjectRef* esFunction) -> ValueRef* {
+ return esFunction->getOwnProperty(esState,
+ StringRef::createFromASCII("name"));
+ },
+ CVAL(this)->value()->asFunctionObject());
+ LWNODE_CHECK(r.isSuccessful());
+
+ return Utils::NewLocal<String>(lwIsolate->toV8(), r.result);
}
Local<Value> Function::GetInferredName() const {
static T getValue(ValueRef* esValue, F toValue) {
auto lwContext = IsolateWrap::GetCurrent()->GetCurrentContext();
LWNODE_CHECK(lwContext != nullptr);
- T v = 0;
+ T outValue = 0;
auto r = Evaluator::execute(
lwContext->get(),
- [](ExecutionStateRef* esState, ValueRef* esValue, T* v, F toValue)
+ [](ExecutionStateRef* esState, ValueRef* esValue, T* outValue, F toValue)
-> ValueRef* {
- *v = toValue(esValue, esState);
+ *outValue = toValue(esValue, esState);
return esValue;
},
esValue,
- &v,
+ &outValue,
toValue);
if (!r.isSuccessful()) {
LWNODE_RETURN_0; // TODO: handle error
}
- return v;
+ return outValue;
}
double Number::Value() const {
LWNODE_RETURN_VOID;
}
-// static void* ExternalValue(i::Object obj) {
-// // Obscure semantics for undefined, but somehow checked in our unit
-// tests... if (obj.IsUndefined()) {
-// return nullptr;
-// }
-// i::Object foreign = i::JSObject::cast(obj).GetEmbedderField(0);
-// return
-// reinterpret_cast<void*>(i::Foreign::cast(foreign).foreign_address());
-// }
} // namespace v8
bool v8::V8::Initialize(const int build_config) {
Engine::Initialize();
+
+ RegisterExtension(std::make_unique<ExternalizeStringExtension>());
+ RegisterExtension(std::make_unique<ExternalizeGcExtension>());
return true;
}
esNewGlobalObjectTemplate = esGlobalTemplate;
}
- auto lwContext = ContextWrap::New(IsolateWrap::fromV8(external_isolate));
+ auto lwContext =
+ ContextWrap::New(IsolateWrap::fromV8(external_isolate), extensions);
if (esNewGlobalObjectTemplate) {
auto esContext = lwContext->get();
lwContext->GetExtrasBindingObject());
}
-void Context::AllowCodeGenerationFromStrings(bool allow) {}
+void Context::AllowCodeGenerationFromStrings(bool allow) {
+ LWNODE_RETURN_VOID;
+}
bool Context::IsCodeGenerationFromStringsAllowed() {
LWNODE_RETURN_FALSE;
auto rightStrBufferData = esRightStr->stringBufferAccessData();
size_t nchars = leftStrBufferData.length + rightStrBufferData.length;
- size_t charSize = sizeof(char16_t);
- Local<String> r;
+
+ Local<String> result;
if (leftStrBufferData.has8BitContent && rightStrBufferData.has8BitContent) {
- unsigned char* buf = new unsigned char[nchars];
- memcpy(buf, leftStrBufferData.buffer, leftStrBufferData.length);
- memcpy(buf + leftStrBufferData.length,
+ unsigned char* buffer = new unsigned char[nchars];
+ memcpy(buffer, leftStrBufferData.buffer, leftStrBufferData.length);
+ memcpy(buffer + leftStrBufferData.length,
rightStrBufferData.buffer,
rightStrBufferData.length);
- r = Utils::NewLocal<String>(v8_isolate,
- StringRef::createFromLatin1(buf, nchars));
- delete[] buf;
+ result = Utils::NewLocal<String>(
+ v8_isolate, StringRef::createFromLatin1(buffer, nchars));
+ delete[] buffer;
} else {
- char16_t* buf = new char16_t[nchars];
- copyStringToTwoByteArray(buf, esLeftStr);
- copyStringToTwoByteArray(buf + leftStrBufferData.length, esRightStr);
+ char16_t* buffer = new char16_t[nchars];
+ copyStringToTwoByteArray(buffer, esLeftStr);
+ copyStringToTwoByteArray(buffer + leftStrBufferData.length, esRightStr);
- r = Utils::NewLocal<String>(v8_isolate,
- StringRef::createFromUTF16(buf, nchars));
- delete[] buf;
+ result = Utils::NewLocal<String>(
+ v8_isolate, StringRef::createFromUTF16(buffer, nchars));
+ delete[] buffer;
}
- return r;
+ return result;
}
MaybeLocal<String> v8::String::NewExternalTwoByte(
EvalResult r = Evaluator::execute(
esContext,
[](ExecutionStateRef* esState, double value) -> ValueRef* {
- auto obj = NumberObjectRef::create(esState);
- obj->setPrimitiveValue(esState, ValueRef::create(value));
- return obj;
+ auto object = NumberObjectRef::create(esState);
+ object->setPrimitiveValue(esState, ValueRef::create(value));
+ return object;
},
value);
LWNODE_CHECK(r.isSuccessful());
EvalResult r = Evaluator::execute(
esContext,
[](ExecutionStateRef* esState, bool value) -> ValueRef* {
- auto b = BooleanObjectRef::create(esState);
- b->setPrimitiveValue(esState, ValueRef::create(value));
- return b;
+ auto object = BooleanObjectRef::create(esState);
+ object->setPrimitiveValue(esState, ValueRef::create(value));
+ return object;
},
value);
LWNODE_CHECK(r.isSuccessful());
EvalResult r = Evaluator::execute(
esContext,
[](ExecutionStateRef* esState, StringRef* esValue) -> ValueRef* {
- auto obj = StringObjectRef::create(esState);
- obj->setPrimitiveValue(esState, esValue);
- return obj;
+ auto object = StringObjectRef::create(esState);
+ object->setPrimitiveValue(esState, esValue);
+ return object;
},
esValue);
LWNODE_CHECK(r.isSuccessful());
EvalResult r = Evaluator::execute(
esContext,
[](ExecutionStateRef* esState, SymbolRef* esValue) -> ValueRef* {
- auto obj = SymbolObjectRef::create(esState);
- obj->setPrimitiveValue(esState, esValue);
- return obj;
+ auto object = SymbolObjectRef::create(esState);
+ object->setPrimitiveValue(esState, esValue);
+ return object;
},
esValue);
LWNODE_CHECK(r.isSuccessful());
[](ExecutionStateRef* state,
RegExpObjectRef* self,
StringRef* subject) -> ValueRef* {
- RegExpObjectRef::RegexMatchResult r;
- self->match(state, subject, r, false, 0);
+ RegExpObjectRef::RegexMatchResult result;
+ self->match(state, subject, result, false, 0);
- if (r.m_matchResults.empty()) {
+ if (result.m_matchResults.empty()) {
return ValueRef::createNull();
}
auto vector = ValueVectorRef::create();
- for (auto tokens : r.m_matchResults) {
+ for (auto tokens : result.m_matchResults) {
for (auto token : tokens) {
auto match = subject->substring(token.m_start, token.m_end);
vector->pushBack(match);
Local<v8::Array> v8::Array::New(Isolate* isolate, int length) {
API_ENTER_NO_EXCEPTION(isolate);
- auto lwContext = lwIsolate->GetCurrentContext();
- uint64_t len = 0;
- if (length > 0) {
- len = length;
- }
-
- auto r = Evaluator::execute(
- lwContext->get(),
- [](ExecutionStateRef* esState, uint64_t len) -> ValueRef* {
- return ArrayObjectRef::create(esState, len);
- },
- len);
- LWNODE_CHECK(r.isSuccessful());
-
- return Utils::NewLocal<Array>(isolate, r.result);
+ return Utils::NewLocal<Array>(
+ isolate,
+ ArrayObjectRefHelper::create(lwIsolate->GetCurrentContext()->get(),
+ (length >= 0) ? length : 0));
}
Local<v8::Array> v8::Array::New(Isolate* isolate,
Local<Value>* elements,
size_t length) {
API_ENTER_NO_EXCEPTION(isolate);
- auto lwContext = lwIsolate->GetCurrentContext();
auto vector = ValueVectorRef::create();
for (size_t i = 0; i < length; i++) {
vector->pushBack(VAL(**(elements + i))->value());
}
- auto r = Evaluator::execute(
- lwContext->get(),
- [](ExecutionStateRef* esState, ValueVectorRef* vector) -> ValueRef* {
- return ArrayObjectRef::create(esState, vector);
- },
- vector);
- LWNODE_CHECK(r.isSuccessful());
-
- return Utils::NewLocal<Array>(isolate, r.result);
+ return Utils::NewLocal<Array>(
+ isolate,
+ ArrayObjectRefHelper::create(lwIsolate->GetCurrentContext()->get(),
+ vector));
}
uint32_t v8::Array::Length() const {
- auto esArrayObject = CVAL(this)->value()->asArrayObject();
- auto lwIsolate = IsolateWrap::GetCurrent();
- auto lwContext = lwIsolate->GetCurrentContext();
-
- auto r = Evaluator::execute(
- lwContext->get(),
- [](ExecutionStateRef* esState, ArrayObjectRef* object) -> ValueRef* {
- return ValueRef::create(object->length(esState));
- },
- esArrayObject);
- API_HANDLE_EXCEPTION(r, lwIsolate, 0);
-
- return r.result->asUInt32();
+ return static_cast<uint32_t>(ArrayObjectRefHelper::length(
+ IsolateWrap::GetCurrent()->GetCurrentContext()->get(),
+ CVAL(this)->value()->asArrayObject()));
}
Local<v8::Map> v8::Map::New(Isolate* isolate) {
- API_ENTER_NO_EXCEPTION(isolate);
- auto esContext = lwIsolate->GetCurrentContext()->get();
+ EsScope scope(isolate);
EvalResult r = Evaluator::execute(
- esContext, [](ExecutionStateRef* esState) -> ValueRef* {
+ scope.context(), [](ExecutionStateRef* esState) -> ValueRef* {
return MapObjectRef::create(esState);
});
LWNODE_CHECK(r.isSuccessful());
}
size_t v8::Map::Size() const {
- auto lwIsolate = IsolateWrap::GetCurrent();
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
+ API_ENTER_NO_TERMINATION_CHECK(EsScope, nullptr);
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState, MapObjectRef* esSelf) -> ValueRef* {
return ValueRef::create(esSelf->size(esState));
},
- esSelf);
+ scope.self()->asMapObject());
LWNODE_CHECK(r.isSuccessful());
return r.result->asNumber();
}
void Map::Clear() {
- auto lwIsolate = IsolateWrap::GetCurrent();
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
+ API_ENTER_NO_TERMINATION_CHECK(EsScope, nullptr);
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState, MapObjectRef* esSelf) -> ValueRef* {
esSelf->clear(esState);
return ValueRef::createNull();
},
- esSelf);
+ scope.self()->asMapObject());
LWNODE_CHECK(r.isSuccessful());
}
MaybeLocal<Value> Map::Get(Local<Context> context, Local<Value> key) {
- API_ENTER_WITH_CONTEXT(context, MaybeLocal<Value>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
- auto esKey = CVAL(*key)->value();
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, MaybeLocal<Value>());
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState,
MapObjectRef* esSelf,
ValueRef* esKey) -> ValueRef* { return esSelf->get(esState, esKey); },
- esSelf,
- esKey);
- API_HANDLE_EXCEPTION(r, lwIsolate, MaybeLocal<Value>());
+ scope.self()->asMapObject(),
+ scope.asValue(key));
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, MaybeLocal<Value>());
- return Utils::NewLocal<Value>(lwIsolate->toV8(), r.result);
+ return Utils::NewLocal<Value>(scope.v8Isolate(), r.result);
}
MaybeLocal<Map> Map::Set(Local<Context> context,
Local<Value> key,
Local<Value> value) {
- API_ENTER_WITH_CONTEXT(context, MaybeLocal<Map>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
- auto esKey = CVAL(*key)->value();
- auto esValue = CVAL(*value)->value();
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, MaybeLocal<Map>());
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState,
MapObjectRef* esSelf,
ValueRef* esKey,
ValueRef* esValue) -> ValueRef* {
esSelf->set(esState, esKey, esValue);
- return ValueRef::createNull();
+ return esSelf;
},
- esSelf,
- esKey,
- esValue);
- API_HANDLE_EXCEPTION(r, lwIsolate, MaybeLocal<Map>());
+ scope.self()->asMapObject(),
+ scope.asValue(key),
+ scope.asValue(value));
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, MaybeLocal<Map>());
- return Utils::NewLocal<Map>(lwIsolate->toV8(), esSelf);
+ return Utils::NewLocal<Map>(scope.v8Isolate(), r.result);
}
Maybe<bool> Map::Has(Local<Context> context, Local<Value> key) {
- API_ENTER_WITH_CONTEXT(context, Nothing<bool>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
- auto esKey = CVAL(*key)->value();
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, Nothing<bool>());
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState,
MapObjectRef* esSelf,
ValueRef* esKey) -> ValueRef* {
return ValueRef::create(esSelf->has(esState, esKey));
},
- esSelf,
- esKey);
- API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
+ scope.self()->asMapObject(),
+ scope.asValue(key));
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, Nothing<bool>());
return Just(r.result->asBoolean());
}
Maybe<bool> Map::Delete(Local<Context> context, Local<Value> key) {
- API_ENTER_WITH_CONTEXT(context, Nothing<bool>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
- auto esKey = CVAL(*key)->value();
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, Nothing<bool>());
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState,
MapObjectRef* esSelf,
ValueRef* esKey) -> ValueRef* {
return ValueRef::create(esSelf->deleteOperation(esState, esKey));
},
- esSelf,
- esKey);
- API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
+ scope.self()->asMapObject(),
+ scope.asValue(key));
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, Nothing<bool>());
return Just(r.result->asBoolean());
}
Local<Array> Map::AsArray() const {
- auto lwIsolate = IsolateWrap::GetCurrent();
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esSelf = CVAL(this)->value()->asMapObject();
+ API_ENTER_NO_TERMINATION_CHECK(EsScope, nullptr);
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* esState, MapObjectRef* esSelf) -> ValueRef* {
auto done = StringRef::createFromASCII("done");
auto value = StringRef::createFromASCII("value");
return ArrayObjectRef::create(esState, vector);
},
- esSelf);
+ scope.self()->asMapObject());
LWNODE_CHECK(r.isSuccessful());
- return Utils::NewLocal<Array>(lwIsolate->toV8(), r.result);
+ return Utils::NewLocal<Array>(scope.v8Isolate(), r.result);
}
Local<v8::Set> v8::Set::New(Isolate* isolate) {
for (auto entry = itr->next(esState);
entry->asObject()->get(esState, done)->isFalse();
entry = itr->next(esState)) {
- auto val = entry->asObject()->get(esState, value);
- vector->pushBack(val);
+ vector->pushBack(entry->asObject()->get(esState, value));
}
return ArrayObjectRef::create(esState, vector);
Maybe<bool> Promise::Resolver::Resolve(Local<Context> context,
Local<Value> value) {
- API_ENTER_WITH_CONTEXT(context, Nothing<bool>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto self = CVAL(this)->value()->asPromiseObject();
-
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, Nothing<bool>());
+ auto self = scope.self()->asPromiseObject();
if (self->state() != Escargot::PromiseObjectRef::PromiseState::Pending) {
return Just(true);
}
+ auto esValueTofulfill = scope.asValue(value);
+ if (esValueTofulfill->isPromiseObject()) {
+ esValueTofulfill = esValueTofulfill->asPromiseObject()->promiseResult();
+ }
+
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* state,
PromiseObjectRef* promise,
- ValueRef* esValue,
+ ValueRef* value,
EscargotShim::IsolateWrap* lwIsolate) -> ValueRef* {
- promise->fulfill(state, esValue);
+ promise->fulfill(state, value);
return ValueRef::createUndefined();
},
self,
- CVAL(*value)->value(),
- lwIsolate);
- API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
+ esValueTofulfill,
+ scope.lwIsolate());
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, Nothing<bool>());
return Just(true);
}
Maybe<bool> Promise::Resolver::Reject(Local<Context> context,
Local<Value> value) {
- API_ENTER_WITH_CONTEXT(context, Nothing<bool>());
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto self = CVAL(this)->value()->asPromiseObject();
-
+ API_ENTER_AND_EXIT_IF_TERMINATING(EsScope, context, Nothing<bool>());
+ auto self = scope.self()->asPromiseObject();
if (self->state() != Escargot::PromiseObjectRef::PromiseState::Pending) {
return Just(true);
}
+ auto esValueToReject = scope.asValue(value);
+ if (esValueToReject->isPromiseObject()) {
+ esValueToReject = esValueToReject->asPromiseObject()->promiseResult();
+ }
+
EvalResult r = Evaluator::execute(
- esContext,
+ scope.context(),
[](ExecutionStateRef* state,
PromiseObjectRef* promise,
ValueRef* esValue,
return ValueRef::createUndefined();
},
self,
- CVAL(*value)->value(),
- lwIsolate);
- API_HANDLE_EXCEPTION(r, lwIsolate, Nothing<bool>());
+ esValueToReject,
+ scope.lwIsolate());
+ API_EXIT_IF_EXCEPTION_OCCURRED(r, Nothing<bool>());
return Just(true);
}
}
bool Promise::HasHandler() {
- LWNODE_RETURN_FALSE;
+ auto self = CVAL(this)->value()->asPromiseObject();
+ return self->hasRejectHandlers() || self->hasResolveHandlers();
}
Local<Value> Promise::Result() {
}
bool v8::ArrayBuffer::IsDetachable() const {
- LWNODE_RETURN_FALSE;
+ auto esSelf = CVAL(this)->value()->asArrayBufferObject();
+ return !esSelf->isDetachedBuffer();
}
v8::ArrayBuffer::Contents::Contents(void* data,
}
void Isolate::ClearKeptObjects() {
- LWNODE_RETURN_VOID;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
}
v8::Local<v8::Context> Isolate::GetCurrentContext() {
}
void Isolate::RequestGarbageCollectionForTesting(GarbageCollectionType type) {
- IsolateWrap::GetCurrent()->CollectGarbage();
+ IsolateWrap::GetCurrent()->CollectGarbage(GarbageCollectionReason::kTesting);
}
Isolate* Isolate::GetCurrent() {
// static
Isolate* Isolate::Allocate() {
+ Engine::current()->initializeThread();
return IsolateWrap::toV8(IsolateWrap::New());
}
void Isolate::Dispose() {
IsolateWrap::fromV8(this)->Dispose();
+ Engine::current()->finalizeThread();
}
void Isolate::DumpAndResetStats() {
void Isolate::SetHostImportModuleDynamicallyCallback(
HostImportModuleDynamicallyCallback callback) {
- LWNODE_RETURN_VOID;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
}
void Isolate::SetHostInitializeImportMetaObjectCallback(
HostInitializeImportMetaObjectCallback callback) {
- LWNODE_RETURN_VOID;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
}
void Isolate::SetPrepareStackTraceCallback(PrepareStackTraceCallback callback) {
Isolate* isolate,
Isolate::DisallowJavascriptExecutionScope::OnFailure on_failure)
: on_failure_(on_failure) {
- LWNODE_RETURN_VOID;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
}
Isolate::DisallowJavascriptExecutionScope::~DisallowJavascriptExecutionScope() {
- LWNODE_RETURN_VOID;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
}
Isolate::AllowJavascriptExecutionScope::AllowJavascriptExecutionScope(
}
void Isolate::PerformMicrotaskCheckpoint() {
- LWNODE_RETURN_VOID;
+ IsolateWrap::fromV8(this)->PerformMicrotaskCheckpoint();
}
void Isolate::EnqueueMicrotask(Local<Function> v8_function) {
String::Value::Value(v8::Isolate* isolate, v8::Local<v8::Value> obj)
: str_(nullptr), length_(0) {
- MaybeLocal<String> s = obj->ToString(isolate->GetCurrentContext());
Local<String> str;
- if (!s.ToLocal(&str)) {
+ if (!obj->ToString(isolate->GetCurrentContext()).ToLocal(&str)) {
return;
}
Local<Message> Exception::CreateMessage(Isolate* isolate,
Local<Value> exception) {
API_ENTER_NO_EXCEPTION(isolate);
+ auto esContext = lwIsolate->GetCurrentContext()->get();
auto esException = CVAL(*exception)->value();
- return Utils::NewLocal<Message>(isolate, esException);
+
+ StringRef* messageString = StringRef::emptyString();
+ if (esException->isString()) {
+ messageString = esException->asString();
+ }
+
+ EvalResult r = Evaluator::execute(
+ esContext,
+ [](ExecutionStateRef* esState, StringRef* messageString) -> ValueRef* {
+ auto object = StringObjectRef::create(esState);
+ object->setPrimitiveValue(esState, messageString);
+ return object;
+ },
+ messageString);
+ LWNODE_CHECK(r.isSuccessful());
+
+ if (esException->isObject()) {
+ auto esExtraData = ObjectRefHelper::getExtraData(esException->asObject());
+ if (esExtraData) {
+ auto esExceptionData = esExtraData->asExceptionObjectData();
+ ExtraDataHelper::setExtraData(r.result->asObject(), esExceptionData);
+ } else {
+ // FIXME: Check if missing extradata is ok. We print a warning
+ // here until this issue is investigated
+ LWNODE_LOG_WARN("esException does not have an extraData\n");
+ }
+ }
+
+ return Utils::NewLocal<Message>(isolate, r.result);
}
Local<StackTrace> Exception::GetStackTrace(Local<Value> exception) {
// message, script, and location need to be restored to Isolate TLS
// for reuse. capture_message_ needs to be disabled so that Throw()
// does not create a new message.
+ isolate_->RestorePendingMessageFromTryCatch(this);
}
isolate_->UnregisterTryCatchHandler(this);
reinterpret_cast<Isolate*>(isolate_)->ThrowException(v8Exception);
}
isolate_->UnregisterTryCatchHandler(this);
}
+
+ if (isolate_->sholdReportPendingMessage(is_verbose_)) {
+ isolate_->ReportPendingMessages(true);
+ }
}
void* v8::TryCatch::operator new(size_t) {
bool v8::TryCatch::HasCaught() const {
bool hasCaught = (exception_ != nullptr);
- LWNODE_CALL_TRACE_ID(TRYCATCH, "hasCaught: %s", strBool(hasCaught));
+ LWNODE_CALL_TRACE_ID(TRYCATCH, "hasCaught: %b", hasCaught);
return hasCaught;
}
bool v8::TryCatch::CanContinue() const {
- LWNODE_RETURN_FALSE;
+ LWNODE_UNIMPLEMENT;
+ return false;
}
bool v8::TryCatch::HasTerminated() const {
- LWNODE_RETURN_FALSE;
+ return has_terminated_;
}
v8::Local<v8::Value> v8::TryCatch::ReThrow() {
}
void v8::TryCatch::SetVerbose(bool value) {
- LWNODE_CALL_TRACE_ID(TRYCATCH, "%s", strBool(value));
+ LWNODE_CALL_TRACE_ID(TRYCATCH, "%b", value);
is_verbose_ = value;
}
}
void v8::TryCatch::SetCaptureMessage(bool value) {
- LWNODE_CALL_TRACE_ID(TRYCATCH, "%s", strBool(value));
+ LWNODE_CALL_TRACE_ID(TRYCATCH, "%b", value);
capture_message_ = value;
}
v8::Local<Value> Message::GetScriptResourceName() const {
auto lwIsolate = IsolateWrap::GetCurrent();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return Utils::NewLocal<String>(lwIsolate->toV8(), StringRef::emptyString());
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
return Utils::NewLocal<String>(lwIsolate->toV8(), top->src());
}
Maybe<int> Message::GetLineNumber(Local<Context> context) const {
- auto lwIsolate = CVAL(*context)->context()->GetIsolate();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return Just<int>(0);
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
return Just<int>(top->loc().line);
}
int Message::GetStartPosition() const {
- auto lwIsolate = IsolateWrap::GetCurrent();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return 0;
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
return top->loc().index;
}
int Message::GetEndPosition() const {
- auto lwIsolate = IsolateWrap::GetCurrent();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return 0;
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
return top->loc().index + 1;
}
Maybe<int> Message::GetStartColumn(Local<Context> context) const {
- auto lwIsolate = CVAL(*context)->context()->GetIsolate();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return Nothing<int>();
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
return Just<int>(top->loc().column - 1);
}
Maybe<int> Message::GetEndColumn(Local<Context> context) const {
- auto lwIsolate = CVAL(*context)->context()->GetIsolate();
+ auto self = CVAL(this)->value();
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ if (!hasStackTraceData(self)) {
return Nothing<int>();
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
int endCol = top->loc().column;
MaybeLocal<String> Message::GetSourceLine(Local<Context> context) const {
auto lwIsolate = CVAL(*context)->context()->GetIsolate();
-
- if (!hasStackTraceData(lwIsolate->pending_exception())) {
+ auto self = CVAL(this)->value();
+ if (!hasStackTraceData(self)) {
return Utils::NewLocal<String>(lwIsolate->toV8(), StringRef::emptyString());
}
- auto stackTrace = ExceptionObjectData::stackTrace(
- lwIsolate->pending_exception()->asObject());
+ auto stackTrace = ExceptionObjectData::stackTrace(self->asObject());
auto top = stackTrace->front();
std::string code = top->sourceCode()->toStdUTF8String();
--- /dev/null
+/*
+ * Copyright (c) 2021-present Samsung Electronics 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.
+ */
+
+#if defined(LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION)
+
+#include "api.h"
+#include "base.h"
+
+using namespace Escargot;
+using namespace EscargotShim;
+
+namespace v8 {
+// --- V a l u e S e r i a l i z a t i o n ---
+
+Maybe<bool> ValueSerializer::Delegate::WriteHostObject(Isolate* v8_isolate,
+ Local<Object> object) {
+ LWNODE_RETURN_MAYBE(bool);
+}
+
+Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
+ Isolate* v8_isolate, Local<SharedArrayBuffer> shared_array_buffer) {
+ LWNODE_RETURN_MAYBE(uint32_t);
+}
+
+Maybe<uint32_t> ValueSerializer::Delegate::GetWasmModuleTransferId(
+ Isolate* v8_isolate, Local<WasmModuleObject> module) {
+ LWNODE_RETURN_MAYBE(uint32_t);
+}
+
+void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
+ size_t size,
+ size_t* actual_size) {
+ *actual_size = size;
+ return realloc(old_buffer, size);
+}
+
+void ValueSerializer::Delegate::FreeBufferMemory(void* buffer) {
+ return free(buffer);
+}
+
+struct ValueSerializer::PrivateData {
+ explicit PrivateData(Isolate* isolate, ValueSerializer::Delegate* delegate)
+ : serializer(IsolateWrap::fromV8(isolate), delegate) {}
+ EscargotShim::ValueSerializer serializer;
+};
+
+ValueSerializer::ValueSerializer(Isolate* isolate)
+ : ValueSerializer(isolate, nullptr) {
+ LWNODE_UNIMPLEMENT;
+}
+
+ValueSerializer::ValueSerializer(Isolate* isolate, Delegate* delegate)
+ : private_(new PrivateData(isolate, delegate)) {}
+
+ValueSerializer::~ValueSerializer() {
+ delete private_;
+}
+
+void ValueSerializer::WriteHeader() {
+ private_->serializer.WriteHeader();
+}
+
+void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) {}
+
+Maybe<bool> ValueSerializer::WriteValue(Local<Context> context,
+ Local<Value> value) {
+ bool result = private_->serializer.WriteValue(CVAL(*value)->value());
+ return Just(result);
+}
+
+std::pair<uint8_t*, size_t> ValueSerializer::Release() {
+ auto result = private_->serializer.Release();
+ return result;
+}
+
+void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
+ Local<ArrayBuffer> array_buffer) {
+ LWNODE_RETURN_VOID;
+}
+
+void ValueSerializer::WriteUint32(uint32_t value) {
+ private_->serializer.WriteUint32(value);
+}
+
+void ValueSerializer::WriteUint64(uint64_t value) {
+ LWNODE_RETURN_VOID;
+}
+
+void ValueSerializer::WriteDouble(double value) {
+ LWNODE_RETURN_VOID;
+}
+
+void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
+ LWNODE_RETURN_VOID;
+}
+
+MaybeLocal<Object> ValueDeserializer::Delegate::ReadHostObject(
+ Isolate* v8_isolate) {
+ LWNODE_RETURN_LOCAL(Object);
+}
+
+MaybeLocal<WasmModuleObject> ValueDeserializer::Delegate::GetWasmModuleFromId(
+ Isolate* v8_isolate, uint32_t id) {
+ LWNODE_RETURN_LOCAL(WasmModuleObject);
+}
+
+MaybeLocal<SharedArrayBuffer>
+ValueDeserializer::Delegate::GetSharedArrayBufferFromId(Isolate* v8_isolate,
+ uint32_t id) {
+ LWNODE_RETURN_LOCAL(SharedArrayBuffer);
+}
+
+struct ValueDeserializer::PrivateData {
+ explicit PrivateData(Isolate* isolate,
+ Delegate* delegate,
+ const uint8_t* data,
+ size_t size)
+ : deserializer(IsolateWrap::fromV8(isolate), delegate, data, size) {}
+ EscargotShim::ValueDeserializer deserializer;
+};
+
+ValueDeserializer::ValueDeserializer(Isolate* isolate,
+ const uint8_t* data,
+ size_t size)
+ : ValueDeserializer(isolate, data, size, nullptr) {}
+
+ValueDeserializer::ValueDeserializer(Isolate* isolate,
+ const uint8_t* data,
+ size_t size,
+ Delegate* delegate)
+ : private_(new PrivateData(isolate, delegate, data, size)) {}
+
+ValueDeserializer::~ValueDeserializer() {
+ delete private_;
+}
+
+Maybe<bool> ValueDeserializer::ReadHeader(Local<Context> context) {
+ return Just(true);
+}
+
+void ValueDeserializer::SetSupportsLegacyWireFormat(
+ bool supports_legacy_wire_format) {
+ LWNODE_RETURN_VOID;
+}
+
+uint32_t ValueDeserializer::GetWireFormatVersion() const {
+ LWNODE_RETURN_0;
+}
+
+MaybeLocal<Value> ValueDeserializer::ReadValue(Local<Context> context) {
+ API_ENTER_WITH_CONTEXT(context, MaybeLocal<Value>());
+ auto esContext = lwIsolate->GetCurrentContext()->get();
+ auto output = private_->deserializer.ReadValue();
+ if (!output.hasValue()) {
+ LWNODE_CALL_TRACE_ID(SERIALIZER, "Cannot read value");
+ return Utils::NewLocal<Uint32>(
+ lwIsolate->toV8(),
+ ExceptionHelper::createErrorObject(esContext,
+ ErrorMessageType::kNotReadValue));
+ }
+ return Utils::NewLocal<Uint32>(lwIsolate->toV8(), output.get());
+}
+
+void ValueDeserializer::TransferArrayBuffer(uint32_t transfer_id,
+ Local<ArrayBuffer> array_buffer) {
+ LWNODE_RETURN_VOID;
+}
+
+void ValueDeserializer::TransferSharedArrayBuffer(
+ uint32_t transfer_id, Local<SharedArrayBuffer> shared_array_buffer) {
+ LWNODE_RETURN_VOID;
+}
+
+bool ValueDeserializer::ReadUint32(uint32_t* value) {
+ return private_->deserializer.ReadUint32(value);
+}
+
+bool ValueDeserializer::ReadUint64(uint64_t* value) {
+ LWNODE_RETURN_FALSE;
+}
+
+bool ValueDeserializer::ReadDouble(double* value) {
+ LWNODE_RETURN_FALSE;
+}
+
+bool ValueDeserializer::ReadRawBytes(size_t length, const void** data) {
+ LWNODE_RETURN_FALSE;
+}
+} // namespace v8
+
+#endif
if (lwIsolate->isCurrentScopeSealed()) {
lwIsolate->onFatalError(
- createCodeLocation(TRACE_ARGS).c_str(),
+ __CODE_LOCATION__,
"the current scope isn't allowed to allocate a handle");
}
// static
Local<PrimitiveArray> PrimitiveArray::New(Isolate* v8_isolate, int length) {
- auto array = Array::New(v8_isolate, length);
- return Utils::NewLocal<PrimitiveArray>(v8_isolate, CVAL(*array)->value());
+ API_ENTER_NO_EXCEPTION(v8_isolate);
+ return Utils::NewLocal<PrimitiveArray>(
+ v8_isolate,
+ ArrayObjectRefHelper::create(lwIsolate->GetCurrentContext()->get(),
+ (length >= 0) ? length : 0));
}
int PrimitiveArray::Length() const {
- auto v8Isolate = Isolate::GetCurrent();
- auto array = Utils::NewLocal<Array>(v8Isolate, CVAL(this)->value());
- return array->Length();
+ API_ENTER_NO_EXCEPTION(Isolate::GetCurrent());
+ return static_cast<int>(
+ ArrayObjectRefHelper::length(lwIsolate->GetCurrentContext()->get(),
+ CVAL(this)->value()->asArrayObject()));
}
void PrimitiveArray::Set(Isolate* v8_isolate,
int index,
Local<Primitive> item) {
- auto v8Context = v8_isolate->GetCurrentContext();
- auto array = Utils::NewLocal<Array>(v8_isolate, CVAL(this)->value());
- auto ok = array->Set(v8Context, index, item);
+ API_ENTER_NO_EXCEPTION(v8_isolate);
+ ArrayObjectRefHelper::set(lwIsolate->GetCurrentContext()->get(),
+ CVAL(this)->value()->asArrayObject(),
+ index,
+ VAL(*item)->value());
}
Local<Primitive> PrimitiveArray::Get(Isolate* v8_isolate, int index) {
- auto v8Context = v8_isolate->GetCurrentContext();
- auto array = Utils::NewLocal<Array>(v8_isolate, CVAL(this)->value());
- auto val = array->Get(v8Context, index);
- auto r = val.FromMaybe(
- Utils::NewLocal<Value>(v8_isolate, ValueRef::createUndefined()));
- return Utils::NewLocal<Primitive>(v8_isolate, CVAL(*r)->value());
+ API_ENTER_NO_EXCEPTION(v8_isolate);
+ return Utils::NewLocal<Primitive>(
+ v8_isolate,
+ ArrayObjectRefHelper::get(lwIsolate->GetCurrentContext()->get(),
+ CVAL(this)->value()->asArrayObject(),
+ index));
}
Module::Status Module::GetStatus() const {
r.error = ExceptionHelper::createErrorObject(
esPureContext, result.parseErrorCode, result.parseErrorMessage);
- lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTraceData);
+ lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTrace);
lwIsolate->ReportPendingMessages();
return MaybeLocal<UnboundScript>();
}
- // wrap the parsed script with the current isolate
- // auto lwValue = ValueWrap::createScript(result.script.get());
-
- // ExtraData extra(1, lwIsolate);
- // lwValue->setExtra(std::move(extra));
return Utils::NewLocal<UnboundScript>(v8_isolate, result.script.get());
}
auto maybe =
CompileUnboundInternal(isolate, source, options, no_cache_reason);
Local<UnboundScript> result;
- if (!maybe.ToLocal(&result)) return MaybeLocal<Script>();
+ if (!maybe.ToLocal(&result)) {
+ return MaybeLocal<Script>();
+ }
v8::Context::Scope scope(context);
return result->BindToCurrentContext();
}
auto esContext = VAL(*v8_context)->context()->get();
auto esSource = VAL(*source->source_string)->value()->asString();
+ StringRef* esSourceName = nullptr;
+ if (*source->resource_name) {
+ esSourceName = VAL(*source->resource_name)->value()->asString();
+ } else {
+ esSourceName = StringRef::emptyString();
+ }
handleShebang(esSource);
for (size_t i = 0; i < arguments_count; i++) {
arguments_list.push_back(VAL(*arguments[i])->value());
}
- arguments_list.push_back(esSource);
EvalResult r = Evaluator::execute(
esContext,
[](ExecutionStateRef* state,
- FunctionObjectRef* function,
- const size_t argc,
+ StringRef* sourceName,
+ StringRef* source,
+ size_t argc,
ValueRef** argv) -> ValueRef* {
- LWNODE_CHECK(function->isConstructible());
- return function->construct(state, argc, argv);
+ return FunctionObjectRef::create(
+ state,
+ sourceName,
+ AtomicStringRef::create(state->context(), "anonymous"),
+ argc,
+ argv,
+ source);
},
- esContext->globalObject()->function(),
+ esSourceName,
+ esSource,
arguments_list.size(),
arguments_list.data());
// note: expand API_HANDLE_EXCEPTION and add the resource name
if (!r.isSuccessful()) {
LWNODE_DLOG_ERROR("Evaluate");
- LWNODE_DLOG_RAW("Execute:\n %s (%s:%d)\nResource:\n %s\n%s",
- TRACE_ARGS2,
- VAL(*source->resource_name)
- ->value()
- ->asString()
- ->toStdUTF8String()
- .c_str(),
+ LWNODE_DLOG_RAW("Execute:\n %s\nResource:\n %s\n%s",
+ __CODE_LOCATION__,
+ esSourceName->toStdUTF8String().c_str(),
EvalResultHelper::getErrorString(
lwIsolate->GetCurrentContext()->get(), r)
.c_str());
- lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTraceData);
+ lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTrace);
lwIsolate->ReportPendingMessages();
return MaybeLocal<Function>();
}
* limitations under the License.
*/
+#if !defined(LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION)
+
#include "api.h"
#include "base.h"
LWNODE_RETURN_FALSE;
}
} // namespace v8
+
+#endif
bool isEnumerable = !(attribute & DontEnum);
bool isConfigurable = !(attribute & DontDelete);
- TemplateRef* esTemplate = CVAL(this)->tpl();
-
+ EsScopeTemplate scope(this);
// Name can be either a string or symbol
- auto esName = CVAL(*name)->value();
auto lwValue = CVAL(*value);
-
if (lwValue->type() == HandleWrap::Type::ObjectTemplate ||
lwValue->type() == HandleWrap::Type::FunctionTemplate) {
- esTemplate->set(
- esName, lwValue->tpl(), isWritable, isEnumerable, isConfigurable);
+ scope.self()->set(scope.asValue(name),
+ lwValue->tpl(),
+ isWritable,
+ isEnumerable,
+ isConfigurable);
} else {
- esTemplate->set(
- esName, lwValue->value(), isWritable, isEnumerable, isConfigurable);
+ scope.self()->set(scope.asValue(name),
+ lwValue->value(),
+ isWritable,
+ isEnumerable,
+ isConfigurable);
}
}
v8::Local<FunctionTemplate> setter,
v8::PropertyAttribute attribute,
v8::AccessControl access_control) {
- auto esTemplate = CVAL(this)->tpl();
- auto esName = CVAL(*name)->value()->asString();
+ EsScopeTemplate scope(this);
+
FunctionTemplateRef* esGetter = nullptr;
if (!getter.IsEmpty()) {
esGetter = CVAL(*getter)->ftpl();
esSetter = CVAL(*setter)->ftpl();
}
- esTemplate->setAccessorProperty(esName,
- OptionalRef<FunctionTemplateRef>(esGetter),
- OptionalRef<FunctionTemplateRef>(esSetter),
- !(attribute & DontEnum),
- !(attribute & DontDelete));
+ scope.self()->setAccessorProperty(scope.asValue(name)->asString(),
+ OptionalRef<FunctionTemplateRef>(esGetter),
+ OptionalRef<FunctionTemplateRef>(esSetter),
+ !(attribute & DontEnum),
+ !(attribute & DontDelete));
}
// --- F u n c t i o n T e m p l a t e ---
Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
- FunctionTemplateRef* esFunctionTemplate = CVAL(this)->ftpl();
+ EsScopeFunctionTemplate scope(this);
- return Utils::NewLocal(IsolateWrap::GetCurrent()->toV8(),
- esFunctionTemplate->prototypeTemplate());
+ return Utils::NewLocal(scope.v8Isolate(), scope.self()->prototypeTemplate());
}
void FunctionTemplate::SetPrototypeProviderTemplate(
}
void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) {
- auto esThisFunctionTemplate = CVAL(this)->ftpl();
- auto esThatFunctionTemplate = CVAL(*value)->ftpl();
+ EsScopeFunctionTemplate scope(this);
+ auto esThatFunctionTemplate = scope.asFunctionTemplate(value);
LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
"FunctionTemplate(%p)::Inherit(): %p",
- esThisFunctionTemplate,
+ scope.self(),
esThatFunctionTemplate);
- esThisFunctionTemplate->inherit(esThatFunctionTemplate);
+ scope.self()->inherit(esThatFunctionTemplate);
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"FunctionTemplate(%p)::Inherit(): ExtraData1: %p, ExtraData2: %p",
- esThisFunctionTemplate,
- ExtraDataHelper::getExtraData(esThisFunctionTemplate),
- ExtraDataHelper::getExtraData(esThatFunctionTemplate));
+ scope.self(),
+ ExtraDataHelper::getFunctionTemplateExtraData(scope.self()),
+ ExtraDataHelper::getFunctionTemplateExtraData(esThatFunctionTemplate));
}
-// e.g.,
-// sig_obj()
-// var s = new sig_obj();
-// target: sig_obj == constructor obj, thisValue: s
-// s.x();
-// target: null, thisValue: s
-static ValueRef* FunctionTemplateNativeFunction(
- ExecutionStateRef* state,
- ValueRef* thisValue,
- size_t argc,
- ValueRef** argv,
- OptionalRef<ObjectRef> newTarget) {
- Escargot::OptionalRef<Escargot::FunctionObjectRef> callee =
- state->resolveCallee();
-
- auto calleeExtraData = ExtraDataHelper::getExtraData(callee.value());
+static FunctionData* getFunctionDataFromCallee(FunctionObjectRef* callee) {
+ auto calleeExtraData = ExtraDataHelper::getExtraData(callee);
LWNODE_DCHECK_NOT_NULL(calleeExtraData);
- FunctionData* fnData = nullptr;
+ FunctionData* functionData = nullptr;
// callee->extraData() is one of two types below
if (calleeExtraData->isFunctionData()) {
- fnData = calleeExtraData->asFunctionData();
+ functionData = calleeExtraData->asFunctionData();
} else if (calleeExtraData->isFunctionTemplateData()) {
- fnData = new FunctionData(
+ functionData = new FunctionData(
calleeExtraData->asFunctionTemplateData()->functionTemplate());
} else {
LWNODE_CHECK(false);
}
- LWNODE_CALL_TRACE_ID(TEMPLATE,
- "es: %p newTarget: %s",
- thisValue,
- strBool(newTarget.hasValue()));
+ return functionData;
+}
- if (newTarget.hasValue()) {
- auto extraData = ExtraDataHelper::getExtraData(newTarget.value());
- if (extraData) {
- // targetData was created in FunctionTemplate::GetFunction()
- auto targetData = extraData->asFunctionData();
- auto functionTemplate = targetData->functionTemplate();
- auto objectTemplate = functionTemplate->instanceTemplate();
-
- LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
- "FunctionTemplateNativeFunction::targetData: %p",
- targetData);
-
- auto newInstance = thisValue->asObject();
- auto newInstanceData = ExtraDataHelper::getExtraData(newInstance);
- if (newInstanceData) {
- // newInstanceData was created in FunctionTemplate::InstanceTemplate()
- LWNODE_CHECK(newInstanceData->isObjectTemplateData());
- auto objectTemplateData = newInstanceData->asObjectTemplateData();
- LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
- "FunctionTemplateNative: Existing Data 1: %p",
- newInstanceData);
- auto newObjectData =
- objectTemplateData->createObjectData(objectTemplate);
- // Replace ObjectTemplateData with its objectData, i.e., creating an
- // instance
- ObjectRefHelper::setExtraData(newInstance, newObjectData, true);
- } else {
- auto objectTemplateData = ExtraDataHelper::getExtraData(objectTemplate);
- if (objectTemplateData) {
- // newInstance was created from ObjectTemplate
- auto newObjectData =
- objectTemplateData->asObjectTemplateData()->createObjectData(
- objectTemplate);
- LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
- "FunctionTemplateNative: New ExtaData: %p",
- newObjectData);
- ObjectRefHelper::setExtraData(newInstance, newObjectData);
- } else {
- // newInstance was created from FunctionTemplate::GetFunction(), and
- // with "var s = new S();" in Javascript
- LWNODE_CHECK(targetData);
- auto newObjectData = new ObjectData(targetData->functionTemplate());
- LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
- "FunctionTemplateNative: New ExtraData: %p",
- newObjectData);
- ObjectRefHelper::setExtraData(newInstance, newObjectData);
- }
- }
+static void setExtraDataToNewObjectInstance(ValueRef* thisValue,
+ ObjectRef* newTarget) {
+ auto targetData = ExtraDataHelper::getFunctionExtraData(newTarget);
+ LWNODE_CALL_TRACE_ID_LOG(EXTRADATA, "targetData: %p", targetData);
+
+ if (!targetData) {
+ return;
+ }
+
+ // newTarget was created from JavaScript using Template.
+ // targetData was created from FunctionTemplate::GetFunction()
+ auto objectTemplate = targetData->functionTemplate()->instanceTemplate();
+
+ auto newInstance = thisValue->asObject();
+ auto newInstanceObjectTemplateData =
+ ExtraDataHelper::getObjectTemplateExtraData(newInstance);
+ ObjectData* newObjectData = nullptr;
+ if (newInstanceObjectTemplateData) {
+ // 1. newInstanceObjectTemplateData was created from
+ // FunctionTemplate::InstanceTemplate()
+ LWNODE_CALL_TRACE_ID_LOG(
+ EXTRADATA, "Existing Data: %p", newInstanceObjectTemplateData);
+ newObjectData =
+ newInstanceObjectTemplateData->createObjectData(objectTemplate);
+ // Replace ObjectTemplateData with correct ObjectData
+ ObjectRefHelper::setExtraData(newInstance, newObjectData, true);
+ } else {
+ auto objectTemplateData =
+ ExtraDataHelper::getObjectTemplateExtraData(objectTemplate);
+ if (objectTemplateData) {
+ // 2. newInstance was created from ObjectTemplate
+ newObjectData = objectTemplateData->createObjectData(objectTemplate);
+ ObjectRefHelper::setExtraData(newInstance, newObjectData);
} else {
- // newTarget was created from JavaScript without using Template
+ // 3. newInstance was created from FunctionTemplate::GetFunction(),
+ // with "var s = new S();" in Javascript
+ newObjectData = new ObjectData(targetData->functionTemplate());
+ ObjectRefHelper::setExtraData(newInstance, newObjectData);
}
}
+ LWNODE_CALL_TRACE_ID_LOG(EXTRADATA, "New ExtraData: %p", newObjectData);
+}
+
+static ValueRef* runNativeFunctionCallback(ExecutionStateRef* state,
+ FunctionData* functionData,
+ ValueRef* thisValue,
+ ObjectRef* newTarget,
+ size_t argc,
+ ValueRef** argv) {
auto lwIsolate = IsolateWrap::GetCurrent();
- if (!newTarget.hasValue() &&
- !fnData->checkSignature(state, thisValue->asObject())) {
- lwIsolate->ScheduleThrow(TypeErrorObjectRef::create(
- state, StringRef::createFromASCII("Illegal invocation")));
+ if (!functionData->checkSignature(state, thisValue->asObject())) {
+ lwIsolate->ScheduleThrow(ExceptionHelper::createErrorObject(
+ state->context(), ErrorMessageType::kIllegalInvocation));
lwIsolate->ThrowErrorIfHasException(state);
LWNODE_DLOG_ERROR("Signature mismatch!");
- return ValueRef::createUndefined();
+ return nullptr;
}
Local<Value> result;
- if (fnData->callback()) {
+ if (functionData->callback()) {
LWNODE_CALL_TRACE_ID(TEMPLATE, "> Call JS callback");
- FunctionCallbackInfoWrap info(fnData->isolate(),
+ lwIsolate->increaseCallDepth();
+ FunctionCallbackInfoWrap info(functionData->isolate(),
thisValue,
thisValue,
newTarget,
- VAL(fnData->callbackData()),
+ VAL(functionData->callbackData()),
argc,
argv);
- fnData->callback()(info);
+ functionData->callback()(info);
+ lwIsolate->decreaseCallDepth();
+
lwIsolate->ThrowErrorIfHasException(state);
result = info.GetReturnValue().Get();
}
- if (newTarget.hasValue()) {
- return thisValue;
- }
-
if (!result.IsEmpty()) {
return VAL(*result)->value();
}
return ValueRef::createUndefined();
}
+// e.g.,
+// sig_obj()
+// var s = new sig_obj();
+// target: sig_obj == constructor obj, thisValue: s
+// s.x();
+// target: null, thisValue: s
+static ValueRef* FunctionTemplateNativeFunction(
+ ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) {
+ LWNODE_CALL_TRACE_ID(
+ TEMPLATE, "es: %p newTarget: %b", thisValue, newTarget.hasValue());
+
+ FunctionData* functionData =
+ getFunctionDataFromCallee(state->resolveCallee().value());
+
+ if (newTarget.hasValue()) {
+ setExtraDataToNewObjectInstance(thisValue, newTarget.value());
+ }
+
+ ValueRef* result = runNativeFunctionCallback(
+ state, functionData, thisValue, newTarget.value(), argc, argv);
+ if (result == nullptr) {
+ return ValueRef::createUndefined();
+ }
+
+ if (newTarget.hasValue()) {
+ return thisValue;
+ }
+
+ return result;
+}
+
Local<FunctionTemplate> FunctionTemplate::New(Isolate* isolate,
FunctionCallback callback,
v8::Local<Value> data,
LWNODE_RETURN_VOID;
}
- Escargot::FunctionTemplateRef* esFunctionTemplate = CVAL(this)->ftpl();
- auto functionTemplateData = ExtraDataHelper::getExtraData(esFunctionTemplate)
- ->asFunctionTemplateData();
+ EsScopeFunctionTemplate scope(this);
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(scope.self());
functionTemplateData->setCallback(callback);
functionTemplateData->setCallbackData(*data);
}
Local<ObjectTemplate> FunctionTemplate::InstanceTemplate() {
- FunctionTemplateRef* esFunctionTemplate = CVAL(this)->ftpl();
- LWNODE_CALL_TRACE_ID_LOG(
- EXTRADATA, "InstanceTemplate(%p)", esFunctionTemplate);
+ EsScopeFunctionTemplate scope(this);
+ LWNODE_CALL_TRACE_ID_LOG(EXTRADATA, "InstanceTemplate(%p)", scope.self());
- auto functionTemplateData = ExtraDataHelper::getExtraData(esFunctionTemplate)
- ->asFunctionTemplateData();
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(scope.self());
LWNODE_CHECK(functionTemplateData);
// Only one instanceTemplate should exist.
- auto esObjectTemplate = esFunctionTemplate->instanceTemplate();
- auto objectTemplateData = ExtraDataHelper::getExtraData(esObjectTemplate);
+ auto esObjectTemplate = scope.self()->instanceTemplate();
+ auto objectTemplateData =
+ ExtraDataHelper::getObjectTemplateExtraData(esObjectTemplate);
if (objectTemplateData) {
// objectTemplateData was set by itself in the below 'else'
- LWNODE_CHECK(objectTemplateData->isObjectTemplateData());
LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
"InstanceTemplate(%p): Existing ExtraData: %p",
esObjectTemplate,
objectTemplateData);
} else {
- auto objectTemplateData = new ObjectTemplateData(esFunctionTemplate);
+ auto objectTemplateData = new ObjectTemplateData(scope.self());
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"FunctionTemplate(%p)::InstanceTemplate(%p): New ExtarData: %p",
- esFunctionTemplate,
+ scope.self(),
esObjectTemplate,
objectTemplateData);
ExtraDataHelper::setExtraData(esObjectTemplate, objectTemplateData);
}
void FunctionTemplate::SetLength(int length) {
- FunctionTemplateRef* self = CVAL(this)->ftpl();
- self->setLength(length);
+ EsScopeFunctionTemplate scope(this);
+ scope.self()->setLength(length);
}
void FunctionTemplate::SetClassName(Local<String> name) {
- auto lwIsolate = IsolateWrap::GetCurrent();
- auto lwContext = lwIsolate->GetCurrentContext();
- FunctionTemplateRef* self = CVAL(this)->ftpl();
+ EsScopeFunctionTemplate scope(this);
auto esName = CVAL(*name)->value()->asString();
auto r = Evaluator::execute(
- lwContext->get(),
+ scope.context(),
[](ExecutionStateRef* esState,
FunctionTemplateRef* esFunctionTemplate,
StringRef* esName) -> ValueRef* {
AtomicStringRef::create(esState->context(), esName));
return ValueRef::createNull();
},
- self,
+ scope.self(),
esName);
LWNODE_CHECK(r.isSuccessful());
}
// Returns a unique function instance in the "current execution context"
MaybeLocal<v8::Function> FunctionTemplate::GetFunction(Local<Context> context) {
- API_ENTER_WITH_CONTEXT(context, MaybeLocal<Function>(), TEMPLATE);
- auto esContext = lwIsolate->GetCurrentContext()->get();
- auto esFunctionTemplate = CVAL(this)->ftpl();
-
- auto esFunction = esFunctionTemplate->instantiate(esContext);
- auto functionTemplateData = ExtraDataHelper::getExtraData(esFunctionTemplate)
- ->asFunctionTemplateData();
+ API_ENTER_AND_EXIT_IF_TERMINATING(
+ EsScopeFunctionTemplate, context, MaybeLocal<Function>());
+ auto esFunction = scope.self()->instantiate(scope.context());
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(scope.self());
LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
"FunctionTemplate(%p)::GetFunction(%p)",
- esFunctionTemplate,
+ scope.self(),
esFunction);
auto functionData = ExtraDataHelper::getExtraData(esFunction);
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"FunctionTemplate(%p)::GetFunction(%p): Existing functionData: %p",
- esFunctionTemplate,
+ scope.self(),
esFunction,
functionData);
if (functionData->isFunctionTemplateData()) {
// This functionData was created by FunctionTemplate::New(), and
// FunctionTemplateData was set in esFunction. We need to replace it with
// a new FunctionData
- auto newFunctionData = new FunctionData(esFunctionTemplate);
+ auto newFunctionData = new FunctionData(scope.self());
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"FunctionTemplate(%p)::GetFunction(%p): New functionData: %p",
- esFunctionTemplate,
+ scope.self(),
esFunction,
newFunctionData);
ExtraDataHelper::setExtraData(
LWNODE_CHECK(false);
}
- return Utils::NewLocal<Function>(lwIsolate->toV8(), esFunction);
+ return Utils::NewLocal<Function>(scope.v8Isolate(), esFunction);
}
MaybeLocal<v8::Object> FunctionTemplate::NewRemoteInstance() {
}
bool FunctionTemplate::HasInstance(v8::Local<v8::Value> value) {
+ EsScopeFunctionTemplate scope(this);
LWNODE_CALL_TRACE();
- auto esSelf = CVAL(this)->ftpl();
LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
"FunctionTemplate(%p)::HasInstance(): %p",
- CVAL(this)->ftpl(),
- CVAL(*value)->value()->asObject());
+ scope.self(),
+ scope.asValue(value)->asObject());
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
- "FunctionTemplate(%p)::HasInstance: ExtraData: %p, %p?",
- esSelf,
- ExtraDataHelper::getExtraData(CVAL(this)->ftpl()),
- ExtraDataHelper::getExtraData(CVAL(*value)->value()->asObject()));
+ "FunctionTemplate(%p)::HasInstance: ExtraData: %p, %p",
+ scope.self(),
+ ExtraDataHelper::getFunctionTemplateExtraData(scope.self()),
+ ExtraDataHelper::getExtraData(scope.asValue(value)->asObject()));
- auto esContext = IsolateWrap::GetCurrent()->GetCurrentContext()->get();
- auto esValue = CVAL(*value)->value();
+ auto esValue = scope.asValue(value);
if (!esValue->isObject()) {
return false;
}
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"FunctionTemplate(%p)::HasInstance: Existing ExtraData: %p",
- esSelf,
+ scope.self(),
extraData->asObjectData());
if (extraData->asObjectData()->objectTemplate()) {
- auto objectTemplateData = ExtraDataHelper::getExtraData(
- extraData->asObjectData()->objectTemplate())
- ->asObjectTemplateData();
+ auto objectTemplateData = ExtraDataHelper::getObjectTemplateExtraData(
+ extraData->asObjectData()->objectTemplate());
esOtherFunctionTemplate = objectTemplateData->functionTemplate();
}
} else if (extraData->isObjectTemplateData()) {
LWNODE_CHECK(false);
}
- for (auto p = esOtherFunctionTemplate; p; p = p->parent().value()) {
- if (esSelf == p) {
+ for (auto functionTemplate = esOtherFunctionTemplate;
+ functionTemplate != nullptr;
+ functionTemplate = functionTemplate->parent().value()) {
+ if (scope.self() == functionTemplate) {
return true;
}
}
attribute);
}
-struct HandlerConfiguration : public gc {
- HandlerConfiguration(
- v8::Isolate* isolate,
- const v8::NamedPropertyHandlerConfiguration& namedPropertyHandler)
- : m_isolate(isolate), m_namedPropertyHandler(namedPropertyHandler) {}
- v8::Isolate* m_isolate;
- v8::NamedPropertyHandlerConfiguration m_namedPropertyHandler;
+class NamePropertyPolicy {
+ public:
+ static Local<Name> getPropertyName(ExecutionStateRef* state,
+ ValueRef* value) {
+ return Utils::ToLocal<Name>(value);
+ }
};
-void ObjectTemplate::SetHandler(
- const NamedPropertyHandlerConfiguration& config) {
- ObjectTemplateRef* esObjectTemplate = CVAL(this)->otpl();
- HandlerConfiguration* handlerConfiguration =
- new HandlerConfiguration(IsolateWrap::GetCurrent()->toV8(), config);
-
- ObjectTemplateNamedPropertyHandlerData esHandlerData;
- if (config.getter) {
- esHandlerData.getter = [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName) -> OptionalRef<ValueRef> {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
-
- if (!handlerConfiguration->m_namedPropertyHandler.getter) {
- return Escargot::OptionalRef<Escargot::ValueRef>();
- }
-
- Local<Name> v8PropertyName = Utils::ToLocal<Name>(propertyName);
-
- PropertyCallbackInfoWrap<v8::Value> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
+class IndexPropertyPolicy {
+ public:
+ static uint32_t getPropertyName(ExecutionStateRef* state, ValueRef* value) {
+ auto index = value->tryToUseAsIndexProperty(state);
+ LWNODE_DCHECK(index != ValueRef::InvalidIndexPropertyValue);
+ return index;
+ }
+};
- handlerConfiguration->m_namedPropertyHandler.getter(v8PropertyName, info);
+template <typename T>
+struct ObjectTemplateLocalData : public gc {
+ public:
+ ObjectTemplateLocalData(v8::Isolate* isolate,
+ const T& propertyHandlerConfiguration)
+ : isolate(isolate), config(propertyHandlerConfiguration) {}
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- return CVAL(*ret)->value();
- }
+ v8::Isolate* isolate{nullptr};
+ T config;
+};
- return Escargot::OptionalRef<Escargot::ValueRef>();
- };
+template <typename T, typename GetPropertyNamePolicy>
+class ObjectTemplateSetHandlerCallbackHelper {
+ public:
+ static void applyHelper(
+ const T& v8Config, ObjectTemplatePropertyHandlerConfiguration& esConfig) {
+ esConfig.getter = v8Config.getter ? getterCallback : nullptr;
+ esConfig.setter = v8Config.setter ? setterCallback : nullptr;
+ esConfig.query = v8Config.query ? queryCallback : nullptr;
+ esConfig.deleter = v8Config.deleter ? deleterCallback : nullptr;
+ esConfig.enumerator = v8Config.enumerator ? enumeratorCallback : nullptr;
+ esConfig.definer = v8Config.definer ? definerCallback : nullptr;
+ esConfig.descriptor = v8Config.descriptor ? descriptorCallback : nullptr;
}
- if (config.setter) {
- esHandlerData.setter = [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName,
- ValueRef* esValue) -> OptionalRef<ValueRef> {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
+ static ObjectTemplateLocalData<T>* getHelperData(void* data) {
+ LWNODE_DCHECK_NOT_NULL(data);
+ return reinterpret_cast<ObjectTemplateLocalData<T>*>(data);
+ }
- if (!handlerConfiguration->m_namedPropertyHandler.setter) {
- return Escargot::OptionalRef<Escargot::ValueRef>();
- }
+ static OptionalRef<ValueRef> getterCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName) {
+ auto helperData = getHelperData(data);
- Local<Name> v8PropertyName = v8::Utils::ToLocal<Name>(propertyName);
-
- PropertyCallbackInfoWrap<v8::Value> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
-
- handlerConfiguration->m_namedPropertyHandler.setter(
- v8PropertyName, v8::Utils::ToLocal<Value>(esValue), info);
-
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- if (ret->IsFalse()) {
- return Escargot::OptionalRef<Escargot::ValueRef>(
- ValueRef::create(false));
- }
- return Escargot::OptionalRef<Escargot::ValueRef>(
- ValueRef::create(true));
- }
+ PropertyCallbackInfoWrap<v8::Value> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
- return Escargot::OptionalRef<Escargot::ValueRef>();
- };
- }
+ LWNODE_DCHECK_NOT_NULL(helperData->config.getter);
+ helperData->config.getter(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName), info);
- if (config.query) {
- esHandlerData.query =
- [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName) -> TemplatePropertyAttribute {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
+ if (info.hasReturnValue()) {
+ return CVAL(*info.GetReturnValue().Get())->value();
+ }
- if (!handlerConfiguration->m_namedPropertyHandler.query) {
- return TemplatePropertyAttribute::TemplatePropertyAttributeNotExist;
- }
+ return OptionalRef<ValueRef>();
+ }
- Local<Name> v8PropertyName = v8::Utils::ToLocal<Name>(propertyName);
-
- PropertyCallbackInfoWrap<Integer> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
-
- handlerConfiguration->m_namedPropertyHandler.query(v8PropertyName, info);
- Local<Value> ret = info.GetReturnValue().Get();
- if (info.hasReturnValue()) {
- bool hasNone = (handlerConfiguration->m_namedPropertyHandler.flags ==
- PropertyHandlerFlags::kNone);
- bool hasNoSideEffect =
- (static_cast<int>(
- handlerConfiguration->m_namedPropertyHandler.flags) &
- static_cast<int>(PropertyHandlerFlags::kHasNoSideEffect));
-
- if (hasNone) {
- return TemplatePropertyAttribute::TemplatePropertyAttributeExist;
- } else if (hasNoSideEffect) {
- return TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable;
- } else {
- LWNODE_UNIMPLEMENT;
- }
- return TemplatePropertyAttribute::TemplatePropertyAttributeExist;
+ static OptionalRef<ValueRef> setterCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName,
+ ValueRef* esValue) {
+ auto helperData = getHelperData(data);
+
+ PropertyCallbackInfoWrap<v8::Value> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
+
+ LWNODE_DCHECK_NOT_NULL(helperData->config.setter);
+ helperData->config.setter(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName),
+ v8::Utils::ToLocal<Value>(esValue),
+ info);
+
+ if (info.hasReturnValue()) {
+ if (info.GetReturnValue().Get()->IsFalse()) {
+ return OptionalRef<ValueRef>(ValueRef::create(false));
}
+ return OptionalRef<ValueRef>(ValueRef::create(true));
+ }
- return TemplatePropertyAttribute::TemplatePropertyAttributeNotExist;
- };
+ return OptionalRef<ValueRef>();
}
- if (config.deleter) {
- esHandlerData.deleter =
- [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName) -> OptionalRef<ValueRef> {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
-
- if (!handlerConfiguration->m_namedPropertyHandler.deleter) {
- return Escargot::OptionalRef<Escargot::ValueRef>();
+ static ObjectTemplatePropertyAttribute queryCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName) {
+ auto helperData = getHelperData(data);
+
+ PropertyCallbackInfoWrap<v8::Integer> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
+
+ LWNODE_DCHECK_NOT_NULL(helperData->config.query);
+ helperData->config.query(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName), info);
+
+ if (info.hasReturnValue()) {
+ bool hasNone = (helperData->config.flags == PropertyHandlerFlags::kNone);
+ bool hasNoSideEffect =
+ (static_cast<int>(helperData->config.flags) &
+ static_cast<int>(PropertyHandlerFlags::kHasNoSideEffect));
+
+ if (hasNone) {
+ return ObjectTemplatePropertyAttribute::PropertyAttributeExist;
+ } else if (hasNoSideEffect) {
+ return ObjectTemplatePropertyAttribute::PropertyAttributeEnumerable;
+ } else {
+ LWNODE_UNIMPLEMENT;
}
+ return ObjectTemplatePropertyAttribute::PropertyAttributeExist;
+ }
+
+ return ObjectTemplatePropertyAttribute::PropertyAttributeNotExist;
+ }
- Local<Name> v8PropertyName = v8::Utils::ToLocal<Name>(propertyName);
+ static OptionalRef<ValueRef> deleterCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName) {
+ auto helperData = getHelperData(data);
- PropertyCallbackInfoWrap<v8::Boolean> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
+ PropertyCallbackInfoWrap<v8::Boolean> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
- handlerConfiguration->m_namedPropertyHandler.deleter(v8PropertyName,
- info);
+ LWNODE_DCHECK_NOT_NULL(helperData->config.deleter);
+ helperData->config.deleter(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName), info);
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- return CVAL(*ret)->value();
- }
+ if (info.hasReturnValue()) {
+ return CVAL(*info.GetReturnValue().Get())->value();
+ }
- return Escargot::OptionalRef<Escargot::ValueRef>();
- };
+ return OptionalRef<ValueRef>();
}
- if (config.enumerator) {
- esHandlerData.enumerator = [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data) -> ValueVectorRef* {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
-
- if (!handlerConfiguration->m_namedPropertyHandler.enumerator) {
- return ValueVectorRef::create(0);
- }
+ static ValueVectorRef* enumeratorCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data) {
+ auto helperData = getHelperData(data);
- PropertyCallbackInfoWrap<v8::Array> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
+ PropertyCallbackInfoWrap<v8::Array> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
- handlerConfiguration->m_namedPropertyHandler.enumerator(info);
+ LWNODE_DCHECK_NOT_NULL(helperData->config.enumerator);
+ helperData->config.enumerator(info);
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- auto esArray = CVAL(*ret)->value()->asArrayObject();
- auto length = esArray->length(state);
- auto v = ValueVectorRef::create(length);
+ if (info.hasReturnValue()) {
+ auto esArray =
+ CVAL(*info.GetReturnValue().Get())->value()->asArrayObject();
+ auto length = esArray->length(state);
+ auto vector = ValueVectorRef::create(length);
- for (size_t i = 0; i < length; i++) {
- v->set(i, esArray->get(state, ValueRef::create(i))->toString(state));
- }
- return v;
+ for (size_t i = 0; i < length; i++) {
+ vector->set(i,
+ esArray->get(state, ValueRef::create(i))->toString(state));
}
+ return vector;
+ }
- return ValueVectorRef::create(0);
- };
+ return ValueVectorRef::create(0);
}
- if (config.definer) {
- esHandlerData.definer =
- [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName,
- const ObjectPropertyDescriptorRef& esDesc) -> OptionalRef<ValueRef> {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
+ static OptionalRef<ValueRef> definerCallback(
+ ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName,
+ const ObjectPropertyDescriptorRef& esDescriptor) {
+ auto helperData = getHelperData(data);
+
+ PropertyCallbackInfoWrap<v8::Value> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
+
+ PropertyDescriptor descriptor;
+ descriptor.get_private()->setDescriptor(
+ const_cast<ObjectPropertyDescriptorRef*>(&esDescriptor));
+
+ LWNODE_DCHECK_NOT_NULL(helperData->config.definer);
+ helperData->config.definer(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName),
+ descriptor,
+ info);
+
+ if (info.hasReturnValue()) {
+ return OptionalRef<ValueRef>(CVAL(*info.GetReturnValue().Get())->value());
+ }
+ return OptionalRef<ValueRef>();
+ }
- if (!handlerConfiguration->m_namedPropertyHandler.definer) {
- return Escargot::OptionalRef<Escargot::ValueRef>();
- }
+ static OptionalRef<ValueRef> descriptorCallback(ExecutionStateRef* state,
+ ObjectRef* esSelf,
+ ValueRef* esReceiver,
+ void* data,
+ ValueRef* propertyName) {
+ auto helperData = getHelperData(data);
- Local<Name> v8PropertyName = v8::Utils::ToLocal<Name>(propertyName);
+ PropertyCallbackInfoWrap<v8::Value> info(
+ helperData->isolate, esSelf, esReceiver, VAL(*helperData->config.data));
- PropertyCallbackInfoWrap<v8::Value> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
+ LWNODE_DCHECK_NOT_NULL(helperData->config.descriptor);
+ helperData->config.descriptor(
+ GetPropertyNamePolicy::getPropertyName(state, propertyName), info);
- PropertyDescriptor desc;
- desc.get_private()->setDescriptor(
- const_cast<ObjectPropertyDescriptorRef*>(&esDesc));
- handlerConfiguration->m_namedPropertyHandler.definer(
- v8PropertyName, desc, info);
+ if (info.hasReturnValue()) {
+ return OptionalRef<ValueRef>(CVAL(*info.GetReturnValue().Get())->value());
+ }
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- return Escargot::OptionalRef<Escargot::ValueRef>(CVAL(*ret)->value());
- }
- return Escargot::OptionalRef<Escargot::ValueRef>();
- };
+ return OptionalRef<ValueRef>();
}
+};
- if (config.descriptor) {
- esHandlerData.descriptor =
- [](ExecutionStateRef* state,
- ObjectRef* esSelf,
- ValueRef* esReceiver,
- void* data,
- ValueRef* propertyName) -> OptionalRef<ValueRef> {
- auto handlerConfiguration = reinterpret_cast<HandlerConfiguration*>(data);
-
- if (!handlerConfiguration->m_namedPropertyHandler.descriptor) {
- return Escargot::OptionalRef<Escargot::ValueRef>();
- }
-
- Local<Name> v8PropertyName = v8::Utils::ToLocal<Name>(propertyName);
-
- PropertyCallbackInfoWrap<v8::Value> info(
- handlerConfiguration->m_isolate,
- esSelf,
- esReceiver,
- VAL(*handlerConfiguration->m_namedPropertyHandler.data));
+void ObjectTemplate::SetHandler(
+ const NamedPropertyHandlerConfiguration& config) {
+ EsScopeObjectTemplate scope(this);
- handlerConfiguration->m_namedPropertyHandler.descriptor(v8PropertyName,
- info);
+ ObjectTemplatePropertyHandlerConfiguration esConfig;
+ esConfig.data =
+ new ObjectTemplateLocalData<NamedPropertyHandlerConfiguration>(
+ IsolateWrap::GetCurrent()->toV8(), config);
- if (info.hasReturnValue()) {
- Local<Value> ret = info.GetReturnValue().Get();
- return Escargot::OptionalRef<Escargot::ValueRef>(CVAL(*ret)->value());
- }
+ ObjectTemplateSetHandlerCallbackHelper<
+ NamedPropertyHandlerConfiguration,
+ NamePropertyPolicy>::applyHelper(config, esConfig);
- return Escargot::OptionalRef<Escargot::ValueRef>();
- };
- }
-
- esHandlerData.data = handlerConfiguration;
- esObjectTemplate->setNamedPropertyHandler(esHandlerData);
+ scope.self()->setNamedPropertyHandler(esConfig);
}
void ObjectTemplate::MarkAsUndetectable() {
void ObjectTemplate::SetHandler(
const IndexedPropertyHandlerConfiguration& config) {
- LWNODE_RETURN_VOID;
+ EsScopeObjectTemplate scope(this);
+
+ ObjectTemplatePropertyHandlerConfiguration esConfig;
+ esConfig.data =
+ new ObjectTemplateLocalData<IndexedPropertyHandlerConfiguration>(
+ IsolateWrap::GetCurrent()->toV8(), config);
+
+ ObjectTemplateSetHandlerCallbackHelper<
+ IndexedPropertyHandlerConfiguration,
+ IndexPropertyPolicy>::applyHelper(config, esConfig);
+
+ scope.self()->setIndexedPropertyHandler(esConfig);
}
void ObjectTemplate::SetCallAsFunctionHandler(FunctionCallback callback,
}
int ObjectTemplate::InternalFieldCount() {
- auto esObjectTemplate = CVAL(this)->otpl();
- return ObjectTemplateRefHelper::getInternalFieldCount(esObjectTemplate);
+ EsScopeObjectTemplate scope(this);
+ return ObjectTemplateRefHelper::getInternalFieldCount(scope.self());
}
void ObjectTemplate::SetInternalFieldCount(int value) {
- auto esObjectTemplate = CVAL(this)->otpl();
- if (esObjectTemplate->didInstantiate()) {
+ EsScopeObjectTemplate scope(this);
+
+ if (scope.self()->didInstantiate()) {
LWNODE_DLOG_WARN(
"Don't modify internal field count after instantiating object");
return;
}
- ObjectTemplateRefHelper::setInternalFieldCount(esObjectTemplate, value);
+ ObjectTemplateRefHelper::setInternalFieldCount(scope.self(), value);
}
bool ObjectTemplate::IsImmutableProto() {
}
MaybeLocal<v8::Object> ObjectTemplate::NewInstance(Local<Context> context) {
- API_ENTER_WITH_CONTEXT(context, MaybeLocal<v8::Object>(), TEMPLATE);
- auto esContext = VAL(*context)->context()->get();
- auto esObjectTemplate = CVAL(this)->otpl();
+ API_ENTER_AND_EXIT_IF_TERMINATING(
+ EsScopeObjectTemplate, context, MaybeLocal<v8::Object>());
- auto esNewObject = esObjectTemplate->instantiate(esContext);
+ auto esNewObject = scope.self()->instantiate(scope.context());
LWNODE_CALL_TRACE_ID_LOG(EXTRADATA,
"ObjectTemplate(%p)::NewInstance(): %p",
- esObjectTemplate,
+ scope.self(),
esNewObject);
auto objectTemplateData =
- ExtraDataHelper::getExtraData(esObjectTemplate)->asObjectTemplateData();
+ ExtraDataHelper::getObjectTemplateExtraData(scope.self());
auto objectData = ObjectRefHelper::getExtraData(esNewObject);
if (objectData) {
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"ObjectTemplate(%p)::NewInstance(): Existing Data: %p",
- esObjectTemplate,
+ scope.self(),
objectData);
- auto objectData = objectTemplateData->createObjectData(esObjectTemplate);
+ auto objectData = objectTemplateData->createObjectData(scope.self());
LWNODE_CALL_TRACE_ID_LOG(
EXTRADATA,
"ObjectTemplate(%p)::NewInstance() New objectData: %p",
- esObjectTemplate,
+ scope.self(),
objectData);
ObjectRefHelper::setExtraData(esNewObject, objectData, true);
} else {
LWNODE_CHECK(false);
}
- return Utils::NewLocal<Object>(lwIsolate->toV8(), esNewObject);
+ return Utils::NewLocal<Object>(scope.v8Isolate(), esNewObject);
}
} // namespace v8
// found in the LICENSE file.
#include "api.h"
+
+#include <malloc.h> // for malloc_trim
+#include <cstdlib>
#include <sstream>
+
+#include "api/global.h"
#include "base.h"
using namespace Escargot;
}
void V8::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags) {
- flag_t flags = FlagType::Empty;
+ flag_t userOptions = Flag::Type::Empty;
for (int i = 1; i < *argc; i++) {
- char* arg = argv[i];
- bool checked = false;
-
- if (strEquals("--expose-gc", arg) || strEquals("--expose_gc", arg)) {
- flags |= FlagType::ExposeGC;
- checked = true;
- } else if (strEquals("--use-strict", arg) ||
- strEquals("--use_strict", arg)) {
- flags |= FlagType::UseStrict;
- checked = true;
- } else if (strEquals("--off-idlegc", arg) ||
- strEquals("--off_idlegc", arg)) {
- flags |= FlagType::DisableIdleGC;
- checked = true;
- } else if (strEquals("--harmony-top-level-await", arg)) {
- // @check https://github.sec.samsung.net/lws/node-escargot/issues/394
- flags |= FlagType::TopLevelWait;
- checked = true;
- } else if (strEquals("--trace-gc", arg)) {
- flags |= FlagType::TraceGC;
- checked = true;
- } else if (strStartsWith(arg, "--trace-call")) {
- flags |= FlagType::TraceCall;
- checked = true;
-
- std::string str(arg);
- std::string::size_type pos = str.find_first_of('=');
- if (std::string::npos != pos) {
- std::stringstream ss(str.substr(pos + 1)); // +1 for skipping =
- std::string token;
- while (std::getline(ss, token, ',')) {
- if (token.find('-') == 0) {
- Flags::setNagativeTraceCallId(token.substr(1));
- } else {
- Flags::setTraceCallId(token);
- }
- }
- }
- } else if (strEquals("--internal-log", arg)) {
- flags |= FlagType::InternalLog;
- checked = true;
- } else if (remove_flags && (strStartsWith(arg, "--debug") ||
- strStartsWith(arg, "--stack-size=") ||
- strStartsWith(arg, "--nolazy") ||
- strStartsWith(arg, "--trace_debug"))) {
- checked = true;
- } else {
- LWNODE_DLOG_WARN("'%s' flag is ignored", arg);
- }
+ std::string userOption = argv[i];
+ bool isValidToRemove = false;
+
+ EscargotShim::Global::flags()->add(userOption);
- if (checked && remove_flags) {
+ if (remove_flags) {
argv[i] = nullptr;
}
}
- Flags::set(flags);
-
if (remove_flags) {
- int count = 0;
- for (int idx = 0; idx < *argc; idx++) {
- if (argv[idx]) {
- argv[count++] = argv[idx];
+ EscargotShim::Global::flags()->shrinkArgumentList(argc, argv);
+ }
+}
+
+// v8::Extension
+
+class ExtensionSourceString : public v8::String::ExternalOneByteStringResource {
+ public:
+ explicit ExtensionSourceString(const char* data)
+ : v8::String::ExternalOneByteStringResource(), data_(data) {}
+ virtual ~ExtensionSourceString() override {}
+
+ static void* operator new(size_t size) { return malloc(size); }
+ static void operator delete(void* ptr) { free(ptr); }
+
+ const char* data() const override { return data_; }
+ size_t length() const override { return strlen(data_); }
+
+ private:
+ const char* data_;
+};
+
+std::vector<std::unique_ptr<Extension>> RegisteredExtension::extensions;
+
+void RegisterExtension(std::unique_ptr<Extension> extension) {
+ RegisteredExtension::registerExtension(std::move(extension));
+}
+
+void RegisteredExtension::registerExtension(
+ std::unique_ptr<Extension> extension) {
+ extensions.push_back(std::move(extension));
+}
+
+void RegisteredExtension::unregisterAll() {
+ extensions.clear();
+}
+
+void RegisteredExtension::applyAll(ContextRef* context) {
+ for (auto& extension : extensions) {
+ if (isLwExtension(extension.get())) {
+ auto lwExtension = reinterpret_cast<LwExtension*>(extension.get());
+ if (lwExtension->isRegisteredExtension()) {
+ lwExtension->apply(context);
}
+ } else {
+ applyV8Extension(context, extension.get());
}
- *argc = count;
}
}
-// RegisteredExtension* RegisteredExtension::first_extension_ = nullptr;
+bool RegisteredExtension::isLwExtension(Extension* extension) {
+ std::string name = extension->name();
+ return (name == "v8/externalize") || (name == "v8/gc");
+}
+
+void RegisteredExtension::applyV8Extension(ContextRef* context,
+ Extension* extension) {
+ if (extension->source() == nullptr ||
+ extension->source()->data() == nullptr) {
+ LWNODE_LOG_WARN("Extension empty string");
+ return;
+ }
+
+ std::string src = extension->source()->data();
+ std::string prefix = "native function ";
+ size_t prefixPosition = src.find(prefix.c_str());
+ if (prefixPosition == std::string::npos) {
+ EvalResultHelper::compileRun(context, extension->source()->data());
+ return;
+ }
-// RegisteredExtension::RegisteredExtension(std::unique_ptr<Extension>
-// extension)
-// : extension_(std::move(extension)) {}
+ // Register v8 native function extension
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ v8::Local<v8::String> str;
+ v8::Local<v8::FunctionTemplate> v8FunctionTemplate =
+ extension->GetNativeFunctionTemplate(lwIsolate->toV8(), str);
-// // static
-// void RegisteredExtension::Register(std::unique_ptr<Extension> extension) {
-// LWNODE_RETURN_VOID;
-// }
+ auto esFunctionTemplate = CVAL(*v8FunctionTemplate)->ftpl();
+ src = src.substr(prefixPosition + prefix.length());
+ src = src.substr(0, src.find("("));
+ std::string name = src;
-// // static
-// void RegisteredExtension::UnregisterAll() {
-// LWNODE_RETURN_VOID;
-// }
+ EvalResult r = Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state,
+ std::string* name,
+ FunctionTemplateRef* functionTemplate) -> ValueRef* {
+ state->context()->globalObject()->defineDataProperty(
+ state,
+ StringRef::createFromASCII(name->c_str(), name->length()),
+ functionTemplate->instantiate(state->context()),
+ false,
+ false,
+ false);
+ return ValueRef::createUndefined();
+ },
+ &name,
+ esFunctionTemplate);
+ LWNODE_CHECK(r.isSuccessful());
+}
Extension::Extension(const char* name,
const char* source,
dep_count_(dep_count),
deps_(deps),
auto_enable_(false) {
- LWNODE_UNIMPLEMENT;
+ if (source) {
+ source_ = new ExtensionSourceString(source);
+ }
}
+// --expose-externalize-string
+
+ValueRef* ExternalizeStringExtension::externalizeStringCallback(
+ ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ if (argc > 0 && argv[0]->isString()) {
+ return argv[0]->asString();
+ }
+
+ return ValueRef::createUndefined();
+}
+
+ValueRef* ExternalizeStringExtension::isOneByteStringCallback(
+ ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ if (argc > 0 && argv[0]->isString()) {
+ return ValueRef::create(
+ StringRefHelper::isOneByteString(argv[0]->asString()));
+ }
+ return ValueRef::create(false);
+}
+
+// NOTE: --expose-externalize-string requires to define this dummy function
+// i.e., function x() { return 1; }
+ValueRef* ExternalizeStringExtension::xFunctionCallback(
+ ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ return ValueRef::create(1);
+}
+
+v8::Local<v8::FunctionTemplate>
+ExternalizeStringExtension::GetNativeFunctionTemplate(
+ v8::Isolate* isolate, v8::Local<v8::String> name) {
+ EsScope scope(isolate);
+ std::string functionName = scope.asValue(name)->toStdUTF8String();
+
+ FunctionTemplateRef* functionTemplate = nullptr;
+ if (functionName == "externalizeString") {
+ functionTemplate = createExternalizeString(scope.lwIsolate());
+ } else if (functionName == "isOneByteString") {
+ functionTemplate = createIsOneByteString(scope.lwIsolate());
+ } else {
+ functionTemplate = createXFunction(scope.lwIsolate());
+ }
+
+ return Utils::NewLocal(isolate, functionTemplate);
+}
+
+bool ExternalizeStringExtension::isRegisteredExtension() {
+ return EscargotShim::Global::flags()->isOn(
+ Flag::Type::ExposeExternalizeString);
+}
+
+void ExternalizeStringExtension::apply(ContextRef* context) {
+ ObjectRefHelper::addNativeFunction(
+ context,
+ context->globalObject(),
+ StringRef::createFromASCII("externalizeString"),
+ ExternalizeStringExtension::externalizeStringCallback);
+ ObjectRefHelper::addNativeFunction(
+ context,
+ context->globalObject(),
+ StringRef::createFromASCII("isOneByteString"),
+ ExternalizeStringExtension::isOneByteStringCallback);
+ ObjectRefHelper::addNativeFunction(
+ context,
+ context->globalObject(),
+ StringRef::createFromASCII("x"),
+ ExternalizeStringExtension::xFunctionCallback);
+}
+
+FunctionTemplateRef* ExternalizeStringExtension::createExternalizeString(
+ IsolateWrap* isolate) {
+ auto functionTemplate = FunctionTemplateRef::create(
+ AtomicStringRef::emptyAtomicString(),
+ 1,
+ false,
+ false,
+ ExternalizeStringExtension::externalizeString);
+ auto extraData = new FunctionTemplateData(functionTemplate,
+ isolate->toV8(),
+ v8::FunctionCallback(),
+ nullptr,
+ nullptr);
+ ExtraDataHelper::setExtraData(functionTemplate, extraData);
+
+ return functionTemplate;
+}
+
+FunctionTemplateRef* ExternalizeStringExtension::createIsOneByteString(
+ IsolateWrap* isolate) {
+ auto functionTemplate =
+ FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(),
+ 1,
+ false,
+ false,
+ ExternalizeStringExtension::isOneByteString);
+ auto extraData = new FunctionTemplateData(functionTemplate,
+ isolate->toV8(),
+ v8::FunctionCallback(),
+ nullptr,
+ nullptr);
+ ExtraDataHelper::setExtraData(functionTemplate, extraData);
+
+ return functionTemplate;
+}
+
+FunctionTemplateRef* ExternalizeStringExtension::createXFunction(
+ IsolateWrap* isolate) {
+ auto functionTemplate =
+ FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(),
+ 1,
+ false,
+ false,
+ ExternalizeStringExtension::xFunction);
+ auto extraData = new FunctionTemplateData(functionTemplate,
+ isolate->toV8(),
+ v8::FunctionCallback(),
+ nullptr,
+ nullptr);
+ ExtraDataHelper::setExtraData(functionTemplate, extraData);
+
+ return functionTemplate;
+}
+
+// --expose-gc
+
+v8::Local<v8::FunctionTemplate>
+ExternalizeGcExtension::GetNativeFunctionTemplate(v8::Isolate* isolate,
+ v8::Local<v8::String> name) {
+ EsScope scope(isolate);
+ std::string functionName = scope.asValue(name)->toStdUTF8String();
+
+ FunctionTemplateRef* gcFunctionTemplate = nullptr;
+ if (functionName == "gc") {
+ gcFunctionTemplate = createGcFunctionTemplate(scope.lwIsolate());
+ }
+
+ return Utils::NewLocal(isolate, gcFunctionTemplate);
+}
+
+FunctionTemplateRef* ExternalizeGcExtension::createGcFunctionTemplate(
+ EscargotShim::IsolateWrap* isolate) {
+ auto gcFunctionTemplate =
+ FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(),
+ 1,
+ false,
+ false,
+ ExternalizeGcExtension::gc);
+ auto extraData = new FunctionTemplateData(gcFunctionTemplate,
+ isolate->toV8(),
+ v8::FunctionCallback(),
+ nullptr,
+ nullptr);
+
+ return gcFunctionTemplate;
+}
+
+ValueRef* ExternalizeGcExtension::gcCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ if (argc > 0) {
+ return ValueRef::createUndefined();
+ }
+
+ auto isolate = IsolateWrap::GetCurrent();
+ if (isolate) {
+ // cleanup gc objects and then delete native c++ objects
+ // that were linked to a gc object, but became dangling by
+ // the above gc
+ isolate->CollectGarbage(); // should release weak values
+ }
+
+ Escargot::Memory::gc();
+ malloc_trim(0);
+
+ return ValueRef::createUndefined();
+}
+
+bool ExternalizeGcExtension::isRegisteredExtension() {
+ return EscargotShim::Global::flags()->isOn(Flag::Type::ExposeGC);
+}
+
+void ExternalizeGcExtension::apply(ContextRef* context) {
+ ObjectRefHelper::addNativeFunction(context,
+ context->globalObject(),
+ StringRef::createFromASCII("gc"),
+ ExternalizeGcExtension::gcCallback);
+}
+
+// -------------------------
+
void ResourceConstraints::ConfigureDefaultsFromHeapSize(
size_t initial_heap_size_in_bytes, size_t maximum_heap_size_in_bytes) {
LWNODE_RETURN_VOID;
return reinterpret_cast<i::Address*>(persistent);
#endif
- IsolateWrap::fromV8(isolate)->globalHandles()->Create(VAL(obj));
+ IsolateWrap::fromV8(isolate)->global_handles()->create(VAL(obj));
return obj;
}
#if defined(LWNODE_ENABLE_EXPERIMENTAL)
return GlobalHandles::ClearWeakness(VAL(location));
#else
- LWNODE_RETURN_NULLPTR;
+ LWNODE_ONCE(LWNODE_UNIMPLEMENT);
+ return nullptr;
#endif
}
#include "v8-util.h"
#include "api/context.h"
+#include "api/error-message.h"
#include "api/es-helper.h"
#include "api/es-v8-helper.h"
#include "api/extra-data.h"
#include "api/isolate.h"
#include "api/module.h"
#include "api/object.h"
+#include "api/serializer.h"
#include "api/utils/string-util.h"
namespace i = v8::internal;
};
// @lwnode
-struct v8::PropertyDescriptor::PrivateData {
+class v8::PropertyDescriptor::PrivateData {
public:
PrivateData() = default;
PrivateData(Escargot::ObjectPropertyDescriptorRef& esDescriptor);
Escargot::ObjectPropertyDescriptorRef* descriptor() { return descriptor_; }
private:
- bool isExternalDescriptor{false};
+ bool isExternalDescriptor_{false};
Escargot::ObjectPropertyDescriptorRef* descriptor_{nullptr};
};
// end @lwnode
+// v8 Extension
+class RegisteredExtension {
+ public:
+ static void registerExtension(std::unique_ptr<Extension> extension);
+ static void unregisterAll();
+ static void applyAll(ContextRef* context);
+
+ private:
+ explicit RegisteredExtension(Extension*) {}
+ explicit RegisteredExtension(std::unique_ptr<Extension>) {}
+
+ static bool isLwExtension(Extension* extension);
+ static void applyV8Extension(ContextRef* context, Extension* extension);
+
+ static std::vector<std::unique_ptr<Extension>> extensions;
+};
+
+class LwExtension : public v8::Extension {
+ public:
+ LwExtension(const char* name,
+ const char* source = nullptr,
+ int dep_count = 0,
+ const char** deps = nullptr,
+ int source_length = -1)
+ : v8::Extension(name, source, dep_count, deps, source_length) {}
+
+ v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
+ v8::Isolate* isolate, v8::Local<v8::String> name) override {
+ return Local<FunctionTemplate>();
+ };
+
+ virtual bool isRegisteredExtension() = 0;
+ virtual void apply(ContextRef* context) = 0;
+};
+
+class ExternalizeStringExtension : public LwExtension {
+ public:
+ ExternalizeStringExtension() : LwExtension("v8/externalize") {}
+ v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
+ v8::Isolate* isolate, v8::Local<v8::String> name) override;
+
+ bool isRegisteredExtension() override;
+ void apply(ContextRef* context) override;
+
+ FunctionTemplateRef* createExternalizeString(
+ EscargotShim::IsolateWrap* isolate);
+ FunctionTemplateRef* createIsOneByteString(
+ EscargotShim::IsolateWrap* isolate);
+ FunctionTemplateRef* createXFunction(EscargotShim::IsolateWrap* isolate);
+
+ static ValueRef* externalizeStringCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+ static ValueRef* externalizeString(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) {
+ return externalizeStringCallback(state, thisValue, argc, argv, false);
+ }
+
+ static ValueRef* isOneByteStringCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+ static ValueRef* isOneByteString(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) {
+ return isOneByteStringCallback(state, thisValue, argc, argv, false);
+ }
+
+ static ValueRef* xFunctionCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+ static ValueRef* xFunction(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) {
+ return xFunctionCallback(state, thisValue, argc, argv, false);
+ }
+
+ private:
+};
+
+class ExternalizeGcExtension : public LwExtension {
+ public:
+ ExternalizeGcExtension() : LwExtension("v8/gc") {}
+ v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
+ v8::Isolate* isolate, v8::Local<v8::String> name) override;
+
+ bool isRegisteredExtension() override;
+ void apply(ContextRef* context) override;
+
+ FunctionTemplateRef* createGcFunctionTemplate(
+ EscargotShim::IsolateWrap* isolate);
+
+ static ValueRef* gcCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+
+ static ValueRef* gc(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) {
+ return gcCallback(state, thisValue, argc, argv, false);
+ }
+};
+
} // namespace v8
namespace {
*/
#include "context.h"
+
+#include <malloc.h> // for malloc_trim
#include "base.h"
#include "es-helper.h"
#include "extra-data.h"
#include "isolate.h"
#include "stack-trace.h"
+#include "api.h"
+#include "api/global.h"
+
using namespace Escargot;
namespace EscargotShim {
StringRef::createFromASCII("stackTraceLimit"),
ValueRef::create(
20)); // TODO: get number from '--stack-trace-limit' options
+
+ if (!EscargotShim::Global::flags()->isOn(
+ EscargotShim::Flag::Type::AllowCodeGenerationFromString)) {
+ // @note --disallow-code-generation-from-strings as default
+ state->context()->globalObject()->defineDataProperty(
+ state,
+ StringRef::createFromASCII("eval"),
+ FunctionObjectRef::create(
+ state,
+ FunctionObjectRef::NativeFunctionInfo(
+ AtomicStringRef::emptyAtomicString(),
+ [](ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) -> ValueRef* {
+ state->throwException(
+ ExceptionHelper::createErrorObject(
+ state->context(),
+ ErrorMessageType::kDisallowCodeGeneration));
+ return ValueRef::createUndefined();
+ },
+ 0,
+ true,
+ false)),
+ false,
+ false,
+ false);
+ }
+
return ValueRef::createUndefined();
});
LWNODE_CHECK(r.isSuccessful());
}
// ContextWrap
-
-ContextWrap::ContextWrap(IsolateWrap* isolate) {
+ContextWrap::ContextWrap(IsolateWrap* isolate,
+ v8::ExtensionConfiguration* extensionConfiguration) {
isolate_ = isolate;
context_ = ContextRef::create(isolate->vmInstance());
callSite_ = new CallSite(context_);
+ // NOTE: Not tested with multi initialization
+ initDebugger();
+
auto globalObjectData = new GlobalObjectData();
globalObjectData->setInternalFieldCount(
GlobalObjectData::kInternalFieldCount);
val_ = context_;
type_ = Type::Context;
+
+ RegisteredExtension::applyAll(context_);
+}
+
+void ContextWrap::initDebugger() {
+ if (Global::flags()->isOn(Flag::Type::DebugServer)) {
+ // FIXME: Have a port allocator
+ context_->initDebuggerRemote("--port=6501");
+ }
}
-ContextWrap* ContextWrap::New(IsolateWrap* isolate) {
+ContextWrap* ContextWrap::New(
+ IsolateWrap* isolate, v8::ExtensionConfiguration* extensionConfiguration) {
LWNODE_CHECK_NOT_NULL(isolate);
- return new ContextWrap(isolate);
+ return new ContextWrap(isolate, extensionConfiguration);
}
ContextWrap* ContextWrap::fromEscargot(Escargot::ContextRef* esContext) {
return lwCreationContext;
}
+ContextWrap* ContextWrap::fromV8(v8::Context* context) {
+ return CVAL(context)->context();
+}
+
void ContextWrap::Enter() {
isolate_->Enter();
isolate_->pushContext(this);
class ContextWrap : public ValueWrap {
public:
- static ContextWrap* New(IsolateWrap* isolate);
+ static ContextWrap* New(
+ IsolateWrap* isolate,
+ v8::ExtensionConfiguration* extensionConfiguration = nullptr);
static ContextWrap* fromEscargot(Escargot::ContextRef* context);
+ static ContextWrap* fromV8(v8::Context* context);
void Enter();
void Exit();
CallSite* callSite() { return callSite_; }
+ void initDebugger();
+
private:
EmbedderDataMap* embedder_data_{nullptr};
- ContextWrap(IsolateWrap* isolate);
+ ContextWrap(IsolateWrap* isolate,
+ v8::ExtensionConfiguration* extensionConfiguration);
void setEmbedderData(int index, void* value);
void* getEmbedderData(int index);
*/
#include "engine.h"
+
#include <iomanip>
#include <sstream>
+
+#include "api/global.h"
#include "handle.h"
#include "utils/misc.h"
#include "utils/string-util.h"
}
void GCHeap::printStatus(bool forcePrint) {
- if (Flags::isTraceCallEnabled("GCHEAP") == false) {
+ if (!Global::flags()->isOn(Flag::Type::TraceCall, "GCHEAP")) {
return;
}
LWNODE_DLOG_INFO("[HOLD]");
printAddress(persistents_,
[](std::stringstream& stream, const HeapSegment& iter) {
+ std::ios_base::fmtflags flags(stream.flags());
stream << std::setw(15) << std::right
<< GC_UNWRAP_PERSISTENT_POINTER(iter.first) << " ("
<< "S" << std::setw(3) << iter.second.strong << " W"
<< std::setw(3) << iter.second.weak << ") ";
+ stream.flags(flags);
});
LWNODE_DLOG_INFO(CLR_GREEN "------------------" CLR_RESET);
LWNODE_DLOG_INFO("[PHANTOM]");
printAddress(weakPhantoms_,
[](std::stringstream& stream, const HeapSegment& iter) {
+ std::ios_base::fmtflags flags(stream.flags());
stream << std::setw(15) << std::right
<< GC_UNWRAP_WEAK_POINTER(iter.first) << " ("
<< "S" << std::setw(3) << iter.second.strong << " W"
<< std::setw(3) << iter.second.weak << ") ";
+ stream.flags(flags);
});
LWNODE_DLOG_INFO(CLR_GREEN "------------------" CLR_RESET);
Memory::setGCFrequency(GC_FREE_SPACE_DIVISOR);
gcHeap_.reset(GCHeap::create());
- auto flags = Flags::get();
- if (Flags::isTraceGCEnabled()) {
+ if (Global::flags()->isOn(Flag::Type::TraceGC)) {
LWNODE_DLOG_WARN("temporary blocked for postGarbageCollectionProcessing");
registerGCEventListeners();
}
+
+ mainThreadId_ = std::this_thread::get_id();
}
void Engine::dispose() {
unregisterGCEventListeners();
gcHeap_.release();
- MemoryUtil::gcFull();
GC_invoke_finalizers();
Globals::finalize();
return s_engine;
}
+void Engine::initializeThread() {
+ if (mainThreadId_ != std::this_thread::get_id()) {
+ Globals::initializeThread();
+ }
+}
+
+void Engine::finalizeThread() {
+ if (mainThreadId_ != std::this_thread::get_id()) {
+ Globals::finalizeThread();
+ }
+}
+
Engine::State Engine::getState() {
return s_state;
}
#include <EscargotPublic.h>
#include <string.h>
#include <v8.h>
+#include <thread>
#include <vector>
#include "utils/gc.h"
StringRef* src,
PromiseObjectRef* promise) override;
+ void customInfoLogger(const char* format, va_list args) override {}
+ void customErrorLogger(const char* format, va_list args) override {}
+
std::vector<std::tuple<std::string /* abs path */,
ContextRef*,
PersistentRefHolder<ScriptRef>>>
static State getState();
+ void initializeThread();
+ void finalizeThread();
+
private:
Engine() = default;
void initialize();
s_externalStrings;
PersistentRefHolder<GCHeap> gcHeap_;
+ std::thread::id mainThreadId_;
};
} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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.
+ */
+
+#pragma once
+
+namespace EscargotShim {
+
+#define ERROR_MESSAGE_TEMPLATES(T) \
+ T(None, None, "") \
+ T(DataCloneErrorOutOfMemory, \
+ RangeError, \
+ "Data cannot be cloned, out of memory.") \
+ T(InternalFieldsOutOfRange, RangeError, "Internal field out of bounds.") \
+ T(NotReadValue, RangeError, "Cannot read value") \
+ T(IllegalInvocation, TypeError, "Illegal invocation") \
+ T(DisallowCodeGeneration, \
+ EvalError, \
+ "Code generation from strings disallowed for this context")
+} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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 "error-message.h"
+#include "utils/misc.h"
+
+using namespace Escargot;
+
+namespace EscargotShim {
+
+ErrorObjectRef::Code ErrorMessage::getErrorCode(ErrorMessageType type) {
+ switch (type) {
+#define CASE(NAME, CODE, STRING) \
+ case ErrorMessageType::k##NAME: \
+ return Escargot::ErrorObjectRef::Code::CODE;
+ ERROR_MESSAGE_TEMPLATES(CASE)
+#undef CASE
+ case ErrorMessageType::kErrorMessageCount:
+ default:
+ return ErrorObjectRef::Code::None;
+ }
+}
+
+const char* ErrorMessage::getErrorString(ErrorMessageType type) {
+ switch (type) {
+#define CASE(NAME, CODE, STRING) \
+ case ErrorMessageType::k##NAME: \
+ return STRING;
+ ERROR_MESSAGE_TEMPLATES(CASE)
+#undef CASE
+ case ErrorMessageType::kErrorMessageCount:
+ default:
+ return nullptr;
+ }
+}
+
+Escargot::StringRef* ErrorMessage::createErrorStringRef(ErrorMessageType type) {
+ auto errorString = ErrorMessage::getErrorString(type);
+ LWNODE_DCHECK_NOT_NULL(errorString);
+ return StringRef::createFromASCII(errorString, strlen(errorString));
+}
+
+} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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.
+ */
+
+#pragma once
+
+#include <EscargotPublic.h>
+#include "error-message-template.h"
+
+namespace Escargot {
+class StringRef;
+}
+
+namespace EscargotShim {
+
+enum class ErrorMessageType {
+#define TEMPLATE(NAME, CODE, STRING) k##NAME,
+ ERROR_MESSAGE_TEMPLATES(TEMPLATE)
+#undef TEMPLATE
+ kErrorMessageCount
+};
+
+class ErrorMessage {
+ public:
+ static Escargot::ErrorObjectRef::Code getErrorCode(ErrorMessageType type);
+ static const char* getErrorString(ErrorMessageType type);
+ static Escargot::StringRef* createErrorStringRef(ErrorMessageType type);
+};
+
+} // namespace EscargotShim
*/
#include "es-helper.h"
+
+#include "api/error-message.h"
+#include "base.h"
#include "context.h"
#include "extra-data.h"
#include "isolate.h"
+#include "stack-trace.h"
#include "utils/misc.h"
#include "utils/string-util.h"
#include <sstream>
using namespace Escargot;
+using namespace v8;
namespace EscargotShim {
context,
[](ExecutionStateRef* state,
ObjectRef* object,
- ValueRef* param1,
- ValueRef* param2) -> ValueRef* {
+ ValueRef* key,
+ ValueRef* value) -> ValueRef* {
// 1. if failed because of its descriptor or strict mode, ObjectRef::set
// returns false, but evalResult will has no error.
// 2. if failed because of throwing an exception, evalResult will be an
// error.
- return ValueRef::create(object->set(state, param1, param2));
+ return ValueRef::create(object->set(state, key, value));
},
object,
key,
ValueRef* ObjectRefHelper::getOwnPropertyAttributes(ExecutionStateRef* state,
ObjectRef* object,
ValueRef* key) {
- auto val = object->getOwnPropertyDescriptor(state, key);
- if (val->isUndefined()) {
+ auto descriptor = object->getOwnPropertyDescriptor(state, key);
+ if (descriptor->isUndefined()) {
return ValueRef::createUndefined();
}
- int attr = ObjectRef::PresentAttribute::NotPresent;
+ int attribute = ObjectRef::PresentAttribute::NotPresent;
- bool isWritable = val->asObject()
+ bool isWritable = descriptor->asObject()
->get(state, StringRef::createFromASCII("writable"))
->asBoolean();
- bool isEnumerable = val->asObject()
+ bool isEnumerable = descriptor->asObject()
->get(state, StringRef::createFromASCII("enumerable"))
->asBoolean();
bool isConfigurable =
- val->asObject()
+ descriptor->asObject()
->get(state, StringRef::createFromASCII("configurable"))
->asBoolean();
if (isWritable) {
- attr = attr | ObjectRef::PresentAttribute::WritablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::WritablePresent;
} else {
- attr = attr | ObjectRef::PresentAttribute::NonWritablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::NonWritablePresent;
}
if (isEnumerable) {
- attr = attr | ObjectRef::PresentAttribute::EnumerablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::EnumerablePresent;
} else {
- attr = attr | ObjectRef::PresentAttribute::NonEnumerablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::NonEnumerablePresent;
}
if (isConfigurable) {
- attr = attr | ObjectRef::PresentAttribute::ConfigurablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::ConfigurablePresent;
} else {
- attr = attr | ObjectRef::PresentAttribute::NonConfigurablePresent;
+ attribute = attribute | ObjectRef::PresentAttribute::NonConfigurablePresent;
}
- return ValueRef::create(attr);
+ return ValueRef::create(attribute);
}
-EvalResult ObjectRefHelper::getPropertyAttributes(ContextRef* context,
- ObjectRef* object,
- ValueRef* key,
- bool skipPrototype) {
+EvalResult ObjectRefHelper::getPropertyAttributes(
+ ContextRef* context,
+ ObjectRef* object,
+ ValueRef* key,
+ bool skipTraversingPrototypeChain) {
LWNODE_DCHECK_NOT_NULL(object);
LWNODE_DCHECK_NOT_NULL(key);
- EvalResult r = Evaluator::execute(
+ return Evaluator::execute(
context,
[](ExecutionStateRef* state,
ObjectRef* object,
ValueRef* key,
- bool skipPrototype) -> ValueRef* {
+ bool skipTraversingPrototypeChain) -> ValueRef* {
LWNODE_DCHECK_NOT_NULL(object);
LWNODE_DCHECK_NOT_NULL(key);
- ValueRef* attr = ValueRef::createUndefined();
- for (ObjectRef* o = object; o;
- o = o->getPrototypeObject(state).value()) {
- attr = getOwnPropertyAttributes(state, o, key);
- if (skipPrototype || !attr->isUndefined()) {
+ ValueRef* attribute = ValueRef::createUndefined();
+
+ for (ObjectRef* currentObject = object; currentObject;
+ currentObject = currentObject->getPrototypeObject(state).value()) {
+ attribute = getOwnPropertyAttributes(state, currentObject, key);
+ if (skipTraversingPrototypeChain || !attribute->isUndefined()) {
break;
}
}
- return attr;
+ return attribute;
},
object,
key,
- skipPrototype);
-
- return r;
+ skipTraversingPrototypeChain);
}
EvalResult ObjectRefHelper::getProperty(ContextRef* context,
EvalResult ObjectRefHelper::setPrototype(ContextRef* context,
ObjectRef* object,
- ValueRef* proto) {
+ ValueRef* prototype) {
auto r = Evaluator::execute(
context,
[](ExecutionStateRef* state,
ObjectRef* object,
- ValueRef* proto) -> ValueRef* {
- return ValueRef::create(object->setPrototype(state, proto));
+ ValueRef* prototype) -> ValueRef* {
+ return ValueRef::create(object->setPrototype(state, prototype));
},
object,
- proto);
+ prototype);
LWNODE_CHECK(r.isSuccessful());
return r;
ContextRef* context,
SymbolRef* privateValueSymbol,
ObjectRef* object,
- ValueRef* param1,
- ValueRef* param2) -> ValueRef* {
+ ValueRef* key,
+ ValueRef* value) -> ValueRef* {
ValueRef* hiddenValuesRef = object->get(state, privateValueSymbol);
ObjectRef* hiddenValuesObject = nullptr;
hiddenValuesObject = hiddenValuesRef->asObject();
}
- hiddenValuesObject->set(state, param1, param2);
+ hiddenValuesObject->set(state, key, value);
return ValueRef::create(true);
},
return r.result->asString();
}
+void ObjectRefHelper::addNativeFunction(ContextRef* context,
+ ObjectRef* object,
+ StringRef* name,
+ NativeFunctionPointer function) {
+ Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state,
+ ObjectRef* target,
+ StringRef* name,
+ NativeFunctionPointer nativeFunction) -> ValueRef* {
+ target->defineDataProperty(
+ state,
+ name,
+ FunctionObjectRef::create(state,
+ FunctionObjectRef::NativeFunctionInfo(
+ AtomicStringRef::emptyAtomicString(),
+ nativeFunction,
+ 0,
+ true,
+ false)),
+ false,
+ false,
+ false);
+
+ return ValueRef::createUndefined();
+ },
+ object,
+ name,
+ function);
+}
+
+ArrayObjectRef* ArrayObjectRefHelper::create(ContextRef* context,
+ const uint64_t length) {
+ auto result = Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state, uint64_t length) -> ValueRef* {
+ return ArrayObjectRef::create(state, length);
+ },
+ length);
+ LWNODE_CHECK(result.isSuccessful());
+ return result.result->asArrayObject();
+}
+
+ArrayObjectRef* ArrayObjectRefHelper::create(ContextRef* context,
+ ValueVectorRef* elements) {
+ auto result = Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state, ValueVectorRef* elements) -> ValueRef* {
+ return ArrayObjectRef::create(state, elements);
+ },
+ elements);
+ LWNODE_CHECK(result.isSuccessful());
+ return result.result->asArrayObject();
+}
+
+uint64_t ArrayObjectRefHelper::length(ContextRef* context,
+ ArrayObjectRef* object) {
+ uint64_t output = 0;
+ auto result = Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state,
+ ArrayObjectRef* object,
+ uint64_t* output) -> ValueRef* {
+ *output = object->length(state);
+ return ValueRef::createUndefined();
+ },
+ object,
+ &output);
+ LWNODE_CHECK(result.isSuccessful());
+ return output;
+}
+
+ValueRef* ArrayObjectRefHelper::get(ContextRef* context,
+ ArrayObjectRef* object,
+ ValueRef::ValueIndex index) {
+ auto result =
+ ObjectRefHelper::getProperty(context, object, ValueRef::create(index));
+ LWNODE_CHECK(result.isSuccessful());
+ return result.result;
+}
+
+void ArrayObjectRefHelper::set(ContextRef* context,
+ ArrayObjectRef* object,
+ ValueRef::ValueIndex index,
+ ValueRef* value) {
+ auto result = ObjectRefHelper::setProperty(
+ context, object, ValueRef::create(index), value);
+ LWNODE_CHECK(result.isSuccessful());
+}
+
static std::string getCodeLine(const std::string& codeString, int errorLine) {
if (errorLine < 1 || codeString.empty()) {
return "";
oss << "Call Stack:" << std::endl;
for (size_t i = 0; i < maxPrintStackSize; ++i) {
const auto& iter = traceData[i];
- const auto& resourceName = iter.src->toStdUTF8String();
+ const auto& resourceName = iter.srcName->toStdUTF8String();
const auto& codeString = iter.sourceCode->toStdUTF8String();
const int errorLine = iter.loc.line;
const int errorColumn = iter.loc.column;
return oss.str();
}
-std::string EvalResultHelper::getCallStackStringAsNodeStyle(
- const GCManagedVector<Evaluator::StackTraceData>& traceData,
- size_t maxStackSize) {
- std::ostringstream oss;
- const std::string separator = " ";
- size_t maxPrintStackSize = std::min((int)maxStackSize, (int)traceData.size());
-
- for (size_t i = 0; i < maxPrintStackSize; ++i) {
- const auto& iter = traceData[i];
- const auto& resourceName = iter.src->toStdUTF8String();
- const auto& functionName = iter.functionName->toStdUTF8String();
- const int errorLine = iter.loc.line;
- const int errorColumn = iter.loc.column;
-
- oss << separator << "at "
- << (functionName == "" ? "Object.<anonymous>" : functionName) << " "
- << "(" << (resourceName == "" ? "?" : resourceName) << ":" << errorLine
- << ":" << errorColumn << ")" << std::endl;
- }
-
- return oss.str();
-}
-
std::string EvalResultHelper::getErrorString(
ContextRef* context, const Evaluator::EvaluatorResult& result) {
- const auto& traceData = result.stackTraceData;
+ const auto& traceData = result.stackTrace;
const auto& reasonString =
result.resultOrErrorToString(context)->toStdUTF8String();
const std::string separator = " ";
if (traceData.size()) {
const auto& lastTraceData = traceData[0];
- const auto& resourceName = lastTraceData.src->toStdUTF8String();
+ const auto& resourceName = lastTraceData.srcName->toStdUTF8String();
const auto& codeString = lastTraceData.sourceCode->toStdUTF8String();
const int errorLine = lastTraceData.loc.line;
const int errorColumn = lastTraceData.loc.column;
compileResult.script.get());
if (r.isSuccessful() == false) {
- LWNODE_LOG_INTERNAL("Execute:\n %s (%s:%d)\n%s",
- TRACE_ARGS2,
+ LWNODE_LOG_INTERNAL(RAW,
+ "Execute:\n %s\n%s",
+ __CODE_LOCATION__,
EvalResultHelper::getErrorString(context, r).c_str());
}
return r;
ss << " ";
}
- LWNODE_LOG_INTERNAL("%s", ss.str().c_str());
+ LWNODE_LOG_INTERNAL(RAW, "%s", ss.str().c_str());
}
return ValueRef::createUndefined();
};
ss << " ";
}
- LWNODE_LOG_INTERNAL("%s", ss.str().c_str());
+ LWNODE_LOG_INTERNAL(RAW, "%s", ss.str().c_str());
}
return ValueRef::createUndefined();
};
}
LWNODE_LOG_INTERNAL(
+ RAW,
"%s",
- getCallStackString(state->computeStackTraceData(), maxStackSize)
- .c_str());
+ getCallStackString(state->computeStackTrace(), maxStackSize).c_str());
return ValueRef::createUndefined();
};
void ObjectTemplateRefHelper::setInternalFieldCount(ObjectTemplateRef* otpl,
int size) {
- auto objectTemplateData =
- ExtraDataHelper::getExtraData(otpl)->asObjectTemplateData();
+ auto objectTemplateData = ExtraDataHelper::getObjectTemplateExtraData(otpl);
objectTemplateData->setInternalFieldCount(size);
}
int ObjectTemplateRefHelper::getInternalFieldCount(ObjectTemplateRef* otpl) {
- auto data = ExtraDataHelper::getExtraData(otpl);
+ auto data = ExtraDataHelper::getObjectTemplateExtraData(otpl);
if (data == nullptr) {
return 0;
}
- return data->asObjectTemplateData()->internalFieldCount();
+ return data->internalFieldCount();
}
// --- ExceptionHelper ---
ErrorObjectRef::Code code,
StringRef* errorMessage) -> ValueRef* {
auto errorObject = ErrorObjectRef::create(state, code, errorMessage);
- ExceptionHelper::setStackPropertyIfNotExist(state, errorObject);
+ ExceptionHelper::addStackPropertyCallback(state, errorObject);
return errorObject;
},
code,
return r.result->asErrorObject();
}
-void ExceptionHelper::setStackPropertyIfNotExist(ExecutionStateRef* state,
- Escargot::ValueRef* error) {
+ErrorObjectRef* ExceptionHelper::createErrorObject(ContextRef* context,
+ ErrorMessageType type) {
+ return ExceptionHelper::createErrorObject(
+ context,
+ ErrorMessage::getErrorCode(type),
+ ErrorMessage::createErrorStringRef(type));
+}
+
+void ExceptionHelper::addStackPropertyCallback(ExecutionStateRef* state,
+ Escargot::ValueRef* error) {
if (!error->isObject()) {
return;
}
+ auto lwIsolate = IsolateWrap::GetCurrent();
auto errorObject = error->asObject();
- auto stackString = StringRef::createFromASCII("stack");
- if (errorObject->has(state, stackString)) {
- return;
- }
- auto stack = EvalResultHelper::getCallStackStringAsNodeStyle(
- state->computeStackTraceData(), 1);
- auto message = errorObject->toString(state)->toStdUTF8String();
+ ValueRef* formattedStackTrace = StringRef::emptyString();
+ StackTrace stackTrace(state);
+ if (lwIsolate->HasPrepareStackTraceCallback()) {
+ auto lwContext = lwIsolate->GetCurrentContext();
- if (message.length() > 0) {
- stack = message + "\n" + stack;
+ auto sites = stackTrace.genCallSites();
+ formattedStackTrace = lwIsolate->RunPrepareStackTraceCallback(
+ state, lwContext, errorObject, sites);
+ } else {
+ formattedStackTrace =
+ stackTrace.formatStackTraceStringNodeStyle(errorObject);
}
- errorObject->set(state,
- stackString,
- StringRef::createFromASCII(stack.data(), stack.length()));
+ bool ok = errorObject->defineDataProperty(state,
+ StringRef::createFromASCII("stack"),
+ formattedStackTrace,
+ true,
+ false,
+ true);
+ LWNODE_CHECK(ok);
}
// --- StringRefHelper ---
-bool StringRefHelper::isAsciiString(StringRef* str) {
- auto bufferData = str->stringBufferAccessData();
+bool StringRefHelper::isAsciiString(StringRef* string) {
+ auto bufferData = string->stringBufferAccessData();
if (!bufferData.has8BitContent) {
return false;
return isAscii;
}
+bool StringRefHelper::isOneByteString(StringRef* str) {
+ auto bufferData = str->stringBufferAccessData();
+
+ for (size_t i = 0; i < bufferData.length; i++) {
+ char16_t c = bufferData.charAt(i);
+ if (c > 255) { // including all 8 bit code
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace EscargotShim
class ObjectData;
class FunctionData;
class ValueWrap;
+enum class ErrorMessageType;
typedef Evaluator::EvaluatorResult EvalResult;
typedef FunctionObjectRef::NativeFunctionPointer NativeFunctionPointer;
ObjectRef* object,
ValueRef* key);
- static EvalResult getPropertyAttributes(ContextRef* context,
- ObjectRef* object,
- ValueRef* key,
- bool skipPrototype = false);
+ static EvalResult getPropertyAttributes(
+ ContextRef* context,
+ ObjectRef* object,
+ ValueRef* key,
+ bool skipTraversingPrototypeChain = false);
static EvalResult hasProperty(ContextRef* context,
ObjectRef* object,
static ObjectRef* toObject(ContextRef* context, ValueRef* value);
static StringRef* getConstructorName(ContextRef* context, ObjectRef* object);
+ static void addNativeFunction(ContextRef* context,
+ ObjectRef* object,
+ StringRef* name,
+ NativeFunctionPointer function);
+
private:
static ValueRef* getOwnPropertyAttributes(ExecutionStateRef* state,
ObjectRef* object,
ValueRef* key);
};
+class ArrayObjectRefHelper {
+ public:
+ static ArrayObjectRef* create(ContextRef* context, const uint64_t length);
+ static ArrayObjectRef* create(ContextRef* context, ValueVectorRef* elements);
+ static uint64_t length(ContextRef* context, ArrayObjectRef* object);
+ static ValueRef* get(ContextRef* context,
+ ArrayObjectRef* object,
+ ValueRef::ValueIndex index);
+ static void set(ContextRef* context,
+ ArrayObjectRef* object,
+ ValueRef::ValueIndex index,
+ ValueRef* value);
+};
+
class ObjectTemplateData;
class FunctionTemplateData;
class FunctionData;
return (ExtraData*)object->extraData();
}
- static ExtraData* getExtraData(TemplateRef* esTemplate) {
- return (ExtraData*)esTemplate->instanceExtraData();
+ static FunctionData* getFunctionExtraData(ObjectRef* functionObject) {
+ auto extraData = (ExtraData*)functionObject->extraData();
+ if (extraData) {
+ return extraData->asFunctionData();
+ }
+
+ return nullptr;
+ }
+
+ static ObjectTemplateData* getObjectTemplateExtraData(
+ ObjectRef* objectTemplate) {
+ auto extraData = (ExtraData*)objectTemplate->extraData();
+ if (extraData) {
+ return extraData->asObjectTemplateData();
+ }
+
+ return nullptr;
+ }
+
+ static ObjectTemplateData* getObjectTemplateExtraData(
+ ObjectTemplateRef* objectTemplate) {
+ auto extraData = (ExtraData*)objectTemplate->instanceExtraData();
+ if (extraData) {
+ return extraData->asObjectTemplateData();
+ }
+
+ return nullptr;
+ }
+
+ static FunctionTemplateData* getFunctionTemplateExtraData(
+ FunctionTemplateRef* functionTemplate) {
+ auto extraData = (ExtraData*)functionTemplate->instanceExtraData();
+ if (extraData) {
+ return extraData->asFunctionTemplateData();
+ }
+
+ return nullptr;
}
// Only allow the following (Object, extraData) pairs when adding extraData
static ErrorObjectRef* createErrorObject(ContextRef* context,
ErrorObjectRef::Code code,
StringRef* errorMessage);
- static void setStackPropertyIfNotExist(ExecutionStateRef* state,
- Escargot::ValueRef* error);
+ static ErrorObjectRef* createErrorObject(ContextRef* context,
+ ErrorMessageType type);
+ static void addStackPropertyCallback(ExecutionStateRef* state,
+ Escargot::ValueRef* error);
};
class StringRefHelper {
}
static bool isAsciiString(StringRef* str);
+ static bool isOneByteString(StringRef* str);
};
} // namespace EscargotShim
void InternalFieldData::setInternalFieldCount(int size) {
LWNODE_CALL_TRACE_ID(OBJDATA, "%d", size);
- // TODO: throw internal error
if (size <= 0) {
- LWNODE_DLOG_ERROR("InternalField: Invalid field count: %d\n", size);
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ lwIsolate->onFatalError("InternalFieldData::setInternalFieldCount",
+ "Internal field out of bounds");
return;
}
}
void InternalFieldData::setInternalField(int idx, void* lwValue) {
- // TODO: throw internal error
LWNODE_CHECK_NOT_NULL(internalFields_);
if (!isValidIndex(idx)) {
- LWNODE_DLOG_ERROR("InternalField: Internal field out of bounds");
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ lwIsolate->onFatalError("InternalFieldData::setInternalField",
+ "Internal field out of bounds");
return;
}
}
void* InternalFieldData::internalField(int idx) {
- // TODO: throw internal error
LWNODE_CHECK_NOT_NULL(internalFields_);
if (!isValidIndex(idx)) {
- LWNODE_DLOG_ERROR("InternalField: Internal field out of bounds");
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ lwIsolate->onFatalError("InternalFieldData::internalField",
+ "Internal field out of bounds");
return nullptr;
}
}
ObjectData::ObjectData(ObjectTemplateRef* objectTemplate)
- : TemplateData(ExtraDataHelper::getExtraData(objectTemplate)
- ->asObjectTemplateData()
+ : TemplateData(ExtraDataHelper::getObjectTemplateExtraData(objectTemplate)
->functionTemplate()),
objectTemplate_(objectTemplate) {}
v8::Isolate* FunctionData::isolate() {
if (functionTemplate_) {
- auto functionTemplateData = ExtraDataHelper::getExtraData(functionTemplate_)
- ->asFunctionTemplateData();
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(functionTemplate_);
return functionTemplateData->isolate();
}
v8::FunctionCallback FunctionData::callback() {
if (functionTemplate_) {
- auto functionTemplateData = ExtraDataHelper::getExtraData(functionTemplate_)
- ->asFunctionTemplateData();
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(functionTemplate_);
return functionTemplateData->callback();
}
v8::Value* FunctionData::callbackData() {
if (functionTemplate_) {
- auto functionTemplateData = ExtraDataHelper::getExtraData(functionTemplate_)
- ->asFunctionTemplateData();
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(functionTemplate_);
return functionTemplateData->callbackData();
}
v8::Signature* FunctionData::signature() {
if (functionTemplate_) {
- auto functionTemplateData = ExtraDataHelper::getExtraData(functionTemplate_)
- ->asFunctionTemplateData();
+ auto functionTemplateData =
+ ExtraDataHelper::getFunctionTemplateExtraData(functionTemplate_);
return functionTemplateData->signature();
}
GCVector<StackTraceData*>* ExceptionObjectData::stackTrace(
ObjectRef* exceptionObject) {
- auto exceptionObjectData =
- ExtraDataHelper::getExtraData(exceptionObject)->asExceptionObjectData();
-
- return exceptionObjectData->stackTrace();
+ auto extraData = ExtraDataHelper::getExtraData(exceptionObject);
+ if (extraData) {
+ return extraData->asExceptionObjectData()->stackTrace();
+ } else {
+ // FIXME: Check if missing extradata is ok. We print a warning
+ // here until this issue is investigated
+ LWNODE_LOG_WARN("esException does not have an extraData\n");
+ return new GCVector<StackTraceData*>();
+ }
}
} // namespace EscargotShim
class StackTraceData : public ExtraData {
public:
StackTraceData(const Escargot::Evaluator::StackTraceData& data)
- : src_(data.src),
+ : src_(data.srcName),
sourceCode_(data.sourceCode),
loc_(data.loc),
functionName_(data.functionName),
isConstructor_(data.isConstructor),
isAssociatedWithJavaScriptCode_(data.isAssociatedWithJavaScriptCode),
- isEval_(data.isEval) {}
+ isEval_(data.isEval),
+ callee_(data.callee) {}
bool isStackTraceData() const override { return true; }
return isAssociatedWithJavaScriptCode_;
}
bool isEval() const { return isEval_; }
+ OptionalRef<FunctionObjectRef> callee() { return callee_; }
private:
StringRef* src_{nullptr};
bool isConstructor_{false};
bool isAssociatedWithJavaScriptCode_{false};
bool isEval_{false};
+ OptionalRef<FunctionObjectRef> callee_;
};
// NOTE: ExceptionObjectData does not use any InternalFields.
#include "base.h"
#include "global-handles.h"
-namespace EscargotShim {
-
-std::vector<GlobalHandles*> g_globalHandlesVector;
-
-class GlobalWeakHandler {
- public:
- void pushBlock(ValueWrap* lwValue,
- std::unique_ptr<GlobalHandles::NodeBlock> block) {
- auto iter = weakValues_.find(lwValue);
- if (iter != weakValues_.end()) {
- // TODO
- LWNODE_CHECK_NOT_REACH_HERE();
- }
- weakValues_.emplace(lwValue, std::move(block));
+#include "api/utils/gc.h"
+
+namespace v8 {
+namespace internal {
+void GlobalHandles::Destroy(EscargotShim::ValueWrap* lwValue) {
+ auto isolate = EscargotShim::IsolateWrap::GetCurrent();
+ if (isolate) {
+ isolate->global_handles()->destroy(lwValue);
}
-
- std::unique_ptr<GlobalHandles::NodeBlock> popBlock(ValueWrap* lwValue) {
- auto iter = weakValues_.find(lwValue);
- if (iter == weakValues_.end()) {
- return nullptr;
- }
- auto nodeBlock = std::move(iter->second);
- weakValues_.erase(iter);
-
- return nodeBlock;
- }
-
- size_t clearWeakValue() {
- auto weakValuesSize = weakValues_.size();
- if (weakValuesSize == 0) {
- return 0;
- }
- LWNODE_CALL_TRACE_ID(
- GLOBALHANDLES, "Clear weak values: %ld", weakValuesSize);
- for (auto& iter : weakValues_) {
- iter.second->releaseValue();
- }
- return weakValuesSize;
- }
-
- bool isWeak(ValueWrap* lwValue) {
- return weakValues_.find(lwValue) != weakValues_.end();
- }
-
- void dispose() { weakValues_.clear(); }
-
- private:
- std::unordered_map<ValueWrap*, std::unique_ptr<GlobalHandles::NodeBlock>>
- weakValues_;
-};
-
-GlobalWeakHandler g_globalWeakHandler;
-
-GlobalHandles::Node::Node(void* parameter,
- v8::WeakCallbackInfo<void>::Callback callback)
- : parameter_(parameter), callback_(callback) {}
-
-GlobalHandles::Node::~Node() {}
-
-GlobalHandles::NodeBlock::NodeBlock(v8::Isolate* isolate,
- ValueWrap* value,
- uint32_t count)
- : isolate_(isolate), value_(value), usedNodes_(count) {
- holder_.reset(value_);
}
-GlobalHandles::NodeBlock::~NodeBlock() {
- delete firstNode_;
- holder_.release();
+void GlobalHandles::MakeWeak(EscargotShim::ValueWrap* lwValue,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback callback) {
+ auto isolate = EscargotShim::IsolateWrap::GetCurrent();
+ isolate->global_handles()->makeWeak(lwValue, parameter, callback);
}
-GlobalHandles::Node* GlobalHandles::NodeBlock::pushNode(Node* node) {
- if (firstNode_ == nullptr) {
- firstNode_ = node;
- } else {
- // TODO
- LWNODE_DLOG_WARN("The weak callback is registered several times.");
- if (node) {
- delete node;
- return nullptr;
- }
- }
-
- return node;
+void* GlobalHandles::ClearWeakness(EscargotShim::ValueWrap* lwValue) {
+ auto isolate = EscargotShim::IsolateWrap::GetCurrent();
+ isolate->global_handles()->clearWeakness(lwValue);
+ return nullptr;
}
-void GlobalHandles::NodeBlock::registerWeakCallback() {
- MemoryUtil::gcRegisterFinalizer(value_, [](void* self) {
- LWNODE_CALL_TRACE_GC_START();
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Call weak callback: %p", self);
+} // namespace internal
+} // namespace v8
- auto block = g_globalWeakHandler.popBlock(VAL(self));
- if (!block) {
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot invoke callback: %p", self);
- return;
- }
-
- auto curNode = block->firstNode_;
- if (curNode) {
- if (curNode->callback()) {
- void* embedderFields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
- nullptr};
- v8::WeakCallbackInfo<void> info(
- block->isolate(), curNode->parameter(), embedderFields, nullptr);
- LWNODE_CHECK_NOT_NULL(block->isolate());
- curNode->callback()(info);
- }
- // TODO: The weak callback is registered several times.(create nextNode)
- }
- LWNODE_CALL_TRACE_GC_END();
- });
-}
-
-void GlobalHandles::NodeBlock::releaseValue() {
- registerWeakCallback();
- holder_.release();
-}
+namespace EscargotShim {
-GlobalHandles::GlobalHandles(v8::Isolate* isolate) : isolate_(isolate) {
- g_globalHandlesVector.push_back(this);
-}
+GlobalHandles::GlobalHandles(IsolateWrap* isolate)
+ : v8::internal::GlobalHandles(isolate), isolate_(isolate) {}
-void GlobalHandles::Dispose() {
+void GlobalHandles::dispose() {
LWNODE_CALL_TRACE_ID(GLOBALHANDLES);
persistentValues_.clear();
- auto it = std::find(
- g_globalHandlesVector.begin(), g_globalHandlesVector.end(), this);
- if (it != g_globalHandlesVector.end()) {
- g_globalHandlesVector.erase(it);
- }
- // TODO: consider multi isolate
- if (g_globalHandlesVector.size() == 0) {
- g_globalWeakHandler.dispose();
+ for (auto gcObjectInfo : gcObjectInfos_) {
+ delete gcObjectInfo;
}
+
+ isolate_ = nullptr;
}
-void GlobalHandles::Create(ValueWrap* lwValue) {
+void GlobalHandles::create(ValueWrap* lwValue) {
auto iter = persistentValues_.find(lwValue);
if (iter == persistentValues_.end()) {
persistentValues_.emplace(lwValue, 1);
}
}
-void GlobalHandles::Destroy(ValueWrap* lwValue) {
- for (auto globalHandles : g_globalHandlesVector) {
- if (globalHandles->destroy(lwValue)) {
- return;
- }
- }
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot destroy: %p", lwValue);
-}
-
bool GlobalHandles::destroy(ValueWrap* lwValue) {
auto iter = persistentValues_.find(lwValue);
if (iter != persistentValues_.end()) {
size_t GlobalHandles::PostGarbageCollectionProcessing(
/*const v8::GCCallbackFlags gc_callback_flags*/) {
#if defined(LWNODE_ENABLE_EXPERIMENTAL)
- return g_globalWeakHandler.clearWeakValue();
-#else
- return 0;
+ clearWeakValues();
#endif
+
+ return 0;
}
-void GlobalHandles::MakeWeak(ValueWrap* lwValue,
- void* parameter,
- v8::WeakCallbackInfo<void>::Callback callback) {
- for (auto globalHandles : g_globalHandlesVector) {
- if (globalHandles->makeWeak(lwValue, parameter, callback)) {
- return;
+void GlobalHandles::releaseWeakValues() {
+ std::set<GcObjectInfo*, ObjectInfoComparator> objectsInfoPersistent;
+ std::vector<GcObjectInfo*> objectsInfoWeak;
+
+ for (auto gcObjectInfo : gcObjectInfos_) {
+ if (gcObjectInfo->isPersistent()) {
+ objectsInfoPersistent.insert(gcObjectInfo);
+ } else {
+ // TODO: check if the weak pointer is valid
+ if (gcObjectInfo->hasCallback()) {
+ void* embedderFields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
+ nullptr};
+ v8::WeakCallbackInfo<void> info(isolate_->toV8(),
+ gcObjectInfo->parameter(),
+ embedderFields,
+ nullptr);
+ gcObjectInfo->runCallback(info);
+ }
+
+ // reset callback
+ MemoryUtil::gcRegisterFinalizer(
+ gcObjectInfo->lwValue(), [](void* self, void* data) {}, nullptr);
+
+ objectsInfoWeak.push_back(gcObjectInfo);
+
+ GC_invoke_finalizers();
}
}
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot make weak value: %p", lwValue);
+
+ gcObjectInfos_.clear();
+ gcObjectInfos_.insert(objectsInfoPersistent.begin(),
+ objectsInfoPersistent.end());
+ for (auto objectInfo : objectsInfoWeak) {
+ delete objectInfo;
+ }
}
bool GlobalHandles::makeWeak(ValueWrap* lwValue,
- void* parameter,
+ void* parameter, // c++ native object
v8::WeakCallbackInfo<void>::Callback callback) {
- auto iter = persistentValues_.find(lwValue);
- if (iter == persistentValues_.end()) {
- return false;
- }
+ addWeakValue(lwValue, parameter, callback);
+
+ MemoryUtil::gcRegisterFinalizer(
+ lwValue,
+ [](void* self, void* data) {
+ LWNODE_CALL_TRACE_GC_START();
+ auto globalHandles = static_cast<GlobalHandles*>(data);
+
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES,
+ "Call weak callback: %p %p",
+ self,
+ globalHandles->isolate_);
+ if (!globalHandles->isolate_) {
+ return;
+ }
+
+ auto gcObjectInfo = globalHandles->findGcObjectInfo((ValueWrap*)self);
+ if (gcObjectInfo) {
+ if (gcObjectInfo->hasCallback()) {
+ void* embedderFields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
+ nullptr};
+ v8::WeakCallbackInfo<void> info(globalHandles->isolate_->toV8(),
+ gcObjectInfo->parameter(),
+ embedderFields,
+ nullptr);
+ LWNODE_CHECK_NOT_NULL(globalHandles->isolate_);
+ gcObjectInfo->runCallback(info);
+ }
+
+ globalHandles->removeGcObjectInfo(gcObjectInfo->lwValue());
+ }
+
+ LWNODE_CALL_TRACE_GC_END();
+ },
+ isolate_->GetCurrent()->global_handles());
- if (g_globalWeakHandler.isWeak(lwValue)) {
- return false;
+ return true;
+}
+
+bool GlobalHandles::clearWeakness(ValueWrap* lwValue) {
+ setPersistent(lwValue);
+ return true;
+}
+
+void GlobalHandles::addWeakValue(
+ ValueWrap* lwValue,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback callback) {
+ GcObjectInfo objInfo(lwValue, parameter, callback);
+ auto itr = gcObjectInfos_.find(&objInfo);
+ if (itr != gcObjectInfos_.end()) {
+ (*itr)->unsetPersistent();
+ return;
}
- auto block = std::make_unique<GlobalHandles::NodeBlock>(
- isolate_, lwValue, iter->second);
- block->pushNode(new Node(parameter, callback));
- g_globalWeakHandler.pushBlock(lwValue, std::move(block));
+ GcObjectInfo* newObjInfo = new GcObjectInfo(lwValue, parameter, callback);
+ gcObjectInfos_.insert(newObjInfo);
+}
- LWNODE_CALL_TRACE_ID(
- GLOBALHANDLES, "MakeWeak: %p(%ld)", lwValue, iter->second);
- persistentValues_.erase(iter);
- return true;
+void GlobalHandles::setPersistent(ValueWrap* lwValue) {
+ GcObjectInfo objInfo(lwValue);
+ auto itr = gcObjectInfos_.find(&objInfo);
+ if (itr != gcObjectInfos_.end()) {
+ (*itr)->setPersistent();
+ return;
+ }
+
+ GcObjectInfo* newObjInfo = new GcObjectInfo(lwValue);
+ gcObjectInfos_.insert(newObjInfo);
}
-void* GlobalHandles::ClearWeakness(ValueWrap* lwValue) {
- for (auto globalHandles : g_globalHandlesVector) {
- if (globalHandles->clearWeak(lwValue)) {
- return lwValue;
+void GlobalHandles::clearWeakValues() {
+ std::set<GcObjectInfo*, ObjectInfoComparator> objectsInfoPersistent;
+ for (auto objectInfo : gcObjectInfos_) {
+ if (objectInfo->isPersistent()) {
+ objectsInfoPersistent.insert(objectInfo);
+ } else {
+ // TODO: check validness of a weak pointer, and call finalizer
}
}
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot clear weak value: %p", lwValue);
+
+ gcObjectInfos_.clear();
+ gcObjectInfos_.insert(objectsInfoPersistent.begin(),
+ objectsInfoPersistent.end());
+}
+
+GcObjectInfo* GlobalHandles::findGcObjectInfo(ValueWrap* lwValue) {
+ GcObjectInfo objInfo(lwValue);
+ auto itr = gcObjectInfos_.find(&objInfo);
+ if (itr != gcObjectInfos_.end()) {
+ return *itr;
+ }
+
return nullptr;
}
-bool GlobalHandles::clearWeak(ValueWrap* lwValue) {
- auto block = g_globalWeakHandler.popBlock(lwValue);
- if (!block) {
- return false;
+void GlobalHandles::removeGcObjectInfo(ValueWrap* lwValue) {
+ GcObjectInfo objInfo(lwValue);
+ auto itr = gcObjectInfos_.find(&objInfo);
+ if (itr != gcObjectInfos_.end()) {
+ auto objectInfo = *itr;
+ gcObjectInfos_.erase(itr);
+ delete objectInfo;
}
- LWNODE_CHECK(persistentValues_.find(lwValue) == persistentValues_.end());
- persistentValues_.emplace(lwValue, block->usedNodes());
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "ClearWeak: %p", lwValue);
- return true;
}
-size_t GlobalHandles::handles_count() {
- return persistentValues_.size();
+size_t GlobalHandles::handles_count() const {
+ size_t count = 0;
+ for (auto gcObjectInfo : gcObjectInfos_) {
+ if (gcObjectInfo->isPersistent()) {
+ count++;
+ }
+ }
+
+ return count;
}
} // namespace EscargotShim
#pragma once
+#include <set>
+
#include <EscargotPublic.h>
+
#include "handle.h"
#include "utils/gc.h"
-namespace EscargotShim {
-
-class GlobalHandles final : public gc {
+namespace v8 {
+namespace internal {
+class GlobalHandles : public gc {
public:
- GlobalHandles(v8::Isolate* isolate);
+ GlobalHandles(Isolate* isolate) : isolate_(isolate) {}
- void Create(ValueWrap* lwValue);
- static void Destroy(ValueWrap* lwValue);
- static void MakeWeak(ValueWrap* lwValue,
+ static void MakeWeak(EscargotShim::ValueWrap* lwValue,
void* parameter,
v8::WeakCallbackInfo<void>::Callback callback);
- static void* ClearWeakness(ValueWrap* lwValue);
-
- size_t PostGarbageCollectionProcessing(
- /*const v8::GCCallbackFlags gc_callback_flags*/);
-
- bool destroy(ValueWrap* lwValue);
- bool makeWeak(ValueWrap* lwValue,
- void* parameter,
- v8::WeakCallbackInfo<void>::Callback callback);
-
- bool clearWeak(ValueWrap* lwValue);
-
- size_t handles_count();
+ // void Create(EscargotShim::ValueWrap* lwValue);
+ static void Destroy(EscargotShim::ValueWrap* lwValue);
+ static void* ClearWeakness(EscargotShim::ValueWrap* lwValue);
- void Dispose();
+ virtual size_t handles_count() const = 0;
- class Node {
- public:
- Node(void* parameter, v8::WeakCallbackInfo<void>::Callback callback);
- ~Node();
-
- Node(const Node&) = delete;
-
- void* parameter() { return parameter_; }
- v8::WeakCallbackInfo<void>::Callback callback() { return callback_; }
-
- private:
- void* parameter_{nullptr};
- v8::WeakCallbackInfo<void>::Callback callback_;
- };
+ private:
+ Isolate* const isolate_ = nullptr;
+};
- class NodeBlock {
- public:
- NodeBlock(v8::Isolate* isolate, ValueWrap* value, uint32_t count);
+} // namespace internal
+} // namespace v8
- ~NodeBlock();
+namespace EscargotShim {
- NodeBlock(const NodeBlock&) = delete;
+class GlobalWeakHandler;
- uint32_t usedNodes() { return usedNodes_; }
+class GcObjectInfo {
+ public:
+ GcObjectInfo(ValueWrap* lwValue,
+ void* parameter = nullptr,
+ v8::WeakCallbackInfo<void>::Callback callback = nullptr)
+ : lwValue_(lwValue), parameter_(parameter), callback_(callback) {}
+
+ void setPersistent() {
+ holder_.reset(lwValue_);
+ isPersistent_ = true;
+ }
+
+ void unsetPersistent() {
+ holder_.reset(nullptr);
+ isPersistent_ = false;
+ }
+
+ bool isPersistent() { return isPersistent_; }
+ ValueWrap* lwValue() const { return lwValue_; }
+ void* parameter() { return parameter_; }
+ bool hasCallback() { return callback_ != nullptr; }
+ void runCallback(v8::WeakCallbackInfo<void>& info) { callback_(info); }
- Node* firstNode() { return firstNode_; }
- void setFirstNode(Node* node) { firstNode_ = node; }
+ private:
+ ValueWrap* lwValue_ = nullptr;
+ void* parameter_ = nullptr;
+ v8::WeakCallbackInfo<void>::Callback callback_ = nullptr;
+ Escargot::PersistentRefHolder<ValueWrap> holder_;
+ bool isPersistent_ = false;
+};
- Node* pushNode(Node* node);
+class GlobalHandles final : public v8::internal::GlobalHandles {
+ public:
+ GlobalHandles(IsolateWrap* isolate);
- void registerWeakCallback();
+ size_t PostGarbageCollectionProcessing(
+ /*const v8::GCCallbackFlags gc_callback_flags*/);
- void releaseValue();
+ void create(ValueWrap* lwValue);
+ bool makeWeak(ValueWrap* lwValue,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback callback);
+ bool clearWeakness(ValueWrap* lwValue);
+ bool destroy(ValueWrap* lwValue);
+ void dispose();
- v8::Isolate* isolate() { return isolate_; }
+ void releaseWeakValues();
+ size_t handles_count() const override;
- private:
- v8::Isolate* isolate_{nullptr};
- ValueWrap* value_{nullptr};
- uint32_t usedNodes_{0};
- Node* firstNode_{nullptr};
- Escargot::PersistentRefHolder<ValueWrap> holder_;
- };
+ void addWeakValue(ValueWrap* lwValue,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback callback);
+ void setPersistent(ValueWrap* lwValue);
+ void clearWeakValues();
+ GcObjectInfo* findGcObjectInfo(ValueWrap* value);
+ void removeGcObjectInfo(ValueWrap* lwValue);
private:
GCUnorderedMap<ValueWrap*, size_t> persistentValues_;
- v8::Isolate* isolate_{nullptr};
+ IsolateWrap* isolate_{nullptr};
+ struct ObjectInfoComparator {
+ bool operator()(const GcObjectInfo* a, const GcObjectInfo* b) const {
+ return a->lwValue() < b->lwValue();
+ }
+ };
+
+ // TODO: use std::unique_ptr
+ std::set<GcObjectInfo*, ObjectInfoComparator> gcObjectInfos_;
};
+
} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2021-present Samsung Electronics 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 "global.h"
+
+namespace EscargotShim {
+std::unique_ptr<Flags> Global::s_flags = std::make_unique<Flags>();
+
+Flags* Global::flags() {
+ return s_flags.get();
+}
+} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2021-present Samsung Electronics 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "utils/logger/flags.h"
+
+namespace EscargotShim {
+class Flags;
+
+class Global {
+ public:
+ Global() {}
+
+ static Flags* flags();
+
+ private:
+ static std::unique_ptr<Flags> s_flags;
+};
+
+} // namespace EscargotShim
void PersistentWrap::DisposeGlobal(void* address) {
PersistentWrap* persistent = reinterpret_cast<PersistentWrap*>(address);
- LWNODE_CALL_TRACE_ID(
- GCHEAP, "%s", persistent->getPersistentInfoString().c_str());
+ LWNODE_CALL_TRACE_ID(GCHEAP, "%s", persistent->getPersistentInfoString());
LWNODE_DCHECK(persistent->location_ != Local);
void PersistentWrap::dispose() {
LWNODE_CALL_TRACE_ID(GCHEAP,
- "%s isFinalizerCalled: %s",
- getPersistentInfoString().c_str(),
- strBool(isFinalizerCalled));
+ "%s isFinalizerCalled: %b",
+ getPersistentInfoString(),
+ isFinalizerCalled);
if (isFinalizerCalled) {
return;
}
*/
#include "handlescope.h"
+
+#include "api/global.h"
#include "handle.h"
#include "isolate.h"
#include "utils/misc.h"
void HandleScopeWrap::clear() {
LWNODE_CALL_TRACE_ID(HDLSCOPE);
- if (Flags::isTraceCallEnabled("HDLSCOPE")) {
+ if (Global::flags()->isOn(Flag::Type::TraceCall, "HDLSCOPE")) {
std::stringstream ss;
std::vector<std::string> vector;
// using v8:tryCatch, etc. In this case, we should not do any exception
// handling.
+ bool rethrow = has_pending_exception();
+
set_scheduled_exception(value);
+ set_pending_exception(value);
- // Note: No stack data exist
- GCManagedVector<Escargot::Evaluator::StackTraceData> stackTraceData;
- SetPendingExceptionAndMessage(value, stackTraceData);
- PropagatePendingExceptionToExternalTryCatch();
+ if (PropagatePendingExceptionToExternalTryCatch()) {
+ clear_pending_exception();
+ if (!rethrow) {
+ clear_scheduled_exception();
+ }
+ }
}
void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) {
bool Isolate::has_scheduled_exception() {
LWNODE_CALL_TRACE_ID(TRYCATCH);
- return !isHole(scheduled_exception_);
+ return scheduled_exception_ != nullptr;
}
void Isolate::set_scheduled_exception(Escargot::ValueRef* exception_obj) {
void Isolate::clear_scheduled_exception() {
LWNODE_CALL_TRACE_ID(TRYCATCH);
- scheduled_exception_ = hole()->value();
+ scheduled_exception_ = nullptr;
}
Escargot::ValueRef* Isolate::pending_exception() {
if (handler->exception_) {
LWNODE_CALL_TRACE_ID(TRYCATCH,
"The previous exception has not yet been handled.");
- return true;
+ return false;
}
handler->can_continue_ = true;
// setting them to an external v8::TryCatch handler.
handler->exception_ = reinterpret_cast<void*>(pending_exception_);
handler->message_obj_ = reinterpret_cast<void*>(pending_message_obj_);
+
+ return true;
}
- return true;
+ return false;
}
-void Isolate::ReportPendingMessages() {
+void Isolate::ReportPendingMessages(bool isVerbose) {
LWNODE_CALL_TRACE_ID(TRYCATCH);
- PropagatePendingExceptionToExternalTryCatch();
-
bool should_report_exception = true;
+ auto pendingException = pending_exception();
- if (hasExternalTryCatch()) {
- should_report_exception = getExternalTryCatchOnTop()->is_verbose_;
+ if (!isVerbose) {
+ PropagatePendingExceptionToExternalTryCatch();
+
+ if (try_catch_handler() != nullptr) {
+ should_report_exception = getExternalTryCatchOnTop()->is_verbose_;
+ }
+
+ clear_pending_exception();
+
+ if (pending_exception_ == scheduled_exception_) {
+ clear_scheduled_exception();
+ }
}
// Actually report the message to all message handlers.
- if (should_report_exception) {
+ if (isVerbose || should_report_exception) {
v8::HandleScope scope(EscargotShim::IsolateWrap::toV8(this));
v8::Local<v8::Value> exception = v8::Utils::NewLocal<v8::Value>(
- EscargotShim::IsolateWrap::toV8(this), pending_exception_);
+ EscargotShim::IsolateWrap::toV8(this), pendingException);
v8::Local<v8::Message> message = v8::Exception::CreateMessage(
EscargotShim::IsolateWrap::toV8(this), exception);
}
}
+void Isolate::RestorePendingMessageFromTryCatch(v8::TryCatch* handler) {
+ LWNODE_DCHECK(handler == try_catch_handler());
+ LWNODE_DCHECK(handler->HasCaught());
+ LWNODE_DCHECK(handler->rethrow_);
+ LWNODE_DCHECK(handler->capture_message_);
+ LWNODE_DCHECK(!has_pending_exception());
+
+ set_pending_exception(VAL(*handler->Exception())->value());
+}
+
+void Isolate::handleException(EscargotShim::EvalResult& evalResult) {
+ LWNODE_DCHECK(!evalResult.isSuccessful());
+
+ auto exception = evalResult.error.get();
+
+ if (hasCallDepth()) {
+ if (exception->isObject()) {
+ ExtraDataHelper::setExtraData(
+ exception->asObject(),
+ new ExceptionObjectData(evalResult.stackTrace));
+ }
+ ScheduleThrow(evalResult.error.get());
+ } else {
+ SetPendingExceptionAndMessage(exception, evalResult.stackTrace);
+ ReportPendingMessages();
+ }
+}
+
void Isolate::RunPromiseHook(PromiseHookType type,
Escargot::PromiseObjectRef* promise,
Escargot::ValueRef* parent) {
v8::Utils::ToLocal<Value>(parent));
}
+void Isolate::SetPromiseRejectCallback(v8::PromiseRejectCallback callback) {
+ promise_reject_callback_ = callback;
+
+ auto fn = [](ExecutionStateRef* state,
+ Escargot::PromiseObjectRef* promise,
+ Escargot::ValueRef* value,
+ Escargot::VMInstanceRef::PromiseRejectEvent event) {
+ IsolateWrap::GetCurrent()->ReportPromiseReject(promise, value, event);
+ };
+
+ IsolateWrap::fromV8(this)->vmInstance()->registerPromiseRejectCallback(fn);
+}
+
+void Isolate::ReportPromiseReject(
+ Escargot::PromiseObjectRef* promise,
+ Escargot::ValueRef* value,
+ Escargot::VMInstanceRef::PromiseRejectEvent event) {
+ PromiseRejectMessage v8Message(v8::Utils::ToLocal<Promise>(promise),
+ static_cast<v8::PromiseRejectEvent>(event),
+ v8::Utils::ToLocal<Value>(value));
+#ifdef LWNODE_ENABLE_EXPERIMENTAL_PROMISE
+ if (promise_reject_callback_ && !promise->hasRejectHandlers() &&
+ event == Escargot::VMInstanceRef::PromiseRejectEvent::
+ PromiseRejectWithNoHandler) {
+ promise_reject_callback_(v8Message);
+ }
+#else
+ LWNODE_UNIMPLEMENT;
+#endif
+}
+
+ValueRef* Isolate::RunPrepareStackTraceCallback(ExecutionStateRef* state,
+ ContextWrap* lwContext,
+ ValueRef* error,
+ ArrayObjectRef* sites) {
+ if (prepare_stack_trace_callback_) {
+ LWNODE_CALL_TRACE_ID_LOG(STACKTRACE,
+ "RunPrepareStackTraceCallback: %p",
+ PrepareStackTraceCallback());
+
+ auto v8Isolate = lwContext->GetIsolate()->toV8();
+ v8::MaybeLocal<v8::Value> maybyResult = prepare_stack_trace_callback_(
+ v8::Utils::NewLocal<Context>(v8Isolate, lwContext),
+ v8::Utils::NewLocal<Value>(v8Isolate, error),
+ v8::Utils::NewLocal<Array>(v8Isolate, sites));
+
+ if (!maybyResult.IsEmpty()) {
+ Local<Value> v8Result;
+ if (maybyResult.ToLocal(&v8Result)) {
+ return CVAL(*v8Result)->value();
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+bool Isolate::hasCallDepth() {
+ return callDepth() > 0;
+}
+
+bool Isolate::sholdReportPendingMessage(bool isVerbose) {
+ if (has_pending_exception() && (callDepth() == 0 || isVerbose)) {
+ return true;
+ }
+ return false;
+}
+
} // namespace internal
} // namespace v8
IsolateWrap::IsolateWrap() {
LWNODE_CALL_TRACE_ID(ISOWRAP, "malc: %p", this);
- globalHandles_ = new GlobalHandles(toV8());
+ global_handles_ = new GlobalHandles(this);
privateValuesSymbol_ = PersistentRefHolder<SymbolRef>(
SymbolRef::create(StringRef::createFromUTF8(PRIVATE_VALUES.data(),
IsolateWrap::~IsolateWrap() {
LWNODE_CALL_TRACE_ID(ISOWRAP, "free: %p", this);
- globalHandles_->Dispose();
- LWNODE_CALL_TRACE_GC_START();
- // NOTE: Called when this IsolateWrap is deallocated by gc
- LWNODE_CALL_TRACE_GC_END();
}
IsolateWrap* IsolateWrap::New() {
LWNODE_CALL_TRACE_GC_START();
// NOTE: check unlock_gc_release(); is needed (and where)
// unlock_gc_release();
+
+ global_handles()->dispose();
+
LWNODE_CALL_TRACE_GC_END();
}
vmInstance_->registerErrorCreationCallback(
[](ExecutionStateRef* state, ErrorObjectRef* error) {
- ExceptionHelper::setStackPropertyIfNotExist(state, error);
+ ExceptionHelper::addStackPropertyCallback(state, error);
});
InitializeGlobalSlots();
- scheduled_exception_ = hole()->value();
-
// Register lwnode internal promise hook to create the internal field.
LWNODE_ONCE(LWNODE_DLOG_INFO("v8::Promise::kEmbedderFieldCount: %d",
v8::Promise::kEmbedderFieldCount));
clear_pending_message_obj();
}
-void IsolateWrap::CollectGarbage() {
- globalHandles_->PostGarbageCollectionProcessing();
+void IsolateWrap::CollectGarbage(GarbageCollectionReason reason) {
+ if (reason == GarbageCollectionReason::kTesting) {
+ global_handles_->releaseWeakValues();
+ } else {
+ global_handles_->PostGarbageCollectionProcessing();
+ }
}
void IsolateWrap::SetPendingExceptionAndMessage(
LWNODE_CALL_TRACE_ID(TRYCATCH);
if (exception->isObject()) {
- ExtraDataHelper::setExtraData(exception->asObject(),
- new ExceptionObjectData(stackTraceData));
+ auto extraData = ExtraDataHelper::getExtraData(exception->asObject());
+
+ if (extraData) {
+ LWNODE_CHECK(extraData->isExceptionObjectData());
+ // NOTE: Exception has created in the `else` below.
+ LWNODE_LOG_WARN("esException already contains an extraData: %p\n",
+ extraData);
+ } else {
+ ExtraDataHelper::setExtraData(exception->asObject(),
+ new ExceptionObjectData(stackTraceData));
+ }
}
set_pending_exception(exception);
clear_scheduled_exception();
if (isCatchableByJavascript(state)) {
+ ClearPendingExceptionAndMessage();
if (hasExternalTryCatch()) {
- ClearPendingExceptionAndMessage();
SetTerminationOnExternalTryCatch();
}
state->throwException(exception);
+ return;
} else if (!hasExternalTryCatch()) {
- state->throwException(exception);
+ ReportPendingMessages();
+ return;
+ }
+
+ if (has_pending_exception()) {
+ ReportPendingMessages();
}
}
return globalSlot_[internal::Internals::kUndefinedValueRootIndex];
}
-ValueWrap* IsolateWrap::hole() {
- return globalSlot_[internal::Internals::kTheHoleValueRootIndex];
-}
-
ValueWrap* IsolateWrap::null() {
return globalSlot_[internal::Internals::kNullValueRootIndex];
}
return globalSlot_[internal::Internals::kDefaultReturnValueRootIndex];
}
-bool IsolateWrap::isHole(const ValueWrap* wrap) {
- return globalSlot_[internal::Internals::kTheHoleValueRootIndex] == wrap;
-}
-
-bool IsolateWrap::isHole(const Escargot::ValueRef* ref) {
- return globalSlot_[internal::Internals::kTheHoleValueRootIndex]->value() ==
- ref;
-}
-
void IsolateWrap::onFatalError(const char* location, const char* message) {
if (fatal_error_callback_) {
fatal_error_callback_(location, message);
#pragma once
+#include "api/es-helper.h"
#include "arraybuffer-allocator.h"
#include "engine.h"
#include "execution/v8threads.h"
bool IsExecutionTerminating();
void CancelScheduledExceptionFromTryCatch(v8::TryCatch* that);
void ThrowException(Escargot::ValueRef* value);
+ void RestorePendingMessageFromTryCatch(v8::TryCatch* handler);
TryCatch* try_catch_handler();
virtual void SetPendingExceptionAndMessage(
Escargot::ValueRef* exception,
GCManagedVector<Escargot::Evaluator::StackTraceData>& stackTraceData) = 0;
- bool PropagatePendingExceptionToExternalTryCatch();
- void ReportPendingMessages();
+ virtual bool PropagatePendingExceptionToExternalTryCatch();
+ virtual void ReportPendingMessages(bool isVerbose = false);
- virtual EscargotShim::ValueWrap* hole() = 0;
- virtual bool isHole(const EscargotShim::ValueWrap* wrap) = 0;
- virtual bool isHole(const Escargot::ValueRef* ref) = 0;
-
- void SetPromiseRejectCallback(v8::PromiseRejectCallback callback) {
- promise_reject_callback_ = callback;
- }
+ void handleException(EscargotShim::EvalResult& evalResult);
void RunPromiseHook(PromiseHookType type,
Escargot::PromiseObjectRef* promise,
Escargot::ValueRef* parent);
+ virtual void SetPromiseRejectCallback(v8::PromiseRejectCallback callback);
+ virtual void ReportPromiseReject(
+ Escargot::PromiseObjectRef* promise,
+ Escargot::ValueRef* value,
+ Escargot::VMInstanceRef::PromiseRejectEvent event);
+
void SetFatalErrorHandler(v8::FatalErrorCallback callback) {
fatal_error_callback_ = callback;
}
- void SetPrepareStackTraceCallback(v8::PrepareStackTraceCallback callback) {
+ virtual void SetPrepareStackTraceCallback(
+ v8::PrepareStackTraceCallback callback) {
prepare_stack_trace_callback_ = callback;
}
- bool HasPrepareStackTraceCallback() const {
+ virtual bool HasPrepareStackTraceCallback() const {
return prepare_stack_trace_callback_ != nullptr;
}
return prepare_stack_trace_callback_;
}
+ virtual ValueRef* RunPrepareStackTraceCallback(
+ ExecutionStateRef* state,
+ EscargotShim::ContextWrap* lwContext,
+ ValueRef* error,
+ ArrayObjectRef* sites);
+
void SetAbortOnUncaughtExceptionCallback(
v8::Isolate::AbortOnUncaughtExceptionCallback callback) {
abort_on_uncaught_exception_callback_ = callback;
}
+ v8::Isolate::AbortOnUncaughtExceptionCallback
+ abortOnUncaughtExceptionCallback() {
+ return abort_on_uncaught_exception_callback_;
+ }
+
void AddMessageListenerWithErrorLevel(v8::MessageCallback callback) {
LWNODE_DCHECK_NULL(message_callback_);
message_callback_ = callback;
}
+ void increaseCallDepth() { callDepth_++; }
+ void decreaseCallDepth() { callDepth_--; }
+ size_t callDepth() { return callDepth_; }
+ bool hasCallDepth();
+ bool sholdReportPendingMessage(bool isVerbose);
+
+ EscargotShim::GlobalHandles* global_handles() { return global_handles_; }
+
protected:
void set_pending_exception(Escargot::ValueRef* exception_obj);
void set_pending_message_obj(Escargot::ValueRef* message_obj);
v8::Isolate::AbortOnUncaughtExceptionCallback
abort_on_uncaught_exception_callback_{nullptr};
+ EscargotShim::GlobalHandles* global_handles_ = nullptr;
+
private:
v8::TryCatch* try_catch_handler_{nullptr};
Escargot::ValueRef* pending_exception_{nullptr};
Escargot::ValueRef* pending_message_obj_{nullptr};
+ size_t callDepth_ = 0;
};
} // namespace internal
} // namespace v8
ValueWrap** getGlobal(const int idex);
ValueWrap* undefined_value();
- ValueWrap* hole() override;
ValueWrap* null();
ValueWrap* trueValue();
ValueWrap* falseValue();
ValueWrap* emptyString();
ValueWrap* defaultReturnValue();
- bool isHole(const ValueWrap* wrap) override;
- bool isHole(const Escargot::ValueRef* ref) override;
+ bool isDefaultReturnValue(ValueWrap* value);
SymbolRef* createApiSymbol(StringRef* name);
SymbolRef* getApiSymbol(StringRef* name);
SymbolRef* createApiPrivateSymbol(StringRef* name);
SymbolRef* getApiPrivateSymbol(StringRef* name);
- GlobalHandles* globalHandles() { return globalHandles_; }
-
- void CollectGarbage();
+ void CollectGarbage(
+ GarbageCollectionReason reason = GarbageCollectionReason::kRuntime);
void SetPendingExceptionAndMessage(
ValueRef* exception,
ThreadManager* thread_manager() { return threadManager_; }
+ void PerformMicrotaskCheckpoint() {
+ v8::MicrotasksScope::PerformCheckpoint(toV8(this));
+ }
+
private:
IsolateWrap();
VMInstanceRef* vmInstance_ = nullptr;
- GlobalHandles* globalHandles_ = nullptr;
-
PersistentRefHolder<IsolateWrap> release_lock_;
- ValueWrap* globalSlot_[internal::Internals::kRootIndexSize];
+ ValueWrap* globalSlot_[internal::Internals::kRootIndexSize]{};
ThreadManager* threadManager_ = nullptr;
};
--- /dev/null
+/*
+ * Copyright (c) 2021-present Samsung Electronics 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.
+ */
+
+#if defined(LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION)
+
+#include <cmath>
+
+#include "base.h"
+#include "context.h"
+#include "es-helper.h"
+#include "isolate.h"
+#include "serializer.h"
+
+using namespace Escargot;
+using namespace v8;
+
+namespace EscargotShim {
+
+// base on v8/src/objects/value-serializer.cc
+enum class SerializationTag : uint8_t {
+ // version:uint32_t (if at beginning of data, sets version > 0)
+ kVersion = 0xFF,
+ // ignore
+ kPadding = '\0',
+ // refTableSize:uint32_t (previously used for sanity checks; safe to ignore)
+ kVerifyObjectCount = '?',
+ // Oddballs (no data).
+ kTheHole = '-',
+ kUndefined = '_',
+ kNull = '0',
+ kTrue = 'T',
+ kFalse = 'F',
+ // Number represented as 32-bit integer, ZigZag-encoded
+ // (like sint32 in protobuf)
+ kInt32 = 'I',
+ // Number represented as 32-bit unsigned integer, varint-encoded
+ // (like uint32 in protobuf)
+ kUint32 = 'U',
+ // Number represented as a 64-bit double.
+ // Host byte order is used (N.B. this makes the format non-portable).
+ kDouble = 'N',
+ // BigInt. Bitfield:uint32_t, then raw digits storage.
+ kBigInt = 'Z',
+ // byteLength:uint32_t, then raw data
+ kUtf8String = 'S',
+ kOneByteString = '"',
+ kTwoByteString = 'c',
+ // Reference to a serialized object. objectID:uint32_t
+ kObjectReference = '^',
+ // Beginning of a JS object.
+ kBeginJSObject = 'o',
+ // End of a JS object. numProperties:uint32_t
+ kEndJSObject = '{',
+ // Beginning of a sparse JS array. length:uint32_t
+ // Elements and properties are written as key/value pairs, like objects.
+ kBeginSparseJSArray = 'a',
+ // End of a sparse JS array. numProperties:uint32_t length:uint32_t
+ kEndSparseJSArray = '@',
+ // Beginning of a dense JS array. length:uint32_t
+ // |length| elements, followed by properties as key/value pairs
+ kBeginDenseJSArray = 'A',
+ // End of a dense JS array. numProperties:uint32_t length:uint32_t
+ kEndDenseJSArray = '$',
+ // Date. millisSinceEpoch:double
+ kDate = 'D',
+ // Boolean object. No data.
+ kTrueObject = 'y',
+ kFalseObject = 'x',
+ // Number object. value:double
+ kNumberObject = 'n',
+ // BigInt object. Bitfield:uint32_t, then raw digits storage.
+ kBigIntObject = 'z',
+ // String object, UTF-8 encoding. byteLength:uint32_t, then raw data.
+ kStringObject = 's',
+ // Regular expression, UTF-8 encoding. byteLength:uint32_t, raw data,
+ // flags:uint32_t.
+ kRegExp = 'R',
+ // Beginning of a JS map.
+ kBeginJSMap = ';',
+ // End of a JS map. length:uint32_t.
+ kEndJSMap = ':',
+ // Beginning of a JS set.
+ kBeginJSSet = '\'',
+ // End of a JS set. length:uint32_t.
+ kEndJSSet = ',',
+ // Array buffer. byteLength:uint32_t, then raw data.
+ kArrayBuffer = 'B',
+ // Array buffer (transferred). transferID:uint32_t
+ kArrayBufferTransfer = 't',
+ // View into an array buffer.
+ // subtag:ArrayBufferViewTag, byteOffset:uint32_t, byteLength:uint32_t
+ // For typed arrays, byteOffset and byteLength must be divisible by the size
+ // of the element.
+ // Note: kArrayBufferView is special, and should have an ArrayBuffer (or an
+ // ObjectReference to one) serialized just before it. This is a quirk arising
+ // from the previous stack-based implementation.
+ kArrayBufferView = 'V',
+ // Shared array buffer. transferID:uint32_t
+ kSharedArrayBuffer = 'u',
+ // A wasm module object transfer. next value is its index.
+ kWasmModuleTransfer = 'w',
+ // The delegate is responsible for processing all following data.
+ // This "escapes" to whatever wire format the delegate chooses.
+ kHostObject = '\\',
+ // A transferred WebAssembly.Memory object. maximumPages:int32_t, then by
+ // SharedArrayBuffer tag and its data.
+ kWasmMemoryTransfer = 'm',
+ // A list of (subtag: ErrorTag, [subtag dependent data]). See ErrorTag for
+ // details.
+ kError = 'r',
+
+ // The following tags are reserved because they were in use in Chromium before
+ // the kHostObject tag was introduced in format version 13, at
+ // v8 refs/heads/master@{#43466}
+ // chromium/src refs/heads/master@{#453568}
+ //
+ // They must not be reused without a version check to prevent old values from
+ // starting to deserialize incorrectly. For simplicity, it's recommended to
+ // avoid them altogether.
+ //
+ // This is the set of tags that existed in SerializationTag.h at that time and
+ // still exist at the time of this writing (i.e., excluding those that were
+ // removed on the Chromium side because there should be no real user data
+ // containing them).
+ //
+ // It might be possible to also free up other tags which were never persisted
+ // (e.g. because they were used only for transfer) in the future.
+ kLegacyReservedMessagePort = 'M',
+ kLegacyReservedBlob = 'b',
+ kLegacyReservedBlobIndex = 'i',
+ kLegacyReservedFile = 'f',
+ kLegacyReservedFileIndex = 'e',
+ kLegacyReservedDOMFileSystem = 'd',
+ kLegacyReservedFileList = 'l',
+ kLegacyReservedFileListIndex = 'L',
+ kLegacyReservedImageData = '#',
+ kLegacyReservedImageBitmap = 'g',
+ kLegacyReservedImageBitmapTransfer = 'G',
+ kLegacyReservedOffscreenCanvas = 'H',
+ kLegacyReservedCryptoKey = 'K',
+ kLegacyReservedRTCCertificate = 'k',
+};
+
+enum class ArrayBufferViewTag : uint8_t {
+ kInt8Array = 'b',
+ kUint8Array = 'B',
+ kUint8ClampedArray = 'C',
+ kInt16Array = 'w',
+ kUint16Array = 'W',
+ kInt32Array = 'd',
+ kUint32Array = 'D',
+ kFloat32Array = 'f',
+ kFloat64Array = 'F',
+ kBigInt64Array = 'q',
+ kBigUint64Array = 'Q',
+ kDataView = '?',
+};
+
+ValueSerializer::ValueSerializer(IsolateWrap* lwIsolate,
+ v8::ValueSerializer::Delegate* delegate)
+ : lwIsolate_(lwIsolate), delegate_(delegate) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Create serializer");
+}
+
+void ValueSerializer::WriteHeader() {}
+
+bool ValueSerializer::WriteValue(ValueRef* value) {
+ if (value->isUndefined()) {
+ WriteTag(SerializationTag::kUndefined);
+ } else if (value->isNull()) {
+ WriteTag(SerializationTag::kNull);
+ } else if (value->isBoolean()) {
+ return WriteBoolean(value->asBoolean());
+ } else if (value->isUInt32()) {
+ return WriteUint32(value->asUInt32());
+ } else if (value->isInt32()) {
+ return WriteInt32(value->asInt32());
+ } else if (value->isNumber()) {
+ return WriteNumber(value->asNumber());
+ } else if (value->isBigInt()) {
+ LWNODE_UNIMPLEMENT;
+ } else if (value->isArrayBufferObject()) {
+ auto arrayBuffer = value->asArrayBufferObject();
+ return WriteArrayBuffer(arrayBuffer->byteLength(),
+ arrayBuffer->rawBuffer());
+ } else if (value->isDataViewObject()) {
+ LWNODE_UNIMPLEMENT;
+ } else if (value->isString()) {
+ WriteString(value->asString());
+ } else if (value->isTypedArrayObject()) {
+ return WriteTypedArrayObject(value->asArrayBufferView());
+ } else if (value->isArrayObject()) {
+ return WriteJsArray(value->asArrayObject());
+ } else if (value->isSymbol()) {
+ LWNODE_UNIMPLEMENT;
+ } else if (value->isFunctionObject()) {
+ LWNODE_UNIMPLEMENT;
+ } else if (value->isObject()) {
+ if (!WriteObject(value->asObject())) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot write object");
+ return false;
+ }
+ } else {
+ LWNODE_UNIMPLEMENT;
+ return false;
+ }
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteUint32(uint32_t value) {
+ WriteTag(SerializationTag::kUint32);
+ WriteVarint<uint32_t>(value);
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteInt32(int32_t value) {
+ WriteTag(SerializationTag::kInt32);
+ WriteZigZag<int32_t>(value);
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteNumber(double value) {
+ WriteTag(SerializationTag::kDouble);
+ WriteRawBytes(&value, sizeof(value));
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteBoolean(bool value) {
+ if (value) {
+ WriteTag(SerializationTag::kTrue);
+ } else {
+ WriteTag(SerializationTag::kFalse);
+ }
+ return ThrowIfOutOfMemory();
+}
+
+// base on v8
+void ValueSerializer::WriteTag(SerializationTag tag) {
+ uint8_t raw_tag = static_cast<uint8_t>(tag);
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER_TAG, "Write Tag %c", (char)tag);
+ WriteRawBytes(&raw_tag, sizeof(raw_tag));
+}
+
+void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
+ uint8_t* dest = ReserveRawBytes(length);
+ if (dest && length > 0) {
+ memcpy(dest, source, length);
+ }
+}
+
+// base on v8
+uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
+ size_t old_size = buffer_.size;
+ size_t new_size = old_size + bytes;
+ if (LWNODE_UNLIKELY(new_size > buffer_.capacity)) {
+ if (!ExpandBuffer(new_size)) {
+ return nullptr;
+ }
+ }
+ buffer_.size = new_size;
+ return &buffer_.data[old_size];
+}
+
+// base on v8
+template <typename T>
+void ValueSerializer::WriteVarint(T value) {
+ // Writes an unsigned integer as a base-128 varint.
+ // The number is written, 7 bits at a time, from the least significant to the
+ // most significant 7 bits. Each byte, except the last, has the MSB set.
+ // See also https://developers.google.com/protocol-buffers/docs/encoding
+ static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
+ "Only unsigned integer types can be written as varints.");
+ uint8_t stack_buffer[sizeof(T) * 8 / 7 + 1];
+ uint8_t* next_byte = &stack_buffer[0];
+ do {
+ *next_byte = (value & 0x7F) | 0x80;
+ next_byte++;
+ value >>= 7;
+ } while (value);
+ *(next_byte - 1) &= 0x7F;
+ WriteRawBytes(stack_buffer, next_byte - stack_buffer);
+}
+
+template <typename T>
+void ValueSerializer::WriteZigZag(T value) {
+ // Writes a signed integer as a varint using ZigZag encoding (i.e. 0 is
+ // encoded as 0, -1 as 1, 1 as 2, -2 as 3, and so on).
+ // See also https://developers.google.com/protocol-buffers/docs/encoding
+ // Note that this implementation relies on the right shift being arithmetic.
+ static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
+ "Only signed integer types can be written as zigzag.");
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ WriteVarint((static_cast<UnsignedT>(value) << 1) ^
+ (value >> (8 * sizeof(T) - 1)));
+}
+
+void ValueSerializer::WriteString(StringRef* string) {
+ auto bufferData = string->stringBufferAccessData();
+ if (bufferData.has8BitContent) {
+ WriteTag(SerializationTag::kOneByteString);
+ WriteVarint<uint32_t>(bufferData.length);
+ WriteRawBytes(bufferData.buffer, bufferData.length * sizeof(uint8_t));
+ } else {
+ WriteTag(SerializationTag::kTwoByteString);
+ WriteVarint<uint32_t>(bufferData.length);
+ WriteRawBytes(bufferData.buffer, bufferData.length * sizeof(uint16_t));
+ }
+}
+
+bool ValueSerializer::WriteHostObject(ObjectRef* object) {
+ WriteTag(SerializationTag::kHostObject);
+ if (!delegate_) {
+ // @note deps/v8/src/objects/value-serializer.cc(1006) throws an Error
+ LWNODE_CHECK(false);
+ return false;
+ }
+
+ v8::Isolate* v8_isolate = lwIsolate_->toV8();
+ Maybe<bool> result =
+ delegate_->WriteHostObject(v8_isolate, Utils::ToLocal<Object>(object));
+
+ LWNODE_CHECK(!result.IsNothing());
+ LWNODE_CHECK(result.ToChecked());
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteObject(ObjectRef* object) {
+ if (ObjectRefHelper::getInternalFieldCount(object) > 0) {
+ return WriteHostObject(object);
+ }
+
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+ uint32_t propertiesWritten = 0;
+ WriteTag(SerializationTag::kBeginJSObject);
+ Escargot::ValueVectorRef* keys = nullptr;
+ EvalResult r = Evaluator::execute(
+ esContext,
+ [](ExecutionStateRef* state,
+ ObjectRef* object,
+ Escargot::ValueVectorRef** keys) -> ValueRef* {
+ *keys = object->ownPropertyKeys(state);
+ return ValueRef::createUndefined();
+ },
+ object,
+ &keys);
+ LWNODE_CHECK(r.isSuccessful());
+
+ for (size_t i = 0; i < keys->size(); i++) {
+ auto propValueResult =
+ ObjectRefHelper::getProperty(esContext, object, keys->at(i));
+ LWNODE_CHECK(propValueResult.isSuccessful());
+ bool success =
+ WriteValue(keys->at(i)) && WriteValue(propValueResult.result);
+ if (!success) {
+ return false;
+ }
+ propertiesWritten++;
+ }
+
+ WriteTag(SerializationTag::kEndJSObject);
+ WriteVarint<uint32_t>(propertiesWritten);
+
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteJsArray(ArrayObjectRef* array) {
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+
+ uint32_t length = ArrayObjectRefHelper::length(esContext, array);
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "WriteJsArray start: %u", length);
+
+ WriteTag(SerializationTag::kBeginDenseJSArray);
+ WriteVarint<uint32_t>(length);
+
+ for (uint32_t i = 0; i < length; i++) {
+ auto esValue = ArrayObjectRefHelper::get(esContext, array, i);
+ if (!WriteValue(esValue)) {
+ return false;
+ }
+ }
+
+ WriteTag(SerializationTag::kEndDenseJSArray);
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "WriteJsArray end");
+ return true;
+}
+
+bool ValueSerializer::WriteArrayBuffer(size_t length, uint8_t* bytes) {
+ WriteTag(SerializationTag::kArrayBuffer);
+ WriteVarint<uint32_t>(length);
+ WriteRawBytes(bytes, length);
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteArrayBufferView(
+ ArrayBufferViewRef* arrayBufferView) {
+ WriteTag(SerializationTag::kArrayBufferView);
+
+ ArrayBufferViewTag typeTag = ArrayBufferViewTag::kInt8Array;
+
+ if (arrayBufferView->isDataViewObject()) {
+ typeTag = ArrayBufferViewTag::kDataView;
+ }
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
+ else if (arrayBufferView->is##Type##ArrayObject()) { \
+ typeTag = ArrayBufferViewTag::k##Type##Array; \
+ }
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ else {
+ LWNODE_DLOG_ERROR("Serializer: Invalid buffer type");
+ return false;
+ }
+
+ WriteVarint(static_cast<uint8_t>(typeTag));
+ WriteVarint(static_cast<uint32_t>(arrayBufferView->byteOffset()));
+ WriteVarint(static_cast<uint32_t>(arrayBufferView->arrayLength()));
+ return ThrowIfOutOfMemory();
+}
+
+bool ValueSerializer::WriteTypedArrayObject(
+ Escargot::ArrayBufferViewRef* bufferView) {
+ auto result =
+ WriteArrayBuffer(bufferView->byteLength(), bufferView->rawBuffer());
+ return result && WriteArrayBufferView(bufferView);
+}
+
+// base on v8
+bool ValueSerializer::ExpandBuffer(size_t required_capacity) {
+ LWNODE_CHECK(required_capacity > buffer_.capacity);
+ size_t requested_capacity =
+ std::max(required_capacity, buffer_.capacity * 2) + 64;
+ size_t provided_capacity = 0;
+ void* new_buffer = nullptr;
+ if (delegate_) {
+ new_buffer = delegate_->ReallocateBufferMemory(
+ buffer_.data, requested_capacity, &provided_capacity);
+ } else {
+ new_buffer = realloc(buffer_.data, requested_capacity);
+ provided_capacity = requested_capacity;
+ }
+ if (new_buffer) {
+ LWNODE_CHECK(provided_capacity >= requested_capacity);
+ buffer_.data = reinterpret_cast<uint8_t*>(new_buffer);
+ buffer_.capacity = provided_capacity;
+ return true;
+ } else {
+ out_of_memory_ = true;
+ return false;
+ }
+}
+
+bool ValueSerializer::ThrowIfOutOfMemory() {
+ if (out_of_memory_) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "out of memory");
+ ThrowDataCloneError();
+ return false;
+ }
+ return true;
+}
+
+void ValueSerializer::ThrowDataCloneError() {
+ if (delegate_) {
+ delegate_->ThrowDataCloneError(Utils::NewLocal<String>(
+ lwIsolate_->toV8(),
+ ErrorMessage::createErrorStringRef(
+ ErrorMessageType::kDataCloneErrorOutOfMemory)));
+ } else {
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+ lwIsolate_->ScheduleThrow(ExceptionHelper::createErrorObject(
+ esContext, ErrorMessageType::kDataCloneErrorOutOfMemory));
+ }
+
+ if (lwIsolate_->sholdReportPendingMessage(false)) {
+ lwIsolate_->ReportPendingMessages();
+ }
+}
+
+std::pair<uint8_t*, size_t> ValueSerializer::Release() {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "buffer size: %zu", buffer_.size);
+
+ uint8_t* buffer = nullptr;
+ auto size = buffer_.size;
+ if (size > 0) {
+ if (delegate_) {
+ size_t allocatedSize = 0;
+ buffer = static_cast<uint8_t*>(
+ delegate_->ReallocateBufferMemory(buffer, size, &allocatedSize));
+ LWNODE_CHECK(size == allocatedSize);
+ } else {
+ buffer = static_cast<uint8_t*>(malloc(size * sizeof(uint8_t)));
+ }
+
+ memcpy(buffer, buffer_.data, size);
+ }
+
+ free(buffer_.data);
+ buffer_.size = 0;
+
+ return std::make_pair(buffer, size);
+}
+
+ValueDeserializer::ValueDeserializer(IsolateWrap* lwIsolate,
+ v8::ValueDeserializer::Delegate* delegate,
+ const uint8_t* data,
+ const size_t size)
+ : lwIsolate_(lwIsolate), delegate_(delegate), buffer_(data, size) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Create deserializer (%zu)", size);
+}
+
+OptionalRef<ValueRef> ValueDeserializer::ReadValue() {
+ SerializationTag tag;
+ if (!ReadTag(tag)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read tag");
+ return OptionalRef<ValueRef>();
+ }
+
+ if (tag == SerializationTag::kNull) {
+ return OptionalRef<ValueRef>(ValueRef::createNull());
+ } else if (tag == SerializationTag::kUndefined) {
+ return OptionalRef<ValueRef>(ValueRef::createUndefined());
+ } else if (tag == SerializationTag::kTrue) {
+ return OptionalRef<ValueRef>(ValueRef::create(true));
+ } else if (tag == SerializationTag::kFalse) {
+ return OptionalRef<ValueRef>(ValueRef::create(false));
+ } else if (tag == SerializationTag::kDouble) {
+ double number = .0;
+ if (!ReadDouble(number)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read double value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(ValueRef::create(number));
+ } else if (tag == SerializationTag::kOneByteString) {
+ StringRef* string = nullptr;
+ if (!ReadOneByteString(string)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read one byte string value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(string);
+ } else if (tag == SerializationTag::kTwoByteString) {
+ StringRef* string = nullptr;
+ if (!ReadTwoByteString(string)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read two byte string value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(string);
+ } else if (tag == SerializationTag::kUint32) {
+ uint32_t value = 0;
+ if (!ReadVarint<uint32_t>(value)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read uint32 value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(ValueRef::create(value));
+ } else if (tag == SerializationTag::kInt32) {
+ int32_t value = 0;
+ if (!ReadZigZag<int32_t>(value)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read int32 value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(ValueRef::create(value));
+ } else if (tag == SerializationTag::kBeginJSObject) {
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+ auto object = ObjectRefHelper::create(esContext);
+ if (!ReadObject(object)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read object value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(object);
+ } else if (tag == SerializationTag::kHostObject) {
+ ObjectRef* esObject = nullptr;
+ if (!ReadHostObject(esObject)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read host object value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(esObject);
+ } else if (tag == SerializationTag::kBeginDenseJSArray) {
+ ArrayObjectRef* array = nullptr;
+ if (!ReadJsArray(array)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read array value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(array);
+ } else if (tag == SerializationTag::kArrayBuffer) {
+ ValueRef* arrayBuffer = nullptr;
+ if (!ReadJsArrayBuffer(arrayBuffer)) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read array buffer value");
+ return OptionalRef<ValueRef>();
+ }
+ return OptionalRef<ValueRef>(arrayBuffer);
+ } else {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Fail: %c", (char)tag);
+ LWNODE_UNIMPLEMENT;
+ }
+
+ return OptionalRef<ValueRef>();
+}
+
+// base on v8
+bool ValueDeserializer::ReadTag(SerializationTag& tag) {
+ do {
+ if (buffer_.isOverflow()) {
+ return false;
+ }
+ tag = static_cast<SerializationTag>(buffer_.currentPositionData());
+ buffer_.position++;
+ } while (tag == SerializationTag::kPadding);
+
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER_TAG, "Read Tag %c", (char)tag);
+ return true;
+}
+
+bool ValueDeserializer::CheckTag(SerializationTag check) {
+ SerializationTag tag;
+ size_t curPosition = buffer_.position;
+ do {
+ if (buffer_.isOverflow()) {
+ return false;
+ }
+ tag = static_cast<SerializationTag>(buffer_.data[curPosition]);
+ curPosition++;
+ } while (tag == SerializationTag::kPadding);
+
+ return tag == check;
+}
+
+bool ValueDeserializer::ReadUint32(uint32_t*& value) {
+ if (!CheckTag(SerializationTag::kUint32)) {
+ return false;
+ }
+
+ OptionalRef<ValueRef> valueRef = ReadValue();
+ if (valueRef) {
+ *value = valueRef->asUInt32();
+ }
+ return valueRef;
+}
+
+template <typename T>
+bool ValueDeserializer::ReadVarint(T& value) {
+ // Reads an unsigned integer as a base-128 varint.
+ // The number is written, 7 bits at a time, from the least significant to the
+ // most significant 7 bits. Each byte, except the last, has the MSB set.
+ // If the varint is larger than T, any more significant bits are discarded.
+ // See also https://developers.google.com/protocol-buffers/docs/encoding
+ static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
+ "Only unsigned integer types can be read as varints.");
+ value = 0;
+ unsigned shift = 0;
+ bool has_another_byte;
+ do {
+ if (buffer_.isOverflow()) {
+ return false;
+ }
+ uint8_t byte = buffer_.currentPositionData();
+ if (V8_LIKELY(shift < sizeof(T) * 8)) {
+ value |= static_cast<T>(byte & 0x7F) << shift;
+ shift += 7;
+ }
+ has_another_byte = byte & 0x80;
+ buffer_.position++;
+ } while (has_another_byte);
+ return true;
+}
+
+template <typename T>
+bool ValueDeserializer::ReadZigZag(T& value) {
+ // Writes a signed integer as a varint using ZigZag encoding (i.e. 0 is
+ // encoded as 0, -1 as 1, 1 as 2, -2 as 3, and so on).
+ // See also https://developers.google.com/protocol-buffers/docs/encoding
+ static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
+ "Only signed integer types can be read as zigzag.");
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ UnsignedT unsigned_value;
+ if (!ReadVarint<UnsignedT>(unsigned_value)) {
+ return false;
+ }
+ value = static_cast<T>((unsigned_value >> 1) ^
+ -static_cast<T>(unsigned_value & 1));
+ return true;
+}
+
+bool ValueDeserializer::ReadDouble(double& value) {
+ if (buffer_.position > buffer_.size - sizeof(double)) {
+ return false;
+ }
+ value = 0;
+ memcpy(&value, buffer_.currentPositionAddress(), sizeof(double));
+ buffer_.position += sizeof(double);
+ if (std::isnan(value)) {
+ return false;
+ }
+ return true;
+}
+
+bool ValueDeserializer::ReadOneByteString(StringRef*& string) {
+ uint32_t length = 0;
+ const uint8_t* data;
+ if (!ReadVarint<uint32_t>(length) || !ReadRawBytes(length, data)) {
+ return false;
+ }
+ string = StringRef::createFromUTF8(reinterpret_cast<const char*>(data),
+ static_cast<size_t>(length));
+ return true;
+}
+
+bool ValueDeserializer::ReadTwoByteString(StringRef*& string) {
+ uint32_t length = 0;
+ const uint8_t* data;
+ if (!ReadVarint<uint32_t>(length) ||
+ !ReadRawBytes(length * sizeof(uint16_t), data)) {
+ return false;
+ }
+ string = StringRef::createFromUTF16(reinterpret_cast<const char16_t*>(data),
+ static_cast<size_t>(length));
+ return true;
+}
+
+bool ValueDeserializer::ReadObject(ObjectRef* object) {
+ size_t propertiesRead = 0;
+
+ while (!CheckTag(SerializationTag::kEndJSObject)) {
+ auto key = ReadValue();
+ if (!key.hasValue() || key.get()->isUndefinedOrNull()) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read key of object");
+ return false;
+ }
+ auto value = ReadValue();
+ if (!value.hasValue()) {
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "Cannot read value of object");
+ return false;
+ }
+
+ ObjectRefHelper::setProperty(
+ lwIsolate_->GetCurrentContext()->context()->get(),
+ object,
+ key.get(),
+ value.get());
+
+ propertiesRead++;
+ }
+
+ SerializationTag tag;
+ if (!ReadTag(tag)) {
+ return false;
+ }
+ if (tag == SerializationTag::kEndJSObject) {
+ uint32_t propertiesWritten;
+ if (ReadVarint<uint32_t>(propertiesWritten)) {
+ return propertiesRead == propertiesWritten;
+ }
+ }
+ return false;
+}
+
+bool ValueDeserializer::ReadHostObject(ObjectRef*& esObject) {
+ esObject = nullptr;
+ if (!delegate_) {
+ return false;
+ }
+ v8::Isolate* v8_isolate = lwIsolate_->toV8();
+ v8::Local<v8::Object> object;
+ if (!delegate_->ReadHostObject(v8_isolate).ToLocal(&object)) {
+ return false;
+ }
+
+ esObject = VAL(*object)->value()->asObject();
+ return true;
+}
+
+bool ValueDeserializer::ReadJsArray(ArrayObjectRef*& array) {
+ uint32_t length = 0;
+ if (!ReadVarint<uint32_t>(length)) {
+ return false;
+ }
+
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "ReadJsArray start: %u", length);
+ auto vector = ValueVectorRef::create();
+
+ for (uint32_t i = 0; i < length; i++) {
+ OptionalRef<ValueRef> valueRef = ReadValue();
+ if (!valueRef.hasValue()) {
+ return false;
+ }
+ vector->pushBack(valueRef.get());
+ }
+
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+ array = ArrayObjectRefHelper::create(esContext, vector);
+
+ SerializationTag tag;
+ ReadTag(tag);
+
+ LWNODE_CALL_TRACE_ID_LOG(SERIALIZER, "ReadJsArray end");
+
+ return (tag == SerializationTag::kEndDenseJSArray);
+}
+
+bool ValueDeserializer::ReadJsArrayBuffer(ValueRef*& value) {
+ ArrayBufferObjectRef* arrayBuffer = nullptr;
+ if (!ReadArrayBuffer(arrayBuffer)) {
+ return false;
+ }
+
+ if (CheckTag(SerializationTag::kArrayBufferView)) {
+ SerializationTag tag;
+ ReadTag(tag);
+ ArrayBufferViewRef* arrayBufferView = nullptr;
+ if (!ReadArrayBufferView(arrayBufferView, arrayBuffer)) {
+ return false;
+ }
+ value = arrayBufferView;
+ return true;
+ }
+ value = arrayBuffer;
+ return true;
+}
+
+bool ValueDeserializer::ReadArrayBuffer(
+ ArrayBufferObjectRef*& arrayBufferObject) {
+ uint32_t length = 0;
+ const uint8_t* bytes = nullptr;
+ if (!ReadVarint<uint32_t>(length) || !ReadRawBytes(length, bytes)) {
+ return false;
+ }
+
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+ EvalResult r = Evaluator::execute(
+ esContext,
+ [](ExecutionStateRef* esState, size_t byteLength) -> ValueRef* {
+ auto arrayBuffer = ArrayBufferObjectRef::create(esState);
+ arrayBuffer->allocateBuffer(esState, byteLength);
+ return arrayBuffer;
+ },
+ (size_t)length);
+ LWNODE_CHECK(r.isSuccessful());
+
+ auto backingStore =
+ BackingStoreRef::createDefaultNonSharedBackingStore(length);
+ arrayBufferObject = r.result->asArrayBufferObject();
+ arrayBufferObject->attachBuffer(backingStore);
+
+ memcpy(arrayBufferObject->rawBuffer(), bytes, length);
+
+ return true;
+}
+
+bool ValueDeserializer::ReadArrayBufferView(
+ ArrayBufferViewRef*& arrayBufferView, ArrayBufferObjectRef* abo) {
+ uint8_t tag = 0;
+ uint32_t byteOffset = 0;
+ uint32_t arrayLength = 0;
+
+ if (!ReadVarint<uint8_t>(tag) || !ReadVarint<uint32_t>(byteOffset) ||
+ !ReadVarint<uint32_t>(arrayLength)) {
+ return false;
+ }
+
+ auto esContext = lwIsolate_->GetCurrentContext()->get();
+
+ switch (static_cast<ArrayBufferViewTag>(tag)) {
+ case ArrayBufferViewTag::kDataView: {
+ return false;
+ }
+#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
+ case ArrayBufferViewTag::k##Type##Array: \
+ arrayBufferView = ArrayBufferHelper::createView<Type##ArrayObjectRef>( \
+ esContext, \
+ abo, \
+ byteOffset, \
+ arrayLength, \
+ ArrayBufferHelper::ArrayType::kExternal##Type##Array); \
+ return true;
+ TYPED_ARRAYS(TYPED_ARRAY_CASE)
+#undef TYPED_ARRAY_CASE
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+bool ValueDeserializer::ReadRawBytes(size_t size, const uint8_t*& data) {
+ if (size > buffer_.size - buffer_.position) {
+ return false;
+ }
+
+ data = buffer_.currentPositionAddress();
+ buffer_.position += size;
+ return true;
+}
+
+} // namespace EscargotShim
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021-present Samsung Electronics 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.
+ */
+
+#if defined(LWNODE_ENABLE_EXPERIMENTAL_SERIALIZATION)
+
+#pragma once
+
+#include <EscargotPublic.h>
+#include <v8.h>
+
+#include "utils/optional.h"
+
+using namespace Escargot;
+
+namespace EscargotShim {
+
+enum class SerializationTag : uint8_t;
+
+class IsolateWrap;
+
+struct SerializerBuffer {
+ SerializerBuffer() = default;
+ SerializerBuffer(const uint8_t* _data, size_t _size)
+ : data(const_cast<uint8_t*>(_data)), size(_size) {}
+ uint8_t* data = nullptr;
+ size_t size = 0;
+ size_t capacity = 0;
+ size_t position = 0;
+
+ uint8_t currentPositionData() {
+ LWNODE_CHECK(!isOverflow());
+ return data[position];
+ }
+
+ uint8_t* currentPositionAddress() {
+ LWNODE_CHECK(!isOverflow());
+ return &data[position];
+ }
+
+ bool isOverflow() { return position >= size; }
+};
+
+class ValueSerializer {
+ public:
+ ValueSerializer(IsolateWrap* lwIsolate,
+ v8::ValueSerializer::Delegate* delegate);
+ void WriteHeader();
+ bool WriteValue(ValueRef* value);
+ bool WriteUint32(uint32_t value);
+ bool WriteInt32(int32_t value);
+ bool WriteNumber(double value);
+
+ std::pair<uint8_t*, size_t> Release();
+
+ private:
+ void WriteTag(SerializationTag tag);
+ void WriteRawBytes(const void* source, size_t length);
+ uint8_t* ReserveRawBytes(size_t bytes);
+
+ template <typename T>
+ void WriteVarint(T value);
+ template <typename T>
+ void WriteZigZag(T value);
+ bool WriteBoolean(bool value);
+ void WriteString(StringRef* string);
+ bool WriteObject(ObjectRef* object);
+ bool WriteJsArray(ArrayObjectRef* array);
+ bool WriteHostObject(ObjectRef* object);
+ bool WriteArrayBuffer(size_t length, uint8_t* bytes);
+ bool WriteArrayBufferView(ArrayBufferViewRef* arrayBufferView);
+ bool WriteTypedArrayObject(Escargot::ArrayBufferViewRef* bufferView);
+ bool ExpandBuffer(size_t required_capacity);
+ bool ThrowIfOutOfMemory();
+ void ThrowDataCloneError();
+
+ IsolateWrap* lwIsolate_ = nullptr;
+ v8::ValueSerializer::Delegate* delegate_ = nullptr;
+ SerializerBuffer buffer_;
+ bool out_of_memory_ = false;
+};
+
+class ValueDeserializer {
+ public:
+ ValueDeserializer(IsolateWrap* isolate,
+ v8::ValueDeserializer::Delegate* delegate,
+ const uint8_t* data,
+ const size_t size);
+ ~ValueDeserializer(){};
+
+ OptionalRef<ValueRef> ReadValue();
+ bool ReadUint32(uint32_t*& value);
+
+ private:
+ bool ReadTag(SerializationTag& tag);
+ bool CheckTag(SerializationTag tag);
+ template <typename T>
+ bool ReadVarint(T& value);
+ template <typename T>
+ bool ReadZigZag(T& value);
+ bool ReadDouble(double& value);
+ bool ReadOneByteString(StringRef*& string);
+ bool ReadTwoByteString(StringRef*& string);
+ bool ReadObject(ObjectRef* object);
+ bool ReadHostObject(ObjectRef*& object);
+ bool ReadJsArray(ArrayObjectRef*& array);
+ bool ReadJsArrayBuffer(ValueRef*& value);
+ bool ReadArrayBuffer(ArrayBufferObjectRef*& arayBufferObject);
+ bool ReadArrayBufferView(ArrayBufferViewRef*& arrayBufferView,
+ ArrayBufferObjectRef* arrayBufferObject);
+
+ bool ReadRawBytes(size_t size, const uint8_t*& data);
+
+ IsolateWrap* lwIsolate_ = nullptr;
+ v8::ValueDeserializer::Delegate* delegate_ = nullptr;
+ SerializerBuffer buffer_;
+};
+
+} // namespace EscargotShim
+
+#endif
namespace EscargotShim {
-class NativeDataAccessorPropertyDataForStackTrace
- : public ObjectRef::NativeDataAccessorPropertyData {
- public:
- NativeDataAccessorPropertyDataForStackTrace(
- bool isWritable,
- bool isEnumerable,
- bool isConfigurable,
- ObjectRef::NativeDataAccessorPropertyGetter getter,
- ObjectRef::NativeDataAccessorPropertySetter setter,
- ValueVectorRef* stackTraceVector)
- : NativeDataAccessorPropertyData(
- isWritable, isEnumerable, isConfigurable, getter, setter),
- stackTraceVector_(stackTraceVector) {}
-
- ValueVectorRef* stackTraceVector() { return stackTraceVector_; }
-
- void* operator new(size_t size) { return GC_MALLOC(size); }
-
- private:
- ValueVectorRef* stackTraceVector_;
-};
-
-static size_t getStackTraceLimit(ExecutionStateRef* state) {
+size_t StackTrace::getStackTraceLimit(ExecutionStateRef* state) {
auto errorObject = state->context()->globalObject()->get(
state, StringRef::createFromASCII("Error"));
LWNODE_CHECK(errorObject->isObject());
return stackTraceLimitValue->asNumber();
}
-static ValueRef* StackTraceGetter(
+ValueRef* StackTrace::StackTraceGetter(
ExecutionStateRef* state,
ObjectRef* self,
ValueRef* receiver,
ObjectRef::NativeDataAccessorPropertyData* data) {
auto lwIsolate = IsolateWrap::GetCurrent();
auto lwContext = lwIsolate->GetCurrentContext();
- auto accessorData =
- reinterpret_cast<NativeDataAccessorPropertyDataForStackTrace*>(data);
+ auto accessorData = reinterpret_cast<NativeAccessorProperty*>(data);
if (lwIsolate->HasPrepareStackTraceCallback()) {
auto sites =
ArrayObjectRef::create(state, accessorData->stackTraceVector());
- v8::MaybeLocal<v8::Value> callbackResult =
- lwIsolate->PrepareStackTraceCallback()(
- v8::Utils::NewLocal<Context>(lwIsolate->toV8(), lwContext),
- v8::Utils::NewLocal<Value>(lwIsolate->toV8(), self),
- v8::Utils::NewLocal<Array>(lwIsolate->toV8(), sites));
-
- if (!callbackResult.IsEmpty()) {
- Local<Value> callbackResultLocal;
- if (callbackResult.ToLocal(&callbackResultLocal)) {
- return CVAL(*callbackResultLocal)->value();
- }
+
+ auto formattedStackTrace =
+ lwIsolate->RunPrepareStackTraceCallback(state, lwContext, self, sites);
+ if (formattedStackTrace) {
+ return formattedStackTrace;
}
}
return ValueRef::createUndefined();
}
-static bool StackTraceSetter(ExecutionStateRef* state,
- ObjectRef* self,
- ValueRef* receiver,
- ObjectRef::NativeDataAccessorPropertyData* data,
- ValueRef* setterInputData) {
+bool StackTrace::StackTraceSetter(
+ ExecutionStateRef* state,
+ ObjectRef* self,
+ ValueRef* receiver,
+ ObjectRef::NativeDataAccessorPropertyData* data,
+ ValueRef* setterInputData) {
LWNODE_RETURN_FALSE;
}
-static ValueRef* captureStackTraceCallback(ExecutionStateRef* state,
- ValueRef* thisValue,
- size_t argc,
- ValueRef** argv,
- bool isConstructCall) {
+ValueRef* StackTrace::captureStackTraceCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
if (argc < 1 || !argv[0]->isObject()) {
return ValueRef::createUndefined();
}
auto exceptionObject = argv[0]->asObject();
auto callSite = IsolateWrap::GetCurrent()->GetCurrentContext()->callSite();
- auto stackTrace = state->computeStackTraceData();
+ auto stackTrace = state->computeStackTrace();
auto stackTraceVector = ValueVectorRef::create();
std::string filterFunctionName;
callSite->instantiate(state->context(), stackTrace[i]));
}
- exceptionObject->defineNativeDataAccessorProperty(
- state,
- StringRef::createFromUTF8("stack"),
- new NativeDataAccessorPropertyDataForStackTrace(false,
- false,
- false,
- StackTraceGetter,
- StackTraceSetter,
- stackTraceVector));
+ // FIXME: it seems there are some cases where we need to freeze the
+ // stack string here. Investigate further
+ addStackProperty(state, exceptionObject, stackTraceVector);
return ValueRef::createUndefined();
}
+void StackTrace::addStackProperty(ExecutionStateRef* state,
+ ObjectRef* object,
+ ValueVectorRef* stackTraceVector) {
+ // NOTE: either Error or Exception contains stack.
+ object->defineNativeDataAccessorProperty(
+ state,
+ StringRef::createFromUTF8("stack"),
+ new NativeAccessorProperty(false,
+ false,
+ false,
+ StackTraceGetter,
+ StackTraceSetter,
+ stackTraceVector));
+}
+
ValueRef* StackTrace::createCaptureStackTrace(
Escargot::ExecutionStateRef* state) {
FunctionObjectRef::NativeFunctionInfo info(
return FunctionObjectRef::create(state, info);
}
-static void setCallSitePrototype(
- ContextRef* context,
- ObjectTemplateRef* otpl,
- const char* name,
- Escargot::FunctionObjectRef::NativeFunctionPointer fn) {
- auto length = strlen(name);
+ValueRef* StackTrace::createPrepareStackTrace(
+ Escargot::ExecutionStateRef* state) {
+ FunctionObjectRef::NativeFunctionInfo info(
+ AtomicStringRef::create(state->context(), "prepareStackTrace"),
+ prepareStackTraceCallback,
+ 2,
+ true,
+ false);
- EvalResult r = Evaluator::execute(
- context,
- [](ExecutionStateRef* state,
- const char* name,
- size_t length,
- Escargot::FunctionObjectRef::NativeFunctionPointer fn) -> ValueRef* {
- return FunctionObjectRef::create(
- state,
- FunctionObjectRef::NativeFunctionInfo(
- AtomicStringRef::create(state->context(), name, length),
- fn,
- 0,
- true,
- false));
- },
- name,
- length,
- fn);
- LWNODE_CHECK(r.isSuccessful());
+ return FunctionObjectRef::create(state, info);
+}
+
+ValueRef* StackTrace::prepareStackTraceCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ // TODO: JS registers prepareStackTrace() to reformat StackTrace.
+ LWNODE_UNIMPLEMENT;
+ return StringRef::emptyString();
+}
+
+StringRef* StackTrace::formatStackTraceStringNodeStyle(ObjectRef* errorObject,
+ size_t maxStackSize) {
+ std::ostringstream oss;
+ auto message = errorObject->toString(state_)->toStdUTF8String();
+ if (message.length() > 0) {
+ oss << message << "\n";
+ }
+
+ auto traceData = state_->computeStackTrace();
+ size_t maxPrintStackSize = std::min((int)maxStackSize, (int)traceData.size());
+
+ const std::string separator = " ";
+ for (size_t i = 0; i < maxPrintStackSize; ++i) {
+ oss << separator << "at " << formatStackTraceLine(traceData[i])
+ << std::endl;
+ }
+
+ auto stringNodeStyle = oss.str();
+ return StringRef::createFromUTF8(stringNodeStyle.c_str(),
+ stringNodeStyle.length());
+}
+
+std::string StackTrace::formatStackTraceLine(
+ const Evaluator::StackTraceData& line) {
+ std::ostringstream oss;
+
+ const auto& iter = line;
+ const auto& resourceName = iter.srcName->toStdUTF8String();
+ const auto& functionName = iter.functionName->toStdUTF8String();
+ const int errorLine = iter.loc.line;
+ const int errorColumn = iter.loc.column;
+
+ oss << (functionName == "" ? "Object.<anonymous>" : functionName) << " "
+ << "(" << (resourceName == "" ? "?" : resourceName) << ":" << errorLine
+ << ":" << errorColumn << ")";
+
+ return oss.str();
+}
+
+ArrayObjectRef* StackTrace::genCallSites() {
+ auto stackTrace = state_->computeStackTrace();
+ auto stackTraceVector = ValueVectorRef::create();
+ auto callSite = IsolateWrap::GetCurrent()->GetCurrentContext()->callSite();
+
+ for (size_t i = 0; i < stackTrace.size(); i++) {
+ stackTraceVector->pushBack(
+ callSite->instantiate(state_->context(), stackTrace[i]));
+ }
+
+ return ArrayObjectRef::create(state_, stackTraceVector);
+}
- otpl->set(
- StringRef::createFromUTF8(name, length), r.result, false, false, true);
+CallSite::CallSite(ContextRef* context) : context_(context) {
+ template_ = FunctionTemplateRef::create(
+ AtomicStringRef::create(context, "CallSite"),
+ 0,
+ true,
+ true,
+ [](ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ OptionalRef<ObjectRef> newTarget) -> ValueRef* {
+ return ValueRef::createUndefined();
+ });
+ injectSitePrototype();
}
-static void injectSitePrototype(ContextRef* context, ObjectTemplateRef* otpl) {
+ValueRef* CallSite::instantiate(ContextRef* context,
+ const Evaluator::StackTraceData& data) {
+ auto callSite = template_->instanceTemplate()->instantiate(context);
+ ExtraDataHelper::setExtraData(callSite, new StackTraceData(data));
+ return callSite;
+};
+
+void CallSite::injectSitePrototype() {
setCallSitePrototype(
- context,
- otpl,
"getFunctionName",
[](ExecutionStateRef* state,
ValueRef* thisValue,
});
setCallSitePrototype(
- context,
- otpl,
"getFileName",
[](ExecutionStateRef* state,
ValueRef* thisValue,
});
setCallSitePrototype(
- context,
- otpl,
"getLineNumber",
[](ExecutionStateRef* state,
ValueRef* thisValue,
});
setCallSitePrototype(
- context,
- otpl,
"getColumnNumber",
[](ExecutionStateRef* state,
ValueRef* thisValue,
});
setCallSitePrototype(
- context,
- otpl,
"isEval",
[](ExecutionStateRef* state,
ValueRef* thisValue,
});
setCallSitePrototype(
- context,
- otpl,
"toString",
[](ExecutionStateRef* state,
ValueRef* thisValue,
auto string = stream.str();
return StringRef::createFromUTF8(string.data(), string.length());
});
-}
-CallSite::CallSite(ContextRef* context) {
- template_ = FunctionTemplateRef::create(
- AtomicStringRef::create(context, "CallSite"),
- 0,
- true,
- true,
+ setCallSitePrototype(
+ "getFunction",
[](ExecutionStateRef* state,
ValueRef* thisValue,
size_t argc,
ValueRef** argv,
- OptionalRef<ObjectRef> newTarget) -> ValueRef* {
+ bool isNewExpression) -> ValueRef* {
+ auto data = ObjectRefHelper::getExtraData(thisValue->asObject())
+ ->asStackTraceData();
+ auto esFunction = data->callee();
+ if (esFunction.hasValue()) {
+ return esFunction.get();
+ }
return ValueRef::createUndefined();
});
- injectSitePrototype(context, template_->prototypeTemplate());
}
-ValueRef* CallSite::instantiate(ContextRef* context,
- const Evaluator::StackTraceData& data) {
- auto callSite = template_->instanceTemplate()->instantiate(context);
- ExtraDataHelper::setExtraData(callSite, new StackTraceData(data));
- return callSite;
-};
+void CallSite::setCallSitePrototype(
+ const std::string& name,
+ Escargot::FunctionObjectRef::NativeFunctionPointer fn) {
+ EvalResult r = Evaluator::execute(
+ context_,
+ [](ExecutionStateRef* state,
+ const std::string* name,
+ Escargot::FunctionObjectRef::NativeFunctionPointer fn) -> ValueRef* {
+ return FunctionObjectRef::create(
+ state,
+ FunctionObjectRef::NativeFunctionInfo(
+ AtomicStringRef::create(
+ state->context(), name->c_str(), name->length()),
+ fn,
+ 0,
+ true,
+ false));
+ },
+ &name,
+ fn);
+ LWNODE_CHECK(r.isSuccessful());
+
+ template_->prototypeTemplate()->set(
+ StringRef::createFromUTF8(name.c_str(), name.length()),
+ r.result,
+ false,
+ false,
+ true);
+}
} // namespace EscargotShim
class StackTrace {
public:
+ class NativeAccessorProperty
+ : public ObjectRef::NativeDataAccessorPropertyData {
+ public:
+ NativeAccessorProperty(bool isWritable,
+ bool isEnumerable,
+ bool isConfigurable,
+ ObjectRef::NativeDataAccessorPropertyGetter getter,
+ ObjectRef::NativeDataAccessorPropertySetter setter,
+ ValueVectorRef* stackTraceVector)
+ : NativeDataAccessorPropertyData(
+ isWritable, isEnumerable, isConfigurable, getter, setter),
+ stackTraceVector_(stackTraceVector) {}
+
+ ValueVectorRef* stackTraceVector() { return stackTraceVector_; }
+
+ void* operator new(size_t size) { return GC_MALLOC(size); }
+
+ private:
+ ValueVectorRef* stackTraceVector_ = nullptr;
+ };
+
+ StackTrace(ExecutionStateRef* state) : state_(state) {}
+
static ValueRef* createCaptureStackTrace(ExecutionStateRef* state);
+ static ValueRef* captureStackTraceCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+
+ static ValueRef* createPrepareStackTrace(Escargot::ExecutionStateRef* state);
+ static ValueRef* prepareStackTraceCallback(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall);
+
+ static ValueRef* StackTraceGetter(
+ ExecutionStateRef* state,
+ ObjectRef* self,
+ ValueRef* receiver,
+ ObjectRef::NativeDataAccessorPropertyData* data);
+ static bool StackTraceSetter(ExecutionStateRef* state,
+ ObjectRef* self,
+ ValueRef* receiver,
+ ObjectRef::NativeDataAccessorPropertyData* data,
+ ValueRef* setterInputData);
+
+ static void addStackProperty(ExecutionStateRef* state,
+ ObjectRef* object,
+ ValueVectorRef* stackTraceVector);
+
+ ArrayObjectRef* genCallSites();
+
+ // FIXME: Having maxStackSize=1 does not print full stack. Find the right
+ // value
+ StringRef* formatStackTraceStringNodeStyle(ObjectRef* errorObject,
+ size_t maxStackSize = 1);
+
+ private:
+ ExecutionStateRef* state_ = nullptr;
+
+ static size_t getStackTraceLimit(ExecutionStateRef* state);
+
+ std::string formatStackTraceLine(const Evaluator::StackTraceData& line);
+
+ // NOTE: StackTrace can only be initialized as a local variable
+ void* operator new(size_t size);
+ void* operator new[](size_t size);
+ void operator delete(void*, size_t);
+ void operator delete[](void*, size_t);
};
class CallSite : public gc {
const Evaluator::StackTraceData& data);
private:
- FunctionTemplateRef* template_;
+ ContextRef* context_ = nullptr;
+ FunctionTemplateRef* template_ = nullptr;
+
+ void injectSitePrototype();
+ void setCallSitePrototype(
+ const std::string& name,
+ Escargot::FunctionObjectRef::NativeFunctionPointer fn);
};
} // namespace EscargotShim
#if defined(LWNODE_PLATFORM_LINUX)
#include <execinfo.h>
#endif
+
+#include "api/context.h"
+#include "api/es-helper.h"
+#include "api/global.h"
+#include "api/isolate.h"
#include "debug.h"
#include "misc.h"
+using namespace Escargot;
+using namespace v8;
+
namespace EscargotShim {
#if defined(LWNODE_PLATFORM_LINUX)
void DebugUtils::printStackTrace() {
#if defined(NDEBUG)
- if (EscargotShim::Flags::isInternalLogEnabled() == false) {
+ if (!EscargotShim::Global::flags()->isOn(
+ EscargotShim::Flag::Type::InternalLog)) {
return;
}
#endif
return;
}
- LWNODE_LOG_RAW("\n[Backtrace]");
+ LWNODE_LOGR("\n[Backtrace]");
for (int i = 1; i < frameSize - 2; ++i) {
char cmd[kStackTraceBufferSize];
snprintf(cmd,
while (fgets(line, sizeof(line), stream)) {
line[strnlen(line, kStackTraceBufferSize) - 1] = 0;
if (isFirstLine) {
- LWNODE_LOG_RAW("#%-2d: %s", i - 1, line);
+ LWNODE_LOGR("#%-2d: %s", i - 1, line);
isFirstLine = false;
} else {
- LWNODE_LOG_RAW("%s", line);
+ LWNODE_LOGR("%s", line);
}
}
- LWNODE_LOG_RAW("");
+ LWNODE_LOGR("");
pclose(stream);
} else if (symbols[i]) {
- LWNODE_LOG_RAW("#%-2d: %s", i - 1, symbols[i]);
+ LWNODE_LOGR("#%-2d: %s", i - 1, symbols[i]);
}
}
#endif
}
+static void printObjectProperties(ExecutionStateRef* state,
+ ObjectRef* object,
+ int depth = 0) {
+ auto propertyNames = object->ownPropertyKeys(state);
+ LWNODE_LOGR("%*s[prop(%zu)]\n", depth, "", propertyNames->size());
+ for (size_t i = 0; i < propertyNames->size(); i++) {
+ auto propertyValue = object->getOwnProperty(state, propertyNames->at(i));
+ auto propertyNameString = propertyNames->at(i)->toString(state);
+ auto propertyValueString = propertyValue->toString(state);
+ LWNODE_LOGR(" %*s%s: %s\n",
+ depth,
+ "",
+ propertyNameString->toStdUTF8String().data(),
+ propertyValueString->toStdUTF8String().data());
+ }
+}
+
+void DebugUtils::printObject(ObjectRef* value, int depth) {
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ auto r = Evaluator::execute(
+ lwIsolate->GetCurrentContext()->get(),
+ [](ExecutionStateRef* state, ObjectRef* object, int depth) -> ValueRef* {
+ printObjectProperties(state, object, depth);
+
+ OptionalRef<ObjectRef> maybePrototype =
+ object->getPrototypeObject(state);
+ if (maybePrototype.hasValue()) {
+ auto prototype = maybePrototype.value();
+ printObjectProperties(state, prototype, depth);
+
+ OptionalRef<ObjectRef> maybePrototype =
+ prototype->getPrototypeObject(state);
+ if (maybePrototype.hasValue()) {
+ printObject(maybePrototype.value(), depth + 2);
+ }
+ }
+ return ValueRef::createNull();
+ },
+ value,
+ depth);
+ LWNODE_CHECK(r.isSuccessful());
+}
+
+void DebugUtils::printToString(ValueRef* value) {
+ auto context = IsolateWrap::GetCurrent()->GetCurrentContext()->get();
+ EvalResult r = Evaluator::execute(
+ context,
+ [](ExecutionStateRef* state, ValueRef* value) -> ValueRef* {
+ auto valueString = value->toString(state);
+ LWNODE_LOGR("toString: %s\n", valueString->toStdUTF8String().c_str());
+ return value;
+ },
+ value);
+}
+
+std::string DebugUtils::v8StringToStd(Isolate* isolate, Local<Value> value) {
+ Local<String> s;
+ value->ToString(isolate->GetCurrentContext()).ToLocal(&s);
+ v8::String::Utf8Value utf8String(isolate, s);
+ return std::string(*utf8String);
+}
+
} // namespace EscargotShim
#pragma once
+#include "EscargotPublic.h"
+#include "v8.h"
+
namespace EscargotShim {
class DebugUtils {
public:
static void printStackTrace();
+ static void printObject(Escargot::ObjectRef* value, int depth = 0);
+ static void printToString(Escargot::ValueRef* value);
+ static std::string v8StringToStd(v8::Isolate* isolate,
+ v8::Local<v8::Value> value);
};
} // namespace EscargotShim
#include "gc.h"
#include <EscargotPublic.h>
+#include "api/global.h"
#include "misc.h"
using namespace Escargot;
#define LOG_HANDLER(msg, ...) \
do { \
- if (EscargotShim::Flags::isTraceGCEnabled()) { \
- LWNODE_LOG_RAW(CLR_DIM msg CLR_RESET, ##__VA_ARGS__); \
+ if (EscargotShim::Global::flags()->isOn( \
+ EscargotShim::Flag::Type::TraceGC)) { \
+ LWNODE_LOGR(CLR_DIM msg CLR_RESET, ##__VA_ARGS__); \
} \
} while (0)
typename Allocator = GCUtil::gc_malloc_allocator<T>>
using GCVectorT = Starfish::Vector<T, Allocator, isEraseStrategyStrict>;
-#if !defined(NDEBUG)
+#if defined(GC_DEBUG)
template <typename T,
bool isEraseStrategyStrict = true,
typename Allocator = GCUtil::gc_malloc_allocator<T>>
OUT_OF_MEMORY,
};
+enum class GarbageCollectionReason {
+ // NOTE: From the following enum values from v8,
+ // we only use a few.
+ // kUnknown = 0,
+ // kAllocationFailure = 1,
+ // kAllocationLimit = 2,
+ // kContextDisposal = 3,
+ // kCountersExtension = 4,
+ // kDebugger = 5,
+ // kDeserializer = 6,
+ // kExternalMemoryPressure = 7,
+ // kFinalizeMarkingViaStackGuard = 8,
+ // kFinalizeMarkingViaTask = 9,
+ // kFullHashtable = 10,
+ // kHeapProfiler = 11,
+ // kTask = 12,
+ // kLastResort = 13,
+ // kLowMemoryNotification = 14,
+ // kMakeHeapIterable = 15,
+ // kMemoryPressure = 16,
+ // kMemoryReducer = 17,
+ kRuntime = 18,
+ // kSamplingProfiler = 19,
+ // kSnapshotCreator = 20,
+ kTesting = 21,
+ // kExternalFinalize = 22,
+ // kGlobalAllocationLimit = 23,
+ // kMeasureMemory = 24
+};
+
class ESCARGOT_EXPORT MemoryUtil {
public:
typedef void (*OnGCWarnEventListener)(WarnEventType type);
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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.
+ */
+
+#pragma once
+
+#define CLR_RESET "\033[0m"
+#define CLR_DIM "\033[0;2m"
+#define CLR_RED "\033[0;31m"
+#define CLR_GREEN "\033[0;32m"
+#define CLR_GREY "\033[0;37m"
+#define CLR_BLACK "\033[0;30m"
+#define CLR_YELLOW "\033[0;33m"
+#define CLR_BLUE "\033[0;34m"
+#define CLR_MAGENTA "\033[0;35m"
+#define CLR_CYAN "\033[0;36m"
+#define CLR_DARKGREY "\033[01;30m"
+#define CLR_BRED "\033[01;31m"
+#define CLR_BYELLOW "\033[01;33m"
+#define CLR_BBLUE "\033[01;34m"
+#define CLR_BMAGENTA "\033[01;35m"
+#define CLR_BCYAN "\033[01;36m"
+#define CLR_BGREEN "\033[01;32m"
+#define CLR_WHITE "\033[01;37m"
+#define CLR_REDBG "\033[0;41m"
#include "flags.h"
+#include <algorithm>
+
+#include "api/utils/string-util.h"
+
namespace EscargotShim {
-flag_t Flags::s_flags = FlagType::Empty;
-std::set<std::string> Flags::s_trace_ids;
-std::set<std::string> Flags::s_negative_trace_ids;
+std::vector<Flag> Flags::s_validFlags = {
+ // v8 flags
+ Flag("--expose-gc", Flag::Type::ExposeGC),
+ Flag("--use-strict", Flag::Type::UseStrict),
+ Flag("--off-idlegc", Flag::Type::DisableIdleGC),
+ Flag("--harmony-top-level-await", Flag::Type::TopLevelWait),
+ Flag("--allow-code-generation-from-strings",
+ Flag::Type::AllowCodeGenerationFromString),
+ Flag("--abort-on-uncaught-exception", Flag::Type::AbortOnUncaughtException),
+ Flag("--expose-externalize-string", Flag::Type::ExposeExternalizeString),
+ FlagWithValues("--unhandled-rejections=", Flag::Type::UnhandledRejections),
+ Flag("--trace-debug", Flag::Type::LWNodeOther, true),
+ Flag("--debug", Flag::Type::LWNodeOther, true),
+ Flag("--stack-size=", Flag::Type::LWNodeOther, true),
+ Flag("--nolazy", Flag::Type::LWNodeOther, true),
+ // lwnode flags
+ Flag("--trace-gc", Flag::Type::TraceGC),
+ FlagWithNegativeValues("--trace-call=", Flag::Type::TraceCall, true),
+ Flag("--internal-log", Flag::Type::InternalLog),
+ Flag("--start-debug-server", Flag::Type::DebugServer),
+};
-bool Flags::isTraceCallEnabled(std::string id) {
- if (!(s_flags & FlagType::TraceCall)) {
- return false;
+bool Flag::isPrefixOf(const std::string& name) {
+ if (useAsPrefix_ && (name.find(name_) != std::string::npos)) {
+ return true;
+ }
+
+ return false;
+}
+
+Flag* Flags::findFlagObject(const std::string& name) {
+ std::string normalized = name;
+ std::replace(normalized.begin(), normalized.end(), '_', '-');
+
+ for (size_t i = 0; i < s_validFlags.size(); i++) {
+ Flag* flag = &s_validFlags[i];
+ if ((flag->name() == normalized) || flag->isPrefixOf(normalized)) {
+ return flag;
+ }
}
- if (!s_trace_ids.empty()) {
- if (s_trace_ids.find(id) == s_trace_ids.end()) {
- return false;
+ return nullptr;
+}
+
+Flag* Flags::findFlagObject(Flag::Type type) {
+ for (size_t i = 0; i < s_validFlags.size(); i++) {
+ Flag* flag = &s_validFlags[i];
+ if (flag->type() == type) {
+ return flag;
}
}
- if (!s_negative_trace_ids.empty()) {
- if (s_negative_trace_ids.find(id) != s_negative_trace_ids.end()) {
- return false;
+ return nullptr;
+}
+
+void Flags::add(const std::string& userOption) {
+ // @check https://github.sec.samsung.net/lws/node-escargot/issues/394
+ Flag* flag = findFlagObject(userOption);
+ if (!flag || flag->type() == Flag::Empty) {
+ LWNODE_DLOG_WARN("Ignore flag: '%s'\n", flag ? flag->name().c_str() : "");
+ return;
+ }
+
+ add(flag);
+
+ if (flag->type() == Flag::Type::TraceCall ||
+ flag->type() == Flag::Type::UnhandledRejections) {
+ std::string optionValues = userOption.substr(userOption.find_first_of('=') +
+ 1); // +1 for skipping '='
+ auto tokens = strSplit(optionValues, ',');
+ for (auto token : tokens) {
+ flag->addValue(token);
}
}
+}
+
+void Flags::add(Flag::Type type) {
+ add(findFlagObject(type));
+}
+
+void Flags::add(Flag* flag) {
+ if (!flag || flag->type() == Flag::Type::LWNodeOther) {
+ return;
+ }
+
+ flags_.insert(flag);
+}
+
+Flag* Flags::getFlag(Flag::Type type) {
+ Flag flag("", type);
+ auto itr = flags_.find(&flag);
+ if (itr == flags_.end()) {
+ return nullptr;
+ }
+
+ return *itr;
+}
+
+bool Flags::isOn(Flag::Type type, const std::string& value) {
+ Flag* flag = getFlag(type);
+ if (!flag) {
+ return false;
+ }
+
+ if (value == "") {
+ return true;
+ }
- return true;
+ return flag->hasValue(value);
+}
+
+void Flags::shrinkArgumentList(int* argc, char** argv) {
+ int count = 0;
+ for (int idx = 0; idx < *argc; idx++) {
+ if (argv[idx]) {
+ argv[count++] = argv[idx];
+ }
+ }
+ *argc = count;
}
} // namespace EscargotShim
#pragma once
#include <cstdint>
+#include <memory>
#include <set>
#include <string>
+#include <vector>
#if !defined(LWNODE_EXPORT)
#define LWNODE_EXPORT __attribute__((visibility("default")))
typedef uint16_t flag_t;
-enum FlagType : flag_t {
- Empty = 0,
- ExposeGC = 1 << 1,
- UseStrict = 1 << 2,
- DisableIdleGC = 1 << 3,
- TopLevelWait = 1 << 4,
- // lwnode
- TraceCall = 1 << 5,
- TraceGC = 1 << 6,
- InternalLog = 1 << 7,
+class Flag {
+ public:
+ enum Type : flag_t {
+ Empty = 0,
+ ExposeGC,
+ UseStrict,
+ DisableIdleGC,
+ TopLevelWait,
+ AllowCodeGenerationFromString,
+ AbortOnUncaughtException,
+ ExposeExternalizeString,
+ UnhandledRejections,
+ // lwnode
+ TraceCall,
+ TraceGC,
+ InternalLog,
+ LWNodeOther,
+ DebugServer,
+ };
+
+ Flag(const std::string& name, Type type, bool useAsPrefix = false)
+ : name_(name), type_(type), useAsPrefix_(useAsPrefix) {}
+
+ virtual std::string name() { return name_; }
+ virtual Type type() const { return type_; }
+ virtual bool isPrefixOf(const std::string& name);
+
+ virtual void addValue(const std::string& value){};
+ virtual bool hasValue(const std::string& value) { return false; }
+
+ virtual void addNegativeValue(const std::string& value) {}
+ virtual bool hasNegativeValue(const std::string& value) { return false; }
+
+ protected:
+ std::string name_;
+ Type type_ = Type::Empty;
+ bool useAsPrefix_ = false;
};
-class LWNODE_EXPORT Flags {
+class FlagWithValues : public Flag {
public:
- static void set(flag_t flags) { s_flags = flags; }
- static void add(flag_t flags) { s_flags |= flags; }
- static flag_t get() { return s_flags; };
+ FlagWithValues(const std::string& name, Type type, bool useAsPrefix = false)
+ : Flag(name, type, useAsPrefix) {}
- static bool isTraceCallEnabled(std::string id = "*");
- static bool isTraceGCEnabled() { return s_flags & FlagType::TraceGC; }
- static bool isInternalLogEnabled() { return s_flags & FlagType::InternalLog; }
+ virtual void addValue(const std::string& value) override {
+ values_.insert(value);
+ }
- static void setTraceCallId(std::string id) { s_trace_ids.insert(id); }
- static void setNagativeTraceCallId(std::string id) {
- s_negative_trace_ids.insert(id);
+ virtual bool hasValue(const std::string& value) override {
+ return values_.find(value) != values_.end();
}
private:
- static std::set<std::string> s_trace_ids;
- static std::set<std::string> s_negative_trace_ids;
- static flag_t s_flags;
+ std::set<std::string> values_;
+};
+
+class FlagWithNegativeValues : public FlagWithValues {
+ public:
+ FlagWithNegativeValues(const std::string& name,
+ Type type,
+ bool useAsPrefix = false)
+ : FlagWithValues(name, type, useAsPrefix) {}
+
+ void addNegativeValue(const std::string& value) override {
+ negativeValues_.insert(value);
+ }
+
+ bool hasNegativeValue(const std::string& value) override {
+ return negativeValues_.find(value) != negativeValues_.end();
+ }
+
+ private:
+ std::set<std::string> negativeValues_;
+};
+
+class LWNODE_EXPORT Flags {
+ public:
+ static std::vector<Flag> s_validFlags;
+ struct FlagComparator {
+ bool operator()(const Flag* a, const Flag* b) const {
+ return a->type() < b->type();
+ }
+ };
+
+ void add(const std::string& flag);
+ void add(Flag::Type type);
+ void add(Flag* flag);
+
+ bool isOn(Flag::Type type, const std::string& value = "");
+ void shrinkArgumentList(int* argc, char** argv);
+
+ std::set<Flag*, FlagComparator> get() { return flags_; };
+ void set(std::set<Flag*, FlagComparator> flags) { flags_ = flags; }
+
+ private:
+ Flag* findFlagObject(const std::string& name);
+ Flag* findFlagObject(Flag::Type type);
+
+ Flag* getFlag(Flag::Type type);
+ std::set<Flag*, FlagComparator> flags_;
};
} // namespace EscargotShim
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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 "logger-impl.h"
+
+#include <iomanip> // for setfill and setw
+#include <map>
+#include <thread>
+
+#include "api/global.h"
+#include "color.h"
+#include "flags.h"
+#include "logger.h"
+
+// --- Formatter ---
+
+#define PADDING(N) std::left << std::setfill(' ') << std::setw(N)
+#define DISPLAY_TYPE_LENGTH_LIMIT 5
+#define DISPLAY_TRACE_ID_LENGTH_LIMIT 10
+
+std::map<std::thread::id, int> s_thread_ids;
+
+void printThreadIdentifier(std::stringstream& stream) {
+ static int s_id = 0;
+
+ auto id = std::this_thread::get_id();
+ if (s_thread_ids.find(id) == s_thread_ids.end()) {
+ s_thread_ids[id] = ++s_id;
+ }
+
+ stream << "[" << s_thread_ids[id] << "] ";
+}
+
+std::string LogFormatter::header() {
+ std::stringstream stream;
+
+ printThreadIdentifier(stream);
+ printHeader(stream);
+
+ return stream.str();
+}
+
+void LogTYPED::printHeader(std::stringstream& stream) {
+ switch (type_) {
+ case Type::INFO:
+ stream << PADDING(DISPLAY_TYPE_LENGTH_LIMIT) << "INFO"
+ << " ";
+ break;
+ case Type::WARN:
+ stream << CLR_YELLOW << PADDING(DISPLAY_TYPE_LENGTH_LIMIT) << "WARN"
+ << " ";
+ break;
+ case Type::ERROR:
+ stream << CLR_RED << PADDING(DISPLAY_TYPE_LENGTH_LIMIT) << "ERROR"
+ << " ";
+ break;
+
+ default:
+ break;
+ }
+}
+
+LogINTERNAL::LogINTERNAL(Type type) : LogTYPED(type) {
+#if defined(NDEBUG)
+ isEnabled_ = EscargotShim::Global::flags()->isOn(
+ EscargotShim::Flag::Type::InternalLog);
+#endif
+}
+
+LogTRACE::LogTRACE(std::string id,
+ const char* functionName,
+ const char* filename,
+ const int line) {
+ if (!EscargotShim::Global::flags()->isOn(EscargotShim::Flag::Type::TraceCall,
+ id)) {
+ isEnabled_ = false;
+ return;
+ }
+
+ id_ = id;
+ functionName_ = createCodeLocation(functionName, filename, line);
+}
+
+void LogTRACE::printHeader(std::stringstream& stream) {
+ stream << CLR_DIM << PADDING(DISPLAY_TYPE_LENGTH_LIMIT) << "TRACE ("
+ << PADDING(DISPLAY_TRACE_ID_LENGTH_LIMIT)
+ << std::string(id_).substr(0, DISPLAY_TRACE_ID_LENGTH_LIMIT).c_str()
+ << ") ";
+
+ stream << IndentCounter::getString(id_) << functionName_ << " " << CLR_RESET;
+}
+
+// --- Logger ---
+thread_local std::shared_ptr<StdOut> s_loggerOutput;
+
+Logger::Logger(const std::string& header, std::shared_ptr<Output> out)
+ : out_(out) {
+ initialize(header, out);
+}
+
+Logger::Logger(LogFormatter&& formatter, std::shared_ptr<Output> out)
+ : out_(out) {
+ if (formatter.isEnabled()) {
+ initialize(formatter.header(), out_);
+ }
+}
+
+Logger::~Logger() {
+ if (out_ == nullptr) {
+ return;
+ }
+ stream_ << CLR_RESET << std::endl;
+ out_->flush(stream_);
+}
+
+void Logger::initialize(const std::string& header,
+ std::shared_ptr<Output> out) {
+ if (out_ == nullptr) {
+ if (s_loggerOutput == nullptr) {
+ s_loggerOutput = std::make_shared<StdOut>();
+ }
+ out_ = s_loggerOutput;
+ }
+ stream_ << header;
+}
+
+Logger& Logger::print(const char* string_without_format_specifiers) {
+ if (out_ == nullptr) {
+ return *this;
+ }
+
+ while (*string_without_format_specifiers) {
+ if (*string_without_format_specifiers == '%' &&
+ *(++string_without_format_specifiers) != '%') {
+ assert(((void)"runtime error: invalid format-string", false));
+ }
+ stream_ << *string_without_format_specifiers++;
+ }
+ return *this;
+}
+
+Logger& Logger::flush() {
+ if (out_) {
+ out_->flush(stream_);
+ }
+ stream_.str("");
+ return *this;
+}
+// --- Output ---
+
+void StdOut::flush(std::stringstream& stream) {
+ std::cout << stream.str();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+
+class LogFormatter {
+ public:
+ std::string header();
+ bool isEnabled() { return isEnabled_; }
+
+ protected:
+ virtual void printHeader(std::stringstream& stream) = 0;
+ bool isEnabled_{true};
+};
+
+class LogTYPED : public LogFormatter {
+ public:
+ enum class Type {
+ RAW,
+ INFO,
+ WARN,
+ ERROR,
+ };
+ LogTYPED(Type type = Type::RAW) : type_(type) {}
+ void printHeader(std::stringstream& stream) override;
+
+ protected:
+ Type type_{Type::RAW};
+};
+
+class LogINTERNAL : public LogTYPED {
+ public:
+ LogINTERNAL(Type type = Type::RAW);
+};
+
+class LogTRACE : public LogFormatter {
+ public:
+ LogTRACE(std::string id,
+ const char* functionName,
+ const char* filename,
+ const int line);
+ void printHeader(std::stringstream& stream) override;
+
+ private:
+ std::string id_;
+ std::string functionName_;
+};
+
+class Logger {
+ public:
+ class Output {
+ public:
+ virtual void flush(std::stringstream& ss) = 0;
+ };
+
+ Logger(const std::string& header = "", std::shared_ptr<Output> out = nullptr);
+ Logger(LogFormatter&& formatter, std::shared_ptr<Output> out = nullptr);
+ ~Logger();
+
+ template <class T>
+ Logger& operator<<(const T& msg) {
+ stream_ << msg;
+ return *this;
+ }
+
+ template <typename T, typename... Args>
+ Logger& print(const char* format, T value, Args... args) {
+ if (out_ == nullptr) {
+ return *this;
+ }
+
+ while (*format) {
+ if (*format == '%' && *(++format) != '%') {
+ stream_ << value;
+
+ // handle sub-specifiers
+ if ((*format == 'z')) {
+ format++;
+ } else if ((*format == 'l') || (*format == 'h')) {
+ format++;
+ if (*format == *(format + 1)) {
+ format++;
+ }
+ }
+ format++;
+
+ print(format, args...);
+ return *this;
+ }
+ stream_ << *format++;
+ }
+ assert(((void)"logical error: should not come here", false));
+ return *this;
+ };
+
+ Logger& print(const char* string_without_format_specifiers = "");
+ Logger& flush();
+
+ private:
+ std::shared_ptr<Output> out_;
+ std::stringstream stream_;
+
+ void initialize(const std::string& header, std::shared_ptr<Output> out);
+};
+
+class StdOut : public Logger::Output {
+ public:
+ void flush(std::stringstream& ss) override;
+};
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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 "logger-util.h"
+
+#include <assert.h>
+#include <regex>
+#include <set>
+#include <sstream>
+
+#include "api/global.h"
+
+std::string getPrettyFunctionName(const std::string fullname) {
+ std::smatch match;
+ const std::regex re(R"(([\w\:~]+)\()");
+
+ if (std::regex_search(fullname, match, re) && match.size() > 1) {
+ return match.str(1);
+ }
+ return "";
+}
+
+std::string createCodeLocation(const char* functionName,
+ const char* filename,
+ const int line) {
+ std::ostringstream oss;
+ oss << getPrettyFunctionName(functionName) << " (" << filename << ":" << line
+ << ")";
+ return oss.str();
+}
+
+thread_local int s_indentCount = 0;
+thread_local int s_deltaCount = 0;
+thread_local std::set<std::string> s_counterIds;
+
+#define FORCE_ENABLE_INDENT_ID "NODE"
+
+static bool isIndentIdEnabled(std::string id) {
+ if (id == FORCE_ENABLE_INDENT_ID) {
+ return true;
+ }
+
+ return EscargotShim::Global::flags()->isOn(
+ EscargotShim::Flag::Type::TraceCall, id);
+}
+
+void IndentCounter::indent(std::string id) {
+ if (isIndentIdEnabled(id) == false) {
+ return;
+ }
+ s_deltaCount++;
+}
+
+void IndentCounter::unIndent(std::string id) {
+ if (isIndentIdEnabled(id) == false) {
+ return;
+ }
+ s_deltaCount--;
+}
+
+IndentCounter::IndentCounter(std::string id) {
+ id_ = id;
+
+ if (isIndentIdEnabled(id) == false) {
+ return;
+ }
+
+ s_indentCount++;
+ s_counterIds.insert(id);
+}
+
+IndentCounter::~IndentCounter() {
+ if (isIndentIdEnabled(id_) == false) {
+ return;
+ }
+
+ s_indentCount--;
+ s_counterIds.erase(id_);
+}
+
+std::string IndentCounter::getString(std::string id) {
+ if (s_counterIds.find(id) == s_counterIds.end()) {
+ return "";
+ }
+
+ assert(s_indentCount >= 0);
+
+ std::ostringstream oss;
+ int indentCount = s_indentCount + s_deltaCount;
+
+ if (s_deltaCount > 0) {
+ oss << s_deltaCount << " ";
+ }
+
+ for (int i = 1; i < std::min(30, indentCount); ++i) {
+ oss << " ";
+ }
+
+ return oss.str();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022-present Samsung Electronics 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 <string>
+
+#define __FILE_NAME__ \
+ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+#define __FUNCTION_NAME__ getPrettyFunctionName(__PRETTY_FUNCTION__)
+
+#define __CODE_LOCATION__ \
+ createCodeLocation(__PRETTY_FUNCTION__, __FILE_NAME__, __LINE__).c_str()
+
+std::string createCodeLocation(const char* functionName,
+ const char* filename,
+ const int line);
+
+std::string getPrettyFunctionName(const std::string fullname);
+
+class IndentCounter {
+ public:
+ IndentCounter(std::string id);
+ ~IndentCounter();
+ static std::string getString(std::string id = "");
+ static void indent(std::string id);
+ static void unIndent(std::string id);
+
+ private:
+ std::string id_;
+};
+++ /dev/null
-/*
- * Copyright (c) 2021-present Samsung Electronics 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 "logger.h"
-#include <assert.h>
-#include <regex>
-#include <set>
-#include <sstream>
-
-std::string getPrettyFunctionName(const std::string fullname) {
- std::smatch match;
- const std::regex re(R"(([\w\:~]+)\()");
-
- if (std::regex_search(fullname, match, re) && match.size() > 1) {
- return match.str(1);
- }
- return "";
-}
-
-std::string createCodeLocation(const char* functionName,
- const char* filename,
- const int line) {
- std::ostringstream oss;
- oss << getPrettyFunctionName(functionName) << " (" << filename << ":" << line
- << ")" << std::endl;
- return oss.str();
-}
-
-thread_local int s_indentCount = 0;
-thread_local int s_deltaCount = 0;
-thread_local std::set<std::string> s_counterIds;
-
-#define FORCE_ENABLE_INDENT_ID "NODE"
-
-static bool isIndentIdEnabled(std::string id) {
- if (id == FORCE_ENABLE_INDENT_ID) {
- return true;
- }
- return EscargotShim::Flags::isTraceCallEnabled(id);
-}
-
-void IndentCounter::indent(std::string id) {
- if (isIndentIdEnabled(id) == false) return;
- s_deltaCount++;
-}
-
-void IndentCounter::unIndent(std::string id) {
- if (isIndentIdEnabled(id) == false) return;
- s_deltaCount--;
-}
-
-IndentCounter::IndentCounter(std::string id) {
- id_ = id;
-
- if (isIndentIdEnabled(id) == false) return;
-
- s_indentCount++;
- s_counterIds.insert(id);
-}
-
-IndentCounter::~IndentCounter() {
- if (isIndentIdEnabled(id_) == false) return;
-
- s_indentCount--;
- s_counterIds.erase(id_);
-}
-
-std::string IndentCounter::getString(std::string id) {
- if (s_counterIds.find(id) == s_counterIds.end()) {
- return "";
- }
-
- assert(s_indentCount >= 0);
-
- std::ostringstream oss;
- int indentCount = s_indentCount + s_deltaCount;
-
- if (s_deltaCount > 0) {
- oss << s_deltaCount << " ";
- }
-
- for (int i = 1; i < std::min(30, indentCount); ++i) {
- oss << " ";
- }
-
- return oss.str();
-}
#pragma once
-#include <string.h>
-#include <cstdio>
-
-#include "flags.h"
-
-#define CLR_RESET "\033[0m"
-#define CLR_DIM "\033[0;2m"
-#define CLR_RED "\033[0;31m"
-#define CLR_GREEN "\033[0;32m"
-#define CLR_GREY "\033[0;37m"
-#define CLR_BLACK "\033[0;30m"
-#define CLR_YELLOW "\033[0;33m"
-#define CLR_BLUE "\033[0;34m"
-#define CLR_MAGENTA "\033[0;35m"
-#define CLR_CYAN "\033[0;36m"
-#define CLR_DARKGREY "\033[01;30m"
-#define CLR_BRED "\033[01;31m"
-#define CLR_BYELLOW "\033[01;33m"
-#define CLR_BBLUE "\033[01;34m"
-#define CLR_BMAGENTA "\033[01;35m"
-#define CLR_BCYAN "\033[01;36m"
-#define CLR_BGREEN "\033[01;32m"
-#define CLR_WHITE "\033[01;37m"
-#define CLR_REDBG "\033[0;41m"
-
-std::string getPrettyFunctionName(const std::string fullname);
-std::string createCodeLocation(const char* functionName,
- const char* filename,
- const int line);
-inline const char* strBool(bool value) {
- return value ? "True" : "False";
-}
-
-class IndentCounter {
- public:
- IndentCounter(std::string id);
- ~IndentCounter();
- static std::string getString(std::string id = "");
- static void indent(std::string id);
- static void unIndent(std::string id);
-
- private:
- std::string id_;
-};
-
-#define __FILENAME__ \
- (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
-#define TRACE_FMT "%s (%s:%d)"
-#define TRACE_ARGS __PRETTY_FUNCTION__, __FILENAME__, __LINE__
-#define TRACE_ARGS2 \
- getPrettyFunctionName(__PRETTY_FUNCTION__).c_str(), __FILENAME__, __LINE__
-
-#define LWNODE_LOG_RAW(fmt, ...) \
- do { \
- fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
- } while (0);
-
-#if !defined(NDEBUG)
-#define LWNODE_LOG_INTERNAL(fmt, ...) LWNODE_LOG_RAW(fmt, ##__VA_ARGS__);
-#else
-#define LWNODE_LOG_INTERNAL(fmt, ...) \
- if (EscargotShim::Flags::isInternalLogEnabled()) { \
- LWNODE_LOG_RAW(fmt, ##__VA_ARGS__); \
- }
-#endif
-
-#define LWNODE_LOG_INFO(fmt, ...) \
- LWNODE_LOG_INTERNAL("INFO " fmt CLR_RESET, ##__VA_ARGS__);
-
-#define LWNODE_LOG_WARN(fmt, ...) \
- LWNODE_LOG_INTERNAL(CLR_YELLOW "WARN " fmt CLR_RESET, ##__VA_ARGS__);
-
+#include "color.h"
+#include "logger-impl.h"
+#include "logger-util.h"
+
+// loggers (release)
+#define LWNODE_LOG(tag) Logger(LogTYPED(LogTYPED::Type::tag))
+#define LWNODE_LOGF(tag, fmt, ...) LWNODE_LOG(tag).print(fmt, ##__VA_ARGS__)
+
+#define LWNODE_LOGR(fmt, ...) LWNODE_LOGF(RAW, fmt, ##__VA_ARGS__)
+#define LWNODE_LOGI(fmt, ...) LWNODE_LOGF(INFO, fmt, ##__VA_ARGS__)
+#define LWNODE_LOGW(fmt, ...) LWNODE_LOGF(WARN, fmt, ##__VA_ARGS__)
+#define LWNODE_LOGE(fmt, ...) LWNODE_LOGF(ERROR, fmt, ##__VA_ARGS__)
+
+// loggers (internal)
+// enabled if running with debug build or force-logging option
+#define LWNODE_LOG_INTERNAL(tag, fmt, ...) \
+ Logger(LogINTERNAL(LogTYPED::Type::tag)).print(fmt, ##__VA_ARGS__)
+
+#define LWNODE_LOG_RAW(fmt, ...) LWNODE_LOG_INTERNAL(RAW, fmt, ##__VA_ARGS__)
+#define LWNODE_LOG_INFO(fmt, ...) LWNODE_LOG_INTERNAL(INFO, fmt, ##__VA_ARGS__)
+#define LWNODE_LOG_WARN(fmt, ...) LWNODE_LOG_INTERNAL(WARN, fmt, ##__VA_ARGS__)
#define LWNODE_LOG_ERROR(fmt, ...) \
- LWNODE_LOG_INTERNAL(CLR_BRED "ERROR " fmt CLR_RESET, ##__VA_ARGS__);
+ LWNODE_LOG_INTERNAL(ERROR, fmt, ##__VA_ARGS__)
#define LWNODE_UNIMPLEMENT \
- LWNODE_LOG_INTERNAL(CLR_RED "UNIMPLEMENTED " TRACE_FMT CLR_RESET, \
- TRACE_ARGS2);
+ LWNODE_LOG_INTERNAL(RAW, CLR_RED "UNIMPLEMENTED " CLR_RESET) \
+ << CLR_RESET << __CODE_LOCATION__
#define LWNODE_UNIMPLEMENT_IGNORED \
- LWNODE_LOG_INTERNAL(CLR_DIM "UNIMPLEMENTED (IGNORED) " TRACE_FMT CLR_RESET, \
- TRACE_ARGS2);
+ LWNODE_LOG_INTERNAL(RAW, CLR_DIM "UNIMPLEMENTED (IGNORED) ") \
+ << CLR_RESET << __CODE_LOCATION__
#define LWNODE_UNIMPLEMENT_WORKAROUND \
- LWNODE_LOG_INTERNAL(CLR_DIM \
- "UNIMPLEMENTED (USE WORKAROUND) " TRACE_FMT CLR_RESET, \
- TRACE_ARGS2);
-
-// conditional loggers
+ LWNODE_LOG_INTERNAL(RAW, CLR_DIM "UNIMPLEMENTED (USE WORKAROUND) ") \
+ << CLR_RESET << __CODE_LOCATION__
+// loggers (debug)
#if !defined(NDEBUG)
#define LWNODE_DLOG_RAW(fmt, ...) LWNODE_LOG_RAW(fmt, ##__VA_ARGS__)
#define LWNODE_DLOG_INFO(fmt, ...) LWNODE_LOG_INFO(fmt, ##__VA_ARGS__)
#pragma once
-#include "flags.h"
#include "logger.h"
#if !defined(NDEBUG)
-#define FIRST_ARG(N, ...) N
-#define LEFT_ARGS(N, ...) , ##__VA_ARGS__
-#define TRACE_TAG_FMT CLR_DIM "TRACE (%-10s)"
-#define TRACE_TAG_ARG(id) std::string(id).substr(0, 10).c_str()
-#define COUNTER_FMT "%s"
-#define COUNTER_ARG(id) IndentCounter::getString(id).c_str()
-
+// LWNODE_CALL_TRACE with ID
#define LWNODE_CALL_TRACE_ID_LOG(id, ...) \
- if (EscargotShim::Flags::isTraceCallEnabled(#id)) { \
- LWNODE_DLOG_RAW(TRACE_TAG_FMT " " COUNTER_FMT TRACE_FMT \
- " " CLR_RESET FIRST_ARG(__VA_ARGS__) \
- CLR_RESET, \
- TRACE_TAG_ARG(#id), \
- COUNTER_ARG(#id), \
- TRACE_ARGS2 LEFT_ARGS(__VA_ARGS__)); \
- }
+ Logger(LogTRACE(#id, __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)) \
+ .print(__VA_ARGS__)
#define LWNODE_CALL_TRACE_ID(id, ...) \
IndentCounter __counter(#id); \
#define LWNODE_CALL_TRACE_ID_INDENT(id) IndentCounter::indent(#id);
#define LWNODE_CALL_TRACE_ID_UNINDENT(id) IndentCounter::unIndent(#id);
+// LWNODE_CALL_TRACE == LWNODE_CALL_TRACE_ID(COMMON, ...)
#define LWNODE_CALL_TRACE(msg, ...) \
LWNODE_CALL_TRACE_ID(COMMON, msg, ##__VA_ARGS__);
+
#define LWNODE_CALL_TRACE_LOG(msg, ...) \
LWNODE_CALL_TRACE_ID_LOG(COMMON, msg, ##__VA_ARGS__);
+
#define LWNODE_CALL_TRACE_INDENT() LWNODE_CALL_TRACE_ID_INDENT(COMMON);
#define LWNODE_CALL_TRACE_UNINDENT() LWNODE_CALL_TRACE_ID_UNINDENT(COMMON);
+// GC
#define LWNODE_CALL_TRACE_GC_START(msg, ...) \
- if (EscargotShim::Flags::isTraceCallEnabled("gc")) { \
- LWNODE_DLOG_INFO("GC: %s (%s:%d): " msg, TRACE_ARGS, ##__VA_ARGS__); \
- }
+ LWNODE_CALL_TRACE_ID_LOG(GC, "GC: %s", ##__VA_ARGS__)
#define LWNODE_CALL_TRACE_GC_END(msg, ...) \
- if (EscargotShim::Flags::isTraceCallEnabled("gc")) { \
- LWNODE_DLOG_INFO("GC: /%s (%s:%d): " msg, TRACE_ARGS, ##__VA_ARGS__); \
- }
+ LWNODE_CALL_TRACE_ID_LOG(GC, "GC: /%s", ##__VA_ARGS__)
#else
#define LWNODE_CALL_TRACE_ID(...)
#endif
#endif
+#if !defined(FALLTHROUGH) && defined(__has_cpp_attribute)
+#if __has_cpp_attribute(fallthrough)
+#define FALLTHROUGH [[fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+#define FALLTHROUGH [[gnu::fallthrough]]
+#endif
+#endif
+
+#if !defined(FALLTHROUGH) && defined(__has_attribute)
+#if __has_attribute(__fallthrough__)
+#define FALLTHROUGH __attribute__((__fallthrough__))
+#endif
+#endif
+
+#if !defined(FALLTHROUGH)
+#define FALLTHROUGH /* fallthrough */
+#endif
+
/* CHECK */
// Use CHECK when abort should occurs if the condition fails
#define CHECK_FMT CLR_REDBG "CHECK FAILED" CLR_RESET " "
#define _LWNODE_CHECK_FAILED_HANDLER(msg, ...) \
LWNODE_LOG_INTERNAL( \
- CHECK_FMT msg "\n\t " TRACE_FMT, ##__VA_ARGS__, TRACE_ARGS); \
+ RAW, CHECK_FMT msg "\n\t %s", ##__VA_ARGS__, __CODE_LOCATION__); \
EscargotShim::DebugUtils::printStackTrace(); \
std::abort();
#include "string-util.h"
+#include <sstream>
+
// Magic values subtracted from a buffer value during UTF8 conversion.
// This table contains as many values as there might be trailing bytes
// in a UTF-8 sequence.
return true;
}
+
+std::vector<std::string> strSplit(const std::string& str, char delimiter) {
+ std::vector<std::string> tokens;
+ std::stringstream ss(str);
+ std::string token;
+
+ while (getline(ss, token, delimiter)) {
+ tokens.push_back(token);
+ }
+
+ return tokens;
+}
#include "misc.h"
+#include <vector>
+
namespace EscargotShim {
class Constants {
public:
return strncmp(str, prefix, N - 1) == 0;
}
+std::vector<std::string> strSplit(const std::string& str, char delimiter);
+
class UTF8Sequence {
public:
static inline bool isASCII(uint16_t character) {
switch (length) {
case 6:
character += static_cast<uint8_t>(*sequence++);
- character <<= 6; // Fall through.
+ character <<= 6;
+ FALLTHROUGH;
case 5:
character += static_cast<uint8_t>(*sequence++);
- character <<= 6; // Fall through.
+ character <<= 6;
+ FALLTHROUGH;
case 4:
character += static_cast<uint8_t>(*sequence++);
- character <<= 6; // Fall through.
+ character <<= 6;
+ FALLTHROUGH;
case 3:
character += static_cast<uint8_t>(*sequence++);
- character <<= 6; // Fall through.
+ character <<= 6;
+ FALLTHROUGH;
case 2:
character += static_cast<uint8_t>(*sequence++);
- character <<= 6; // Fall through.
+ character <<= 6;
+ FALLTHROUGH;
case 1:
character += static_cast<uint8_t>(*sequence++);
}
--- /dev/null
+#include "base.h"
+
+#include "api.h"
+
+using namespace v8;
+
+namespace EscargotShim {
+
+EsScope::EsScope(v8::Isolate* isolate, const v8::Value* self) {
+ isolate_ = isolate != nullptr ? IsolateWrap::fromV8(isolate)
+ : IsolateWrap::GetCurrent();
+ initSelf(self);
+}
+
+EsScope::EsScope(const Local<Context>& context, const v8::Value* self) {
+ isolate_ = context.IsEmpty() ? IsolateWrap::GetCurrent()
+ : VAL(*context)->context()->GetIsolate();
+ initSelf(self);
+}
+
+EsScopeTemplate::EsScopeTemplate(const v8::Template* self) : EsScope() {
+ initSelf(self);
+}
+
+EsScopeTemplate::EsScopeTemplate(const v8::Local<v8::Context>& context,
+ const v8::Template* self)
+ : EsScope(context, nullptr) {
+ initSelf(self);
+}
+
+} // namespace EscargotShim
#pragma once
+#include "api.h"
#include "unimplemented.h"
+#include <EscargotPublic.h>
+#include <v8.h>
+
namespace EscargotShim {
class ValueWrap;
}
#define __TERMINATION_CHECK(lwIsolate, bailout_value) \
if (lwIsolate->IsExecutionTerminating()) { \
LWNODE_DLOG_WARN( \
- "%s (%s:%d) is ignored due to script execution being terminated.", \
- TRACE_ARGS2); \
+ "%s is ignored due to script execution being terminated.", \
+ __CODE_LOCATION__); \
return bailout_value; \
}
#if !defined(NDEBUG)
#define __DLOG_EVAL_EXCEPTION(eval_result) \
- LWNODE_DLOG_RAW("Execute:\n %s (%s:%d)\n%s", \
- TRACE_ARGS2, \
+ LWNODE_DLOG_RAW("Execute:\n %s\n%s", \
+ __CODE_LOCATION__, \
EvalResultHelper::getErrorString( \
lwIsolate->GetCurrentContext()->get(), eval_result) \
.c_str());
#define API_HANDLE_EXCEPTION(eval_result, lwIsolate, bailout_value) \
if (!eval_result.isSuccessful()) { \
__DLOG_EVAL_EXCEPTION(eval_result); \
- lwIsolate->SetPendingExceptionAndMessage(eval_result.error.get(), \
- eval_result.stackTraceData); \
- lwIsolate->ReportPendingMessages(); \
+ lwIsolate->handleException(eval_result); \
return bailout_value; \
}
+#define API_ENTER_AND_EXIT_IF_TERMINATING(ScopeType, context, returnValue) \
+ ScopeType scope(context, this); \
+ if (scope.isTerminating()) { \
+ return returnValue; \
+ }
+
+#define API_ENTER_NO_TERMINATION_CHECK(ScopeType, isolate) \
+ ScopeType scope(isolate, this);
+
+#define API_EXIT_IF_EXCEPTION_OCCURRED(r, returnValue) \
+ if (!scope.isSuccessful(r)) { \
+ scope.printDebug(r); \
+ return returnValue; \
+ }
+
// V has parameters (Type, type, TYPE, C type)
#define TYPED_ARRAYS(V) \
V(Uint8, uint8, UINT8, uint8_t) \
V(Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) \
V(BigUint64, biguint64, BIGUINT64, uint64_t) \
V(BigInt64, bigint64, BIGINT64, int64_t)
+
+namespace EscargotShim {
+
+class EsScope {
+ public:
+ EsScope(const v8::Value* self = nullptr) : EsScope(nullptr, self) {}
+ EsScope(v8::Isolate* isolate, const v8::Value* self = nullptr);
+ EsScope(const v8::Local<v8::Context>& context,
+ const v8::Value* self = nullptr);
+
+ Escargot::ValueRef* self() { return self_; }
+ virtual EscargotShim::IsolateWrap* lwIsolate() { return isolate_; }
+ virtual v8::Isolate* v8Isolate() { return isolate_->toV8(); }
+ virtual Escargot::ContextRef* context() {
+ if (context_) {
+ return context_->get();
+ }
+ return isolate_->GetCurrentContext()->get();
+ }
+
+ virtual ValueRef* asValue(const v8::Local<v8::Value>& value) {
+ return CVAL(*value)->value();
+ }
+
+ virtual ValueRef* asValue(const v8::Local<v8::Name>& value) {
+ return CVAL(*value)->value();
+ }
+
+ virtual StringRef* asValue(const v8::Local<v8::String>& value) {
+ return CVAL(*value)->value()->asString();
+ }
+
+ void printDebug(Escargot::Evaluator::EvaluatorResult& r) {
+#if !defined(NDEBUG)
+ LWNODE_DLOG_RAW("Execute:\n %s\n%s",
+ __CODE_LOCATION__,
+ EvalResultHelper::getErrorString(context(), r).c_str());
+#endif
+ }
+
+ bool isTerminating() { return isolate_->IsExecutionTerminating(); }
+
+ bool isSuccessful(Escargot::Evaluator::EvaluatorResult& r) {
+ if (!r.isSuccessful()) {
+ isolate_->SetPendingExceptionAndMessage(r.error.get(), r.stackTrace);
+ isolate_->ReportPendingMessages();
+ return false;
+ }
+ return true;
+ }
+
+ protected:
+ EscargotShim::IsolateWrap* isolate_ = nullptr;
+ EscargotShim::ContextWrap* context_ = nullptr;
+
+ private:
+ Escargot::ValueRef* self_ = nullptr;
+
+ void initSelf(const v8::Value* self) {
+ if (self) {
+ self_ = CVAL(self)->value();
+ }
+ }
+};
+
+class EsScopeTemplate : public EsScope {
+ public:
+ EsScopeTemplate(const v8::Template* self = nullptr);
+ EsScopeTemplate(const v8::Local<v8::Context>& context,
+ const v8::Template* self = nullptr);
+ EscargotShim::TemplateRef* self() { return self_; }
+
+ virtual FunctionTemplateRef* asFunctionTemplate(
+ const v8::Local<v8::FunctionTemplate>& value) {
+ return CVAL(*value)->ftpl();
+ }
+
+ protected:
+ EscargotShim::TemplateRef* self_ = nullptr;
+
+ private:
+ void initSelf(const v8::Template* self) {
+ if (self) {
+ self_ = CVAL(self)->tpl();
+ }
+ }
+};
+
+class EsScopeFunctionTemplate : public EsScopeTemplate {
+ public:
+ EsScopeFunctionTemplate(const v8::FunctionTemplate* self)
+ : EsScopeTemplate(self) {}
+ EsScopeFunctionTemplate(const v8::Local<v8::Context>& context,
+ const v8::FunctionTemplate* self)
+ : EsScopeTemplate(context, self) {}
+
+ EscargotShim::FunctionTemplateRef* self() {
+ LWNODE_CHECK(self_->isFunctionTemplate());
+ return reinterpret_cast<EscargotShim::FunctionTemplateRef*>(self_);
+ }
+
+ private:
+};
+
+class EsScopeObjectTemplate : public EsScopeTemplate {
+ public:
+ EsScopeObjectTemplate(const v8::ObjectTemplate* self)
+ : EsScopeTemplate(self) {}
+ EsScopeObjectTemplate(const v8::Local<v8::Context>& context,
+ const v8::ObjectTemplate* self)
+ : EsScopeTemplate(context, self) {}
+
+ EscargotShim::ObjectTemplateRef* self() {
+ LWNODE_CHECK(self_->isObjectTemplate());
+ return reinterpret_cast<EscargotShim::ObjectTemplateRef*>(self_);
+ }
+
+ private:
+};
+
+} // namespace EscargotShim
// Treat non-latin1 as UTF-8 and encode it as UTF-16 Little Endian.
if (encodingHint == Encoding::kUnknown) {
LWNODE_LOG_INFO("%s contains characters outside of the Latin1 range.",
- filename.c_str());
+ filename);
}
char* newStringBuffer = nullptr;
(const char*)bufferHolder.get(),
bufferSize);
if (isConverted == false) {
+ LWNODE_LOG_ERROR("convertUTF8ToUTF16le failed (%s)", filename);
return FileData();
}
} else {
if (encoding == Encoding::kLatin1) {
if (encodingHint == Encoding::kUnknown) {
- LWNODE_LOG_INFO("%s contains Latin1 characters.", filename.c_str());
+ LWNODE_LOG_INFO("%s contains Latin1 characters.", filename);
}
bufferSize = latin1String.length();
bufferHolder.reset(allocateStringBuffer(bufferSize + 1));
+ LWNODE_CHECK(bufferHolder.get() != nullptr);
((uint8_t*)bufferHolder.get())[bufferSize] = '\0';
memcpy(bufferHolder.get(), latin1String.data(), bufferSize);
std::unique_ptr<void, std::function<void(void*)>> bufferHolder(
buffer, freeStringBuffer);
- if (std::fread(buffer, sizeof(uint8_t), bufferSize, file) == 0) {
+ size_t readBytes = std::fread(buffer, sizeof(uint8_t), bufferSize, file);
+ if (readBytes == 0) {
return FileData();
}
+ LWNODE_CHECK(readBytes == bufferSize);
return Loader::createFileDataForReloadableString(
filename, std::move(bufferHolder), bufferSize, encodingHint);
loadCallback = [](void* userData) -> void* {
auto data = reinterpret_cast<Loader::ReloadableSourceData*>(userData);
- LWNODE_LOG_INFO(" Load: %d (%d) %p %s (+%.2f kB)",
- ++s_stat.loaded,
- s_stat.reloaded,
- data->preloadedData,
- data->path(),
- (float)data->preloadedDataLength() / 1024);
+ LWNODE_CALL_TRACE_ID(LOADER,
+ " Load: %d (%d) %p %s (+%.2f kB)",
+ ++s_stat.loaded,
+ s_stat.reloaded,
+ data->preloadedData,
+ data->path(),
+ (float)data->preloadedDataLength() / 1024);
if (data->preloadedData) {
auto buffer = data->preloadedData;
unloadCallback = [](void* preloadedData, void* userData) -> void {
auto data = reinterpret_cast<Loader::ReloadableSourceData*>(userData);
- LWNODE_LOG_INFO(" Unload: %d (%d) %p %s (-%.2f kB)",
- --s_stat.loaded,
- s_stat.reloaded,
- preloadedData,
- data->path(),
- (float)data->preloadedDataLength() / 1024);
+ LWNODE_CALL_TRACE_ID(LOADER,
+ "Unload: %d (%d) %p %s (-%.2f kB)",
+ --s_stat.loaded,
+ s_stat.reloaded,
+ preloadedData,
+ data->path(),
+ (float)data->preloadedDataLength() / 1024);
if (data->preloadedData) {
freeStringBuffer(data->preloadedData);
return ValueRef::createUndefined();
}
+static ValueRef* checkIfHandledAsOneByteString(ExecutionStateRef* state,
+ ValueRef* thisValue,
+ size_t argc,
+ ValueRef** argv,
+ bool isConstructCall) {
+ if (argc > 0 && argv[0]->isString()) {
+ return ValueRef::create(argv[0]->asString()->has8BitContent());
+ }
+ return ValueRef::createUndefined();
+}
+
void InitializeProcessMethods(Local<Object> target, Local<Context> context) {
auto esContext = CVAL(*context)->context()->get();
auto esTarget = CVAL(*target)->value()->asObject();
SetMethod(esContext, esTarget, "RssUsage", RssUsage);
SetMethod(esContext, esTarget, "PssSwapUsage", PssSwapUsage);
SetMethod(esContext, esTarget, "MemSnapshot", MemSnapshot);
+ SetMethod(esContext,
+ esTarget,
+ "checkIfHandledAsOneByteString",
+ checkIfHandledAsOneByteString);
#ifdef LWNODE_USE_RELOAD_SCRIPT
SetMethod(esContext,
esTarget,
void IdleGC(v8::Isolate* isolate) {
LWNODE_LOG_INFO("IdleGC");
- IsolateWrap::fromV8(isolate)->vmInstance()->enterIdleMode();
+ if (isolate) {
+ IsolateWrap::fromV8(isolate)->vmInstance()->enterIdleMode();
+ }
Escargot::Memory::gc();
malloc_trim(0);
}
+void initDebugger() {
+ auto lwIsolate = IsolateWrap::GetCurrent();
+ if (lwIsolate) {
+ lwIsolate->GetCurrentContext()->initDebugger();
+ }
+}
+
class MessageLoop::Internal {
public:
Internal() { gcStrategy_ = std::make_unique<DelayedGC>(); }
internal_->handleGC(isolate);
}
+Escargot::ContextRef* Utils::ToEsContext(v8::Context* context) {
+ return ContextWrap::fromV8(context)->get();
+}
+
+v8::Local<v8::Value> Utils::NewLocal(v8::Isolate* isolate,
+ Escargot::ValueRef* ptr) {
+ return v8::Utils::NewLocal<v8::Value>(isolate, ptr);
+}
+
} // namespace LWNode
+++ /dev/null
-/*
- * Copyright (c) 2019-present Samsung Electronics 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 <glib.h>
-#include <cassert>
-#include "uv.h"
-#include "node_bindings.h"
-#include "node_escargot_logger.h"
-#ifdef HOST_TIZEN
-#include "Extension.h"
-#endif
-
-namespace glib {
-
-using namespace nescargot;
-
-struct SourceData {
- GSource source;
- gpointer tag;
- NodeBindings* node_bindings;
-};
-
-// TODO: classify EventLoop if needed
-
-static GMainContext* gcontext;
-static GMainLoop* gmainLoop;
-static GSource* uvsource;
-static GSourceFuncs source_funcs;
-static bool gmainLoopDone = false;
-
-static gboolean GmainLoopPrepareCallback(GSource* source, gint* timeout) {
- uv_update_time(uv_default_loop());
- *timeout = uv_backend_timeout(uv_default_loop());
-
- if (!uv_watcher_queue_empty(uv_default_loop())) {
- return TRUE;
- }
-
- return 0 == *timeout;
-}
-
-static gboolean GmainLoopCheckCallback(GSource* source) {
- if (!uv_backend_timeout(uv_default_loop())) {
- return TRUE;
- }
-
- return (G_IO_IN ==
- g_source_query_unix_fd(source, ((SourceData*)source)->tag));
-}
-
-static gboolean GmainLoopDispatchCallback(GSource* source, GSourceFunc callback,
- gpointer user_data) {
- assert(gcontext);
- g_main_context_iteration(gcontext, FALSE);
-
- if (gmainLoopDone) {
- return G_SOURCE_REMOVE;
- }
-
- NodeBindings* node_bindings = ((SourceData*)source)->node_bindings;
-
- node_bindings->RunOnce();
-
- if (!node_bindings->HasMoreTasks()) {
- g_main_loop_quit(gmainLoop);
- return G_SOURCE_REMOVE;
- }
- return G_SOURCE_CONTINUE;
-}
-
-void GmainLoopInit(NodeBindings* self) {
- gcontext = g_main_context_default();
- gmainLoop = g_main_loop_new(gcontext, FALSE);
- source_funcs = {
- .prepare = GmainLoopPrepareCallback,
- .check = GmainLoopCheckCallback,
- .dispatch = GmainLoopDispatchCallback,
- };
-
- uvsource = g_source_new(&source_funcs, sizeof(SourceData));
- ((SourceData*)uvsource)->tag = g_source_add_unix_fd(
- uvsource, uv_backend_fd(uv_default_loop()),
- (GIOCondition)(G_IO_IN | G_IO_OUT | G_IO_ERR | G_IO_PRI));
- ((SourceData*)uvsource)->node_bindings = self;
-
- g_source_attach(uvsource, gcontext);
- g_source_unref(uvsource);
-
-#ifdef HOST_TIZEN
- DeviceAPI::ESPostMessageListener::SetMainThreadIdlerRegister(
- [](DeviceAPI::ESPostMessageListener::Idler_t idler, void* data) {
- g_idle_add(idler, data);
- });
-#endif
-}
-
-void GmainLoopStart() {
- assert(gmainLoop);
- assert(gcontext);
-
- g_main_loop_run(gmainLoop);
- gmainLoopDone = true;
- g_main_context_iteration(gcontext, TRUE);
-}
-
-void GmainLoopExit() {
- if (uvsource) {
- g_source_destroy(uvsource);
- }
- if (gmainLoop) {
- g_main_loop_unref(gmainLoop);
- }
- if (gcontext) {
- g_main_context_unref(gcontext);
- }
-}
-
-} // namespace glib
-
-#ifdef HOST_TIZEN
-#include "Queue.hpp"
-#include <mutex>
-#include <thread>
-
-#define NESCARGOT_AUL_TERMINATION_MESSAGE "AUL_TERMINATION"
-
-namespace nescargot {
-
-struct Task {
- std::string data;
-};
-
-Queue<Task> g_queue;
-
-static void UvNoOp(uv_async_t* handle) {
- uv_close((uv_handle_t*)handle,
- [](uv_handle_t* handle) { delete (uv_async_t*)handle; });
-}
-
-void push_aul_message(const char* message) {
- g_queue.push({.data = message});
- // wake up the uv queue
- uv_async_t* async = new uv_async_t;
- uv_async_init(uv_default_loop(), async, UvNoOp);
- uv_async_send(async);
-}
-
-void push_aul_termination_message() {
- push_aul_message(NESCARGOT_AUL_TERMINATION_MESSAGE);
-}
-} // namespace nescargot
-#endif
-
-namespace nescargot {
-
-static void pump_aul_message(v8::Isolate* isolate, NodeBindings* bindings) {
-#ifdef HOST_TIZEN
- // TODO: move the following to m_platform.PumpMessageLoop(isolate)
- if (!g_queue.empty()) {
- auto task = g_queue.pop();
- node::EmitMessage(isolate, task.data.c_str());
- if (task.data == std::string(NESCARGOT_AUL_TERMINATION_MESSAGE)) {
- bindings->TerminateGMainLoop();
- }
- }
-#endif
-}
-
-NodeBindings::NodeBindings() {}
-
-void NodeBindings::Initialize(Environment&& env, Platform&& platform,
- Node&& node) {
- assert(platform.PumpMessageLoop);
- assert(platform.EnterIdleMode);
-
- m_env = std::move(env);
- m_platform = std::move(platform);
- m_node = std::move(node);
- m_isInitialize = true;
-}
-
-void NodeBindings::StartEventLoop() {
- assert(m_isInitialize);
-
- glib::GmainLoopInit(this);
-
- RunOnce();
-
- // NOTE: We try an intense memory saving mode called Idle Mode.
- // It will be exited once any javascript operation runs.
- m_platform.EnterIdleMode(m_env.isolate());
-
- if (HasMoreTasks()) {
- glib::GmainLoopStart();
- }
-
- glib::GmainLoopExit();
-}
-
-bool NodeBindings::HasMoreTasks() {
- return (m_hasMoreNodeTasks && !m_isTerminated);
-}
-
-void NodeBindings::RunOnce() {
- auto isolate = m_env.isolate();
- auto event_loop = m_env.event_loop();
-
- m_platform.PumpMessageLoop(isolate);
- pump_aul_message(isolate, this);
-
- bool more = uv_run(event_loop, UV_RUN_NOWAIT);
-
- if (more == false) {
- m_platform.PumpMessageLoop(isolate);
- pump_aul_message(isolate, this);
-
- m_node.EmitBeforeExit();
-
- // Emit `beforeExit` if the loop became alive either after emitting
- // event, or after running some callbacks.
- more = uv_loop_alive(event_loop);
- if (uv_run(event_loop, UV_RUN_NOWAIT) != 0) {
- more = true;
- }
- }
- m_hasMoreNodeTasks = more;
-
- if (m_idleCheckTimeoutID) {
- g_source_remove(m_idleCheckTimeoutID);
- }
-
-#ifndef IDLE_CHECK_TIMEOUT
-#define IDLE_CHECK_TIMEOUT 500
-#endif
- m_idleCheckTimeoutID = g_timeout_add(
- IDLE_CHECK_TIMEOUT,
- [](gpointer data) -> gboolean {
- NodeBindings* self = (NodeBindings*)data;
- self->m_idleCheckTimeoutID = 0;
- self->m_platform.EnterIdleMode(self->m_env.isolate());
- return G_SOURCE_REMOVE;
- },
- this);
-}
-
-} // namespace nescargot
+++ /dev/null
-/*
- * Copyright (c) 2019-present Samsung Electronics 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 NODE_BINDINGS_H
-#define NODE_BINDINGS_H
-
-#include <functional>
-
-namespace v8 {
-class Isolate;
-}
-
-namespace node {
-class Environment;
-void EmitMessage(v8::Isolate* isolate, const char* fmt, ...);
-} // namespace node
-
-typedef struct uv_loop_s uv_loop_t;
-
-namespace nescargot {
-
-void push_aul_message(const char* message);
-void push_aul_termination_message();
-
-class NodeBindings {
- public:
- NodeBindings();
- virtual ~NodeBindings(){};
-
- struct Platform {
- void (*PumpMessageLoop)(v8::Isolate* isolate);
- void (*EnterIdleMode)(v8::Isolate* isolate);
- };
-
- struct Environment {
- std::function<v8::Isolate*()> isolate;
- std::function<uv_loop_t*()> event_loop;
- };
-
- struct Node {
- std::function<void()> EmitBeforeExit;
- };
-
- void Initialize(Environment&& env, Platform&& platform, Node&& node);
- void StartEventLoop();
- void RunOnce();
- bool HasMoreTasks();
- void TerminateGMainLoop() { m_isTerminated = true; }
-
- private:
- Environment m_env;
- Platform m_platform;
- Node m_node;
- bool m_isInitialize = {false};
- bool m_hasMoreNodeTasks = {true};
- bool m_isTerminated = {false};
- unsigned int m_idleCheckTimeoutID = {0};
-};
-
-} // namespace nescargot
-
-#endif
--- /dev/null
+#!/bin/bash
+
+# Copyright (c) 2020-present Samsung Electronics 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.
+
+set -e
+
+[[ -z $TEST_ROOT ]] && TEST_ROOT=$(pwd)/test
+[[ -z $SKIP_TESTS_PATH ]] && SKIP_TESTS_PATH=$TEST_ROOT/skip_tests.txt
+[[ -z $UNSUPPORTED_TESTS_PATH ]] && UNSUPPORTED_TESTS_PATH=$TEST_ROOT/skip_features.txt
+# SKIP_TESTS_PATH=./skip_list.gen.txt
+
+echo root: $TEST_ROOT
+echo skip: $SKIP_TESTS_PATH
+echo drop: $UNSUPPORTED_TESTS_PATH
+
+function cleanup()
+{
+ local user=$(whoami)
+
+ rm -rf $TEST_ROOT/.tmp.*
+
+ if [[ -z $VM_PATH ]]; then
+ ps -fu $user | grep 'Release\/lwnode' | grep -v grep | awk '{print $2" "$3}' | xargs -r kill -9
+ else
+ ps -fu $user | grep $VM_PATH | grep -v grep | awk '{print $2" "$3}' | xargs -r kill -9
+ fi
+ # ps -fu $user | grep '.tmp.' | grep -v grep | awk '{print $2" "$3}' | xargs -r kill -9
+ ps -fu $user | grep 'defunct' | grep -v grep | awk '{print $2" "$3}' | xargs -r kill -9
+
+ echo -e "cleanup remaining processes"
+}
+
+trap cleanup SIGINT EXIT
+
+cleanup
+
+ARGS=$*
+SKIP_TEST_OPTION=
+UNSUPPORTED_TEST_OPTION=
+
+[[ -f $SKIP_TESTS_PATH ]] && \
+ SKIP_TEST_OPTION="--skip-tests=$(sed 's/\s#.*//g' $SKIP_TESTS_PATH | paste -sd,)"
+[[ -f $UNSUPPORTED_TESTS_PATH ]] && \
+ UNSUPPORTED_TEST_OPTION="--unsupported-tests=$(sed '/#\|^$/d' $UNSUPPORTED_TESTS_PATH | paste -sd,)"
+
+if [[ -z ${ARGS[0]} ]]; then
+ ARGS="test/parallel test/regression"
+else
+ SKIP_TEST_OPTION=""
+fi
+
+tools/test.py -J -p color --report --time \
+ --timeout 60 --repeat 1 \
+ --test-root=$TEST_ROOT \
+ $SKIP_TEST_OPTION \
+ $UNSUPPORTED_TEST_OPTION \
+ $ARGS
BuildRequires: python
BuildRequires: ninja
BuildRequires: zip
+BuildRequires: rsync
BuildRequires: pkgconfig(dlog)
BuildRequires: pkgconfig(aul)
BuildRequires: pkgconfig(capi-appfw-app-common)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: nghttp2-devel
BuildRequires: pkgconfig(libcares)
+BuildRequires: pkgconfig(sqlite3)
%if (0%{?tizen_version_major} >= 6)
BuildRequires: pkgconfig(openssl1.1)
%endif
%endif
+%if 0%{?asan} == 1
+BuildRequires: libasan
+%endif
+
##############################################
# Packages for profiles
##############################################
Development files for Lightweight node.js.
# Initialize the variables
-%{!?node_engine: %define node_engine escargot}
+%{!?target: %define target lwnode} #taget = [lwnode/v8/modules/apps]
%{!?lib_type: %define lib_type shared}
%{!?feature_mode: %define feature_mode production}
+%{!?app_name: %define app_name unkown}
%description
Node.js on Escargot is a memory efficient node.js implementation,
by Samsung Research, instead of the default V8 JS engine.
+# Add subpackage for apps
+%if "%{target}" == "apps"
+%package %{app_name}
+Summary: lwnode apps
+%description %{app_name}
+lwnode %{app_name} app
+
+# variables related to app
+%define project_path %{_builddir}/%{name}-%{version}
+%define local_app_path %{project_path}/lwnode/apps/%{app_name}
+%define app_out_path %{project_path}/out/apps/%{app_name}
+%define app_files_path /tmp/%{app_name}/files
+%define target_app_path /usr/apps/lwnode/apps/%{app_name}
+%define app_variables BUILD_OUT_PATH=%{app_out_path} APP_PATH=%{local_app_path} FILES_PATH=%{buildroot}%{app_files_path}
+%define app_post_variables APP_PATH=%{target_app_path} FILES_PATH=%{app_files_path}
+%endif
+
%prep
%setup -q
%endif
%if 0%{?asan} == 1
-CFLAGS+="-fsanitize=address -fsanitize-recover=address -U_FORTIFY_SOURCE -fno-omit-frame-pointer -fno-common"
-CXXFLAGS+="-fsanitize=address -fsanitize-recover=address -U_FORTIFY_SOURCE -fno-omit-frame-pointer -fno-common"
-LDFLAGS+="-fsanitize=address"
+%define asan_config --enable-asan
%endif
-%if "%{node_engine}" == "escargot"
-%define target lwnode
+%if "%{target}" == "lwnode"
%define target_lib liblwnode
%define target_src out/tizen/Release
-%define engine_config --without-bundled-v8 --engine escargot
+
+%if %{?static_escargot:0}%{!?static_escargot:1}
+ %define engine_config --without-bundled-v8 --engine escargot
%else
+ %define engine_config --without-bundled-v8 --engine escargot --static-escargot
+%endif
+%endif
+
+%if "%{target}" == "v8"
%define target node
%define target_src out/v8/Release
%endif
%define extra_config --escargot-threading
%endif
-echo "Building:" %{target}
-CFLAGS+=' -Os '
-CXXFLAGS+=' -Os '
+echo "Build Target:" %{target}
+echo $CFLAGS
+
+%if "%{target}" == "modules"
+./lwnode/build-modules.sh %{?modules_list} --os=tizen
+%endif
+%if "%{target}" == "lwnode" || "%{target}" == "v8"
# building liblwnode.so
./configure --tizen --without-npm \
--without-inspector --without-node-code-cache --without-node-snapshot \
--with-intl none %{?libshared} \
--enable-reload-script --enable-external-builtin-script \
--dest-os linux --dest-cpu '%{tizen_arch}' \
- --ninja %{?engine_config} %{?extra_config} %{?lib_type_config}
+ --ninja %{?engine_config} %{?extra_config} %{?lib_type_config} %{?asan_config}
-%if "%{node_engine}" == "escargot" && "%{lib_type}" == "shared"
+%if "%{target}" == "lwnode" && "%{lib_type}" == "shared"
ninja -C %{target_src} %{target_lib}
%endif
--with-intl none %{?libshared} \
--enable-reload-script --enable-external-builtin-script \
--dest-os linux --dest-cpu '%{tizen_arch}' \
- --ninja %{?engine_config} %{?extra_config}
+ --ninja %{?engine_config} %{?extra_config} %{?asan_config}
ninja -C %{target_src} %{target}
+%endif
+
+%if "%{target}" == "apps"
+%{app_variables} %{local_app_path}/build/build.sh
+%endif
##############################################
mkdir -p %{buildroot}%{_libdir}
rm -f %{target_src}/lib/*.tmp %{target_src}/lib/*.TOC
-%if "%{node_engine}" == "escargot"
- cp %{target_src}/gen/escargot/libescargot.so %{buildroot}%{_libdir}
+%if "%{target}" == "lwnode"
+ %if %{?static_escargot:0}%{!?static_escargot:1}
+ cp %{target_src}/gen/escargot/libescargot.so %{buildroot}%{_libdir}
+ %endif
%if "%{lib_type}" == "shared"
cp %{target_src}/lib/liblwnode.so* %{buildroot}%{_libdir}
%endif
-%endif
# for devel files
-strip -v -g %{target_src}/%{target}
+%if %{?debug_symbols:0}%{!?debug_symbols:1}
+ strip -v -g %{target_src}/%{target}
+%endif
+
cp %{target_src}/%{target} %{buildroot}%{_bindir}
cp %{target_src}/%{target}.dat %{buildroot}%{_bindir}
+%endif # "%{target}" == "lwnode"
+
+%if "%{target}" == "apps"
+rm -rf %{buildroot}%{app_files_path}
+mkdir -p %{buildroot}%{app_files_path}
+
+%{app_variables} %{local_app_path}/build/install.sh
+mkdir -p %{buildroot}%{target_app_path}/build
+cp %{local_app_path}/build/post.sh %{buildroot}%{target_app_path}/build
+cp %{local_app_path}/build/%{app_name}.manifest %{buildroot}%{target_app_path}
+
+%endif
+
%clean
rm -fr ./*.list
rm -fr ./*.manifest
%postun
/sbin/ldconfig
-
##############################################
## Packaging rpms
##############################################
%files
%manifest packaging/%{name}.manifest
%defattr(-,root,root,-)
-%if "%{node_engine}" == "escargot"
- %{_libdir}/libescargot.so
+%if "%{target}" == "lwnode"
+ %if %{?static_escargot:0}%{!?static_escargot:1}
+ %{_libdir}/libescargot.so
+ %endif
%if "%{lib_type}" == "shared"
%{_libdir}/liblwnode.so*
%endif
%files devel
%manifest packaging/%{name}.manifest
-%{_bindir}/%{target}
-%{_bindir}/%{target}.dat
+%if "%{target}" == "lwnode"
+ %{_bindir}/%{target}
+ %{_bindir}/%{target}.dat
+%endif
+
+%if "%{target}" == "apps"
+%post %{app_name}
+%{app_post_variables} %{target_app_path}/build/post.sh
+
+%files %{app_name}
+%{target_app_path}
+%{app_files_path}
+%endif
#ifdef NAPI_EXPERIMENTAL
typedef struct {
- uint64_t lower;
- uint64_t upper;
+ uint64_t lower = 0;
+ uint64_t upper = 0;
} napi_type_tag;
#endif // NAPI_EXPERIMENTAL
#include "node_revert.h"
#include "node_v8_platform-inl.h"
#include "node_version.h"
+#ifdef LWNODE // @lwnode
+#include "node_main_lw_runner-inl.h"
+#endif
#if HAVE_OPENSSL
#include "allocated_buffer-inl.h" // Inlined functions needed by node_crypto.h
if (result.early_return) {
return result.exit_code;
}
-#ifdef LWNODE
- // @lwndoe
- ArrayBufferAllocator* allocator = nullptr;
- // end of @lwnode
+
+ #if defined(LWNODE)
+ LWNode::LWNodeMainRunner nodeMainRunner; // @lwndoe
{
Isolate::CreateParams params;
const std::vector<size_t>* indexes = nullptr;
result.exec_args,
indexes);
- // @lwnode
- allocator = main_instance.arrayBufferAllocator();
- // endof @lwnode
- result.exit_code = main_instance.Run();
+ result.exit_code = nodeMainRunner.Run(main_instance); // @lwndoe
}
-
- TearDownOncePerProcess();
- // @lwnode
- v8::V8::ShutdownPlatform();
- delete allocator;
- // end of @lwnode
#else
- {
+ {
Isolate::CreateParams params;
const std::vector<size_t>* indexes = nullptr;
std::vector<intptr_t> external_references;
indexes);
result.exit_code = main_instance.Run();
}
+#endif
TearDownOncePerProcess();
-#endif
return result.exit_code;
}
new NodeMainInstance(isolate, event_loop, platform, args, exec_args));
}
-#ifdef LWNODE
NodeMainInstance::NodeMainInstance(
Isolate::CreateParams* params,
uv_loop_t* event_loop,
const std::vector<size_t>* per_isolate_data_indexes)
: args_(args),
exec_args_(exec_args),
-// @lwnode
-#if 0
array_buffer_allocator_(ArrayBufferAllocator::Create()),
-#endif
- array_buffer_allocator_(ArrayBufferAllocator::Create().release()),
isolate_(nullptr),
platform_(platform),
isolate_data_(nullptr),
owns_isolate_(true) {
-// @lwnode
-#if 0
params->array_buffer_allocator = array_buffer_allocator_.get();
-#endif
- params->array_buffer_allocator = array_buffer_allocator_;
isolate_ = Isolate::Allocate();
CHECK_NOT_NULL(isolate_);
// Register the isolate on the platform before the isolate gets initialized,
isolate_data_ = std::make_unique<IsolateData>(isolate_,
event_loop,
platform,
-// @lwnode
-#if 0
array_buffer_allocator_.get(),
-#endif
- array_buffer_allocator_,
per_isolate_data_indexes);
IsolateSettings s;
SetIsolateMiscHandlers(isolate_, s);
}
}
-#else
-
-NodeMainInstance::NodeMainInstance(
- Isolate::CreateParams* params,
- uv_loop_t* event_loop,
- MultiIsolatePlatform* platform,
- const std::vector<std::string>& args,
- const std::vector<std::string>& exec_args,
- const std::vector<size_t>* per_isolate_data_indexes)
- : args_(args),
- exec_args_(exec_args),
- array_buffer_allocator_(ArrayBufferAllocator::Create()),
- isolate_(nullptr),
- platform_(platform),
- isolate_data_(nullptr),
- owns_isolate_(true) {
- params->array_buffer_allocator = array_buffer_allocator_.get();
- isolate_ = Isolate::Allocate();
- CHECK_NOT_NULL(isolate_);
- // Register the isolate on the platform before the isolate gets initialized,
- // so that the isolate can access the platform during initialization.
- platform->RegisterIsolate(isolate_, event_loop);
- SetIsolateCreateParamsForNode(params);
- Isolate::Initialize(isolate_, *params);
-
- deserialize_mode_ = per_isolate_data_indexes != nullptr;
- // If the indexes are not nullptr, we are not deserializing
- CHECK_IMPLIES(deserialize_mode_, params->external_references != nullptr);
- isolate_data_ = std::make_unique<IsolateData>(isolate_,
- event_loop,
- platform,
- array_buffer_allocator_.get(),
- per_isolate_data_indexes);
- IsolateSettings s;
- SetIsolateMiscHandlers(isolate_, s);
- if (!deserialize_mode_) {
- // If in deserialize mode, delay until after the deserialization is
- // complete.
- SetIsolateErrorHandlers(isolate_, s);
- }
-}
-
-#endif
-
void NodeMainInstance::Dispose() {
CHECK(!owns_isolate_);
platform_->DrainTasks(isolate_);
}
#endif
-// @lwnode
-// We prevent premature termination when detecting leak,
-// as our GC runs after shutting down node platform.
-#if 0
#if defined(LEAK_SANITIZER)
__lsan_do_leak_check();
-#endif
#endif
return exit_code;
#include "uv.h"
#include "v8.h"
+// @lwnodes
+namespace LWNode {
+ class LWNodeMainRunner;
+}
+
namespace node {
// TODO(joyeecheung): align this with the Worker/WorkerThreadData class.
std::vector<std::string> args_;
std::vector<std::string> exec_args_;
-#if LWNODE
- // @lwnode
- public:
- ArrayBufferAllocator* arrayBufferAllocator() {
- return array_buffer_allocator_;
- }
-
- private:
- ArrayBufferAllocator* array_buffer_allocator_;
-#else
std::unique_ptr<ArrayBufferAllocator> array_buffer_allocator_;
-#endif
v8::Isolate* isolate_;
MultiIsolatePlatform* platform_;
std::unique_ptr<IsolateData> isolate_data_;
bool owns_isolate_ = false;
bool deserialize_mode_ = false;
+
+ friend class LWNode::LWNodeMainRunner; // @lwnodes
};
} // namespace node
--- /dev/null
+#pragma once
+#include "node_bindings.h"
+#include "node_internals.h"
+#include "node_main_instance.h"
+#include "node_options-inl.h"
+#include "node_v8_platform-inl.h"
+#include "util-inl.h"
+
+#if defined(LEAK_SANITIZER)
+#include <sanitizer/lsan_interface.h>
+#endif
+
+using v8::Context;
+using v8::HandleScope;
+using v8::Isolate;
+using v8::Local;
+using v8::Locker;
+using v8::SealHandleScope;
+using namespace node;
+
+namespace LWNode {
+
+class MainLoopStrategy {
+ public:
+ virtual void RunLoop(node::Environment* env) = 0;
+};
+
+class GmainLoopStrategy : public MainLoopStrategy, public GmainLoopWork {
+ public:
+ void RunLoop(node::Environment* env) override {
+ CHECK_NOT_NULL(env);
+ env_ = env;
+
+ LWNode::GmainLoopNodeBindings node_bindings(this);
+ node_bindings.StartEventLoop();
+ }
+
+ bool RunOnce() override {
+ CHECK_NOT_NULL(env_);
+
+ auto event_loop = env_->event_loop();
+
+ uv_run(event_loop, UV_RUN_NOWAIT);
+
+ per_process::v8_platform.DrainVMTasks(env_->isolate());
+
+ bool more = uv_loop_alive(event_loop);
+ if (more && !env_->is_stopping()) {
+ return true;
+ }
+
+ if (!uv_loop_alive(event_loop)) {
+ EmitBeforeExit(env_);
+ }
+
+ more = uv_loop_alive(event_loop);
+
+ return (more == true && !env_->is_stopping());
+ }
+
+ private:
+ node::Environment* env_ = nullptr;
+};
+
+class LoopStrategy : public MainLoopStrategy {
+ void RunLoop(node::Environment* env) override {
+ bool more;
+ do {
+ uv_run(env->event_loop(), UV_RUN_DEFAULT);
+
+ per_process::v8_platform.DrainVMTasks(env->isolate());
+
+ more = uv_loop_alive(env->event_loop());
+ if (more && !env->is_stopping()) continue;
+
+ if (!uv_loop_alive(env->event_loop())) {
+ EmitBeforeExit(env);
+ }
+
+ // Emit `beforeExit` if the loop became alive either after emitting
+ // event, or after running some callbacks.
+ more = uv_loop_alive(env->event_loop());
+ } while (more == true && !env->is_stopping());
+ }
+};
+
+class LWNodeMainRunner {
+ public:
+ ~LWNodeMainRunner() { v8::V8::ShutdownPlatform(); }
+
+ int Run(node::NodeMainInstance& nodeMainInstance) {
+ // To release array buffer allocator after node is finished,
+ // this runner should has it.
+ array_buffer_allocator_ =
+ std::move(nodeMainInstance.array_buffer_allocator_);
+
+ v8::Isolate* isolate_ = nodeMainInstance.isolate_;
+
+ Locker locker(isolate_);
+ Isolate::Scope isolate_scope(isolate_);
+ HandleScope handle_scope(isolate_);
+
+ int exit_code = 0;
+ DeleteFnPtr<Environment, FreeEnvironment> env_ =
+ nodeMainInstance.CreateMainEnvironment(&exit_code);
+
+ CHECK_NOT_NULL(env_);
+ Context::Scope context_scope(env_->context());
+
+ if (exit_code == 0) {
+ LoadEnvironment(env_.get());
+
+ env_->set_trace_sync_io(env_->options()->trace_sync_io);
+
+ {
+ SealHandleScope seal(isolate_);
+ env_->performance_state()->Mark(
+ node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
+
+ // Run main loop
+ std::unique_ptr<MainLoopStrategy> mainLoop =
+ std::make_unique<LoopStrategy>();
+ mainLoop->RunLoop(env_.get());
+
+ env_->performance_state()->Mark(
+ node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
+ }
+
+ env_->set_trace_sync_io(false);
+ exit_code = EmitExit(env_.get());
+ }
+
+ ResetStdio();
+
+ // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
+ // make sense here.
+#if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
+ if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF) continue;
+ act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
+ CHECK_EQ(0, sigaction(nr, &act, nullptr));
+ }
+#endif
+
+ // @lwnode
+ // We prevent premature termination when detecting leak,
+ // as our GC runs after shutting down node platform.
+ LWNode::IdleGC();
+#if defined(LEAK_SANITIZER)
+ __lsan_do_leak_check();
+#endif
+ return exit_code;
+ }
+
+ private:
+ std::unique_ptr<node::ArrayBufferAllocator> array_buffer_allocator_;
+};
+
+} // namespace LWNode
MaybeLocal<String> NativeModuleLoader::LoadBuiltinModuleSource(Isolate* isolate,
const char* id) {
-#ifdef LWNODE_EXTERNAL_BUILTINS_FILENAME
- return LoadExternalBuiltinSource(isolate, id);
-#endif
-
#ifdef NODE_BUILTIN_MODULES_PATH
std::string filename = OnDiskFileName(id);
return String::NewFromUtf8(
isolate, contents.c_str(), v8::NewStringType::kNormal, contents.length());
-#else
+#elif defined(LWNODE_EXTERNAL_BUILTINS_FILENAME)
+ return LoadExternalBuiltinSource(isolate, id);
+#else // LWNODE_EXTERNAL_BUILTINS_FILENAME
const auto source_it = source_.find(id);
CHECK_NE(source_it, source_.end());
return source_it->second.ToStringChecked(isolate);
uLong uncompressedSize{0};
};
-static ArchiveFileScope s_archiveFileScope;
-static std::map<std::string, UnzFileCachedInfo> s_unzFileInfoDictionary;
+enum class ReaderError {
+ NO_ERROR = 0,
+ UNZ_OPEN_CURRENTFILE,
+ UNZ_GOTO_FIRSTFILE,
+ UNZ_GET_CURRENTFILEINFO,
+ READ_CURRENTFILE_FROMARCHIVE,
+ READ_FILE_FROMARCHIVE
+};
+
+static thread_local ArchiveFileScope s_archiveFileScope;
+static thread_local std::map<std::string, UnzFileCachedInfo>
+ s_unzFileInfoDictionary;
+static thread_local ReaderError s_lastError = ReaderError::NO_ERROR;
std::string getSelfProcPath() {
char path[PATH_MAX + 1];
return std::string(path);
}
+void setError(ReaderError error) {
+ s_lastError = error;
+ ERROR_AND_ABORT(s_lastError);
+}
+
bool readCurrentFileFromArchive(const unzFile file,
uLong uncompressedSize,
char** buffer,
size_t* fileSize) {
if (unzOpenCurrentFile(file) < 0) {
+ setError(ReaderError::UNZ_OPEN_CURRENTFILE);
return false;
}
// 2. read the data by searching the file position from the first one.
if (unzGoToFirstFile(file) < 0) {
+ setError(ReaderError::UNZ_OPEN_CURRENTFILE);
return false;
}
0,
nullptr,
0) < 0) {
+ setError(ReaderError::UNZ_GET_CURRENTFILEINFO);
return false;
}
// 2.1 read the data from the current file poistion
if (readCurrentFileFromArchive(
file, fileInfo.uncompressed_size, buffer, fileSize) == false) {
+ setError(ReaderError::READ_CURRENTFILE_FROMARCHIVE);
return false;
}
if (readFileFromArchive(
s_externalBuiltinsPath, filename, &buffer, &bufferSize) == false) {
+ setError(ReaderError::READ_FILE_FROMARCHIVE);
return FileData();
}
if (readFileFromArchive(
s_externalBuiltinsPath, filename, &buffer, &bufferSize) == false) {
+ LWNODE_LOG_ERROR("readFileFromArchive (%s) failed:", filename);
+ setError(ReaderError::READ_FILE_FROMARCHIVE);
return FileData();
}
const path = require('path');
const { isDefiningError } = require('./rules-utils.js');
-const doc = fs.readFileSync(path.resolve(__dirname, '../../doc/api/errors.md'),
+// @lwnode
+const doc = fs.readFileSync(path.resolve(__dirname, './errors.md'),
'utf8');
function isInDoc(code) {
--- /dev/null
+# Errors
+
+<!--introduced_in=v4.0.0-->
+<!--type=misc-->
+
+Applications running in Node.js will generally experience four categories of
+errors:
+
+* Standard JavaScript errors such as {EvalError}, {SyntaxError}, {RangeError},
+ {ReferenceError}, {TypeError}, and {URIError}.
+* System errors triggered by underlying operating system constraints such
+ as attempting to open a file that does not exist or attempting to send data
+ over a closed socket.
+* User-specified errors triggered by application code.
+* `AssertionError`s are a special class of error that can be triggered when
+ Node.js detects an exceptional logic violation that should never occur. These
+ are raised typically by the `assert` module.
+
+All JavaScript and system errors raised by Node.js inherit from, or are
+instances of, the standard JavaScript {Error} class and are guaranteed
+to provide *at least* the properties available on that class.
+
+## Error propagation and interception
+
+<!--type=misc-->
+
+Node.js supports several mechanisms for propagating and handling errors that
+occur while an application is running. How these errors are reported and
+handled depends entirely on the type of `Error` and the style of the API that is
+called.
+
+All JavaScript errors are handled as exceptions that *immediately* generate
+and throw an error using the standard JavaScript `throw` mechanism. These
+are handled using the [`try…catch` construct][try-catch] provided by the
+JavaScript language.
+
+```js
+// Throws with a ReferenceError because z is not defined.
+try {
+ const m = 1;
+ const n = m + z;
+} catch (err) {
+ // Handle the error here.
+}
+```
+
+Any use of the JavaScript `throw` mechanism will raise an exception that
+*must* be handled using `try…catch` or the Node.js process will exit
+immediately.
+
+With few exceptions, _Synchronous_ APIs (any blocking method that does not
+accept a `callback` function, such as [`fs.readFileSync`][]), will use `throw`
+to report errors.
+
+Errors that occur within _Asynchronous APIs_ may be reported in multiple ways:
+
+* Most asynchronous methods that accept a `callback` function will accept an
+ `Error` object passed as the first argument to that function. If that first
+ argument is not `null` and is an instance of `Error`, then an error occurred
+ that should be handled.
+
+<!-- eslint-disable no-useless-return -->
+ ```js
+ const fs = require('fs');
+ fs.readFile('a file that does not exist', (err, data) => {
+ if (err) {
+ console.error('There was an error reading the file!', err);
+ return;
+ }
+ // Otherwise handle the data
+ });
+ ```
+
+* When an asynchronous method is called on an object that is an
+ [`EventEmitter`][], errors can be routed to that object's `'error'` event.
+
+ ```js
+ const net = require('net');
+ const connection = net.connect('localhost');
+
+ // Adding an 'error' event handler to a stream:
+ connection.on('error', (err) => {
+ // If the connection is reset by the server, or if it can't
+ // connect at all, or on any sort of error encountered by
+ // the connection, the error will be sent here.
+ console.error(err);
+ });
+
+ connection.pipe(process.stdout);
+ ```
+
+* A handful of typically asynchronous methods in the Node.js API may still
+ use the `throw` mechanism to raise exceptions that must be handled using
+ `try…catch`. There is no comprehensive list of such methods; please
+ refer to the documentation of each method to determine the appropriate
+ error handling mechanism required.
+
+The use of the `'error'` event mechanism is most common for [stream-based][]
+and [event emitter-based][] APIs, which themselves represent a series of
+asynchronous operations over time (as opposed to a single operation that may
+pass or fail).
+
+For *all* [`EventEmitter`][] objects, if an `'error'` event handler is not
+provided, the error will be thrown, causing the Node.js process to report an
+uncaught exception and crash unless either: The [`domain`][domains] module is
+used appropriately or a handler has been registered for the
+[`'uncaughtException'`][] event.
+
+```js
+const EventEmitter = require('events');
+const ee = new EventEmitter();
+
+setImmediate(() => {
+ // This will crash the process because no 'error' event
+ // handler has been added.
+ ee.emit('error', new Error('This will crash'));
+});
+```
+
+Errors generated in this way *cannot* be intercepted using `try…catch` as
+they are thrown *after* the calling code has already exited.
+
+Developers must refer to the documentation for each method to determine
+exactly how errors raised by those methods are propagated.
+
+### Error-first callbacks
+
+<!--type=misc-->
+
+Most asynchronous methods exposed by the Node.js core API follow an idiomatic
+pattern referred to as an _error-first callback_. With this pattern, a callback
+function is passed to the method as an argument. When the operation either
+completes or an error is raised, the callback function is called with the
+`Error` object (if any) passed as the first argument. If no error was raised,
+the first argument will be passed as `null`.
+
+```js
+const fs = require('fs');
+
+function errorFirstCallback(err, data) {
+ if (err) {
+ console.error('There was an error', err);
+ return;
+ }
+ console.log(data);
+}
+
+fs.readFile('/some/file/that/does-not-exist', errorFirstCallback);
+fs.readFile('/some/file/that/does-exist', errorFirstCallback);
+```
+
+The JavaScript `try…catch` mechanism **cannot** be used to intercept errors
+generated by asynchronous APIs. A common mistake for beginners is to try to
+use `throw` inside an error-first callback:
+
+```js
+// THIS WILL NOT WORK:
+const fs = require('fs');
+
+try {
+ fs.readFile('/some/file/that/does-not-exist', (err, data) => {
+ // Mistaken assumption: throwing here...
+ if (err) {
+ throw err;
+ }
+ });
+} catch (err) {
+ // This will not catch the throw!
+ console.error(err);
+}
+```
+
+This will not work because the callback function passed to `fs.readFile()` is
+called asynchronously. By the time the callback has been called, the
+surrounding code, including the `try…catch` block, will have already exited.
+Throwing an error inside the callback **can crash the Node.js process** in most
+cases. If [domains][] are enabled, or a handler has been registered with
+`process.on('uncaughtException')`, such errors can be intercepted.
+
+## Class: `Error`
+
+<!--type=class-->
+
+A generic JavaScript {Error} object that does not denote any specific
+circumstance of why the error occurred. `Error` objects capture a "stack trace"
+detailing the point in the code at which the `Error` was instantiated, and may
+provide a text description of the error.
+
+All errors generated by Node.js, including all system and JavaScript errors,
+will either be instances of, or inherit from, the `Error` class.
+
+### `new Error(message)`
+
+* `message` {string}
+
+Creates a new `Error` object and sets the `error.message` property to the
+provided text message. If an object is passed as `message`, the text message
+is generated by calling `message.toString()`. The `error.stack` property will
+represent the point in the code at which `new Error()` was called. Stack traces
+are dependent on [V8's stack trace API][]. Stack traces extend only to either
+(a) the beginning of *synchronous code execution*, or (b) the number of frames
+given by the property `Error.stackTraceLimit`, whichever is smaller.
+
+### `Error.captureStackTrace(targetObject[, constructorOpt])`
+
+* `targetObject` {Object}
+* `constructorOpt` {Function}
+
+Creates a `.stack` property on `targetObject`, which when accessed returns
+a string representing the location in the code at which
+`Error.captureStackTrace()` was called.
+
+```js
+const myObject = {};
+Error.captureStackTrace(myObject);
+myObject.stack; // Similar to `new Error().stack`
+```
+
+The first line of the trace will be prefixed with
+`${myObject.name}: ${myObject.message}`.
+
+The optional `constructorOpt` argument accepts a function. If given, all frames
+above `constructorOpt`, including `constructorOpt`, will be omitted from the
+generated stack trace.
+
+The `constructorOpt` argument is useful for hiding implementation
+details of error generation from the user. For instance:
+
+```js
+function MyError() {
+ Error.captureStackTrace(this, MyError);
+}
+
+// Without passing MyError to captureStackTrace, the MyError
+// frame would show up in the .stack property. By passing
+// the constructor, we omit that frame, and retain all frames below it.
+new MyError().stack;
+```
+
+### `Error.stackTraceLimit`
+
+* {number}
+
+The `Error.stackTraceLimit` property specifies the number of stack frames
+collected by a stack trace (whether generated by `new Error().stack` or
+`Error.captureStackTrace(obj)`).
+
+The default value is `10` but may be set to any valid JavaScript number. Changes
+will affect any stack trace captured *after* the value has been changed.
+
+If set to a non-number value, or set to a negative number, stack traces will
+not capture any frames.
+
+### `error.code`
+
+* {string}
+
+The `error.code` property is a string label that identifies the kind of error.
+`error.code` is the most stable way to identify an error. It will only change
+between major versions of Node.js. In contrast, `error.message` strings may
+change between any versions of Node.js. See [Node.js error codes][] for details
+about specific codes.
+
+### `error.message`
+
+* {string}
+
+The `error.message` property is the string description of the error as set by
+calling `new Error(message)`. The `message` passed to the constructor will also
+appear in the first line of the stack trace of the `Error`, however changing
+this property after the `Error` object is created *may not* change the first
+line of the stack trace (for example, when `error.stack` is read before this
+property is changed).
+
+```js
+const err = new Error('The message');
+console.error(err.message);
+// Prints: The message
+```
+
+### `error.stack`
+
+* {string}
+
+The `error.stack` property is a string describing the point in the code at which
+the `Error` was instantiated.
+
+```console
+Error: Things keep happening!
+ at /home/gbusey/file.js:525:2
+ at Frobnicator.refrobulate (/home/gbusey/business-logic.js:424:21)
+ at Actor.<anonymous> (/home/gbusey/actors.js:400:8)
+ at increaseSynergy (/home/gbusey/actors.js:701:6)
+```
+
+The first line is formatted as `<error class name>: <error message>`, and
+is followed by a series of stack frames (each line beginning with "at ").
+Each frame describes a call site within the code that lead to the error being
+generated. V8 attempts to display a name for each function (by variable name,
+function name, or object method name), but occasionally it will not be able to
+find a suitable name. If V8 cannot determine a name for the function, only
+location information will be displayed for that frame. Otherwise, the
+determined function name will be displayed with location information appended
+in parentheses.
+
+Frames are only generated for JavaScript functions. If, for example, execution
+synchronously passes through a C++ addon function called `cheetahify` which
+itself calls a JavaScript function, the frame representing the `cheetahify` call
+will not be present in the stack traces:
+
+```js
+const cheetahify = require('./native-binding.node');
+
+function makeFaster() {
+ // `cheetahify()` *synchronously* calls speedy.
+ cheetahify(function speedy() {
+ throw new Error('oh no!');
+ });
+}
+
+makeFaster();
+// will throw:
+// /home/gbusey/file.js:6
+// throw new Error('oh no!');
+// ^
+// Error: oh no!
+// at speedy (/home/gbusey/file.js:6:11)
+// at makeFaster (/home/gbusey/file.js:5:3)
+// at Object.<anonymous> (/home/gbusey/file.js:10:1)
+// at Module._compile (module.js:456:26)
+// at Object.Module._extensions..js (module.js:474:10)
+// at Module.load (module.js:356:32)
+// at Function.Module._load (module.js:312:12)
+// at Function.Module.runMain (module.js:497:10)
+// at startup (node.js:119:16)
+// at node.js:906:3
+```
+
+The location information will be one of:
+
+* `native`, if the frame represents a call internal to V8 (as in `[].forEach`).
+* `plain-filename.js:line:column`, if the frame represents a call internal
+ to Node.js.
+* `/absolute/path/to/file.js:line:column`, if the frame represents a call in
+ a user program, or its dependencies.
+
+The string representing the stack trace is lazily generated when the
+`error.stack` property is **accessed**.
+
+The number of frames captured by the stack trace is bounded by the smaller of
+`Error.stackTraceLimit` or the number of available frames on the current event
+loop tick.
+
+## Class: `AssertionError`
+
+* Extends: {errors.Error}
+
+Indicates the failure of an assertion. For details, see
+[`Class: assert.AssertionError`][].
+
+## Class: `RangeError`
+
+* Extends: {errors.Error}
+
+Indicates that a provided argument was not within the set or range of
+acceptable values for a function; whether that is a numeric range, or
+outside the set of options for a given function parameter.
+
+```js
+require('net').connect(-1);
+// Throws "RangeError: "port" option should be >= 0 and < 65536: -1"
+```
+
+Node.js will generate and throw `RangeError` instances *immediately* as a form
+of argument validation.
+
+## Class: `ReferenceError`
+
+* Extends: {errors.Error}
+
+Indicates that an attempt is being made to access a variable that is not
+defined. Such errors commonly indicate typos in code, or an otherwise broken
+program.
+
+While client code may generate and propagate these errors, in practice, only V8
+will do so.
+
+```js
+doesNotExist;
+// Throws ReferenceError, doesNotExist is not a variable in this program.
+```
+
+Unless an application is dynamically generating and running code,
+`ReferenceError` instances indicate a bug in the code or its dependencies.
+
+## Class: `SyntaxError`
+
+* Extends: {errors.Error}
+
+Indicates that a program is not valid JavaScript. These errors may only be
+generated and propagated as a result of code evaluation. Code evaluation may
+happen as a result of `eval`, `Function`, `require`, or [vm][]. These errors
+are almost always indicative of a broken program.
+
+```js
+try {
+ require('vm').runInThisContext('binary ! isNotOk');
+} catch (err) {
+ // 'err' will be a SyntaxError.
+}
+```
+
+`SyntaxError` instances are unrecoverable in the context that created them –
+they may only be caught by other contexts.
+
+## Class: `SystemError`
+
+* Extends: {errors.Error}
+
+Node.js generates system errors when exceptions occur within its runtime
+environment. These usually occur when an application violates an operating
+system constraint. For example, a system error will occur if an application
+attempts to read a file that does not exist.
+
+* `address` {string} If present, the address to which a network connection
+ failed
+* `code` {string} The string error code
+* `dest` {string} If present, the file path destination when reporting a file
+ system error
+* `errno` {number} The system-provided error number
+* `info` {Object} If present, extra details about the error condition
+* `message` {string} A system-provided human-readable description of the error
+* `path` {string} If present, the file path when reporting a file system error
+* `port` {number} If present, the network connection port that is not available
+* `syscall` {string} The name of the system call that triggered the error
+
+### `error.address`
+
+* {string}
+
+If present, `error.address` is a string describing the address to which a
+network connection failed.
+
+### `error.code`
+
+* {string}
+
+The `error.code` property is a string representing the error code.
+
+### `error.dest`
+
+* {string}
+
+If present, `error.dest` is the file path destination when reporting a file
+system error.
+
+### `error.errno`
+
+* {number}
+
+The `error.errno` property is a negative number which corresponds
+to the error code defined in [`libuv Error handling`][].
+
+On Windows the error number provided by the system will be normalized by libuv.
+
+To get the string representation of the error code, use
+[`util.getSystemErrorName(error.errno)`][].
+
+### `error.info`
+
+* {Object}
+
+If present, `error.info` is an object with details about the error condition.
+
+### `error.message`
+
+* {string}
+
+`error.message` is a system-provided human-readable description of the error.
+
+### `error.path`
+
+* {string}
+
+If present, `error.path` is a string containing a relevant invalid pathname.
+
+### `error.port`
+
+* {number}
+
+If present, `error.port` is the network connection port that is not available.
+
+### `error.syscall`
+
+* {string}
+
+The `error.syscall` property is a string describing the [syscall][] that failed.
+
+### Common system errors
+
+This is a list of system errors commonly-encountered when writing a Node.js
+program. For a comprehensive list, see the [`errno`(3) man page][].
+
+* `EACCES` (Permission denied): An attempt was made to access a file in a way
+ forbidden by its file access permissions.
+
+* `EADDRINUSE` (Address already in use): An attempt to bind a server
+ ([`net`][], [`http`][], or [`https`][]) to a local address failed due to
+ another server on the local system already occupying that address.
+
+* `ECONNREFUSED` (Connection refused): No connection could be made because the
+ target machine actively refused it. This usually results from trying to
+ connect to a service that is inactive on the foreign host.
+
+* `ECONNRESET` (Connection reset by peer): A connection was forcibly closed by
+ a peer. This normally results from a loss of the connection on the remote
+ socket due to a timeout or reboot. Commonly encountered via the [`http`][]
+ and [`net`][] modules.
+
+* `EEXIST` (File exists): An existing file was the target of an operation that
+ required that the target not exist.
+
+* `EISDIR` (Is a directory): An operation expected a file, but the given
+ pathname was a directory.
+
+* `EMFILE` (Too many open files in system): Maximum number of
+ [file descriptors][] allowable on the system has been reached, and
+ requests for another descriptor cannot be fulfilled until at least one
+ has been closed. This is encountered when opening many files at once in
+ parallel, especially on systems (in particular, macOS) where there is a low
+ file descriptor limit for processes. To remedy a low limit, run
+ `ulimit -n 2048` in the same shell that will run the Node.js process.
+
+* `ENOENT` (No such file or directory): Commonly raised by [`fs`][] operations
+ to indicate that a component of the specified pathname does not exist. No
+ entity (file or directory) could be found by the given path.
+
+* `ENOTDIR` (Not a directory): A component of the given pathname existed, but
+ was not a directory as expected. Commonly raised by [`fs.readdir`][].
+
+* `ENOTEMPTY` (Directory not empty): A directory with entries was the target
+ of an operation that requires an empty directory, usually [`fs.unlink`][].
+
+* `ENOTFOUND` (DNS lookup failed): Indicates a DNS failure of either
+ `EAI_NODATA` or `EAI_NONAME`. This is not a standard POSIX error.
+
+* `EPERM` (Operation not permitted): An attempt was made to perform an
+ operation that requires elevated privileges.
+
+* `EPIPE` (Broken pipe): A write on a pipe, socket, or FIFO for which there is
+ no process to read the data. Commonly encountered at the [`net`][] and
+ [`http`][] layers, indicative that the remote side of the stream being
+ written to has been closed.
+
+* `ETIMEDOUT` (Operation timed out): A connect or send request failed because
+ the connected party did not properly respond after a period of time. Usually
+ encountered by [`http`][] or [`net`][]. Often a sign that a `socket.end()`
+ was not properly called.
+
+## Class: `TypeError`
+
+* Extends {errors.Error}
+
+Indicates that a provided argument is not an allowable type. For example,
+passing a function to a parameter which expects a string would be a `TypeError`.
+
+```js
+require('url').parse(() => { });
+// Throws TypeError, since it expected a string.
+```
+
+Node.js will generate and throw `TypeError` instances *immediately* as a form
+of argument validation.
+
+## Exceptions vs. errors
+
+<!--type=misc-->
+
+A JavaScript exception is a value that is thrown as a result of an invalid
+operation or as the target of a `throw` statement. While it is not required
+that these values are instances of `Error` or classes which inherit from
+`Error`, all exceptions thrown by Node.js or the JavaScript runtime *will* be
+instances of `Error`.
+
+Some exceptions are *unrecoverable* at the JavaScript layer. Such exceptions
+will *always* cause the Node.js process to crash. Examples include `assert()`
+checks or `abort()` calls in the C++ layer.
+
+## OpenSSL errors
+
+Errors originating in `crypto` or `tls` are of class `Error`, and in addition to
+the standard `.code` and `.message` properties, may have some additional
+OpenSSL-specific properties.
+
+### `error.opensslErrorStack`
+
+An array of errors that can give context to where in the OpenSSL library an
+error originates from.
+
+### `error.function`
+
+The OpenSSL function the error originates in.
+
+### `error.library`
+
+The OpenSSL library the error originates in.
+
+### `error.reason`
+
+A human-readable string describing the reason for the error.
+
+<a id="nodejs-error-codes"></a>
+## Node.js error codes
+
+<a id="ERR_AMBIGUOUS_ARGUMENT"></a>
+### `ERR_AMBIGUOUS_ARGUMENT`
+
+A function argument is being used in a way that suggests that the function
+signature may be misunderstood. This is thrown by the `assert` module when the
+`message` parameter in `assert.throws(block, message)` matches the error message
+thrown by `block` because that usage suggests that the user believes `message`
+is the expected message rather than the message the `AssertionError` will
+display if `block` does not throw.
+
+<a id="ERR_ARG_NOT_ITERABLE"></a>
+### `ERR_ARG_NOT_ITERABLE`
+
+An iterable argument (i.e. a value that works with `for...of` loops) was
+required, but not provided to a Node.js API.
+
+<a id="ERR_ASSERTION"></a>
+### `ERR_ASSERTION`
+
+A special type of error that can be triggered whenever Node.js detects an
+exceptional logic violation that should never occur. These are raised typically
+by the `assert` module.
+
+<a id="ERR_ASYNC_CALLBACK"></a>
+### `ERR_ASYNC_CALLBACK`
+
+An attempt was made to register something that is not a function as an
+`AsyncHooks` callback.
+
+<a id="ERR_ASYNC_TYPE"></a>
+### `ERR_ASYNC_TYPE`
+
+The type of an asynchronous resource was invalid. Users are also able
+to define their own types if using the public embedder API.
+
+<a id="ERR_BROTLI_COMPRESSION_FAILED"></a>
+### `ERR_BROTLI_COMPRESSION_FAILED`
+
+Data passed to a Brotli stream was not successfully compressed.
+
+<a id="ERR_BROTLI_INVALID_PARAM"></a>
+### `ERR_BROTLI_INVALID_PARAM`
+
+An invalid parameter key was passed during construction of a Brotli stream.
+
+<a id="ERR_BUFFER_CONTEXT_NOT_AVAILABLE"></a>
+### `ERR_BUFFER_CONTEXT_NOT_AVAILABLE`
+
+An attempt was made to create a Node.js `Buffer` instance from addon or embedder
+code, while in a JS engine Context that is not associated with a Node.js
+instance. The data passed to the `Buffer` method will have been released
+by the time the method returns.
+
+When encountering this error, a possible alternative to creating a `Buffer`
+instance is to create a normal `Uint8Array`, which only differs in the
+prototype of the resulting object. `Uint8Array`s are generally accepted in all
+Node.js core APIs where `Buffer`s are; they are available in all Contexts.
+
+<a id="ERR_BUFFER_OUT_OF_BOUNDS"></a>
+### `ERR_BUFFER_OUT_OF_BOUNDS`
+
+An operation outside the bounds of a `Buffer` was attempted.
+
+<a id="ERR_BUFFER_TOO_LARGE"></a>
+### `ERR_BUFFER_TOO_LARGE`
+
+An attempt has been made to create a `Buffer` larger than the maximum allowed
+size.
+
+<a id="ERR_CANNOT_WATCH_SIGINT"></a>
+### `ERR_CANNOT_WATCH_SIGINT`
+
+Node.js was unable to watch for the `SIGINT` signal.
+
+<a id="ERR_CHILD_CLOSED_BEFORE_REPLY"></a>
+### `ERR_CHILD_CLOSED_BEFORE_REPLY`
+
+A child process was closed before the parent received a reply.
+
+<a id="ERR_CHILD_PROCESS_IPC_REQUIRED"></a>
+### `ERR_CHILD_PROCESS_IPC_REQUIRED`
+
+Used when a child process is being forked without specifying an IPC channel.
+
+<a id="ERR_CHILD_PROCESS_STDIO_MAXBUFFER"></a>
+### `ERR_CHILD_PROCESS_STDIO_MAXBUFFER`
+
+Used when the main process is trying to read data from the child process's
+STDERR/STDOUT, and the data's length is longer than the `maxBuffer` option.
+
+<a id="ERR_CONSOLE_WRITABLE_STREAM"></a>
+### `ERR_CONSOLE_WRITABLE_STREAM`
+
+`Console` was instantiated without `stdout` stream, or `Console` has a
+non-writable `stdout` or `stderr` stream.
+
+<a id="ERR_CONSTRUCT_CALL_INVALID"></a>
+### `ERR_CONSTRUCT_CALL_INVALID`
+<!--
+added: v12.5.0
+-->
+
+A class constructor was called that is not callable.
+
+<a id="ERR_CONSTRUCT_CALL_REQUIRED"></a>
+### `ERR_CONSTRUCT_CALL_REQUIRED`
+
+A constructor for a class was called without `new`.
+
+<a id="ERR_CONTEXT_NOT_INITIALIZED"></a>
+### `ERR_CONTEXT_NOT_INITIALIZED`
+
+The vm context passed into the API is not yet initialized. This could happen
+when an error occurs (and is caught) during the creation of the
+context, for example, when the allocation fails or the maximum call stack
+size is reached when the context is created.
+
+<a id="ERR_CPU_USAGE"></a>
+### `ERR_CPU_USAGE`
+
+The native call from `process.cpuUsage` could not be processed.
+
+<a id="ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED"></a>
+### `ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED`
+
+A client certificate engine was requested that is not supported by the version
+of OpenSSL being used.
+
+<a id="ERR_CRYPTO_ECDH_INVALID_FORMAT"></a>
+### `ERR_CRYPTO_ECDH_INVALID_FORMAT`
+
+An invalid value for the `format` argument was passed to the `crypto.ECDH()`
+class `getPublicKey()` method.
+
+<a id="ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY"></a>
+### `ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY`
+
+An invalid value for the `key` argument has been passed to the
+`crypto.ECDH()` class `computeSecret()` method. It means that the public
+key lies outside of the elliptic curve.
+
+<a id="ERR_CRYPTO_ENGINE_UNKNOWN"></a>
+### `ERR_CRYPTO_ENGINE_UNKNOWN`
+
+An invalid crypto engine identifier was passed to
+[`require('crypto').setEngine()`][].
+
+<a id="ERR_CRYPTO_FIPS_FORCED"></a>
+### `ERR_CRYPTO_FIPS_FORCED`
+
+The [`--force-fips`][] command-line argument was used but there was an attempt
+to enable or disable FIPS mode in the `crypto` module.
+
+<a id="ERR_CRYPTO_FIPS_UNAVAILABLE"></a>
+### `ERR_CRYPTO_FIPS_UNAVAILABLE`
+
+An attempt was made to enable or disable FIPS mode, but FIPS mode was not
+available.
+
+<a id="ERR_CRYPTO_HASH_FINALIZED"></a>
+### `ERR_CRYPTO_HASH_FINALIZED`
+
+[`hash.digest()`][] was called multiple times. The `hash.digest()` method must
+be called no more than one time per instance of a `Hash` object.
+
+<a id="ERR_CRYPTO_HASH_UPDATE_FAILED"></a>
+### `ERR_CRYPTO_HASH_UPDATE_FAILED`
+
+[`hash.update()`][] failed for any reason. This should rarely, if ever, happen.
+
+<a id="ERR_CRYPTO_INCOMPATIBLE_KEY"></a>
+### `ERR_CRYPTO_INCOMPATIBLE_KEY`
+
+The given crypto keys are incompatible with the attempted operation.
+
+<a id="ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS"></a>
+### `ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS`
+
+The selected public or private key encoding is incompatible with other options.
+
+<a id="ERR_CRYPTO_INVALID_DIGEST"></a>
+### `ERR_CRYPTO_INVALID_DIGEST`
+
+An invalid [crypto digest algorithm][] was specified.
+
+<a id="ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE"></a>
+### `ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE`
+
+The given crypto key object's type is invalid for the attempted operation.
+
+<a id="ERR_CRYPTO_INVALID_STATE"></a>
+### `ERR_CRYPTO_INVALID_STATE`
+
+A crypto method was used on an object that was in an invalid state. For
+instance, calling [`cipher.getAuthTag()`][] before calling `cipher.final()`.
+
+<a id="ERR_CRYPTO_PBKDF2_ERROR"></a>
+### `ERR_CRYPTO_PBKDF2_ERROR`
+
+The PBKDF2 algorithm failed for unspecified reasons. OpenSSL does not provide
+more details and therefore neither does Node.js.
+
+<a id="ERR_CRYPTO_SCRYPT_INVALID_PARAMETER"></a>
+### `ERR_CRYPTO_SCRYPT_INVALID_PARAMETER`
+
+One or more [`crypto.scrypt()`][] or [`crypto.scryptSync()`][] parameters are
+outside their legal range.
+
+<a id="ERR_CRYPTO_SCRYPT_NOT_SUPPORTED"></a>
+### `ERR_CRYPTO_SCRYPT_NOT_SUPPORTED`
+
+Node.js was compiled without `scrypt` support. Not possible with the official
+release binaries but can happen with custom builds, including distro builds.
+
+<a id="ERR_CRYPTO_SIGN_KEY_REQUIRED"></a>
+### `ERR_CRYPTO_SIGN_KEY_REQUIRED`
+
+A signing `key` was not provided to the [`sign.sign()`][] method.
+
+<a id="ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH"></a>
+### `ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH`
+
+[`crypto.timingSafeEqual()`][] was called with `Buffer`, `TypedArray`, or
+`DataView` arguments of different lengths.
+
+<a id="ERR_CRYPTO_UNKNOWN_CIPHER"></a>
+### `ERR_CRYPTO_UNKNOWN_CIPHER`
+
+An unknown cipher was specified.
+
+<a id="ERR_CRYPTO_UNKNOWN_DH_GROUP"></a>
+### `ERR_CRYPTO_UNKNOWN_DH_GROUP`
+
+An unknown Diffie-Hellman group name was given. See
+[`crypto.getDiffieHellman()`][] for a list of valid group names.
+
+<a id="ERR_DIR_CLOSED"></a>
+### `ERR_DIR_CLOSED`
+
+The [`fs.Dir`][] was previously closed.
+
+<a id="ERR_DIR_CONCURRENT_OPERATION"></a>
+### `ERR_DIR_CONCURRENT_OPERATION`
+<!-- YAML
+added: v14.3.0
+-->
+
+A synchronous read or close call was attempted on an [`fs.Dir`][] which has
+ongoing asynchronous operations.
+
+<a id="ERR_DNS_SET_SERVERS_FAILED"></a>
+### `ERR_DNS_SET_SERVERS_FAILED`
+
+`c-ares` failed to set the DNS server.
+
+<a id="ERR_DOMAIN_CALLBACK_NOT_AVAILABLE"></a>
+### `ERR_DOMAIN_CALLBACK_NOT_AVAILABLE`
+
+The `domain` module was not usable since it could not establish the required
+error handling hooks, because
+[`process.setUncaughtExceptionCaptureCallback()`][] had been called at an
+earlier point in time.
+
+<a id="ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE"></a>
+### `ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`
+
+[`process.setUncaughtExceptionCaptureCallback()`][] could not be called
+because the `domain` module has been loaded at an earlier point in time.
+
+The stack trace is extended to include the point in time at which the
+`domain` module had been loaded.
+
+<a id="ERR_ENCODING_INVALID_ENCODED_DATA"></a>
+### `ERR_ENCODING_INVALID_ENCODED_DATA`
+
+Data provided to `TextDecoder()` API was invalid according to the encoding
+provided.
+
+<a id="ERR_ENCODING_NOT_SUPPORTED"></a>
+### `ERR_ENCODING_NOT_SUPPORTED`
+
+Encoding provided to `TextDecoder()` API was not one of the
+[WHATWG Supported Encodings][].
+
+<a id="ERR_EVAL_ESM_CANNOT_PRINT"></a>
+### `ERR_EVAL_ESM_CANNOT_PRINT`
+
+`--print` cannot be used with ESM input.
+
+<a id="ERR_EVENT_RECURSION"></a>
+### `ERR_EVENT_RECURSION`
+
+Thrown when an attempt is made to recursively dispatch an event on `EventTarget`.
+
+<a id="ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE"></a>
+### `ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE`
+
+The JS execution context is not associated with a Node.js environment.
+This may occur when Node.js is used as an embedded library and some hooks
+for the JS engine are not set up properly.
+
+<a id="ERR_FALSY_VALUE_REJECTION"></a>
+### `ERR_FALSY_VALUE_REJECTION`
+
+A `Promise` that was callbackified via `util.callbackify()` was rejected with a
+falsy value.
+
+<a id="ERR_FEATURE_UNAVAILABLE_ON_PLATFORM"></a>
+### `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM`
+<!-- YAML
+added: v14.0.0
+-->
+
+Used when a feature that is not available
+to the current platform which is running Node.js is used.
+
+<a id="ERR_FS_EISDIR"></a>
+### `ERR_FS_EISDIR`
+
+Path is a directory.
+
+<a id="ERR_FS_FILE_TOO_LARGE"></a>
+### `ERR_FS_FILE_TOO_LARGE`
+
+An attempt has been made to read a file whose size is larger than the maximum
+allowed size for a `Buffer`.
+
+<a id="ERR_FS_INVALID_SYMLINK_TYPE"></a>
+### `ERR_FS_INVALID_SYMLINK_TYPE`
+
+An invalid symlink type was passed to the [`fs.symlink()`][] or
+[`fs.symlinkSync()`][] methods.
+
+<a id="ERR_HTTP_HEADERS_SENT"></a>
+### `ERR_HTTP_HEADERS_SENT`
+
+An attempt was made to add more headers after the headers had already been sent.
+
+<a id="ERR_HTTP_INVALID_HEADER_VALUE"></a>
+### `ERR_HTTP_INVALID_HEADER_VALUE`
+
+An invalid HTTP header value was specified.
+
+<a id="ERR_HTTP_INVALID_STATUS_CODE"></a>
+### `ERR_HTTP_INVALID_STATUS_CODE`
+
+Status code was outside the regular status code range (100-999).
+
+<a id="ERR_HTTP_TRAILER_INVALID"></a>
+### `ERR_HTTP_TRAILER_INVALID`
+
+The `Trailer` header was set even though the transfer encoding does not support
+that.
+
+<a id="ERR_HTTP2_ALTSVC_INVALID_ORIGIN"></a>
+### `ERR_HTTP2_ALTSVC_INVALID_ORIGIN`
+
+HTTP/2 ALTSVC frames require a valid origin.
+
+<a id="ERR_HTTP2_ALTSVC_LENGTH"></a>
+### `ERR_HTTP2_ALTSVC_LENGTH`
+
+HTTP/2 ALTSVC frames are limited to a maximum of 16,382 payload bytes.
+
+<a id="ERR_HTTP2_CONNECT_AUTHORITY"></a>
+### `ERR_HTTP2_CONNECT_AUTHORITY`
+
+For HTTP/2 requests using the `CONNECT` method, the `:authority` pseudo-header
+is required.
+
+<a id="ERR_HTTP2_CONNECT_PATH"></a>
+### `ERR_HTTP2_CONNECT_PATH`
+
+For HTTP/2 requests using the `CONNECT` method, the `:path` pseudo-header is
+forbidden.
+
+<a id="ERR_HTTP2_CONNECT_SCHEME"></a>
+### `ERR_HTTP2_CONNECT_SCHEME`
+
+For HTTP/2 requests using the `CONNECT` method, the `:scheme` pseudo-header is
+forbidden.
+
+<a id="ERR_HTTP2_ERROR"></a>
+### `ERR_HTTP2_ERROR`
+
+A non-specific HTTP/2 error has occurred.
+
+<a id="ERR_HTTP2_GOAWAY_SESSION"></a>
+### `ERR_HTTP2_GOAWAY_SESSION`
+
+New HTTP/2 Streams may not be opened after the `Http2Session` has received a
+`GOAWAY` frame from the connected peer.
+
+<a id="ERR_HTTP2_HEADER_SINGLE_VALUE"></a>
+### `ERR_HTTP2_HEADER_SINGLE_VALUE`
+
+Multiple values were provided for an HTTP/2 header field that was required to
+have only a single value.
+
+<a id="ERR_HTTP2_HEADERS_AFTER_RESPOND"></a>
+### `ERR_HTTP2_HEADERS_AFTER_RESPOND`
+
+An additional headers was specified after an HTTP/2 response was initiated.
+
+<a id="ERR_HTTP2_HEADERS_SENT"></a>
+### `ERR_HTTP2_HEADERS_SENT`
+
+An attempt was made to send multiple response headers.
+
+<a id="ERR_HTTP2_INFO_STATUS_NOT_ALLOWED"></a>
+### `ERR_HTTP2_INFO_STATUS_NOT_ALLOWED`
+
+Informational HTTP status codes (`1xx`) may not be set as the response status
+code on HTTP/2 responses.
+
+<a id="ERR_HTTP2_INVALID_CONNECTION_HEADERS"></a>
+### `ERR_HTTP2_INVALID_CONNECTION_HEADERS`
+
+HTTP/1 connection specific headers are forbidden to be used in HTTP/2
+requests and responses.
+
+<a id="ERR_HTTP2_INVALID_HEADER_VALUE"></a>
+### `ERR_HTTP2_INVALID_HEADER_VALUE`
+
+An invalid HTTP/2 header value was specified.
+
+<a id="ERR_HTTP2_INVALID_INFO_STATUS"></a>
+### `ERR_HTTP2_INVALID_INFO_STATUS`
+
+An invalid HTTP informational status code has been specified. Informational
+status codes must be an integer between `100` and `199` (inclusive).
+
+<a id="ERR_HTTP2_INVALID_ORIGIN"></a>
+### `ERR_HTTP2_INVALID_ORIGIN`
+
+HTTP/2 `ORIGIN` frames require a valid origin.
+
+<a id="ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH"></a>
+### `ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH`
+
+Input `Buffer` and `Uint8Array` instances passed to the
+`http2.getUnpackedSettings()` API must have a length that is a multiple of
+six.
+
+<a id="ERR_HTTP2_INVALID_PSEUDOHEADER"></a>
+### `ERR_HTTP2_INVALID_PSEUDOHEADER`
+
+Only valid HTTP/2 pseudoheaders (`:status`, `:path`, `:authority`, `:scheme`,
+and `:method`) may be used.
+
+<a id="ERR_HTTP2_INVALID_SESSION"></a>
+### `ERR_HTTP2_INVALID_SESSION`
+
+An action was performed on an `Http2Session` object that had already been
+destroyed.
+
+<a id="ERR_HTTP2_INVALID_SETTING_VALUE"></a>
+### `ERR_HTTP2_INVALID_SETTING_VALUE`
+
+An invalid value has been specified for an HTTP/2 setting.
+
+<a id="ERR_HTTP2_INVALID_STREAM"></a>
+### `ERR_HTTP2_INVALID_STREAM`
+
+An operation was performed on a stream that had already been destroyed.
+
+<a id="ERR_HTTP2_MAX_PENDING_SETTINGS_ACK"></a>
+### `ERR_HTTP2_MAX_PENDING_SETTINGS_ACK`
+
+Whenever an HTTP/2 `SETTINGS` frame is sent to a connected peer, the peer is
+required to send an acknowledgment that it has received and applied the new
+`SETTINGS`. By default, a maximum number of unacknowledged `SETTINGS` frames may
+be sent at any given time. This error code is used when that limit has been
+reached.
+
+<a id="ERR_HTTP2_NESTED_PUSH"></a>
+### `ERR_HTTP2_NESTED_PUSH`
+
+An attempt was made to initiate a new push stream from within a push stream.
+Nested push streams are not permitted.
+
+<a id="ERR_HTTP2_NO_SOCKET_MANIPULATION"></a>
+### `ERR_HTTP2_NO_SOCKET_MANIPULATION`
+
+An attempt was made to directly manipulate (read, write, pause, resume, etc.) a
+socket attached to an `Http2Session`.
+
+<a id="ERR_HTTP2_ORIGIN_LENGTH"></a>
+### `ERR_HTTP2_ORIGIN_LENGTH`
+
+HTTP/2 `ORIGIN` frames are limited to a length of 16382 bytes.
+
+<a id="ERR_HTTP2_OUT_OF_STREAMS"></a>
+### `ERR_HTTP2_OUT_OF_STREAMS`
+
+The number of streams created on a single HTTP/2 session reached the maximum
+limit.
+
+<a id="ERR_HTTP2_PAYLOAD_FORBIDDEN"></a>
+### `ERR_HTTP2_PAYLOAD_FORBIDDEN`
+
+A message payload was specified for an HTTP response code for which a payload is
+forbidden.
+
+<a id="ERR_HTTP2_PING_CANCEL"></a>
+### `ERR_HTTP2_PING_CANCEL`
+
+An HTTP/2 ping was canceled.
+
+<a id="ERR_HTTP2_PING_LENGTH"></a>
+### `ERR_HTTP2_PING_LENGTH`
+
+HTTP/2 ping payloads must be exactly 8 bytes in length.
+
+<a id="ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED"></a>
+### `ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED`
+
+An HTTP/2 pseudo-header has been used inappropriately. Pseudo-headers are header
+key names that begin with the `:` prefix.
+
+<a id="ERR_HTTP2_PUSH_DISABLED"></a>
+### `ERR_HTTP2_PUSH_DISABLED`
+
+An attempt was made to create a push stream, which had been disabled by the
+client.
+
+<a id="ERR_HTTP2_SEND_FILE"></a>
+### `ERR_HTTP2_SEND_FILE`
+
+An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
+send a directory.
+
+<a id="ERR_HTTP2_SEND_FILE_NOSEEK"></a>
+### `ERR_HTTP2_SEND_FILE_NOSEEK`
+
+An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
+send something other than a regular file, but `offset` or `length` options were
+provided.
+
+<a id="ERR_HTTP2_SESSION_ERROR"></a>
+### `ERR_HTTP2_SESSION_ERROR`
+
+The `Http2Session` closed with a non-zero error code.
+
+<a id="ERR_HTTP2_SETTINGS_CANCEL"></a>
+### `ERR_HTTP2_SETTINGS_CANCEL`
+
+The `Http2Session` settings canceled.
+
+<a id="ERR_HTTP2_SOCKET_BOUND"></a>
+### `ERR_HTTP2_SOCKET_BOUND`
+
+An attempt was made to connect a `Http2Session` object to a `net.Socket` or
+`tls.TLSSocket` that had already been bound to another `Http2Session` object.
+
+<a id="ERR_HTTP2_SOCKET_UNBOUND"></a>
+### `ERR_HTTP2_SOCKET_UNBOUND`
+
+An attempt was made to use the `socket` property of an `Http2Session` that
+has already been closed.
+
+<a id="ERR_HTTP2_STATUS_101"></a>
+### `ERR_HTTP2_STATUS_101`
+
+Use of the `101` Informational status code is forbidden in HTTP/2.
+
+<a id="ERR_HTTP2_STATUS_INVALID"></a>
+### `ERR_HTTP2_STATUS_INVALID`
+
+An invalid HTTP status code has been specified. Status codes must be an integer
+between `100` and `599` (inclusive).
+
+<a id="ERR_HTTP2_STREAM_CANCEL"></a>
+### `ERR_HTTP2_STREAM_CANCEL`
+
+An `Http2Stream` was destroyed before any data was transmitted to the connected
+peer.
+
+<a id="ERR_HTTP2_STREAM_ERROR"></a>
+### `ERR_HTTP2_STREAM_ERROR`
+
+A non-zero error code was been specified in an `RST_STREAM` frame.
+
+<a id="ERR_HTTP2_STREAM_SELF_DEPENDENCY"></a>
+### `ERR_HTTP2_STREAM_SELF_DEPENDENCY`
+
+When setting the priority for an HTTP/2 stream, the stream may be marked as
+a dependency for a parent stream. This error code is used when an attempt is
+made to mark a stream and dependent of itself.
+
+<a id="ERR_HTTP2_TRAILERS_ALREADY_SENT"></a>
+### `ERR_HTTP2_TRAILERS_ALREADY_SENT`
+
+Trailing headers have already been sent on the `Http2Stream`.
+
+<a id="ERR_HTTP2_TRAILERS_NOT_READY"></a>
+### `ERR_HTTP2_TRAILERS_NOT_READY`
+
+The `http2stream.sendTrailers()` method cannot be called until after the
+`'wantTrailers'` event is emitted on an `Http2Stream` object. The
+`'wantTrailers'` event will only be emitted if the `waitForTrailers` option
+is set for the `Http2Stream`.
+
+<a id="ERR_HTTP2_UNSUPPORTED_PROTOCOL"></a>
+### `ERR_HTTP2_UNSUPPORTED_PROTOCOL`
+
+`http2.connect()` was passed a URL that uses any protocol other than `http:` or
+`https:`.
+
+<a id="ERR_INCOMPATIBLE_OPTION_PAIR"></a>
+### `ERR_INCOMPATIBLE_OPTION_PAIR`
+
+An option pair is incompatible with each other and cannot be used at the same
+time.
+
+<a id="ERR_INPUT_TYPE_NOT_ALLOWED"></a>
+### `ERR_INPUT_TYPE_NOT_ALLOWED`
+
+> Stability: 1 - Experimental
+
+The `--input-type` flag was used to attempt to execute a file. This flag can
+only be used with input via `--eval`, `--print` or `STDIN`.
+
+<a id="ERR_INSPECTOR_ALREADY_ACTIVATED"></a>
+### `ERR_INSPECTOR_ALREADY_ACTIVATED`
+
+While using the `inspector` module, an attempt was made to activate the
+inspector when it already started to listen on a port. Use `inspector.close()`
+before activating it on a different address.
+
+<a id="ERR_INSPECTOR_ALREADY_CONNECTED"></a>
+### `ERR_INSPECTOR_ALREADY_CONNECTED`
+
+While using the `inspector` module, an attempt was made to connect when the
+inspector was already connected.
+
+<a id="ERR_INSPECTOR_CLOSED"></a>
+### `ERR_INSPECTOR_CLOSED`
+
+While using the `inspector` module, an attempt was made to use the inspector
+after the session had already closed.
+
+<a id="ERR_INSPECTOR_COMMAND"></a>
+### `ERR_INSPECTOR_COMMAND`
+
+An error occurred while issuing a command via the `inspector` module.
+
+<a id="ERR_INSPECTOR_NOT_ACTIVE"></a>
+### `ERR_INSPECTOR_NOT_ACTIVE`
+
+The `inspector` is not active when `inspector.waitForDebugger()` is called.
+
+<a id="ERR_INSPECTOR_NOT_AVAILABLE"></a>
+### `ERR_INSPECTOR_NOT_AVAILABLE`
+
+The `inspector` module is not available for use.
+
+<a id="ERR_INSPECTOR_NOT_CONNECTED"></a>
+### `ERR_INSPECTOR_NOT_CONNECTED`
+
+While using the `inspector` module, an attempt was made to use the inspector
+before it was connected.
+
+<a id="ERR_INSPECTOR_NOT_WORKER"></a>
+### `ERR_INSPECTOR_NOT_WORKER`
+
+An API was called on the main thread that can only be used from
+the worker thread.
+
+<a id="ERR_INTERNAL_ASSERTION"></a>
+### `ERR_INTERNAL_ASSERTION`
+
+There was a bug in Node.js or incorrect usage of Node.js internals.
+To fix the error, open an issue at <https://github.com/nodejs/node/issues>.
+
+<a id="ERR_INVALID_ADDRESS_FAMILY"></a>
+### `ERR_INVALID_ADDRESS_FAMILY`
+
+The provided address family is not understood by the Node.js API.
+
+<a id="ERR_INVALID_ARG_TYPE"></a>
+### `ERR_INVALID_ARG_TYPE`
+
+An argument of the wrong type was passed to a Node.js API.
+
+<a id="ERR_INVALID_ARG_VALUE"></a>
+### `ERR_INVALID_ARG_VALUE`
+
+An invalid or unsupported value was passed for a given argument.
+
+<a id="ERR_INVALID_ASYNC_ID"></a>
+### `ERR_INVALID_ASYNC_ID`
+
+An invalid `asyncId` or `triggerAsyncId` was passed using `AsyncHooks`. An id
+less than -1 should never happen.
+
+<a id="ERR_INVALID_BUFFER_SIZE"></a>
+### `ERR_INVALID_BUFFER_SIZE`
+
+A swap was performed on a `Buffer` but its size was not compatible with the
+operation.
+
+<a id="ERR_INVALID_CALLBACK"></a>
+### `ERR_INVALID_CALLBACK`
+
+A callback function was required but was not been provided to a Node.js API.
+
+<a id="ERR_INVALID_CHAR"></a>
+### `ERR_INVALID_CHAR`
+
+Invalid characters were detected in headers.
+
+<a id="ERR_INVALID_CURSOR_POS"></a>
+### `ERR_INVALID_CURSOR_POS`
+
+A cursor on a given stream cannot be moved to a specified row without a
+specified column.
+
+<a id="ERR_INVALID_FD"></a>
+### `ERR_INVALID_FD`
+
+A file descriptor ('fd') was not valid (e.g. it was a negative value).
+
+<a id="ERR_INVALID_FD_TYPE"></a>
+### `ERR_INVALID_FD_TYPE`
+
+A file descriptor ('fd') type was not valid.
+
+<a id="ERR_INVALID_FILE_URL_HOST"></a>
+### `ERR_INVALID_FILE_URL_HOST`
+
+A Node.js API that consumes `file:` URLs (such as certain functions in the
+[`fs`][] module) encountered a file URL with an incompatible host. This
+situation can only occur on Unix-like systems where only `localhost` or an empty
+host is supported.
+
+<a id="ERR_INVALID_FILE_URL_PATH"></a>
+### `ERR_INVALID_FILE_URL_PATH`
+
+A Node.js API that consumes `file:` URLs (such as certain functions in the
+[`fs`][] module) encountered a file URL with an incompatible path. The exact
+semantics for determining whether a path can be used is platform-dependent.
+
+<a id="ERR_INVALID_HANDLE_TYPE"></a>
+### `ERR_INVALID_HANDLE_TYPE`
+
+An attempt was made to send an unsupported "handle" over an IPC communication
+channel to a child process. See [`subprocess.send()`][] and [`process.send()`][]
+for more information.
+
+<a id="ERR_INVALID_HTTP_TOKEN"></a>
+### `ERR_INVALID_HTTP_TOKEN`
+
+An invalid HTTP token was supplied.
+
+<a id="ERR_INVALID_IP_ADDRESS"></a>
+### `ERR_INVALID_IP_ADDRESS`
+
+An IP address is not valid.
+
+<a id="ERR_INVALID_MODULE_SPECIFIER"></a>
+### `ERR_INVALID_MODULE_SPECIFIER`
+
+The imported module string is an invalid URL, package name, or package subpath
+specifier.
+
+<a id="ERR_INVALID_OPT_VALUE"></a>
+### `ERR_INVALID_OPT_VALUE`
+
+An invalid or unexpected value was passed in an options object.
+
+<a id="ERR_INVALID_OPT_VALUE_ENCODING"></a>
+### `ERR_INVALID_OPT_VALUE_ENCODING`
+
+An invalid or unknown file encoding was passed.
+
+<a id="ERR_INVALID_PACKAGE_CONFIG"></a>
+### `ERR_INVALID_PACKAGE_CONFIG`
+
+An invalid [`package.json`][] file was found which failed parsing.
+
+<a id="ERR_INVALID_PACKAGE_TARGET"></a>
+### `ERR_INVALID_PACKAGE_TARGET`
+
+The `package.json` [`"exports"`][] field contains an invalid target mapping
+value for the attempted module resolution.
+
+<a id="ERR_INVALID_PERFORMANCE_MARK"></a>
+### `ERR_INVALID_PERFORMANCE_MARK`
+
+While using the Performance Timing API (`perf_hooks`), a performance mark is
+invalid.
+
+<a id="ERR_INVALID_PROTOCOL"></a>
+### `ERR_INVALID_PROTOCOL`
+
+An invalid `options.protocol` was passed to `http.request()`.
+
+<a id="ERR_INVALID_REPL_EVAL_CONFIG"></a>
+### `ERR_INVALID_REPL_EVAL_CONFIG`
+
+Both `breakEvalOnSigint` and `eval` options were set in the [`REPL`][] config,
+which is not supported.
+
+<a id="ERR_INVALID_REPL_INPUT"></a>
+### `ERR_INVALID_REPL_INPUT`
+
+The input may not be used in the [`REPL`][]. All prohibited inputs are
+documented in the [`REPL`][]'s documentation.
+
+<a id="ERR_INVALID_RETURN_PROPERTY"></a>
+### `ERR_INVALID_RETURN_PROPERTY`
+
+Thrown in case a function option does not provide a valid value for one of its
+returned object properties on execution.
+
+<a id="ERR_INVALID_RETURN_PROPERTY_VALUE"></a>
+### `ERR_INVALID_RETURN_PROPERTY_VALUE`
+
+Thrown in case a function option does not provide an expected value
+type for one of its returned object properties on execution.
+
+<a id="ERR_INVALID_RETURN_VALUE"></a>
+### `ERR_INVALID_RETURN_VALUE`
+
+Thrown in case a function option does not return an expected value
+type on execution, such as when a function is expected to return a promise.
+
+<a id="ERR_INVALID_SYNC_FORK_INPUT"></a>
+### `ERR_INVALID_SYNC_FORK_INPUT`
+
+A `Buffer`, `TypedArray`, `DataView` or `string` was provided as stdio input to
+an asynchronous fork. See the documentation for the [`child_process`][] module
+for more information.
+
+<a id="ERR_INVALID_THIS"></a>
+### `ERR_INVALID_THIS`
+
+A Node.js API function was called with an incompatible `this` value.
+
+```js
+const urlSearchParams = new URLSearchParams('foo=bar&baz=new');
+
+const buf = Buffer.alloc(1);
+urlSearchParams.has.call(buf, 'foo');
+// Throws a TypeError with code 'ERR_INVALID_THIS'
+```
+
+<a id="ERR_INVALID_TRANSFER_OBJECT"></a>
+### `ERR_INVALID_TRANSFER_OBJECT`
+
+An invalid transfer object was passed to `postMessage()`.
+
+<a id="ERR_INVALID_TUPLE"></a>
+### `ERR_INVALID_TUPLE`
+
+An element in the `iterable` provided to the [WHATWG][WHATWG URL API]
+[`URLSearchParams` constructor][`new URLSearchParams(iterable)`] did not
+represent a `[name, value]` tuple – that is, if an element is not iterable, or
+does not consist of exactly two elements.
+
+<a id="ERR_INVALID_URI"></a>
+### `ERR_INVALID_URI`
+
+An invalid URI was passed.
+
+<a id="ERR_INVALID_URL"></a>
+### `ERR_INVALID_URL`
+
+An invalid URL was passed to the [WHATWG][WHATWG URL API]
+[`URL` constructor][`new URL(input)`] to be parsed. The thrown error object
+typically has an additional property `'input'` that contains the URL that failed
+to parse.
+
+<a id="ERR_INVALID_URL_SCHEME"></a>
+### `ERR_INVALID_URL_SCHEME`
+
+An attempt was made to use a URL of an incompatible scheme (protocol) for a
+specific purpose. It is only used in the [WHATWG URL API][] support in the
+[`fs`][] module (which only accepts URLs with `'file'` scheme), but may be used
+in other Node.js APIs as well in the future.
+
+<a id="ERR_IPC_CHANNEL_CLOSED"></a>
+### `ERR_IPC_CHANNEL_CLOSED`
+
+An attempt was made to use an IPC communication channel that was already closed.
+
+<a id="ERR_IPC_DISCONNECTED"></a>
+### `ERR_IPC_DISCONNECTED`
+
+An attempt was made to disconnect an IPC communication channel that was already
+disconnected. See the documentation for the [`child_process`][] module
+for more information.
+
+<a id="ERR_IPC_ONE_PIPE"></a>
+### `ERR_IPC_ONE_PIPE`
+
+An attempt was made to create a child Node.js process using more than one IPC
+communication channel. See the documentation for the [`child_process`][] module
+for more information.
+
+<a id="ERR_IPC_SYNC_FORK"></a>
+### `ERR_IPC_SYNC_FORK`
+
+An attempt was made to open an IPC communication channel with a synchronously
+forked Node.js process. See the documentation for the [`child_process`][] module
+for more information.
+
+<a id="ERR_MANIFEST_ASSERT_INTEGRITY"></a>
+### `ERR_MANIFEST_ASSERT_INTEGRITY`
+
+An attempt was made to load a resource, but the resource did not match the
+integrity defined by the policy manifest. See the documentation for [policy][]
+manifests for more information.
+
+<a id="ERR_MANIFEST_DEPENDENCY_MISSING"></a>
+### `ERR_MANIFEST_DEPENDENCY_MISSING`
+
+An attempt was made to load a resource, but the resource was not listed as a
+dependency from the location that attempted to load it. See the documentation
+for [policy][] manifests for more information.
+
+<a id="ERR_MANIFEST_INTEGRITY_MISMATCH"></a>
+### `ERR_MANIFEST_INTEGRITY_MISMATCH`
+
+An attempt was made to load a policy manifest, but the manifest had multiple
+entries for a resource which did not match each other. Update the manifest
+entries to match in order to resolve this error. See the documentation for
+[policy][] manifests for more information.
+
+<a id="ERR_MANIFEST_INVALID_RESOURCE_FIELD"></a>
+### `ERR_MANIFEST_INVALID_RESOURCE_FIELD`
+
+A policy manifest resource had an invalid value for one of its fields. Update
+the manifest entry to match in order to resolve this error. See the
+documentation for [policy][] manifests for more information.
+
+<a id="ERR_MANIFEST_PARSE_POLICY"></a>
+### `ERR_MANIFEST_PARSE_POLICY`
+
+An attempt was made to load a policy manifest, but the manifest was unable to
+be parsed. See the documentation for [policy][] manifests for more information.
+
+<a id="ERR_MANIFEST_TDZ"></a>
+### `ERR_MANIFEST_TDZ`
+
+An attempt was made to read from a policy manifest, but the manifest
+initialization has not yet taken place. This is likely a bug in Node.js.
+
+<a id="ERR_MANIFEST_UNKNOWN_ONERROR"></a>
+### `ERR_MANIFEST_UNKNOWN_ONERROR`
+
+A policy manifest was loaded, but had an unknown value for its "onerror"
+behavior. See the documentation for [policy][] manifests for more information.
+
+<a id="ERR_MEMORY_ALLOCATION_FAILED"></a>
+### `ERR_MEMORY_ALLOCATION_FAILED`
+
+An attempt was made to allocate memory (usually in the C++ layer) but it
+failed.
+
+<a id="ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE"></a>
+### `ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE`
+<!-- YAML
+added: v14.5.0
+-->
+
+A message posted to a [`MessagePort`][] could not be deserialized in the target
+[vm][] `Context`. Not all Node.js objects can be successfully instantiated in
+any context at this time, and attempting to transfer them using `postMessage()`
+can fail on the receiving side in that case.
+
+<a id="ERR_METHOD_NOT_IMPLEMENTED"></a>
+### `ERR_METHOD_NOT_IMPLEMENTED`
+
+A method is required but not implemented.
+
+<a id="ERR_MISSING_ARGS"></a>
+### `ERR_MISSING_ARGS`
+
+A required argument of a Node.js API was not passed. This is only used for
+strict compliance with the API specification (which in some cases may accept
+`func(undefined)` but not `func()`). In most native Node.js APIs,
+`func(undefined)` and `func()` are treated identically, and the
+[`ERR_INVALID_ARG_TYPE`][] error code may be used instead.
+
+<a id="ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST"></a>
+### `ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`
+
+An object that needs to be explicitly listed in the `transferList` argument
+is in the object passed to a `postMessage()` call, but is not provided
+in the `transferList` for that call. Usually, this is a `MessagePort`.
+
+<a id="ERR_MISSING_OPTION"></a>
+### `ERR_MISSING_OPTION`
+
+For APIs that accept options objects, some options might be mandatory. This code
+is thrown if a required option is missing.
+
+<a id="ERR_MISSING_PASSPHRASE"></a>
+### `ERR_MISSING_PASSPHRASE`
+
+An attempt was made to read an encrypted key without specifying a passphrase.
+
+<a id="ERR_MISSING_PLATFORM_FOR_WORKER"></a>
+### `ERR_MISSING_PLATFORM_FOR_WORKER`
+
+The V8 platform used by this instance of Node.js does not support creating
+Workers. This is caused by lack of embedder support for Workers. In particular,
+this error will not occur with standard builds of Node.js.
+
+<a id="ERR_MODULE_NOT_FOUND"></a>
+### `ERR_MODULE_NOT_FOUND`
+
+> Stability: 1 - Experimental
+
+An [ES Module][] could not be resolved.
+
+<a id="ERR_MULTIPLE_CALLBACK"></a>
+### `ERR_MULTIPLE_CALLBACK`
+
+A callback was called more than once.
+
+A callback is almost always meant to only be called once as the query
+can either be fulfilled or rejected but not both at the same time. The latter
+would be possible by calling a callback more than once.
+
+<a id="ERR_NAPI_CONS_FUNCTION"></a>
+### `ERR_NAPI_CONS_FUNCTION`
+
+While using `N-API`, a constructor passed was not a function.
+
+<a id="ERR_NAPI_INVALID_DATAVIEW_ARGS"></a>
+### `ERR_NAPI_INVALID_DATAVIEW_ARGS`
+
+While calling `napi_create_dataview()`, a given `offset` was outside the bounds
+of the dataview or `offset + length` was larger than a length of given `buffer`.
+
+<a id="ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT"></a>
+### `ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT`
+
+While calling `napi_create_typedarray()`, the provided `offset` was not a
+multiple of the element size.
+
+<a id="ERR_NAPI_INVALID_TYPEDARRAY_LENGTH"></a>
+### `ERR_NAPI_INVALID_TYPEDARRAY_LENGTH`
+
+While calling `napi_create_typedarray()`, `(length * size_of_element) +
+byte_offset` was larger than the length of given `buffer`.
+
+<a id="ERR_NAPI_TSFN_CALL_JS"></a>
+### `ERR_NAPI_TSFN_CALL_JS`
+
+An error occurred while invoking the JavaScript portion of the thread-safe
+function.
+
+<a id="ERR_NAPI_TSFN_GET_UNDEFINED"></a>
+### `ERR_NAPI_TSFN_GET_UNDEFINED`
+
+An error occurred while attempting to retrieve the JavaScript `undefined`
+value.
+
+<a id="ERR_NAPI_TSFN_START_IDLE_LOOP"></a>
+### `ERR_NAPI_TSFN_START_IDLE_LOOP`
+
+On the main thread, values are removed from the queue associated with the
+thread-safe function in an idle loop. This error indicates that an error
+has occurred when attempting to start the loop.
+
+<a id="ERR_NAPI_TSFN_STOP_IDLE_LOOP"></a>
+### `ERR_NAPI_TSFN_STOP_IDLE_LOOP`
+
+Once no more items are left in the queue, the idle loop must be suspended. This
+error indicates that the idle loop has failed to stop.
+
+<a id="ERR_NO_CRYPTO"></a>
+### `ERR_NO_CRYPTO`
+
+An attempt was made to use crypto features while Node.js was not compiled with
+OpenSSL crypto support.
+
+<a id="ERR_NO_ICU"></a>
+### `ERR_NO_ICU`
+
+An attempt was made to use features that require [ICU][], but Node.js was not
+compiled with ICU support.
+
+<a id="ERR_NON_CONTEXT_AWARE_DISABLED"></a>
+### `ERR_NON_CONTEXT_AWARE_DISABLED`
+
+A non-context-aware native addon was loaded in a process that disallows them.
+
+<a id="ERR_OUT_OF_RANGE"></a>
+### `ERR_OUT_OF_RANGE`
+
+A given value is out of the accepted range.
+
+<a id="ERR_PACKAGE_IMPORT_NOT_DEFINED"></a>
+### `ERR_PACKAGE_IMPORT_NOT_DEFINED`
+
+The `package.json` [`"imports"`][] field does not define the given internal
+package specifier mapping.
+
+<a id="ERR_PACKAGE_PATH_NOT_EXPORTED"></a>
+### `ERR_PACKAGE_PATH_NOT_EXPORTED`
+
+The `package.json` [`"exports"`][] field does not export the requested subpath.
+Because exports are encapsulated, private internal modules that are not exported
+cannot be imported through the package resolution, unless using an absolute URL.
+
+<a id="ERR_PROTO_ACCESS"></a>
+### `ERR_PROTO_ACCESS`
+
+Accessing `Object.prototype.__proto__` has been forbidden using
+[`--disable-proto=throw`][]. [`Object.getPrototypeOf`][] and
+[`Object.setPrototypeOf`][] should be used to get and set the prototype of an
+object.
+
+<a id="ERR_REQUIRE_ESM"></a>
+### `ERR_REQUIRE_ESM`
+
+> Stability: 1 - Experimental
+
+An attempt was made to `require()` an [ES Module][].
+
+<a id="ERR_SCRIPT_EXECUTION_INTERRUPTED"></a>
+### `ERR_SCRIPT_EXECUTION_INTERRUPTED`
+
+Script execution was interrupted by `SIGINT` (For example,
+<kbd>Ctrl</kbd>+<kbd>C</kbd> was pressed.)
+
+<a id="ERR_SCRIPT_EXECUTION_TIMEOUT"></a>
+### `ERR_SCRIPT_EXECUTION_TIMEOUT`
+
+Script execution timed out, possibly due to bugs in the script being executed.
+
+<a id="ERR_SERVER_ALREADY_LISTEN"></a>
+### `ERR_SERVER_ALREADY_LISTEN`
+
+The [`server.listen()`][] method was called while a `net.Server` was already
+listening. This applies to all instances of `net.Server`, including HTTP, HTTPS,
+and HTTP/2 `Server` instances.
+
+<a id="ERR_SERVER_NOT_RUNNING"></a>
+### `ERR_SERVER_NOT_RUNNING`
+
+The [`server.close()`][] method was called when a `net.Server` was not
+running. This applies to all instances of `net.Server`, including HTTP, HTTPS,
+and HTTP/2 `Server` instances.
+
+<a id="ERR_SOCKET_ALREADY_BOUND"></a>
+### `ERR_SOCKET_ALREADY_BOUND`
+
+An attempt was made to bind a socket that has already been bound.
+
+<a id="ERR_SOCKET_BAD_BUFFER_SIZE"></a>
+### `ERR_SOCKET_BAD_BUFFER_SIZE`
+
+An invalid (negative) size was passed for either the `recvBufferSize` or
+`sendBufferSize` options in [`dgram.createSocket()`][].
+
+<a id="ERR_SOCKET_BAD_PORT"></a>
+### `ERR_SOCKET_BAD_PORT`
+
+An API function expecting a port >= 0 and < 65536 received an invalid value.
+
+<a id="ERR_SOCKET_BAD_TYPE"></a>
+### `ERR_SOCKET_BAD_TYPE`
+
+An API function expecting a socket type (`udp4` or `udp6`) received an invalid
+value.
+
+<a id="ERR_SOCKET_BUFFER_SIZE"></a>
+### `ERR_SOCKET_BUFFER_SIZE`
+
+While using [`dgram.createSocket()`][], the size of the receive or send `Buffer`
+could not be determined.
+
+<a id="ERR_SOCKET_CLOSED"></a>
+### `ERR_SOCKET_CLOSED`
+
+An attempt was made to operate on an already closed socket.
+
+<a id="ERR_SOCKET_DGRAM_IS_CONNECTED"></a>
+### `ERR_SOCKET_DGRAM_IS_CONNECTED`
+
+A [`dgram.connect()`][] call was made on an already connected socket.
+
+<a id="ERR_SOCKET_DGRAM_NOT_CONNECTED"></a>
+### `ERR_SOCKET_DGRAM_NOT_CONNECTED`
+
+A [`dgram.disconnect()`][] or [`dgram.remoteAddress()`][] call was made on a
+disconnected socket.
+
+<a id="ERR_SOCKET_DGRAM_NOT_RUNNING"></a>
+### `ERR_SOCKET_DGRAM_NOT_RUNNING`
+
+A call was made and the UDP subsystem was not running.
+
+<a id="ERR_SRI_PARSE"></a>
+### `ERR_SRI_PARSE`
+
+A string was provided for a Subresource Integrity check, but was unable to be
+parsed. Check the format of integrity attributes by looking at the
+[Subresource Integrity specification][].
+
+<a id="ERR_STREAM_ALREADY_FINISHED"></a>
+### `ERR_STREAM_ALREADY_FINISHED`
+
+A stream method was called that cannot complete because the stream was
+finished.
+
+<a id="ERR_STREAM_CANNOT_PIPE"></a>
+### `ERR_STREAM_CANNOT_PIPE`
+
+An attempt was made to call [`stream.pipe()`][] on a [`Writable`][] stream.
+
+<a id="ERR_STREAM_DESTROYED"></a>
+### `ERR_STREAM_DESTROYED`
+
+A stream method was called that cannot complete because the stream was
+destroyed using `stream.destroy()`.
+
+<a id="ERR_STREAM_NULL_VALUES"></a>
+### `ERR_STREAM_NULL_VALUES`
+
+An attempt was made to call [`stream.write()`][] with a `null` chunk.
+
+<a id="ERR_STREAM_PREMATURE_CLOSE"></a>
+### `ERR_STREAM_PREMATURE_CLOSE`
+
+An error returned by `stream.finished()` and `stream.pipeline()`, when a stream
+or a pipeline ends non gracefully with no explicit error.
+
+<a id="ERR_STREAM_PUSH_AFTER_EOF"></a>
+### `ERR_STREAM_PUSH_AFTER_EOF`
+
+An attempt was made to call [`stream.push()`][] after a `null`(EOF) had been
+pushed to the stream.
+
+<a id="ERR_STREAM_UNSHIFT_AFTER_END_EVENT"></a>
+### `ERR_STREAM_UNSHIFT_AFTER_END_EVENT`
+
+An attempt was made to call [`stream.unshift()`][] after the `'end'` event was
+emitted.
+
+<a id="ERR_STREAM_WRAP"></a>
+### `ERR_STREAM_WRAP`
+
+Prevents an abort if a string decoder was set on the Socket or if the decoder
+is in `objectMode`.
+
+```js
+const Socket = require('net').Socket;
+const instance = new Socket();
+
+instance.setEncoding('utf8');
+```
+
+<a id="ERR_STREAM_WRITE_AFTER_END"></a>
+### `ERR_STREAM_WRITE_AFTER_END`
+
+An attempt was made to call [`stream.write()`][] after `stream.end()` has been
+called.
+
+<a id="ERR_STRING_TOO_LONG"></a>
+### `ERR_STRING_TOO_LONG`
+
+An attempt has been made to create a string longer than the maximum allowed
+length.
+
+<a id="ERR_SYNTHETIC"></a>
+### `ERR_SYNTHETIC`
+
+An artificial error object used to capture the call stack for diagnostic
+reports.
+
+<a id="ERR_SYSTEM_ERROR"></a>
+### `ERR_SYSTEM_ERROR`
+
+An unspecified or non-specific system error has occurred within the Node.js
+process. The error object will have an `err.info` object property with
+additional details.
+
+<a id="ERR_TLS_CERT_ALTNAME_INVALID"></a>
+### `ERR_TLS_CERT_ALTNAME_INVALID`
+
+While using TLS, the host name/IP of the peer did not match any of the
+`subjectAltNames` in its certificate.
+
+<a id="ERR_TLS_DH_PARAM_SIZE"></a>
+### `ERR_TLS_DH_PARAM_SIZE`
+
+While using TLS, the parameter offered for the Diffie-Hellman (`DH`)
+key-agreement protocol is too small. By default, the key length must be greater
+than or equal to 1024 bits to avoid vulnerabilities, even though it is strongly
+recommended to use 2048 bits or larger for stronger security.
+
+<a id="ERR_TLS_HANDSHAKE_TIMEOUT"></a>
+### `ERR_TLS_HANDSHAKE_TIMEOUT`
+
+A TLS/SSL handshake timed out. In this case, the server must also abort the
+connection.
+
+<a id="ERR_TLS_INVALID_CONTEXT"></a>
+### `ERR_TLS_INVALID_CONTEXT`
+<!-- YAML
+added: v13.3.0
+-->
+
+The context must be a `SecureContext`.
+
+<a id="ERR_TLS_INVALID_PROTOCOL_METHOD"></a>
+### `ERR_TLS_INVALID_PROTOCOL_METHOD`
+
+The specified `secureProtocol` method is invalid. It is either unknown, or
+disabled because it is insecure.
+
+<a id="ERR_TLS_INVALID_PROTOCOL_VERSION"></a>
+### `ERR_TLS_INVALID_PROTOCOL_VERSION`
+
+Valid TLS protocol versions are `'TLSv1'`, `'TLSv1.1'`, or `'TLSv1.2'`.
+
+<a id="ERR_TLS_INVALID_STATE"></a>
+### `ERR_TLS_INVALID_STATE`
+<!-- YAML
+added:
+ - v13.10.0
+ - v12.17.0
+-->
+
+The TLS socket must be connected and securily established. Ensure the 'secure'
+event is emitted before continuing.
+
+<a id="ERR_TLS_PROTOCOL_VERSION_CONFLICT"></a>
+### `ERR_TLS_PROTOCOL_VERSION_CONFLICT`
+
+Attempting to set a TLS protocol `minVersion` or `maxVersion` conflicts with an
+attempt to set the `secureProtocol` explicitly. Use one mechanism or the other.
+
+<a id="ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED"></a>
+### `ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED`
+
+Failed to set PSK identity hint. Hint may be too long.
+
+<a id="ERR_TLS_RENEGOTIATION_DISABLED"></a>
+### `ERR_TLS_RENEGOTIATION_DISABLED`
+
+An attempt was made to renegotiate TLS on a socket instance with TLS disabled.
+
+<a id="ERR_TLS_REQUIRED_SERVER_NAME"></a>
+### `ERR_TLS_REQUIRED_SERVER_NAME`
+
+While using TLS, the `server.addContext()` method was called without providing
+a host name in the first parameter.
+
+<a id="ERR_TLS_SESSION_ATTACK"></a>
+### `ERR_TLS_SESSION_ATTACK`
+
+An excessive amount of TLS renegotiations is detected, which is a potential
+vector for denial-of-service attacks.
+
+<a id="ERR_TLS_SNI_FROM_SERVER"></a>
+### `ERR_TLS_SNI_FROM_SERVER`
+
+An attempt was made to issue Server Name Indication from a TLS server-side
+socket, which is only valid from a client.
+
+<a id="ERR_TRACE_EVENTS_CATEGORY_REQUIRED"></a>
+### `ERR_TRACE_EVENTS_CATEGORY_REQUIRED`
+
+The `trace_events.createTracing()` method requires at least one trace event
+category.
+
+<a id="ERR_TRACE_EVENTS_UNAVAILABLE"></a>
+### `ERR_TRACE_EVENTS_UNAVAILABLE`
+
+The `trace_events` module could not be loaded because Node.js was compiled with
+the `--without-v8-platform` flag.
+
+<a id="ERR_TRANSFORM_ALREADY_TRANSFORMING"></a>
+### `ERR_TRANSFORM_ALREADY_TRANSFORMING`
+
+A `Transform` stream finished while it was still transforming.
+
+<a id="ERR_TRANSFORM_WITH_LENGTH_0"></a>
+### `ERR_TRANSFORM_WITH_LENGTH_0`
+
+A `Transform` stream finished with data still in the write buffer.
+
+<a id="ERR_TTY_INIT_FAILED"></a>
+### `ERR_TTY_INIT_FAILED`
+
+The initialization of a TTY failed due to a system error.
+
+<a id="ERR_UNAVAILABLE_DURING_EXIT"></a>
+### `ERR_UNAVAILABLE_DURING_EXIT`
+
+Function was called within a [`process.on('exit')`][] handler that shouldn't be
+called within [`process.on('exit')`][] handler.
+
+<a id="ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET"></a>
+### `ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET`
+
+[`process.setUncaughtExceptionCaptureCallback()`][] was called twice,
+without first resetting the callback to `null`.
+
+This error is designed to prevent accidentally overwriting a callback registered
+from another module.
+
+<a id="ERR_UNESCAPED_CHARACTERS"></a>
+### `ERR_UNESCAPED_CHARACTERS`
+
+A string that contained unescaped characters was received.
+
+<a id="ERR_UNHANDLED_ERROR"></a>
+### `ERR_UNHANDLED_ERROR`
+
+An unhandled error occurred (for instance, when an `'error'` event is emitted
+by an [`EventEmitter`][] but an `'error'` handler is not registered).
+
+<a id="ERR_UNKNOWN_BUILTIN_MODULE"></a>
+### `ERR_UNKNOWN_BUILTIN_MODULE`
+
+Used to identify a specific kind of internal Node.js error that should not
+typically be triggered by user code. Instances of this error point to an
+internal bug within the Node.js binary itself.
+
+<a id="ERR_UNKNOWN_CREDENTIAL"></a>
+### `ERR_UNKNOWN_CREDENTIAL`
+
+A Unix group or user identifier that does not exist was passed.
+
+<a id="ERR_UNKNOWN_ENCODING"></a>
+### `ERR_UNKNOWN_ENCODING`
+
+An invalid or unknown encoding option was passed to an API.
+
+<a id="ERR_UNKNOWN_FILE_EXTENSION"></a>
+### `ERR_UNKNOWN_FILE_EXTENSION`
+
+> Stability: 1 - Experimental
+
+An attempt was made to load a module with an unknown or unsupported file
+extension.
+
+<a id="ERR_UNKNOWN_MODULE_FORMAT"></a>
+### `ERR_UNKNOWN_MODULE_FORMAT`
+
+> Stability: 1 - Experimental
+
+An attempt was made to load a module with an unknown or unsupported format.
+
+<a id="ERR_UNKNOWN_SIGNAL"></a>
+### `ERR_UNKNOWN_SIGNAL`
+
+An invalid or unknown process signal was passed to an API expecting a valid
+signal (such as [`subprocess.kill()`][]).
+
+<a id="ERR_UNSUPPORTED_DIR_IMPORT"></a>
+### `ERR_UNSUPPORTED_DIR_IMPORT`
+
+`import` a directory URL is unsupported. Instead,
+[self-reference a package using its name][] and [define a custom subpath][] in
+the [`"exports"`][] field of the [`package.json`][] file.
+
+<!-- eslint-skip -->
+```js
+import './'; // unsupported
+import './index.js'; // supported
+import 'package-name'; // supported
+```
+
+<a id="ERR_UNSUPPORTED_ESM_URL_SCHEME"></a>
+### `ERR_UNSUPPORTED_ESM_URL_SCHEME`
+
+`import` with URL schemes other than `file` and `data` is unsupported.
+
+<a id="ERR_VALID_PERFORMANCE_ENTRY_TYPE"></a>
+### `ERR_VALID_PERFORMANCE_ENTRY_TYPE`
+
+While using the Performance Timing API (`perf_hooks`), no valid performance
+entry types are found.
+
+<a id="ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING"></a>
+### `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`
+
+A dynamic import callback was not specified.
+
+<a id="ERR_VM_MODULE_ALREADY_LINKED"></a>
+### `ERR_VM_MODULE_ALREADY_LINKED`
+
+The module attempted to be linked is not eligible for linking, because of one of
+the following reasons:
+
+* It has already been linked (`linkingStatus` is `'linked'`)
+* It is being linked (`linkingStatus` is `'linking'`)
+* Linking has failed for this module (`linkingStatus` is `'errored'`)
+
+<a id="ERR_VM_MODULE_CACHED_DATA_REJECTED"></a>
+### `ERR_VM_MODULE_CACHED_DATA_REJECTED`
+
+The `cachedData` option passed to a module constructor is invalid.
+
+<a id="ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA"></a>
+### `ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA`
+
+Cached data cannot be created for modules which have already been evaluated.
+
+<a id="ERR_VM_MODULE_DIFFERENT_CONTEXT"></a>
+### `ERR_VM_MODULE_DIFFERENT_CONTEXT`
+
+The module being returned from the linker function is from a different context
+than the parent module. Linked modules must share the same context.
+
+<a id="ERR_VM_MODULE_LINKING_ERRORED"></a>
+### `ERR_VM_MODULE_LINKING_ERRORED`
+
+The linker function returned a module for which linking has failed.
+
+<a id="ERR_VM_MODULE_NOT_MODULE"></a>
+### `ERR_VM_MODULE_NOT_MODULE`
+
+The fulfilled value of a linking promise is not a `vm.Module` object.
+
+<a id="ERR_VM_MODULE_STATUS"></a>
+### `ERR_VM_MODULE_STATUS`
+
+The current module's status does not allow for this operation. The specific
+meaning of the error depends on the specific function.
+
+<a id="ERR_WASI_ALREADY_STARTED"></a>
+### `ERR_WASI_ALREADY_STARTED`
+
+The WASI instance has already started.
+
+<a id="ERR_WASI_NOT_STARTED"></a>
+### `ERR_WASI_NOT_STARTED`
+
+The WASI instance has not been started.
+
+<a id="ERR_WORKER_INIT_FAILED"></a>
+### `ERR_WORKER_INIT_FAILED`
+
+The `Worker` initialization failed.
+
+<a id="ERR_WORKER_INVALID_EXEC_ARGV"></a>
+### `ERR_WORKER_INVALID_EXEC_ARGV`
+
+The `execArgv` option passed to the `Worker` constructor contains
+invalid flags.
+
+<a id="ERR_WORKER_NOT_RUNNING"></a>
+### `ERR_WORKER_NOT_RUNNING`
+
+An operation failed because the `Worker` instance is not currently running.
+
+<a id="ERR_WORKER_OUT_OF_MEMORY"></a>
+### `ERR_WORKER_OUT_OF_MEMORY`
+
+The `Worker` instance terminated because it reached its memory limit.
+
+<a id="ERR_WORKER_PATH"></a>
+### `ERR_WORKER_PATH`
+
+The path for the main script of a worker is neither an absolute path
+nor a relative path starting with `./` or `../`.
+
+<a id="ERR_WORKER_UNSERIALIZABLE_ERROR"></a>
+### `ERR_WORKER_UNSERIALIZABLE_ERROR`
+
+All attempts at serializing an uncaught exception from a worker thread failed.
+
+<a id="ERR_WORKER_UNSUPPORTED_EXTENSION"></a>
+### `ERR_WORKER_UNSUPPORTED_EXTENSION`
+
+The pathname used for the main script of a worker has an
+unknown file extension.
+
+<a id="ERR_WORKER_UNSUPPORTED_OPERATION"></a>
+### `ERR_WORKER_UNSUPPORTED_OPERATION`
+
+The requested functionality is not supported in worker threads.
+
+<a id="ERR_ZLIB_INITIALIZATION_FAILED"></a>
+### `ERR_ZLIB_INITIALIZATION_FAILED`
+
+Creation of a [`zlib`][] object failed due to incorrect configuration.
+
+<a id="HPE_HEADER_OVERFLOW"></a>
+### `HPE_HEADER_OVERFLOW`
+<!-- YAML
+changes:
+ - version:
+ - v11.4.0
+ - v10.15.0
+ pr-url: https://github.com/nodejs/node/commit/186035243fad247e3955f
+ description: Max header size in `http_parser` was set to 8KB.
+-->
+
+Too much HTTP header data was received. In order to protect against malicious or
+malconfigured clients, if more than 8KB of HTTP header data is received then
+HTTP parsing will abort without a request or response object being created, and
+an `Error` with this code will be emitted.
+
+<a id="HPE_UNEXPECTED_CONTENT_LENGTH"></a>
+### `HPE_UNEXPECTED_CONTENT_LENGTH`
+
+Server is sending both a `Content-Length` header and `Transfer-Encoding: chunked`.
+
+`Transfer-Encoding: chunked` allows the server to maintain an HTTP persistent
+connection for dynamically generated content.
+In this case, the `Content-Length` HTTP header cannot be used.
+
+Use `Content-Length` or `Transfer-Encoding: chunked`.
+
+<a id="MODULE_NOT_FOUND"></a>
+### `MODULE_NOT_FOUND`
+<!-- YAML
+changes:
+ - version: v12.0.0
+ pr-url: https://github.com/nodejs/node/pull/25690
+ description: Added `requireStack` property.
+-->
+A module file could not be resolved while attempting a [`require()`][] or
+`import` operation.
+
+## Legacy Node.js error codes
+
+> Stability: 0 - Deprecated. These error codes are either inconsistent, or have
+> been removed.
+
+<a id="ERR_CANNOT_TRANSFER_OBJECT"></a>
+### `ERR_CANNOT_TRANSFER_OBJECT`
+<!--
+added: v10.5.0
+removed: v12.5.0
+-->
+
+The value passed to `postMessage()` contained an object that is not supported
+for transferring.
+
+<a id="ERR_CLOSED_MESSAGE_PORT"></a>
+### `ERR_CLOSED_MESSAGE_PORT`
+<!-- YAML
+added: v10.5.0
+removed: v11.12.0
+-->
+
+There was an attempt to use a `MessagePort` instance in a closed
+state, usually after `.close()` has been called.
+
+<a id="ERR_CRYPTO_HASH_DIGEST_NO_UTF16"></a>
+### `ERR_CRYPTO_HASH_DIGEST_NO_UTF16`
+<!-- YAML
+added: v9.0.0
+removed: v12.12.0
+-->
+
+The UTF-16 encoding was used with [`hash.digest()`][]. While the
+`hash.digest()` method does allow an `encoding` argument to be passed in,
+causing the method to return a string rather than a `Buffer`, the UTF-16
+encoding (e.g. `ucs` or `utf16le`) is not supported.
+
+<a id="ERR_HTTP2_FRAME_ERROR"></a>
+### `ERR_HTTP2_FRAME_ERROR`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when a failure occurs sending an individual frame on the HTTP/2
+session.
+
+<a id="ERR_HTTP2_HEADERS_OBJECT"></a>
+### `ERR_HTTP2_HEADERS_OBJECT`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when an HTTP/2 Headers Object is expected.
+
+<a id="ERR_HTTP2_HEADER_REQUIRED"></a>
+### `ERR_HTTP2_HEADER_REQUIRED`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when a required header is missing in an HTTP/2 message.
+
+<a id="ERR_HTTP2_INFO_HEADERS_AFTER_RESPOND"></a>
+### `ERR_HTTP2_INFO_HEADERS_AFTER_RESPOND`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+HTTP/2 informational headers must only be sent *prior* to calling the
+`Http2Stream.prototype.respond()` method.
+
+<a id="ERR_HTTP2_STREAM_CLOSED"></a>
+### `ERR_HTTP2_STREAM_CLOSED`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when an action has been performed on an HTTP/2 Stream that has already
+been closed.
+
+<a id="ERR_HTTP_INVALID_CHAR"></a>
+### `ERR_HTTP_INVALID_CHAR`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when an invalid character is found in an HTTP response status message
+(reason phrase).
+
+<a id="ERR_HTTP_REQUEST_TIMEOUT"></a>
+### `ERR_HTTP_REQUEST_TIMEOUT`
+
+The client has not sent the entire request within the allowed time.
+
+<a id="ERR_INDEX_OUT_OF_RANGE"></a>
+### `ERR_INDEX_OUT_OF_RANGE`
+<!-- YAML
+ added: v10.0.0
+ removed: v11.0.0
+-->
+A given index was out of the accepted range (e.g. negative offsets).
+
+<a id="ERR_NAPI_CONS_PROTOTYPE_OBJECT"></a>
+### `ERR_NAPI_CONS_PROTOTYPE_OBJECT`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used by the `N-API` when `Constructor.prototype` is not an object.
+
+<a id="ERR_NO_LONGER_SUPPORTED"></a>
+### `ERR_NO_LONGER_SUPPORTED`
+
+A Node.js API was called in an unsupported manner, such as
+`Buffer.write(string, encoding, offset[, length])`.
+
+<a id="ERR_OUTOFMEMORY"></a>
+### `ERR_OUTOFMEMORY`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used generically to identify that an operation caused an out of memory
+condition.
+
+<a id="ERR_PARSE_HISTORY_DATA"></a>
+### `ERR_PARSE_HISTORY_DATA`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+The `repl` module was unable to parse data from the REPL history file.
+
+<a id="ERR_SOCKET_CANNOT_SEND"></a>
+### `ERR_SOCKET_CANNOT_SEND`
+<!-- YAML
+added: v9.0.0
+removed: v14.0.0
+-->
+
+Data could not be sent on a socket.
+
+<a id="ERR_STDERR_CLOSE"></a>
+### `ERR_STDERR_CLOSE`
+<!-- YAML
+removed: v10.12.0
+changes:
+ - version: v10.12.0
+ pr-url: https://github.com/nodejs/node/pull/23053
+ description: Rather than emitting an error, `process.stderr.end()` now
+ only closes the stream side but not the underlying resource,
+ making this error obsolete.
+-->
+
+An attempt was made to close the `process.stderr` stream. By design, Node.js
+does not allow `stdout` or `stderr` streams to be closed by user code.
+
+<a id="ERR_STDOUT_CLOSE"></a>
+### `ERR_STDOUT_CLOSE`
+<!-- YAML
+removed: v10.12.0
+changes:
+ - version: v10.12.0
+ pr-url: https://github.com/nodejs/node/pull/23053
+ description: Rather than emitting an error, `process.stderr.end()` now
+ only closes the stream side but not the underlying resource,
+ making this error obsolete.
+-->
+
+An attempt was made to close the `process.stdout` stream. By design, Node.js
+does not allow `stdout` or `stderr` streams to be closed by user code.
+
+<a id="ERR_STREAM_READ_NOT_IMPLEMENTED"></a>
+### `ERR_STREAM_READ_NOT_IMPLEMENTED`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when an attempt is made to use a readable stream that has not implemented
+[`readable._read()`][].
+
+<a id="ERR_TLS_RENEGOTIATION_FAILED"></a>
+### `ERR_TLS_RENEGOTIATION_FAILED`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when a TLS renegotiation request has failed in a non-specific way.
+
+<a id="ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER"></a>
+### `ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER`
+<!-- YAML
+added: v10.5.0
+removed: v14.0.0
+-->
+
+A `SharedArrayBuffer` whose memory is not managed by the JavaScript engine
+or by Node.js was encountered during serialization. Such a `SharedArrayBuffer`
+cannot be serialized.
+
+This can only happen when native addons create `SharedArrayBuffer`s in
+"externalized" mode, or put existing `SharedArrayBuffer` into externalized mode.
+
+<a id="ERR_UNKNOWN_STDIN_TYPE"></a>
+### `ERR_UNKNOWN_STDIN_TYPE`
+<!-- YAML
+added: v8.0.0
+removed: v11.7.0
+-->
+
+An attempt was made to launch a Node.js process with an unknown `stdin` file
+type. This error is usually an indication of a bug within Node.js itself,
+although it is possible for user code to trigger it.
+
+<a id="ERR_UNKNOWN_STREAM_TYPE"></a>
+### `ERR_UNKNOWN_STREAM_TYPE`
+<!-- YAML
+added: v8.0.0
+removed: v11.7.0
+-->
+
+An attempt was made to launch a Node.js process with an unknown `stdout` or
+`stderr` file type. This error is usually an indication of a bug within Node.js
+itself, although it is possible for user code to trigger it.
+
+<a id="ERR_V8BREAKITERATOR"></a>
+### `ERR_V8BREAKITERATOR`
+
+The V8 `BreakIterator` API was used but the full ICU data set is not installed.
+
+<a id="ERR_VALUE_OUT_OF_RANGE"></a>
+### `ERR_VALUE_OUT_OF_RANGE`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when a given value is out of the accepted range.
+
+<a id="ERR_VM_MODULE_NOT_LINKED"></a>
+### `ERR_VM_MODULE_NOT_LINKED`
+
+The module must be successfully linked before instantiation.
+
+<a id="ERR_ZLIB_BINDING_CLOSED"></a>
+### `ERR_ZLIB_BINDING_CLOSED`
+<!-- YAML
+added: v9.0.0
+removed: v10.0.0
+-->
+
+Used when an attempt is made to use a `zlib` object after it has already been
+closed.
+
+[ES Module]: esm.md
+[ICU]: intl.md#intl_internationalization_support
+[Node.js error codes]: #nodejs-error-codes
+[Subresource Integrity specification]: https://www.w3.org/TR/SRI/#the-integrity-attribute
+[V8's stack trace API]: https://github.com/v8/v8/wiki/Stack-Trace-API
+[WHATWG Supported Encodings]: util.md#util_whatwg_supported_encodings
+[WHATWG URL API]: url.md#url_the_whatwg_url_api
+[`"exports"`]: packages.md#packages_exports
+[`"imports"`]: packages.md#packages_imports
+[`'uncaughtException'`]: process.md#process_event_uncaughtexception
+[`--disable-proto=throw`]: cli.md#cli_disable_proto_mode
+[`--force-fips`]: cli.md#cli_force_fips
+[`Class: assert.AssertionError`]: assert.md#assert_class_assert_assertionerror
+[`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE
+[`EventEmitter`]: events.md#events_class_eventemitter
+[`MessagePort`]: worker_threads.md#worker_threads_class_messageport
+[`Object.getPrototypeOf`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
+[`Object.setPrototypeOf`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
+[`REPL`]: repl.md
+[`Writable`]: stream.md#stream_class_stream_writable
+[`child_process`]: child_process.md
+[`cipher.getAuthTag()`]: crypto.md#crypto_cipher_getauthtag
+[`crypto.getDiffieHellman()`]: crypto.md#crypto_crypto_getdiffiehellman_groupname
+[`crypto.scrypt()`]: crypto.md#crypto_crypto_scrypt_password_salt_keylen_options_callback
+[`crypto.scryptSync()`]: crypto.md#crypto_crypto_scryptsync_password_salt_keylen_options
+[`crypto.timingSafeEqual()`]: crypto.md#crypto_crypto_timingsafeequal_a_b
+[`dgram.connect()`]: dgram.md#dgram_socket_connect_port_address_callback
+[`dgram.createSocket()`]: dgram.md#dgram_dgram_createsocket_options_callback
+[`dgram.disconnect()`]: dgram.md#dgram_socket_disconnect
+[`dgram.remoteAddress()`]: dgram.md#dgram_socket_remoteaddress
+[`errno`(3) man page]: https://man7.org/linux/man-pages/man3/errno.3.html
+[`fs.Dir`]: fs.md#fs_class_fs_dir
+[`fs.readFileSync`]: fs.md#fs_fs_readfilesync_path_options
+[`fs.readdir`]: fs.md#fs_fs_readdir_path_options_callback
+[`fs.symlink()`]: fs.md#fs_fs_symlink_target_path_type_callback
+[`fs.symlinkSync()`]: fs.md#fs_fs_symlinksync_target_path_type
+[`fs.unlink`]: fs.md#fs_fs_unlink_path_callback
+[`fs`]: fs.md
+[`hash.digest()`]: crypto.md#crypto_hash_digest_encoding
+[`hash.update()`]: crypto.md#crypto_hash_update_data_inputencoding
+[`http`]: http.md
+[`https`]: https.md
+[`libuv Error handling`]: https://docs.libuv.org/en/v1.x/errors.html
+[`net`]: net.md
+[`new URL(input)`]: url.md#url_new_url_input_base
+[`new URLSearchParams(iterable)`]: url.md#url_new_urlsearchparams_iterable
+[`package.json`]: packages.md#packages_node_js_package_json_field_definitions
+[`process.on('exit')`]: process.md#Event:-`'exit'`
+[`process.send()`]: process.md#process_process_send_message_sendhandle_options_callback
+[`process.setUncaughtExceptionCaptureCallback()`]: process.md#process_process_setuncaughtexceptioncapturecallback_fn
+[`readable._read()`]: stream.md#stream_readable_read_size_1
+[`require('crypto').setEngine()`]: crypto.md#crypto_crypto_setengine_engine_flags
+[`require()`]: modules.md#modules_require_id
+[`server.close()`]: net.md#net_server_close_callback
+[`server.listen()`]: net.md#net_server_listen
+[`sign.sign()`]: crypto.md#crypto_sign_sign_privatekey_outputencoding
+[`stream.pipe()`]: stream.md#stream_readable_pipe_destination_options
+[`stream.push()`]: stream.md#stream_readable_push_chunk_encoding
+[`stream.unshift()`]: stream.md#stream_readable_unshift_chunk_encoding
+[`stream.write()`]: stream.md#stream_writable_write_chunk_encoding_callback
+[`subprocess.kill()`]: child_process.md#child_process_subprocess_kill_signal
+[`subprocess.send()`]: child_process.md#child_process_subprocess_send_message_sendhandle_options_callback
+[`util.getSystemErrorName(error.errno)`]: util.md#util_util_getsystemerrorname_err
+[`zlib`]: zlib.md
+[crypto digest algorithm]: crypto.md#crypto_crypto_gethashes
+[define a custom subpath]: packages.md#packages_subpath_exports
+[domains]: domain.md
+[event emitter-based]: events.md#events_class_eventemitter
+[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
+[policy]: policy.md
+[self-reference a package using its name]: packages.md#packages_self_referencing_a_package_using_its_name
+[stream-based]: stream.md
+[syscall]: https://man7.org/linux/man-pages/man2/syscalls.2.html
+[try-catch]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
+[vm]: vm.md
name = name.replace('out/', '')
name = os.path.abspath(name + '.exe')
+ name = os.environ.get('VM_PATH', name)
+
if not exists(name):
raise ValueError('Could not find executable. Should be ' + name)
if failed_count:
skip_tests = options.skip_tests[:]
skip_tests.extend(failed_case_paths)
- skip_tests.extend(excluded_case_paths)
+ # skip_tests.extend(excluded_case_paths)
skip_tests = list(set(skip_tests))
skip_tests.sort()
WriteFileWithList(SKIP_LIST_FILENAME, skip_tests)
if __name__ == '__main__':
+ os.environ['LWNODE_RUNNING_ON_TESTS'] = '1'
sys.exit(Main())