Skip to content

Commit 2cf4f49

Browse files
bnoordhuissaghul
authored andcommitted
Add support for text imports
The two failing tests are: - a quine (imports itself); needs some more work in the module loader - dynamic imports; the dynamic loader doesn't support attributes at all at the moment
1 parent e6e4e76 commit 2cf4f49

File tree

5 files changed

+87
-118
lines changed

5 files changed

+87
-118
lines changed

quickjs-libc.c

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -788,28 +788,14 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
788788
return 0;
789789
}
790790

791-
static int json_module_init(JSContext *ctx, JSModuleDef *m)
791+
static int default_module_init(JSContext *ctx, JSModuleDef *m)
792792
{
793793
JSValue val;
794794
val = JS_GetModulePrivateValue(ctx, m);
795795
JS_SetModuleExport(ctx, m, "default", val);
796796
return 0;
797797
}
798798

799-
static JSModuleDef *create_json_module(JSContext *ctx, const char *module_name, JSValue val)
800-
{
801-
JSModuleDef *m;
802-
m = JS_NewCModule(ctx, module_name, json_module_init);
803-
if (!m) {
804-
JS_FreeValue(ctx, val);
805-
return NULL;
806-
}
807-
/* only export the "default" symbol which will contain the JSON object */
808-
JS_AddModuleExport(ctx, m, "default");
809-
JS_SetModulePrivateValue(ctx, m, val);
810-
return m;
811-
}
812-
813799
/* in order to conform with the specification, only the keys should be
814800
tested and not the associated values. */
815801
int js_module_check_attributes(JSContext *ctx, void *opaque,
@@ -842,91 +828,115 @@ int js_module_check_attributes(JSContext *ctx, void *opaque,
842828
return ret;
843829
}
844830

831+
enum {
832+
JS_IMPORT_TYPE_JS,
833+
JS_IMPORT_TYPE_JSON,
834+
JS_IMPORT_TYPE_TEXT,
835+
};
836+
845837
/* return > 0 if the attributes indicate a JSON module, 0 otherwise, -1 on error */
846-
int js_module_test_json(JSContext *ctx, JSValueConst attributes)
838+
static int js_module_import_type(JSContext *ctx, JSValueConst attributes)
847839
{
848840
JSValue str;
849841
const char *cstr;
850842
size_t len;
851843
int res;
852844

853845
if (JS_IsUndefined(attributes))
854-
return 0;
846+
return JS_IMPORT_TYPE_JS;
855847
str = JS_GetPropertyStr(ctx, attributes, "type");
856848
if (JS_IsException(str))
857849
return -1;
858850
if (!JS_IsString(str)) {
859851
JS_FreeValue(ctx, str);
860-
return 0;
852+
return JS_IMPORT_TYPE_JS;
861853
}
862854
cstr = JS_ToCStringLen(ctx, &len, str);
863855
JS_FreeValue(ctx, str);
864856
if (!cstr)
865857
return -1;
866858
if (len == 4 && !memcmp(cstr, "json", len)) {
867-
res = 1;
859+
res = JS_IMPORT_TYPE_JSON;
860+
} else if (len == 4 && !memcmp(cstr, "text", len)) {
861+
res = JS_IMPORT_TYPE_TEXT;
868862
} else {
869863
/* unknown type - throw error */
870864
JS_ThrowTypeError(ctx, "unsupported module type: '%s'", cstr);
871-
JS_FreeCString(ctx, cstr);
872-
return -1;
865+
res = -1;
873866
}
874867
JS_FreeCString(ctx, cstr);
875868
return res;
876869
}
877870

878-
JSModuleDef *js_module_loader(JSContext *ctx,
879-
const char *module_name, void *opaque,
880-
JSValueConst attributes)
871+
JSModuleDef *js_module_load(JSContext *ctx, const char *module_name,
872+
void *opaque, JSValueConst attributes,
873+
JSLoadFileFunc *load_file)
881874
{
882875
JSModuleDef *m;
876+
JSValue val;
877+
size_t buf_len;
878+
char *buf;
879+
int type;
883880

884-
if (js__has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX)) {
885-
m = js_module_loader_so(ctx, module_name);
886-
} else {
887-
int res;
888-
size_t buf_len;
889-
uint8_t *buf;
890-
891-
res = js_module_test_json(ctx, attributes);
892-
if (res < 0)
893-
return NULL;
894-
buf = js_load_file(ctx, &buf_len, module_name);
895-
if (!buf) {
896-
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
897-
module_name);
881+
type = js_module_import_type(ctx, attributes);
882+
if (type < 0)
883+
return NULL;
884+
if (js__has_suffix(module_name, ".json"))
885+
type = JS_IMPORT_TYPE_JSON;
886+
buf = (char *)load_file(ctx, &buf_len, module_name);
887+
if (!buf) {
888+
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
889+
module_name);
890+
return NULL;
891+
}
892+
switch (type) {
893+
case JS_IMPORT_TYPE_JS:
894+
val = JS_Eval(ctx, buf, buf_len, module_name,
895+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
896+
break;
897+
case JS_IMPORT_TYPE_JSON:
898+
val = JS_ParseJSON(ctx, buf, buf_len, module_name);
899+
break;
900+
case JS_IMPORT_TYPE_TEXT:
901+
val = JS_NewStringLen(ctx, buf, buf_len);
902+
break;
903+
default:
904+
val = JS_ThrowInternalError(ctx, "unhandled import type");
905+
break;
906+
}
907+
js_free(ctx, buf);
908+
if (JS_IsException(val))
909+
return NULL;
910+
if (type == JS_IMPORT_TYPE_JS) {
911+
if (js_module_set_import_meta(ctx, val, true, false) < 0) {
912+
JS_FreeValue(ctx, val);
898913
return NULL;
899914
}
900-
if (js__has_suffix(module_name, ".json") || res > 0) {
901-
/* compile as JSON */
902-
JSValue val;
903-
val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);
904-
js_free(ctx, buf);
905-
if (JS_IsException(val))
906-
return NULL;
907-
m = create_json_module(ctx, module_name, val);
908-
if (!m)
909-
return NULL;
910-
} else {
911-
JSValue func_val;
912-
/* compile the module */
913-
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
914-
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
915-
js_free(ctx, buf);
916-
if (JS_IsException(func_val))
917-
return NULL;
918-
if (js_module_set_import_meta(ctx, func_val, true, false) < 0) {
919-
JS_FreeValue(ctx, func_val);
920-
return NULL;
921-
}
922-
/* the module is already referenced, so we must free it */
923-
m = JS_VALUE_GET_PTR(func_val);
924-
JS_FreeValue(ctx, func_val);
915+
// the module is already referenced, so we must free it
916+
m = JS_VALUE_GET_PTR(val);
917+
JS_FreeValue(ctx, val);
918+
} else {
919+
m = JS_NewCModule(ctx, module_name, default_module_init);
920+
if (!m) {
921+
JS_FreeValue(ctx, val);
922+
return NULL;
925923
}
924+
// only export the "default" symbol which will contain the string
925+
// or JSON object
926+
JS_AddModuleExport(ctx, m, "default");
927+
JS_SetModulePrivateValue(ctx, m, val);
926928
}
927929
return m;
928930
}
929931

932+
JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name,
933+
void *opaque, JSValueConst attributes)
934+
{
935+
if (js__has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX))
936+
return js_module_loader_so(ctx, module_name);
937+
return js_module_load(ctx, module_name, opaque, attributes, js_load_file);
938+
}
939+
930940
static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val,
931941
int argc, JSValueConst *argv)
932942
{

quickjs-libc.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#define QUICKJS_LIBC_H
2626

2727
#include <stdbool.h>
28+
#include <stdint.h>
2829
#include <stdio.h>
2930
#include <stdlib.h>
3031

@@ -34,6 +35,9 @@
3435
extern "C" {
3536
#endif
3637

38+
typedef uint8_t *JSLoadFileFunc(JSContext *ctx, size_t *pbuf_len,
39+
const char *filename);
40+
3741
JS_LIBC_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx,
3842
const char *module_name);
3943
JS_LIBC_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx,
@@ -55,9 +59,13 @@ JS_LIBC_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_v
5559
JS_LIBC_EXTERN JSModuleDef *js_module_loader(JSContext *ctx,
5660
const char *module_name, void *opaque,
5761
JSValueConst attributes);
62+
// like js_module_loader but does not load .so objects and the file reader
63+
// is pluggable; js_module_loader is implemented in terms of js_module_load
64+
JS_LIBC_EXTERN JSModuleDef *js_module_load(JSContext *ctx, const char *module_name,
65+
void *opaque, JSValueConst attributes,
66+
JSLoadFileFunc *load_file);
5867
JS_LIBC_EXTERN int js_module_check_attributes(JSContext *ctx, void *opaque,
5968
JSValueConst attributes);
60-
JS_LIBC_EXTERN int js_module_test_json(JSContext *ctx, JSValueConst attributes);
6169
JS_LIBC_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf,
6270
size_t buf_len, int flags);
6371
JS_LIBC_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx,

