/** * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Polyfill Number.isNaN if (Number.isNaN === undefined) { Number.isNaN = Number.isNaN || function(value) { return value !== value; } } var __jerryRefs = { // Jerryscript values (jerry_value_t) are integers which contain information // about some javascript value that has been internally created. // Create _objMap which will allow us to store and retrieve javascript values // from a jerry_value_t, and perform refcounts on values that we still need an // internal reference to, and avoid them from being garbage collected. _objMap : {}, _nextObjectRef : 1, _findValue : function(value) { if (Number.isNaN(value)) { // Special case to find NaN for (var jerry_val in this._objMap) { if (Number.isNaN(this._objMap[jerry_val].value)) { return jerry_val; } } } else { for (var jerry_val in this._objMap) { if (this._objMap[jerry_val].value === value) { return jerry_val; } } } return 0; }, _getEntry : function(jerry_value) { var entry = this._objMap[jerry_value]; if (!entry) { throw new Error('Entry at ' + jerry_value + ' does not exist'); } return entry; }, reset: function() { this._objMap = {}; this._nextObjectRef = 1; }, // Given a jerry value, return the stored javascript value. get : function(jerry_value) { return this._getEntry(jerry_value).value; }, // Given a javascript value, return a jerry value that refers to it. // If the value already exists in the map, increment its refcount and return // the jerry value. // Otherwise, create a new entry and return the jerry value. ref : function(value) { var jerry_value = this._findValue(value); if (jerry_value) { this._getEntry(jerry_value).refCount++; return jerry_value; } jerry_value = this._nextObjectRef++; this._objMap[jerry_value] = { refCount : 1, value : value, error : false, }; // console.log('created entry ' + jerry_value + ' for ' + value + ' at ' + stackTrace()); return jerry_value; }, getRefCount : function(jerry_value) { return this._getEntry(jerry_value).refCount; }, // Increase the reference count of the given jerry value acquire : function(jerry_value) { this._getEntry(jerry_value).refCount++; return jerry_value; }, // Decrease the reference count of the given jerry value and delete it if // there are no more internal references. release : function(ref) { var entry = this._getEntry(ref); entry.refCount--; if (entry.refCount <= 0) { if (entry.freeCallbackPtr) { Module.ccall( 'emscripten_call_jerry_object_free_callback', null, ['number', 'number'], [entry.freeCallbackPtr, entry.nativeHandlePtr]); } // console.log('deleting ' + ref + ' at ' + stackTrace()); delete this._objMap[ref]; } }, setError : function(ref, state) { this._getEntry(ref).error = state; }, getError : function(ref) { return this._getEntry(ref).error; }, setNativeHandle : function(jerryValue, nativeHandlePtr, freeCallbackPtr) { var entry = this._getEntry(jerryValue); entry.nativeHandlePtr = nativeHandlePtr; entry.freeCallbackPtr = freeCallbackPtr; }, getNativeHandle : function(jerryValue) { return this._getEntry(jerryValue).nativeHandlePtr; } }; function __jerry_create_external_function(function_ptr) { var f = function() { var nativeHandlerArgs = [ function_ptr, /* the function pointer for us to call */ __jerryRefs.ref(f), /* ref to the actual js function */ __jerryRefs.ref(this) /* our this object */ ]; var numArgs = arguments.length; var jsRefs = []; for (var i = 0; i < numArgs; i++) { jsRefs.push(__jerryRefs.ref(arguments[i])); } // Arg 4 is a uint32 array of jerry_value_t arguments var jsArgs = Module._malloc(numArgs * 4); for (var i = 0; i < numArgs; i++) { Module.setValue(jsArgs + i*4, jsRefs[i], 'i32'); } nativeHandlerArgs.push(jsArgs); nativeHandlerArgs.push(numArgs); // this is just the classy Emscripten calling. function_ptr is a C-pointer here // and we know the signature of the C function as it needs to follow var result_ref = Module.ccall('emscripten_call_jerry_function', 'number', ['number', 'number', 'number', 'number', 'number'], nativeHandlerArgs); // Free and release all js args Module._free(jsArgs); while (jsRefs.length > 0) { __jerryRefs.release(jsRefs.pop()); } // decrease refcount of native handler arguments __jerryRefs.release(nativeHandlerArgs[1]); // jsFunctionRef __jerryRefs.release(nativeHandlerArgs[2]); // our this object // delete native handler arguments nativeHandlerArgs.length = 0; var result_val = __jerryRefs.get(result_ref); var has_error = __jerryRefs.getError(result_ref); __jerryRefs.release(result_ref); if (has_error) { throw result_val; } return result_val; }; return __jerryRefs.ref(f); }