├── LICENSE.TXT ├── README.md ├── memprof ├── CMakeLists.txt ├── include │ └── cheerp │ │ └── memprof.h ├── memprof.cpp ├── memprof_priv.h ├── memprofui.cpp └── tests │ ├── example1.html │ ├── example2.html │ ├── example3.html │ ├── exampleUI1.html │ ├── exampleUI2.html │ ├── memoryHoarder.cpp │ ├── test1.cpp │ ├── test2.cpp │ └── test3.c ├── stdlibs └── Makefile ├── system ├── CMakeLists.txt ├── browser.cpp ├── common.cpp ├── divti3.c ├── futex.h ├── impl.h ├── threads.cpp ├── wasi.cpp ├── wasi_api.h └── wasi_shim.cpp ├── wasm ├── Makefile └── memory.cpp └── webgles ├── Makefile ├── gl2.h ├── webgles.cpp ├── webgles.h └── webglesutils.cpp /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: 3 | ============================================================================== 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | ---- LLVM Exceptions to the Apache 2.0 License ---- 209 | 210 | As an exception, if, as a result of your compiling your source code, portions 211 | of this Software are embedded into an Object form of such source code, you 212 | may redistribute such embedded portions in such Object form without complying 213 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 214 | 215 | In addition, if you combine or link compiled forms of this Software with 216 | software that is licensed under the GPLv2 ("Combined Software") and if a 217 | court of competent jurisdiction determines that the patent provision (Section 218 | 3), the indemnity provision (Section 9) or other Section of the License 219 | conflicts with the conditions of the GPLv2, you may retroactively and 220 | prospectively choose to deem waived or otherwise exclude such Section(s) of 221 | the License, but only in their entirety and only with respect to the Combined 222 | Software. 223 | 224 | ============================================================================== 225 | Software from third parties included in the LLVM Project: 226 | ============================================================================== 227 | The LLVM Project contains third party software which is under different license 228 | terms. All such code will be identified clearly using at least one of two 229 | mechanisms: 230 | 1) It will be in a separate directory tree with its own `LICENSE.txt` or 231 | `LICENSE` file at the top containing the specific license and restrictions 232 | which apply to that software, or 233 | 2) It will contain specific license and restriction terms at the top of every 234 | file. 235 | 236 | ============================================================================== 237 | Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): 238 | ============================================================================== 239 | University of Illinois/NCSA 240 | Open Source License 241 | 242 | Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. 243 | All rights reserved. 244 | 245 | Developed by: 246 | 247 | LLVM Team 248 | 249 | University of Illinois at Urbana-Champaign 250 | 251 | http://llvm.org 252 | 253 | Permission is hereby granted, free of charge, to any person obtaining a copy of 254 | this software and associated documentation files (the "Software"), to deal with 255 | the Software without restriction, including without limitation the rights to 256 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 257 | of the Software, and to permit persons to whom the Software is furnished to do 258 | so, subject to the following conditions: 259 | 260 | * Redistributions of source code must retain the above copyright notice, 261 | this list of conditions and the following disclaimers. 262 | 263 | * Redistributions in binary form must reproduce the above copyright notice, 264 | this list of conditions and the following disclaimers in the 265 | documentation and/or other materials provided with the distribution. 266 | 267 | * Neither the names of the LLVM Team, University of Illinois at 268 | Urbana-Champaign, nor the names of its contributors may be used to 269 | endorse or promote products derived from this Software without specific 270 | prior written permission. 271 | 272 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 273 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 274 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 275 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 276 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 277 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 278 | SOFTWARE. 279 | 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cheerp: A C++ compiler for the Web 2 | ================================== 3 | 4 | Please report bugs on GitHub: 5 | https://github.com/leaningtech/cheerp-meta/issues 6 | 7 | Cheerp GLES implementation installation 8 | --------------------------------------- 9 | 10 | ``` 11 | make -C webgles install INSTALL_PREFIX=/opt/cheerp CHEERP_PREFIX=/opt/cheerp 12 | ``` 13 | 14 | Cheerp helper library for Wasm 15 | --------------------------------------- 16 | 17 | ``` 18 | make -C wasm install INSTALL_PREFIX=/opt/cheerp CHEERP_PREFIX=/opt/cheerp 19 | ``` 20 | 21 | Cheerp PreExecuted standard C++ library installation (both generic and asm.js version) 22 | ---------------------------------------------------- 23 | 24 | ``` 25 | make -C stdlibs install INSTALL_PREFIX=/opt/cheerp CHEERP_PREFIX=/opt/cheerp 26 | ``` 27 | 28 | Cheerp syscall library installation (only generic js version) 29 | --------------------------------------- 30 | 31 | ``` 32 | cd system 33 | cmake -B build_genericjs -DCMAKE_INSTALL_PREFIX=/opt/cheerp -DCMAKE_TOOLCHAIN_FILE=/opt/cheerp/share/cmake/Modules/CheerpToolchain.cmake -DCMAKE_BUILD_TYPE=Release . 34 | make -C build_genericjs 35 | make install -C build_genericjs 36 | cd .. 37 | ``` 38 | 39 | Cheerp syscall library installation (only asm.js version) 40 | --------------------------------------- 41 | 42 | ``` 43 | cmake -B build_asmjs -DCMAKE_INSTALL_PREFIX=/opt/cheerp -DCMAKE_TOOLCHAIN_FILE=/opt/cheerp/share/cmake/Modules/CheerpWasmToolchain.cmake -DCMAKE_BUILD_TYPE=Release . 44 | make -C build_asmjs 45 | make install -C build_asmjs 46 | cd .. 47 | ``` 48 | -------------------------------------------------------------------------------- /memprof/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | 3 | cmake_minimum_required (VERSION 3.20) 4 | # Rename as appropiate 5 | project (CHEERP_MEMPROF) 6 | 7 | add_library(memprof STATIC memprof.cpp memprofui.cpp) 8 | target_include_directories(memprof PUBLIC include) 9 | set_target_properties(memprof PROPERTIES PUBLIC_HEADER "include/cheerp/memprof.h") 10 | 11 | install(TARGETS memprof 12 | ARCHIVE DESTINATION lib/asmjs 13 | PUBLIC_HEADER DESTINATION include/client/cheerp 14 | ) 15 | 16 | macro(def_test pathVar) 17 | set(path "${pathVar}") 18 | cmake_path(GET path STEM testName) 19 | add_executable(${testName} EXCLUDE_FROM_ALL tests/${path}) 20 | target_link_libraries(${testName} PUBLIC memprof) 21 | target_link_options(${testName} PRIVATE -cheerp-make-module=es6 -cheerp-pretty-code) 22 | endmacro() 23 | 24 | def_test(test1.cpp) 25 | def_test(test2.cpp) 26 | def_test(test3.c) 27 | def_test(memoryHoarder.cpp) 28 | -------------------------------------------------------------------------------- /memprof/include/cheerp/memprof.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | #ifndef CHEERP_MEMPROF 3 | #define CHEERP_MEMPROF 4 | 5 | #include 6 | typedef int CHEERP_MEMPROF_TAG; 7 | 8 | #ifdef __cplusplus 9 | extern "C" 10 | { 11 | #endif 12 | //Create a named tag that will records the allocations/free that will follow 13 | __attribute__((cheerp_wasm)) CHEERP_MEMPROF_TAG cheerpMemProfAnnotate(const char *tag_name); 14 | 15 | //Close the tag and report what was create and not yet freed (logging the report to the console) 16 | __attribute__((cheerp_wasm)) void cheerpMemProfClose(CHEERP_MEMPROF_TAG tag); 17 | 18 | //Log to the console the memory allocated after the tag creation and yet to be freed 19 | __attribute__((cheerp_wasm)) void cheerpMemProfLive(CHEERP_MEMPROF_TAG tag); 20 | 21 | //Return the amount of memory allocated after the tag creation and yet to be freed 22 | __attribute__((cheerp_wasm)) size_t cheerpMemProfUsed(CHEERP_MEMPROF_TAG tag); 23 | 24 | //Return the total amount of memory currently allocated by the program 25 | __attribute__((cheerp_wasm)) size_t cheerpMemProfTotalUsed(); 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /memprof/memprof.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | #define INTERNAL_MUSL 3 | #include 4 | #include 5 | #include 6 | #include "memprof_priv.h" 7 | 8 | //Undefine the next row if you prefer to have the stacktrace built at the error generation 9 | #define STACKTRACE_BUILT_LAZILY 10 | 11 | //Change the number here to have a different depth, or comment it to have it unlimited 12 | //#define STACKTRACE_DEPTH 15 13 | 14 | class [[cheerp::genericjs]] CheerpAllocationsTracker 15 | { 16 | class AllocationData 17 | { 18 | public: 19 | AllocationData(const size_t dim, client::Error* error) : 20 | AllocationData(dim, error, NULL) {} 21 | AllocationData(const size_t dim, client::String* stackTrace) : 22 | AllocationData(dim, NULL, stackTrace) {} 23 | client::String* getStackTrace() 24 | { 25 | if (stackTrace == NULL) 26 | { 27 | stackTrace = error->get_stack(); 28 | if (stackTrace->startsWith("Error")) 29 | stackTrace = stackTrace->slice(6); 30 | error = NULL; 31 | } 32 | return stackTrace; 33 | } 34 | client::TArray* getPrettyStackTrace(bool isTopDown) 35 | { 36 | auto& curr = arrayStackTrace[isTopDown?1:0]; 37 | if (curr == NULL) 38 | { 39 | if (arrayStackTrace[0] == NULL) 40 | { 41 | StackTraceParsing stackTraceParsing(getStackTrace()); 42 | arrayStackTrace[0] = stackTraceParsing.getStackTrace(); 43 | } 44 | if (isTopDown) 45 | { 46 | arrayStackTrace[1] = new client::TArray; 47 | for (int i = 0; iget_length(); i++) 48 | arrayStackTrace[1]->push((*arrayStackTrace[0])[i]); 49 | arrayStackTrace[1]->reverse(); 50 | } 51 | } 52 | return curr; 53 | } 54 | const CHEERP_MEMPROF_TAG insertionTime; 55 | const size_t dim; 56 | private: 57 | AllocationData(const size_t dim, client::Error* error, client::String* stackTrace) : 58 | insertionTime(getTimestamp()), dim(dim), 59 | error(error), stackTrace(stackTrace), arrayStackTrace{NULL,NULL} {} 60 | client::Error* error; 61 | client::String* stackTrace; 62 | client::TArray* arrayStackTrace[2]; 63 | }; 64 | class StatTreeNode 65 | { 66 | public: 67 | StatTreeNode(client::String* name) : 68 | liveMemory(0), function(name), childrenNodes(NULL) {} 69 | void addAllocation(const bool isTopDown, AllocationData* data, int depth) 70 | { 71 | liveMemory += data->dim; 72 | 73 | auto stack = data->getPrettyStackTrace(isTopDown); 74 | if (depth >= stack->get_length()) 75 | return; 76 | if (childrenNodes == NULL) 77 | childrenNodes = new client::Map; 78 | 79 | client::String* line = new client::String((*stack)[depth]); 80 | if (childrenNodes->has(line) == false) 81 | { 82 | childrenNodes->set(line, new StatTreeNode(line)); 83 | } 84 | childrenNodes->get(line)->addAllocation(isTopDown, data, depth+1); 85 | } 86 | client::Object* buildObject() 87 | { 88 | client::TArray* children = new client::TArray; 89 | if (childrenNodes) 90 | { 91 | client::TArray* sons= new client::TArray; 92 | 93 | childrenNodes->forEach(cheerp::Callback([sons](StatTreeNode* a, client::String* s) 94 | { 95 | sons->push(a); 96 | })); 97 | 98 | sons->sort(cheerp::Callback([](StatTreeNode* a, StatTreeNode* b) 99 | { 100 | if (a->liveMemory < b->liveMemory) 101 | return 1; 102 | if (a->liveMemory > b->liveMemory) 103 | return -1; 104 | return 0; 105 | })); 106 | sons->forEach(cheerp::Callback([children](StatTreeNode* a) 107 | { 108 | children->push(a->buildObject()); 109 | })); 110 | } 111 | return CHEERP_OBJECT(liveMemory, function, children); 112 | } 113 | private: 114 | size_t liveMemory; 115 | client::String* function; 116 | client::Map* childrenNodes; 117 | }; 118 | CheerpAllocationsTracker() 119 | { 120 | nameTags = new client::Map; 121 | allocatedMemory = new client::Map; 122 | totalMemoryAllocated = 0; 123 | } 124 | static CheerpAllocationsTracker *instance() 125 | { 126 | static CheerpAllocationsTracker *AT = 0; 127 | if (AT == 0) 128 | AT = new CheerpAllocationsTracker(); 129 | return AT; 130 | } 131 | void registerAlloc_(uintptr_t ptr, size_t dim, client::String* s) 132 | { 133 | if (ptr == 0) 134 | return; 135 | allocatedMemory->set(ptr, new AllocationData(dim, s)); 136 | totalMemoryAllocated += dim; 137 | } 138 | void registerAlloc_(uintptr_t ptr, size_t dim, client::Error* e) 139 | { 140 | if (ptr == 0) 141 | return; 142 | allocatedMemory->set(ptr, new AllocationData(dim, e)); 143 | totalMemoryAllocated += dim; 144 | } 145 | void registerFree_(uintptr_t ptr) 146 | { 147 | if (ptr == 0) 148 | return; 149 | if (allocatedMemory->has(ptr) == false) 150 | client::console.log("Invalid free detected\n", getStackTrace()); 151 | else 152 | { 153 | totalMemoryAllocated -= allocatedMemory->get(ptr)->dim; 154 | allocatedMemory->delete_(ptr); 155 | } 156 | } 157 | CHEERP_MEMPROF_TAG createLabel_(client::String* name_tag) 158 | { 159 | timestampManager.increaseTimestamp(); 160 | CHEERP_MEMPROF_TAG t = getTimestamp(); 161 | nameTags->set(t, name_tag); 162 | return t; 163 | } 164 | void statusTracking_(CHEERP_MEMPROF_TAG tag) 165 | { 166 | client::String* s = new client::String(""); 167 | if (nameTags->get(tag)->get_length() != 0) 168 | s = s->concat("Tag ")->concat(nameTags->get(tag))->concat("\n"); 169 | else 170 | s = s->concat("Untagged\n"); 171 | s = s->concat("Memory allocated after tag : ")->concat(client::String(memoryUsedStartingAtTag(tag)).padStart(8))->concat("\n"); 172 | s = s->concat("Memory allocated in total : ")->concat(client::String(totalUsed()).padStart(8)); 173 | client::console.log(s); 174 | allocatedMemory->forEach(cheerp::Callback([tag](AllocationData *d, uintptr_t ptr) 175 | { 176 | if (d->insertionTime < tag) 177 | return; 178 | logMemoryBlock(d, ptr); 179 | } 180 | )); 181 | } 182 | size_t memoryUsedStartingAtTag(CHEERP_MEMPROF_TAG tag) 183 | { 184 | size_t currMemoryAllocated = 0; 185 | allocatedMemory->forEach(cheerp::Callback([tag, &currMemoryAllocated](AllocationData *d, uintptr_t ptr) 186 | { 187 | if (d->insertionTime < tag) 188 | return; 189 | currMemoryAllocated += d->dim; 190 | } 191 | )); 192 | return currMemoryAllocated; 193 | } 194 | size_t totalUsed() 195 | { 196 | return totalMemoryAllocated; 197 | } 198 | static client::String *getStackTrace() 199 | { 200 | client::Error* e = getError(); 201 | client::String* s = e->get_stack(); 202 | if (s->startsWith("Error")) 203 | s = s->slice(6); 204 | return s; 205 | } 206 | static client::Error *getError() 207 | { 208 | client::Object* old; 209 | __asm__("Error.stackTraceLimit" : "=r"(old) : ); 210 | #ifndef STACKTRACE_DEPTH 211 | __asm__("Error.stackTraceLimit = Infinity"); 212 | #else 213 | __asm__("Error.stackTraceLimit = %0" : : "r"(STACKTRACE_DEPTH + 5)); 214 | #endif 215 | client::Error* e = new client::Error(); 216 | __asm__("Error.stackTraceLimit = %0" : : "r"(old)); 217 | return e; 218 | } 219 | public: 220 | static client::TArray* liveAllocations() 221 | { 222 | client::TArray* allocations= new client::TArray; 223 | 224 | instance()->allocatedMemory->forEach(cheerp::Callback([&allocations](AllocationData *d, uintptr_t ptr) 225 | { 226 | size_t size = d->dim; 227 | uintptr_t address = ptr; 228 | client::TArray* stackTrace = d->getPrettyStackTrace(true); 229 | client::Object* composite = CHEERP_OBJECT(size, address, stackTrace); 230 | allocations->push(composite); 231 | } 232 | )); 233 | return allocations; 234 | } 235 | static client::Object* liveAllocationsTree(const bool isTopDown) 236 | { 237 | StatTreeNode stat(new client::String("")); 238 | instance()->allocatedMemory->forEach(cheerp::Callback([&stat, isTopDown](AllocationData *d, uintptr_t ptr) 239 | { 240 | stat.addAllocation(isTopDown, d, 0); 241 | } 242 | )); 243 | return stat.buildObject(); 244 | } 245 | static void statusTracking(int tracker_descriptor) 246 | { 247 | CheerpAllocationsTracker::instance()->statusTracking_(tracker_descriptor); 248 | } 249 | 250 | static CHEERP_MEMPROF_TAG createLabel(const char *name_tag) 251 | { 252 | client::String* s = new client::String(name_tag); 253 | return CheerpAllocationsTracker::instance()->createLabel_(s); 254 | } 255 | 256 | static void closeTracking(CHEERP_MEMPROF_TAG tag) 257 | { 258 | } 259 | 260 | static size_t totalLiveMemory() 261 | { 262 | return CheerpAllocationsTracker::instance()->totalUsed(); 263 | } 264 | 265 | static size_t liveMemory(CHEERP_MEMPROF_TAG tag) 266 | { 267 | return CheerpAllocationsTracker::instance()->memoryUsedStartingAtTag(tag); 268 | } 269 | 270 | static void registerAlloc(uintptr_t ptr, uintptr_t size) 271 | { 272 | #ifndef STACKTRACE_BUILT_LAZILY 273 | client::String* x = CheerpAllocationsTracker::getStackTrace(); 274 | #else 275 | client::Error* x = CheerpAllocationsTracker::getError(); 276 | #endif 277 | CheerpAllocationsTracker::instance()->registerAlloc_(ptr, size, x); 278 | } 279 | 280 | static void registerFree(uintptr_t ptr) 281 | { 282 | CheerpAllocationsTracker::instance()->registerFree_(ptr); 283 | } 284 | 285 | private: 286 | static uintptr_t charCode(const uintptr_t digit) 287 | { 288 | if (digit < 10) 289 | return (digit + '0'); 290 | else 291 | return (digit - 10 + 'a'); 292 | } 293 | static client::String *integerBaseToString(uintptr_t ptr, int base, int requiredDigits) 294 | { 295 | client::String *s = new client::String(""); 296 | while (requiredDigits-- > 0 || ptr > 0) 297 | { 298 | s = client::String::fromCharCode(charCode(ptr%base))->concat(s); 299 | ptr /= base; 300 | } 301 | return s; 302 | } 303 | class TimestampManager 304 | { 305 | public: 306 | TimestampManager() : t(0) {} 307 | void increaseTimestamp() 308 | { 309 | t++; 310 | } 311 | CHEERP_MEMPROF_TAG getCurrentTimestamp() 312 | { 313 | return t; 314 | } 315 | private: 316 | CHEERP_MEMPROF_TAG t; 317 | }; 318 | static CHEERP_MEMPROF_TAG getTimestamp() 319 | { 320 | return timestampManager.getCurrentTimestamp(); 321 | } 322 | class StackTraceParsing 323 | { 324 | public: 325 | StackTraceParsing(client::String *s) : linesParsed(1), representation(s), parsed(false) 326 | { 327 | parse(); 328 | } 329 | client::String* getRepresentation() 330 | { 331 | return representation; 332 | } 333 | client::TArray *getStackTrace() 334 | { 335 | return stackTrace; 336 | } 337 | private: 338 | client::String *parseLine(const client::String *str, const client::String *separator, const int blocks, const int index) 339 | { 340 | client::TArray *B = str->split(separator); 341 | if (B->get_length() != blocks || index >= blocks) 342 | return new client::String(""); 343 | return (*B)[index]; 344 | } 345 | void parseStackTrace(const client::String *separator, const int blocks, const int index) 346 | { 347 | client::TArray *A = representation->split("\n"); 348 | for (int i=0; iget_length(); i++) 349 | { 350 | (*A)[i] = parseLine((*A)[i], separator, blocks, index); 351 | } 352 | int score = 0; 353 | for (int i=0; iget_length() && (*A)[i]->get_length(); i++) 354 | { 355 | score++; 356 | } 357 | if (score > linesParsed) 358 | { 359 | linesParsed = score; 360 | while (A->get_length() > linesParsed) 361 | { 362 | A->pop(); 363 | } 364 | stackTrace = A; 365 | parsed = true; 366 | } 367 | } 368 | bool isParsed() 369 | { 370 | return parsed; 371 | } 372 | void parse() 373 | { 374 | //Chrome-like stack trace 375 | parseStackTrace(client::String::fromCharCode(' '), 7, 5); 376 | 377 | //Firefox-like stack trace 378 | parseStackTrace(client::String::fromCharCode('@'), 2, 0); 379 | 380 | if (!isParsed()) 381 | return; 382 | int mallocRow = -1; 383 | for (int i = 0; iget_length(); i++) 384 | { 385 | if (((*stackTrace)[i])->indexOf("_malloc") >= 0) 386 | { 387 | mallocRow = i; 388 | break; 389 | } 390 | } 391 | stackTrace->splice(0, mallocRow + 1); 392 | 393 | representation = new client::String(""); 394 | if (mallocRow == -1) 395 | representation = new client::String("\nRecommended: flag -cheerp-pretty-code"); 396 | for (int i = 0; iget_length(); i++) 397 | { 398 | representation = representation->concat("\n@")->concat(*(*stackTrace)[i]); 399 | } 400 | representation = representation->substring(1); 401 | } 402 | private: 403 | client::String *representation; 404 | client::TArray *stackTrace; 405 | int linesParsed; 406 | bool parsed; 407 | }; 408 | 409 | static client::String *prettyStack(client::String *s) 410 | { 411 | StackTraceParsing stackTraceParsing(s); 412 | return stackTraceParsing.getRepresentation(); 413 | } 414 | static void logMemoryBlock(AllocationData *d, uintptr_t p) 415 | { 416 | client::String *s = new client::String("Address : "); 417 | s = s->concat(integerBaseToString(p, 16, 8)); 418 | s = s->concat("\t\tSize : "); 419 | s = s->concat(integerBaseToString(d->dim, 10, 1)->padStart(8)); 420 | s = s->concat("\n"); 421 | s = s->concat(prettyStack(d->getStackTrace())); 422 | client::console.log(s); 423 | } 424 | client::Map * nameTags; 425 | client::Map * allocatedMemory; 426 | size_t totalMemoryAllocated; 427 | static TimestampManager timestampManager; 428 | }; 429 | 430 | [[cheerp::genericjs]] client::TArray* CheerpMemProf::liveAllocations() 431 | { 432 | return CheerpAllocationsTracker::liveAllocations(); 433 | } 434 | [[cheerp::genericjs]] client::Object* CheerpMemProf::liveAllocationsTree(bool isTopDown) 435 | { 436 | return CheerpAllocationsTracker::liveAllocationsTree(isTopDown); 437 | } 438 | [[cheerp::genericjs]] int32_t CheerpMemProf::totalLiveMemory() 439 | { 440 | return CheerpAllocationsTracker::totalLiveMemory(); 441 | } 442 | 443 | [[cheerp::wasm]] CHEERP_MEMPROF_TAG cheerpMemProfAnnotate(const char *tag_name) 444 | { 445 | return CheerpAllocationsTracker::createLabel(tag_name); 446 | } 447 | 448 | [[cheerp::wasm]] void cheerpMemProfClose(CHEERP_MEMPROF_TAG tag) 449 | { 450 | CheerpAllocationsTracker::statusTracking(tag); 451 | CheerpAllocationsTracker::closeTracking(tag); 452 | } 453 | 454 | [[cheerp::wasm]] void cheerpMemProfLive(CHEERP_MEMPROF_TAG tag) 455 | { 456 | CheerpAllocationsTracker::statusTracking(tag); 457 | } 458 | 459 | [[cheerp::wasm]] size_t cheerpMemProfUsed(CHEERP_MEMPROF_TAG tag) 460 | { 461 | return CheerpAllocationsTracker::liveMemory(tag); 462 | } 463 | 464 | [[cheerp::wasm]] size_t cheerpMemProfTotalUsed() 465 | { 466 | return CheerpAllocationsTracker::totalLiveMemory(); 467 | } 468 | 469 | extern "C" { 470 | 471 | [[cheerp::wasm]] void *__cheerp_malloc(size_t n); 472 | [[cheerp::wasm]] void *__cheerp_realloc(void* ptr, size_t n); 473 | [[cheerp::wasm]] void *__cheerp_calloc(size_t nmemb, size_t size); 474 | [[cheerp::wasm]] void __cheerp_free(void* ptr); 475 | 476 | [[cheerp::wasm]] void* malloc(size_t size) 477 | { 478 | void* ret = __cheerp_malloc(size); 479 | CheerpAllocationsTracker::registerAlloc(reinterpret_cast(ret), size); 480 | return ret; 481 | } 482 | [[cheerp::wasm]] void* realloc(void* ptr, size_t size) 483 | { 484 | CheerpAllocationsTracker::registerFree(reinterpret_cast(ptr)); 485 | void* ret = __cheerp_realloc(ptr, size); 486 | CheerpAllocationsTracker::registerAlloc(reinterpret_cast(ret), size); 487 | return ret; 488 | } 489 | [[cheerp::wasm]] void* calloc(size_t nmemb, size_t size) 490 | { 491 | void* ret = __cheerp_calloc(nmemb, size); 492 | CheerpAllocationsTracker::registerAlloc(reinterpret_cast(ret), nmemb*size); 493 | return ret; 494 | } 495 | [[cheerp::wasm]] void free(void* ptr) 496 | { 497 | CheerpAllocationsTracker::registerFree(reinterpret_cast(ptr)); 498 | __cheerp_free(ptr); 499 | } 500 | 501 | 502 | } 503 | -------------------------------------------------------------------------------- /memprof/memprof_priv.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Leaning Technologies Ltd. All Rights Reserved. 2 | #ifndef CHEERP_MEMPROF_PRIV 3 | #define CHEERP_MEMPROF_PRIV 4 | 5 | #include 6 | #include 7 | 8 | class [[cheerp::jsexport]] [[cheerp::genericjs]] CheerpMemProf 9 | { 10 | private: 11 | public: 12 | CheerpMemProf() 13 | { 14 | } 15 | client::TArray* liveAllocations(); 16 | client::Object* liveAllocationsTree(bool isTopDown); 17 | int32_t totalLiveMemory(); 18 | }; 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /memprof/memprofui.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | 3 | #include 4 | #include "memprof_priv.h" 5 | 6 | static int max(int a, int b) 7 | { 8 | return a<=b? b : a; 9 | } 10 | static int min(int a, int b) 11 | { 12 | return a<=b? a : b; 13 | } 14 | 15 | class [[cheerp::genericjs]] GraphAppearence 16 | { 17 | public: 18 | GraphAppearence() 19 | : maxMemory(1.0), widthBox (-1), howOftenDoLine(1000) 20 | { 21 | } 22 | int barTotal() const 23 | { 24 | return barSpacing + barWidth; 25 | } 26 | bool checkAgainstMaxMemory(double sample) 27 | { 28 | bool modified = false; 29 | while (sample > heightGraph * maxMemory) 30 | { 31 | modified = true; 32 | maxMemory *= 1.1; 33 | } 34 | return modified; 35 | } 36 | int heightBox() const 37 | { 38 | return marginWidth*3 + heightGraph; 39 | } 40 | bool hasBoxWidthChanged(int width, int samples = 0) 41 | { 42 | bool flag = (width != widthBox); 43 | widthBox = width; 44 | return flag; 45 | } 46 | bool areWeGoingToOverflow(int samples) const 47 | { 48 | return (samples * barTotal() - barSpacing > widthGraph()); 49 | } 50 | int widthGraph() const 51 | { 52 | return widthBox - 2*marginWidth; 53 | } 54 | void setBarWidth(int samples) 55 | { 56 | int pixelPerSample = widthGraph() / (minSamplesToShow); 57 | if (samples > minSamplesToShow) 58 | { 59 | pixelPerSample = widthGraph() / samples; 60 | } 61 | if (pixelPerSample < 1) pixelPerSample = 1; 62 | barWidth = pixelPerSample * barProportions; 63 | barSpacing = pixelPerSample * (1.0 - barProportions); 64 | if (barTotal() < pixelPerSample) 65 | { 66 | //this means that there is one extra pixel to be assigned 67 | if (barWidth == 0) 68 | barWidth ++; 69 | else if (barSpacing == 0 || barProportions < 0.5) 70 | barSpacing ++; 71 | else 72 | barWidth ++; 73 | } 74 | howOftenDoLine = 300 / barTotal(); 75 | } 76 | public: 77 | static constexpr int heightLine = 2; 78 | static constexpr int heightGraph = 130; 79 | static constexpr int marginWidth = 10; 80 | static constexpr int textBaseLine = heightGraph + marginWidth * 1.6; 81 | static constexpr double barProportions = 0.65; 82 | double maxMemory; 83 | int widthBox; 84 | int barWidth; 85 | int barSpacing; 86 | int minSamplesToShow; 87 | int howOftenDoLine; 88 | }; 89 | 90 | class [[cheerp::genericjs]] GraphData 91 | { 92 | public: 93 | GraphData(int windowLenghtInSamples) : firstIndex(0), erasedSamples(0), 94 | infiniteLength(windowLenghtInSamples <= 0 ? true : false), 95 | minimumSamplesToRemember(infiniteLength ? -1 : windowLenghtInSamples), maxMemory(0), 96 | samples(*new client::TArray()) 97 | { 98 | } 99 | client::TArray* lastSamples() 100 | { 101 | return samples.slice(); 102 | } 103 | void walkBack(int ammount) 104 | { 105 | if (minimumSamplesToRemember < 0) 106 | return; 107 | int limit = shownSamples() / 2; 108 | if (ammount > limit) ammount = limit; 109 | while (ammount-- && size() > firstIndex && shownSamples() > minimumSamplesToRemember) 110 | { 111 | firstIndex++; 112 | } 113 | if (shownSamples() > minimumSamplesToRemember) 114 | { 115 | while (erasedSamples < firstIndex) 116 | { 117 | erasedSamples++; 118 | samples.shift(); 119 | } 120 | } 121 | } 122 | int getMaxMemory() const 123 | { 124 | return maxMemory; 125 | } 126 | int shownSamples() const 127 | { 128 | return size() - firstIndex; 129 | } 130 | int sample(int index) const 131 | { 132 | return samples[index - erasedSamples]; 133 | } 134 | int skipped() const 135 | { 136 | return firstIndex; 137 | } 138 | int size() const 139 | { 140 | return samples.get_length() + erasedSamples; 141 | } 142 | void push_back(int x) 143 | { 144 | samples.push(x); 145 | maxMemory = max(maxMemory, x); 146 | } 147 | void backInTime() 148 | { 149 | firstIndex = erasedSamples; 150 | } 151 | private: 152 | client::TArray& samples; 153 | int firstIndex; 154 | int erasedSamples; 155 | public: 156 | const bool infiniteLength; 157 | private: 158 | const int minimumSamplesToRemember; 159 | int maxMemory; 160 | }; 161 | 162 | class [[cheerp::genericjs]] GraphDrawer 163 | { 164 | public: 165 | GraphDrawer() : 166 | appearence(), 167 | samplesPerPixel(1), 168 | box(nullptr), 169 | line(nullptr), 170 | currMemoryText(nullptr) 171 | { 172 | } 173 | bool checkWidthBox(int collectedSamples) 174 | { 175 | return appearence.hasBoxWidthChanged(box->get_offsetWidth(), collectedSamples); 176 | } 177 | private: 178 | static client::String *cutRepresentation(client::String* nbr, int digits) 179 | { 180 | int index = nbr->indexOf("."); 181 | if (index > 0) 182 | return nbr->substr(0, nbr->indexOf(".") + digits + 1); 183 | else 184 | return nbr; 185 | } 186 | static client::String *representTime(double X) 187 | { 188 | X /= 1000.0; 189 | double M = int(X/60); 190 | double S = X - M * 60.0 + 1e-6; 191 | 192 | S += 100.0; 193 | client::String *seconds = cutRepresentation(new client::String(S), 1)->substr(1)->concat("s"); 194 | 195 | if (M == 0) return seconds->substr(S>=110?0:1); 196 | return (cutRepresentation(new client::String(M), -1))->concat(":")->concat(seconds); 197 | } 198 | static client::String *representMemory(const char * name, double X) 199 | { 200 | int steps = 0; 201 | while (X >= 16768.0 && steps < 3) 202 | { 203 | X/=1024.0; 204 | steps++; 205 | } 206 | client::String *suffix; 207 | if (steps == 0) suffix = new client::String(" B"); 208 | if (steps == 1) suffix = new client::String(" KB"); 209 | if (steps == 2) suffix = new client::String(" MB"); 210 | if (steps == 3) suffix= new client::String(" GB"); 211 | return (new client::String(name))->concat(cutRepresentation(new client::String(X),-1))->concat(suffix); 212 | } 213 | enum class Alignment{ 214 | Left, Right, Center 215 | }; 216 | client::HTMLElement* text(int bottom, int height, int middle, Alignment alignment, client::String *str, client::HTMLElement* parent = client::document.get_body()) 217 | { 218 | client::HTMLElement* curr = client::document.createElement("span"); 219 | auto style = curr->get_style(); 220 | style->set_height(getNumberPX(height)); 221 | style->set_position("fixed"); 222 | style->set_bottom(getNumberPX(bottom)); 223 | switch (alignment) 224 | { 225 | case Alignment::Left: 226 | { 227 | style->set_textAlign("left"); 228 | style->set_right(getNumberPX(middle+300)); 229 | style->set_left(getNumberPX(middle)); 230 | break; 231 | } 232 | case Alignment::Right: 233 | { 234 | style->set_textAlign("right"); 235 | style->set_right(getNumberPX(middle)); 236 | style->set_left(getNumberPX(middle-300)); 237 | break; 238 | } 239 | case Alignment::Center: 240 | { 241 | style->set_textAlign("center"); 242 | style->set_right(getNumberPX(middle+150)); 243 | style->set_left(getNumberPX(middle-150)); 244 | break; 245 | } 246 | } 247 | style->set_width("300px"); 248 | parent->appendChild(curr); 249 | curr->set_innerHTML(str); 250 | return curr; 251 | } 252 | client::HTMLElement* buildCurrentMemoryText(double maxMemory, double currLiveMemory) 253 | { 254 | return text(appearence.textBaseLine, 5, appearence.widthBox - 12, Alignment::Right, 255 | representMemory("Max:", maxMemory)->concat(representMemory(" | Current:", currLiveMemory))); 256 | } 257 | static client::HTMLElement* rectangle(int bottom, int height, int left, int width, const char *color, client::HTMLElement* parent) 258 | { 259 | client::HTMLElement* curr = rectangleBase(bottom, height, color, parent); 260 | auto style = curr->get_style(); 261 | style->set_left(getNumberPX(left)); 262 | style->set_width(getNumberPX(width)); 263 | return curr; 264 | } 265 | static client::String* getNumberPX(int a) 266 | { 267 | return (new client::String (a))->concat("px"); 268 | } 269 | static client::HTMLElement* rectangleWide(int bottom, int height, const char *color, client::HTMLElement* parent) 270 | { 271 | client::HTMLElement* curr = rectangleBase(bottom, height, color, parent); 272 | auto style = curr->get_style(); 273 | style->set_left(0); 274 | style->set_right(0); 275 | return curr; 276 | } 277 | static client::HTMLElement* rectangleBase(int bottom, int height, const char *color, client::HTMLElement* parent) 278 | { 279 | client::HTMLElement* curr = client::document.createElement("div"); 280 | auto style = curr->get_style(); 281 | style->set_position("fixed"); 282 | style->set_bottom(getNumberPX(bottom)); 283 | style->set_height(getNumberPX(height)); 284 | style->set_background(client::String(color)); 285 | parent->appendChild(curr); 286 | return curr; 287 | } 288 | void drawSingleBar(int value, int index, const char*color) 289 | { 290 | rectangle(appearence.marginWidth, height(value), xLocation((index) / samplesPerPixel), appearence.barWidth, color, box); 291 | } 292 | public: 293 | void drawBar(const GraphData& data, int firstSample, int endSample) 294 | { 295 | int low = data.sample(firstSample); 296 | int high = low; 297 | for (int i=firstSample + 1; i < endSample; i++) 298 | { 299 | int curr = data.sample(i); 300 | low = min(low, curr); 301 | high = max(high, curr); 302 | } 303 | if (endSample - firstSample > 1) 304 | { 305 | drawSingleBar(high, endSample - 1 - data.skipped(), "cornflowerblue"); 306 | } 307 | drawSingleBar(low, endSample - 1 - data.skipped(), "lightblue"); 308 | } 309 | void redrawSamples(const GraphData& data) 310 | { 311 | samplesPerPixel = 1; 312 | while (data.shownSamples() / samplesPerPixel * appearence.barTotal() > appearence.widthGraph()) { 313 | samplesPerPixel += max(1, (int)(samplesPerPixel * 0.3)); 314 | } 315 | for (int i = data.skipped(); i < data.size(); i+= samplesPerPixel) 316 | { 317 | drawBar(data, i, min(i+samplesPerPixel, data.size())); 318 | } 319 | } 320 | void drawTimelineLocations(int deltaOldSamples, double timeFromStart, double frequencyMilliseconds) 321 | { 322 | appearence.hasBoxWidthChanged(box->get_offsetWidth()); 323 | int X = 0; 324 | int Y = appearence.barTotal(); 325 | int index = 0; 326 | while (X + appearence.howOftenDoLine*Y < appearence.widthBox) 327 | { 328 | rectangle(appearence.marginWidth, appearence.heightGraph + appearence.marginWidth, X + appearence.marginWidth, 1, "grey", box); 329 | text(appearence.textBaseLine, 5, 12 + X, Alignment::Left, representTime(timeFromStart + (index * appearence.howOftenDoLine + deltaOldSamples) * frequencyMilliseconds * samplesPerPixel)); 330 | X += appearence.howOftenDoLine * Y; 331 | index++; 332 | } 333 | } 334 | void redrawBox() 335 | { 336 | if (box) 337 | { 338 | for (int i = 0; i < box->get_children()->get_length(); i++) 339 | { 340 | box->removeChild((*box->get_children())[i]); 341 | } 342 | client::document.get_body()->removeChild(box); 343 | } 344 | box = rectangleWide(0, appearence.heightBox(), "white", client::document.get_body()); 345 | line = rectangleWide(appearence.heightBox(), appearence.heightLine, "black", box); 346 | } 347 | void printCurrentMemoryText(int maxMemory, int currentLiveMemory) 348 | { 349 | if (currMemoryText != nullptr) 350 | client::document.get_body()->removeChild(currMemoryText); 351 | currMemoryText = buildCurrentMemoryText(maxMemory, currentLiveMemory); 352 | } 353 | int height(int sample_memory) 354 | { 355 | return sample_memory/appearence.maxMemory; 356 | } 357 | int xLocation(int number) 358 | { 359 | return appearence.marginWidth + number * appearence.barTotal(); 360 | } 361 | GraphAppearence appearence; 362 | int samplesPerPixel; 363 | public: 364 | client::HTMLElement* box; 365 | client::HTMLElement* line; 366 | client::HTMLElement* currMemoryText; 367 | }; 368 | 369 | 370 | class [[cheerp::genericjs]] CheerpMemProfGraphInternals { 371 | public: 372 | CheerpMemProfGraphInternals(int intervalLengthMilliseconds, int samplingPeriodMilliseconds) : 373 | frequencyMilliseconds(samplingPeriodToMilliseconds(samplingPeriodMilliseconds)), 374 | graphDrawer(), graphData(intervalLengthMilliseconds / samplingPeriodMilliseconds) 375 | { 376 | int movingWindowSeconds = intervalLenghtToSeconds(intervalLengthMilliseconds); 377 | graphDrawer.appearence.minSamplesToShow = intervalLengthMilliseconds / samplingPeriodMilliseconds * 1.2; 378 | if (graphDrawer.appearence.minSamplesToShow < 0) graphDrawer.appearence.minSamplesToShow = 120; 379 | if (graphData.infiniteLength) 380 | client::console.log("MemProfGraph invoked.\nInterval length: unbounded\nSampling period: ", frequencyMilliseconds, " milliseconds"); 381 | else 382 | client::console.log("MemProfGraph invoked.\nInterval length: ", movingWindowSeconds, " seconds\nSampling period: ", frequencyMilliseconds, " milliseconds"); 383 | client::console.log("For additional data, call from the Console cheerpMemUI.liveAllocations() for the details with stack strace of the current allocations or cheerpMemUI.lastSamples() for the array with the memory consumption data"); 384 | JsMemProf = new CheerpMemProf(); 385 | memProfGraph = this; 386 | redraw(); 387 | auto interval = client::setInterval(cheerp::Callback(mainLoop), frequencyMilliseconds); 388 | } 389 | client::TArray* lastSamples() 390 | { 391 | return graphData.lastSamples(); 392 | } 393 | private: 394 | static int intervalLenghtToSeconds(int intervalLengthMilliseconds) 395 | { 396 | if (intervalLengthMilliseconds == -1) 397 | return -1; 398 | else 399 | return (intervalLengthMilliseconds + 999) / 1000; 400 | } 401 | static int samplingPeriodToMilliseconds(int samplingPeriod) 402 | { 403 | if (samplingPeriod == -1) 404 | return 1000; 405 | else 406 | return samplingPeriod; 407 | } 408 | static CheerpMemProfGraphInternals* instance() 409 | { 410 | return memProfGraph; 411 | } 412 | void redraw() 413 | { 414 | graphDrawer.redrawBox(); 415 | graphDrawer.appearence.setBarWidth(graphData.shownSamples()); 416 | graphDrawer.drawTimelineLocations(graphData.skipped(), 0.0, frequencyMilliseconds); 417 | graphDrawer.redrawSamples(graphData); 418 | } 419 | void addSample() 420 | { 421 | double currLiveMemory = JsMemProf->totalLiveMemory(); 422 | graphData.push_back(currLiveMemory); 423 | bool hasChanged = graphDrawer.appearence.checkAgainstMaxMemory(currLiveMemory); 424 | if (graphDrawer.checkWidthBox(graphData.shownSamples())) 425 | { 426 | hasChanged = true; 427 | graphData.backInTime(); 428 | } 429 | int tries = 10; 430 | while (tries-- && graphDrawer.appearence.areWeGoingToOverflow(graphData.shownSamples() / graphDrawer.samplesPerPixel)) 431 | { 432 | hasChanged = true; 433 | graphData.walkBack(graphDrawer.appearence.howOftenDoLine); 434 | } 435 | if (!hasChanged && (graphData.shownSamples() % graphDrawer.samplesPerPixel == graphDrawer.samplesPerPixel -1)) 436 | graphDrawer.drawBar(graphData, graphData.size() - graphDrawer.samplesPerPixel, graphData.size()); 437 | else 438 | redraw(); 439 | graphDrawer.printCurrentMemoryText(graphData.getMaxMemory(), currLiveMemory); 440 | } 441 | static void mainLoop() 442 | { 443 | instance()->addSample(); 444 | } 445 | static CheerpMemProfGraphInternals* memProfGraph; 446 | const int frequencyMilliseconds; 447 | CheerpMemProf* JsMemProf; 448 | GraphData graphData; 449 | GraphDrawer graphDrawer; 450 | }; 451 | 452 | class [[cheerp::jsexport]] [[cheerp::genericjs]] CheerpMemUI 453 | { 454 | public: 455 | CheerpMemUI(int windowLenghtSeconds, int samplingPeriodMilliseconds) 456 | { 457 | cheerpMemProf = new CheerpMemProf(); 458 | memProfGraph = new CheerpMemProfGraphInternals(windowLenghtSeconds * 1000, samplingPeriodMilliseconds); 459 | } 460 | client::TArray* lastSamples() 461 | { 462 | return memProfGraph->lastSamples(); 463 | } 464 | client::TArray* liveAllocations() 465 | { 466 | return cheerpMemProf->liveAllocations(); 467 | } 468 | client::Object* liveAllocationsBottomUp() 469 | { 470 | return cheerpMemProf->liveAllocationsTree(false); 471 | } 472 | client::Object* liveAllocationsTopDown() 473 | { 474 | return cheerpMemProf->liveAllocationsTree(true); 475 | } 476 | int32_t totalLiveMemory() 477 | { 478 | return cheerpMemProf->totalLiveMemory(); 479 | } 480 | private: 481 | CheerpMemProf* cheerpMemProf; 482 | CheerpMemProfGraphInternals* memProfGraph; 483 | }; 484 | -------------------------------------------------------------------------------- /memprof/tests/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cheerp test1.js 6 | 7 | 8 | 12 |