run-test262.c

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -970,24 +970,11 @@ static char *load_file(const char *filename, size_t *lenp)
970970
return buf;
971971
}
972972

973-
static int json_module_init(JSContext *ctx, JSModuleDef *m)
974-
{
975-
JSValue val;
976-
val = JS_GetModulePrivateValue(ctx, m);
977-
JS_SetModuleExport(ctx, m, "default", val);
978-
return 0;
979-
}
980-
981973
static JSModuleDef *js_module_loader_test(JSContext *ctx,
982974
const char *module_name, void *opaque,
983975
JSValueConst attributes)
984976
{
985-
size_t buf_len;
986-
uint8_t *buf;
987-
JSModuleDef *m;
988-
JSValue func_val;
989977
char *filename, *slash, path[1024];
990-
int res;
991978

992979
// interpret import("bar.js") from path/to/foo.js as
993980
// import("path/to/bar.js") but leave import("./bar.js") untouched
@@ -1000,46 +987,7 @@ static JSModuleDef *js_module_loader_test(JSContext *ctx,
1000987
module_name = path;
1001988
}
1002989
}
1003-
1004-
/* check for JSON module */
1005-
res = js_module_test_json(ctx, attributes);
1006-
if (res < 0)
1007-
return NULL;
1008-
1009-
buf = js_load_file(ctx, &buf_len, module_name);
1010-
if (!buf) {
1011-
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
1012-
module_name);
1013-
return NULL;
1014-
}
1015-
1016-
if (res > 0 || js__has_suffix(module_name, ".json")) {
1017-
/* compile as JSON */
1018-
JSValue val;
1019-
val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);
1020-
js_free(ctx, buf);
1021-
if (JS_IsException(val))
1022-
return NULL;
1023-
m = JS_NewCModule(ctx, module_name, json_module_init);
1024-
if (!m) {
1025-
JS_FreeValue(ctx, val);
1026-
return NULL;
1027-
}
1028-
JS_AddModuleExport(ctx, m, "default");
1029-
JS_SetModulePrivateValue(ctx, m, val);
1030-
return m;
1031-
}
1032-
1033-
/* compile the module */
1034-
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
1035-
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
1036-
js_free(ctx, buf);
1037-
if (JS_IsException(func_val))
1038-
return NULL;
1039-
/* the module is already referenced, so we must free it */
1040-
m = JS_VALUE_GET_PTR(func_val);
1041-
JS_FreeValue(ctx, func_val);
1042-
return m;
990+
return js_module_load(ctx, module_name, opaque, attributes, js_load_file);
1043991
}
1044992

