Skip to content

Commit

Permalink
Remove require from the core binding
Browse files Browse the repository at this point in the history
  • Loading branch information
Geequlim committed Aug 30, 2020
1 parent dfbd376 commit 03a12ec
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 142 deletions.
8 changes: 4 additions & 4 deletions ecmascript.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#include "scene/resources/text_file.h"

#define EXT_JSCLASS "jsx"
#define EXT_JSCLASS_BYTECODE "jsxb"
#define EXT_JSCLASS_ENCRYPTED "jsxe"
#define EXT_JSCLASS_BYTECODE EXT_JSCLASS "b"
#define EXT_JSCLASS_ENCRYPTED EXT_JSCLASS "e"
#define EXT_JSMODULE "js"
#define EXT_JSMODULE_BYTECODE "jsb"
#define EXT_JSMODULE_ENCRYPTED "jse"
#define EXT_JSMODULE_BYTECODE EXT_JSMODULE "b"
#define EXT_JSMODULE_ENCRYPTED EXT_JSMODULE "e"
#define EXT_JSON "json"

class ECMAScript : public Script {
Expand Down
5 changes: 0 additions & 5 deletions misc/godot.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ declare module globalThis {
*/
function cancelAnimationFrame(request_id: FrameRequetID): void;

/** Used to import CommonJS modules or load resources */
function require(path: string): any;
const module: { exports: any };
const exports: any;

/**
* The Console API provides functionality to allow developers to perform debugging tasks, such as logging messages or the values of variables at set points in your code, or timing how long an operation takes to complete.
*/
Expand Down
143 changes: 15 additions & 128 deletions quickjs/quickjs_binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,6 @@ void QuickJSBinder::add_global_console() {
}

void QuickJSBinder::add_global_properties() {
// globalThis.require
JSValue js_func_require = JS_NewCFunction(ctx, require_function, "require", 1);
JS_DefinePropertyValueStr(ctx, global_object, "require", js_func_require, PROP_DEF_DEFAULT);

// globalThis.requestAnimationFrame
JSValue js_func_requestAnimationFrame = JS_NewCFunction(ctx, global_request_animation_frame, "requestAnimationFrame", 1);
JS_DefinePropertyValueStr(ctx, global_object, "requestAnimationFrame", js_func_requestAnimationFrame, PROP_DEF_DEFAULT);
Expand Down Expand Up @@ -579,7 +575,7 @@ void QuickJSBinder::add_debug_binding_info(JSContext *ctx, JSValueConst p_obj, c

static HashMap<String, String> resolve_path_cache;

static String resolve_module_file(const String &file, bool allow_node_module = false) {
String QuickJSBinder::resolve_module_file(const String &file) {
if (const String *ptr = resolve_path_cache.getptr(file)) {
return *ptr;
}
Expand All @@ -604,34 +600,6 @@ static String resolve_module_file(const String &file, bool allow_node_module = f
return path;
}
}

if (allow_node_module && !file.begins_with(".")) {
String package_file = "node_modules/" + file + "/package.json";
bool package_found = false;
if (FileAccess::exists("user://" + package_file)) {
package_file = "user://" + package_file;
package_found = true;
} else if (FileAccess::exists("res://" + package_file)) {
package_file = "res://" + package_file;
package_found = true;
}
if (package_found) {
Error err;
String package_content = FileAccess::get_file_as_string(package_file, &err);
ERR_FAIL_COND_V_MSG(err != OK, "", "Fail to load module package: " + package_file);
Variant package_parse_ret;
String package_parse_err;
int error_line;
if (OK != JSON::parse(package_content, package_parse_ret, package_parse_err, error_line)) {
ERR_FAIL_V_MSG("", "Fail to parse module package:" + package_file + ENDL + package_parse_err + ENDL + "At " + itos(error_line));
}
Dictionary dict = package_parse_ret;
String entry = dict.has("main") ? dict["main"] : "index.js";
entry = "node_modules/" + file + "/" + entry;
ERR_FAIL_COND_V_MSG(!FileAccess::exists(entry), "", "Module entry does not exists: " + entry);
return entry;
}
}
return "";
}

Expand All @@ -642,7 +610,7 @@ JSModuleDef *QuickJSBinder::js_module_loader(JSContext *ctx, const char *module_
String resolving_file;
resolving_file.parse_utf8(module_name);

String file = resolve_module_file(resolving_file, false);
String file = resolve_module_file(resolving_file);
ERR_FAIL_COND_V_MSG(file.empty(), NULL, "Failed to resolve module: '" + resolving_file + "'.");
resolve_path_cache.set(resolving_file, file);

Expand Down Expand Up @@ -684,14 +652,9 @@ JSModuleDef *QuickJSBinder::js_module_loader(JSContext *ctx, const char *module_
JS_ThrowReferenceError(ctx, "Could not load module '%s'", file.utf8().get_data());
return NULL;
}
// hack the quick module to make the resource value as default entry
m = JS_NewCModule(ctx, file.utf8().get_data(), resource_module_initializer);
JS_AddModuleExport(ctx, m, "default");
JSValue func = JS_MKPTR(JS_TAG_MODULE, m);
JS_DupValue(ctx, func);
JS_EvalFunction(ctx, func);
JSValue val = variant_to_var(ctx, res);
JS_SetModuleExport(ctx, m, "default", val);
m = js_make_module(ctx, file, val);
JS_FreeValue(ctx, val);

ModuleCache module;
Variant hash_var = res;
Expand All @@ -701,14 +664,24 @@ JSModuleDef *QuickJSBinder::js_module_loader(JSContext *ctx, const char *module_
module.res->reference(); // Avoid auto release as module don't release automaticly
module.res_value = val;
module.flags = MODULE_FLAG_RESOURCE;
module.module = static_cast<JSModuleDef *>(JS_VALUE_GET_PTR(func));
module.module = m;
binder->module_cache.set(file, module);
}
}

return m;
}

JSModuleDef *QuickJSBinder::js_make_module(JSContext *ctx, const String &p_id, const JSValue &p_value) {
JSModuleDef *m = JS_NewCModule(ctx, p_id.utf8().get_data(), resource_module_initializer);
JS_AddModuleExport(ctx, m, "default");
JSValue func = JS_MKPTR(JS_TAG_MODULE, m);
JS_DupValue(ctx, func);
JS_EvalFunction(ctx, func);
JS_SetModuleExport(ctx, m, "default", JS_DupValue(ctx, p_value));
return m;
}

QuickJSBinder::ModuleCache QuickJSBinder::js_compile_module(JSContext *ctx, const String &p_code, const String &p_filename, ECMAscriptScriptError *r_error) {

if (NULL != compiling_modules.find(p_filename)) {
Expand Down Expand Up @@ -822,91 +795,6 @@ int QuickJSBinder::resource_module_initializer(JSContext *ctx, JSModuleDef *m) {
return JS_SetModuleExport(ctx, m, "default", JS_UNDEFINED);
}

JSValue QuickJSBinder::require_function(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
ERR_FAIL_COND_V(argc < 1 || !validate_type(ctx, Variant::STRING, argv[0]), JS_ThrowTypeError(ctx, "A string argument expected for require"));
String file = js_to_string(ctx, argv[0]);
if (file.begins_with(".")) {
JSValue func = JS_GetStackFunction(ctx, 1);
JSValue filename = JS_GetProperty(ctx, func, JS_ATOM_fileName);
String caller_path = js_to_string(ctx, filename);
JS_FreeValue(ctx, filename);
JS_FreeValue(ctx, func);
file = ECMAScriptLanguage::globalize_relative_path(file, caller_path.get_base_dir());
}
String resolving_file = file;
file = resolve_module_file(file, true);
ERR_FAIL_COND_V_MSG(file.empty(), (JS_UNDEFINED), "Failed to resolve module '" + resolving_file + "'.");
resolve_path_cache.set(resolving_file, file);

if (NULL != compiling_modules.find(file)) {
String chain;
for (List<String>::Element *E = compiling_modules.front(); E; E = E->next()) {
chain += E->get();
if (E->next() != NULL) {
chain += " <- ";
}
}
return JS_ThrowTypeError(ctx, "Cyclic module import detected:\r\n %s", chain.utf8().get_data());
}

JSValue ret = JS_UNDEFINED;
String md5 = FileAccess::get_md5(file);
QuickJSBinder *binder = QuickJSBinder::get_context_binder(ctx);
if (CommonJSModule *ptr = binder->commonjs_module_cache.getptr(md5)) {
ret = JS_DupValue(ctx, ptr->exports);
} else {
CommonJSModule m;
m.md5 = md5;
m.exports = JS_UNDEFINED;
List<String> extensions;
ECMAScriptLanguage::get_singleton()->get_recognized_extensions(&extensions);
if (extensions.find(file.get_extension()) != NULL) {
Error err;
Ref<ECMAScriptModule> em = ResourceFormatLoaderECMAScriptModule::load_static(file, "", &err);
ERR_FAIL_COND_V(err != OK || em.is_null(), JS_ThrowTypeError(ctx, "Error to load module file %s", file.utf8().get_data()));
String text = em->get_source_code();
if (!text.empty()) {
if (file.ends_with(EXT_JSON)) {
CharString utf8code = text.utf8();
ret = JS_ParseJSON(ctx, utf8code.get_data(), utf8code.length(), file.utf8().get_data());
} else {
String code = "(function() {"
" const module = {"
" exports: {}"
" };"
" let exports = module.exports;"
" (function(){ " +
text +
" }"
" )();"
" return module.exports;"
"})();";
CharString utf8code = code.utf8();
compiling_modules.push_back(file);
ret = JS_Eval(ctx, utf8code.get_data(), utf8code.length(), file.utf8().get_data(), JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRICT);
compiling_modules.pop_back();
}
} else {
// TODO: require module from bytecode
}
m.exports = JS_DupValue(ctx, ret);
m.flags = MODULE_FLAG_SCRIPT;
} else {
RES res = ResourceLoader::load(file);
if (!res.is_null()) {
ret = variant_to_var(ctx, res);
m.exports = ret;
m.flags = MODULE_FLAG_RESOURCE;
m.res = res;
} else {
ret = JS_ThrowReferenceError(ctx, "Cannot load resource from '%s'", file.utf8().get_data());
}
}
binder->commonjs_module_cache.set(md5, m);
}
return ret;
}

JSClassID QuickJSBinder::register_class(const ClassDB::ClassInfo *p_cls) {

ClassBindData data;
Expand Down Expand Up @@ -1392,7 +1280,6 @@ void QuickJSBinder::initialize() {
add_global_console();
// binding script
String script_binding_error;

ECMAScriptGCHandler eval_ret;
if (OK == safe_eval_text(ECMAScriptBinder::BINDING_SCRIPT_CONTENT, ECMAScriptBinder::EVAL_TYPE_GLOBAL, "<internal: binding_script.js>", script_binding_error, eval_ret)) {
#ifdef TOOLS_ENABLED
Expand Down
9 changes: 5 additions & 4 deletions quickjs/quickjs_binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ class QuickJSBinder : public ECMAScriptBinder {

enum ECMAScriptModuleFlags {
MODULE_FLAG_SCRIPT = 1,
MODULE_FLAG_EVALUATED = 1 << 1,
MODULE_FLAG_RESOURCE = 1 << 2,
MODULE_FLAG_NATIVE = 2,
MODULE_FLAG_EVALUATED = 1 << 3,
MODULE_FLAG_RESOURCE = 1 << 4,
};

struct ModuleCache {
Expand Down Expand Up @@ -121,14 +122,14 @@ class QuickJSBinder : public ECMAScriptBinder {
}
_FORCE_INLINE_ static void *js_binder_realloc(JSMallocState *s, void *ptr, size_t size) { return memrealloc(ptr, size); }

static String resolve_module_file(const String &file);
static JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque);
static JSModuleDef *js_make_module(JSContext *ctx, const String &p_id, const JSValueConst &p_value);
ModuleCache *js_compile_and_cache_module(JSContext *ctx, const String &p_code, const String &p_filename, ECMAscriptScriptError *r_error);
ModuleCache *js_compile_and_cache_module(JSContext *ctx, const Vector<uint8_t> &p_bytecode, const String &p_filename, ECMAscriptScriptError *r_error);
ModuleCache js_compile_module(JSContext *ctx, const String &p_code, const String &p_filename, ECMAscriptScriptError *r_error);
static Error js_evalute_module(JSContext *ctx, ModuleCache *p_module, ECMAscriptScriptError *r_error);

static int resource_module_initializer(JSContext *ctx, JSModuleDef *m);
static JSValue require_function(JSContext *ctx, JSValue this_val, int argc, JSValue *argv);

struct ClassBindData {
JSClassID class_id;
Expand Down
2 changes: 1 addition & 1 deletion quickjs/quickjs_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ JSValue QuickJSWorker::global_import_scripts(JSContext *ctx, JSValue this_val, i
for (int i = 0; i < argc; i++) {
if (JS_IsString(argv[i])) {
Error err;
String path = js_to_string(ctx, argv[0]);
String path = resolve_module_file(js_to_string(ctx, argv[0]));
String source = FileAccess::get_file_as_string(path, &err);
QuickJSBinder *bind = get_context_binder(ctx);
ECMAScriptGCHandler eval_ret;
Expand Down

0 comments on commit 03a12ec

Please sign in to comment.