mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-06 12:03:39 +00:00
1251 lines
40 KiB
C++
1251 lines
40 KiB
C++
/*
|
|
* Tencent is pleased to support the open source community by making ScriptX available.
|
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "WasmHelper.h"
|
|
#include "../../src/Native.hpp"
|
|
#include "../../src/Scope.h"
|
|
#include "WasmEngine.hpp"
|
|
#include "WasmScope.hpp"
|
|
|
|
namespace script::wasm_backend {
|
|
|
|
static struct {
|
|
// if there is any js exception thrown, and we should rethrow to cpp
|
|
// the js exception is on stack top
|
|
bool hasPendingJsException_ = false;
|
|
|
|
// if there is any cpp exception thrown, and we should rethrow to js
|
|
// the cpp exception is converted to js Error and pushed onto stack top
|
|
bool hasPendingCppException_ = false;
|
|
} kExceptionStore;
|
|
|
|
WasmEngine ¤tEngine() { return EngineScope::currentEngineCheckedAs<WasmEngine>(); }
|
|
|
|
// ========== stack ===========
|
|
// emscripten's val.h is not fully developed, we use our own implementation based on lua stack
|
|
// concepts.
|
|
|
|
void invokeFunctionHelper(Stack::RawFunctionType func, void *ptr0, void *ptr1, int stackBase,
|
|
WasmEngine *engine) {
|
|
try {
|
|
// stack[0] == this
|
|
// stack[...] -> args
|
|
auto top = Stack::top();
|
|
|
|
auto args = wasm_backend::WasmEngine::make<Arguments>(
|
|
ArgumentsData{stackBase, stackBase + 1, top - stackBase, engine});
|
|
|
|
int retIndex = Stack::pushNull();
|
|
|
|
// this function may be directly called from JS...
|
|
// in which case we don't have a EngineScope
|
|
|
|
// plus: EngineScope can't escape any local object... we have to hack it
|
|
{
|
|
EngineScope scope(engine);
|
|
auto ret = (*func)(args, ptr0, ptr1);
|
|
Stack::set(retIndex, ret);
|
|
}
|
|
} catch (const Exception &e) {
|
|
rethrowException(e);
|
|
}
|
|
}
|
|
|
|
void deleteScriptClassHelper(ScriptClass *scriptClass) {
|
|
wasm_backend::WasmEngine::doDeleteScriptClass(scriptClass);
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
// exported helper function called by js
|
|
// return has return value or not
|
|
// this function is so simple, we don't need to use bind.h
|
|
|
|
// the function name is using C mangling convention by adding '_' prefix to function name.
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ScriptX_Bridge_invokeFunction(intptr_t func, intptr_t ptr0, intptr_t ptr1, int stackBase,
|
|
intptr_t engine) {
|
|
invokeFunctionHelper(reinterpret_cast<Stack::RawFunctionType>(func),
|
|
reinterpret_cast<void *>(ptr0), reinterpret_cast<void *>(ptr1), stackBase,
|
|
reinterpret_cast<WasmEngine *>(engine));
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ScriptX_Stack_finalizeFunction(intptr_t func) {
|
|
delete reinterpret_cast<script::FunctionCallback *>(func);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ScriptX_Native_deleteScriptClass(intptr_t scriptClass) {
|
|
deleteScriptClassHelper(reinterpret_cast<ScriptClass *>(scriptClass));
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
intptr_t ScriptX_NativeBuffer_newSharedPtr(intptr_t ptr) {
|
|
return reinterpret_cast<intptr_t>(
|
|
new std::shared_ptr<void>(reinterpret_cast<void *>(ptr), [](void *ptr) { std::free(ptr); }));
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ScriptX_NativeBuffer_deleteSharedPtr(intptr_t sharedPtr) {
|
|
delete reinterpret_cast<std::shared_ptr<void> *>(sharedPtr);
|
|
}
|
|
}
|
|
|
|
void rethrowException(const Exception &e) {
|
|
kExceptionStore.hasPendingCppException_ = true;
|
|
Stack::pushValue(e.exception());
|
|
}
|
|
|
|
void checkException() {
|
|
if (kExceptionStore.hasPendingJsException_) {
|
|
kExceptionStore.hasPendingJsException_ = false;
|
|
throw Exception(Stack::topValue());
|
|
}
|
|
}
|
|
|
|
// wrap a exception handling around EM_JS
|
|
#define CHECKED_EM_JS(ret, name, params, ...) \
|
|
EM_JS(ret, name, params, { \
|
|
try { \
|
|
__VA_ARGS__ \
|
|
} catch (e) { \
|
|
return Module._ScriptX_rethrowException(e); \
|
|
} \
|
|
})
|
|
|
|
#define CHECKED_JS_CALL(call) \
|
|
auto &&ret = call; \
|
|
::script::wasm_backend::checkException(); \
|
|
return std::move(ret)
|
|
|
|
#define CHECKED_VOID_JS_CALL(call) \
|
|
call; \
|
|
::script::wasm_backend::checkException();
|
|
|
|
// <editor-fold desc="Emscripten JS Library">
|
|
// clang-format off
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_setInternalStatePointer,
|
|
(intptr_t hasPendingJsException, intptr_t hasPendingCppException), {
|
|
Object.defineProperty(Module, 'SCRIPTX_HAS_PENDING_JS_EXCEPTION', {
|
|
get() { return Module.HEAPU8[hasPendingJsException] !== 0;},
|
|
set(has) { Module.HEAPU8[hasPendingJsException] = (has ? 1 : 0); },
|
|
enumerable: false,
|
|
configurable: false
|
|
});
|
|
|
|
Object.defineProperty(Module, 'SCRIPTX_HAS_PENDING_CPP_EXCEPTION', {
|
|
get() { return Module.HEAPU8[hasPendingCppException] !== 0;},
|
|
set(has) { Module.HEAPU8[hasPendingCppException] = (has ? 1 : 0); },
|
|
enumerable: false,
|
|
configurable: false
|
|
});
|
|
});
|
|
|
|
EM_JS(void, _ScriptX_initJavaScriptLibrary, (), {
|
|
// declare how js stack class should be
|
|
|
|
class ScriptStack {
|
|
constructor() {
|
|
this.values = [];
|
|
}
|
|
|
|
push(val) {
|
|
return this.values.push(val) - 1;
|
|
}
|
|
|
|
setTop(newTop) { this.values.length = newTop + 1; }
|
|
|
|
getTop() { return this.values.length - 1; }
|
|
|
|
getTopValue() { return this.values[this.values.length - 1]; }
|
|
|
|
pop(count) {
|
|
this.setTop(this.getTop() - count);
|
|
return this.getTop();
|
|
}
|
|
|
|
set(atIndex, valueIndex) {
|
|
this.values[atIndex] = this.values[valueIndex];
|
|
}
|
|
};
|
|
Module.SCRIPTX_STACK = new ScriptStack();
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
|
|
Module._ScriptX_getGlobalObject = function () {
|
|
const ret = globalThis || self || window || global;
|
|
|
|
if (typeof ret !== 'object') {
|
|
ret = (new Function("return this;"))();
|
|
}
|
|
|
|
if (typeof ret !== 'object') {
|
|
throw TypeError("can't find global");
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Module._ScriptX_rethrowException = function(e) {
|
|
Module.SCRIPTX_HAS_PENDING_JS_EXCEPTION = true;
|
|
Module.SCRIPTX_STACK.push(e);
|
|
return -1;
|
|
};
|
|
|
|
Module._ScriptX_checkException = function() {
|
|
if (Module.SCRIPTX_HAS_PENDING_CPP_EXCEPTION) {
|
|
Module.SCRIPTX_HAS_PENDING_CPP_EXCEPTION = false;
|
|
throw Module.SCRIPTX_STACK.getTopValue();
|
|
}
|
|
};
|
|
|
|
Module._ScriptX_TypedArray_memcpy = function(from, to) {
|
|
function makeInt8Array(what) {
|
|
if (what instanceof Int8Array) {
|
|
return what;
|
|
} else if (what instanceof ArrayBuffer) {
|
|
return new Int8Array(what);
|
|
} else {
|
|
// TypedArray || DataView || ScriptX.SharedByteBuffer
|
|
return new Int8Array(what.buffer, what.byteOffset, what.byteLength);
|
|
}
|
|
}
|
|
makeInt8Array(to).set(makeInt8Array(from));
|
|
};
|
|
Module._ScriptX_SharedByteBuffer = function(byteOffset, byteLength, sharedPtr) {
|
|
// ctor1: ScriptX.SharedByteBuffer(bufferSize)
|
|
// ctor2: ScriptX.SharedByteBuffer(...) internally used
|
|
|
|
if (!(this instanceof Module._ScriptX_SharedByteBuffer)) {
|
|
throw Error("must call as constructor, with new operator!");
|
|
}
|
|
|
|
if (arguments.length == 1 && typeof arguments[0] === 'number') {
|
|
// ctor 1
|
|
byteLength = arguments[0];
|
|
byteOffset = Module._malloc(byteLength);
|
|
sharedPtr = Module._ScriptX_NativeBuffer_newSharedPtr(byteOffset);
|
|
}
|
|
|
|
// ctor 2
|
|
if (!(typeof byteOffset === 'number' &&
|
|
typeof byteLength === 'number' &&
|
|
typeof sharedPtr === 'number')) {
|
|
throw TypeError("use factory method to create SharedByteBuffer");
|
|
}
|
|
|
|
const sym = Module._ScriptX_Native_Symbol_SharedByteBuffer_ptr;
|
|
Object.defineProperty(this, 'byteOffset', {
|
|
value: byteOffset,
|
|
enumerable: true
|
|
});
|
|
Object.defineProperty(this, 'byteLength', {
|
|
value: byteLength,
|
|
enumerable: true
|
|
});
|
|
Object.defineProperty(this, sym, {
|
|
value: sharedPtr
|
|
});
|
|
Object.defineProperty(this, 'buffer', {
|
|
get: function() { return Module.HEAP8.buffer; },
|
|
enumerable: true,
|
|
configurable: false
|
|
});
|
|
|
|
this.destroy = function() {
|
|
const sharedPtrValue = this[sym];
|
|
|
|
// sentence this object DEATH
|
|
this[sym] = 0;
|
|
this.death = true;
|
|
|
|
Module._ScriptX_NativeBuffer_deleteSharedPtr(sharedPtrValue);
|
|
};
|
|
};
|
|
|
|
Module._ScriptX_Native_destroyInstanceInternal = function(instance) {
|
|
if (typeof instance !== 'object') return;
|
|
const ptr = instance[Module._ScriptX_Native_Symbol_instance];
|
|
if (ptr) {
|
|
_ScriptX_Native_deleteScriptClass(ptr);
|
|
|
|
// sentence this object DEATH
|
|
instance[Module._ScriptX_Native_Symbol_InternalStore] = undefined;
|
|
instance[Module._ScriptX_Native_Symbol_classDefine] = undefined;
|
|
instance[Module._ScriptX_Native_Symbol_instance] = undefined;
|
|
Object.setPrototypeOf(instance, {});
|
|
instance.death = true;
|
|
}
|
|
};
|
|
|
|
// export to global namespace
|
|
const exports = {};
|
|
Module._ScriptX_getGlobalObject().ScriptX = exports;
|
|
exports.SharedByteBuffer = Module._ScriptX_SharedByteBuffer;
|
|
exports.destroyScriptClass = Module._ScriptX_Native_destroyInstanceInternal;
|
|
exports.destroySharedByteBuffer = function (sbb) {
|
|
if (sbb instanceof Module._ScriptX_SharedByteBuffer) sbb.destroy();
|
|
};
|
|
|
|
Module._ScriptX_Global_ref = {};
|
|
Module._ScriptX_Global_ref_key = 0;
|
|
Module._ScriptX_Weak_ref = {};
|
|
Module._ScriptX_Weak_ref_key = 0;
|
|
Module._ScriptX_Global_getMap = function(isWeak) {
|
|
if (isWeak) {
|
|
return Module._ScriptX_Weak_ref;
|
|
} else {
|
|
return Module._ScriptX_Global_ref;
|
|
}
|
|
};
|
|
Module._ScriptX_Global_newKey = function(isWeak) {
|
|
const INT_MAX = 2147483647;
|
|
let ret = 0;
|
|
if (isWeak) {
|
|
ret = Module._ScriptX_Weak_ref_key++;
|
|
if (ret < 0 || ret >= INT_MAX) {
|
|
Module._ScriptX_Weak_ref_key = 0;
|
|
ret = 0;
|
|
}
|
|
} else {
|
|
ret = Module._ScriptX_Global_ref_key++;
|
|
if (ret < 0 || ret >= INT_MAX) {
|
|
Module._ScriptX_Global_ref_key = 0;
|
|
ret = 0;
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Module._ScriptX_Native_Symbol_instance =
|
|
Symbol('ScriptX_Native_Symbol_instance');
|
|
Module._ScriptX_Native_Symbol_classDefine =
|
|
Symbol('ScriptX_Native_Symbol_classDefine');
|
|
Module._ScriptX_Native_Symbol_CppNewMark =
|
|
Symbol('ScriptX_Native_Symbol_CppNewMark');
|
|
Module._ScriptX_Native_Symbol_InternalStore =
|
|
Symbol('ScriptX_Native_Symbol_InternalStore');
|
|
Module._ScriptX_Native_Symbol_SharedByteBuffer_ptr =
|
|
Symbol('ScriptX_Native_Symbol_SharedByteBuffer_ptr');
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_top, (),
|
|
{ return Module.SCRIPTX_STACK.getTop(); });
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_setTop, (int top),
|
|
{ return Module.SCRIPTX_STACK.setTop(top); });
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_set, (int atIndex, int valueIndex),
|
|
{ Module.SCRIPTX_STACK.set(atIndex, valueIndex); });
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newObject, (),
|
|
{ return Module.SCRIPTX_STACK.push({}); });
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newObjectArgs, (int type, int stackBase), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
|
|
const Type = stack.values[type];
|
|
const args = [];
|
|
for (let i = stackBase; i <= stack.getTop(); i++) {
|
|
args.push(stack.values[i]);
|
|
}
|
|
|
|
// clear args
|
|
stack.setTop(stackBase - 1);
|
|
|
|
// use spread operator
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
|
const ret = new Type(...args);
|
|
return stack.push(ret);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newArray, (int size), {
|
|
const arr = [];
|
|
arr.length = size;
|
|
return Module.SCRIPTX_STACK.push(arr);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newStringLen, (const char* string, int len), {
|
|
return Module.SCRIPTX_STACK.push(UTF8ToString(
|
|
string,
|
|
len,
|
|
len));
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newNumInt, (int num),
|
|
{ return Module.SCRIPTX_STACK.push(num); });
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newNumDouble, (double num),
|
|
{ return Module.SCRIPTX_STACK.push(num); });
|
|
|
|
// Local<Value> (*func)(const Arguments &, void *, void *)
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newRawFunction,
|
|
(intptr_t func, intptr_t ptr0, intptr_t ptr1,
|
|
WasmEngine* engine, bool checkForConstructorCall), {
|
|
// use IIFE to erase Function.name for wrapper
|
|
let wrapper = (function() { return function() {
|
|
// check is constructor call
|
|
if (checkForConstructorCall && !(this instanceof wrapper)) {
|
|
throw Error("constructor can't be call as normal function");
|
|
}
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const stackBase = stack.getTop();
|
|
try {
|
|
const newBase = stack.push(this);
|
|
for (let arg of arguments) {
|
|
stack.push(arg);
|
|
}
|
|
|
|
// stack[0] == this
|
|
// stack[...] -> args
|
|
Module._ScriptX_Bridge_invokeFunction(func, ptr0, ptr1, newBase, engine);
|
|
Module._ScriptX_checkException();
|
|
|
|
return stack.getTopValue();
|
|
} finally {
|
|
// restore stack
|
|
stack.setTop(stackBase);
|
|
}
|
|
}})();
|
|
|
|
return Module.SCRIPTX_STACK.push(wrapper);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_callFunction, (int stackBase), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const func = stack.values[stackBase];
|
|
const thiz = stack.values[stackBase + 1];
|
|
const args = [];
|
|
for (let i = stackBase + 2; i <= stack.getTop(); i++) {
|
|
args.push(stack.values[i]);
|
|
}
|
|
// pop args
|
|
stack.setTop(stackBase - 1);
|
|
|
|
const ret = func.apply(thiz, args);
|
|
return stack.push(ret);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newBoolean, (int boolean),
|
|
{ return Module.SCRIPTX_STACK.push(boolean !== 0); });
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_newError, (int message), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const err = new Error(stack.values[message] || "unknown error");
|
|
return stack.push(err);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_pushArrayBuffer, (size_t length), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.push(new ArrayBuffer(length));
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_pushValue, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.push(stack.values[index]);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_pop, (int count), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.pop(count);
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isObject, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[index] instanceof Object;
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isArray, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return Array.isArray(stack.values[index]);
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isString, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return typeof stack.values[index] == "string";
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isNum, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return typeof stack.values[index] == "number";
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isBoolean, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return typeof stack.values[index] == "boolean";
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isFunction, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[index] instanceof Function;
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isByteBuffer, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const val = stack.values[index];
|
|
return val instanceof ArrayBuffer ||
|
|
ArrayBuffer.isView(val) ||
|
|
val instanceof Module._ScriptX_SharedByteBuffer;
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isNullOrUndefined, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const value = stack.values[index];
|
|
return value === null || value === undefined;
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_isUnsupported, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const val = stack.values[index];
|
|
return !(
|
|
val === null ||
|
|
val === undefined ||
|
|
val instanceof Object ||
|
|
Array.isArray(val) ||
|
|
typeof val === 'string' ||
|
|
typeof val === 'number' ||
|
|
typeof val === 'boolean' ||
|
|
val instanceof Function ||
|
|
val instanceof ArrayBuffer ||
|
|
ArrayBuffer.isView(val));
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_toString, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const val = stack.values[index];
|
|
let str;
|
|
if (val === undefined) {
|
|
str = typeof val;
|
|
} else if (val === null) {
|
|
str = 'null';
|
|
} else if (val.toString) {
|
|
str = val.toString();
|
|
}
|
|
|
|
return stack.push(str);
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_equals, (int lhs, int rhs), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[lhs] === stack.values[rhs];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_objectGet, (int obj, int key), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
const ret = obj[stack.values[key]];
|
|
return stack.push(ret);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_objectSet, (int obj, int key, int value), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
key = stack.values[key];
|
|
value = stack.values[value];
|
|
obj[key] = value;
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_objectRemove, (int obj, int key), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
key = stack.values[key];
|
|
delete obj[key];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_objectHas, (int obj, int key), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
key = stack.values[key];
|
|
return typeof obj == "object" && obj.hasOwnProperty(key);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_objectInstanceOf, (int obj, int who), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
who = stack.values[who];
|
|
return obj instanceof who;
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_objectGetKeys, (int obj), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
const keys = Object.getOwnPropertyNames(obj);
|
|
return stack.push(keys);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_arrayGet, (int array, int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
array = stack.values[array];
|
|
const ret = array[index];
|
|
return stack.push(ret);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_arraySet, (int array, int index, int valueIndex), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
array = stack.values[array];
|
|
array[index] = stack.values[valueIndex];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_arrayLength, (int array), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[array].length;
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_arrayClear, (int array), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
stack.values[array].length = 0;
|
|
});
|
|
|
|
CHECKED_EM_JS(int32_t, _ScriptX_Stack_toNumInt32, (int num), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
num = stack.values[num];
|
|
return num
|
|
});
|
|
|
|
CHECKED_EM_JS(int64_t, _ScriptX_Stack_toNumInt64, (int num), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
num = stack.values[num];
|
|
return num
|
|
});
|
|
|
|
CHECKED_EM_JS(float, _ScriptX_Stack_toNumFloat, (int num), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
num = stack.values[num];
|
|
return num
|
|
});
|
|
|
|
CHECKED_EM_JS(double, _ScriptX_Stack_toNumDouble, (int num), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
num = stack.values[num];
|
|
return num
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_Stack_toBoolean, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[index];
|
|
});
|
|
|
|
CHECKED_EM_JS(intptr_t, _ScriptX_Stack_toCString, (int index), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
let str = stack.values[index];
|
|
|
|
if (str === undefined) {
|
|
str = typeof val;
|
|
} else if (str === null) {
|
|
str = 'null';
|
|
} else if (str.toString) {
|
|
str = str.toString();
|
|
}
|
|
|
|
const buffer = allocateUTF8(str);
|
|
return buffer;
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_evaluateJavaScriptCode, (int code, int source), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
code = stack.values[code];
|
|
source = stack.values[source];
|
|
|
|
const useFunction = false;
|
|
if (useFunction) {
|
|
let func = new Function("'use strict';" + code);
|
|
if (typeof source === 'string') {
|
|
const real = func;
|
|
const wrapper = {
|
|
[source]: function() { return real(); }
|
|
};
|
|
// the wrapper function has a name [source]
|
|
func = wrapper[source];
|
|
}
|
|
const ret = func();
|
|
return stack.push(ret);
|
|
} else {
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
|
|
// If you use the eval function indirectly, by invoking it via a reference other than eval,
|
|
// as of ECMAScript 5 it works in the global scope rather than the local scope.
|
|
const geval = eval;
|
|
const ret = geval(code);
|
|
return stack.push(ret);
|
|
}
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_getGlobal, (), {
|
|
const globalValue = Module._ScriptX_getGlobalObject();
|
|
return Module.SCRIPTX_STACK.push(globalValue);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Stack_globalGet, (int key), {
|
|
const globalValue = Module._ScriptX_getGlobalObject();
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.push(globalValue[stack.values[key]]);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Stack_globalSet, (int key, int value), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const globalValue = Module._ScriptX_getGlobalObject();
|
|
globalValue[stack.values[key]] = stack.values[value];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Global_new, (int localValueIndex, bool isWeak), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const key = Module._ScriptX_Global_newKey(isWeak);
|
|
const map = Module._ScriptX_Global_getMap(isWeak);
|
|
map[key] = stack.values[localValueIndex];
|
|
return key;
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Global_dup, (int key, bool isFromWeak, bool isToWeak), {
|
|
const fromMap = Module._ScriptX_Global_getMap(isFromWeak);
|
|
const toMap = Module._ScriptX_Global_getMap(isToWeak);
|
|
const newKey = Module._ScriptX_Global_newKey(isToWeak);
|
|
|
|
toMap[newKey] = fromMap[key];
|
|
return newKey;
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Global_delete, (int key, bool isWeak), {
|
|
const map = Module._ScriptX_Global_getMap(isWeak);
|
|
delete map[key];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Global_get, (int key, bool isWeak), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const map = Module._ScriptX_Global_getMap(isWeak);
|
|
const value = map[key];
|
|
return stack.push(value);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Native_defineProperty,
|
|
(int obj, int name, int getter, int setter), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
name = stack.values[name];
|
|
getter = stack.values[getter] || function() {};
|
|
setter = stack.values[setter] || function() {};
|
|
|
|
Object.defineProperty(obj, name, {
|
|
get: getter,
|
|
set: setter,
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Native_getNamespaceForRegister, (int nameSpace), {
|
|
try {
|
|
const globalValue = Module._ScriptX_getGlobalObject();
|
|
const stack = Module.SCRIPTX_STACK;
|
|
nameSpace = stack.values[nameSpace];
|
|
|
|
let obj = globalValue;
|
|
if (nameSpace && nameSpace !== "") {
|
|
let segs = nameSpace.split('.');
|
|
for (let s of segs) {
|
|
let n = obj[s];
|
|
if (!(n instanceof Object)) {
|
|
if (n === undefined || n === null) {
|
|
n = {};
|
|
obj[s] = n;
|
|
} else {
|
|
throw Error("already has a non object value");
|
|
}
|
|
}
|
|
obj = n;
|
|
}
|
|
}
|
|
return stack.push(obj);
|
|
} catch (e) {
|
|
console.log("failed to getNamespaceForRegister [" + nameSpace + "] " + e);
|
|
return -1;
|
|
}
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Native_setInternalState,
|
|
(int obj, intptr_t classDefine, intptr_t instance), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
obj[Module._ScriptX_Native_Symbol_instance] = instance;
|
|
obj[Module._ScriptX_Native_Symbol_classDefine] = classDefine;
|
|
});
|
|
|
|
CHECKED_EM_JS(intptr_t, _ScriptX_Native_getInternalStateInstance, (int obj), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
if (!(obj instanceof Object)) {
|
|
return 0;
|
|
}
|
|
return obj[Module._ScriptX_Native_Symbol_instance];
|
|
});
|
|
|
|
CHECKED_EM_JS(intptr_t, _ScriptX_Native_getInternalStateClassDefine, (int obj), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
obj = stack.values[obj];
|
|
if (!(obj instanceof Object)) {
|
|
return 0;
|
|
}
|
|
return obj[Module._ScriptX_Native_Symbol_classDefine];
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Native_pushCppNewMark, (), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.push(Module._ScriptX_Native_Symbol_CppNewMark);
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Native_isCppNewMark, (int mark), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
return stack.values[mark] === Module._ScriptX_Native_Symbol_CppNewMark;
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_Native_getInternalStore, (int weakThis), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const sym = Module._ScriptX_Native_Symbol_InternalStore;
|
|
const weakMap = Module._ScriptX_Global_getMap(true);
|
|
const thiz = weakMap[weakThis];
|
|
if (!thiz) {
|
|
return -1;
|
|
}
|
|
if (typeof thiz[sym] !== 'object') {
|
|
thiz[sym] = [];
|
|
}
|
|
return stack.push(thiz[sym]);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_Native_destroyInstance, (int scriptClassInstance), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
scriptClassInstance = stack.values[scriptClassInstance];
|
|
|
|
Module._ScriptX_Native_destroyInstanceInternal(scriptClassInstance);
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_ByteBuffer_fillBackingStore,
|
|
(int val, intptr_t sharedPtr, intptr_t buffer), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
val = stack.values[val];
|
|
|
|
if (val instanceof Module._ScriptX_SharedByteBuffer) {
|
|
const sym = Module._ScriptX_Native_Symbol_SharedByteBuffer_ptr;
|
|
setValue(sharedPtr, val[sym], "void*");
|
|
} else {
|
|
// val should be ArrayBuffer, DataView, TypedArray
|
|
let length = val.byteLength;
|
|
let backingBuffer = _malloc(length);
|
|
let backingI8 = new Int8Array(Module.HEAPU8.buffer, backingBuffer, length);
|
|
Module._ScriptX_TypedArray_memcpy(val, backingI8);
|
|
setValue(buffer, backingBuffer, "void*");
|
|
}
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_ByteBuffer_fillTypeAndSize,
|
|
(int val, intptr_t pType, intptr_t pSize), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
val = stack.values[val];
|
|
|
|
let length = val.byteLength;
|
|
setValue(pSize, length, "i32");
|
|
|
|
// NOTE: KEEP SYNC WITH CPP
|
|
const kUnspecified = 0x1;
|
|
const kInt8 = 0x101;
|
|
const kUint8 = 0x201;
|
|
const kInt16 = 0x302;
|
|
const kUint16 = 0x402;
|
|
const kInt32 = 0x504;
|
|
const kUint32 = 0x604;
|
|
const kInt64 = 0x708;
|
|
const kUint64 = 0x808;
|
|
const KFloat32 = 0x904;
|
|
const kFloat64 = 0xa08;
|
|
|
|
let type = kUnspecified;
|
|
if (val instanceof Int8Array) {
|
|
type = kInt8;
|
|
} else if (val instanceof Uint8Array || val instanceof Uint8ClampedArray) {
|
|
type = kUint8;
|
|
} else if (val instanceof Int16Array) {
|
|
type = kInt16;
|
|
} else if (val instanceof Uint16Array) {
|
|
type = kUint16;
|
|
} else if (val instanceof Int16Array) {
|
|
type = kUint16;
|
|
} else if (val instanceof Int32Array) {
|
|
type = kInt32;
|
|
} else if (val instanceof Uint32Array) {
|
|
type = kUint32;
|
|
} else if (val instanceof Float32Array) {
|
|
type = KFloat32;
|
|
} else if (val instanceof Float64Array) {
|
|
type = kFloat64;
|
|
} else if (val instanceof BigInt64Array) {
|
|
type = kInt64;
|
|
} else if (val instanceof BigUint64Array) {
|
|
type = kUint64;
|
|
}
|
|
|
|
setValue(pType, type, "i16");
|
|
});
|
|
|
|
CHECKED_EM_JS(int, _ScriptX_ByteBuffer_fillWrapper,
|
|
(intptr_t pointer, intptr_t sharedPtr, size_t size), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
const sharedByteBuffer = new Module._ScriptX_SharedByteBuffer(pointer, size, sharedPtr);
|
|
return stack.push(sharedByteBuffer);
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_ByteBuffer_freeSharedByteBuffer, (int val), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
val = stack.values[val];
|
|
if (val instanceof Module._ScriptX_SharedByteBuffer) {
|
|
val.destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
CHECKED_EM_JS(void, _ScriptX_ByteBuffer_syncData,
|
|
(int val, intptr_t buffer, size_t length, bool isCommitOrSync), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
val = stack.values[val];
|
|
|
|
// only if not shared
|
|
if (!(val instanceof Module._ScriptX_SharedByteBuffer)) {
|
|
let backingI8 = new Int8Array(Module.HEAPU8.buffer, buffer, length);
|
|
if (isCommitOrSync) {
|
|
Module._ScriptX_TypedArray_memcpy(backingI8, val);
|
|
} else {
|
|
Module._ScriptX_TypedArray_memcpy(val, backingI8);
|
|
}
|
|
}
|
|
});
|
|
|
|
CHECKED_EM_JS(bool, _ScriptX_ByteBuffer_isSharedByteBuffer, (int val), {
|
|
const stack = Module.SCRIPTX_STACK;
|
|
val = stack.values[val];
|
|
return val instanceof Module._ScriptX_SharedByteBuffer;
|
|
});
|
|
|
|
// clang-format on
|
|
// </editor-fold>
|
|
|
|
namespace {
|
|
|
|
struct StaticInit {
|
|
StaticInit() noexcept;
|
|
} dummy;
|
|
|
|
StaticInit::StaticInit() noexcept {
|
|
_ScriptX_setInternalStatePointer(
|
|
reinterpret_cast<intptr_t>(&kExceptionStore.hasPendingJsException_),
|
|
reinterpret_cast<intptr_t>(&kExceptionStore.hasPendingCppException_));
|
|
|
|
_ScriptX_initJavaScriptLibrary();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int Stack::top() noexcept { CHECKED_JS_CALL(_ScriptX_Stack_top()); }
|
|
|
|
Local<Value> Stack::topValue() noexcept {
|
|
CHECKED_JS_CALL(wasm_backend::WasmEngine::make<Local<Value>>(top()));
|
|
}
|
|
|
|
int Stack::setTop(int top) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_setTop(top)); }
|
|
|
|
int Stack::set(int atIndex, int valueIndex) noexcept {
|
|
_ScriptX_Stack_set(atIndex, valueIndex);
|
|
return atIndex;
|
|
}
|
|
|
|
int Stack::set(int atIndex, const Local<Value> &value) noexcept {
|
|
CHECKED_JS_CALL(set(atIndex, WasmEngine::refIndex(value)));
|
|
}
|
|
|
|
int Stack::newObject() { CHECKED_JS_CALL(_ScriptX_Stack_newObject()); }
|
|
|
|
int Stack::newObject(int type, int argsBase) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_newObjectArgs(type, argsBase));
|
|
}
|
|
|
|
int Stack::newArray(size_t size) { CHECKED_JS_CALL(_ScriptX_Stack_newArray(size)); }
|
|
|
|
int Stack::newString(const char *str) { return newString(str, std::strlen(str)); }
|
|
|
|
int Stack::newString(const char *str, size_t length) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_newStringLen(str, length));
|
|
}
|
|
|
|
int Stack::newNumber(int32_t num) { CHECKED_JS_CALL(_ScriptX_Stack_newNumInt(num)); }
|
|
|
|
int Stack::newNumber(double num) { CHECKED_JS_CALL(_ScriptX_Stack_newNumDouble(num)); }
|
|
|
|
int Stack::newBoolean(bool boolean) { CHECKED_JS_CALL(_ScriptX_Stack_newBoolean(boolean ? 1 : 0)); }
|
|
|
|
int Stack::newFunction(RawFunctionType callback, const void *ptr0, const void *ptr1,
|
|
bool checkForConstructorCall) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_newRawFunction(
|
|
reinterpret_cast<intptr_t>(callback), reinterpret_cast<intptr_t>(ptr0),
|
|
reinterpret_cast<intptr_t>(ptr1), ¤tEngine(), checkForConstructorCall));
|
|
}
|
|
|
|
int Stack::pushArrayBuffer(size_t length) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_pushArrayBuffer(length));
|
|
}
|
|
|
|
int Stack::newError(int messageIndex) { CHECKED_JS_CALL(_ScriptX_Stack_newError(messageIndex)); }
|
|
|
|
int Stack::pushValue(int index) { CHECKED_JS_CALL(_ScriptX_Stack_pushValue(index)); }
|
|
|
|
int Stack::pushValue(const Local<Value> &ref) {
|
|
CHECKED_JS_CALL(pushValue(WasmEngine::refIndex(ref)));
|
|
}
|
|
|
|
int Stack::pop(int count) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_pop(count)); }
|
|
|
|
bool Stack::isObject(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isObject(index)); }
|
|
|
|
bool Stack::isArray(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isArray(index)); }
|
|
|
|
bool Stack::isString(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isString(index)); }
|
|
|
|
bool Stack::isNumber(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isNum(index)); }
|
|
|
|
bool Stack::isBoolean(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isBoolean(index)); }
|
|
|
|
bool Stack::isFunction(int index) noexcept { CHECKED_JS_CALL(_ScriptX_Stack_isFunction(index)); }
|
|
|
|
bool Stack::isByteBuffer(int index) noexcept {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_isByteBuffer(index));
|
|
}
|
|
|
|
bool Stack::isUnsupported(int index) noexcept {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_isUnsupported(index));
|
|
}
|
|
|
|
bool Stack::isNullOrUndefined(int index) noexcept {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_isNullOrUndefined(index));
|
|
}
|
|
|
|
int Stack::toString(int index) { CHECKED_JS_CALL(_ScriptX_Stack_toString(index)); }
|
|
|
|
bool Stack::equals(int lhs, int rhs) { CHECKED_JS_CALL(_ScriptX_Stack_equals(lhs, rhs)); }
|
|
|
|
int Stack::objectGet(int obj, int key) { CHECKED_JS_CALL(_ScriptX_Stack_objectGet(obj, key)); }
|
|
|
|
void Stack::objectSet(int obj, int key, int value) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Stack_objectSet(obj, key, value));
|
|
}
|
|
|
|
void Stack::objectRemove(int obj, int key) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Stack_objectRemove(obj, key));
|
|
}
|
|
|
|
int Stack::objectHas(int obj, int key) { CHECKED_JS_CALL(_ScriptX_Stack_objectHas(obj, key)); }
|
|
|
|
int Stack::objectInstanceOf(int obj, int who) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_objectInstanceOf(obj, who));
|
|
}
|
|
|
|
int Stack::objectGetKeys(int obj) { CHECKED_JS_CALL(_ScriptX_Stack_objectGetKeys(obj)); }
|
|
|
|
int Stack::arrayGet(int arrayIndex, int index) {
|
|
CHECKED_JS_CALL(_ScriptX_Stack_arrayGet(arrayIndex, index));
|
|
}
|
|
|
|
void Stack::arraySet(int arrayIndex, int index, int valueIndex) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Stack_arraySet(arrayIndex, index, valueIndex));
|
|
}
|
|
|
|
int Stack::arrayLength(int arrayIndex) { CHECKED_JS_CALL(_ScriptX_Stack_arrayLength(arrayIndex)); }
|
|
|
|
void Stack::arrayClear(int arrayIndex) { _ScriptX_Stack_arrayClear(arrayIndex); }
|
|
|
|
float Stack::toNumberFloat(int num) { CHECKED_JS_CALL(_ScriptX_Stack_toNumFloat(num)); }
|
|
double Stack::toNumberDouble(int num) { CHECKED_JS_CALL(_ScriptX_Stack_toNumDouble(num)); }
|
|
int32_t Stack::toNumberInt32(int num) { CHECKED_JS_CALL(_ScriptX_Stack_toNumInt32(num)); }
|
|
int64_t Stack::toNumberInt64(int num) { CHECKED_JS_CALL(_ScriptX_Stack_toNumInt64(num)); }
|
|
bool Stack::toBoolean(int index) { CHECKED_JS_CALL(_ScriptX_Stack_toBoolean(index)); }
|
|
const char *Stack::toCString(int index) {
|
|
CHECKED_JS_CALL(reinterpret_cast<const char *>(_ScriptX_Stack_toCString(index)));
|
|
}
|
|
|
|
int Stack::getGlobal() { CHECKED_JS_CALL(_ScriptX_Stack_getGlobal()); }
|
|
|
|
int Stack::globalGet(int key) { CHECKED_JS_CALL(_ScriptX_Stack_globalGet(key)); }
|
|
|
|
void Stack::globalSet(int key, int value) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Stack_globalSet(key, value));
|
|
}
|
|
|
|
int evaluateJavaScriptCode(int code, int source) {
|
|
CHECKED_JS_CALL(_ScriptX_evaluateJavaScriptCode(code, source));
|
|
}
|
|
|
|
Local<Value> callFunction(const Local<Function> &func, const Local<Value> &thiz, size_t size,
|
|
const Local<Value> *args) {
|
|
/*
|
|
* call function
|
|
* args are [stackBase, stackTop],
|
|
* stack[0] == func
|
|
* stack[1] == this
|
|
* ... -> args
|
|
*
|
|
* pop args from stack and push result onto it.
|
|
*/
|
|
|
|
Local<Value> ret;
|
|
{
|
|
StackFrameScope frame;
|
|
int base = Stack::pushValue(func);
|
|
Stack::pushValue(thiz);
|
|
for (size_t i = 0; i < size; ++i) {
|
|
Stack::pushValue(args[i]);
|
|
}
|
|
auto retIndex = _ScriptX_callFunction(base);
|
|
checkException();
|
|
ret = frame.returnValue(wasm_backend::WasmEngine::make<Local<Value>>(retIndex));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int GlobalHelper::newGlobal(int localValueIndex, bool isWeak) {
|
|
// TODO(taylor): the weak is fake for now
|
|
// https://v8.dev/features/weak-references
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef
|
|
CHECKED_JS_CALL(_ScriptX_Global_new(localValueIndex, isWeak));
|
|
}
|
|
|
|
int GlobalHelper::dupGlobal(int key, bool isFromWeak, bool isToWeak) {
|
|
if (key == -1) return -1;
|
|
CHECKED_JS_CALL(_ScriptX_Global_dup(key, isFromWeak, isToWeak));
|
|
}
|
|
|
|
void GlobalHelper::deleteGlobal(int value, bool isWeak) {
|
|
if (value != -1) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Global_delete(value, isWeak));
|
|
}
|
|
}
|
|
|
|
int GlobalHelper::getGlobal(int key, bool isWeak) {
|
|
if (key == -1) return -1;
|
|
CHECKED_JS_CALL(_ScriptX_Global_get(key, isWeak));
|
|
}
|
|
|
|
void NativeHelper::setInternalState(int obj, const void *classDefine, void *instance) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Native_setInternalState(
|
|
obj, reinterpret_cast<uintptr_t>(classDefine), reinterpret_cast<uintptr_t>(instance)));
|
|
}
|
|
|
|
void *NativeHelper::getInternalStateInstance(int obj) {
|
|
CHECKED_JS_CALL(reinterpret_cast<void *>(_ScriptX_Native_getInternalStateInstance(obj)));
|
|
}
|
|
|
|
void *NativeHelper::getInternalStateClassDefine(int obj) {
|
|
CHECKED_JS_CALL(reinterpret_cast<void *>(_ScriptX_Native_getInternalStateClassDefine(obj)));
|
|
}
|
|
|
|
void NativeHelper::defineProperty(int obj, int name, int getter, int setter) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_Native_defineProperty(obj, name, getter, setter));
|
|
}
|
|
|
|
int NativeHelper::getNamespaceForRegister(int nameSpace) {
|
|
CHECKED_JS_CALL(_ScriptX_Native_getNamespaceForRegister(nameSpace));
|
|
}
|
|
|
|
int NativeHelper::pushCppNewMark() { CHECKED_JS_CALL(_ScriptX_Native_pushCppNewMark()); }
|
|
|
|
bool NativeHelper::isCppNewMark(int mark) { CHECKED_JS_CALL(_ScriptX_Native_isCppNewMark(mark)); }
|
|
|
|
int NativeHelper::getInternalStore(int weakThis) {
|
|
CHECKED_JS_CALL(_ScriptX_Native_getInternalStore(weakThis));
|
|
}
|
|
|
|
void ByteBufferHelper::fillBackingStore(int val, std::shared_ptr<void> **sharedPtr, void **buffer) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_ByteBuffer_fillBackingStore(
|
|
val, reinterpret_cast<intptr_t>(sharedPtr), reinterpret_cast<intptr_t>(buffer)));
|
|
}
|
|
|
|
int ByteBufferHelper::fillWrapper(void *pVoid, std::shared_ptr<void> *sharedPtr, size_t size) {
|
|
CHECKED_JS_CALL(_ScriptX_ByteBuffer_fillWrapper(reinterpret_cast<intptr_t>(pVoid),
|
|
reinterpret_cast<intptr_t>(sharedPtr), size));
|
|
}
|
|
|
|
void ByteBufferHelper::fillTypeAndSize(int val, uint16_t *pType, size_t *pSize) {
|
|
CHECKED_VOID_JS_CALL(_ScriptX_ByteBuffer_fillTypeAndSize(val, reinterpret_cast<intptr_t>(pType),
|
|
reinterpret_cast<intptr_t>(pSize)));
|
|
}
|
|
|
|
bool ByteBufferHelper::freeSharedByteBuffer(int val) {
|
|
CHECKED_JS_CALL(_ScriptX_ByteBuffer_freeSharedByteBuffer(val));
|
|
}
|
|
|
|
void ByteBufferHelper::commit(const Local<script::ByteBuffer> &buffer) {
|
|
auto &&internalState = wasm_backend::WasmEngine::refIndex(buffer);
|
|
auto jsBuffer = internalState.val_;
|
|
auto ptr = internalState.backingStore_.get();
|
|
// only do sync when we have 1) js array buffer 2) native buffer
|
|
if (jsBuffer != -1 && ptr) {
|
|
CHECKED_VOID_JS_CALL(wasm_backend::_ScriptX_ByteBuffer_syncData(
|
|
jsBuffer, reinterpret_cast<intptr_t>(ptr), buffer.byteLength(), true));
|
|
}
|
|
}
|
|
|
|
void ByteBufferHelper::sync(const Local<script::ByteBuffer> &buffer) {
|
|
auto &&internalState = wasm_backend::WasmEngine::refIndex(buffer);
|
|
auto jsBuffer = internalState.val_;
|
|
auto ptr = internalState.backingStore_.get();
|
|
// only do sync when we have 1) js array buffer 2) native buffer
|
|
if (jsBuffer != -1 && ptr) {
|
|
CHECKED_VOID_JS_CALL(wasm_backend::_ScriptX_ByteBuffer_syncData(
|
|
jsBuffer, reinterpret_cast<intptr_t>(ptr), buffer.byteLength(), false));
|
|
}
|
|
}
|
|
|
|
bool ByteBufferHelper::isSharedByteBuffer(const Local<ByteBuffer> &sharedByteBuffer) {
|
|
auto &&internalState = wasm_backend::WasmEngine::refIndex(sharedByteBuffer);
|
|
if (internalState.val_ == -1 && internalState.backingStore_) {
|
|
// not realized SharedByteBuffer
|
|
return true;
|
|
}
|
|
if (internalState.val_ != -1) {
|
|
CHECKED_JS_CALL(wasm_backend::_ScriptX_ByteBuffer_isSharedByteBuffer(internalState.val_));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace script::wasm_backend
|
|
|
|
namespace script {
|
|
|
|
Local<script::ByteBuffer> wasm_interop::newSharedByteBuffer(size_t size) {
|
|
return ByteBuffer::newByteBuffer(
|
|
std::shared_ptr<void>(std::malloc(size), [](void *ptr) { std::free(ptr); }), size);
|
|
}
|
|
|
|
void wasm_interop::destroySharedByteBuffer(const Local<ByteBuffer> &sharedByteBuffer) {
|
|
auto &&internalState = wasm_backend::WasmEngine::refIndex(sharedByteBuffer);
|
|
if (internalState.val_ != -1 &&
|
|
wasm_backend::ByteBufferHelper::freeSharedByteBuffer(internalState.val_)) {
|
|
internalState.val_ = -1;
|
|
}
|
|
}
|
|
|
|
void wasm_interop::destroyScriptClass(const Local<Object> &scriptClass) {
|
|
CHECKED_VOID_JS_CALL(wasm_backend::_ScriptX_Native_destroyInstance(
|
|
wasm_backend::WasmEngine::refIndex(scriptClass)));
|
|
}
|
|
|
|
} // namespace script
|