remove property access for bytecode functions
This commit is contained in:
@@ -72,7 +72,7 @@ function use_core(path) {
|
||||
var script = text(script_blob)
|
||||
var mod = `(function setup_module(use){${script}})`
|
||||
var fn = js.eval('core:' + path, mod)
|
||||
var result = fn.call(sym, use_core);
|
||||
var result = call(fn,sym, use_core)
|
||||
use_cache[cache_key] = result;
|
||||
return result;
|
||||
}
|
||||
@@ -432,7 +432,7 @@ $_.start = function start(cb, program, ...args) {
|
||||
if (!program) return
|
||||
|
||||
var id = guid()
|
||||
if (args.length == 1 && Array.isArray(args[0])) args = args[0]
|
||||
if (args.length == 1 && is_array(args[0])) args = args[0]
|
||||
var startup = {
|
||||
id,
|
||||
overling: $_.self,
|
||||
@@ -786,7 +786,7 @@ $_.clock(_ => {
|
||||
|
||||
// Call with signature: setup_module(args, use, ...capabilities)
|
||||
// The script wrapper builds $_ from the injected capabilities for backward compatibility
|
||||
var val = locator.symbol.call(null, _cell.args.arg, use_fn, ...vals)
|
||||
var val = call(locator.symbol, null, _cell.args.arg, use_fn, ...vals)
|
||||
|
||||
if (val)
|
||||
throw new Error('Program must not return anything');
|
||||
|
||||
@@ -782,6 +782,12 @@ function make_use_fn(pkg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Call a C module loader and execute the entrypoint
|
||||
function call_c_module(c_resolve) {
|
||||
var entry = c_resolve.symbol() // load symbol (returns function)
|
||||
return entry(null, my$_) // execute it to get exports/context
|
||||
}
|
||||
|
||||
function execute_module(info)
|
||||
{
|
||||
var c_resolve = info.c_resolve
|
||||
@@ -792,25 +798,30 @@ function execute_module(info)
|
||||
if (mod_resolve.scope < 900) {
|
||||
var context = null
|
||||
if (c_resolve.scope < 900) {
|
||||
context = c_resolve.symbol(null, my$_)
|
||||
context = call_c_module(c_resolve)
|
||||
}
|
||||
|
||||
|
||||
// Get file info to determine inject list
|
||||
var file_info = Shop.file_info(mod_resolve.path)
|
||||
var inject = Shop.script_inject_for(file_info)
|
||||
var vals = inject_values(inject)
|
||||
var pkg = file_info.package
|
||||
var use_fn = make_use_fn(pkg)
|
||||
|
||||
|
||||
// Call with signature: setup_module(args, use, ...capabilities)
|
||||
// args is null for module loading
|
||||
used = mod_resolve.symbol.call(context, null, use_fn, ...vals)
|
||||
used = call(mod_resolve.symbol, context, null, use_fn, ...vals)
|
||||
} else if (c_resolve.scope < 900) {
|
||||
// C only
|
||||
used = c_resolve.symbol(null, my$_)
|
||||
used = call_c_module(c_resolve)
|
||||
} else {
|
||||
throw new Error(`Module ${info.path} could not be found`)
|
||||
} if (!used)
|
||||
}
|
||||
|
||||
if (is_function(used))
|
||||
throw new Error('C module loader returned a function; did you forget to call it?')
|
||||
|
||||
if (!used)
|
||||
throw new Error(`Module ${info} returned null`)
|
||||
|
||||
// stone(used)
|
||||
|
||||
170
source/quickjs.c
170
source/quickjs.c
@@ -13252,6 +13252,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-1]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
sf->cur_pc = pc;
|
||||
val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
@@ -14370,6 +14379,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
#endif
|
||||
obj = sp[-1];
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(obj);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
/* Monomorphic IC fast path: shape-guarded own-property lookup */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
@@ -14450,6 +14468,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
#endif
|
||||
obj = sp[-1];
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(obj);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
/* Monomorphic IC fast path */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
@@ -14516,6 +14543,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
/* Record property access site */
|
||||
profile_record_prop_site(rt, b, (uint32_t)(pc - b->byte_code_buf), atom);
|
||||
#endif
|
||||
/* User-defined functions don't support property assignment in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-2]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot set property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2],
|
||||
JS_PROP_THROW_STRICT);
|
||||
JS_FreeValue(ctx, sp[-2]);
|
||||
@@ -14653,6 +14689,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-2]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
sf->cur_pc = pc;
|
||||
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
|
||||
JS_FreeValue(ctx, sp[-2]);
|
||||
@@ -14667,6 +14712,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-2]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
sf->cur_pc = pc;
|
||||
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
|
||||
sp[-1] = val;
|
||||
@@ -14679,6 +14733,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
|
||||
/* User-defined functions don't support property access in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-2]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot get property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
switch (JS_VALUE_GET_TAG(sp[-2])) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_STRING:
|
||||
@@ -14747,6 +14810,15 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* User-defined functions don't support property assignment in cell script */
|
||||
if (JS_VALUE_GET_TAG(sp[-3]) == JS_TAG_OBJECT) {
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(sp[-3]);
|
||||
if (fp->class_id == JS_CLASS_BYTECODE_FUNCTION) {
|
||||
JS_ThrowTypeError(ctx, "cannot set property of function");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
sf->cur_pc = pc;
|
||||
ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
|
||||
JS_FreeValue(ctx, sp[-3]);
|
||||
@@ -29320,13 +29392,19 @@ static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
|
||||
return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
JSValue name;
|
||||
const char *pref, *suff;
|
||||
pref = "function ";
|
||||
pref = "function ";
|
||||
suff = "() {\n [native code]\n}";
|
||||
name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
|
||||
/* Get name directly from structure rather than via property access */
|
||||
if (js_class_has_bytecode(p->class_id)) {
|
||||
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
||||
name = JS_AtomToString(ctx, b->func_name);
|
||||
} else {
|
||||
name = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
||||
}
|
||||
if (JS_IsNull(name))
|
||||
name = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
||||
return JS_ConcatString3(ctx, pref, name, suff);
|
||||
@@ -35480,6 +35558,7 @@ static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
|
||||
static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
|
||||
JSMapState *s;
|
||||
JSValue obj, adder = JS_NULL, iter = JS_NULL, next_method = JS_NULL;
|
||||
JSValueConst arr;
|
||||
@@ -35570,11 +35649,13 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
||||
JS_FreeValue(ctx, iter);
|
||||
JS_FreeValue(ctx, adder);
|
||||
}
|
||||
|
||||
return obj;
|
||||
fail_close:
|
||||
/* close the iterator object, preserving pending exception */
|
||||
JS_IteratorClose(ctx, iter, TRUE);
|
||||
fail:
|
||||
|
||||
JS_FreeValue(ctx, next_method);
|
||||
JS_FreeValue(ctx, iter);
|
||||
JS_FreeValue(ctx, adder);
|
||||
@@ -37184,6 +37265,42 @@ static JSValue js_cell_text(JSContext *ctx, JSValueConst this_val,
|
||||
return string_buffer_end(b);
|
||||
}
|
||||
|
||||
/* Handle function - return source or native stub */
|
||||
if (JS_IsFunction(ctx, arg)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(arg);
|
||||
if (js_class_has_bytecode(p->class_id)) {
|
||||
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
||||
if (b->has_debug && b->debug.source) {
|
||||
return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
|
||||
}
|
||||
}
|
||||
/* Native function - generate stub */
|
||||
const char *pref = "function ";
|
||||
const char *suff = "() {\n [native code]\n}";
|
||||
const char *name = NULL;
|
||||
JSObject *fp = JS_VALUE_GET_OBJ(arg);
|
||||
if (js_class_has_bytecode(fp->class_id)) {
|
||||
JSFunctionBytecode *fb = fp->u.func.function_bytecode;
|
||||
name = JS_AtomToCString(ctx, fb->func_name);
|
||||
}
|
||||
if (!name) name = "";
|
||||
size_t plen = strlen(pref);
|
||||
size_t nlen = strlen(name);
|
||||
size_t slen = strlen(suff);
|
||||
char *result = js_malloc(ctx, plen + nlen + slen + 1);
|
||||
if (!result) {
|
||||
if (name[0]) JS_FreeCString(ctx, name);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
memcpy(result, pref, plen);
|
||||
memcpy(result + plen, name, nlen);
|
||||
memcpy(result + plen + nlen, suff, slen + 1);
|
||||
JSValue ret = JS_NewString(ctx, result);
|
||||
js_free(ctx, result);
|
||||
if (name[0]) JS_FreeCString(ctx, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle null */
|
||||
if (JS_IsNull(arg))
|
||||
return JS_NULL;
|
||||
@@ -39530,12 +39647,19 @@ static JSValue js_cell_length(JSContext *ctx, JSValueConst this_val,
|
||||
if (JS_IsNull(val))
|
||||
return JS_NULL;
|
||||
|
||||
/* Functions return arity */
|
||||
/* Functions return arity (accessed directly, not via properties) */
|
||||
if (JS_IsFunction(ctx, val)) {
|
||||
JSValue len = JS_GetPropertyStr(ctx, val, "length");
|
||||
if (JS_IsException(len))
|
||||
return len;
|
||||
return len;
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
switch (p->class_id) {
|
||||
case JS_CLASS_BYTECODE_FUNCTION:
|
||||
return JS_NewInt32(ctx, p->u.func.function_bytecode->defined_arg_count);
|
||||
case JS_CLASS_C_FUNCTION:
|
||||
return JS_NewInt32(ctx, p->u.cfunc.length);
|
||||
case JS_CLASS_C_FUNCTION_DATA:
|
||||
return JS_NewInt32(ctx, p->u.c_function_data_record->length);
|
||||
default:
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int tag = JS_VALUE_GET_TAG(val);
|
||||
@@ -39579,6 +39703,31 @@ static JSValue js_cell_length(JSContext *ctx, JSValueConst this_val,
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* call() function - call a function with explicit this and arguments
|
||||
* ============================================================================ */
|
||||
|
||||
/* call(func, this_val, ...args) */
|
||||
static JSValue js_cell_call(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "call requires a function argument");
|
||||
|
||||
JSValue func = argv[0];
|
||||
if (!JS_IsFunction(ctx, func))
|
||||
return JS_ThrowTypeError(ctx, "first argument must be a function");
|
||||
|
||||
JSValue this_arg = JS_NULL;
|
||||
if (argc >= 2)
|
||||
this_arg = argv[1];
|
||||
|
||||
int call_argc = argc > 2 ? argc - 2 : 0;
|
||||
JSValueConst *call_argv = call_argc > 0 ? &argv[2] : NULL;
|
||||
|
||||
return JS_Call(ctx, func, this_arg, call_argc, call_argv);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* is_* type checking functions
|
||||
* ============================================================================ */
|
||||
@@ -40011,6 +40160,11 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
||||
JS_NewCFunction(ctx, js_cell_length, "length", 1),
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
|
||||
/* call() function */
|
||||
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "call",
|
||||
JS_NewCFunction(ctx, js_cell_call, "call", 3),
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
|
||||
/* is_* type checking functions */
|
||||
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "is_array",
|
||||
JS_NewCFunction(ctx, js_cell_is_array, "is_array", 1),
|
||||
|
||||
@@ -1966,4 +1966,85 @@ return {
|
||||
if (result != null) throw "object get with null key should return null"
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// FUNCTION AS VALUE (not object) - functions should not have properties
|
||||
// ============================================================================
|
||||
|
||||
test_function_property_get_throws: function() {
|
||||
var fn = function(a, b) { return a + b }
|
||||
var caught = false
|
||||
try {
|
||||
var x = fn.length
|
||||
} catch (e) {
|
||||
caught = true
|
||||
}
|
||||
if (!caught) throw "getting property on function should throw"
|
||||
},
|
||||
|
||||
test_function_property_set_throws: function() {
|
||||
var fn = function() {}
|
||||
var caught = false
|
||||
try {
|
||||
fn.foo = 123
|
||||
} catch (e) {
|
||||
caught = true
|
||||
}
|
||||
if (!caught) throw "setting property on function should throw"
|
||||
},
|
||||
|
||||
test_function_bracket_access_throws: function() {
|
||||
var fn = function() {}
|
||||
var caught = false
|
||||
try {
|
||||
var x = fn["length"]
|
||||
} catch (e) {
|
||||
caught = true
|
||||
}
|
||||
if (!caught) throw "bracket access on function should throw"
|
||||
},
|
||||
|
||||
test_length_returns_function_arity: function() {
|
||||
var fn0 = function() { return 1 }
|
||||
var fn1 = function(a) { return a }
|
||||
var fn2 = function(a, b) { return a + b }
|
||||
var fn3 = function(a, b, c) { return a + b + c }
|
||||
|
||||
if (length(fn0) != 0) throw "length(fn0) should be 0"
|
||||
if (length(fn1) != 1) throw "length(fn1) should be 1"
|
||||
if (length(fn2) != 2) throw "length(fn2) should be 2"
|
||||
if (length(fn3) != 3) throw "length(fn3) should be 3"
|
||||
},
|
||||
|
||||
test_text_returns_function_source: function() {
|
||||
var fn = function(x) { return x * 2 }
|
||||
var src = text(fn)
|
||||
if (src.indexOf("function") == -1) throw "text(fn) should contain 'function'"
|
||||
if (src.indexOf("return") == -1) throw "text(fn) should contain function body"
|
||||
},
|
||||
|
||||
test_call_invokes_function: function() {
|
||||
var fn = function(a, b) { return a + b }
|
||||
var result = call(fn, null, 3, 4)
|
||||
if (result != 7) throw "call(fn, null, 3, 4) should return 7"
|
||||
},
|
||||
|
||||
test_call_with_this_binding: function() {
|
||||
var obj = { value: 10 }
|
||||
var fn = function(x) { return this.value + x }
|
||||
var result = call(fn, obj, 5)
|
||||
if (result != 15) throw "call(fn, obj, 5) should return 15"
|
||||
},
|
||||
|
||||
test_call_no_args: function() {
|
||||
var fn = function() { return 42 }
|
||||
var result = call(fn, null)
|
||||
if (result != 42) throw "call(fn, null) should return 42"
|
||||
},
|
||||
|
||||
test_builtin_function_properties_still_work: function() {
|
||||
// Built-in functions like number, text, array should still have properties
|
||||
var min_result = number.min(5, 3)
|
||||
if (min_result != 3) throw "number.min should work"
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user