remove property access for bytecode functions

This commit is contained in:
2026-01-11 13:22:36 -06:00
parent 17b9aaaf51
commit ffe7b61ae2
4 changed files with 263 additions and 17 deletions

View File

@@ -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');

View File

@@ -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,7 +798,7 @@ 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
@@ -804,13 +810,18 @@ function execute_module(info)
// 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)

View File

@@ -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]);
@@ -29326,7 +29398,13 @@ static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
const char *pref, *suff;
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),

View File

@@ -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"
},
}