Test1.js results

13 |

(open the debug console)

14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /memprof/tests/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cheerp test2.js 6 | 7 | 8 | 12 |

Test2.js results

13 |

(open the debug console)

14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /memprof/tests/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cheerp test3.js 6 | 7 | 8 | 12 |

Test3.js results

13 |

(open the debug console)

14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /memprof/tests/exampleUI1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cheerp test 6 | 7 | 8 |

Possibly open the console, and press this button:

9 | 10 |

Otherwise singe methods could be called by either directly

11 |

cheerpMemUI.totalLiveMemory()

12 |

Number of bytes currently allocated

13 |

cheerpMemUI.lastSamples()

14 |

Number of bytes allocated in the last profiling slots

15 |

cheerpMemUI.liveAllocations()

16 |

Object version with all current live allocations

17 |

cheerpMemUI.liveAllocationsTopDown()

18 |

Top Down tree of which function, and where on the call stack, generated an allocation

19 |

cheerpMemUI.liveAllocationsBottomUp()

20 |

Bottom up tree of which function, and where on the call stack, generated an allocation

21 |

Another test is resizing the window (back and forth)

22 | 36 | 37 |

text

38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /memprof/tests/exampleUI2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cheerp test 6 | 7 | 8 |

Possibly open the console, and press this button:

9 | 10 |

Otherwise singe methods could be called by either directly

11 |

cheerpMemUI.totalLiveMemory()

12 |

Number of bytes currently allocated

13 |

cheerpMemUI.lastSamples()

14 |

Number of bytes allocated in the last profiling slots

15 |

cheerpMemUI.liveAllocations()

16 |

Object version with all current live allocations

17 |

cheerpMemUI.liveAllocationsTopDown()

18 |

Top Down tree of which function, and where on the call stack, generated an allocation

19 |

cheerpMemUI.liveAllocationsBottomUp()

20 |

Bottom up tree of which function, and where on the call stack, generated an allocation

21 |

Another test is resizing the window (back and forth)

22 | 36 | 37 |

text