1045993
int is_line_sep(char c)

test262.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ immutable-arraybuffer
116116
import-attributes
117117
import-bytes=skip
118118
import-defer=skip
119-
import-text=skip
119+
import-text
120120
import.meta
121121
Int16Array
122122
Int32Array

test262_errors.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,13 @@ test262/test/language/expressions/assignment/target-member-computed-reference.js
7171
test262/test/language/expressions/assignment/target-member-computed-reference.js:22: strict mode: Test262Error: Expected a DummyError but got a Test262Error
7272
test262/test/language/expressions/assignment/target-super-computed-reference.js:20: Test262Error: Expected a DummyError but got a Test262Error
7373
test262/test/language/expressions/assignment/target-super-computed-reference.js:20: strict mode: Test262Error: Expected a DummyError but got a Test262Error
74+
test262/test/language/expressions/dynamic-import/import-attributes/2nd-param-with-type-text.js:10: TypeError: $DONE() not called
75+
test262/test/language/expressions/dynamic-import/import-attributes/2nd-param-with-type-text.js:10: strict mode: TypeError: $DONE() not called
7476
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated.
7577
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated.
7678
test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true
7779
test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: strict mode: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true
80+
test262/test/language/import/import-attributes/text-self.js:10: SyntaxError: Could not find export 'default' in module 'test262/test/language/import/import-attributes/text-self.js'
7881
test262/test/language/module-code/ambiguous-export-bindings/import-and-export-propagates-binding.js:75: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/imp' is ambiguous
7982
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from-and-import-star-as-and-export.js:74: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/nam' is ambiguous
8083
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js:75: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/nam' is ambiguous

0 commit comments

Comments
 (0)