38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /memprof/tests/memoryHoarder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int sort_of_random() 9 | { 10 | return rand()%100000 + 1; 11 | } 12 | 13 | void function() 14 | { 15 | static std::vector M; 16 | static int times = 0; 17 | int DIM = sort_of_random(); 18 | char * P = (char *)malloc(DIM); 19 | if (M.size() > 15) 20 | { 21 | for (int i=0; i sort_of_random()%(times++/100+1)) 26 | { 27 | int index = sort_of_random()%M.size(); 28 | free(M[index]); 29 | M[index] = P; 30 | } 31 | else 32 | M.push_back(P); 33 | for (int i = 6; i 4 | #include 5 | #include 6 | 7 | int *leak(int N) 8 | { 9 | cheerpMemProfAnnotate("leak"); 10 | int *p = (int *)malloc(sizeof(int) * N); 11 | for (int i=1; i 1) fibonacci[i] = fibonacci[i-1] + fibonacci[i-2]; 34 | } 35 | leak(fibonacci[N]); 36 | free(fibonacci); 37 | cheerpMemProfClose(tag); 38 | } 39 | 40 | // A program that do not do anything meaningful apart from allocating memory without ever calling free. 41 | // The calls to the stack trace allow to identify the function that leaks 42 | 43 | void webMain() 44 | { 45 | CHEERP_MEMPROF_TAG tag = cheerpMemProfAnnotate("main"); 46 | functionA(10); 47 | functionA(13); 48 | cheerpMemProfLive(tag); 49 | leakOnlyUntilThereIsSpace(100000); 50 | cheerpMemProfClose(tag); 51 | } 52 | -------------------------------------------------------------------------------- /memprof/tests/test2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Leaning Technologies Ltd. All Rights Reserved. 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | void populateItems(std::vector & items, int item) 9 | { 10 | char * p = (char *)malloc(sizeof(char) * 100); 11 | CHEERP_MEMPROF_TAG tag = cheerpMemProfAnnotate("populateItems, it should not leak since we used a preallocated vector"); 12 | items.push_back(item); 13 | free(p); 14 | if (cheerpMemProfUsed(tag) > 0) 15 | { 16 | //This should never happens in this case, but here you may handle the error / save the state 17 | cheerpMemProfLive(tag); 18 | assert(false); 19 | } 20 | cheerpMemProfClose(tag); 21 | } 22 | 23 | void populateItemsRandom(std::vector & items, int item) 24 | { 25 | CHEERP_MEMPROF_TAG tag = cheerpMemProfAnnotate("populateItemsRandom, it should not leak, but it does. Why?"); 26 | items.push_back(item % (rand()+1)); 27 | if (cheerpMemProfUsed(tag) > 0 && item > 0) 28 | { 29 | //This should never happens in this case, but here you may handle the error / save the state 30 | cheerpMemProfLive(tag); 31 | assert(false); 32 | //In this case it will be triggered by rand() that needs to allocate his internal state 33 | } 34 | cheerpMemProfClose(tag); 35 | } 36 | 37 | // webMain is the entry point for web applications written in Cheerp. 38 | void webMain() 39 | { 40 | const int N = 5; 41 | 42 | CHEERP_MEMPROF_TAG tag = cheerpMemProfAnnotate("main"); 43 | std::vector items; 44 | items.reserve(N*2); 45 | for (int i=0; i 4 | #include 5 | 6 | char *allocateArray(int dim) 7 | { 8 | CHEERP_MEMPROF_TAG x = cheerpMemProfAnnotate("function"); 9 | cheerpMemProfClose(x); 10 | return (char *)malloc(dim); 11 | } 12 | 13 | //Works even for C sources 14 | int main() 15 | { 16 | const int N = 200; 17 | CHEERP_MEMPROF_TAG x = cheerpMemProfAnnotate("main"); 18 | char *p = allocateArray(N); 19 | for (int i=1; i 3 | #include 4 | #include 5 | } 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "impl.h" 14 | #include "futex.h" 15 | 16 | namespace { 17 | 18 | class [[cheerp::genericjs]] CheerpStringBuilder 19 | { 20 | private: 21 | client::String* out; 22 | void outputCodepoint(unsigned int codepoint); 23 | public: 24 | // Keep codepoint and remaining updated while encountering char ch 25 | // Potentially calls (either 0 or 1 times) outputCodepoint to populate out 26 | void processChar(unsigned int& codepoint, unsigned int& remaining, unsigned char ch); 27 | client::String* getString() 28 | { 29 | return out; 30 | } 31 | void setString(client::String* a) 32 | { 33 | out = a; 34 | } 35 | }; 36 | 37 | void CheerpStringBuilder::outputCodepoint(unsigned int codepoint) 38 | { 39 | if (codepoint <= 0xffff) 40 | { 41 | if (codepoint) 42 | out = out->concat(client::String::fromCharCode(codepoint)); 43 | } 44 | else 45 | { 46 | // surrogate pair 47 | codepoint -= 0x10000; 48 | unsigned int highSurrogate = (codepoint >> 10) + 0xd800; 49 | unsigned int lowSurrogate = (codepoint & 0x3ff) + 0xdc00; 50 | out = out->concat(client::String::fromCharCode(highSurrogate)); 51 | out = out->concat(client::String::fromCharCode(lowSurrogate)); 52 | } 53 | } 54 | 55 | void CheerpStringBuilder::processChar(unsigned int& codepoint, unsigned int& remaining, unsigned char ch) 56 | { 57 | if (ch < 192) 58 | { 59 | // Continuation bytes 60 | if (remaining > 0) 61 | { 62 | #ifndef NDEBUG 63 | assert((ch & 192) == 128); 64 | assert(remaining); 65 | #endif 66 | codepoint = codepoint << 6 | (ch & 0x3f); 67 | remaining--; 68 | // If there are more, wait 69 | if (remaining > 0) 70 | return; 71 | // Otherwise, output the codepoint 72 | } 73 | // We should be in ASCII range 74 | else 75 | { 76 | #ifndef NDEBUG 77 | assert(ch < 128u); 78 | #endif 79 | codepoint = ch; 80 | } 81 | 82 | // Output the current codepoint 83 | outputCodepoint(codepoint); 84 | } 85 | else 86 | { 87 | #ifndef NDEBUG 88 | assert(remaining == 0); 89 | #endif 90 | unsigned int mask; 91 | // Start of 2-bytes sequence 92 | if (ch <= 0xdf) 93 | { 94 | remaining = 1; 95 | mask = 0x1f; 96 | } 97 | // Start of 3-bytes sequence 98 | else if (ch <= 0xef) 99 | { 100 | remaining = 2; 101 | mask = 0x0f; 102 | } 103 | // Start of 4-bytes sequence 104 | else 105 | { 106 | remaining = 3; 107 | mask = 0x07; 108 | } 109 | 110 | codepoint = ch & mask; 111 | } 112 | } 113 | 114 | [[cheerp::genericjs]] 115 | long do_syscall_writev(const iovec* ios, long len) 116 | { 117 | static client::String* curr = new client::String(); 118 | static unsigned int codepoint = 0; 119 | static unsigned int remaining = 0; 120 | 121 | CheerpStringBuilder builder; 122 | builder.setString(curr); 123 | 124 | long __ret = 0; 125 | for (int i=0; i < len; i++) 126 | { 127 | if (ios[i].iov_len == 0) 128 | continue; 129 | 130 | int curr_len = ios[i].iov_len; 131 | __ret += curr_len; 132 | unsigned char* begin = (unsigned char*)ios[i].iov_base; 133 | for (int j=0; j* arr = builder.getString()->split("\n"); 140 | const int L = arr->get_length(); 141 | for (int i=0; i+1getTimezoneOffset() * -60.0 * 1000.0; 155 | } 156 | [[cheerp::genericjs]] 157 | double performanceNow() 158 | { 159 | return client::performance.now(); 160 | } 161 | 162 | [[cheerp::genericjs]] [[noreturn]] void raiseSignal() 163 | { 164 | __asm__("throw new Error('Cheerp: Signal raised')"); 165 | __builtin_unreachable(); 166 | } 167 | 168 | } //unnamed namespace 169 | 170 | 171 | namespace sys_internal { 172 | 173 | double timezone_offset() 174 | { 175 | return offsetInMilliseconds(); 176 | } 177 | double real_time_now() 178 | { 179 | return cheerp::date_now(); 180 | } 181 | double monotonic_time_now() 182 | { 183 | return cheerp::date_now(); 184 | } 185 | double cpu_time_now() 186 | { 187 | return performanceNow(); 188 | } 189 | 190 | bool WEAK exit_thread() 191 | { 192 | return false; 193 | } 194 | 195 | } //namespace sys_internal 196 | 197 | extern "C" { 198 | 199 | [[cheerp::genericjs]] 200 | void WEAK worker_close() 201 | { 202 | return; 203 | } 204 | 205 | long WEAK __syscall_exit(long code) 206 | { 207 | if (sys_internal::exit_thread()) 208 | { 209 | worker_close(); 210 | return 0; 211 | } 212 | 213 | raiseSignal(); 214 | return 0; 215 | } 216 | 217 | long WEAK __syscall_writev(long fd, const iovec* ios, long len) 218 | { 219 | return do_syscall_writev(ios, len); 220 | } 221 | 222 | long WEAK __syscall_readv(long fd, const iovec* ios, long len) 223 | { 224 | return -ENOSYS; 225 | } 226 | 227 | int WEAK __syscall_read(int fd, void* buf, int count) 228 | { 229 | return -ENOSYS; 230 | } 231 | 232 | long WEAK __syscall_open(const char* pathname, int flags, ...) 233 | { 234 | return -ENOSYS; 235 | 236 | } 237 | long WEAK __syscall_close(int fd) 238 | { 239 | return 0; 240 | } 241 | 242 | size_t WEAK __syscall__llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, 243 | unsigned long long* result, unsigned int whence) 244 | { 245 | return -ENOSYS; 246 | } 247 | 248 | int WEAK __syscall_rename(const char *oldpath, const char *newpath) 249 | { 250 | return -ENOSYS; 251 | } 252 | 253 | int WEAK __syscall_access(const char *pathname, int mode) 254 | { 255 | return -ENOSYS; 256 | } 257 | 258 | [[cheerp::genericjs]] 259 | bool testUseAtomicWaitJS() 260 | { 261 | bool canWait; 262 | __asm__("(()=>{var ret;try{Atomics.wait(HEAP32,0,0,0);ret=true;}catch(e){ret=false;}return ret;})()" : "=r"(canWait)); 263 | return canWait; 264 | } 265 | 266 | bool testUseAtomicWait() 267 | { 268 | return testUseAtomicWaitJS(); 269 | } 270 | 271 | 272 | } // extern "C" 273 | -------------------------------------------------------------------------------- /system/common.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include 3 | #include 4 | #include 5 | #include 6 | } 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | # define LEAN_CXX_LIB 17 | # include 18 | 19 | #include "impl.h" 20 | 21 | [[cheerp::genericjs]] client::TArray* __builtin_cheerp_environ(); 22 | [[cheerp::genericjs]] client::TArray* __builtin_cheerp_argv(); 23 | 24 | _Thread_local int tid = 1; 25 | _Thread_local int *clear_child_tid = nullptr; 26 | 27 | extern "C" { 28 | 29 | 30 | #ifdef __ASMJS__ 31 | // HACK: The value of this variables will be rewritten to the correct heap start 32 | // and end by the compiler backend 33 | char* volatile _heapStart = (char*)0xdeadbeef; 34 | char* volatile _heapEnd = (char*)0xdeadbeef; 35 | 36 | char* _heapCur = 0; 37 | #endif 38 | 39 | 40 | long WEAK __syscall_ioctl(long fd, long req, void* arg) 41 | { 42 | switch(req) 43 | { 44 | case TIOCGWINSZ: 45 | { 46 | winsize* ws = static_cast(arg); 47 | ws->ws_row = 25; 48 | ws->ws_col = 80; 49 | ws->ws_xpixel = 0; 50 | ws->ws_ypixel = 0; 51 | return 0; 52 | } 53 | default: 54 | { 55 | return -EINVAL; 56 | } 57 | } 58 | return -EINVAL; 59 | } 60 | 61 | #define WASM_PAGE (64*1024) 62 | #define ALIGN(x) ((decltype (x))((((uintptr_t)x) + WASM_PAGE-1) & ~(WASM_PAGE-1))) 63 | long WEAK __syscall_brk(void* newaddr) 64 | { 65 | #ifndef __ASMJS__ 66 | return 0; 67 | #else 68 | static char* brkEnd = nullptr; 69 | if (!brkEnd) 70 | { 71 | brkEnd = ALIGN(_heapStart); 72 | } 73 | char* a1 = reinterpret_cast(newaddr); 74 | if (a1 <= brkEnd) 75 | { 76 | return reinterpret_cast(brkEnd); 77 | } 78 | unsigned int length = (char*)newaddr - brkEnd; 79 | length = ALIGN(length); 80 | if (brkEnd < _heapEnd) 81 | { 82 | unsigned int avail = _heapEnd - brkEnd; 83 | if (avail >= length) 84 | { 85 | brkEnd += length; 86 | return reinterpret_cast(brkEnd); 87 | } 88 | length -= avail; 89 | brkEnd += avail; 90 | } 91 | int res = __builtin_cheerp_grow_memory(length>>16); 92 | if (res == -1) 93 | { 94 | return reinterpret_cast(brkEnd); 95 | } 96 | brkEnd += length; 97 | return reinterpret_cast(brkEnd); 98 | #endif 99 | } 100 | 101 | long WEAK __syscall_clock_gettime64(int clock_id, struct timespec* tp) 102 | { 103 | if (!tp) 104 | return 0; 105 | switch (clock_id) 106 | { 107 | case CLOCK_REALTIME: 108 | { 109 | // 'now' is in milliseconds 110 | double now = sys_internal::real_time_now(); 111 | now += sys_internal::timezone_offset(); 112 | tp->tv_sec = now / 1000; 113 | tp->tv_nsec = (now-(tp->tv_sec*1000.0))*1000.0*1000.0; 114 | break; 115 | } 116 | case CLOCK_MONOTONIC: 117 | { 118 | // 'now' is in milliseconds 119 | double now = sys_internal::monotonic_time_now(); 120 | tp->tv_sec = now / 1000; 121 | tp->tv_nsec = (now-(tp->tv_sec*1000.0))*1000.0*1000.0; 122 | break; 123 | } 124 | case CLOCK_PROCESS_CPUTIME_ID: 125 | { 126 | // 'now' is in milliseconds 127 | double now = sys_internal::cpu_time_now(); 128 | tp->tv_sec = now / 1000; 129 | tp->tv_nsec = (now-(tp->tv_sec*1000.0))*1000.0*1000.0; 130 | break; 131 | } 132 | default: 133 | { 134 | return -EINVAL; 135 | } 136 | } 137 | return 0; 138 | } 139 | 140 | long __syscall_exit(long); 141 | 142 | long WEAK __syscall_tkill(long a1, ...) 143 | { 144 | __syscall_exit(EX_OSERR); 145 | return 0; 146 | } 147 | 148 | long WEAK __syscall_exit_group(long code,...) 149 | { 150 | __syscall_exit(code); 151 | return 0; 152 | } 153 | 154 | 155 | long WEAK __syscall_futex(uint32_t* uaddr, int futex_op, ...) 156 | { 157 | return -ENOSYS; 158 | } 159 | 160 | int WEAK __syscall_mprotect(long addr, size_t len, int prot) 161 | { 162 | return 0; 163 | } 164 | 165 | long WEAK __syscall_rt_sigprocmask(long a1, ...) 166 | { 167 | return 0; 168 | } 169 | 170 | long WEAK __syscall_futex_time64(long a1,...) 171 | { 172 | return -ENOSYS; 173 | } 174 | 175 | long WEAK __syscall_clock_gettime32(long a1,...) 176 | { 177 | return -ENOSYS; 178 | } 179 | 180 | long WEAK __syscall_gettimeofday_time32(long a1,...) 181 | { 182 | return -ENOSYS; 183 | } 184 | 185 | long WEAK __syscall_set_robust_list(long a1, ...) 186 | { 187 | return -ENOSYS; 188 | } 189 | 190 | long WEAK __syscall_fcntl64(long a1, ...) 191 | { 192 | return -ENOSYS; 193 | } 194 | 195 | long WEAK __syscall_fstat64(long a1,...) 196 | { 197 | return -ENOSYS; 198 | } 199 | 200 | long WEAK __syscall_rt_sigaction(long a1,...) 201 | { 202 | return -ENOSYS; 203 | } 204 | 205 | long WEAK __syscall_mmap2(long a1, long a2, long a3, long a4, long a5, long a6) 206 | { 207 | return -ENOSYS; 208 | } 209 | 210 | long WEAK __syscall_munmap(long a1, long length) 211 | { 212 | return -ENOSYS; 213 | } 214 | 215 | int WEAK pthread_cancel(struct __pthread* t) 216 | { 217 | return __syscall_exit(EX_SOFTWARE); 218 | } 219 | 220 | [[cheerp::genericjs]] static size_t client_to_utf8(char *dest, size_t dlen, const client::String *str) { 221 | constexpr uint32_t REPLACEMENT_CHARACTER = 0xFFFD; 222 | constexpr uint32_t MAX_CODEPOINT = 0x10FFFF; 223 | constexpr uint32_t INVALID_CODEPOINT = -1; 224 | 225 | const size_t slen = str->get_length(); 226 | size_t j = 0; 227 | for (size_t i = 0; i < slen; ++i) { 228 | uint32_t cp = str->charCodeAt(i); 229 | 230 | if (cp >= 0xD800 && cp <= 0xDFFF) { 231 | if (i + 1 < slen) { 232 | uint32_t trail = str->charCodeAt(++i); 233 | cp = 0x10000 + ((cp & 0x3FF) | (trail & 0x3FF)); 234 | } else { 235 | // Missing lower surrogate 236 | cp = INVALID_CODEPOINT; 237 | } 238 | } 239 | 240 | if (cp > MAX_CODEPOINT) 241 | cp = INVALID_CODEPOINT; 242 | 243 | if (cp <= 0x7F) { 244 | if (j < dlen) 245 | *dest++ = static_cast(cp); 246 | 247 | j += 1; 248 | } else if (cp <= 0x7FF) { 249 | if (j + 1 < dlen) { 250 | *dest++ = 0xC0 | (cp >> 6); 251 | *dest++ = 0x80 | (cp & 63); 252 | } 253 | 254 | j += 2; 255 | } else if (cp <= 0xFFFF) { 256 | if (j + 2 < dlen) { 257 | *dest++ = 0xE0 | (cp >> 12); 258 | *dest++ = 0x80 | ((cp >> 6) & 63); 259 | *dest++ = 0x80 | (cp & 63); 260 | } 261 | 262 | j += 3; 263 | } else { 264 | if (j + 3 < dlen) { 265 | *dest++ = 0xF0 | (cp >> 18); 266 | *dest++ = 0x80 | ((cp >> 12) & 63); 267 | *dest++ = 0x80 | ((cp >> 6) & 63); 268 | *dest++ = 0x80 | (cp & 63); 269 | } 270 | 271 | j += 4; 272 | } 273 | } 274 | return j; 275 | } 276 | 277 | #define MAX_ENTRIES 64 278 | static size_t buf_size = 0; 279 | static char argv_environ_buf[MAX_ENTRIES * 1024]; 280 | 281 | [[cheerp::genericjs]] static size_t read_to_buf(char* dest, size_t n, const client::TArray *arr, size_t idx) 282 | { 283 | if (idx >= arr->get_length()) 284 | return 0; 285 | 286 | size_t len = client_to_utf8(dest, n, (*arr)[idx]); 287 | if (len < n) 288 | dest[len] = '\0'; 289 | return len + 1; 290 | } 291 | 292 | [[cheerp::genericjs]] static client::TArray* read_nodejs_args(const client::String *opt_name) { 293 | client::TArray *result = new client::TArray(); 294 | 295 | const client::TArray *argv = nullptr; 296 | __asm__("(typeof process == 'undefined' ? [] : process.argv) || []" : "=r"(argv)); 297 | 298 | for (size_t i = 0; i < argv->get_length(); ++i) { 299 | const client::String *arg = (*argv)[i]; 300 | if (arg->startsWith(opt_name)) 301 | result->push(arg->substr(opt_name->get_length())); 302 | } 303 | return result; 304 | } 305 | 306 | [[cheerp::genericjs]] static size_t read_arg(char *dest, size_t n, size_t idx) 307 | { 308 | static client::TArray *client_argv = __builtin_cheerp_argv() 309 | ? __builtin_cheerp_argv() 310 | : read_nodejs_args(new client::String("--cheerp-arg=")); 311 | return read_to_buf(dest, n, client_argv, idx); 312 | } 313 | 314 | void WEAK __syscall_main_args(int* argc_p, char*** argv_p) 315 | { 316 | static char* argv[MAX_ENTRIES]; 317 | 318 | size_t i = 0; 319 | while (true) { 320 | if (i > sizeof(argv)/sizeof(argv[0])) 321 | abort(); 322 | 323 | const size_t rem = sizeof argv_environ_buf - buf_size; 324 | size_t len = read_arg(&argv_environ_buf[buf_size], rem, i); 325 | 326 | if (!len) 327 | break; 328 | if (len > rem) 329 | abort(); 330 | 331 | argv[i++] = &argv_environ_buf[buf_size]; 332 | buf_size += len; 333 | } 334 | 335 | *argc_p = i; 336 | *argv_p = argv; 337 | } 338 | 339 | [[cheerp::genericjs]] static size_t read_env(char *dest, size_t n, size_t idx) 340 | { 341 | static client::TArray *client_environ = __builtin_cheerp_environ() 342 | ? __builtin_cheerp_environ() 343 | : read_nodejs_args(new client::String("--cheerp-env=")); 344 | return read_to_buf(dest, n, client_environ, idx); 345 | } 346 | 347 | 348 | extern "C" char **environ; 349 | void WEAK __syscall_main_environ() { 350 | static char* cheerp_environ[MAX_ENTRIES]; 351 | 352 | size_t i = 0; 353 | while (true) { 354 | if (i >= sizeof(cheerp_environ)/sizeof(cheerp_environ[0])) 355 | abort(); 356 | 357 | const size_t rem = sizeof argv_environ_buf - buf_size; 358 | size_t len = read_env(&argv_environ_buf[buf_size], rem, i); 359 | 360 | if (!len) 361 | break; 362 | if (len > rem) 363 | abort(); 364 | 365 | cheerp_environ[i++] = &argv_environ_buf[buf_size]; 366 | buf_size += len; 367 | } 368 | 369 | cheerp_environ[i] = 0; 370 | environ = cheerp_environ; 371 | } 372 | 373 | long WEAK __syscall_membarrier(int cmd, unsigned int flags) 374 | { 375 | return 0; 376 | } 377 | 378 | long WEAK __syscall_sched_setscheduler(pid_t pid, int policy, const struct sched_param *param) 379 | { 380 | return 0; 381 | } 382 | 383 | long WEAK __syscall_sched_yield() 384 | { 385 | return 0; 386 | } 387 | 388 | #if defined(__CHEERP__) && defined(__ASMJS__) 389 | [[cheerp::wasm]] 390 | #endif 391 | long __syscall_set_thread_area(unsigned long tp) 392 | { 393 | #if defined(__CHEERP__) && defined(__ASMJS__) 394 | __builtin_cheerp_set_thread_pointer(tp); 395 | #endif 396 | return 0; 397 | } 398 | 399 | long WEAK __syscall_set_tid_address(int *tidptr) 400 | { 401 | clear_child_tid = tidptr; 402 | return tid; 403 | } 404 | 405 | long WEAK __syscall_sched_getaffinity(pid_t pid, int cpusetsize, unsigned long* mask) 406 | { 407 | // Only a pid of 0 (the current process) is supported. 408 | assert(pid == 0); 409 | 410 | unsigned char* set = reinterpret_cast(mask); 411 | set[0] = 1; 412 | for (int i = 1; i < cpusetsize; i++) 413 | set[i] = 0; 414 | return cpusetsize; 415 | } 416 | 417 | } 418 | -------------------------------------------------------------------------------- /system/divti3.c: -------------------------------------------------------------------------------- 1 | 2 | // Adapted from newlib/libc/machine/h8500/divsi3.c 3 | 4 | #define divnorm(num, den, sign) \ 5 | { \ 6 | if (num < 0) \ 7 | { \ 8 | num = -num; \ 9 | sign = 1; \ 10 | } \ 11 | else \ 12 | { \ 13 | sign = 0; \ 14 | } \ 15 | \ 16 | if (den < 0) \ 17 | { \ 18 | den = - den; \ 19 | sign = 1 - sign; \ 20 | } \ 21 | } 22 | 23 | unsigned long long 24 | divmodti4(int modwanted, unsigned long long num, unsigned long long den) 25 | { 26 | unsigned long long int bit = 1; 27 | long long int res = 0; 28 | long long prevden; 29 | while (den < num && bit && !(den & (1LL<<63))) 30 | { 31 | den = den << 1; 32 | bit = bit << 1; 33 | } 34 | while (bit) 35 | { 36 | if (num >= den) 37 | { 38 | num -= den; 39 | res |= bit; 40 | } 41 | bit = bit >> 1; 42 | den = den >> 1; 43 | } 44 | if (modwanted) 45 | return num; 46 | else 47 | return res; 48 | } 49 | 50 | 51 | #define exitdiv(sign, res) if (sign) { res = - res;} return res; 52 | 53 | long long 54 | __modti3 (long long numerator, long long denominator) 55 | { 56 | int sign = 0; 57 | long long dividend; 58 | long long modul; 59 | 60 | 61 | if (numerator < 0) 62 | { 63 | numerator = -numerator; 64 | sign = 1; 65 | } 66 | if (denominator < 0) 67 | { 68 | denominator = -denominator; 69 | } 70 | 71 | modul = divmodti4 (1, numerator, denominator); 72 | if (sign) 73 | return - modul; 74 | return modul; 75 | } 76 | 77 | 78 | long long 79 | __divti3 (long long numerator, long long denominator) 80 | { 81 | int sign; 82 | long long dividend; 83 | long long modul; 84 | divnorm (numerator, denominator, sign); 85 | 86 | dividend = divmodti4 (0, numerator, denominator); 87 | exitdiv (sign, dividend); 88 | } 89 | 90 | long long 91 | __umodti3 (unsigned long long numerator, unsigned long long denominator) 92 | { 93 | long long dividend; 94 | long long modul; 95 | 96 | modul = divmodti4 (1, numerator, denominator); 97 | return modul; 98 | } 99 | 100 | long long 101 | __udivti3 (unsigned long long numerator, unsigned long long denominator) 102 | { 103 | int sign; 104 | long long dividend; 105 | long long modul; 106 | dividend = divmodti4 (0, numerator, denominator); 107 | return dividend; 108 | } 109 | -------------------------------------------------------------------------------- /system/futex.h: -------------------------------------------------------------------------------- 1 | // This file was copied from musl at src/internal/futex.h 2 | 3 | #ifndef _INTERNAL_FUTEX_H 4 | #define _INTERNAL_FUTEX_H 5 | 6 | #define FUTEX_WAIT 0 7 | #define FUTEX_WAKE 1 8 | #define FUTEX_FD 2 9 | #define FUTEX_REQUEUE 3 10 | #define FUTEX_CMP_REQUEUE 4 11 | #define FUTEX_WAKE_OP 5 12 | #define FUTEX_LOCK_PI 6 13 | #define FUTEX_UNLOCK_PI 7 14 | #define FUTEX_TRYLOCK_PI 8 15 | #define FUTEX_WAIT_BITSET 9 16 | // The following 4 are not included in the original musl file. I've added them here for completion. 17 | #define FUTEX_WAKE_BITSET 10 18 | #define FUTEX_WAIT_REQUEUE_PI 11 19 | #define FUTEX_CMP_REQUEUE_PI 12 20 | #define FUTEX_LOCK_PI2 13 21 | 22 | #define FUTEX_PRIVATE 128 23 | 24 | #define FUTEX_CLOCK_REALTIME 256 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /system/impl.h: -------------------------------------------------------------------------------- 1 | #ifndef _IMPL_H_ 2 | #define _IMPL_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define WEAK __attribute__((weak)) 8 | 9 | namespace sys_internal { 10 | double timezone_offset(); 11 | double real_time_now(); 12 | double monotonic_time_now(); 13 | double cpu_time_now(); 14 | bool exit_thread(); 15 | } 16 | 17 | extern _Thread_local int tid; 18 | extern _Thread_local int *clear_child_tid; 19 | 20 | class FutexSpinLock 21 | { 22 | private: 23 | pthread_spinlock_t spinLock; 24 | 25 | public: 26 | FutexSpinLock() 27 | { 28 | pthread_spin_init(&spinLock, PTHREAD_PROCESS_PRIVATE); 29 | } 30 | ~FutexSpinLock() 31 | { 32 | pthread_spin_destroy(&spinLock); 33 | } 34 | void lock() 35 | { 36 | pthread_spin_lock(&spinLock); 37 | } 38 | void unlock() 39 | { 40 | pthread_spin_unlock(&spinLock); 41 | } 42 | }; 43 | 44 | struct ThreadSpawnInfo { 45 | int func; 46 | int args; 47 | int tls; 48 | int tid; 49 | int stack; 50 | int ctid; 51 | }; 52 | 53 | enum QueueMessageType { 54 | SPAWN_THREAD = 0, 55 | KILL_THREAD, 56 | KILL_ALL_THREADS, 57 | }; 58 | 59 | struct QueueMessage { 60 | QueueMessageType type; 61 | union { 62 | ThreadSpawnInfo spawnInfo; 63 | int tid; 64 | }; 65 | }; 66 | 67 | template 68 | class MessageQueue 69 | { 70 | private: 71 | T messageBuffer; 72 | pthread_mutex_t mutexQueue; 73 | pthread_cond_t condQueueEmpty; 74 | pthread_cond_t condQueueFull; 75 | bool queueFull = false; 76 | 77 | public: 78 | MessageQueue() 79 | { 80 | pthread_mutex_init(&mutexQueue, NULL); 81 | pthread_cond_init(&condQueueEmpty, NULL); 82 | pthread_cond_init(&condQueueFull, NULL); 83 | } 84 | ~MessageQueue() 85 | { 86 | pthread_cond_destroy(&condQueueFull); 87 | pthread_cond_destroy(&condQueueEmpty); 88 | pthread_mutex_destroy(&mutexQueue); 89 | } 90 | void send(T message) 91 | { 92 | pthread_mutex_lock(&mutexQueue); 93 | while (queueFull) 94 | pthread_cond_wait(&condQueueEmpty, &mutexQueue); 95 | 96 | messageBuffer = cheerp::utility::move(message); 97 | queueFull = true; 98 | pthread_cond_signal(&condQueueFull); 99 | pthread_mutex_unlock(&mutexQueue); 100 | } 101 | T receive() 102 | { 103 | T copiedBuffer; 104 | pthread_mutex_lock(&mutexQueue); 105 | while (!queueFull) 106 | pthread_cond_wait(&condQueueFull, &mutexQueue); 107 | 108 | copiedBuffer = cheerp::utility::move(messageBuffer); 109 | queueFull = false; 110 | pthread_cond_signal(&condQueueEmpty); 111 | pthread_mutex_unlock(&mutexQueue); 112 | 113 | return copiedBuffer; 114 | } 115 | }; 116 | 117 | extern "C" { 118 | void __syscall_main_args(int* argc, char*** argv); 119 | bool testUseAtomicWait(); 120 | } 121 | 122 | #endif //_IMPL_H_ 123 | -------------------------------------------------------------------------------- /system/threads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #define LEAN_CXX_LIB 10 | #include 11 | #include 12 | 13 | #include "impl.h" 14 | #include "futex.h" 15 | 16 | namespace [[cheerp::genericjs]] client { 17 | class ThreadingObject : public Object{ 18 | public: 19 | void set_func(int); 20 | void set_args(int); 21 | void set_tls(int); 22 | void set_tid(int); 23 | int get_tid(); 24 | void set_stack(int); 25 | void set_ctid(int); 26 | }; 27 | } 28 | 29 | enum atomicWaitStatus { 30 | UNINITIALIZED = 0, 31 | YES, 32 | NO, 33 | }; 34 | 35 | [[cheerp::genericjs]] client::ThreadingObject* __builtin_cheerp_get_threading_object(); 36 | [[cheerp::genericjs]] client::Blob* __builtin_cheerp_get_threading_blob(); 37 | 38 | [[cheerp::genericjs]] client::Worker* utilityWorker = nullptr; 39 | [[cheerp::genericjs]] client::Map* workerList = nullptr; 40 | FutexSpinLock futexSpinLock; 41 | MessageQueue threadMessagingQueue; 42 | 43 | extern "C" { 44 | 45 | void _start(); 46 | 47 | std::atomic mainThreadWaitAddress = 0; 48 | 49 | bool isBrowserMainThread() 50 | { 51 | static _Thread_local atomicWaitStatus canUseAtomicWait = UNINITIALIZED; 52 | if (canUseAtomicWait == UNINITIALIZED) 53 | { 54 | if (testUseAtomicWait()) 55 | canUseAtomicWait = YES; 56 | else 57 | canUseAtomicWait = NO; 58 | } 59 | 60 | return (canUseAtomicWait == NO); 61 | } 62 | 63 | uint32_t wakeThreadsFutex(uint32_t* uaddr, uint32_t amount) 64 | { 65 | uint32_t threadsWokenUp = 0; 66 | // If the main thread is waiting on this address, handle this case specially. 67 | futexSpinLock.lock(); 68 | if (uaddr == mainThreadWaitAddress.load()) 69 | { 70 | // Notify the main thread that it can wake up. 71 | mainThreadWaitAddress.store(0); 72 | amount -= 1; 73 | threadsWokenUp = 1; 74 | } 75 | futexSpinLock.unlock(); 76 | if (amount == 0) 77 | return threadsWokenUp; 78 | return threadsWokenUp + __builtin_cheerp_atomic_notify(uaddr, amount); 79 | 80 | } 81 | 82 | long __syscall_futex(uint32_t* uaddr, int futex_op, ...) 83 | { 84 | bool isPrivate = futex_op & FUTEX_PRIVATE; 85 | bool isRealTime = futex_op & FUTEX_CLOCK_REALTIME; 86 | futex_op &= ~FUTEX_PRIVATE; 87 | futex_op &= ~FUTEX_CLOCK_REALTIME; 88 | (void)isPrivate; 89 | (void)isRealTime; 90 | 91 | // These ops are currently not implemented, since musl doesn't use them. 92 | assert(futex_op != FUTEX_FD); 93 | assert(futex_op != FUTEX_WAKE_OP); 94 | assert(futex_op != FUTEX_WAIT_BITSET); 95 | assert(futex_op != FUTEX_WAKE_BITSET); 96 | assert(futex_op != FUTEX_LOCK_PI2); 97 | assert(futex_op != FUTEX_TRYLOCK_PI); 98 | assert(futex_op != FUTEX_CMP_REQUEUE_PI); 99 | assert(futex_op != FUTEX_WAIT_REQUEUE_PI); 100 | 101 | bool isBrowserMain = isBrowserMainThread(); 102 | 103 | switch (futex_op) 104 | { 105 | case FUTEX_WAKE: 106 | { 107 | va_list ap; 108 | va_start(ap, futex_op); 109 | uint32_t val = va_arg(ap, uint32_t); 110 | va_end(ap); 111 | 112 | return wakeThreadsFutex(uaddr, val); 113 | } 114 | case FUTEX_WAIT: 115 | { 116 | va_list ap; 117 | va_start(ap, futex_op); 118 | uint32_t val = va_arg(ap, uint32_t); 119 | const struct timespec *ts = va_arg(ap, const struct timespec *); 120 | va_end(ap); 121 | int64_t timeout = -1; 122 | if (ts != nullptr) 123 | timeout = ts->tv_sec * 1000000000 + ts->tv_nsec; 124 | 125 | // If this is the main thread, it's illegal to do a futex wait operation. 126 | // Instead, we busy wait while checking a special value. 127 | if (isBrowserMain) 128 | { 129 | // Manually test the value at uaddr against val. If they do not match, return EAGAIN. 130 | futexSpinLock.lock(); 131 | if (*uaddr != val) 132 | { 133 | futexSpinLock.unlock(); 134 | return -EAGAIN; 135 | } 136 | mainThreadWaitAddress.store(uaddr); 137 | if (*uaddr != val) 138 | { 139 | mainThreadWaitAddress.store(0); 140 | futexSpinLock.unlock(); 141 | return -EAGAIN; 142 | } 143 | futexSpinLock.unlock(); 144 | int64_t startTime = 0; 145 | if (timeout != -1) 146 | { 147 | struct timespec startTimeStruct; 148 | clock_gettime(CLOCK_MONOTONIC, &startTimeStruct); 149 | startTime = startTimeStruct.tv_sec * 1000000000 + startTimeStruct.tv_nsec; 150 | } 151 | while (mainThreadWaitAddress.load() != 0) 152 | { 153 | if (timeout != -1) 154 | { 155 | struct timespec timeNowStruct; 156 | clock_gettime(CLOCK_MONOTONIC, &timeNowStruct); 157 | int64_t timeNow = timeNowStruct.tv_sec * 1000000000 + timeNowStruct.tv_nsec; 158 | if (timeNow - startTime >= timeout) 159 | return -ETIMEDOUT; 160 | } 161 | } 162 | } 163 | else 164 | { 165 | uint32_t ret = __builtin_cheerp_atomic_wait(uaddr, val, timeout); 166 | if (ret == 1) 167 | return -EAGAIN; // Value at uaddr did not match val. 168 | else if (ret == 2) 169 | return -ETIMEDOUT; 170 | } 171 | return 0; 172 | } 173 | case FUTEX_REQUEUE: 174 | case FUTEX_CMP_REQUEUE: 175 | { 176 | va_list ap; 177 | va_start(ap, futex_op); 178 | uint32_t val = va_arg(ap, uint32_t); 179 | uint32_t val2 = va_arg(ap, unsigned long); 180 | uint32_t* uaddr2 = va_arg(ap, uint32_t*); 181 | 182 | if (futex_op == FUTEX_CMP_REQUEUE) 183 | { 184 | uint32_t val3 = va_arg(ap, uint32_t); 185 | if (*uaddr != val3) 186 | { 187 | va_end(ap); 188 | return -EAGAIN; 189 | } 190 | } 191 | va_end(ap); 192 | 193 | // We wake up val + val2 threads. The requeued threads are spurious wake-ups. 194 | uint32_t threadsWokenUp = wakeThreadsFutex(uaddr, val + val2); 195 | 196 | if (futex_op == FUTEX_REQUEUE && threadsWokenUp >= val) 197 | return val; 198 | return threadsWokenUp; 199 | } 200 | case FUTEX_LOCK_PI: 201 | { 202 | // TODO 203 | assert(false); 204 | } 205 | case FUTEX_UNLOCK_PI: 206 | { 207 | // TODO 208 | assert(false); 209 | } 210 | default: 211 | { 212 | // This should be unreachable, all unhandled cases are asserted for at the top. 213 | assert(false); 214 | } 215 | } 216 | } 217 | 218 | [[cheerp::wasm]] 219 | long __syscall_set_thread_area(unsigned long tp); 220 | 221 | [[cheerp::genericjs]] 222 | void startWorkerFunction(unsigned int fp, unsigned int args, unsigned int tls, int newThreadId, unsigned int stack, unsigned int ctid) 223 | { 224 | // If this is the main thread, only spawn utility thread. 225 | // Else, use utility thread to spawn the new thread. 226 | if (utilityWorker == nullptr) 227 | { 228 | client::ThreadingObject* threadingObject = __builtin_cheerp_get_threading_object(); 229 | client::Blob* blob = __builtin_cheerp_get_threading_blob(); 230 | threadingObject->set_func(fp); 231 | threadingObject->set_args(args); 232 | threadingObject->set_tls(tls); 233 | threadingObject->set_tid(newThreadId); 234 | threadingObject->set_stack(stack); 235 | threadingObject->set_ctid(ctid); 236 | client::WorkerOptions* opts = new client::WorkerOptions(); 237 | opts->set_name("Utility"); 238 | utilityWorker = new client::Worker(client::URL.createObjectURL(blob), opts); 239 | utilityWorker->postMessage(threadingObject); 240 | return; 241 | } 242 | QueueMessage message; 243 | message.type = QueueMessageType::SPAWN_THREAD; 244 | message.spawnInfo.func = fp; 245 | message.spawnInfo.args = args; 246 | message.spawnInfo.tls = tls; 247 | message.spawnInfo.tid = newThreadId; 248 | message.spawnInfo.stack = stack; 249 | message.spawnInfo.ctid = ctid; 250 | 251 | threadMessagingQueue.send(message); 252 | } 253 | 254 | [[cheerp::genericjs]] 255 | void waitForMessage(); 256 | 257 | [[cheerp::genericjs]] 258 | void spawnThreadFromUtility(ThreadSpawnInfo spawnInfo) 259 | { 260 | client::ThreadingObject* threadingObject = __builtin_cheerp_get_threading_object(); 261 | client::Blob* blob = __builtin_cheerp_get_threading_blob(); 262 | threadingObject->set_func(spawnInfo.func); 263 | threadingObject->set_args(spawnInfo.args); 264 | threadingObject->set_tls(spawnInfo.tls); 265 | threadingObject->set_tid(spawnInfo.tid); 266 | threadingObject->set_stack(spawnInfo.stack); 267 | threadingObject->set_ctid(spawnInfo.ctid); 268 | client::WorkerOptions* opts = new client::WorkerOptions(); 269 | opts->set_name((new client::String("Thread "))->concat(spawnInfo.tid)); 270 | client::Worker* worker = new client::Worker(client::URL.createObjectURL(blob), opts); 271 | worker->set_onmessage(waitForMessage); 272 | worker->postMessage(threadingObject); 273 | workerList->set(spawnInfo.tid, worker); 274 | } 275 | 276 | [[cheerp::genericjs]] 277 | void waitForMessage() 278 | { 279 | while (true) 280 | { 281 | QueueMessage message = threadMessagingQueue.receive(); 282 | if (message.type == QueueMessageType::SPAWN_THREAD) 283 | { 284 | spawnThreadFromUtility(message.spawnInfo); 285 | break; 286 | } 287 | else if (message.type == QueueMessageType::KILL_THREAD) 288 | { 289 | client::String* tidString = new client::String(message.tid); 290 | client::Worker* worker = workerList->get(message.tid); 291 | worker->terminate(); 292 | workerList->delete_(message.tid); 293 | } 294 | else if (message.type == QueueMessageType::KILL_ALL_THREADS) 295 | { 296 | auto terminateWorker = [](client::Worker* w) { 297 | w->terminate(); 298 | }; 299 | workerList->forEach(terminateWorker); 300 | workerList->clear(); 301 | client::self.close(); 302 | break; 303 | } 304 | } 305 | } 306 | 307 | [[cheerp::genericjs]] 308 | void leak_thread() 309 | { 310 | client::String* throwObj = new client::String("LeakUtilityThread"); 311 | __builtin_cheerp_throw(throwObj); 312 | } 313 | 314 | [[cheerp::genericjs]] 315 | void reschedule() 316 | { 317 | workerList = new client::Map(); 318 | client::setTimeout(waitForMessage, 0); 319 | } 320 | 321 | [[cheerp::wasm]] 322 | void *utilityRoutine(void *arg) 323 | { 324 | reschedule(); 325 | leak_thread(); 326 | return NULL; 327 | } 328 | 329 | [[cheerp::wasm]] 330 | void spawnUtilityThread() 331 | { 332 | pthread_t utilityTid; 333 | 334 | pthread_create(&utilityTid, NULL, &utilityRoutine, NULL); 335 | } 336 | 337 | [[cheerp::genericjs]] 338 | void callStart() 339 | { 340 | _start(); 341 | __builtin_cheerp_thread_setup_resolve(); 342 | } 343 | 344 | [[cheerp::genericjs]] 345 | void spawnUtility() 346 | { 347 | spawnUtilityThread(); 348 | // When the utility workers sends a message, continue execution in main thread with the _start function. 349 | utilityWorker->addEventListener("message", callStart); 350 | } 351 | 352 | [[cheerp::genericjs]] [[noreturn]] 353 | void worker_close() 354 | { 355 | QueueMessage message; 356 | message.type = QueueMessageType::KILL_THREAD; 357 | client::ThreadingObject* threadingObject = __builtin_cheerp_get_threading_object(); 358 | message.tid = threadingObject->get_tid(); 359 | threadMessagingQueue.send(message); 360 | client::self.close(); 361 | client::String* throwObj = new client::String("ThreadExit"); 362 | __builtin_cheerp_throw(throwObj); 363 | } 364 | 365 | long WEAK __syscall_gettid(void) 366 | { 367 | return tid; 368 | } 369 | 370 | [[cheerp::wasm]] 371 | [[cheerp::jsexport]] 372 | void workerEntry(unsigned long tp, unsigned int func, unsigned int arg, int newThreadId, unsigned int stack, unsigned int ctid) 373 | { 374 | // This is the setup for a worker thread. 375 | // Set the thread pointer 376 | if (tp != 0) 377 | __syscall_set_thread_area(tp); 378 | // Set the thread stack pointer 379 | __builtin_cheerp_stack_restore(reinterpret_cast(stack)); 380 | // Assign tid 381 | tid = newThreadId; 382 | // Set the clear_child_tid if necessary 383 | if (ctid != 0) 384 | clear_child_tid = reinterpret_cast(ctid); 385 | // Call the function passed to pthread_create with the arguments passed 386 | void *(*entry)(void *) = reinterpret_cast(func); 387 | void *argument = reinterpret_cast(arg); 388 | entry(argument); 389 | } 390 | 391 | [[cheerp::wasm]] 392 | long WEAK __syscall_clone4(int (*func)(void *), void *stack, int flags, void *arg, void *ptid, void *tls, void *ctid) 393 | { 394 | static int uniqueThreadId = 2; 395 | int newThreadId = uniqueThreadId++; 396 | int *set_tid = 0; 397 | void *tlsPointer = 0; 398 | 399 | // Assert that only the flags are set that we expect. 400 | assert(flags & CLONE_VM); 401 | flags &= ~CLONE_VM; 402 | assert(flags & CLONE_FS); 403 | flags &= ~CLONE_FS; 404 | assert(flags & CLONE_FILES); 405 | flags &= ~CLONE_FILES; 406 | assert(flags & CLONE_SIGHAND); 407 | flags &= ~CLONE_SIGHAND; 408 | assert(flags & CLONE_THREAD); 409 | flags &= ~CLONE_THREAD; 410 | assert(flags & CLONE_SYSVSEM); 411 | flags &= ~CLONE_SYSVSEM; 412 | assert(flags & CLONE_DETACHED); 413 | flags &= ~CLONE_DETACHED; 414 | 415 | if (flags & CLONE_SETTLS) 416 | { 417 | flags &= ~CLONE_SETTLS; 418 | tlsPointer = tls; 419 | } 420 | if (flags & CLONE_PARENT_SETTID) 421 | { 422 | flags &= ~CLONE_PARENT_SETTID; 423 | *(int*)ptid = newThreadId; 424 | } 425 | if (flags & CLONE_CHILD_CLEARTID) 426 | { 427 | flags &= ~CLONE_CHILD_CLEARTID; 428 | set_tid = (int*)ctid; 429 | } 430 | 431 | assert(flags == 0); 432 | 433 | startWorkerFunction((unsigned int)func, (unsigned int)arg, (unsigned int)tlsPointer, newThreadId, (unsigned int)stack, (unsigned int)set_tid); 434 | return newThreadId; 435 | } 436 | 437 | long __syscall_membarrier(int cmd, unsigned int flags) 438 | { 439 | return 0; 440 | } 441 | 442 | [[cheerp::genericjs]] 443 | int getHardwareConcurrency() 444 | { 445 | return client::navigator.get_hardwareConcurrency(); 446 | } 447 | 448 | long __syscall_sched_getaffinity(pid_t pid, int cpusetsize, unsigned long* mask) 449 | { 450 | // Only a pid of 0 (the current process) is supported. 451 | assert(pid == 0); 452 | 453 | int amountCores = getHardwareConcurrency(); 454 | unsigned char* set = reinterpret_cast(mask); 455 | for (int i = 0; i < cpusetsize; i++) 456 | { 457 | if (amountCores == 0) 458 | set[i] = 0; 459 | else if (amountCores >= 8) 460 | { 461 | set[i] = 255; 462 | amountCores -= 8; 463 | } 464 | else 465 | { 466 | set[i] = (1 << amountCores) - 1; 467 | amountCores = 0; 468 | } 469 | } 470 | return cpusetsize; 471 | } 472 | 473 | [[cheerp::genericjs]] 474 | void killAllThreads() 475 | { 476 | QueueMessage message; 477 | message.type = QueueMessageType::KILL_ALL_THREADS; 478 | threadMessagingQueue.send(message); 479 | utilityWorker = nullptr; 480 | } 481 | 482 | long __syscall_exit(long); 483 | 484 | long __syscall_exit_group(long code) 485 | { 486 | killAllThreads(); 487 | __syscall_exit(code); 488 | return 0; 489 | } 490 | 491 | } 492 | 493 | namespace sys_internal { 494 | 495 | [[cheerp::genericjs]] [[noreturn]] 496 | void closeMainThreadAsWorker() 497 | { 498 | client::self.close(); 499 | client::String* throwObj = new client::String("ExitMainThreadWorker"); 500 | __builtin_cheerp_throw(throwObj); 501 | } 502 | 503 | bool exit_thread() 504 | { 505 | if (!isBrowserMainThread()) 506 | { 507 | // If the main thread runs in a Worker, close it. 508 | if (tid == 1) 509 | closeMainThreadAsWorker(); 510 | else if (clear_child_tid != nullptr) 511 | { 512 | // If clear_child_tid is set, write 0 to the address it points to, 513 | // and do a FUTEX_WAKE on the address. 514 | uint32_t *wake_address = reinterpret_cast(clear_child_tid); 515 | *clear_child_tid = 0; 516 | __syscall_futex(wake_address, FUTEX_WAKE, 1, nullptr, nullptr, 0); 517 | } 518 | return true; 519 | } 520 | return false; 521 | } 522 | 523 | } 524 | -------------------------------------------------------------------------------- /system/wasi.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | } 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "wasi_api.h" 19 | 20 | #include "impl.h" 21 | 22 | namespace sys_internal { 23 | 24 | double timezone_offset() 25 | { 26 | return 0; 27 | } 28 | double real_time_now() 29 | { 30 | uint64_t time; 31 | __wasi_errno_t err = __wasi_clock_time_get(__WASI_CLOCKID_REALTIME, 0, &time); 32 | return err? std::numeric_limits::quiet_NaN() : time; 33 | } 34 | double monotonic_time_now() 35 | { 36 | uint64_t time; 37 | __wasi_errno_t err = __wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, &time); 38 | return err? std::numeric_limits::quiet_NaN() : time; 39 | } 40 | double cpu_time_now() 41 | { 42 | uint64_t time; 43 | __wasi_errno_t err = __wasi_clock_time_get(__WASI_CLOCKID_PROCESS_CPUTIME_ID, 0, &time); 44 | return err? std::numeric_limits::quiet_NaN() : time; 45 | } 46 | 47 | } //namespace sys_internal 48 | 49 | namespace { 50 | 51 | int rootFd = -1; 52 | 53 | struct DirData { 54 | int fd; 55 | __wasi_dircookie_t cookie; 56 | DirData* next; 57 | 58 | static DirData* opened; 59 | 60 | static void add(int fd) 61 | { 62 | DirData* newD = static_cast(malloc(sizeof(DirData))); 63 | newD->fd = fd; 64 | newD->cookie = 0; 65 | newD->next = opened; 66 | opened = newD; 67 | } 68 | static void remove(int fd) 69 | { 70 | DirData* prev = nullptr; 71 | for(DirData* d = opened; d != nullptr; d = d->next) 72 | { 73 | if (d->fd != fd) 74 | continue; 75 | if (prev) 76 | { 77 | prev->next = d->next; 78 | } 79 | else 80 | { 81 | opened = d->next; 82 | } 83 | free(d); 84 | } 85 | } 86 | static DirData* get(int fd) 87 | { 88 | for(DirData* d = opened; d != nullptr; d = d->next) 89 | { 90 | if (d->fd == fd) 91 | return d; 92 | } 93 | return nullptr; 94 | } 95 | }; 96 | 97 | } 98 | 99 | extern "C" { 100 | 101 | long WEAK __syscall_writev(long fd, const iovec* ios, long len) 102 | { 103 | static_assert(sizeof(iovec) == sizeof(__wasi_iovec_t), "incompatible iovec size"); 104 | __wasi_size_t ret; 105 | __wasi_errno_t err = __wasi_fd_write(fd, reinterpret_cast(ios), len, &ret); 106 | return err? -err : ret; 107 | } 108 | 109 | long WEAK __syscall_write(int fd, void* buf, long count) 110 | { 111 | __wasi_ciovec_t ios { reinterpret_cast(buf), __wasi_size_t(count) }; 112 | __wasi_size_t ret; 113 | __wasi_errno_t err = __wasi_fd_write(fd, &ios, 1, &ret); 114 | return err? -err : ret; 115 | } 116 | 117 | long WEAK __syscall_open(const char* pathname, int flags, ...) 118 | { 119 | if (rootFd < 0) 120 | { 121 | return -EPERM; 122 | } 123 | if (pathname[0] == '/') 124 | pathname++; 125 | 126 | 127 | __wasi_oflags_t oflags = 0; 128 | if (flags & O_CREAT) 129 | oflags |= __WASI_OFLAGS_CREAT; 130 | if (flags & O_DIRECTORY) 131 | oflags |= __WASI_OFLAGS_DIRECTORY; 132 | if (flags & O_EXCL) 133 | oflags |= __WASI_OFLAGS_EXCL; 134 | if (flags & O_TRUNC) 135 | oflags |= __WASI_OFLAGS_TRUNC; 136 | __wasi_fdflags_t fdflags = 0; 137 | if (flags & O_SYNC) 138 | fdflags |= __WASI_FDFLAGS_SYNC; 139 | if (flags & O_RSYNC) 140 | fdflags |= __WASI_FDFLAGS_RSYNC; 141 | if (flags & O_DSYNC) 142 | fdflags |= __WASI_FDFLAGS_DSYNC; 143 | if (flags & O_NONBLOCK) 144 | fdflags |= __WASI_FDFLAGS_NONBLOCK; 145 | if (flags & O_APPEND) 146 | fdflags |= __WASI_FDFLAGS_APPEND; 147 | int openmode = flags & O_ACCMODE; 148 | __wasi_rights_t rights = 0b11111111111111111111111111111; 149 | rights &= ~(__WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_READ | 150 | __WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_ALLOCATE | 151 | __WASI_RIGHTS_FD_READDIR | __WASI_RIGHTS_FD_FILESTAT_SET_SIZE); 152 | if (openmode == O_RDONLY || openmode == O_RDWR) 153 | rights |= __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR; 154 | if (openmode == O_WRONLY || openmode == O_RDWR) 155 | { 156 | rights |= 157 | __WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_WRITE | 158 | __WASI_RIGHTS_FD_ALLOCATE | 159 | __WASI_RIGHTS_FD_FILESTAT_SET_SIZE; 160 | } 161 | 162 | __wasi_lookupflags_t lookup_flags = 0; 163 | if ((flags & O_NOFOLLOW) == 0) 164 | lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW; 165 | __wasi_rights_t fs_rights_base = rights; 166 | __wasi_rights_t fs_rights_inheriting = fs_rights_base; 167 | __wasi_fd_t ret; 168 | __wasi_errno_t err = __wasi_path_open(rootFd, lookup_flags, pathname, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &ret); 169 | if (err == 0 && (flags&O_DIRECTORY)) 170 | { 171 | DirData::add(ret); 172 | } 173 | return err? -err : ret; 174 | } 175 | 176 | long WEAK __syscall_close(int fd) 177 | { 178 | DirData::remove(fd); 179 | return __wasi_fd_close(fd); 180 | } 181 | 182 | // NOTE: Internal musl definition 183 | struct statx 184 | { 185 | uint32_t stx_mask; 186 | uint32_t stx_blksize; 187 | uint64_t stx_attributes; 188 | uint32_t stx_nlink; 189 | uint32_t stx_uid; 190 | uint32_t stx_gid; 191 | uint16_t stx_mode; 192 | uint16_t pad1; 193 | uint64_t stx_ino; 194 | uint64_t stx_size; 195 | uint64_t stx_blocks; 196 | uint64_t stx_attributes_mask; 197 | struct 198 | { 199 | int64_t tv_sec; 200 | uint32_t tv_nsec; 201 | int32_t pad; 202 | } stx_atime, stx_btime, stx_ctime, stx_mtime; 203 | uint32_t stx_rdev_major; 204 | uint32_t stx_rdev_minor; 205 | uint32_t stx_dev_major; 206 | uint32_t stx_dev_minor; 207 | uint64_t spare[14]; 208 | }; 209 | int WEAK __syscall_statx(int dirfd, const char* pathname, int flags, int mask, statx* st) 210 | { 211 | __wasi_lookupflags_t wflags = 0; 212 | __wasi_filestat_t wst; 213 | __wasi_errno_t err = 0; 214 | if ((flags & AT_SYMLINK_NOFOLLOW) == 0) 215 | wflags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW; 216 | memset(st, 0, sizeof(struct statx)); 217 | // TODO: Actually support dirfd, for now we assume it's just fstat 218 | if(dirfd >= 0) 219 | { 220 | err = __wasi_fd_filestat_get(dirfd, &wst); 221 | } 222 | else 223 | { 224 | if(pathname[0] == '/') 225 | pathname++; 226 | err = __wasi_path_filestat_get(rootFd, wflags, pathname, &wst); 227 | } 228 | st->stx_size = wst.size; 229 | switch (wst.filetype) { 230 | case __WASI_FILETYPE_BLOCK_DEVICE: 231 | st->stx_mode |= S_IFBLK; 232 | break; 233 | case __WASI_FILETYPE_CHARACTER_DEVICE: 234 | st->stx_mode |= S_IFCHR; 235 | break; 236 | case __WASI_FILETYPE_DIRECTORY: 237 | st->stx_mode |= S_IFDIR; 238 | break; 239 | case __WASI_FILETYPE_REGULAR_FILE: 240 | st->stx_mode |= S_IFREG; 241 | break; 242 | case __WASI_FILETYPE_SOCKET_DGRAM: 243 | case __WASI_FILETYPE_SOCKET_STREAM: 244 | st->stx_mode |= S_IFSOCK; 245 | break; 246 | case __WASI_FILETYPE_SYMBOLIC_LINK: 247 | st->stx_mode |= S_IFLNK; 248 | break; 249 | } 250 | if(st->stx_mode == 0) 251 | { 252 | err = ENOENT; 253 | } 254 | return -err; 255 | } 256 | 257 | int WEAK __syscall_access(const char *pathname, int mode) 258 | { 259 | if (pathname[0] == '/') 260 | pathname = pathname + 1; 261 | // Check for target file existence. 262 | // TODO: when we support multiple preopened fds, also look at the permissions 263 | // on the parent fd 264 | __wasi_lookupflags_t lookup_flags = __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW; 265 | __wasi_filestat_t file; 266 | __wasi_errno_t err = __wasi_path_filestat_get(rootFd, lookup_flags, pathname, &file); 267 | return err? -err : 0; 268 | } 269 | 270 | int WEAK __syscall_link(const char *oldpath, const char *newpath) 271 | { 272 | assert(oldpath[0] == '/'); 273 | oldpath++; 274 | assert(newpath[0] == '/'); 275 | newpath++; 276 | __wasi_errno_t err = __wasi_path_symlink(oldpath, rootFd, newpath); 277 | return err? -err : 0; 278 | } 279 | 280 | int WEAK __syscall_rename(const char *oldpath, const char *newpath) 281 | { 282 | assert(oldpath[0] == '/'); 283 | oldpath++; 284 | assert(newpath[0] == '/'); 285 | newpath++; 286 | __wasi_errno_t err = __wasi_path_rename(rootFd, oldpath, rootFd, newpath); 287 | return err? -err : 0; 288 | } 289 | 290 | int WEAK __syscall_unlink(const char *pathname) 291 | { 292 | if(pathname[0] == '/') 293 | pathname++; 294 | __wasi_errno_t err = __wasi_path_unlink_file(rootFd, pathname); 295 | return err? -err : 0; 296 | } 297 | 298 | struct linux_dirent64 { 299 | ino64_t d_ino; /* 64-bit inode number */ 300 | off64_t d_off; /* 64-bit offset to next structure */ 301 | unsigned short d_reclen; /* Size of this dirent */ 302 | unsigned char d_type; /* File type */ 303 | char d_name[]; /* Filename (null-terminated) */ 304 | }; 305 | 306 | DirData* DirData::opened = nullptr; 307 | 308 | int WEAK __syscall_getdents64(int fd, void* dir, int count) 309 | { 310 | static_assert(sizeof(__wasi_dirent_t) >= sizeof(linux_dirent64), "wrong assumption"); 311 | size_t used = 0; 312 | __wasi_errno_t err = 0; 313 | __wasi_size_t size = 0; 314 | DirData* data = DirData::get(fd); 315 | if (dir == nullptr) 316 | return -ENOENT; 317 | __wasi_dircookie_t cookie = data->cookie; 318 | uint8_t* buf = static_cast(dir); 319 | err = __wasi_fd_readdir(fd, buf, count, cookie, &size); 320 | if (err) 321 | return -err; 322 | size_t reado = 0; 323 | size_t writeo = 0; 324 | __wasi_dirent_t tmp; 325 | // Make sure a whole entry can still fit 326 | while(reado + sizeof(__wasi_dirent_t) <= size) 327 | { 328 | memcpy(&tmp, buf+reado, sizeof(tmp)); 329 | // Make sure the whole name could fit in the buffer 330 | size_t nextReadOffset = reado + sizeof(__wasi_dirent_t) + tmp.d_namlen; 331 | if(nextReadOffset > size) 332 | break; 333 | linux_dirent64* tmpdst = reinterpret_cast(buf+writeo); 334 | tmpdst->d_ino = 0; 335 | tmpdst->d_off = tmp.d_next; 336 | data->cookie = tmp.d_next; 337 | tmpdst->d_reclen = sizeof(linux_dirent64)+tmp.d_namlen; 338 | tmpdst->d_type = tmp.d_type; 339 | memmove(&tmpdst->d_name, buf + reado + sizeof(__wasi_dirent_t), tmp.d_namlen); 340 | tmpdst->d_name[tmp.d_namlen] = '\0'; 341 | reado = nextReadOffset; 342 | writeo += sizeof(linux_dirent64) + tmp.d_namlen; 343 | } 344 | 345 | return err? -err : writeo; 346 | } 347 | 348 | int WEAK __syscall_mkdir(const char *pathname, int mode) 349 | { 350 | if(pathname[0] == '/') 351 | pathname++; 352 | __wasi_errno_t err = __wasi_path_create_directory(rootFd, pathname); 353 | return err? -err : 0; 354 | } 355 | 356 | size_t WEAK __syscall__llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, 357 | unsigned long long* result, unsigned int whence) 358 | { 359 | __wasi_filedelta_t offset = uint64_t(offset_high) << 32 | offset_low; 360 | __wasi_filesize_t ret; 361 | __wasi_errno_t err = __wasi_fd_seek(fd, offset, whence, &ret); 362 | *result = ret; 363 | return -err; 364 | } 365 | 366 | int WEAK __syscall_read(int fd, void* buf, int count) 367 | { 368 | __wasi_iovec_t ios { reinterpret_cast(buf), __wasi_size_t(count) }; 369 | __wasi_size_t ret; 370 | __wasi_errno_t err = __wasi_fd_read(fd, &ios, 1, &ret); 371 | return err? -err : ret; 372 | } 373 | long WEAK __syscall_readv(long fd, const iovec* ios, long len) 374 | { 375 | static_assert(sizeof(iovec) == sizeof(__wasi_iovec_t), "incompatible iovec size"); 376 | __wasi_size_t ret; 377 | __wasi_errno_t err = __wasi_fd_read(fd, reinterpret_cast(ios), len, &ret); 378 | return err? -err : ret; 379 | } 380 | 381 | // NOTE: numbers from 101 upwards are reserved for C++ constructors 382 | // 11 is chosen as a low number but after potentially more core initialization 383 | __attribute__((constructor(11))) 384 | void __preopen_root_fd() 385 | { 386 | __wasi_prestat_t prestat; 387 | __wasi_errno_t ret = __wasi_fd_prestat_get(3, &prestat); 388 | if (ret == __WASI_ERRNO_BADF) 389 | return; 390 | if (ret != __WASI_ERRNO_SUCCESS) 391 | __wasi_proc_exit(EX_OSERR); 392 | switch (prestat.tag) 393 | { 394 | case __WASI_PREOPENTYPE_DIR: 395 | { 396 | rootFd = 3; 397 | break; 398 | } 399 | default: 400 | { 401 | break; 402 | } 403 | } 404 | } 405 | 406 | static uint8_t argv_buf[32*1024]; 407 | static char* argv[1024]; 408 | void __syscall_main_args(int* argc_p, char*** argv_p) 409 | { 410 | __wasi_errno_t err; 411 | 412 | // Get the sizes of the arrays we'll have to create to copy in the args. 413 | size_t argv_buf_size; 414 | size_t argc; 415 | err = __wasi_args_sizes_get(&argc, &argv_buf_size); 416 | if (err != __WASI_ERRNO_SUCCESS) { 417 | __wasi_proc_exit(EX_OSERR); 418 | } 419 | 420 | // Add 1 for the NULL pointer to mark the end, and check for overflow. 421 | size_t num_ptrs = argc + 1; 422 | if (num_ptrs == 0) { 423 | __wasi_proc_exit(EX_SOFTWARE); 424 | } 425 | if (num_ptrs > 1024 || argv_buf_size > 32*1024) { 426 | __wasi_proc_exit(EX__MAX); 427 | } 428 | 429 | //// Allocate memory for storing the argument chars. 430 | //char *argv_buf = static_cast(malloc(argv_buf_size)); 431 | //if (argv_buf == NULL) { 432 | // __wasi_proc_exit(EX_SOFTWARE); 433 | //} 434 | 435 | //// Allocate memory for the array of pointers. This uses `calloc` both to 436 | //// handle overflow and to initialize the NULL pointer at the end. 437 | //char** argv = static_cast(calloc(num_ptrs, sizeof(char *))); 438 | //if (argv == NULL) { 439 | // free(argv_buf); 440 | // __wasi_proc_exit(EX_SOFTWARE); 441 | //} 442 | 443 | // Fill the argument chars, and the argv array with pointers into those chars. 444 | err = __wasi_args_get(reinterpret_cast(argv), reinterpret_cast(argv_buf)); 445 | if (err != __WASI_ERRNO_SUCCESS) { 446 | //free(argv_buf); 447 | //free(argv); 448 | __wasi_proc_exit(EX_OSERR); 449 | } 450 | *argc_p = argc; 451 | *argv_p = argv; 452 | } 453 | 454 | static uint8_t environ_buf[32*1024]; 455 | static char* cheerp_environ[32]; 456 | extern "C" char ** environ; 457 | void __syscall_main_environ() 458 | { 459 | __wasi_errno_t err; 460 | 461 | // Get the sizes of the arrays we'll have to create to copy in the args. 462 | size_t environ_buf_size; 463 | size_t environ_size; 464 | err = __wasi_environ_sizes_get(&environ_size, &environ_buf_size); 465 | if (err != __WASI_ERRNO_SUCCESS) { 466 | __wasi_proc_exit(EX_OSERR); 467 | } 468 | 469 | // Add 1 for the NULL pointer to mark the end, and check for overflow. 470 | size_t num_ptrs = environ_size + 1; 471 | if (num_ptrs == 0) { 472 | __wasi_proc_exit(EX_SOFTWARE); 473 | } 474 | if (num_ptrs > 32 || environ_buf_size > 32*1024) { 475 | __wasi_proc_exit(EX__MAX); 476 | } 477 | 478 | err = __wasi_environ_get(reinterpret_cast(cheerp_environ), reinterpret_cast(environ_buf)); 479 | if (err != __WASI_ERRNO_SUCCESS) { 480 | __wasi_proc_exit(EX_OSERR); 481 | } 482 | environ = cheerp_environ; 483 | } 484 | 485 | long WEAK __syscall_exit(int code) 486 | { 487 | __wasi_proc_exit(code); 488 | return 0; 489 | } 490 | 491 | void __cxa_throw_wasm_adapter(void *thrown_object, void *tinfo, void (*dest)(void *)) 492 | { 493 | const char msg[] = "Exception raised. Aborting...\n"; 494 | __syscall_write(1, (void*)(msg), sizeof(msg)); 495 | __wasi_proc_exit(EX_OSERR); 496 | } 497 | 498 | bool testUseAtomicWait() 499 | { 500 | return true; 501 | } 502 | 503 | } // extern "C" 504 | 505 | namespace std { 506 | 507 | __attribute__((noreturn)) 508 | __attribute__((nothrow)) 509 | void 510 | terminate() noexcept 511 | { 512 | const char msg[] = "std::terminate() called. Exiting...\n"; 513 | __syscall_write(1, (void*)(msg), sizeof(msg)); 514 | __wasi_proc_exit(EX_OSERR); 515 | } 516 | 517 | } 518 | 519 | namespace __sanitizer { 520 | using uptr = unsigned int; 521 | bool IsWasi() { return true; } 522 | // Stub out genericjs functions 523 | void InitEnv() {} 524 | uptr GetCallstack(uptr *dest, uptr dest_len, uptr skip) {} 525 | uptr GetUtf16FunctionNameAtPc(uptr pc, char16_t *dest, uptr len) {} 526 | } // namespace __sanitizer 527 | -------------------------------------------------------------------------------- /system/wasi_shim.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wasi_api.h" 3 | #include 4 | 5 | extern "C" { 6 | 7 | int32_t __imported_wasi_snapshot_preview1_args_get(int32_t arg0, int32_t arg1) __attribute__(( 8 | __import_module__("wasi_snapshot_preview1"), 9 | __import_name__("args_get") 10 | )); 11 | 12 | __attribute__((__weak__)) __wasi_errno_t __wasi_args_get( 13 | uint8_t * * argv, 14 | uint8_t * argv_buf 15 | ){ 16 | int32_t ret = __imported_wasi_snapshot_preview1_args_get((int32_t) argv, (int32_t) argv_buf); 17 | return (uint16_t) ret; 18 | } 19 | 20 | int32_t __imported_wasi_snapshot_preview1_args_sizes_get(int32_t arg0, int32_t arg1) __attribute__(( 21 | __import_module__("wasi_snapshot_preview1"), 22 | __import_name__("args_sizes_get") 23 | )); 24 | 25 | __attribute__((__weak__)) __wasi_errno_t __wasi_args_sizes_get( 26 | __wasi_size_t *retptr0, 27 | __wasi_size_t *retptr1 28 | ){ 29 | int32_t ret = __imported_wasi_snapshot_preview1_args_sizes_get((int32_t) retptr0, (int32_t) retptr1); 30 | return (uint16_t) ret; 31 | } 32 | 33 | int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t arg0, int32_t arg1) __attribute__(( 34 | __import_module__("wasi_snapshot_preview1"), 35 | __import_name__("environ_get") 36 | )); 37 | 38 | __attribute__((__weak__)) __wasi_errno_t __wasi_environ_get( 39 | uint8_t * * environ, 40 | uint8_t * environ_buf 41 | ){ 42 | int32_t ret = __imported_wasi_snapshot_preview1_environ_get((int32_t) environ, (int32_t) environ_buf); 43 | return (uint16_t) ret; 44 | } 45 | 46 | int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t arg0, int32_t arg1) __attribute__(( 47 | __import_module__("wasi_snapshot_preview1"), 48 | __import_name__("environ_sizes_get") 49 | )); 50 | 51 | __attribute__((__weak__)) __wasi_errno_t __wasi_environ_sizes_get( 52 | __wasi_size_t *retptr0, 53 | __wasi_size_t *retptr1 54 | ){ 55 | int32_t ret = __imported_wasi_snapshot_preview1_environ_sizes_get((int32_t) retptr0, (int32_t) retptr1); 56 | return (uint16_t) ret; 57 | } 58 | 59 | int32_t __imported_wasi_snapshot_preview1_clock_res_get(int32_t arg0, int32_t arg1) __attribute__(( 60 | __import_module__("wasi_snapshot_preview1"), 61 | __import_name__("clock_res_get") 62 | )); 63 | 64 | __wasi_errno_t __wasi_clock_res_get( 65 | __wasi_clockid_t id, 66 | __wasi_timestamp_t *retptr0 67 | ){ 68 | int32_t ret = __imported_wasi_snapshot_preview1_clock_res_get((int32_t) id, (int32_t) retptr0); 69 | return (uint16_t) ret; 70 | } 71 | 72 | int32_t __imported_wasi_snapshot_preview1_clock_time_get(int32_t arg0, int64_t arg1, int32_t arg2) __attribute__(( 73 | __import_module__("wasi_snapshot_preview1"), 74 | __import_name__("clock_time_get") 75 | )); 76 | 77 | __wasi_errno_t __wasi_clock_time_get( 78 | __wasi_clockid_t id, 79 | __wasi_timestamp_t precision, 80 | __wasi_timestamp_t *retptr0 81 | ){ 82 | int32_t ret = __imported_wasi_snapshot_preview1_clock_time_get((int32_t) id, (int64_t) precision, (int32_t) retptr0); 83 | return (uint16_t) ret; 84 | } 85 | 86 | int32_t __imported_wasi_snapshot_preview1_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) __attribute__(( 87 | __import_module__("wasi_snapshot_preview1"), 88 | __import_name__("fd_advise") 89 | )); 90 | 91 | __wasi_errno_t __wasi_fd_advise( 92 | __wasi_fd_t fd, 93 | __wasi_filesize_t offset, 94 | __wasi_filesize_t len, 95 | __wasi_advice_t advice 96 | ){ 97 | int32_t ret = __imported_wasi_snapshot_preview1_fd_advise((int32_t) fd, (int64_t) offset, (int64_t) len, (int32_t) advice); 98 | return (uint16_t) ret; 99 | } 100 | 101 | int32_t __imported_wasi_snapshot_preview1_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2) __attribute__(( 102 | __import_module__("wasi_snapshot_preview1"), 103 | __import_name__("fd_allocate") 104 | )); 105 | 106 | __wasi_errno_t __wasi_fd_allocate( 107 | __wasi_fd_t fd, 108 | __wasi_filesize_t offset, 109 | __wasi_filesize_t len 110 | ){ 111 | int32_t ret = __imported_wasi_snapshot_preview1_fd_allocate((int32_t) fd, (int64_t) offset, (int64_t) len); 112 | return (uint16_t) ret; 113 | } 114 | 115 | int32_t __imported_wasi_snapshot_preview1_fd_close(int32_t arg0) __attribute__(( 116 | __import_module__("wasi_snapshot_preview1"), 117 | __import_name__("fd_close") 118 | )); 119 | 120 | __wasi_errno_t __wasi_fd_close( 121 | __wasi_fd_t fd 122 | ){ 123 | int32_t ret = __imported_wasi_snapshot_preview1_fd_close((int32_t) fd); 124 | return (uint16_t) ret; 125 | } 126 | 127 | int32_t __imported_wasi_snapshot_preview1_fd_datasync(int32_t arg0) __attribute__(( 128 | __import_module__("wasi_snapshot_preview1"), 129 | __import_name__("fd_datasync") 130 | )); 131 | 132 | __wasi_errno_t __wasi_fd_datasync( 133 | __wasi_fd_t fd 134 | ){ 135 | int32_t ret = __imported_wasi_snapshot_preview1_fd_datasync((int32_t) fd); 136 | return (uint16_t) ret; 137 | } 138 | 139 | int32_t __imported_wasi_snapshot_preview1_fd_fdstat_get(int32_t arg0, int32_t arg1) __attribute__(( 140 | __import_module__("wasi_snapshot_preview1"), 141 | __import_name__("fd_fdstat_get") 142 | )); 143 | 144 | __wasi_errno_t __wasi_fd_fdstat_get( 145 | __wasi_fd_t fd, 146 | __wasi_fdstat_t *retptr0 147 | ){ 148 | int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_get((int32_t) fd, (int32_t) retptr0); 149 | return (uint16_t) ret; 150 | } 151 | 152 | int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_flags(int32_t arg0, int32_t arg1) __attribute__(( 153 | __import_module__("wasi_snapshot_preview1"), 154 | __import_name__("fd_fdstat_set_flags") 155 | )); 156 | 157 | __wasi_errno_t __wasi_fd_fdstat_set_flags( 158 | __wasi_fd_t fd, 159 | __wasi_fdflags_t flags 160 | ){ 161 | int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_set_flags((int32_t) fd, flags); 162 | return (uint16_t) ret; 163 | } 164 | 165 | int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2) __attribute__(( 166 | __import_module__("wasi_snapshot_preview1"), 167 | __import_name__("fd_fdstat_set_rights") 168 | )); 169 | 170 | __wasi_errno_t __wasi_fd_fdstat_set_rights( 171 | __wasi_fd_t fd, 172 | __wasi_rights_t fs_rights_base, 173 | __wasi_rights_t fs_rights_inheriting 174 | ){ 175 | int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_set_rights((int32_t) fd, fs_rights_base, fs_rights_inheriting); 176 | return (uint16_t) ret; 177 | } 178 | 179 | int32_t __imported_wasi_snapshot_preview1_fd_filestat_get(int32_t arg0, int32_t arg1) __attribute__(( 180 | __import_module__("wasi_snapshot_preview1"), 181 | __import_name__("fd_filestat_get") 182 | )); 183 | 184 | __wasi_errno_t __wasi_fd_filestat_get( 185 | __wasi_fd_t fd, 186 | __wasi_filestat_t *retptr0 187 | ){ 188 | int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_get((int32_t) fd, (int32_t) retptr0); 189 | return (uint16_t) ret; 190 | } 191 | 192 | int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_size(int32_t arg0, int64_t arg1) __attribute__(( 193 | __import_module__("wasi_snapshot_preview1"), 194 | __import_name__("fd_filestat_set_size") 195 | )); 196 | 197 | __wasi_errno_t __wasi_fd_filestat_set_size( 198 | __wasi_fd_t fd, 199 | __wasi_filesize_t size 200 | ){ 201 | int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_set_size((int32_t) fd, (int64_t) size); 202 | return (uint16_t) ret; 203 | } 204 | 205 | int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) __attribute__(( 206 | __import_module__("wasi_snapshot_preview1"), 207 | __import_name__("fd_filestat_set_times") 208 | )); 209 | 210 | __wasi_errno_t __wasi_fd_filestat_set_times( 211 | __wasi_fd_t fd, 212 | __wasi_timestamp_t atim, 213 | __wasi_timestamp_t mtim, 214 | __wasi_fstflags_t fst_flags 215 | ){ 216 | int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_set_times((int32_t) fd, (int64_t) atim, (int64_t) mtim, fst_flags); 217 | return (uint16_t) ret; 218 | } 219 | 220 | int32_t __imported_wasi_snapshot_preview1_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__(( 221 | __import_module__("wasi_snapshot_preview1"), 222 | __import_name__("fd_pread") 223 | )); 224 | 225 | __wasi_errno_t __wasi_fd_pread( 226 | __wasi_fd_t fd, 227 | const __wasi_iovec_t *iovs, 228 | size_t iovs_len, 229 | __wasi_filesize_t offset, 230 | __wasi_size_t *retptr0 231 | ){ 232 | int32_t ret = __imported_wasi_snapshot_preview1_fd_pread((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int64_t) offset, (int32_t) retptr0); 233 | return (uint16_t) ret; 234 | } 235 | 236 | int32_t __imported_wasi_snapshot_preview1_fd_prestat_get(int32_t arg0, int32_t arg1) __attribute__(( 237 | __import_module__("wasi_snapshot_preview1"), 238 | __import_name__("fd_prestat_get") 239 | )); 240 | 241 | __wasi_errno_t __wasi_fd_prestat_get( 242 | __wasi_fd_t fd, 243 | __wasi_prestat_t *retptr0 244 | ){ 245 | int32_t ret = __imported_wasi_snapshot_preview1_fd_prestat_get((int32_t) fd, (int32_t) retptr0); 246 | return (uint16_t) ret; 247 | } 248 | 249 | int32_t __imported_wasi_snapshot_preview1_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__(( 250 | __import_module__("wasi_snapshot_preview1"), 251 | __import_name__("fd_prestat_dir_name") 252 | )); 253 | 254 | __wasi_errno_t __wasi_fd_prestat_dir_name( 255 | __wasi_fd_t fd, 256 | uint8_t * path, 257 | __wasi_size_t path_len 258 | ){ 259 | int32_t ret = __imported_wasi_snapshot_preview1_fd_prestat_dir_name((int32_t) fd, (int32_t) path, (int32_t) path_len); 260 | return (uint16_t) ret; 261 | } 262 | 263 | int32_t __imported_wasi_snapshot_preview1_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__(( 264 | __import_module__("wasi_snapshot_preview1"), 265 | __import_name__("fd_pwrite") 266 | )); 267 | 268 | __wasi_errno_t __wasi_fd_pwrite( 269 | __wasi_fd_t fd, 270 | const __wasi_ciovec_t *iovs, 271 | size_t iovs_len, 272 | __wasi_filesize_t offset, 273 | __wasi_size_t *retptr0 274 | ){ 275 | int32_t ret = __imported_wasi_snapshot_preview1_fd_pwrite((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int64_t) offset, (int32_t) retptr0); 276 | return (uint16_t) ret; 277 | } 278 | 279 | int32_t __imported_wasi_snapshot_preview1_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__(( 280 | __import_module__("wasi_snapshot_preview1"), 281 | __import_name__("fd_read") 282 | )); 283 | 284 | __wasi_errno_t __wasi_fd_read( 285 | __wasi_fd_t fd, 286 | const __wasi_iovec_t *iovs, 287 | size_t iovs_len, 288 | __wasi_size_t *retptr0 289 | ){ 290 | int32_t ret = __imported_wasi_snapshot_preview1_fd_read((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int32_t) retptr0); 291 | return (uint16_t) ret; 292 | } 293 | 294 | int32_t __imported_wasi_snapshot_preview1_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__(( 295 | __import_module__("wasi_snapshot_preview1"), 296 | __import_name__("fd_readdir") 297 | )); 298 | 299 | __wasi_errno_t __wasi_fd_readdir( 300 | __wasi_fd_t fd, 301 | uint8_t * buf, 302 | __wasi_size_t buf_len, 303 | __wasi_dircookie_t cookie, 304 | __wasi_size_t *retptr0 305 | ){ 306 | int32_t ret = __imported_wasi_snapshot_preview1_fd_readdir((int32_t) fd, (int32_t) buf, (int32_t) buf_len, (int64_t) cookie, (int32_t) retptr0); 307 | return (uint16_t) ret; 308 | } 309 | 310 | int32_t __imported_wasi_snapshot_preview1_fd_renumber(int32_t arg0, int32_t arg1) __attribute__(( 311 | __import_module__("wasi_snapshot_preview1"), 312 | __import_name__("fd_renumber") 313 | )); 314 | 315 | __wasi_errno_t __wasi_fd_renumber( 316 | __wasi_fd_t fd, 317 | __wasi_fd_t to 318 | ){ 319 | int32_t ret = __imported_wasi_snapshot_preview1_fd_renumber((int32_t) fd, (int32_t) to); 320 | return (uint16_t) ret; 321 | } 322 | 323 | int32_t __imported_wasi_snapshot_preview1_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3) __attribute__(( 324 | __import_module__("wasi_snapshot_preview1"), 325 | __import_name__("fd_seek") 326 | )); 327 | 328 | __wasi_errno_t __wasi_fd_seek( 329 | __wasi_fd_t fd, 330 | __wasi_filedelta_t offset, 331 | __wasi_whence_t whence, 332 | __wasi_filesize_t *retptr0 333 | ){ 334 | int32_t ret = __imported_wasi_snapshot_preview1_fd_seek((int32_t) fd, offset, (int32_t) whence, (int32_t) retptr0); 335 | return (uint16_t) ret; 336 | } 337 | 338 | int32_t __imported_wasi_snapshot_preview1_fd_sync(int32_t arg0) __attribute__(( 339 | __import_module__("wasi_snapshot_preview1"), 340 | __import_name__("fd_sync") 341 | )); 342 | 343 | __wasi_errno_t __wasi_fd_sync( 344 | __wasi_fd_t fd 345 | ){ 346 | int32_t ret = __imported_wasi_snapshot_preview1_fd_sync((int32_t) fd); 347 | return (uint16_t) ret; 348 | } 349 | 350 | int32_t __imported_wasi_snapshot_preview1_fd_tell(int32_t arg0, int32_t arg1) __attribute__(( 351 | __import_module__("wasi_snapshot_preview1"), 352 | __import_name__("fd_tell") 353 | )); 354 | 355 | __wasi_errno_t __wasi_fd_tell( 356 | __wasi_fd_t fd, 357 | __wasi_filesize_t *retptr0 358 | ){ 359 | int32_t ret = __imported_wasi_snapshot_preview1_fd_tell((int32_t) fd, (int32_t) retptr0); 360 | return (uint16_t) ret; 361 | } 362 | 363 | int32_t __imported_wasi_snapshot_preview1_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__(( 364 | __import_module__("wasi_snapshot_preview1"), 365 | __import_name__("fd_write") 366 | )); 367 | 368 | __wasi_errno_t __wasi_fd_write( 369 | __wasi_fd_t fd, 370 | const __wasi_ciovec_t *iovs, 371 | size_t iovs_len, 372 | __wasi_size_t *retptr0 373 | ){ 374 | int32_t ret = __imported_wasi_snapshot_preview1_fd_write((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int32_t) retptr0); 375 | return (uint16_t) ret; 376 | } 377 | 378 | int32_t __imported_wasi_snapshot_preview1_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__(( 379 | __import_module__("wasi_snapshot_preview1"), 380 | __import_name__("path_create_directory") 381 | )); 382 | 383 | __wasi_errno_t __wasi_path_create_directory( 384 | __wasi_fd_t fd, 385 | const char *path 386 | ){ 387 | size_t path_len = strlen(path); 388 | int32_t ret = __imported_wasi_snapshot_preview1_path_create_directory((int32_t) fd, (int32_t) path, (int32_t) path_len); 389 | return (uint16_t) ret; 390 | } 391 | 392 | int32_t __imported_wasi_snapshot_preview1_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__(( 393 | __import_module__("wasi_snapshot_preview1"), 394 | __import_name__("path_filestat_get") 395 | )); 396 | 397 | __wasi_errno_t __wasi_path_filestat_get( 398 | __wasi_fd_t fd, 399 | __wasi_lookupflags_t flags, 400 | const char *path, 401 | __wasi_filestat_t *retptr0 402 | ){ 403 | size_t path_len = strlen(path); 404 | int32_t ret = __imported_wasi_snapshot_preview1_path_filestat_get((int32_t) fd, flags, (int32_t) path, (int32_t) path_len, (int32_t) retptr0); 405 | return (uint16_t) ret; 406 | } 407 | 408 | int32_t __imported_wasi_snapshot_preview1_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6) __attribute__(( 409 | __import_module__("wasi_snapshot_preview1"), 410 | __import_name__("path_filestat_set_times") 411 | )); 412 | 413 | __wasi_errno_t __wasi_path_filestat_set_times( 414 | __wasi_fd_t fd, 415 | __wasi_lookupflags_t flags, 416 | const char *path, 417 | __wasi_timestamp_t atim, 418 | __wasi_timestamp_t mtim, 419 | __wasi_fstflags_t fst_flags 420 | ){ 421 | size_t path_len = strlen(path); 422 | int32_t ret = __imported_wasi_snapshot_preview1_path_filestat_set_times((int32_t) fd, flags, (int32_t) path, (int32_t) path_len, (int64_t) atim, (int64_t) mtim, fst_flags); 423 | return (uint16_t) ret; 424 | } 425 | 426 | int32_t __imported_wasi_snapshot_preview1_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6) __attribute__(( 427 | __import_module__("wasi_snapshot_preview1"), 428 | __import_name__("path_link") 429 | )); 430 | 431 | __wasi_errno_t __wasi_path_link( 432 | __wasi_fd_t old_fd, 433 | __wasi_lookupflags_t old_flags, 434 | const char *old_path, 435 | __wasi_fd_t new_fd, 436 | const char *new_path 437 | ){ 438 | size_t old_path_len = strlen(old_path); 439 | size_t new_path_len = strlen(new_path); 440 | int32_t ret = __imported_wasi_snapshot_preview1_path_link((int32_t) old_fd, old_flags, (int32_t) old_path, (int32_t) old_path_len, (int32_t) new_fd, (int32_t) new_path, (int32_t) new_path_len); 441 | return (uint16_t) ret; 442 | } 443 | 444 | int32_t __imported_wasi_snapshot_preview1_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8) __attribute__(( 445 | __import_module__("wasi_snapshot_preview1"), 446 | __import_name__("path_open") 447 | )); 448 | 449 | __wasi_errno_t __wasi_path_open( 450 | __wasi_fd_t fd, 451 | __wasi_lookupflags_t dirflags, 452 | const char *path, 453 | __wasi_oflags_t oflags, 454 | __wasi_rights_t fs_rights_base, 455 | __wasi_rights_t fs_rights_inheriting, 456 | __wasi_fdflags_t fdflags, 457 | __wasi_fd_t *retptr0 458 | ){ 459 | size_t path_len = strlen(path); 460 | int32_t ret = __imported_wasi_snapshot_preview1_path_open((int32_t) fd, dirflags, (int32_t) path, (int32_t) path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, (int32_t) retptr0); 461 | return (uint16_t) ret; 462 | } 463 | 464 | int32_t __imported_wasi_snapshot_preview1_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__(( 465 | __import_module__("wasi_snapshot_preview1"), 466 | __import_name__("path_readlink") 467 | )); 468 | 469 | __wasi_errno_t __wasi_path_readlink( 470 | __wasi_fd_t fd, 471 | const char *path, 472 | uint8_t * buf, 473 | __wasi_size_t buf_len, 474 | __wasi_size_t *retptr0 475 | ){ 476 | size_t path_len = strlen(path); 477 | int32_t ret = __imported_wasi_snapshot_preview1_path_readlink((int32_t) fd, (int32_t) path, (int32_t) path_len, (int32_t) buf, (int32_t) buf_len, (int32_t) retptr0); 478 | return (uint16_t) ret; 479 | } 480 | 481 | int32_t __imported_wasi_snapshot_preview1_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__(( 482 | __import_module__("wasi_snapshot_preview1"), 483 | __import_name__("path_remove_directory") 484 | )); 485 | 486 | __wasi_errno_t __wasi_path_remove_directory( 487 | __wasi_fd_t fd, 488 | const char *path 489 | ){ 490 | size_t path_len = strlen(path); 491 | int32_t ret = __imported_wasi_snapshot_preview1_path_remove_directory((int32_t) fd, (int32_t) path, (int32_t) path_len); 492 | return (uint16_t) ret; 493 | } 494 | 495 | int32_t __imported_wasi_snapshot_preview1_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__(( 496 | __import_module__("wasi_snapshot_preview1"), 497 | __import_name__("path_rename") 498 | )); 499 | 500 | __wasi_errno_t __wasi_path_rename( 501 | __wasi_fd_t fd, 502 | const char *old_path, 503 | __wasi_fd_t new_fd, 504 | const char *new_path 505 | ){ 506 | size_t old_path_len = strlen(old_path); 507 | size_t new_path_len = strlen(new_path); 508 | int32_t ret = __imported_wasi_snapshot_preview1_path_rename((int32_t) fd, (int32_t) old_path, (int32_t) old_path_len, (int32_t) new_fd, (int32_t) new_path, (int32_t) new_path_len); 509 | return (uint16_t) ret; 510 | } 511 | 512 | int32_t __imported_wasi_snapshot_preview1_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__(( 513 | __import_module__("wasi_snapshot_preview1"), 514 | __import_name__("path_symlink") 515 | )); 516 | 517 | __wasi_errno_t __wasi_path_symlink( 518 | const char *old_path, 519 | __wasi_fd_t fd, 520 | const char *new_path 521 | ){ 522 | size_t old_path_len = strlen(old_path); 523 | size_t new_path_len = strlen(new_path); 524 | int32_t ret = __imported_wasi_snapshot_preview1_path_symlink((int32_t) old_path, (int32_t) old_path_len, (int32_t) fd, (int32_t) new_path, (int32_t) new_path_len); 525 | return (uint16_t) ret; 526 | } 527 | 528 | int32_t __imported_wasi_snapshot_preview1_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__(( 529 | __import_module__("wasi_snapshot_preview1"), 530 | __import_name__("path_unlink_file") 531 | )); 532 | 533 | __wasi_errno_t __wasi_path_unlink_file( 534 | __wasi_fd_t fd, 535 | const char *path 536 | ){ 537 | size_t path_len = strlen(path); 538 | int32_t ret = __imported_wasi_snapshot_preview1_path_unlink_file((int32_t) fd, (int32_t) path, (int32_t) path_len); 539 | return (uint16_t) ret; 540 | } 541 | 542 | int32_t __imported_wasi_snapshot_preview1_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__(( 543 | __import_module__("wasi_snapshot_preview1"), 544 | __import_name__("poll_oneoff") 545 | )); 546 | 547 | __wasi_errno_t __wasi_poll_oneoff( 548 | const __wasi_subscription_t * in, 549 | __wasi_event_t * out, 550 | __wasi_size_t nsubscriptions, 551 | __wasi_size_t *retptr0 552 | ){ 553 | int32_t ret = __imported_wasi_snapshot_preview1_poll_oneoff((int32_t) in, (int32_t) out, (int32_t) nsubscriptions, (int32_t) retptr0); 554 | return (uint16_t) ret; 555 | } 556 | 557 | _Noreturn void __imported_wasi_snapshot_preview1_proc_exit(int32_t arg0) __attribute__(( 558 | __import_module__("wasi_snapshot_preview1"), 559 | __import_name__("proc_exit") 560 | )); 561 | 562 | __attribute__((__weak__)) _Noreturn void __wasi_proc_exit( 563 | __wasi_exitcode_t rval 564 | ){ 565 | __imported_wasi_snapshot_preview1_proc_exit((int32_t) rval); 566 | } 567 | 568 | int32_t __imported_wasi_snapshot_preview1_sched_yield() __attribute__(( 569 | __import_module__("wasi_snapshot_preview1"), 570 | __import_name__("sched_yield") 571 | )); 572 | 573 | __wasi_errno_t __wasi_sched_yield( 574 | void 575 | ){ 576 | int32_t ret = __imported_wasi_snapshot_preview1_sched_yield(); 577 | return (uint16_t) ret; 578 | } 579 | 580 | int32_t __imported_wasi_snapshot_preview1_random_get(int32_t arg0, int32_t arg1) __attribute__(( 581 | __import_module__("wasi_snapshot_preview1"), 582 | __import_name__("random_get") 583 | )); 584 | 585 | __wasi_errno_t __wasi_random_get( 586 | uint8_t * buf, 587 | __wasi_size_t buf_len 588 | ){ 589 | int32_t ret = __imported_wasi_snapshot_preview1_random_get((int32_t) buf, (int32_t) buf_len); 590 | return (uint16_t) ret; 591 | } 592 | 593 | int32_t __imported_wasi_snapshot_preview1_sock_accept(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__(( 594 | __import_module__("wasi_snapshot_preview1"), 595 | __import_name__("sock_accept") 596 | )); 597 | 598 | __wasi_errno_t __wasi_sock_accept( 599 | __wasi_fd_t fd, 600 | __wasi_fdflags_t flags, 601 | __wasi_fd_t *retptr0 602 | ){ 603 | int32_t ret = __imported_wasi_snapshot_preview1_sock_accept((int32_t) fd, flags, (int32_t) retptr0); 604 | return (uint16_t) ret; 605 | } 606 | 607 | int32_t __imported_wasi_snapshot_preview1_sock_recv(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__(( 608 | __import_module__("wasi_snapshot_preview1"), 609 | __import_name__("sock_recv") 610 | )); 611 | 612 | __wasi_errno_t __wasi_sock_recv( 613 | __wasi_fd_t fd, 614 | const __wasi_iovec_t *ri_data, 615 | size_t ri_data_len, 616 | __wasi_riflags_t ri_flags, 617 | __wasi_size_t *retptr0, 618 | __wasi_roflags_t *retptr1 619 | ){ 620 | int32_t ret = __imported_wasi_snapshot_preview1_sock_recv((int32_t) fd, (int32_t) ri_data, (int32_t) ri_data_len, ri_flags, (int32_t) retptr0, (int32_t) retptr1); 621 | return (uint16_t) ret; 622 | } 623 | 624 | int32_t __imported_wasi_snapshot_preview1_sock_send(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__(( 625 | __import_module__("wasi_snapshot_preview1"), 626 | __import_name__("sock_send") 627 | )); 628 | 629 | __wasi_errno_t __wasi_sock_send( 630 | __wasi_fd_t fd, 631 | const __wasi_ciovec_t *si_data, 632 | size_t si_data_len, 633 | __wasi_siflags_t si_flags, 634 | __wasi_size_t *retptr0 635 | ){ 636 | int32_t ret = __imported_wasi_snapshot_preview1_sock_send((int32_t) fd, (int32_t) si_data, (int32_t) si_data_len, (int32_t) si_flags, (int32_t) retptr0); 637 | return (uint16_t) ret; 638 | } 639 | 640 | int32_t __imported_wasi_snapshot_preview1_sock_shutdown(int32_t arg0, int32_t arg1) __attribute__(( 641 | __import_module__("wasi_snapshot_preview1"), 642 | __import_name__("sock_shutdown") 643 | )); 644 | 645 | __wasi_errno_t __wasi_sock_shutdown( 646 | __wasi_fd_t fd, 647 | __wasi_sdflags_t how 648 | ){ 649 | int32_t ret = __imported_wasi_snapshot_preview1_sock_shutdown((int32_t) fd, how); 650 | return (uint16_t) ret; 651 | } 652 | 653 | #ifdef _REENTRANT 654 | int32_t __imported_wasi_thread_spawn(int32_t arg0) __attribute__(( 655 | __import_module__("wasi"), 656 | __import_name__("thread-spawn") 657 | )); 658 | 659 | int32_t __wasi_thread_spawn(void* start_arg) { 660 | return __imported_wasi_thread_spawn((int32_t) start_arg); 661 | } 662 | 663 | #endif 664 | 665 | } 666 | -------------------------------------------------------------------------------- /wasm/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | 3 | INSTALL_PREFIX ?= /opt/cheerp 4 | CHEERP_PREFIX ?= /opt/cheerp 5 | 6 | all: libwasm.bc 7 | 8 | libwasm.bc: memory.bc 9 | ${CHEERP_PREFIX}/bin/llvm-link -o $@ $^ 10 | 11 | %.bc: %.cpp 12 | ${CHEERP_PREFIX}/bin/clang++ -c $^ -o $@ -target cheerp-wasm -O3 13 | 14 | install_lib: libwasm.bc 15 | mkdir -p ${INSTALL_PREFIX}/lib/ 16 | cp -v $^ ${INSTALL_PREFIX}/lib/ 17 | 18 | install: install_lib 19 | 20 | clean: 21 | rm -fv libwasm.bc memory.bc 22 | -------------------------------------------------------------------------------- /wasm/memory.cpp: -------------------------------------------------------------------------------- 1 | //===-- memory.cpp - Wasm-optimized memory functions --------------===// 2 | // 3 | // Cheerp: The C++ compiler for the Web 4 | // 5 | // This file is distributed under the Apache License v2.0 with LLVM Exceptions. 6 | // See LICENSE.TXT for details. 7 | // 8 | // Copyright 2018-2023 Leaning Technologies 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #include 13 | #include 14 | 15 | extern "C" 16 | { 17 | /** Implement a faster variant of memcpy, specialized for wasm 18 | * At link time this will replace the 'weak' symbol for memcpy 19 | * provided in the lib C 20 | */ 21 | void* memcpy(void* const dst, void* const src, size_t len) 22 | { 23 | // Wasm supports unaligned load/stores, take advantage of this for a faster memcpy 24 | unsigned char* src8 = (unsigned char*)src; 25 | unsigned char* dst8 = (unsigned char*)dst; 26 | 27 | unsigned char* srcEnd = src8+len; 28 | 29 | while(((unsigned long)dst8)&3) 30 | { 31 | if(src8 == srcEnd) 32 | return dst; 33 | *dst8 = *src8; 34 | dst8++; 35 | src8++; 36 | } 37 | // Loop conditions below will underflow for too small 38 | // lengths and NULL pointers 39 | if(len >= 64) 40 | { 41 | // Now the dest pointer is aligned 42 | // Unroll for 64 bytes at a time 43 | while(uintptr_t(src8) <= uintptr_t(srcEnd - 64)) 44 | { 45 | *((unsigned long long*)dst8) = *((unsigned long long*)src8); 46 | *((unsigned long long*)(dst8+8)) = *((unsigned long long*)(src8+8)); 47 | *((unsigned long long*)(dst8+16)) = *((unsigned long long*)(src8+16)); 48 | *((unsigned long long*)(dst8+24)) = *((unsigned long long*)(src8+24)); 49 | *((unsigned long long*)(dst8+32)) = *((unsigned long long*)(src8+32)); 50 | *((unsigned long long*)(dst8+40)) = *((unsigned long long*)(src8+40)); 51 | *((unsigned long long*)(dst8+48)) = *((unsigned long long*)(src8+48)); 52 | *((unsigned long long*)(dst8+56)) = *((unsigned long long*)(src8+56)); 53 | dst8+=64; 54 | src8+=64; 55 | } 56 | // Loop 8 bytes at a time 57 | while(uintptr_t(src8) <= uintptr_t(srcEnd - 8)) 58 | { 59 | *((unsigned long long*)dst8) = *((unsigned long long*)src8); 60 | dst8+=8; 61 | src8+=8; 62 | } 63 | } 64 | // Byte loop to finish the copy 65 | while(src8 != srcEnd) 66 | { 67 | *dst8 = *src8; 68 | dst8++; 69 | src8++; 70 | } 71 | return dst; 72 | } 73 | 74 | void *memset(void *dst, int c, size_t len) 75 | { 76 | unsigned char cu = c; 77 | unsigned int c32 = (cu<<0) | (cu<<8) | (cu<<16) | (cu<<24); 78 | unsigned long long c64 = ((unsigned long long)(c32)<<0) | ((unsigned long long)(c32)<<32); 79 | unsigned char* dst8 = (unsigned char*)dst; 80 | unsigned char* dstEnd = dst8 + len; 81 | if(len >= 64) 82 | { 83 | while(uintptr_t(dst8) < uintptr_t(dstEnd - 64)) 84 | { 85 | *((unsigned long long*)dst8) = c64; 86 | *((unsigned long long*)(dst8+8)) = c64; 87 | *((unsigned long long*)(dst8+16)) = c64; 88 | *((unsigned long long*)(dst8+24)) = c64; 89 | *((unsigned long long*)(dst8+32)) = c64; 90 | *((unsigned long long*)(dst8+40)) = c64; 91 | *((unsigned long long*)(dst8+48)) = c64; 92 | *((unsigned long long*)(dst8+56)) = c64; 93 | dst8+=64; 94 | } 95 | // Loop 8 bytes at a time 96 | while(uintptr_t(dst8) <= uintptr_t(dstEnd - 8)) 97 | { 98 | *((unsigned long long*)dst8) = c64; 99 | dst8+=8; 100 | } 101 | } 102 | // Byte loop to finish the copy 103 | while(dst8 != dstEnd) 104 | { 105 | *dst8 = cu; 106 | dst8++; 107 | } 108 | return dst; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /webgles/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | 3 | INSTALL_PREFIX ?= /opt/cheerp 4 | CHEERP_PREFIX ?= /opt/cheerp 5 | 6 | all: libGLESv2.bc 7 | 8 | libGLESv2.bc: webgles.bc webglesutils.bc 9 | ${CHEERP_PREFIX}/bin/llvm-link -o $@ $^ 10 | 11 | %.bc: %.cpp 12 | ${CHEERP_PREFIX}/bin/clang++ ${CXXFLAGS} -c $^ -o $@ -target cheerp -O3 13 | 14 | install_headers: webgles.h gl2.h 15 | mkdir -p ${INSTALL_PREFIX}/include/GLES2/ 16 | cp -v $^ ${INSTALL_PREFIX}/include/GLES2/ 17 | 18 | install_lib: libGLESv2.bc 19 | mkdir -p ${INSTALL_PREFIX}/lib/ 20 | cp -v $^ ${INSTALL_PREFIX}/lib/ 21 | 22 | install: install_headers install_lib 23 | 24 | clean: 25 | rm -fv libGLESv2.bc webgles.bc webglesutils.bc 26 | -------------------------------------------------------------------------------- /webgles/gl2.h: -------------------------------------------------------------------------------- 1 | //===-- gl2.h - OpenGL ES API declarations --------------===// 2 | // 3 | // Cheerp: The C++ compiler for the Web 4 | // 5 | // This file is distributed under the Apache License v2.0 with LLVM Exceptions. 6 | // See LICENSE.TXT for details. 7 | // 8 | // Copyright 2018-2023 Leaning Technologies 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #ifndef _GL2_H_ 13 | #define _GL2_H_ 14 | 15 | #define GL_ES_VERSION_2_0 1 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | typedef unsigned int GLenum; 22 | typedef bool GLboolean; 23 | typedef unsigned int GLbitfield; 24 | typedef char GLbyte; 25 | typedef short GLshort; 26 | typedef int GLint; 27 | typedef int GLsizei; 28 | typedef int GLintptr; 29 | typedef int GLsizeiptr; 30 | typedef unsigned char GLubyte; 31 | typedef unsigned short GLushort; 32 | typedef unsigned int GLuint; 33 | typedef float GLfloat; 34 | typedef double GLdouble; 35 | typedef float GLclampf; 36 | typedef char GLchar; 37 | typedef void GLvoid; 38 | #define GL_DEPTH_BUFFER_BIT 0x0100 39 | #define GL_STENCIL_BUFFER_BIT 0x0400 40 | #define GL_COLOR_BUFFER_BIT 0x4000 41 | #define GL_POINTS 0x0000 42 | #define GL_LINES 0x0001 43 | #define GL_LINE_LOOP 0x0002 44 | #define GL_LINE_STRIP 0x0003 45 | #define GL_TRIANGLES 0x0004 46 | #define GL_TRIANGLE_STRIP 0x0005 47 | #define GL_TRIANGLE_FAN 0x0006 48 | #define GL_FALSE 0x0000 49 | #define GL_TRUE 0x0001 50 | #define GL_ZERO 0x0000 51 | #define GL_ONE 0x0001 52 | #define GL_SRC_COLOR 0x0300 53 | #define GL_ONE_MINUS_SRC_COLOR 0x0301 54 | #define GL_SRC_ALPHA 0x0302 55 | #define GL_ONE_MINUS_SRC_ALPHA 0x0303 56 | #define GL_DST_ALPHA 0x0304 57 | #define GL_ONE_MINUS_DST_ALPHA 0x0305 58 | #define GL_DST_COLOR 0x0306 59 | #define GL_ONE_MINUS_DST_COLOR 0x0307 60 | #define GL_SRC_ALPHA_SATURATE 0x0308 61 | #define GL_FUNC_ADD 0x8006 62 | #define GL_BLEND_EQUATION 0x8009 63 | #define GL_BLEND_EQUATION_RGB 0x8009 64 | #define GL_BLEND_EQUATION_ALPHA 0x883d 65 | #define GL_FUNC_SUBTRACT 0x800a 66 | #define GL_FUNC_REVERSE_SUBTRACT 0x800b 67 | #define GL_BLEND_DST_RGB 0x80c8 68 | #define GL_BLEND_SRC_RGB 0x80c9 69 | #define GL_BLEND_DST_ALPHA 0x80ca 70 | #define GL_BLEND_SRC_ALPHA 0x80cb 71 | #define GL_CONSTANT_COLOR 0x8001 72 | #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 73 | #define GL_CONSTANT_ALPHA 0x8003 74 | #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 75 | #define GL_BLEND_COLOR 0x8005 76 | #define GL_ARRAY_BUFFER 0x8892 77 | #define GL_ELEMENT_ARRAY_BUFFER 0x8893 78 | #define GL_ARRAY_BUFFER_BINDING 0x8894 79 | #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 80 | #define GL_STREAM_DRAW 0x88e0 81 | #define GL_STATIC_DRAW 0x88e4 82 | #define GL_DYNAMIC_DRAW 0x88e8 83 | #define GL_BUFFER_SIZE 0x8764 84 | #define GL_BUFFER_USAGE 0x8765 85 | #define GL_CURRENT_VERTEX_ATTRIB 0x8626 86 | #define GL_FRONT 0x0404 87 | #define GL_BACK 0x0405 88 | #define GL_FRONT_AND_BACK 0x0408 89 | #define GL_TEXTURE_2D 0x0de1 90 | #define GL_CULL_FACE 0x0b44 91 | #define GL_BLEND 0x0be2 92 | #define GL_DITHER 0x0bd0 93 | #define GL_STENCIL_TEST 0x0b90 94 | #define GL_DEPTH_TEST 0x0b71 95 | #define GL_SCISSOR_TEST 0x0c11 96 | #define GL_POLYGON_OFFSET_FILL 0x8037 97 | #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809e 98 | #define GL_SAMPLE_COVERAGE 0x80a0 99 | #define GL_NO_ERROR 0x0000 100 | #define GL_INVALID_ENUM 0x0500 101 | #define GL_INVALID_VALUE 0x0501 102 | #define GL_INVALID_OPERATION 0x0502 103 | #define GL_OUT_OF_MEMORY 0x0505 104 | #define GL_CW 0x0900 105 | #define GL_CCW 0x0901 106 | #define GL_LINE_WIDTH 0x0b21 107 | #define GL_ALIASED_POINT_SIZE_RANGE 0x846d 108 | #define GL_ALIASED_LINE_WIDTH_RANGE 0x846e 109 | #define GL_CULL_FACE_MODE 0x0b45 110 | #define GL_FRONT_FACE 0x0b46 111 | #define GL_DEPTH_RANGE 0x0b70 112 | #define GL_DEPTH_WRITEMASK 0x0b72 113 | #define GL_DEPTH_CLEAR_VALUE 0x0b73 114 | #define GL_DEPTH_FUNC 0x0b74 115 | #define GL_STENCIL_CLEAR_VALUE 0x0b91 116 | #define GL_STENCIL_FUNC 0x0b92 117 | #define GL_STENCIL_FAIL 0x0b94 118 | #define GL_STENCIL_PASS_DEPTH_FAIL 0x0b95 119 | #define GL_STENCIL_PASS_DEPTH_PASS 0x0b96 120 | #define GL_STENCIL_REF 0x0b97 121 | #define GL_STENCIL_VALUE_MASK 0x0b93 122 | #define GL_STENCIL_WRITEMASK 0x0b98 123 | #define GL_STENCIL_BACK_FUNC 0x8800 124 | #define GL_STENCIL_BACK_FAIL 0x8801 125 | #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 126 | #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 127 | #define GL_STENCIL_BACK_REF 0x8ca3 128 | #define GL_STENCIL_BACK_VALUE_MASK 0x8ca4 129 | #define GL_STENCIL_BACK_WRITEMASK 0x8ca5 130 | #define GL_VIEWPORT 0x0ba2 131 | #define GL_SCISSOR_BOX 0x0c10 132 | #define GL_COLOR_CLEAR_VALUE 0x0c22 133 | #define GL_COLOR_WRITEMASK 0x0c23 134 | #define GL_UNPACK_ALIGNMENT 0x0cf5 135 | #define GL_PACK_ALIGNMENT 0x0d05 136 | #define GL_MAX_TEXTURE_SIZE 0x0d33 137 | #define GL_MAX_VIEWPORT_DIMS 0x0d3a 138 | #define GL_SUBPIXEL_BITS 0x0d50 139 | #define GL_RED_BITS 0x0d52 140 | #define GL_GREEN_BITS 0x0d53 141 | #define GL_BLUE_BITS 0x0d54 142 | #define GL_ALPHA_BITS 0x0d55 143 | #define GL_DEPTH_BITS 0x0d56 144 | #define GL_STENCIL_BITS 0x0d57 145 | #define GL_POLYGON_OFFSET_UNITS 0x2a00 146 | #define GL_POLYGON_OFFSET_FACTOR 0x8038 147 | #define GL_TEXTURE_BINDING_2D 0x8069 148 | #define GL_SAMPLE_BUFFERS 0x80a8 149 | #define GL_SAMPLES 0x80a9 150 | #define GL_SAMPLE_COVERAGE_VALUE 0x80aa 151 | #define GL_SAMPLE_COVERAGE_INVERT 0x80ab 152 | #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86a2 153 | #define GL_COMPRESSED_TEXTURE_FORMATS 0x86a3 154 | #define GL_DONT_CARE 0x1100 155 | #define GL_FASTEST 0x1101 156 | #define GL_NICEST 0x1102 157 | #define GL_GENERATE_MIPMAP_HINT 0x8192 158 | #define GL_BYTE 0x1400 159 | #define GL_UNSIGNED_BYTE 0x1401 160 | #define GL_SHORT 0x1402 161 | #define GL_UNSIGNED_SHORT 0x1403 162 | #define GL_INT 0x1404 163 | #define GL_UNSIGNED_INT 0x1405 164 | #define GL_FLOAT 0x1406 165 | #define GL_DEPTH_COMPONENT 0x1902 166 | #define GL_ALPHA 0x1906 167 | #define GL_RGB 0x1907 168 | #define GL_RGBA 0x1908 169 | #define GL_LUMINANCE 0x1909 170 | #define GL_LUMINANCE_ALPHA 0x190a 171 | #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 172 | #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 173 | #define GL_UNSIGNED_SHORT_5_6_5 0x8363 174 | #define GL_FRAGMENT_SHADER 0x8b30 175 | #define GL_VERTEX_SHADER 0x8b31 176 | #define GL_MAX_VERTEX_ATTRIBS 0x8869 177 | #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8dfb 178 | #define GL_MAX_VARYING_VECTORS 0x8dfc 179 | #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8b4d 180 | #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8b4c 181 | #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 182 | #define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8dfd 183 | #define GL_SHADER_TYPE 0x8b4f 184 | #define GL_DELETE_STATUS 0x8b80 185 | #define GL_LINK_STATUS 0x8b82 186 | #define GL_VALIDATE_STATUS 0x8b83 187 | #define GL_ATTACHED_SHADERS 0x8b85 188 | #define GL_ACTIVE_UNIFORMS 0x8b86 189 | #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8b87 190 | #define GL_ACTIVE_ATTRIBUTES 0x8b89 191 | #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8b8a 192 | #define GL_SHADING_LANGUAGE_VERSION 0x8b8c 193 | #define GL_CURRENT_PROGRAM 0x8b8d 194 | #define GL_NEVER 0x0200 195 | #define GL_LESS 0x0201 196 | #define GL_EQUAL 0x0202 197 | #define GL_LEQUAL 0x0203 198 | #define GL_GREATER 0x0204 199 | #define GL_NOTEQUAL 0x0205 200 | #define GL_GEQUAL 0x0206 201 | #define GL_ALWAYS 0x0207 202 | #define GL_KEEP 0x1e00 203 | #define GL_REPLACE 0x1e01 204 | #define GL_INCR 0x1e02 205 | #define GL_DECR 0x1e03 206 | #define GL_INVERT 0x150a 207 | #define GL_INCR_WRAP 0x8507 208 | #define GL_DECR_WRAP 0x8508 209 | #define GL_VENDOR 0x1f00 210 | #define GL_RENDERER 0x1f01 211 | #define GL_VERSION 0x1f02 212 | #define GL_EXTENSIONS 0x1f03 213 | #define GL_NEAREST 0x2600 214 | #define GL_LINEAR 0x2601 215 | #define GL_NEAREST_MIPMAP_NEAREST 0x2700 216 | #define GL_LINEAR_MIPMAP_NEAREST 0x2701 217 | #define GL_NEAREST_MIPMAP_LINEAR 0x2702 218 | #define GL_LINEAR_MIPMAP_LINEAR 0x2703 219 | #define GL_TEXTURE_MAG_FILTER 0x2800 220 | #define GL_TEXTURE_MIN_FILTER 0x2801 221 | #define GL_TEXTURE_WRAP_S 0x2802 222 | #define GL_TEXTURE_WRAP_T 0x2803 223 | #define GL_TEXTURE 0x1702 224 | #define GL_TEXTURE_CUBE_MAP 0x8513 225 | #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 226 | #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 227 | #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 228 | #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 229 | #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 230 | #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 231 | #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851a 232 | #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851c 233 | #define GL_TEXTURE0 0x84c0 234 | #define GL_TEXTURE1 0x84c1 235 | #define GL_TEXTURE2 0x84c2 236 | #define GL_TEXTURE3 0x84c3 237 | #define GL_TEXTURE4 0x84c4 238 | #define GL_TEXTURE5 0x84c5 239 | #define GL_TEXTURE6 0x84c6 240 | #define GL_TEXTURE7 0x84c7 241 | #define GL_TEXTURE8 0x84c8 242 | #define GL_TEXTURE9 0x84c9 243 | #define GL_TEXTURE10 0x84ca 244 | #define GL_TEXTURE11 0x84cb 245 | #define GL_TEXTURE12 0x84cc 246 | #define GL_TEXTURE13 0x84cd 247 | #define GL_TEXTURE14 0x84ce 248 | #define GL_TEXTURE15 0x84cf 249 | #define GL_TEXTURE16 0x84d0 250 | #define GL_TEXTURE17 0x84d1 251 | #define GL_TEXTURE18 0x84d2 252 | #define GL_TEXTURE19 0x84d3 253 | #define GL_TEXTURE20 0x84d4 254 | #define GL_TEXTURE21 0x84d5 255 | #define GL_TEXTURE22 0x84d6 256 | #define GL_TEXTURE23 0x84d7 257 | #define GL_TEXTURE24 0x84d8 258 | #define GL_TEXTURE25 0x84d9 259 | #define GL_TEXTURE26 0x84da 260 | #define GL_TEXTURE27 0x84db 261 | #define GL_TEXTURE28 0x84dc 262 | #define GL_TEXTURE29 0x84dd 263 | #define GL_TEXTURE30 0x84de 264 | #define GL_TEXTURE31 0x84df 265 | #define GL_ACTIVE_TEXTURE 0x84e0 266 | #define GL_REPEAT 0x2901 267 | #define GL_CLAMP_TO_EDGE 0x812f 268 | #define GL_MIRRORED_REPEAT 0x8370 269 | #define GL_FLOAT_VEC2 0x8b50 270 | #define GL_FLOAT_VEC3 0x8b51 271 | #define GL_FLOAT_VEC4 0x8b52 272 | #define GL_INT_VEC2 0x8b53 273 | #define GL_INT_VEC3 0x8b54 274 | #define GL_INT_VEC4 0x8b55 275 | #define GL_BOOL 0x8b56 276 | #define GL_BOOL_VEC2 0x8b57 277 | #define GL_BOOL_VEC3 0x8b58 278 | #define GL_BOOL_VEC4 0x8b59 279 | #define GL_FLOAT_MAT2 0x8b5a 280 | #define GL_FLOAT_MAT3 0x8b5b 281 | #define GL_FLOAT_MAT4 0x8b5c 282 | #define GL_SAMPLER_2D 0x8b5e 283 | #define GL_SAMPLER_CUBE 0x8b60 284 | #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 285 | #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 286 | #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 287 | #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 288 | #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886a 289 | #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 290 | #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889f 291 | #define GL_COMPILE_STATUS 0x8b81 292 | #define GL_INFO_LOG_LENGTH 0x8b84 293 | #define GL_SHADER_SOURCE_LENGTH 0x8b88 294 | #define GL_LOW_FLOAT 0x8df0 295 | #define GL_MEDIUM_FLOAT 0x8df1 296 | #define GL_HIGH_FLOAT 0x8df2 297 | #define GL_LOW_INT 0x8df3 298 | #define GL_MEDIUM_INT 0x8df4 299 | #define GL_HIGH_INT 0x8df5 300 | #define GL_FRAMEBUFFER 0x8d40 301 | #define GL_RENDERBUFFER 0x8d41 302 | #define GL_RGBA4 0x8056 303 | #define GL_RGB5_A1 0x8057 304 | #define GL_RGB565 0x8d62 305 | #define GL_DEPTH_COMPONENT16 0x81a5 306 | #define GL_STENCIL_INDEX 0x1901 307 | #define GL_STENCIL_INDEX8 0x8d48 308 | #define GL_DEPTH_STENCIL 0x84f9 309 | #define GL_RENDERBUFFER_WIDTH 0x8d42 310 | #define GL_RENDERBUFFER_HEIGHT 0x8d43 311 | #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8d44 312 | #define GL_RENDERBUFFER_RED_SIZE 0x8d50 313 | #define GL_RENDERBUFFER_GREEN_SIZE 0x8d51 314 | #define GL_RENDERBUFFER_BLUE_SIZE 0x8d52 315 | #define GL_RENDERBUFFER_ALPHA_SIZE 0x8d53 316 | #define GL_RENDERBUFFER_DEPTH_SIZE 0x8d54 317 | #define GL_RENDERBUFFER_STENCIL_SIZE 0x8d55 318 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8cd0 319 | #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8cd1 320 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8cd2 321 | #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8cd3 322 | #define GL_COLOR_ATTACHMENT0 0x8ce0 323 | #define GL_DEPTH_ATTACHMENT 0x8d00 324 | #define GL_STENCIL_ATTACHMENT 0x8d20 325 | #define GL_DEPTH_STENCIL_ATTACHMENT 0x821a 326 | #define GL_NONE 0x0000 327 | #define GL_FRAMEBUFFER_COMPLETE 0x8cd5 328 | #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8cd6 329 | #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8cd7 330 | #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8cd9 331 | #define GL_FRAMEBUFFER_UNSUPPORTED 0x8cdd 332 | #define GL_FRAMEBUFFER_BINDING 0x8ca6 333 | #define GL_RENDERBUFFER_BINDING 0x8ca7 334 | #define GL_MAX_RENDERBUFFER_SIZE 0x84e8 335 | #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 336 | #define GL_UNPACK_FLIP_Y_WEBGL 0x9240 337 | #define GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241 338 | #define GL_CONTEXT_LOST_WEBGL 0x9242 339 | #define GL_UNPACK_COLORSPACE_CONVERSION_WEBGL 0x9243 340 | #define GL_BROWSER_DEFAULT_WEBGL 0x9244 341 | void glActiveTexture(unsigned int texture); 342 | void glAttachShader(unsigned int program, unsigned int shader); 343 | void glBindAttribLocation(unsigned int program, unsigned int index, const char* name); 344 | void glBindBuffer(unsigned int target, unsigned int buffer); 345 | void glBindFramebuffer(unsigned int target, unsigned int framebuffer); 346 | void glBindRenderbuffer(unsigned int target, unsigned int renderbuffer); 347 | void glBindTexture(unsigned int target, unsigned int texture); 348 | void glBlendColor(float red, float green, float blue, float alpha); 349 | void glBlendEquation(unsigned int mode); 350 | void glBlendEquationSeparate(unsigned int modeRGB, unsigned int modeAlpha); 351 | void glBlendFunc(unsigned int sfactor, unsigned int dfactor); 352 | void glBlendFuncSeparate(unsigned int srcRGB, unsigned int dstRGB, unsigned int srcAlpha, unsigned int dstAlpha); 353 | void glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage); 354 | void glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data); 355 | unsigned int glCheckFramebufferStatus(unsigned int target); 356 | void glClear(unsigned int mask); 357 | void glClearColor(float red, float green, float blue, float alpha); 358 | void glClearDepth(float depth); 359 | void glClearDepthf(float depth); 360 | void glClearStencil(int s); 361 | void glColorMask(bool red, bool green, bool blue, bool alpha); 362 | void glCompileShader(unsigned int shader); 363 | void glCopyTexImage2D(unsigned int target, int level, unsigned int internalformat, int x, int y, int width, int height, int border); 364 | void glCopyTexSubImage2D(unsigned int target, int level, int xoffset, int yoffset, int x, int y, int width, int height); 365 | void glGenBuffers (GLsizei n, GLuint* objs); 366 | void glGenFramebuffers (GLsizei n, GLuint* objs); 367 | GLuint glCreateProgram (); 368 | void glGenRenderbuffers (GLsizei n, GLuint* objs); 369 | GLuint glCreateShader (GLenum shaderType); 370 | void glGenTextures (GLsizei n, GLuint* objs); 371 | void glCullFace(unsigned int mode); 372 | void glDeleteBuffers (GLsizei n, GLuint* objs); 373 | void glDeleteFramebuffers (GLsizei n, GLuint* objs); 374 | void glDeleteProgram(unsigned int program); 375 | void glDeleteRenderbuffers (GLsizei n, GLuint* objs); 376 | void glDeleteShader(unsigned int shader); 377 | void glDeleteTextures (GLsizei n, GLuint* objs); 378 | void glDepthFunc(unsigned int func); 379 | void glDepthMask(bool flag); 380 | void glDepthRange(float zNear, float zFar); 381 | void glDetachShader(unsigned int program, unsigned int shader); 382 | void glDisable(unsigned int cap); 383 | void glDisableVertexAttribArray(unsigned int index); 384 | void glDrawArrays(unsigned int mode, int first, int count); 385 | void glDrawElements(unsigned int mode, int count, unsigned int type, int offset); 386 | void glEnable(unsigned int cap); 387 | void glEnableVertexAttribArray(unsigned int index); 388 | void glFinish(); 389 | void glFlush(); 390 | void glFramebufferRenderbuffer(unsigned int target, unsigned int attachment, unsigned int renderbuffertarget, unsigned int renderbuffer); 391 | void glFramebufferTexture2D(unsigned int target, unsigned int attachment, unsigned int textarget, unsigned int texture, int level); 392 | void glFrontFace(unsigned int mode); 393 | void glGenerateMipmap(unsigned int target); 394 | int glGetAttribLocation(unsigned int program, const char* name); 395 | void __attribute__((always_inline)) glGetBooleanv (GLenum pname, GLboolean* data); 396 | void __attribute__((always_inline)) glGetDoublev (GLenum pname, GLdouble* data); 397 | void __attribute__((always_inline)) glGetFloatv (GLenum pname, GLfloat* data); 398 | void __attribute__((always_inline)) glGetIntegerv (GLenum pname, GLint* data); 399 | [[cheerp::wasm]] const GLubyte* wgGetStringWasm (GLenum pname); 400 | [[cheerp::genericjs]] const GLubyte* wgGetStringGenericjs (GLenum pname); 401 | #if defined(__WASM__) || defined(__ASMJS__) 402 | [[cheerp::wasm]] inline const GLubyte* glGetString (GLenum pname) { return wgGetStringWasm(pname); } 403 | #else 404 | [[cheerp::genericjs]] inline const GLubyte* glGetString (GLenum pname) { return wgGetStringGenericjs(pname); } 405 | #endif 406 | unsigned int glGetError(); 407 | GLint glGetUniformLocation (GLuint program, const char* name); 408 | int glGetVertexAttribOffset(unsigned int index, unsigned int pname); 409 | void glHint(unsigned int target, unsigned int mode); 410 | bool glIsBuffer(unsigned int buffer); 411 | bool glIsEnabled(unsigned int cap); 412 | bool glIsFramebuffer(unsigned int framebuffer); 413 | bool glIsProgram(unsigned int program); 414 | bool glIsRenderbuffer(unsigned int renderbuffer); 415 | bool glIsShader(unsigned int shader); 416 | bool glIsTexture(unsigned int texture); 417 | void glLineWidth(float width); 418 | void glLinkProgram(unsigned int program); 419 | void glPixelStorei(unsigned int pname, int param); 420 | void glPolygonOffset(float factor, float units); 421 | void glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data); 422 | void glRenderbufferStorage(unsigned int target, unsigned int internalformat, int width, int height); 423 | void glSampleCoverage(float value, bool invert); 424 | void glScissor(int x, int y, int width, int height); 425 | [[cheerp::wasm]] void wgShaderSourceWasm (GLuint shader, GLsizei count, const char* const *string, const GLint* length); 426 | [[cheerp::genericjs]] void wgShaderSourceGenericjs (GLuint shader, GLsizei count, const char* const *string, const GLint* length); 427 | #if defined(__WASM__) || defined(__ASMJS__) 428 | [[cheerp::wasm]] inline void glShaderSource (GLuint shader, GLsizei count, const char* const *string, const GLint* length) { return wgShaderSourceWasm(shader, count, string, length); } 429 | #else 430 | [[cheerp::genericjs]] inline void glShaderSource (GLuint shader, GLsizei count, const char* const *string, const GLint* length) { return wgShaderSourceGenericjs(shader, count, string, length); } 431 | #endif 432 | void glStencilFunc(unsigned int func, int ref, unsigned int mask); 433 | void glStencilFuncSeparate(unsigned int face, unsigned int func, int ref, unsigned int mask); 434 | void glStencilMask(unsigned int mask); 435 | void glStencilMaskSeparate(unsigned int face, unsigned int mask); 436 | void glStencilOp(unsigned int fail, unsigned int zfail, unsigned int zpass); 437 | void glStencilOpSeparate(unsigned int face, unsigned int fail, unsigned int zfail, unsigned int zpass); 438 | void glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data); 439 | void glTexParameterf(unsigned int target, unsigned int pname, float param); 440 | void glTexParameteri(unsigned int target, unsigned int pname, int param); 441 | void glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data); 442 | void glUniform1f(unsigned int location, float x); 443 | void glUniform1fv (GLint location, GLsizei count, const GLfloat* value); 444 | void glUniform1i(unsigned int location, int x); 445 | void glUniform1iv (GLint location, GLsizei count, const GLint* value); 446 | void glUniform2f(unsigned int location, float x, float y); 447 | void glUniform2fv (GLint location, GLsizei count, const GLfloat* value); 448 | void glUniform2i(unsigned int location, int x, int y); 449 | void glUniform2iv (GLint location, GLsizei count, const GLint* value); 450 | void glUniform3f(unsigned int location, float x, float y, float z); 451 | void glUniform3fv (GLint location, GLsizei count, const GLfloat* value); 452 | void glUniform3i(unsigned int location, int x, int y, int z); 453 | void glUniform3iv (GLint location, GLsizei count, const GLint* value); 454 | void glUniform4f(unsigned int location, float x, float y, float z, float w); 455 | void glUniform4fv (GLint location, GLsizei count, const GLfloat* value); 456 | void glUniform4i(unsigned int location, int x, int y, int z, int w); 457 | void glUniform4iv (GLint location, GLsizei count, const GLint* value); 458 | void glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); 459 | void glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); 460 | void glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); 461 | void glUseProgram(unsigned int program); 462 | void glValidateProgram(unsigned int program); 463 | void glVertexAttrib1f(unsigned int indx, float x); 464 | void glVertexAttrib1fv (GLuint index, const GLfloat* v); 465 | void glVertexAttrib2f(unsigned int indx, float x, float y); 466 | void glVertexAttrib2fv (GLuint index, const GLfloat* v); 467 | void glVertexAttrib3f(unsigned int indx, float x, float y, float z); 468 | void glVertexAttrib3fv (GLuint index, const GLfloat* v); 469 | void glVertexAttrib4f(unsigned int indx, float x, float y, float z, float w); 470 | void glVertexAttrib4fv (GLuint index, const GLfloat* v); 471 | void glVertexAttribPointer(unsigned int indx, int size, unsigned int type, bool normalized, int stride, int offset); 472 | void glViewport(int x, int y, int width, int height); 473 | #define GL_VERTEX_ARRAY_BINDING_OES 0x85b5 474 | void glGenVertexArraysOES (GLsizei n, GLuint* objs); 475 | void glDeleteVertexArraysOES (GLsizei n, GLuint* objs); 476 | bool glIsVertexArrayOES(unsigned int arrayObject); 477 | void glBindVertexArrayOES(unsigned int arrayObject); 478 | void glGetShaderiv(GLuint shader, GLenum pname, GLint *params); 479 | void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog); 480 | void glGetProgramiv(GLuint program, GLenum pname, GLint *params); 481 | void glGetProgramInfoLog(GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog); 482 | void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); 483 | void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); 484 | void glGetAttachedShaders(GLuint program, GLsizei max, GLsizei* count, GLuint* shaders); 485 | void glGetBufferParameteriv(GLenum target, GLenum value, GLint* data); 486 | void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* data); 487 | void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* data); 488 | void glGetShaderPrecisionFormat(GLenum sType, GLenum pType, GLint* range, GLint* prec); 489 | void glGetTexParameteriv(GLenum target, GLenum pname, GLint* data); 490 | void glGetUniformfv(GLuint program, GLint location, GLfloat* data); 491 | void glGetUniformiv(GLuint program, GLint location, GLint* data); 492 | void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* data); 493 | void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* data); 494 | #ifdef __cplusplus 495 | } 496 | #endif 497 | #endif 498 | -------------------------------------------------------------------------------- /webgles/webgles.cpp: -------------------------------------------------------------------------------- 1 | //===-- webgles.cpp - OpenGL ES implementation over WebGL --------------===// 2 | // 3 | // Cheerp: The C++ compiler for the Web 4 | // 5 | // This file is distributed under the Apache License v2.0 with LLVM Exceptions. 6 | // See LICENSE.TXT for details. 7 | // 8 | // Copyright 2018-2023 Leaning Technologies 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #include "webgles.h" 13 | #include 14 | client::Array* WebGLProgramArray; 15 | 16 | client::WebGLProgram* webGLESLookupWebGLProgram(int objId) 17 | { 18 | return static_cast((*WebGLProgramArray)[objId-1]); 19 | } 20 | 21 | client::Array* WebGLShaderArray; 22 | 23 | client::WebGLShader* webGLESLookupWebGLShader(int objId) 24 | { 25 | return static_cast((*WebGLShaderArray)[objId-1]); 26 | } 27 | 28 | client::Array* WebGLBufferArray; 29 | 30 | client::WebGLBuffer* webGLESLookupWebGLBuffer(int objId) 31 | { 32 | return static_cast((*WebGLBufferArray)[objId-1]); 33 | } 34 | 35 | client::Array* WebGLFramebufferArray; 36 | 37 | client::WebGLFramebuffer* webGLESLookupWebGLFramebuffer(int objId) 38 | { 39 | return static_cast((*WebGLFramebufferArray)[objId-1]); 40 | } 41 | 42 | client::Array* WebGLRenderbufferArray; 43 | 44 | client::WebGLRenderbuffer* webGLESLookupWebGLRenderbuffer(int objId) 45 | { 46 | return static_cast((*WebGLRenderbufferArray)[objId-1]); 47 | } 48 | 49 | client::Array* WebGLTextureArray; 50 | 51 | client::WebGLTexture* webGLESLookupWebGLTexture(int objId) 52 | { 53 | return static_cast((*WebGLTextureArray)[objId-1]); 54 | } 55 | 56 | client::Array* WebGLUniformLocationArray; 57 | 58 | client::WebGLUniformLocation* webGLESLookupWebGLUniformLocation(int objId) 59 | { 60 | return static_cast((*WebGLUniformLocationArray)[objId-1]); 61 | } 62 | 63 | client::Array* WebGLVertexArrayOESArray; 64 | 65 | client::WebGLVertexArrayOES* webGLESLookupWebGLVertexArrayOES(int objId) 66 | { 67 | return static_cast((*WebGLVertexArrayOESArray)[objId-1]); 68 | } 69 | 70 | void webGLESLookupArrayInit() { 71 | WebGLProgramArray = new client::Array(); 72 | WebGLShaderArray = new client::Array(); 73 | WebGLBufferArray = new client::Array(); 74 | WebGLFramebufferArray = new client::Array(); 75 | WebGLRenderbufferArray = new client::Array(); 76 | WebGLTextureArray = new client::Array(); 77 | WebGLUniformLocationArray = new client::Array(); 78 | WebGLVertexArrayOESArray = new client::Array(); 79 | } 80 | 81 | void glActiveTexture(unsigned int texture) 82 | { 83 | return webGLES->activeTexture(texture); 84 | } 85 | 86 | void glAttachShader(unsigned int program, unsigned int shader) 87 | { 88 | return webGLES->attachShader(webGLESLookupWebGLProgram(program),webGLESLookupWebGLShader(shader)); 89 | } 90 | 91 | void glBindAttribLocation(unsigned int program, unsigned int index, const char* name) 92 | { 93 | return webGLES->bindAttribLocation(webGLESLookupWebGLProgram(program),index,name); 94 | } 95 | 96 | void glBindBuffer(unsigned int target, unsigned int buffer) 97 | { 98 | return webGLES->bindBuffer(target,webGLESLookupWebGLBuffer(buffer)); 99 | } 100 | 101 | void glBindFramebuffer(unsigned int target, unsigned int framebuffer) 102 | { 103 | return webGLES->bindFramebuffer(target,webGLESLookupWebGLFramebuffer(framebuffer)); 104 | } 105 | 106 | void glBindRenderbuffer(unsigned int target, unsigned int renderbuffer) 107 | { 108 | return webGLES->bindRenderbuffer(target,webGLESLookupWebGLRenderbuffer(renderbuffer)); 109 | } 110 | 111 | void glBindTexture(unsigned int target, unsigned int texture) 112 | { 113 | return webGLES->bindTexture(target,webGLESLookupWebGLTexture(texture)); 114 | } 115 | 116 | void glBlendColor(float red, float green, float blue, float alpha) 117 | { 118 | return webGLES->blendColor(red,green,blue,alpha); 119 | } 120 | 121 | void glBlendEquation(unsigned int mode) 122 | { 123 | return webGLES->blendEquation(mode); 124 | } 125 | 126 | void glBlendEquationSeparate(unsigned int modeRGB, unsigned int modeAlpha) 127 | { 128 | return webGLES->blendEquationSeparate(modeRGB,modeAlpha); 129 | } 130 | 131 | void glBlendFunc(unsigned int sfactor, unsigned int dfactor) 132 | { 133 | return webGLES->blendFunc(sfactor,dfactor); 134 | } 135 | 136 | void glBlendFuncSeparate(unsigned int srcRGB, unsigned int dstRGB, unsigned int srcAlpha, unsigned int dstAlpha) 137 | { 138 | return webGLES->blendFuncSeparate(srcRGB,dstRGB,srcAlpha,dstAlpha); 139 | } 140 | 141 | void glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage) 142 | { 143 | if(data==NULL) webGLES->bufferData(target, size, usage); 144 | else webGLES->bufferData(target, cheerp::MakeArrayBufferView(data, size), usage); 145 | } 146 | 147 | void glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data) 148 | { 149 | webGLES->bufferSubData(target, offset, cheerp::MakeArrayBufferView(data, size)); 150 | } 151 | 152 | unsigned int glCheckFramebufferStatus(unsigned int target) 153 | { 154 | return webGLES->checkFramebufferStatus(target); 155 | } 156 | 157 | void glClear(unsigned int mask) 158 | { 159 | return webGLES->clear(mask); 160 | } 161 | 162 | void glClearColor(float red, float green, float blue, float alpha) 163 | { 164 | return webGLES->clearColor(red,green,blue,alpha); 165 | } 166 | 167 | void glClearDepth(float depth) 168 | { 169 | return webGLES->clearDepth(depth); 170 | } 171 | 172 | void glClearDepthf(float depth) 173 | { 174 | return glClearDepth(depth); 175 | } 176 | 177 | void glClearStencil(int s) 178 | { 179 | return webGLES->clearStencil(s); 180 | } 181 | 182 | void glColorMask(bool red, bool green, bool blue, bool alpha) 183 | { 184 | return webGLES->colorMask(red,green,blue,alpha); 185 | } 186 | 187 | void glCompileShader(unsigned int shader) 188 | { 189 | return webGLES->compileShader(webGLESLookupWebGLShader(shader)); 190 | } 191 | 192 | void glCopyTexImage2D(unsigned int target, int level, unsigned int internalformat, int x, int y, int width, int height, int border) 193 | { 194 | return webGLES->copyTexImage2D(target,level,internalformat,x,y,width,height,border); 195 | } 196 | 197 | void glCopyTexSubImage2D(unsigned int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) 198 | { 199 | return webGLES->copyTexSubImage2D(target,level,xoffset,yoffset,x,y,width,height); 200 | } 201 | 202 | void glGenBuffers (GLsizei n, GLuint* objs) 203 | { 204 | for(GLsizei i=0;icreateBuffer(); 206 | int index=WebGLBufferArray->indexOf(nullptr); 207 | if(index>=0) { (*WebGLBufferArray)[index] = obj; objs[i]=index+1; } 208 | else { index=WebGLBufferArray->push(obj); objs[i]=index; } 209 | } 210 | } 211 | 212 | void glGenFramebuffers (GLsizei n, GLuint* objs) 213 | { 214 | for(GLsizei i=0;icreateFramebuffer(); 216 | int index=WebGLFramebufferArray->indexOf(nullptr); 217 | if(index>=0) { (*WebGLFramebufferArray)[index] = obj; objs[i]=index+1; } 218 | else { index=WebGLFramebufferArray->push(obj); objs[i]=index; } 219 | } 220 | } 221 | 222 | GLuint glCreateProgram () 223 | { 224 | client::WebGLProgram* obj=webGLES->createProgram(); 225 | int index=WebGLProgramArray->indexOf(nullptr); 226 | if(index>=0) { (*WebGLProgramArray)[index] = obj; return index+1; } 227 | else { index=WebGLProgramArray->push(obj); return index; } 228 | } 229 | 230 | void glGenRenderbuffers (GLsizei n, GLuint* objs) 231 | { 232 | for(GLsizei i=0;icreateRenderbuffer(); 234 | int index=WebGLRenderbufferArray->indexOf(nullptr); 235 | if(index>=0) { (*WebGLRenderbufferArray)[index] = obj; objs[i]=index+1; } 236 | else { index=WebGLRenderbufferArray->push(obj); objs[i]=index; } 237 | } 238 | } 239 | 240 | GLuint glCreateShader (GLenum shaderType) 241 | { 242 | client::WebGLShader* obj=webGLES->createShader(shaderType); 243 | int index=WebGLShaderArray->indexOf(nullptr); 244 | if(index>=0) { (*WebGLShaderArray)[index] = obj; return index+1; } 245 | else { index=WebGLShaderArray->push(obj); return index; } 246 | } 247 | 248 | void glGenTextures (GLsizei n, GLuint* objs) 249 | { 250 | for(GLsizei i=0;icreateTexture(); 252 | int index=WebGLTextureArray->indexOf(nullptr); 253 | if(index>=0) { (*WebGLTextureArray)[index] = obj; objs[i]=index+1; } 254 | else { index=WebGLTextureArray->push(obj); objs[i]=index; } 255 | } 256 | } 257 | 258 | void glCullFace(unsigned int mode) 259 | { 260 | return webGLES->cullFace(mode); 261 | } 262 | 263 | void glDeleteBuffers (GLsizei n, GLuint* objs) 264 | { 265 | for(GLsizei i=0;i((*WebGLBufferArray)[index]); 268 | webGLES->deleteBuffer(obj); 269 | (*WebGLBufferArray)[index]=NULL; 270 | } 271 | } 272 | 273 | void glDeleteFramebuffers (GLsizei n, GLuint* objs) 274 | { 275 | for(GLsizei i=0;i((*WebGLFramebufferArray)[index]); 278 | webGLES->deleteFramebuffer(obj); 279 | (*WebGLFramebufferArray)[index]=NULL; 280 | } 281 | } 282 | 283 | void glDeleteProgram(unsigned int program) 284 | { 285 | return webGLES->deleteProgram(webGLESLookupWebGLProgram(program)); 286 | } 287 | 288 | void glDeleteRenderbuffers (GLsizei n, GLuint* objs) 289 | { 290 | for(GLsizei i=0;i((*WebGLRenderbufferArray)[index]); 293 | webGLES->deleteRenderbuffer(obj); 294 | (*WebGLRenderbufferArray)[index]=NULL; 295 | } 296 | } 297 | 298 | void glDeleteShader(unsigned int shader) 299 | { 300 | return webGLES->deleteShader(webGLESLookupWebGLShader(shader)); 301 | } 302 | 303 | void glDeleteTextures (GLsizei n, GLuint* objs) 304 | { 305 | for(GLsizei i=0;i((*WebGLTextureArray)[index]); 308 | webGLES->deleteTexture(obj); 309 | (*WebGLTextureArray)[index]=NULL; 310 | } 311 | } 312 | 313 | void glDepthFunc(unsigned int func) 314 | { 315 | return webGLES->depthFunc(func); 316 | } 317 | 318 | void glDepthMask(bool flag) 319 | { 320 | return webGLES->depthMask(flag); 321 | } 322 | 323 | void glDepthRange(float zNear, float zFar) 324 | { 325 | return webGLES->depthRange(zNear,zFar); 326 | } 327 | 328 | void glDetachShader(unsigned int program, unsigned int shader) 329 | { 330 | return webGLES->detachShader(webGLESLookupWebGLProgram(program),webGLESLookupWebGLShader(shader)); 331 | } 332 | 333 | void glDisable(unsigned int cap) 334 | { 335 | return webGLES->disable(cap); 336 | } 337 | 338 | void glDisableVertexAttribArray(unsigned int index) 339 | { 340 | return webGLES->disableVertexAttribArray(index); 341 | } 342 | 343 | void glDrawArrays(unsigned int mode, int first, int count) 344 | { 345 | return webGLES->drawArrays(mode,first,count); 346 | } 347 | 348 | void glDrawElements(unsigned int mode, int count, unsigned int type, int offset) 349 | { 350 | return webGLES->drawElements(mode,count,type,offset); 351 | } 352 | 353 | void glEnable(unsigned int cap) 354 | { 355 | return webGLES->enable(cap); 356 | } 357 | 358 | void glEnableVertexAttribArray(unsigned int index) 359 | { 360 | return webGLES->enableVertexAttribArray(index); 361 | } 362 | 363 | void glFinish() 364 | { 365 | return webGLES->finish(); 366 | } 367 | 368 | void glFlush() 369 | { 370 | return webGLES->flush(); 371 | } 372 | 373 | void glFramebufferRenderbuffer(unsigned int target, unsigned int attachment, unsigned int renderbuffertarget, unsigned int renderbuffer) 374 | { 375 | return webGLES->framebufferRenderbuffer(target,attachment,renderbuffertarget,webGLESLookupWebGLRenderbuffer(renderbuffer)); 376 | } 377 | 378 | void glFramebufferTexture2D(unsigned int target, unsigned int attachment, unsigned int textarget, unsigned int texture, int level) 379 | { 380 | return webGLES->framebufferTexture2D(target,attachment,textarget,webGLESLookupWebGLTexture(texture),level); 381 | } 382 | 383 | void glFrontFace(unsigned int mode) 384 | { 385 | return webGLES->frontFace(mode); 386 | } 387 | 388 | void glGenerateMipmap(unsigned int target) 389 | { 390 | return webGLES->generateMipmap(target); 391 | } 392 | 393 | int glGetAttribLocation(unsigned int program, const char* name) 394 | { 395 | return webGLES->getAttribLocation(webGLESLookupWebGLProgram(program),name); 396 | } 397 | 398 | void __attribute__((always_inline)) glGetBooleanv (GLenum pname, GLboolean* data) 399 | { 400 | client::Object* ret = webGLES->getParameter(pname); 401 | if(ret==nullptr) 402 | { 403 | *data = 0; 404 | return; 405 | } 406 | int numElements = 0; 407 | switch(pname) 408 | { 409 | case GL_ARRAY_BUFFER_BINDING: 410 | case GL_ELEMENT_ARRAY_BUFFER_BINDING: 411 | *data = WebGLBufferArray->indexOf(ret)+1; 412 | return; 413 | case GL_CURRENT_PROGRAM: 414 | *data = WebGLProgramArray->indexOf(ret)+1; 415 | return; 416 | case GL_FRAMEBUFFER_BINDING: 417 | *data = WebGLFramebufferArray->indexOf(ret)+1; 418 | return; 419 | case GL_RENDERBUFFER_BINDING: 420 | *data = WebGLRenderbufferArray->indexOf(ret)+1; 421 | return; 422 | case GL_TEXTURE_BINDING_2D: 423 | case GL_TEXTURE_BINDING_CUBE_MAP: 424 | *data = WebGLTextureArray->indexOf(ret)+1; 425 | return; 426 | case GL_ALIASED_LINE_WIDTH_RANGE: 427 | case GL_ALIASED_POINT_SIZE_RANGE: 428 | case GL_DEPTH_RANGE: 429 | case GL_MAX_VIEWPORT_DIMS: 430 | numElements = 2; 431 | break; 432 | case GL_BLEND_COLOR: 433 | case GL_COLOR_CLEAR_VALUE: 434 | case GL_COLOR_WRITEMASK: 435 | case GL_SCISSOR_BOX: 436 | case GL_VIEWPORT: 437 | numElements = 4; 438 | break; 439 | default: 440 | break; 441 | } 442 | if(numElements == 0) 443 | { 444 | *data = (double)*ret; 445 | return; 446 | } 447 | for(GLsizei i=0;igetParameter(pname); 457 | if(ret==nullptr) 458 | { 459 | *data = 0; 460 | return; 461 | } 462 | int numElements = 0; 463 | switch(pname) 464 | { 465 | case GL_ARRAY_BUFFER_BINDING: 466 | case GL_ELEMENT_ARRAY_BUFFER_BINDING: 467 | *data = WebGLBufferArray->indexOf(ret)+1; 468 | return; 469 | case GL_CURRENT_PROGRAM: 470 | *data = WebGLProgramArray->indexOf(ret)+1; 471 | return; 472 | case GL_FRAMEBUFFER_BINDING: 473 | *data = WebGLFramebufferArray->indexOf(ret)+1; 474 | return; 475 | case GL_RENDERBUFFER_BINDING: 476 | *data = WebGLRenderbufferArray->indexOf(ret)+1; 477 | return; 478 | case GL_TEXTURE_BINDING_2D: 479 | case GL_TEXTURE_BINDING_CUBE_MAP: 480 | *data = WebGLTextureArray->indexOf(ret)+1; 481 | return; 482 | case GL_ALIASED_LINE_WIDTH_RANGE: 483 | case GL_ALIASED_POINT_SIZE_RANGE: 484 | case GL_DEPTH_RANGE: 485 | case GL_MAX_VIEWPORT_DIMS: 486 | numElements = 2; 487 | break; 488 | case GL_BLEND_COLOR: 489 | case GL_COLOR_CLEAR_VALUE: 490 | case GL_COLOR_WRITEMASK: 491 | case GL_SCISSOR_BOX: 492 | case GL_VIEWPORT: 493 | numElements = 4; 494 | break; 495 | default: 496 | break; 497 | } 498 | if(numElements == 0) 499 | { 500 | *data = (double)*ret; 501 | return; 502 | } 503 | for(GLsizei i=0;igetParameter(pname); 513 | if(ret==nullptr) 514 | { 515 | *data = 0; 516 | return; 517 | } 518 | int numElements = 0; 519 | switch(pname) 520 | { 521 | case GL_ARRAY_BUFFER_BINDING: 522 | case GL_ELEMENT_ARRAY_BUFFER_BINDING: 523 | *data = WebGLBufferArray->indexOf(ret)+1; 524 | return; 525 | case GL_CURRENT_PROGRAM: 526 | *data = WebGLProgramArray->indexOf(ret)+1; 527 | return; 528 | case GL_FRAMEBUFFER_BINDING: 529 | *data = WebGLFramebufferArray->indexOf(ret)+1; 530 | return; 531 | case GL_RENDERBUFFER_BINDING: 532 | *data = WebGLRenderbufferArray->indexOf(ret)+1; 533 | return; 534 | case GL_TEXTURE_BINDING_2D: 535 | case GL_TEXTURE_BINDING_CUBE_MAP: 536 | *data = WebGLTextureArray->indexOf(ret)+1; 537 | return; 538 | case GL_ALIASED_LINE_WIDTH_RANGE: 539 | case GL_ALIASED_POINT_SIZE_RANGE: 540 | case GL_DEPTH_RANGE: 541 | case GL_MAX_VIEWPORT_DIMS: 542 | numElements = 2; 543 | break; 544 | case GL_BLEND_COLOR: 545 | case GL_COLOR_CLEAR_VALUE: 546 | case GL_COLOR_WRITEMASK: 547 | case GL_SCISSOR_BOX: 548 | case GL_VIEWPORT: 549 | numElements = 4; 550 | break; 551 | default: 552 | break; 553 | } 554 | if(numElements == 0) 555 | { 556 | *data = (double)*ret; 557 | return; 558 | } 559 | for(GLsizei i=0;igetParameter(pname); 569 | if(ret==nullptr) 570 | { 571 | *data = 0; 572 | return; 573 | } 574 | int numElements = 0; 575 | switch(pname) 576 | { 577 | case GL_ARRAY_BUFFER_BINDING: 578 | case GL_ELEMENT_ARRAY_BUFFER_BINDING: 579 | *data = WebGLBufferArray->indexOf(ret)+1; 580 | return; 581 | case GL_CURRENT_PROGRAM: 582 | *data = WebGLProgramArray->indexOf(ret)+1; 583 | return; 584 | case GL_FRAMEBUFFER_BINDING: 585 | *data = WebGLFramebufferArray->indexOf(ret)+1; 586 | return; 587 | case GL_RENDERBUFFER_BINDING: 588 | *data = WebGLRenderbufferArray->indexOf(ret)+1; 589 | return; 590 | case GL_TEXTURE_BINDING_2D: 591 | case GL_TEXTURE_BINDING_CUBE_MAP: 592 | *data = WebGLTextureArray->indexOf(ret)+1; 593 | return; 594 | case GL_ALIASED_LINE_WIDTH_RANGE: 595 | case GL_ALIASED_POINT_SIZE_RANGE: 596 | case GL_DEPTH_RANGE: 597 | case GL_MAX_VIEWPORT_DIMS: 598 | numElements = 2; 599 | break; 600 | case GL_BLEND_COLOR: 601 | case GL_COLOR_CLEAR_VALUE: 602 | case GL_COLOR_WRITEMASK: 603 | case GL_SCISSOR_BOX: 604 | case GL_VIEWPORT: 605 | numElements = 4; 606 | break; 607 | default: 608 | break; 609 | } 610 | if(numElements == 0) 611 | { 612 | *data = (double)*ret; 613 | return; 614 | } 615 | for(GLsizei i=0;i& exts = *webGLES->getSupportedExtensions(); 626 | int totalLen = 0; 627 | for(uint32_t i=0;iget_length(); 629 | } 630 | if(computeLen) 631 | return totalLen; 632 | char* tmp = buf; 633 | int usedLen = 0; 634 | for(uint32_t i=0;i0) tmp[usedLen++] = ' '; 636 | tmp[usedLen++] = 'G'; 637 | tmp[usedLen++] = 'L'; 638 | tmp[usedLen++] = '_'; 639 | for(int j=0;jget_length();j++) 640 | tmp[usedLen++] = exts[i]->charCodeAt(j); 641 | } 642 | tmp[usedLen++] = 0; 643 | assert(usedLen == totalLen); 644 | return totalLen; 645 | }else{ 646 | client::String* ret = (client::String*)webGLES->getParameter(pname); 647 | int totalLen = ret->get_length(); 648 | if(computeLen) 649 | return totalLen + 1; 650 | char* tmp = buf; 651 | for(int j=0;jcharCodeAt(j); 653 | tmp[totalLen] = 0; 654 | return totalLen; 655 | } 656 | } 657 | 658 | [[cheerp::wasm]] const GLubyte* wgGetStringWasm (GLenum pname) 659 | { 660 | static char* cachedStrings[5]; 661 | int cachedPos = -1; 662 | if(pname == GL_VENDOR) 663 | cachedPos = 0; 664 | else if(pname == GL_RENDERER) 665 | cachedPos = 1; 666 | else if(pname == GL_VERSION) 667 | cachedPos = 2; 668 | else if(pname == GL_SHADING_LANGUAGE_VERSION) 669 | cachedPos = 3; 670 | else if(pname == GL_EXTENSIONS) 671 | cachedPos = 4; 672 | else 673 | return NULL; 674 | if(cachedStrings[cachedPos]) 675 | return (const GLubyte*)cachedStrings[cachedPos]; 676 | int totalLen = getStringImpl(nullptr, true, pname); 677 | char* buf = (char*)malloc(totalLen); 678 | getStringImpl(buf, false, pname); 679 | cachedStrings[cachedPos] = buf; 680 | return (const GLubyte*)buf; 681 | } 682 | 683 | [[cheerp::genericjs]] const GLubyte* wgGetStringGenericjs (GLenum pname) 684 | { 685 | static char* cachedStrings[5]; 686 | int cachedPos = -1; 687 | if(pname == GL_VENDOR) 688 | cachedPos = 0; 689 | else if(pname == GL_RENDERER) 690 | cachedPos = 1; 691 | else if(pname == GL_VERSION) 692 | cachedPos = 2; 693 | else if(pname == GL_SHADING_LANGUAGE_VERSION) 694 | cachedPos = 3; 695 | else if(pname == GL_EXTENSIONS) 696 | cachedPos = 4; 697 | else 698 | return NULL; 699 | if(cachedStrings[cachedPos]) 700 | return (const GLubyte*)cachedStrings[cachedPos]; 701 | int totalLen = getStringImpl(nullptr, true, pname); 702 | char* buf = (char*)malloc(totalLen); 703 | getStringImpl(buf, false, pname); 704 | cachedStrings[cachedPos] = buf; 705 | return (const GLubyte*)buf; 706 | } 707 | 708 | unsigned int glGetError() 709 | { 710 | return webGLES->getError(); 711 | } 712 | 713 | GLint glGetUniformLocation (GLuint program, const char* name) 714 | { 715 | client::WebGLUniformLocation* obj=webGLES->getUniformLocation(webGLESLookupWebGLProgram(program), name); 716 | int index=WebGLUniformLocationArray->indexOf(obj); 717 | if(index>=0) { return index+1; } 718 | else { index=WebGLUniformLocationArray->push(obj); return index; } 719 | } 720 | 721 | int glGetVertexAttribOffset(unsigned int index, unsigned int pname) 722 | { 723 | return webGLES->getVertexAttribOffset(index,pname); 724 | } 725 | 726 | void glHint(unsigned int target, unsigned int mode) 727 | { 728 | return webGLES->hint(target,mode); 729 | } 730 | 731 | bool glIsBuffer(unsigned int buffer) 732 | { 733 | return webGLES->isBuffer(webGLESLookupWebGLBuffer(buffer)); 734 | } 735 | 736 | bool glIsEnabled(unsigned int cap) 737 | { 738 | return webGLES->isEnabled(cap); 739 | } 740 | 741 | bool glIsFramebuffer(unsigned int framebuffer) 742 | { 743 | return webGLES->isFramebuffer(webGLESLookupWebGLFramebuffer(framebuffer)); 744 | } 745 | 746 | bool glIsProgram(unsigned int program) 747 | { 748 | return webGLES->isProgram(webGLESLookupWebGLProgram(program)); 749 | } 750 | 751 | bool glIsRenderbuffer(unsigned int renderbuffer) 752 | { 753 | return webGLES->isRenderbuffer(webGLESLookupWebGLRenderbuffer(renderbuffer)); 754 | } 755 | 756 | bool glIsShader(unsigned int shader) 757 | { 758 | return webGLES->isShader(webGLESLookupWebGLShader(shader)); 759 | } 760 | 761 | bool glIsTexture(unsigned int texture) 762 | { 763 | return webGLES->isTexture(webGLESLookupWebGLTexture(texture)); 764 | } 765 | 766 | void glLineWidth(float width) 767 | { 768 | return webGLES->lineWidth(width); 769 | } 770 | 771 | void glLinkProgram(unsigned int program) 772 | { 773 | return webGLES->linkProgram(webGLESLookupWebGLProgram(program)); 774 | } 775 | 776 | void glPixelStorei(unsigned int pname, int param) 777 | { 778 | return webGLES->pixelStorei(pname,param); 779 | } 780 | 781 | void glPolygonOffset(float factor, float units) 782 | { 783 | return webGLES->polygonOffset(factor,units); 784 | } 785 | 786 | void glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data) 787 | { 788 | webGLES->readPixels(x, y, width, height, format, type, cheerp::MakeArrayBufferView(data)); 789 | } 790 | 791 | void glRenderbufferStorage(unsigned int target, unsigned int internalformat, int width, int height) 792 | { 793 | return webGLES->renderbufferStorage(target,internalformat,width,height); 794 | } 795 | 796 | void glSampleCoverage(float value, bool invert) 797 | { 798 | return webGLES->sampleCoverage(value,invert); 799 | } 800 | 801 | void glScissor(int x, int y, int width, int height) 802 | { 803 | return webGLES->scissor(x,y,width,height); 804 | } 805 | 806 | void glStencilFunc(unsigned int func, int ref, unsigned int mask) 807 | { 808 | return webGLES->stencilFunc(func,ref,mask); 809 | } 810 | 811 | void glStencilFuncSeparate(unsigned int face, unsigned int func, int ref, unsigned int mask) 812 | { 813 | return webGLES->stencilFuncSeparate(face,func,ref,mask); 814 | } 815 | 816 | void glStencilMask(unsigned int mask) 817 | { 818 | return webGLES->stencilMask(mask); 819 | } 820 | 821 | void glStencilMaskSeparate(unsigned int face, unsigned int mask) 822 | { 823 | return webGLES->stencilMaskSeparate(face,mask); 824 | } 825 | 826 | void glStencilOp(unsigned int fail, unsigned int zfail, unsigned int zpass) 827 | { 828 | return webGLES->stencilOp(fail,zfail,zpass); 829 | } 830 | 831 | void glStencilOpSeparate(unsigned int face, unsigned int fail, unsigned int zfail, unsigned int zpass) 832 | { 833 | return webGLES->stencilOpSeparate(face,fail,zfail,zpass); 834 | } 835 | 836 | void glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data) 837 | { 838 | client::ArrayBufferView* buf=nullptr; 839 | if(data) 840 | { 841 | uint32_t elementSize = 1; 842 | if(format == GL_RGBA) 843 | elementSize = 4; 844 | else if(format == GL_RGB) 845 | elementSize = 3; 846 | else if(format == GL_LUMINANCE_ALPHA) 847 | elementSize = 2; 848 | buf = type==GL_UNSIGNED_BYTE ? (client::ArrayBufferView*)cheerp::MakeTypedArray(data, width*height*elementSize) : 849 | (client::ArrayBufferView*)cheerp::MakeTypedArray(data, width*height*2); 850 | } 851 | webGLES->texImage2D(target, level, internalformat, width, height, border, format, type, buf); 852 | } 853 | 854 | void glTexParameterf(unsigned int target, unsigned int pname, float param) 855 | { 856 | return webGLES->texParameterf(target,pname,param); 857 | } 858 | 859 | void glTexParameteri(unsigned int target, unsigned int pname, int param) 860 | { 861 | return webGLES->texParameteri(target,pname,param); 862 | } 863 | 864 | void glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data) 865 | { 866 | client::ArrayBufferView* buf=nullptr; 867 | if(data) 868 | { 869 | uint32_t elementSize = 1; 870 | if(format == GL_RGBA) 871 | elementSize = 4; 872 | else if(format == GL_RGB) 873 | elementSize = 3; 874 | else if(format == GL_LUMINANCE_ALPHA) 875 | elementSize = 2; 876 | buf = type==GL_UNSIGNED_BYTE ? (client::ArrayBufferView*)cheerp::MakeTypedArray(data, width*height*elementSize) : 877 | (client::ArrayBufferView*)cheerp::MakeTypedArray(data, width*height*2); 878 | } 879 | webGLES->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, buf); 880 | } 881 | 882 | void glUniform1f(unsigned int location, float x) 883 | { 884 | return webGLES->uniform1f(webGLESLookupWebGLUniformLocation(location),x); 885 | } 886 | 887 | void glUniform1fv (GLint location, GLsizei count, const GLfloat* value) 888 | { 889 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 1*count*4); 890 | webGLES->uniform1fv(webGLESLookupWebGLUniformLocation(location), buf); 891 | } 892 | 893 | void glUniform1i(unsigned int location, int x) 894 | { 895 | return webGLES->uniform1i(webGLESLookupWebGLUniformLocation(location),x); 896 | } 897 | 898 | void glUniform1iv (GLint location, GLsizei count, const GLint* value) 899 | { 900 | client::Int32Array* buf=cheerp::MakeTypedArray(value, 1*count*4); 901 | webGLES->uniform1iv(webGLESLookupWebGLUniformLocation(location), buf); 902 | } 903 | 904 | void glUniform2f(unsigned int location, float x, float y) 905 | { 906 | return webGLES->uniform2f(webGLESLookupWebGLUniformLocation(location),x,y); 907 | } 908 | 909 | void glUniform2fv (GLint location, GLsizei count, const GLfloat* value) 910 | { 911 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 2*count*4); 912 | webGLES->uniform2fv(webGLESLookupWebGLUniformLocation(location), buf); 913 | } 914 | 915 | void glUniform2i(unsigned int location, int x, int y) 916 | { 917 | return webGLES->uniform2i(webGLESLookupWebGLUniformLocation(location),x,y); 918 | } 919 | 920 | void glUniform2iv (GLint location, GLsizei count, const GLint* value) 921 | { 922 | client::Int32Array* buf=cheerp::MakeTypedArray(value, 2*count*4); 923 | webGLES->uniform2iv(webGLESLookupWebGLUniformLocation(location), buf); 924 | } 925 | 926 | void glUniform3f(unsigned int location, float x, float y, float z) 927 | { 928 | return webGLES->uniform3f(webGLESLookupWebGLUniformLocation(location),x,y,z); 929 | } 930 | 931 | void glUniform3fv (GLint location, GLsizei count, const GLfloat* value) 932 | { 933 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 3*count*4); 934 | webGLES->uniform3fv(webGLESLookupWebGLUniformLocation(location), buf); 935 | } 936 | 937 | void glUniform3i(unsigned int location, int x, int y, int z) 938 | { 939 | return webGLES->uniform3i(webGLESLookupWebGLUniformLocation(location),x,y,z); 940 | } 941 | 942 | void glUniform3iv (GLint location, GLsizei count, const GLint* value) 943 | { 944 | client::Int32Array* buf=cheerp::MakeTypedArray(value, 3*count*4); 945 | webGLES->uniform3iv(webGLESLookupWebGLUniformLocation(location), buf); 946 | } 947 | 948 | void glUniform4f(unsigned int location, float x, float y, float z, float w) 949 | { 950 | return webGLES->uniform4f(webGLESLookupWebGLUniformLocation(location),x,y,z,w); 951 | } 952 | 953 | void glUniform4fv (GLint location, GLsizei count, const GLfloat* value) 954 | { 955 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 4*count*4); 956 | webGLES->uniform4fv(webGLESLookupWebGLUniformLocation(location), buf); 957 | } 958 | 959 | void glUniform4i(unsigned int location, int x, int y, int z, int w) 960 | { 961 | return webGLES->uniform4i(webGLESLookupWebGLUniformLocation(location),x,y,z,w); 962 | } 963 | 964 | void glUniform4iv (GLint location, GLsizei count, const GLint* value) 965 | { 966 | client::Int32Array* buf=cheerp::MakeTypedArray(value, 4*count*4); 967 | webGLES->uniform4iv(webGLESLookupWebGLUniformLocation(location), buf); 968 | } 969 | 970 | void glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) 971 | { 972 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 2*2*count*4); 973 | webGLES->uniformMatrix2fv(webGLESLookupWebGLUniformLocation(location), transpose, buf); 974 | } 975 | 976 | void glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) 977 | { 978 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 3*3*count*4); 979 | webGLES->uniformMatrix3fv(webGLESLookupWebGLUniformLocation(location), transpose, buf); 980 | } 981 | 982 | void glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) 983 | { 984 | client::Float32Array* buf=cheerp::MakeTypedArray(value, 4*4*count*4); 985 | webGLES->uniformMatrix4fv(webGLESLookupWebGLUniformLocation(location), transpose, buf); 986 | } 987 | 988 | void glUseProgram(unsigned int program) 989 | { 990 | return webGLES->useProgram(webGLESLookupWebGLProgram(program)); 991 | } 992 | 993 | void glValidateProgram(unsigned int program) 994 | { 995 | return webGLES->validateProgram(webGLESLookupWebGLProgram(program)); 996 | } 997 | 998 | void glVertexAttrib1f(unsigned int indx, float x) 999 | { 1000 | return webGLES->vertexAttrib1f(indx,x); 1001 | } 1002 | 1003 | void glVertexAttrib2f(unsigned int indx, float x, float y) 1004 | { 1005 | return webGLES->vertexAttrib2f(indx,x,y); 1006 | } 1007 | 1008 | void glVertexAttrib3f(unsigned int indx, float x, float y, float z) 1009 | { 1010 | return webGLES->vertexAttrib3f(indx,x,y,z); 1011 | } 1012 | 1013 | void glVertexAttrib4f(unsigned int indx, float x, float y, float z, float w) 1014 | { 1015 | return webGLES->vertexAttrib4f(indx,x,y,z,w); 1016 | } 1017 | 1018 | void glVertexAttribPointer(unsigned int indx, int size, unsigned int type, bool normalized, int stride, int offset) 1019 | { 1020 | return webGLES->vertexAttribPointer(indx,size,type,normalized,stride,offset); 1021 | } 1022 | 1023 | void glViewport(int x, int y, int width, int height) 1024 | { 1025 | return webGLES->viewport(x,y,width,height); 1026 | } 1027 | 1028 | void glGenVertexArraysOES (GLsizei n, GLuint* objs) 1029 | { 1030 | for(GLsizei i=0;icreateVertexArrayOES(); 1032 | int index=WebGLVertexArrayOESArray->indexOf(nullptr); 1033 | if(index>=0) { (*WebGLVertexArrayOESArray)[index] = obj; objs[i]=index+1; } 1034 | else { index=WebGLVertexArrayOESArray->push(obj); objs[i]=index; } 1035 | } 1036 | } 1037 | 1038 | void glDeleteVertexArraysOES (GLsizei n, GLuint* objs) 1039 | { 1040 | for(GLsizei i=0;i((*WebGLVertexArrayOESArray)[index]); 1043 | webGLESExtVAO->deleteVertexArrayOES(obj); 1044 | (*WebGLVertexArrayOESArray)[index]=NULL; 1045 | } 1046 | } 1047 | 1048 | bool glIsVertexArrayOES(unsigned int arrayObject) 1049 | { 1050 | return webGLESExtVAO->isVertexArrayOES(webGLESLookupWebGLVertexArrayOES(arrayObject)); 1051 | } 1052 | 1053 | void glBindVertexArrayOES(unsigned int arrayObject) 1054 | { 1055 | return webGLESExtVAO->bindVertexArrayOES(webGLESLookupWebGLVertexArrayOES(arrayObject)); 1056 | } 1057 | 1058 | -------------------------------------------------------------------------------- /webgles/webgles.h: -------------------------------------------------------------------------------- 1 | //===-- webgles.h - OpenGL ES implementation over WebGL --------------===// 2 | // 3 | // Cheerp: The C++ compiler for the Web 4 | // 5 | // This file is distributed under the Apache License v2.0 with LLVM Exceptions. 6 | // See LICENSE.TXT for details. 7 | // 8 | // Copyright 2018-2023 Leaning Technologies 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #ifndef _WEBGLES_H_ 13 | #define _WEBGLES_H_ 14 | 15 | #include 16 | #include 17 | #include "gl2.h" 18 | namespace [[cheerp::genericjs]] client 19 | { 20 | extern client::WebGLRenderingContext* webGLES; 21 | } 22 | using client::webGLES; 23 | [[cheerp::genericjs]] extern client::OESVertexArrayObject* webGLESExtVAO; 24 | enum WEBGLES_OPTIONS { 25 | WG_NO_ALPHA = 1, 26 | WG_NO_DEPTH = 2, 27 | WG_STENCIL = 4, 28 | WG_PREMULTIPLIED_ALPHA = 8, 29 | WG_NO_ANTIALIAS = 16, 30 | }; 31 | [[cheerp::genericjs]] void webGLESInit(const client::String& canvasName, int options = 0); 32 | [[cheerp::genericjs]] void webGLESInit(client::HTMLCanvasElement* canvas, int options = 0); 33 | [[cheerp::genericjs]] bool webGLESInitExtVAO(); 34 | [[cheerp::genericjs]] client::WebGLProgram* webGLESLookupWebGLProgram(int objId); 35 | [[cheerp::genericjs]] client::WebGLShader* webGLESLookupWebGLShader(int objId); 36 | [[cheerp::genericjs]] client::WebGLBuffer* webGLESLookupWebGLBuffer(int objId); 37 | [[cheerp::genericjs]] client::WebGLFramebuffer* webGLESLookupWebGLFramebuffer(int objId); 38 | [[cheerp::genericjs]] client::WebGLRenderbuffer* webGLESLookupWebGLRenderbuffer(int objId); 39 | [[cheerp::genericjs]] client::WebGLTexture* webGLESLookupWebGLTexture(int objId); 40 | [[cheerp::genericjs]] client::WebGLUniformLocation* webGLESLookupWebGLUniformLocation(int objId); 41 | [[cheerp::genericjs]] client::WebGLVertexArrayOES* webGLESLookupWebGLVertexArrayOES(int objId); 42 | [[cheerp::genericjs]] void webGLESLookupArrayInit(); 43 | #endif 44 | -------------------------------------------------------------------------------- /webgles/webglesutils.cpp: -------------------------------------------------------------------------------- 1 | //===-- webglesutils.cpp - OpenGL ES implementation over WebGL --------------===// 2 | // 3 | // Cheerp: The C++ compiler for the Web 4 | // 5 | // This file is distributed under the Apache License v2.0 with LLVM Exceptions. 6 | // See LICENSE.TXT for details. 7 | // 8 | // Copyright 2018-2023 Leaning Technologies 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #include "webgles.h" 13 | #include 14 | 15 | namespace [[cheerp::genericjs]] client 16 | { 17 | client::WebGLRenderingContext* webGLES = nullptr; 18 | } 19 | client::OESVertexArrayObject* webGLESExtVAO; 20 | 21 | extern client::Array* WebGLBufferArray; 22 | extern client::Array* WebGLRenderbufferArray; 23 | extern client::Array* WebGLShaderArray; 24 | extern client::Array* WebGLTextureArray; 25 | 26 | void webGLESInit(const client::String& canvasName, int options) 27 | { 28 | client::HTMLCanvasElement* canvas = static_cast(client::document.getElementById(canvasName)); 29 | return webGLESInit(canvas, options); 30 | } 31 | 32 | void webGLESInit(client::HTMLCanvasElement* canvas, int options) 33 | { 34 | webGLESLookupArrayInit(); 35 | client::WebGLContextAttributes* optObj = new client::WebGLContextAttributes(); 36 | if(options & WG_NO_ALPHA) 37 | optObj->set_alpha(0); 38 | if(options & WG_NO_DEPTH) 39 | optObj->set_depth(0); 40 | if(options & WG_NO_ANTIALIAS) 41 | optObj->set_antialias(0); 42 | if(options & WG_STENCIL) 43 | optObj->set_stencil(1); 44 | if (options & WG_PREMULTIPLIED_ALPHA) 45 | optObj->set_premultipliedAlpha(1); 46 | else 47 | optObj->set_premultipliedAlpha(0); 48 | webGLES = canvas->getContext("experimental-webgl", optObj)->cast(); 49 | if (webGLES == NULL) 50 | client::console.log("Sorry, we looked hard, but no sign of WebGL has been found :("); 51 | } 52 | 53 | bool webGLESInitExtVAO() 54 | { 55 | webGLESExtVAO = webGLES->getExtension("OES_vertex_array_object")->cast(); 56 | return webGLESExtVAO != nullptr; 57 | } 58 | 59 | void webGLESShaderSource(GLuint shader, const char* code) 60 | { 61 | client::WebGLShader* s = webGLESLookupWebGLShader(shader); 62 | webGLES->shaderSource(s, code); 63 | } 64 | 65 | #define shaderSourceImpl() { \ 66 | uint32_t fullLen = 0; \ 67 | for(GLsizei i=0;igetShaderInfoLog(webGLESLookupWebGLShader(shader)); 90 | params[0] = info == nullptr ? 0 : info->get_length() + 1; 91 | } 92 | else if(pname == GL_SHADER_SOURCE_LENGTH) 93 | { 94 | client::String* info = webGLES->getShaderSource(webGLESLookupWebGLShader(shader)); 95 | params[0] = info == nullptr ? 0 : info->get_length() + 1; 96 | } 97 | else 98 | params[0] = (double)*webGLES->getShaderParameter(webGLESLookupWebGLShader(shader), pname); 99 | } 100 | 101 | void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog) 102 | { 103 | client::String* info = webGLES->getShaderInfoLog(webGLESLookupWebGLShader(shader)); 104 | if(info == nullptr) 105 | { 106 | if(length) 107 | *length = 0; 108 | infoLog[0] = 0; 109 | return; 110 | } 111 | int strLen = info->get_length() + 1; 112 | if(strLen > maxLength) 113 | strLen = maxLength; 114 | for(int i=0;icharCodeAt(i); 116 | infoLog[strLen - 1] = 0; 117 | if(length) 118 | *length = strLen - 1; 119 | } 120 | 121 | void glGetProgramiv(GLuint program, GLenum pname, GLint *params) 122 | { 123 | if(pname == GL_ACTIVE_UNIFORM_MAX_LENGTH || pname == GL_ACTIVE_ATTRIBUTE_MAX_LENGTH) 124 | params[0] = 256; 125 | else if(pname == GL_INFO_LOG_LENGTH) 126 | { 127 | client::String* info = webGLES->getProgramInfoLog(webGLESLookupWebGLProgram(program)); 128 | params[0] = info == nullptr ? 0 : info->get_length() + 1; 129 | } 130 | else 131 | params[0] = (double)*webGLES->getProgramParameter(webGLESLookupWebGLProgram(program), pname); 132 | } 133 | 134 | void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei* length, GLchar* infoLog) 135 | { 136 | client::String* info = webGLES->getProgramInfoLog(webGLESLookupWebGLProgram(program)); 137 | if(info == nullptr) 138 | { 139 | if(length) 140 | *length = 0; 141 | infoLog[0] = 0; 142 | return; 143 | } 144 | int strLen = info->get_length() + 1; 145 | if(strLen > maxLength) 146 | strLen = maxLength; 147 | for(int i=0;icharCodeAt(i); 149 | infoLog[strLen - 1] = 0; 150 | if(length) 151 | *length = strLen - 1; 152 | } 153 | 154 | void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) 155 | { 156 | client::WebGLActiveInfo* info = webGLES->getActiveAttrib(webGLESLookupWebGLProgram(program), index); 157 | // TODO: bufSize check 158 | *length = info->get_name()->get_length(); 159 | for(GLsizei i=0;i<*length;i++) 160 | { 161 | name[i] = info->get_name()->charCodeAt(i); 162 | } 163 | name[*length] = 0; 164 | *size = info->get_size(); 165 | *type = info->get_type(); 166 | } 167 | 168 | void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) 169 | { 170 | client::WebGLActiveInfo* info = webGLES->getActiveUniform(webGLESLookupWebGLProgram(program), index); 171 | // TODO: bufSize check 172 | *length = info->get_name()->get_length(); 173 | for(GLsizei i=0;i<*length;i++) 174 | { 175 | name[i] = info->get_name()->charCodeAt(i); 176 | } 177 | name[*length] = 0; 178 | *size = info->get_size(); 179 | *type = info->get_type(); 180 | } 181 | 182 | void glGetAttachedShaders(GLuint program, GLsizei max, GLsizei* count, GLuint* shaders) 183 | { 184 | auto shadersArray = webGLES->getAttachedShaders(webGLESLookupWebGLProgram(program)); 185 | int realMax = max > shadersArray->get_length() ? shadersArray->get_length() : max; 186 | for(int i=0;iindexOf((*shadersArray)[i]); 189 | shaders[i] = index+1; 190 | } 191 | if(count) 192 | *count = realMax; 193 | } 194 | 195 | void glGetBufferParameteriv(GLenum target, GLenum value, GLint* data) 196 | { 197 | data[0] = (int)*webGLES->getBufferParameter(target, value); 198 | } 199 | 200 | void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* data) 201 | { 202 | client::Object* ret = webGLES->getFramebufferAttachmentParameter(target, attachment, pname); 203 | if(pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) 204 | { 205 | int index = WebGLTextureArray->indexOf(ret); 206 | if(index < 0) 207 | index = WebGLRenderbufferArray->indexOf(ret); 208 | data[0] = index+1; 209 | } 210 | else 211 | data[0] = (int)*ret; 212 | } 213 | 214 | void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* data) 215 | { 216 | data[0] = (int)*webGLES->getRenderbufferParameter(target, pname); 217 | } 218 | 219 | void glGetShaderPrecisionFormat(GLenum sType, GLenum pType, GLint* range, GLint* prec) 220 | { 221 | client::WebGLShaderPrecisionFormat* format = webGLES->getShaderPrecisionFormat(sType, pType); 222 | range[0] = format->get_rangeMin(); 223 | range[1] = format->get_rangeMax(); 224 | prec[0] = format->get_precision(); 225 | } 226 | 227 | void glGetTexParameteriv(GLenum target, GLenum pname, GLint* data) 228 | { 229 | data[0] = (int)*webGLES->getTexParameter(target, pname); 230 | } 231 | 232 | void glGetUniformfv(GLuint program, GLint location, GLfloat* data) 233 | { 234 | client::Object* ret = webGLES->getUniform(webGLESLookupWebGLProgram(program), webGLESLookupWebGLUniformLocation(location)); 235 | // We assume the user knows that this uniform is either a float or an array or float 236 | if(ret->hasOwnProperty("0")) 237 | { 238 | // Array or typed array 239 | client::Array* a = (client::Array*)ret; 240 | for(int i=0;iget_length();i++) 241 | data[i] = (double)*((*a)[i]); 242 | } 243 | else 244 | { 245 | // Value 246 | data[0] = (double)*ret; 247 | } 248 | } 249 | 250 | void glGetUniformiv(GLuint program, GLint location, GLint* data) 251 | { 252 | client::Object* ret = webGLES->getUniform(webGLESLookupWebGLProgram(program), webGLESLookupWebGLUniformLocation(location)); 253 | // We assume the user knows that this uniform is either a float or an array or float 254 | if(ret->hasOwnProperty("0")) 255 | { 256 | // Array or typed array 257 | client::Array* a = (client::Array*)ret; 258 | for(int i=0;iget_length();i++) 259 | data[i] = (int)*((*a)[i]); 260 | } 261 | else 262 | { 263 | // Value 264 | data[0] = (int)*ret; 265 | } 266 | } 267 | 268 | void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* data) 269 | { 270 | // We only expect to be here for GL_CURRENT_VERTEX_ATTRIB 271 | client::Float32Array* ret = webGLES->getVertexAttrib(index, pname)->cast(); 272 | for(int i=0;i<4;i++) 273 | data[i] = (*ret)[i]; 274 | } 275 | 276 | void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* data) 277 | { 278 | client::Object* ret = webGLES->getVertexAttrib(index, pname); 279 | if(pname == GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING) 280 | data[0] = WebGLBufferArray->indexOf(ret) + 1; 281 | else 282 | data[0] = (int)*ret; 283 | } 284 | --------------------------------------------------------------------------------